PDA

Visualizza la versione completa : [JAVA] Comunicazione con registratori di cassa


eumene
25-10-2005, 10:24
Ciao a tutti.

Cari ragazzi, ho davvero bisogno di un aiuto.

Devo realizzare la comunicazione tra il mio gestionale e una cassa.

La comunicazione avviene attraverso un cavo che si collega al pc su porta 9 pin ed alla cassa con RJ45 (non so perchè)

Il produttore non fornisce librerie che mi possano permettere di avviare già la comunicazione e limitarmi ad utilizzare i comandi.

C'è qualcuno che può darmi una mano?

Oppure c'è qualcuno che può indicarmi la strada per poter creare connessioni su porte a 9 pin?

Vi ringrazio tantissimo

eumene
25-10-2005, 17:20
Ce l'ho fatta! Ci sono riuscito!

Ho anche realizzato una buona architettura che mi mantiene una buona indipendeza dalla specifica cassa da utilizzare!!

:unz: :unz: :unz:

Abbiate pazienza, me la canto e me la suono da solo, ma ho buttato un po' il sangue sulla questione.

alka
25-10-2005, 18:10
Se hai qualcosa da condividere con chi si troverà nelle tue stesse condizioni per aiutarlo, scrivi pure... :zizi:

eumene
26-10-2005, 01:12
Molto bene. Proverò.

Il mio problema era di riuscire a comunicare ad un registratore di cassa dati da un gestionale in modo da fargli stampare gli scontrini.

I problemi quindi si traducono in:
[list=1]
Creare un sistema di comunicazione su porte seriali RS 232
Sfruttare tale tecnologia per la comunicazione con una cassa
[/list=1]

