Mostrando postagens com marcador microterminal. Mostrar todas as postagens
Mostrando postagens com marcador microterminal. Mostrar todas as postagens

Comunicando com microterminal em ASP.NET

Quem já foi em supermercado e nunca viu aquelas maquininhas que o operador fica digitando? Pois é... estamos falando dela mesma. Parece ser fácil seu uso, mas quem começa a trabalhar com elas é de perder os cabelos.


Nesse artigo veremos como comunicar com um microterminal via TCP/IP e trocar informações (ida e volta). Para isso vamos utilizar o modelo da Gertec MT 720. Uma breve descrição do aparelho e seu uso (retirado daqui):

"O MT 720 é um excelente microterminal que opera em rede Ethernet com protocolos TCP-IP. Seu teclado com 20 teclas programáveis pode ser utilizado com funções específicas de atalhos para agilizar a operação.
As teclas re-legendáveis permitem a identificação da função de cada tecla. O display com back light (iluminado), com 20 caracteres por 2 linhas, permite facilmente a visualização da informação, principalmente em ambientes que possuem pouca iluminação, como bares noturnos, por exemplo.
Através de uma porta de teclado (AT/PS2) e mais 3 portas Seriais (RS-232) é possível a conexão de periféricos como: Leitor de Código de Barras, Display de Cliente, Balança, Impressora, etc.
O MT 720 é ideal para aplicações de Cartão de Consumo, soluções para Bares, Lanchonetes, Restaurantes Self-Service, Livrarias, Papelarias, apontamento de produção em Indústrias."
Na própria página do dispositivo (citada acima) tem alguns downloads de arquivos, dentre eles alguns códigos-fontes com exemplos de como fazer a comunicação bem como realizar as operações pelas quais foi criado. Um porém: só tem disponíveis nas linguagens C++, Delphi e VB (não é VB.NET).

Daí pensamos: "sem problemas! é só converter pra VB.NET a partir do VB"... Também pensei nisso e não consegui! Dando uma olhada de perto no código-fonte dá para saber que as proezas encontradas são só possíveis por artifícios que a linguagem oferece: por exemplo, ponteiros. Quem já programou em VB via a grande flexibilidade da linguagem de fazer tudo o que quer e do jeito que era mais conveniente. Maravilha! Pena que tenhamos que seguir certos padrões. Fica a dica: se não quiser perder tempo, pegue logo o código prontinho lá na página do fabricante (vem também no CD quando compra o aparelho) e utilize. De preferência use o Delphi e em seguida em VB. Se quiser aventurar vamos com C#...

Se escolheu C# siga lendo pois, em diante, veremos como fazer... Teremos os seguintes objetivos na implementação:

  1. Criar uma única aplicação que irá receber e enviar os dados;
  2. Exibir o que foi digitado tanto no microterminal quanto no monitor.


Instalação

Leia o manual do usuário aqui. Em resumo: ligue o microterminal na rede elétrica e conecte na porta traseira um cabo RJ-45 no dispositivo e a rede. Em seguida configure, no microterminal, o IP do Servidor (no meu caso, considerei minha máquina local com o IP 192.168.1.100, por exemplo). Demais configurações a seu gosto...



Implementação

Criemos um Windows Form. Coloquei alguns controles para embelezar a aplicação...



O mais importante é adicionar um objeto Timer. Os demais coloquei para gerar a aplicação final (que ficará no tray - perto do relógio - e outras coisas fru-frus). Configure seu Timer com Interval de 50. Pegue a DLL pmtg.dll e copie para sua aplicação. Eu disse copie! Você não conseguirá aplicar Reference sobre ela. Clique com o botão direito sobre a DLL e escolha Properties. Em Copy to Output Directory coloque Copy Always.



