#33804
Javier Ader
Participante

Bueno, un poco lo mire. Hace solicite a hasar de las Clases Fiscales Multiplaforma (antes me habian mandado la versión en ActiveX) con la esperanza de que me mandaran las fuentes del spooler actual; pero no… Esta libreria C++ esta hecha para comunicarse directamente al controlador via el puerto serial o al spooler via tcp (la unica clase que tratan con TCP/IP son unas llamadas SocketTCP y SocketUDP; tiene metodos simples como enviarByte(), recibirByte pero no mucho más); basicamente tienen toda la lógica de genración de comandos “crudos” (orden de campos, chequeos de errores) que despues lo envian por RS232, TCP o UDP. Esto me hace pensar que el spooler no tienen mocho trabajo más que lo que viene por TCP/IP enviarlo a un puertdo serial y viceversa (ok, esto si puede ser bastante trabajo si se lo quiere hacer multiplataforma).
De cualquier manera, si no hay un spooler de epson (o si genera algun tipo de problemas de licencias ) me da la sensación que esta “mitad” del problema se podria resolver relativamente facil. Como no estoy como para programar en C o C++ y andar haciendo malabares con #endif y librerias que no conozco para poder acceder a los puertos seriales (alguien en algún momento lo va a tener que hacer) intente simplificar y ver si se podia desde Java. Encontre dos librerias, una de Sun que no esta en JDK (creo…) y que me parece que no anda bajo Windows (y tampoco mire mucho la licencia) y esta http://code.google.com/p/giovynetserialport/ , que anda bajo Windwos y Linux y esta bajo GPL. A partir de esta se podria hacer un spoolercito más o menos rápido (falta como manejar TCP/IP, pero eso en Java debe ser incluso más fácil; he visto servers webs de un solo archivo java!).
La otra parte es viene por el lado de manejar la idiosincracia de Epson y entrar en los detalles del protocolo y formato de los paquetes. En este sentido , por lo que vi, tiene buena documentación (y el protocolo y el formato general de los comandos parece bastante simples byteInicio + byteSequencia + byteComando + (byteLimitadorDeCampo + bytesDeValor)* + byteFin + 4byteCodigoBCC ) asi que lei un poco y juntando un poco todo lo anterior hice este programita

serial.Serial.java

Code:
package serial;

import app.Com;
import app.Parameters;

