; SPROTOCOL
;
; Programma per Proteus
;
; (C) 2004 Simone Zanella Productions
;
; Riceve dati inviati nel formato Datalogic® Special protocol da un dispositivo seriale e li
; inserisce in emulazione di tastiera.
; Questo programma può essere installato come script associato al servizio Proteus.
;
; I parametri di comunicazione si trovano all'inizio del programma e sono:
; - COMPORT = porta di comunicazione ("COM1", "COM2", ecc.)
; - COMSPEED = velocità di comunicazione (1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200)
; - COMPARITY = "N" (nessuna), "E" (pari), "O" (dispari), "M" (marca), "S" (spazio)
; - COMDATA = 7 o 8 (bit di dati)
; - COMSTOP = 1 o 2 (bit di stop)
; - COMFLOW = "R" (RTS/CTS detto anche hardware), "X" (XON/XOFF detto anche software), "E" (entrambi), "N" (nessuno)
; - POSTFIX = terminatore (di solito: "{ENTER}")
;
; E' poi possibile specificare:
; - PACE: ritardo dopo l'invio in emulazione di tastiera di ciascuna riga (in secondi);
; - POLLINTERVAL: periodicità (in secondi) con la quale il programma verifica la presenza di dati disponibili;
; - TXSETUP: flag - se non zero, la richiesta di trasmissione è inviata dallo script ed il terminale
; si aspetta un ACK dopo ogni record inviato;
; - TIMEOUT: timeout (in millisecondi) per la ricezione di un pacchetto dal terminale;
; - ASCII_STXO: codice ASCII del carattere Start of Block (per i frame seguenti al primo; impostare a 0 per saltarlo).
;
; Il codice commentato più sotto permette anche in alternativa di:
; - selezionare una specifica finestra (applicativo Ultra-Edit) ed inviare il dato verbatim, seguito da Invio;
; - lanciare il blocco note (se non aperto), introdurre il dato verbatim (seguito da Invio) e tornare alla finestra
; iniziale.
#!proteus -z -j
!include "win32.prt"
; Parametri di comunicazione
; --------------------------
; Porta seriale
COMPORT = "COM1"
; Baud rate
COMSPEED = 9600
; Parità (None, Odd, Even, Mark, Space: solo iniziale)
COMPARITY = "M"
; Bit di dati
COMDATA = 7
; Bit di stop
COMSTOP = 1
; Controllo di flusso (Rts/cts, Xon/xoff, Entrambi, Nessuno: solo iniziale)
COMFLOW = "N"
; Ritardo interlinea
PACE = 0.02
; Intervallo di polling
POLLINTERVAL = 1
; Richiesta di trasmissione dallo script
TXSETUP = 1
; Timeout (in millisecondi) per la ricezione di un pacchetto dal terminale
TIMEOUT = 1500
; Terminatore (default = "{ENTER}")
POSTFIX = "{ENTER}"
; Start of block (per i frame che seguono il primo; impostare a 0 per saltarlo)
ASCII_STXO = 0
; ---------------------------
HCOM = OpenCom(COMPORT, COMSPEED, COMPARITY, COMDATA, COMSTOP, COMFLOW)
STXO = CHR(ASCII_STXO)
; Start of block (primo frame)
ASCII_STXF = 2
STXF = CHR(ASCII_STXF)
; End of text
ASCII_ETX = 3
ETX = CHR(ASCII_ETX)
; Terminatore di record (sostituito da ETX nell'ultimo record)
ASCII_ETB = 23
ETB = CHR(ASCII_ETB)
; End of block
ASCII_EOB = 13
EOB = CHR(ASCII_EOB)
; Primo record:
; STX RECORD ETB LRC CR
;
; Record seguenti:
; [STX] RECORD ETB|ETX LRC CR
; (ETX sostituisce ETB nell'ultimo record)
ACK = CHR(6)
NAK = CHR(21)
WHILE 1
; Attende per l'intervallo specificato
SLEEP(POLLINTERVAL)
; Se non è un servizio e l'utente ha premuto ESC, esce
!ifndef SERVICE
IF KBDHIT()
IF EQ(GETCH(), 27)
BREAK
FI
FI
!endif
IF TXSETUP
; Invia ACK
W32WRITEFILE(HCOM, ACK)
FI
NCHAR = W32WAITRXCHAR(HCOM, TIMEOUT)
IF NCHAR
; Ricevuti dei caratteri
RXSpecialProtocol()
FI
LOOP
W32CLOSEHANDLE(HCOM)
ABORT 0
FUNCTION RXSpecialProtocol()
firstblock = 1
lastblock = 0
rxabort = 0
nakcount = 0
_Q = QUEUENEW()
rest = ""
WHILE 1
s = rest W32READCOM(_HCOM, 0)
WHILE NOT(STRSTR(s, _EOB))
n = W32WAITRXCHAR(_HCOM, _TIMEOUT)
IF n
s = s W32READCOM(_HCOM, 0)
ELSE
; In caso di timeout, invia NAK e riprova 2 volte
INC(@nakcount)
IF LT(nakcount, 2)
s = ""
IF _TXSETUP
W32WRITEFILE(_HCOM, _NAK)
FI
ELSE
rxabort = 1
FI
BREAK
FI
LOOP
IF rxabort
BREAK
FI
; Elimina l'EOB precedente (se necessario)
IF STREQ(LEFT(s, 1), _EOB)
s = RESTFROM(s, 2)
FI
rest = s
REPEAT
p = STRSTR(rest, _EOB)
IF p
s = LEFT(rest, p)
rest = RESTFROM(rest, INC(p))
ELSE
BREAK
FI
record = ""
result = CheckFrame(s, firstblock, @record, @lastblock)
IF result
IF _TXSETUP
W32WRITEFILE(_HCOM, _ACK)
FI
; Elabora il record
IF NOT(firstblock)
ENQUEUE(_Q, record)
ELSE
firstblock = 0
FI
IF lastblock
BREAK
FI
ELSE
IF _TXSETUP
W32WRITEFILE(_HCOM, _NAK)
FI
FI
UNTIL ISEMPTY(rest)
IF lastblock
BREAK
FI
LOOP
IF NOT(rxabort)
EmulateKeyboard(_Q)
FI
QUEUEFREE(_Q)
RETURN
FUNCTION CheckFrame(s, firstblock, record, lastblock)
; L'inizio del frame è valido?
IF firstblock
IF STRNEQ(LEFT(s, 1), _STXF)
RETURN 0
FI
ELSE
IF _ASCII_STXO
IF STRNEQ(LEFT(s, 1), _STXO)
RETURN 0
FI
FI
FI
; Verifica End of block
IF STRNEQ(RIGHT(s, 1), _EOB)
RETURN 0
FI
; Elimina End Of Block
s = LEFT(s, -1)
; Isola LRC
lrc = RIGHT(s, 2)
; Elimina LRC
s = LEFT(s, -2)
; Isola ETB
etb = RIGHT(s, 1)
; Il carattere che precede LRC deve essere ETB o ETX (ultimo blocco)
IF AND(STRNEQ(etb, _ETB), STRNEQ(etb, _ETX))
RETURN 0
FI
; Verifica LRC
IF NOT(VerifyLRC(lrc, s, firstblock))
RETURN 0
FI
; Controlla se è l'ultimo blocco ed aggiorna il relativo parametro
lastblock = STREQ(RIGHT(s, 1), _ETX)
; Memorizza il record
record = LEFT(s, -1)
IF _ASCII_STXO
record = RESTFROM(record, 2)
FI
RETURN 1
FUNCTION CalcLRC(s, firstblock)
acc = 0
l = STRLEN(s)
; XOR di tutti i caratteri in s
FOR x = 1 to l
NXOR(@acc, ASC(SUBSTR(s, x, 1)))
NEXT
; Imposta il bit più significativo
IF firstblock
NAND(@acc, 0x7F)
ELSE
IF MOD(l, 2)
NOR(@acc, 0x80)
ELSE
NAND(@acc, 0x7F)
FI
FI
; Calcola c1 e c2
c1 = CHR(ADD(NAND(SHIFTRT(acc, 4), 0xF), 0x40))
c2 = CHR(ADD(NAND(acc, 0xF), 0x40))
RETURN c1 c2
FUNCTION VerifyLRC(lrc, s, firstblock)
RETURN STREQ(lrc, CalcLRC(s, firstblock))
FUNCTION EmulateKeyboard(q)
; Variante con salvataggio/ripristino finestra corrente e selezione blocco notes
; ; Preleva la finestra corrente
; hOld = W32GETFOCUS()
;
; ; Cerca il blocco notes
; hNotepad = W32FINDWINDOW("*Blocco note*")
; IF EQ(hNotepad, 0)
; ; Non trovato - lo lancia
; W32SHELL("NOTEPAD.EXE")
; ; Attende un secondo che si renda disponibile
; SLEEP(1)
; ; Cerca la sua finestra nuovamente
; hNotepad = W32FINDWINDOW("*Notepad*")
; FI
; ; Se trovato, seleziona la sua finestra ed invia i dati
; IF EQ(hNotepad, 0)
; W32SETFOCUS(hOld)
; RETURN
; FI
; W32SETFOCUS(hNotepad)
WHILE QUEUELEN(q)
scode = DEQUEUE(q)
IF STREQ(LEFT(scode, 1), "^")
stime = SUBSTR(scode, 2, 4)
scode = RESTFROM(scode, 7)
ELSE
stime = ""
scode = RESTFROM(scode, 2)
FI
p = STRSTR(scode, "]")
IF EQ(p, 0)
sqt = "1"
ELSE
sqt = RESTFROM(scode, INC(p))
scode = LEFT(scode, DEC(p))
FI
sbartype = RIGHT(scode, 1)
scode = LEFT(scode, -1)
ProcessRecord(stime, scode, sbartype, sqt)
LOOP
; Variante - ripristina la vecchia finestra
; W32SETFOCUS(hOld)
RETURN
FUNCTION ProcessRecord(stime, scode, sbartype, sqt)
; Se si vuole selezionare una particola finestra, si può utilizzare
; un'espressione simile alla seguente:
; W32SETFOCUS(W32FINDWINDOW("*ULTRAEDIT-32*"))
W32SENDKEYS(KTrans(scode) _POSTFIX)
SLEEP(_PACE)
RETURN
FUNCTION KTrans(s)
l = STRLEN(s)
r = ""
FOR x = 1 TO l
c = SUBSTR(s, x, 1)
; Mappatura dei caratteri speciali: caratteri che non si trovano
; sulla tastiera potrebbero richiedere l'introduzione della sequenza
; con ALT + numero
SWITCH c STREQ
ON "~"
r = r "{ALT DOWN}{NUMPAD1}{NUMPAD2}{NUMPAD6}{ALT UP}"
ON "{"
r = r "{ALT DOWN}{NUMPAD1}{NUMPAD2}{NUMPAD3}{ALT UP}"
ON "}"
r = r "{ALT DOWN}{NUMPAD1}{NUMPAD2}{NUMPAD5}{ALT UP}"
ON "+", "^", "%", "(", ")", "[", "]"
r = r "{" c "}"
OTHER
r = r c
OFF
NEXT
RETURN r
FUNCTION OpenCom(comport, comspeed, comparity, comdata, comstop, comflow)
; Apre la porta seriale con i parametri specificati
hcom = W32CREATEFILE(comport, NOR(_W32_GENERIC_WRITE, _W32_GENERIC_READ), 0, \
_W32_OPEN_EXISTING, 0)
IF EQ(hcom, -1)
!ifndef SERVICE
CONSOLELN "Impossibile aprire " comport "."
!endif
ABORT 2
FI
compar = VECNEW(13)
v = W32GETCOMSTATE(hcom, compar)
VECSET(compar, 2, comspeed)
v = NOR(_W32_COM_BINARY, _W32_COM_PARITY_ON)
SWITCH LEFT(comflow, 1) STRIEQ
ON "R"
NOR(@v, _W32_COM_RTS_HANDSHAKE, _W32_COM_CTSFLOW_ON)
ON "X"
NOR(@v, _W32_COM_XONXOFF_OUT, _W32_COM_XONXOFF_IN, _W32_COM_XOFF_CONTINUE)
ON "E"
NOR(@v, _W32_COM_RTS_HANDSHAKE, _W32_COM_CTSFLOW_ON, \
_W32_COM_XONXOFF_OUT, _W32_COM_XONXOFF_IN, _W32_COM_XOFF_CONTINUE)
ON "N"
OFF
VECSET(compar, 3, v)
VECSET(compar, 7, comdata)
SWITCH LEFT(comparity, 1) STRIEQ
ON "N"
v = _W32_COM_PARITY_NONE
ON "E"
v = _W32_COM_PARITY_EVEN
ON "O"
v = _W32_COM_PARITY_ODD
ON "M"
v = _W32_COM_PARITY_MARK
ON "S"
v = _W32_COM_PARITY_SPACE
OFF
VECSET(compar, 8, v)
SWITCH comstop
ON 1
VECSET(compar, 9, 0)
ON 2
VECSET(compar, 9, 2)
OFF
v = W32SETCOMSTATE(hcom, compar)
VECFREE(compar)
IF v
!ifndef SERVICE
CONSOLELN "Errore nell'impostazione della porta (" W32GETLASTERROR() ")."
!endif
W32CLOSEHANDLE(hcom)
ABORT 3
FI
tout = VECNEW(5)
VECSET(tout, 1, 0)
VECSET(tout, 2, 0)
VECSET(tout, 3, 0)
VECSET(tout, 4, 0)
VECSET(tout, 5, 0)
W32SETCOMTIMEOUTS(hcom, tout)
VECFREE(tout)
RETURN hcom