[SP-pm] Perl para automação em acesso a x3270 IBM

Eden Cardim edencardim at gmail.com
Mon Jul 9 13:59:55 PDT 2012


>>>>> "Marcio" == Marcio  <- Google <marciorp em gmail.com>> writes:
    Marcio> Preciso criar um programa para navegar (ler e inserir) em um sistema
    Marcio> que roda em Mainframe IBM (x3270(9)).

Ai caramba... Lá vem pergunta difícil...

    Marcio> Ele irá simular a interação humana, conecta no host, espera
    Marcio> pela tela, insere dados em determinadas posições e submete
    Marcio> para o host, etc.  Se usar Perl com Windows é moleza com o
    Marcio> módulo Win32::HostExplorer, o problema é que estou usando
    Marcio> Linux em modo texto, nada de ambiente gráfico.  Não achei
    Marcio> uma versão do Win32::HostExplorer para Linux, mais achei o
    Marcio> http://x3270.bgp.nu/, mais especificamente o s3270. Instalei
    Marcio> e compilei, rodei o módulo c3270 e funciona perfeito,
    Marcio> conectou no host, montou as telas, entrou dados e tudo mais.
    Marcio> Agora preciso usar o s3270 que é pelo que entendi o módulo
    Marcio> para automação. Só que a documentação é muito fraca e
    Marcio> encontrei meia dúzia de exemplos mais fracos ainda.

Não sei se você chegou a encontrar essas páginas, mas achei bem
completo:

http://x3270.bgp.nu/s3270-man.html
http://x3270.bgp.nu/x3270-script.html

    Marcio>  Até consegui fazer um script pequeno em Perl (usando um
    Marcio> exemplo) que conecta no host, e faz alguma interação, mais
    Marcio> agora esbarrei no conhecimento limitado de Perl.

    Marcio> O que consegui fazer até agora é isso:
    Marcio> <snip>

    Marcio> O código funciona, pois a variável $out tem tudo o que foi processado
    Marcio> (saída) e os dados (telas) estão certos.
    Marcio> A dificuldade está no fato de que o script é processado como um todo
    Marcio> quando chega à linha "finish $h or die "s3270 returned $?";" e a
    Marcio> variável $out só é alimentada ai. A questão é que cada vez que eu
    Marcio> entro dados eu preciso processar o retorno do host (ler a tela) para
    Marcio> verificar se está tudo certo e ir para a próxima ação.

    Marcio> Para ficar mais fácil de entender, a cada vez que eu mandar a
    Marcio> instrução "Ascii(0,0,24,80)\n" preciso que a variável $out seja
    Marcio> alimentada com tudo o retorno do host até essa ação, para que eu possa
    Marcio> pegar os dados e ver se está tudo certo, depois disso limpar a
    Marcio> variável e continuar até a próxima vez que eu precisar ler o retorno.
    Marcio> Já tentei colocar o "finish ..." no meio e ler a variável de saída,
    Marcio> mais depois do "finish ..." o script é encerrado, independente de eu
    Marcio> enviar as instruções “disconnect” ou “quit”.
    Marcio> A variável de retorno tem as telas que o host retorna (não tem tamanho
    Marcio> fixo), então tenho que sair garimpando no meio os dados que preciso
    Marcio> para saber qual a próxima ação a ser enviada.
    Marcio> Acho que a dificuldade está em saber usar o módulo IPC::Run. Já fui à
    Marcio> página da documentação, mais não entendi muito bem, acho que eu
    Marcio> decorrência da minha limitação em Perl.

Isso é consequência de você não estar avaliando a variável $out quando
alimenta a variável $in. Você teria que fazer algo assim:

  $in .= "connect(10.1.8.192:23)\n";
  $in .= "wait(InputField)\n";
  $in .= "String(usertest)\n";
  pump $h until $out =~ /\Gvalor esperado/
  # etc...

    Marcio> Alguém já fez algo parecido? Tem exemplos mais completos que possa me passar?
    Marcio> Ou sabe onde eu possa achar um bom material?
    Marcio> Ou ao invés de usar esse módulo, tem algo mais simples para essa interação?