public class Serial {

/***
* Pequeño programa que muestra información sobre el controladores/impresores fiscales
* epson modelos TM-2000AF+,TM300AF+, TM-2000AF, TM-U950F,TM-285F,LX-300F
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception{

String strPort= “COM4”;
if (args.length> 0)
strPort = args[0];

Parameters param = new Parameters();
param.setPort(strPort);
param.setBaudRate(“9600″);
Com portCom = new Com(param);

//intervalo entre envio de caracteres
int intervalo = 50; //20 caracteres por segundo en envio, sera mucho?

char numSeq = ComandoEpson.NumSeqInicial;

char codComandoInfo = 0x2a; //codigo de comando de solicitud de informacion
//comando de informacion Normal
ComandoEpson cmdInfoN = ComandoEpson.crearComando(numSeq, codComandoInfo,”N”);
//comando de info sobre las capacidades del controlador
numSeq = ComandoEpson.proximoNumSeq(numSeq);
ComandoEpson cmdInfoP = ComandoEpson.crearComando(numSeq, codComandoInfo,”P”);
//comando de solicitud de info del contribuyente
numSeq = ComandoEpson.proximoNumSeq(numSeq);
ComandoEpson cmdInfoC = ComandoEpson.crearComando(numSeq, codComandoInfo,”C”);
//comando de info sobre los contadores internos de docs. fiscales y no fiscales
numSeq = ComandoEpson.proximoNumSeq(numSeq);
ComandoEpson cmdInfoA = ComandoEpson.crearComando(numSeq, codComandoInfo,”A”);
//comando de info sobre el documento que se esta emitiendo
numSeq = ComandoEpson.proximoNumSeq(numSeq);
ComandoEpson cmdInfoD = ComandoEpson.crearComando(numSeq, codComandoInfo,”D”);

ComandoEpson[] comandos = new ComandoEpson[] {
cmdInfoN, cmdInfoP,cmdInfoC,cmdInfoA, cmdInfoD };

for (int i = 0 ; i < comandos.length; i ++) { ComandoEpson cmd = comandos[i]; System.out.println("Enviando comando..."); portCom.sendString(cmd.getStringRaw(), intervalo); System.out.println("Leyendo respuesta..."); //se lee hasta encontrar el inicio de datos; FALTA VErificar que puede enviar //el controlador en respuestas... portCom.receiveToString(ComandoEpson.INICIODATOS, 10); //lee hasta encontrar el caracter de fin de datos String respSinBCC = portCom.receiveToString(ComandoEpson.FINDATOS, 10); respSinBCC = ComandoEpson.INICIODATOS + respSinBCC; //despues lee los ultimo 4 carateres (deberian ser hexa) que son el BCC String respBCC = portCom.receiveToString(4, 10); System.out.println("Respuesta:" + respSinBCC ); System.out.println("BCC Respuesta: " + respBCC ); char seqResp = respSinBCC.charAt(1); //chequa que el numeor de secuencia sea el esperado System.out.println(cmd.getNumSequencia() == seqResp ? "Secuencia OK!" :"ERORR: Fuera de secuencia"); //chequea el BCC de la respuesta String bccEsperado = ComandoEpson.calBCC(respSinBCC); //por alguna cuestion rara, la documentacion de epson muestra ejemplos //con BCC en minusculas; el emulador que tengo retonra en minuscla //asi que como fix hago el egual ignorando cases System.out.println(respBCC.equals(bccEsperado)?"BCC de Resp OK!" :"ERROR: BCC de Resp"); } portCom.close(); } }


serial.ComandoEpson.java

Code:
package serial;

import java.util.*;

public class ComandoEpson {

public static final char INICIODATOS = 0x02;
public static final char SEPARADORCAMPOS = 0x1c;
public static final char FINDATOS = 0x03;

public static final char NumSeqInicial = 0x20;
//los numeros de secuencia deben estar
//entre 0x20 y 0x7f inclusive
public static char proximoNumSeq(char i)
{
if (i >= 0x7f) //se vuelve al primero
return 0x20;
if (i < 0x20) //no deberia pasar... return 0x20; return 0x20 +1; } char numSeq; char cod; List valoresDeCampos;
boolean error = false;
String descError = “”;
String stringRaw;
public char getNumSequencia() { return numSeq;}
public char getCodigoComando() { return cod;}
public List
getValoresDeCampos(){ return valoresDeCampos;}
public boolean getTieneError(){ return error;}
public String getDescError() { return descError;}
public String getStringRaw() { return stringRaw;}

protected ComandoEpson(char cod, char numSeq, List valoresDeCampos ,
boolean error, String descError, String stringRaw)
{
this.cod = cod;
this.numSeq = numSeq;
this.valoresDeCampos = valoresDeCampos;
this.error = error;
this.descError = descError;
this.stringRaw = stringRaw;
}

public static ComandoEpson crearComando(char numSeq, char codComando,
String valCampo1)
{
List valoresDeCampos = new ArrayList();
valoresDeCampos.add(valCampo1);

return crearComando(numSeq,codComando,valoresDeCampos);
}

public static ComandoEpson crearComando(char numSeq, char codComando,
String valCampo1,String valCampo2)
{
List valoresDeCampos = new ArrayList();
valoresDeCampos.add(valCampo1);
valoresDeCampos.add(valCampo2);

return crearComando(numSeq,codComando,valoresDeCampos);
}
public static ComandoEpson crearComando(char numSeq, char codComando,
String valCampo1,String valCampo2, String valCampo3)
{
List valoresDeCampos = new ArrayList();
valoresDeCampos.add(valCampo1);
valoresDeCampos.add(valCampo2);
valoresDeCampos.add(valCampo3);

return crearComando(numSeq,codComando,valoresDeCampos);
}

public static ComandoEpson crearComando(char numSeq,
char codComando, List valoresDeCampos ){
validarNumSeq(numSeq);
validarCodComando(codComando);
//faltaria valirde la lista de los valores de los campos

//construye la forma “raw” del comado
StringBuilder strB = new StringBuilder();
//cabezera; 0x02, numero de secuencia, codigo de comando
strB.append(new char[]{INICIODATOS,numSeq,codComando});

for(String valCampo : valoresDeCampos )
{
strB.append(SEPARADORCAMPOS);
strB.append(valCampo);
}
strB.append(FINDATOS);
//ahora calcula el bcc y se lo agrega
String bcc = calBCC(strB.toString());
strB.append(bcc);
String strRaw = strB.toString();

//falta
boolean error = false;
String descError = “”;
ComandoEpson cmd = new ComandoEpson(codComando,numSeq,valoresDeCampos,error,
descError,strRaw);
return cmd;

}
private static void validarNumSeq(char num)
{
if (num > 0x7f || num <0x20)
throw new Error("Número de secuencia invalido");

}
private static void validarCodComando(char cod)
{
//FALTA
}
public static String calBCC (String s)
{
char[] chars = s.toCharArray();
int res = 0;
for(int i = 0; i< chars.length; i++) { res = res + chars[i]; } //che.. no confio si >> es portable….
// comvierte res a una string hexa
char d = (Integer.toHexString(res % 16)).charAt(0);
res = res / 16;
char c = (Integer.toHexString(res % 16)).charAt(0);
res = res / 16;
char b = (Integer.toHexString(res % 16)).charAt(0);
res = res / 16;
char a = (Integer.toHexString(res % 16)).charAt(0);
String bccStrHexa = new String (new char[]{a,b,c,d});

//la documentacion de epson NO DEJA EN claro (al menos en los ejemplos)
//si el BCC va en mayuscula o en minuscula… mi emulador retorna
//en mayuscula y parece aceptar cualquirea forma
return bccStrHexa.toUpperCase();
}

}

Arranco siendo un prueba de concepto, pero me termino gustando (mas que nada la clase ComandoEpson que un posible uso real con un par de mejoras).
Que hace: basicamente envia por el port serial que recibe como parametro (el nombre; bajo Windwos tomas COMx, bajo linux, no me fije, pero casi seguro algo de la forma /dev/algox ) el comando “Solicitud de Estado” (pagina 42 del manual MANPRG23.pdf que se puede bajar desde Epson) con sus 4 variantes:
N: Información normal o compatible con TM-300AF . Homologado por GR22 AFIP-DGI (voy a tener que buscar en diccionario “homologado”)
P: Información de las caracterizaticas de controlador
C: Información del contribuyente (CUIT, punto de venta, sit. iva, etc)
A: Información sobre los contadores de documentos fiscales y no fiscales
D: Información sobre el documento que se esta emitiendo
(OJO, lo que retorna el controlador no lo “parsea” simlemente lo manda a la consola, asi que si lo corren desde una terminal puede traer problemas ya que muchos datos no son “ascii”, muchos son binarios puros; redirección la salida a un arhcivo o corranlo dese eclipse que trae su propia cosola).

Bueno, más allá de si tiene utilidad por si mismo (puede servir para testear si un controlador fiscal real al menos esta respondiendo y ver cierta información importante) lo interesante es que lo probé contra un emulador de controladores fiscales (que de paso emula un puerto virtual; por eso en el codigo se ve el nombre “COM4” y no algo mas natural como COM1) lo recibia perfectamente y reconocia los comandos validos (hace un chequeo bastante correcto al parecer; en varias pruebas me detectaba por ej, comandos desconocidos de manera correcta, ed. retornando un error; y tambien chequeba el codigo de comprobación BCC) y retornaba al parecer, lo que tenia que retorna (al menos no le erraba en el numero de secuenca ni el BCC de la respuesta).

Pongo el último spoiler con los un ejemplo de salida (enviando los 4 comando que hablo mas arriba)

Code:
Enviando comando…
Leyendo respuesta…
Respuesta: *008006000091126101539123410001000EMULADOR FISCALTEXTO DE AUDITORIA
BCC Respuesta: 1101
Secuencia OK!
BCC de Resp OK!
Enviando comando…
Leyendo respuesta…
Respuesta:!*00800600080070050080085SSS2REMULADORFISCAL
BCC Respuesta: 0BCB
Secuencia OK!
BCC de Resp OK!
Enviando comando…
Leyendo respuesta…
Respuesta:!*00800600000000000010001C21001000000001000Razon Social
BCC Respuesta: 0DA9
Secuencia OK!
BCC de Resp OK!
Enviando comando…
Leyendo respuesta…
Respuesta:!*008006000000000000000000000000000000000000000000000000000000000
BCC Respuesta: 0D46
Secuencia OK!
BCC de Resp OK!
Enviando comando…
Leyendo respuesta…
Respuesta:!*00808620No hay 1 comprobante fiscal abierto
BCC Respuesta: 0EFE
Secuencia OK!
BCC de Resp OK!

(notese que hay caracters no ascii; es un copy and past crudo)

Bueno, un comando ya esta implementado… faltan los 99 restantes jajaj

P.D: el que lo quiera probar compilarlo y correrlo miren en la página de la librería http://www.giovynet.com/serialport.html que explica como hacerlo bajo eclipse como si el que lee fuera un nene de dos años (lo cual me sirvio muchisimo a ya que mi destreza bajo eclipse es casi nula jaja). Como emulador use el que esta en el post de más arriba; pero estaria bueno sacudirle a un controlador epson real (algún valiente?)