*Este artigo se aplica a versões 2005 ou superior do SQL Server. Na versão 2000, haverá restrições de 8000 caracteres na geração de arquivos, devido a limitação do varchar(max) do SQL 2000.
O título pode soar um tanto estranho. Mas o fato é que esta se revelou uma grande solução “surpresa” durante o desenvolvimento de um projeto de automação através de coletores de dados.
Para explicar melhor o contexto onde isto foi adotado, tudo começou durante a implantação de um projeto de automação na conferência de mercadorias, confecção de embalagens e expedição de volumes. Adotamos 3 coletores ARGOX PT-60 (fig.1) com Windows CE 5.0 e conexão wireless e 3 impressoras ZEBRA GK420t (fig.2) instaladas em um único computador dedicado, compartilhando as impressoras pela rede.
Fig.1 Coletor Argox PT-60 | Fig.2 Impressora Térmica ZEBRA GK420t |
O sistema, nomeado ARKAB, foi desenvolvido em vb.net utilizando o MS Compact framework. Basicamente, o sistema atuava de forma on-line com o servidor SQL, buscando dados referente os pedidos de venda, e alimentando tabelas durante a conferência, possibilitando que dois operadores pudessem conferir o mesmo pedido.
Este sistema permite conferir as mercadorias que estão no pedido de venda. O separador pega todos os produtos utilizando o romaneio de separação (fig.3), e entrega em um carrinho para o conferente. Este identifica o pedido no coletor através de código de barras (fig.3), identifica-se através do seu crachá pessoal e também identifica qual impressora irá utilizar (código de barras na impressora, conf. fig.4).
Fig.3 Romaneio de Separação | Fig.4 Identificação da impressora |
Mas aí surgiram problemas, que prejudicaram o funcionamento do projeto. Durante o final do mês, a empresa realiza muito processamento no servidor na geração de lotes contábeis, fechamentos fiscais, emissão de DREs pesadas e abarrotadas de dados. E os coletores começaram a ficar muito lentos, prejudicando significativamente a performance da equipe de separação.
Outro problema que ocorria sempre era ao enviar uma impressão pela rede. De alguma forma, o Windows CE exibe uma messagebox com o resultado do comando de impressão ao usuário, o que atrapalha (e muito!) durante a conferência. A mensagem aparece na tela, o usuário não percebe, e ele bipa um produto. Como no final do comando sempre tem um ENTER, a tela de mensagem é fechada, e o usuário nem teria percebido que aquele produto bipado somente fechou a mensagem, e não foi processado pelo software. Isto acontecia por que eu gerava um arquivo texto temporário no coletor, e o copiava pela rede diretamente para a impressora, e o próprio Windows CE gerencia esta mensagem, o que parece não ser possível desativar. |
Para resolver isto, modifiquei o sistema do coletor, fazendo com que carregasse em DATASET todos os dados necessários para realizar a conferência, de forma que acessasse o banco de dados duas vezes (na abertura e no fechamento da conferência), e não mais em cada operação. O aumento de performance foi absoluto, o coletor não dependia mais da rede para ter um bom desempenho ou não. Mas ainda restava a etiqueta. Como fazer a impressão de forma que não aparecesse esta mensagem de status de impressão ao usuário?
Resolvi então fazer a impressão da etiqueta térmica utliizando o próprio SQL Server, de forma que o servidor enviasse o comando para a impressora, e não mais o coletor. A solução aparentemente muito simples e funcional me fez perceber que podemos fazer qualquer sistema que utilize base SQL Server imprimir diretamente em impressoras térmicas de linguagem específica (PPLA, PPLB, ZPL, etc).
Para isso, vamos dividir esta informação em três partes fundamentais: geração dos dados da etiqueta, salvar estes dados em um arquivo texto e finalmente copiar este arquivo diretamente à impressora desejada pela rede.
1. Geração dos dados da etiqueta
O primeiro procedimento que montei é o que realiza a busca de todas as informações no banco de dados para a impressão da etiqueta, e monta dinamicamente todos os comandos ZPL necessários para a impressão da etiqueta:
- -- set nocount on
- -- exec spArkabEtiqueta 9
- create proc [dbo].[spArkabEtiqueta]
- @id_volume int
- , @cd_impressora int
- as
- /*
- Autor: Fabiano Cores
- Data : 06/2011
- Objetivo: Gera a etiqueta através de comandos ZPL, salva em arquivo
- através de automação OLE e lança o arquivo diretamente para a impressora
- térmica na rede através de simples compartilhamento, somente copiando
- o arquivo para o dispositivo (como \\pc-na-rede\nome_imp_zebra)
- */
- declare @t table (texto varchar(8000) null, ordem int not null identity)
- declare @cd_empresa int
- declare @nr_pedido int
- declare @nr_volume int
- declare @qt_volume int
- declare @cd_usuario int
- declare @qt_pecas int
- declare @cliente varchar(60)
- declare @transportadora varchar(60)
- declare @endereco varchar(200)
- declare @complemento varchar(200)
- declare @pedido varchar(20)
- declare @volume varchar(20)
- declare @responsavel varchar(50)
- declare @data varchar(20)
- declare @quantidade varchar(20)
- declare @cdb varchar(50)
- declare @vtexto varchar(8000)
- declare @texto varchar(max)
- declare @diretorio varchar(255)
- declare @arquivo varchar(100)
- declare @origem varchar(500)
- declare @destino varchar(500)
- select @cd_empresa = cd_empresa
- , @nr_pedido = nr_pedido
- , @nr_volume = nr_volume
- , @cd_usuario = cd_usuario
- , @cdb = cdb
- from ArkabVolume
- where id_volume = @id_volume
- select @qt_pecas = sum(qt_item)
- from arkabvolumeitem
- where id_Volume = @id_volume
- select @qt_volume = count(*)
- from ArkabVolume
- where cd_empresa = @cd_empresa
- and nr_pedido = @nr_pedido
- select @cliente = rtrim(cadastro.razao)
- , @transportadora = isnull(rtrim(transp.razao),'')
- , @endereco = rtrim(cadastro.endereco)
- , @complemento = rtrim(ltrim( rtrim(isnull(cadastro.complemento, ''))
- + ' ' + rtrim(cidade.nome) + '/' + rtrim(cidade.cd_estado)
- + ' - ' + left(rtrim(cadastro.cep),5) + '-' + right(rtrim(cadastro.cep),3)))
- , @pedido = right('000000' + rtrim(@nr_pedido), 6)
- , @volume = right('000' + rtrim(@nr_volume),3)-- + '/' + right('00' + rtrim(@qt_volume),2)
- , @responsavel = rtrim(usuario.login)
- , @data = convert(char(10), getdate(), 103) + ' '
- + right('00' + rtrim(datepart(hour, getdate())),2) + 'h'
- , @quantidade = rtrim(@qt_pecas) + ' PC'
- from cadastro, cidade, usuario, pedidovenda, cadastro transp
- where pedidovenda.cd_cadastro = cadastro.cd_cadastro
- and cidade.cd_Cidade = cadastro.cd_cidade
- and pedidovenda.cd_empresa = @cd_empresa
- and pedidovenda.nr_pedido = @nr_pedido
- and usuario.cd_usuario = @cd_usuario
- and pedidovenda.cd_transportador *= transp.cd_cadastro
- select @texto = '
- ^XA
- ^MD10
- ^LT0
- ^JMA^FS
- ^MUd,200,200
- ^LH6,0
- ^FO20,7
- ^GB780,625,5,,0
- ^FS
- ^FO20,70
- ^GB645,120,5,,0
- ^FS
- ^FO20,7
- ^GB780,70,70,,0
- ^FS
- ^FO660,70
- ^GB140,562,5,,0
- ^FS
- ^FO20,275
- ^GB235,160,5,,0
- ^FS
- ^FO30,305
- ^GB215,120,120,,0
- ^FS
- ^FO250,275
- ^GB180,160,5,,0
- ^FS
- ^FO260,305
- ^GB160,120,120,,0
- ^FS
- ^FO425,275
- ^GB240,85,5,,0
- ^FS
- ^FO425,355
- ^GB240,80,5,,0
- ^FS
- ^FO425,430
- ^GB240,75,5,,0
- ^FS
- ^FO425,500
- ^GB240,132,5,,0
- ^FS
- ^FO90,20,0
- ^ATN,30,20
- ^FR
- ^FD
- DECORLUX MATERIAL ELETRICO LTDA
- ^FS
- ^FO30,80,0
- ^AQN,20,10
- ^FD
- CLIENTE
- ^FS
- ^FO30,103,0
- ^ATN,30,20
- ^FD
- ' + left(rtrim(@cliente), 29) + '
- ^FS
- ^FO30,138,0
- ^ATN,30,20
- ^FD
- ' + substring(@cliente, 30, 29) + '
- ^FS
- ^FO30,138,0
- ^ASN,30,20
- ^FD
- ^FS
- ^FO30,200,0
- ^AQN,20,10
- ^FD
- TRANSPORTADORA
- ^FS
- ^FO30,223,0
- ^ASN,30,20
- ^FD
- ' + rtrim(@transportadora) + '
- ^FS
- ^FO30,280,0
- ^AQN,20,10
- ^FD
- PEDIDO DE VENDA
- ^FS
- ^FO40,290,0
- ^AVN,120,100
- ^FR
- ^FD
- ' + rtrim(@pedido) + '
- ^FS
- ^FO265,280,0
- ^AQN,20,10
- ^FD
- VOLUME
- ^FS
- ^FO290,290,0
- ^AVN,120,100
- ^FR
- ^FD
- ' + rtrim(@volume) + '
- ^FS
- ^FO435,280,0
- ^AQN,20,10
- ^FD
- RESPONSAVEL
- ^FS
- ^FO435,310,0
- ^ASN,20,10
- ^FD
- ' + rtrim(@responsavel) + '
- ^FS
- ^FO435,360,0
- ^AQN,20,10
- ^FD
- DATA
- ^FS
- ^FO435,385,0
- ^ASN,30,20
- ^FD
- ' + rtrim(@data) + '
- ^FS
- ^FO435,435,0
- ^AQN,20,10
- ^FD
- QUANTIDADE
- ^FS
- ^FO435,460,0
- ^ASN,30,20
- ^FD
- ' + rtrim(@quantidade) + '
- ^FS
- ^FO30,435,0
- ^AQN,20,10
- ^FD
- NOTA FISCAL E VISTO
- ^FS
- ^FO690,97
- ^BY2
- ^BCR,100,Y,N,N
- ^FD
- ' + rtrim(@cdb) + '
- ^FS
- ^XZ'
- -- Diretorio para gravar o arquivo temporário
- set @diretorio = 'D:\Arkab\impressoes\'
- -- Definição do nome do arquivo, coloca dia, mes, ano,
- -- hora, minuto, segundo e ID do volume.
- select @arquivo = 'tmp'
- + right('00' + rtrim(datepart(day, getdate())),2)
- + right('00' + rtrim(datepart(month, getdate())),2)
- + rtrim(datepart(year, getdate()))
- + right('00' + rtrim(datepart(hour, getdate())),2)
- + right('00' + rtrim(datepart(minute, getdate())),2)
- + right('00' + rtrim(datepart(second, getdate())),2)
- + '_vol_' + rtrim(@id_volume) + '.txt'
- -- Gravo o arquivo
- exec spArkabGravaArquivo
- @texto, @diretorio, @arquivo
- -- Definição da origem e destino do arquivo salvo.
- select @origem = rtrim(@diretorio) + rtrim(@arquivo)
- -- Algo como '\\PC-CONF01\ZEBRA-SEP1'
- select @destino = caminho_de_rede
- from ArkabImpressora
- where cd_impressora = @cd_impressora
- -- Envio o arquivo texto pela rede diretamente na impressora térmica.
- exec spArkabCopiaArquivo
- @origem, @destino
Este procedimento coleta todos os dados, concatena em um varchar(max), e passa esta string para a segunda etapa, que é gravar este texto em um arquivo em uma pasta específica no servidor.
Exemplo do texto gerado pelo procedimento:
^XA ^FO435,360,0 ^FO435,385,0 ^FO435,435,0 ^FO435,460,0 ^FO30,435,0 ^FO690,97 |
Veja que depois defino qual o destino final do arquivo (no caso, uma impressora zebra na rede). No caso do sistema Arkab, a definição da impressora é dinâmica, no momento da abertura da conferência o usuário seleciona qual irá utilizar. Ao imprimir a etiqueta, o sistema passa para o procedimento como parâmetro (@cd_impressora) qual será a impressora que deverá enviar o comando. Uma simples tabela permite referenciar o código desta impressora ao seu caminho de rede.
E logo no final, temos o procedimento spArkabCopiaArquivo, que faz a cópia do arquivo do servidor para a impressora utilizando a rede. Vamos então falar de cada um deles agora.
2. spArkabGravaArquivo - Este procedimento grava o texto desejado em um arquivo em disco:
- create proc spArkabGravaArquivo
- @texto varchar(max), --8000 in SQL Server 2000
- @diretorio varchar(255),
- @arquivo varchar(100)
- as
- /*
- Autor: Fabiano Cores
- Data : 06/2011
- Objetivo: Permite gravar arquivos de texto em disco diretamente
- através do T-SQL.
- Observação importante: pode haver a necessidade de ativar a
- opção 'Ole Automation Procedures' no SQL Server através do comando
- sp_reconfigure. Para isto, utilize o script abaixo:
- -- Mostrar opções avançadas.
- exec sp_configure 'show advanced options', 1
- GO
- -- Ativar a opção anterior
- reconfigure
- GO
- -- Ativa a automação OLE
- exec sp_configure 'Ole Automation Procedures', 1
- GO
- -- Reconfigura para aceitar a modificação
- reconfigure
- GO
- */
- declare @objFileSystem int
- declare @objTextStream int
- declare @hr int
- declare @fileAndPath varchar(80)
- exec @hr = sp_OACreate 'Scripting.FileSystemObject'
- , @objFileSystem output
- select @FileAndPath = @diretorio + '\' + @arquivo
- if @hr = 0
- exec @hr = sp_OAMethod @objFileSystem
- , 'CreateTextFile'
- , @objTextStream output
- , @FileAndPath
- , 2
- , True
- if @hr=0
- exec @hr = sp_OAMethod @objTextStream
- , 'Write'
- , Null
- , @texto
- if @hr = 0
- exec @hr = sp_OAMethod @objTextStream, 'Close'
- exec sp_OADestroy @objTextStream
- exec sp_OADestroy @objTextStream
3. spArkabCopiaArquivo – Realiza a cópia do arquivo da etiqueta diretamente para a impressora:
- /*
- -- Para testar, copia o arquivo direto para impressora SEP1.
- exec spArkabCopiaArquivo
- 'D:\Arkab\Impressoes\tmp26062011030828_vol_4.txt'
- , '\\PC-CONF01\ZEBRA-SEP1'
- */
- create proc spArkabCopiaArquivo
- @origem varchar(200)
- , @destino varchar(200)
- as
- /*
- Autor: Fabiano Cores
- Data : 06/2011
- Objetivo: Permite copiar arquivos, informando origem e destino.
- Observação importante: pode haver a necessidade de ativar a
- opção 'xp_cmdshell' no SQL Server através do comando
- sp_reconfigure. Para isto, utilize o script abaixo:
- -- Mostrar opções avançadas.
- exec sp_configure 'show advanced options', 1
- GO
- -- Ativar a opção anterior
- reconfigure
- GO
- -- Ativa o comando
- exec sp_configure 'xp_cmdshell', 1
- GO
- -- Reconfigura para aceitar a modificação
- reconfigure
- GO
- */
- declare @string varchar(400)
- select @string = 'copy ' + rtrim(@origem) + ' ' + rtrim(@destino)
- exec master.dbo.xp_cmdshell @string, no_output
O resultado, é uma etiqueta saindo pela impressora, como esta daqui de exemplo (exatamente a mesma do procedimento acima):
Uma solução interessante…