O IPC::Run não é o módulo ideal pra fazer esse tipo de coisa. Você vai
se dar melhor com o Expect.

  my $exp = Expect->spawn('s3270');
  my $cmd1 = "connect(10.1.8.192:23)\n";
  $cmd1 .= "wait(InputField)\n";
  $cmd1 .= "String(usertest)\n";
  $cmd1 .= "wait(InputField)\n";
  $cmd1 .= "String(passtest)\n";
  $cmd1 .= "Ascii(0,0,24,80)\n";
  
  my $cmd2 = "ENTER\n";
  $cmd2 .= "wait(1, Seconds)\n";
  $cmd2 .= "wait(Unlock)\n";
  $cmd2 .= "wait(InputField)\n";
  $cmd2 .= "String(A4L2)\n";
  $cmd2 .= "Ascii(0,0,24,80)\n";
  
  $exp->send($cmd1);
  $exp->expect(100, [
    qr/resposta cmd1: (.+)/ => sub {
      my $self = shift;

      # $resposta vai ter o que casou com (.+)
      my($resposta) = $self->match_list;

      # faça algo com $resposta aqui

      # enviar segundo comando
      $self->send($cmd2);

      exp_continue;
    },
    qr/resposta cmd2: (.+)/ => sub {
       my $self = shift;
       my($resposta) = $self->match_list;

       # etc...

       exp_continue;
    }, 
    timeout => sub { die 'o sistema parou de responder' }
  ]);

As subrotinas à direita só são executadas se a expressão à esquerda
casar com saída do s3270, basicamente, é uma máquina de estados. Além
disso, o Expect é desenvolvido levando em consideração que você está
conectado em um terminal, então ele já converte sequências de escape e
coisas do gênero, coisa que o IPC::Run não faz e que pode complicar sua
vida.

    Marcio> O cliente usa Mainframe IBM e tem um ERP rodando nele que é acessado
    Marcio> em estações Windows com o Extra!. Tem também outra aplicação em Linux,
    Marcio> feito em várias linguagens, e essa aplicação precisa trocar alguns
    Marcio> dados com o Mainframe. No Windows alguns usuários avançados usam
    Marcio> automação OLE para fazer scripts em Excel e ler dados dele,
    Marcio> funcionando muito bem.

    Marcio> A interface entre os dois aplicativos não existe, assim como o sistema
    Marcio> do Mainframe não tem mais manutenção ou qualquer outra forma de
    Marcio> acesso, não sendo possível mexer nele e ninguém tem acesso ao OS, só a
    Marcio> empresa que faz a manutenção. Assim há um trabalho muito complicado de
    Marcio> ficar atualizando informações entre os dois sistemas, e está gerando
    Marcio> muitos erros. Está fora de cogitação qualquer ideia que tenha que
    Marcio> mexer no Mainframe.

    Marcio> A ideia é que o script Perl navegue pelas telas lendo e inserindo
    Marcio> dados, assim como os scripts em Excel fazem usando a automação OLE do
    Marcio> Extra!.

Que beleza... :)

    Marcio> Pelo que entendi esse s3270 é para isso mesmo, o duro é só saber como usar.

    Marcio> Em tempo, o que estou fazendo é para facilitar a integração do sistema
    Marcio> de telefonia com o CRM.

Olha, na real você só está adiando a dor e o custo de trocar o TN3270
por algo mais manutenível. Isso vai quebrar teu galho agora, mas em
algum momento no futuro, vai aparecer um requisito muito simples mas que
vai ficar caro de implementar por conta da complexidade adicional de
interagir com o sistema dessa forma. Isso sem nem levar em consideração
os bugs que podem existir dentro do s3270 e do Expect, que podem e vão
explodir na hora que você menos esperar e vai ser muito difícil de
consertar. Eu sugiro que você implemente isso como medida provisória e
planeje uma migração definitiva, se puder. Se não puder, meus pêsames,
você está montado numa bomba-relógio sem ponteiros.

-- 
Eden Cardim
+55 11 9644 8225
Insolide TI Ltda.
http://insoli.de


More information about the SaoPaulo-pm mailing list