Il punto 1 si risolve abbastanza facilmente grazie alle librerie SUN javax.comm, che potrete scaricare direttamente da java.sun.com (http://java.sun.com/products/javacomm/downloads/index.html), come già postato sul forum (e qui ifatti che ho trovato il link)

Ho utilizzato la versione 2 (3 per Windows non c'è :bhò: ).
Leggete bene le istruzioni per l'installazione della libreria comm.jar e non dimenticate di aggiungere win32com.dll al vostro JDK (purtoppo occorrono librerie native, quindi non è proprio multipiattaforma).

Troverete anche alcuni esempi su come iniziare a leggere (SimpleRead.java) e/o scrivere (SimpleWrite.java) attraverso porte seriali.

Imparato ciò, proviamo ora a generare una struttura che ci permetta di scrivere driver in Java per differenti tipologie di casse, partendo dal mio caso specifico

eumene
26-10-2005, 01:33
Per creare driver per differenti registratori di cassa ho realizzato tale architettura.

Innanzi tutto ho realizzato un'interfaccia che definisce i metodi di comando base da inviare ad una cassa, come segue:



/**
*

Title: RegistratoreCassaBridge</p>
*

Description: Interfaccia Driver per comunicazioni con Registratori di Cassa</p>
*

Copyright: Copyright (c) 2005</p>
*

Company: </p>
*
* @author Diego Purpo
* @version 1.0
*/
public interface RegistratoreCassaBridge {

/**
* Pagamento per contanti
*/
public static int CONTANTI = 1;

/**
* Pagamento per assegno
*/
public static int ASSEGNO = 2;

/**
* Pagamento per credito
*/
public static int CREDITO = 3;

/**
* Pagamento per carta di credito
*/
public static int CARTA_CREDITO = 4;

/**
* Pagamento per bancomat
*/
public static int BANCOMAT = 5;

/**
* Volore reparto 1
*/
public static int REP_1 = 1;

/**
* Volore reparto 2
*/
public static int REP_2 = 2;

/**
* Volore reparto 3
*/
public static int REP_3 = 3;

/**
* Volore reparto 4
*/
public static int REP_4 = 4;

/**
* Volore reparto 5
*/
public static int REP_5 = 5;

/**
* Volore reparto 6
*/
public static int REP_6 = 6;

/**
* Volore reparto 7
*/
public static int REP_7 = 7;

/**
* Volore reparto 8
*/
public static int REP_8 = 8;

/**
* Volore reparto 9
*/
public static int REP_9 = 9;

/**
* Massima lunghezza per la descrizione.
* Il valore si distingue per ogni registratore
*/
public int MAX_LENGTH = 255;

/**
* Chiudo comunicazione
* @throws RegistratoreCassaException nel caso di porta non trovata
*/
public void close() throws RegistratoreCassaException;

/**
* Apro connessione con la cassa
* @throws RegistratoreCassaException nel caso di porta non trovata
*/
public void open() throws RegistratoreCassaException;

/**
* Aggiunge una voce allo scontrino. Sarà sommato al totale
* @param quantita int numero elementi acquistati per tipo
* @param valore double valore da associare alla battuta
* @param sconto double percentuale di sconto sulla battuta
* @param descrizione String descrizione della mattuta. Massima lunghezza MAX_LENGTH. Le restanti lettere saranno omesse
* @param reparto int reparto della battuta
* @throws RegistratoreCassaException
*/
public void battuta(int quantita, double valore, double sconto, String descrizione, int reparto) throws RegistratoreCassaException;

/**
* Attribuisce lo scoto al totale dello scontrino fino a quel momento raggiunto.
* @param sconto double
* @throws RegistratoreCassaException
*/
public void sconto(double sconto) throws RegistratoreCassaException;

/**
* Chiude il conto e stampa tutto lo scontrino
* @param metodoPagamento int metodo di pagamento utilizzato
* @param descrizione String descrizione pagamento
* @throws RegistratoreCassaException
*/
public void chiudiConto(int metodoPagamento, String descrizione) throws RegistratoreCassaException;

/**
* Richiede apertura del cassetto della cassa
* @throws RegistratoreCassaException
*/
public void apriCassetto() throws RegistratoreCassaException;

/**
* Richiede subtotale.
* @throws RegistratoreCassaException
*/
public void subtotale() throws RegistratoreCassaException;

}



E' chiaro che non sono tutti i comandi eseguibili da una cassa, ma sono probabilmente i necessari.
Ho aggiunto anche alcune costanti per definire elementi tipici dei registratori, ovvero i tasti metodo di pagamento (Contanti, Assegno, ...) e i tasti reparto (REP_X) che sono i tasti da schiacciare per fare le somme. Non so se avete mai avuto a che fare con un registratore, ma ogni battuta prevede l'associazione della voce acquistata ad un reparto (a scopi statistici o per aliquote iva differenti)

Ho anche previsto i metodi open e close per avviare la comunicazione (e diventare proprietario del canale) e chiudere la comunicazione (e rilasciare il casale)

A questa interfaccia ho anche aggiunto una semplicissima classe di eccezione solo allo scopo di uniformare il tutto.



/**
*

Title: RegistratoreCassaException</p>
*

Description: Eccezione generata da errore nella comunicazione con la cassa</p>
*

Copyright: Copyright (c) 2005</p>
*

Company: </p>
*
* @author Diego Purpo
* @version 1.0
*/
public class RegistratoreCassaException extends Exception {

/**
* Costruttore
*/
public RegistratoreCassaException() {
super();
}

/**
* Costruttore
* @param s String messaggio
*/
public RegistratoreCassaException(String s) {
super(s);
}
}



A questo punto non resta che realizzare il driver specifico per la mia cassa, che estenda ovviamente RegistratoreCassaBridge.

Posterò di seguito la mia classe realizzata per casse di cui non diremo marca e modello.

Ricordate che il produttore del registratore dovrà fornirvi le specifiche del protocollo di comunicazione per poter dialogare con la cassa.

Vi invito quindi a soffermare l'attenzione più sui metodi open e close, che utilizzano massicciamente la libreria javax.com.

Per i metodi di invio comando sottolineo l'importanza della chiamata a write che permette l'invio di un array di byte alla cassa, il resto delle funzioni serve principalmente a formattare il messaggio secondo il protocollo previsto.

Inoltre tengo a precisare l'importanza della funzione delay() prima di ogni invio segnale.
Prevedete sempre un ritardo, non si sa mai: essendo porte seriali, a seconda della lunghezza del messaggio i tempi di risposta potrebbero essere più lunghi e l'accavallarsi di richieste potrebbero generare errori.

eumene
26-10-2005, 01:36
Ed ecco il codice del mio driver:


import java.io.IOException;
import java.io.OutputStream;
import java.text.DecimalFormat;

import javax.comm.*;

/**
*

Title: ???Bridge</p>
*

Description: Bridge per registratore di cassa ???</p>
*
* @author Diego Purpo
* @version 1.0
*/
public class ???Bridge implements RegistratoreCassaBridge {

/**
* Formattatore
*/
private DecimalFormat df = null;

/**
* Porta seriale di comunicazione
*/
private SerialPort serialPort;

/**
* Identificativo porta seriale
*/
private String portName = "";

/**
* Identificativo owner
*/
private String owner = "";

/**
* Timeout
*/
private int timeout = 0;

/**
* Delay tra operazioni
*/
private int delay = 0;

/**
* Canale di comunicazione verso la cassa
*/
private OutputStream outputStream;

/**
* Costante Start Transaction
*/
private static final char STX = (char) 2;

/**
* Costante End Transaction
*/
private static final char ETX = (char) 3;

/**
* Costante Meno
*/
private static final char MENO = 'A';

/**
* Costante Moltiplicatore
*/
private static final char MOLTIPLICATORE = 'x';

/**
* Costante Percentuale
*/
private static final char PERCENTUALE = 'B';

/**
* Costante Rep_1 char
*/
private static final char REP1 = 'X';

/**
* Costante Rep_2 char
*/
private static final char REP2 = 'T';

/**
* Costante Rep_3 char
*/
private static final char REP3 = 'P';

/**
* Costante Rep_4 char
*/
private static final char REP4 = 'L';

/**
* Costante Rep_5 char
*/
private static final char REP5 = 'H';

/**
* Costante Rep_6 char
*/
private static final char REP6 = 'Y';

/**
* Costante Rep_7 char
*/
private static final char REP7 = 'U';

/**
* Costante Rep_8 char
*/
private static final char REP8 = 'Q';

/**
* Costante contanti char
*/
private static final char CONTANTI_CHAR = '=';

/**
* Costante assegno char
*/
private static final char ASSEGNO_CHAR = 'S';

/**
* Costante carta di credito/bancomat char
*/
private static final char CARTA_CHAR = 'K';

/**
* Costante credito char
*/
private static final char CREDITO_CHAR = 'O';

/**
* Costante Delimitatore descrizione
*/
private static final char TAG_DESCRIZIONE = '"';

/**
* Costante Subtotale
*/
private static final char SUBTOTALE = 'W';

/**
* Costante Apri cassetto
*/
private static final char APRI_CASSETTO = 'C';

/**
* Massima lunghezza per la descrizione.
*/
public int MAX_LENGTH = 14;

/**
* Costruttore
*/
public ???Bridge() {

// Formattatore valori decimali
df = new DecimalFormat("###0.00");

// Configuro sistema
owner = "???Bridge";
portName = "COM1";
timeout = "20000";
delay = "500";
}

/**
* Chiudo comunicazione
* @throws RegistratoreCassaException nel caso di porta non trovata
*/
public void close() throws RegistratoreCassaException {

// Cerco di chiudere comunicazione
this.serialPort.close();
this.serialPort = null;
}

/**
* Apro connessione con la cassa
* @throws RegistratoreCassaException
*/
public void open() throws RegistratoreCassaException {

CommPortIdentifier portId = null;

// Cerco porta seriale identificata con chiave del configuratore
try {
portId = CommPortIdentifier.getPortIdentifier(portName);

} catch(NoSuchPortException nspe) {
throw new RegistratoreCassaException(nspe.getMessage());
}

// Verifico che sia una porta seriale. Specifica della Winner
if(portId.getPortType() != CommPortIdentifier.PORT_SERIAL) {
throw new RegistratoreCassaException("No serial port found");
}

// Apro la connessione bloccando la porta e
// rendendomi proprietario
try {
serialPort = (SerialPort) portId.open(owner, timeout);

} catch(PortInUseException piue) {
throw new RegistratoreCassaException(piue.getMessage());
}

// Apro canale di comunicazione in uscita
try {
outputStream = serialPort.getOutputStream();
} catch(IOException ioe) {
throw new RegistratoreCassaException(ioe.getMessage());
}

// Setto parametri base per la comunicazione
try {
serialPort.notifyOnDataAvailable(true);
serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_2, SerialPort.PARITY_NONE);
} catch(UnsupportedCommOperationException ucoe) {
throw new RegistratoreCassaException(ucoe.getMessage());
}

}

/**
* Aggiunge una voce allo scontrino. Sarà sommato al totale
* @param quantita int numero elementi acquistati per tipo
* @param valore double valore da associare alla battuta
* @param sconto double percentuale di sconto sulla battuta
* @param descrizione String descrizione della mattuta. Massima lunghezza MAX_LENGTH. Le restanti lettere saranno omesse
* @param reparto int reparto della battuta
* @throws RegistratoreCassaException
*/
public void battuta(int quantita, double valore, double sconto, String descrizione, int reparto) throws RegistratoreCassaException {

// Definizione array messaggio battuta e sconto associato
byte[] battutaMessage = null;
byte[] scontoMessage = null;

// Preparo elementi per battuta e sconto.
String battutaMessageString = "";
String scontoMessageString = "";

String quantitaString = "";
String valoreString = "";
String scontoString = "";
String repartoString = "";

// Se valore della battuta non valido genero eccezione
if(valore <= 0 || quantita <= 0) {
throw new RegistratoreCassaException("Illegal value for parameter");
}

// Quantità e valore ben definito. Formatto
quantitaString = Integer.toString(quantita) + MOLTIPLICATORE;
valoreString = df.format(valore).replaceAll("\\.", "").replaceAll(",", "");

// Se sconto richiesto valorizzo stringa sconto
if(sconto > 0.0) {
scontoString = MENO + df.format(sconto).replaceAll(",", ".") + PERCENTUALE;
}

// Se descrizione richiesta
if(descrizione.length() > 0) {

// Porto descrizione a lunghezza accettata
if(descrizione.length() > MAX_LENGTH) {
descrizione = descrizione.substring(0, MAX_LENGTH);
}

// Formatto descrizione
descrizione = TAG_DESCRIZIONE + descrizione + TAG_DESCRIZIONE;
}

// Definisco il reparto selezionato
repartoString += getRepartoChar(reparto);

// Attendo tempo per invio nuovi dati
delay();

// Eseguo battuta
battutaMessageString = STX + quantitaString + valoreString + descrizione + repartoString + ETX;
battutaMessage = battutaMessageString.getBytes();
try {
outputStream.write(battutaMessage);
} catch(IOException ioe) {
throw new RegistratoreCassaException(ioe.getMessage());
}

// Eseguo sconto
if(scontoString.length() > 0) {

// Attendo tempo per invio nuovi dati
delay();

scontoMessageString = STX + scontoString + ETX;
scontoMessage = scontoMessageString.getBytes();
try {
outputStream.write(scontoMessage);
} catch(Exception e) {
throw new RegistratoreCassaException(e.getMessage());
}
}
}

/**
* Attribuisce lo scoto al totale dello scontrino fino a quel momento raggiunto.
* @param sconto double
* @throws RegistratoreCassaException
*/
public void sconto(double sconto) throws RegistratoreCassaException {

// Definizione array messaggio battuta e sconto associato
byte[] scontoMessage = null;

// Preparo elementi per battuta e sconto.
String scontoMessageString = "";

String scontoString = "";

// Se sconto non è ben definito, lancoio eccezione
if(sconto < 0.0) {
throw new RegistratoreCassaException("Illegal value for parameter");
}

// Sconto ben definito. Formatto richiesta
scontoString = MENO + df.format(sconto).replaceAll(",", ".") + PERCENTUALE;

// Eseguo sconto
if(scontoString.length() > 0) {

// Invoco subtotale
subtotale();

// Attendo tempo per invio nuovi dati
delay();

// Eseguo sconto su valore attuale del conto
scontoMessageString = STX + scontoString + ETX;
scontoMessage = scontoMessageString.getBytes();
try {
outputStream.write(scontoMessage);
} catch(Exception e) {
throw new RegistratoreCassaException(e.getMessage());
}
}
}

/**
* Chiude il conto e stampa tutto lo scontrino
* @param metodoPagamento int metodo di pagamento utilizzato
* @param descrizione String descrizione pagamento
* @throws RegistratoreCassaException
*/
public void chiudiConto(int metodoPagamento, String descrizione) throws RegistratoreCassaException {

// Definizione array messaggio chiudi conto
byte[] chiudiContoMessage = null;

// Preparo elementi per chiudi conto
String chiudiContoString = "";
String metodoPagamentoString = "";

// Se descrizione richiesta
if(descrizione.length() > 0) {

// Porto descrizione a lunghezza accettata
if(descrizione.length() > MAX_LENGTH) {
descrizione = descrizione.substring(0, MAX_LENGTH);
}

// Formatto descrizione
descrizione = TAG_DESCRIZIONE + descrizione + TAG_DESCRIZIONE;
}

// Definisco il metodo di pagamento selezionato
metodoPagamentoString += getPagamentoChar(metodoPagamento);

// Attendo tempo per invio nuovi dati
delay();

// Eseguo chiusura conto
chiudiContoString = STX + descrizione + metodoPagamentoString + ETX;
chiudiContoMessage = chiudiContoString.getBytes();
try {
outputStream.write(chiudiContoMessage);
} catch(IOException ioe) {
throw new RegistratoreCassaException(ioe.getMessage());
}
}

/**
* Richiede apertura del cassetto della cassa
* @throws RegistratoreCassaException
*/
public void apriCassetto() throws RegistratoreCassaException {

// Preparo comando apri cassetto
byte[] apriCassetto = {STX, APRI_CASSETTO, ETX};

// Attendo tempo per invio nuovi dati
delay();

// Invio comando apri cassetto
try {
outputStream.write(apriCassetto);
} catch(IOException ioe) {
throw new RegistratoreCassaException(ioe.getMessage());
}
}

/**
* Richiede subtotale.
* @throws RegistratoreCassaException
*/
public void subtotale() throws RegistratoreCassaException {

// Preparo comando subtotale
byte[] subtotale = {STX, SUBTOTALE, ETX};

// Attendo tempo per invio nuovi dati
delay();

// Invio comando subtotale
try {
outputStream.write(subtotale);
} catch(IOException ioe) {
throw new RegistratoreCassaException(ioe.getMessage());
}
}

/**
* Case su intero in input per determinare quale terminatore vi è associato.
* @param reparto int
* @return char carattere associato al reparto se input è comopreso tra 1 e 8. Altrimenti reparto 1 default
* @throws RegistratoreCassaException
*/
private char getRepartoChar(int reparto) {

// Eseguo case
switch(reparto) {
case REP_2:
return REP2;
case REP_3:
return REP3;
case REP_4:
return REP4;
case REP_5:
return REP5;
case REP_6:
return REP6;
case REP_7:
return REP7;
case REP_8:
return REP8;
default:
return REP1;
}
}

/**
* Case su intero in input per determinare quale terminatore vi è associato.
* @param pagamento int
* @return char carattere associato al pagamento. Default contanti
* @throws RegistratoreCassaException
*/
private char getPagamentoChar(int pagamento) {

// Eseguo case
switch(pagamento) {
case ASSEGNO:
return ASSEGNO_CHAR;
case BANCOMAT:
return CARTA_CHAR;
case CARTA_CREDITO:
return CARTA_CHAR;
case CONTANTI:
return CONTANTI_CHAR;
case CREDITO:
return CREDITO_CHAR;
default:
return CONTANTI_CHAR;
}
}

/**
* Pausa per permettere invio dati corretto
* @throws RegistratoreCassaException
*/
public void delay() throws RegistratoreCassaException {
try {
Thread.sleep(delay);
} catch(Exception e) {
throw new RegistratoreCassaException(e.getMessage());
}
}

public static void main(String[] argv) {

try {

// Apro collegamento con cassa
RegistratoreCassaBridge rcb = new ???Bridge();
rcb.open();

// 10 pezzi di sapone a 1 euro
rcb.battuta(10, 1.00, 0, "sapone", rcb.REP_2);

// 5 pezzi di supercalifragilistichespiralidoso a 2 euro, con sconto speciale del 10%
rcb.battuta(5, 2.00, 10, "supercalifragilistichespiralidoso", rcb.REP_4);

// sconto fedeltà del 5%
rcb.sconto(5);

// chiudi conto e paga con carta
rcb.chiudiConto(rcb.CARTA_CREDITO, "carta credito");

// apri il cassetto
rcb.apriCassetto();

// chiudo connessione alla cassa
rcb.close();

} catch(Exception e) {
e.printStackTrace();
}

}

}

eumene
26-10-2005, 01:40
Ho allegato alla classe ???Bridge anche un main come esempio di utilizzo.

Volevo solo aggiungere una nota circa il costruttore.

Il codice



// Configuro sistema
owner = "???Bridge";
portName = "COM1";
timeout = 20000;
delay = 500;


è chiaramente un esempio. Nella realtà è previsto un file di configurazione per questi 4 parametri

Chiedo scusa eventualmente per la lunghezza del post.

Spero di potervi essere stato utile

Ciao a tutti.

vince821
07-11-2005, 09:36
ti volevo chiedere se da java.sun.com mi devo scaricare la versione:

Download Version 2.0 for Microsoft Windows and Solaris/x86

grazie :ciauz:

vince821
07-11-2005, 11:33
ti volevo anche chiedere se la win32com.dll è presente nel pacchetto che mi devo scaricare

ciao e grazie ancora

aspetto tua risposta

:ciauz:

eumene
07-11-2005, 11:58
Sì, devi scaricare le versione 2.0 per Win, in cui troverai, oltre alla win32com.dll, anche alcuni esempi di uso e qualche applicativo di testing già realizzato.

Ricordati che il tutto funziona solo se:
[list=1]
Copi la win32com.dll nella cartella <jre>/bin sul pc che deve utilizzare l'applicativo
Copi la javax.comm.properties nella cartella <jre>/lib sul pc che deve utilizzare l'applicativo
[/list=1]

Ciao :ciauz: :ciauz:

Loading