Agora vamos ao código! Clique sobre o WinForm e escolha View Code. Cole os códigos abaixo para que possamos usá-las no form:


        // VARIÁVEIS
        IntPtr v_Hwnd;
        int statusCard;
        int statusSerial;
        int MsgReceiveData = 1;
        string[] dados = new string[255];


        // INICIALIZAÇÃO DE FUNÇÕES EXTERNAS
        [DllImport("pmtg.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        public static extern int mt_startserver(IntPtr mywhnd, int conecmsg, int commumsg);
        [DllImport("pmtg.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        public static extern void mt_finishserver();
        [DllImport("pmtg.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        public static extern int mt_getkey(int id, StringBuilder str);
        [DllImport("pmtg.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        public static extern int mt_backspace(int id);
        [DllImport("pmtg.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        public static extern int mt_dispstr(int id, string str);
        [DllImport("pmtg.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        public static extern int mt_dispch(int id, char ch);
        [DllImport("pmtg.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        public static extern int mt_dispclrln(int id, int lin);
        [DllImport("pmtg.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]]
        public static extern int mt_gotoxy(int id, int lin, int col);



Em seguida adicionamos as duas funções primordiais: início (quando carregar o formulário) e término (quando fechar o formulário):


        private void MicroTerminal_Load(object sender, EventArgs e)
        {
            v_Hwnd = this.Handle;
            statusCard = 0;
            statusSerial = 0;
            if (mt_startserver(v_Hwnd, 0, MsgReceiveData) == 1)
            {
                temporizadorLista.Enabled = true;
            }
            else
                MessageBox.Show("Não foi possível carregar os módulos de conexão à leitora!");
        }



        private void MicroTerminal_FormClosed(object sender, FormClosedEventArgs e)
        {
            temporizadorLista.Enabled = false;
            mt_finishserver();
        }



Lembrando que esses eventos devem estar configurados (não é apenas copiar e colar o código, lembra? deve-se indicar lá em Events do Form). Com isso, ao fazer o primeiro teste, sua aplicação já estará recebendo dados do microterminal. Em seguida devemos adicionar um evento Tick ao Timer. Ou seja, a cada 50 milisegundos (lembra que colocamos com esse valor) irá ser executado um determinado método.

Antes disso vamos criar um método que irá limpar a tela do microterminal e reiniciar os dados de entrada:


                 private void ReiniciaEntrada(int i)
        {
            // Limpa as duas linhas e o que foi armazenado
            dados[i] = "";
            mt_dispclrln(i, 1);
            mt_dispclrln(i, 2);
            // Coloca o cursor na primeira linha e prepara o display
            mt_gotoxy(i, 1, 0);
            mt_dispstr(i, "Numero: ");
            mt_gotoxy(i, 2, 0);
        }


Enfim adicionamos o código que irá ler temporariamente cada leitora conectada à rede e coletar/processar os dados de acordo com o que desejar. No meu caso deixei o seguinte:


        private void temporizadorLista_Tick(object sender, EventArgs e)
        {
            StringBuilder rntStr = new StringBuilder();
            int i = 0;
            // Loop em toda faixa de Ips úteis: cada leitora conectada na rede
            while (i < 255)
            {
                // Captura algo do teclado
                if (mt_getkey(i, rntStr) == 1)
                {
                    string chrAsHex = ((int)rntStr[0]).ToString("x");
                    // Entrada do display caso seja um número
                    if ((chrAsHex == "3030") || (chrAsHex == "30") || (chrAsHex == "31") || (chrAsHex == "32") || (chrAsHex == "33") || (chrAsHex == "34") || (chrAsHex == "35") || (chrAsHex == "36") || (chrAsHex == "37") || (chrAsHex == "38") || (chrAsHex == "39"))
                    {
                        if (dados[i].Length >= 20)
                            dados[i] = dados[i].Substring(0, 19) + rntStr[0];
                        else
                            dados[i] += rntStr[0];
                        mt_dispch(i, rntStr[0]);
                    }
                    // Se for um ENTER, dá a entrada na tela para mostrar
                    else if (chrAsHex == "d")
                    {
                        // Captura e trata a entrada
                        if (dados[i] != "")
                        {
                            // Faz o processamento do que desejar em dados[i]
                        }
                        // Limpa tela
                        ReiniciaEntrada(i);
                    }
                    // Se for um ESC, apaga tudo
                    else if (chrAsHex == "1b")
                    {
                        // Limpa tela
                        ReiniciaEntrada(i);
                    }
                    // Se for backspace apaga último caractere do display
                    else if (chrAsHex == "8")
                    {
                        if (dados[i].Length > 0)
                            dados[i] = dados[i].Substring(0, dados[i].Length - 1);
                        mt_backspace(i);
                    }
                    // Se apertar * toca o som
                    else if (chrAsHex == "2a")
                    {
                        // Toca um sonzinho... depois publico o código de tocar som!
                    }
                }
                i++;
            }
        }


Feito! Rode sua aplicação e digite algo no teclado do microterminal e verá que o que está sendo digitado está indo tanto para a aplicação quanto para o display (no microterminal). A saída seria algo do tipo:


Se precisar de saída na tela crie um outro WinForm que ocupe toda tela (sua) e captura o que está no display do microterminal. Assim é mais fácil! Ou faça do jeito que sua imaginação mandar! O mais difícil já passou...

Para quem quiser capturar os IPs de cada microterminal, utilize os métodos:


[DllImport("pmtg.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern TTABSOCK mt_connectlist();

ou


[DllImport("pmtg.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern StringBuilder mt_ipfromid(int id);



Nessas funções tem algumas particularidades muito chatinhas de se trabalhar (veja no manual das funções) por isso deixei de lado. Principalmente a classe:


    [StructLayout(LayoutKind.Sequential)]
    public struct TTABSOCK
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)]
        public UInt32[] TSOCK;
    }


Quem quiser se aventurar nela, fica aí a dica. Esse artigo merecia até uma vídeo-aula, mas a preguiça às vezes é muito forte! É até legalzinho ter esses desafios, mas às vezes se torna muito chato quando não consegue. Enfim, quanto menos se estressar, melhor! Então tente usar as implementações que já a Gertec já disponibilizou, caso não queira aventurar, a menos que precise de algo mais específico.
Falar que nem Marcoratti: "eu sei, é apenas ASP.NET, mas eu gosto!"