; ACKNAK32
;
; Programma per Proteus
;
; (C) 2004 Simone Zanella Productions
;
; Riceve dati da un terminale connesso via RS-232 che comunica con il protocollo Datalogic Ack-Nak PC32;
; i dati ricevuti (se la trasmissione ha avuto successo) sono successivamente introdotti in emulazione di
; tastiera - ogni dato è terminato da Enter (Invio).
; 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)
;
; 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;
; - TIMEOUT: timeout (in millisecondi) per la ricezione di un pacchetto dal terminale;
; - EVALFIRSTBLOCK: indica se il primo pacchetto debba essere valutato o scartato;
; - CHARDELAY: ritardo intercarattere per la trasmissione;
; - DTRDELAY: intervallo per l'abbassamento del segnale DTR;
; - NAKSTREAM: numero di NAK inviati per forzare un abort della trasmissione (out-of-sync).
;
; 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 configurazione
; ---------------------------
; Porta seriale
COMPORT = "COM1"
; Baud rate
COMSPEED = 9600
; Parità (None, Odd, Even, Mark, Space: solo iniziale)
COMPARITY = "N"
; Bit di dati
COMDATA = 8
; 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 = 0.5
; Timeout (in millisecondi) per la ricezione di un pacchetto dal terminale
TIMEOUT = 3000
; Valuta o meno il primo blocco inviato (di solito utilizzato per identificare il nome del file)
EVALFIRSTBLOCK = 1
; Ritardo intercarattere
CHARDELAY = 0.001
; Tempo di alzata DTR
DTRDELAY = 0.002
; Numero di NAK inviati per "scollegare" un terminale dopo un timeout
NAKSTREAM = 10
; ---------------------------
ACK = CHR(6)
NAK = CHR(21)
HCOM = OPENCOM(COMPORT, COMSPEED, COMPARITY, COMDATA, COMSTOP, COMFLOW)
WHILE 1
; Attende l'intervallo di poll
SLEEP(POLLINTERVAL)
R = W32READCOM(HCOM, 0)
IF STRLEN(R)
Q = MSRicevi232(R)
IF NEQ(Q, -1)
WHILE QUEUELEN(Q)
L = DEQUEUE(Q)
; Se si vuole selezionare una particola finestra, si può utilizzare
; un'espressione simile alla seguente:
; W32SETFOCUS(W32FINDWINDOW("*ULTRAEDIT-32*"))
W32SENDKEYS(KTrans(L) "{ENTER}")
SLEEP(PACE)
LOOP
QUEUEFREE(Q)
FI
; Variante con salvataggio/ripristino finestra corrente e selezione blocco notes
; IF NEQ(Q, -1)
; ; Preleva la finestra corrente
; HOLD = W32GETFOCUS()
;
; ; Cerca il blocco notes
; H = W32FINDWINDOW("*Blocco note*")
; IF EQ(H, 0)
; ; Non trovato - lo lancia
; W32SHELL("NOTEPAD.EXE")
; ; Attende un secondo che si renda disponibile
; SLEEP(1)
; ; Cerca la sua finestra nuovamente
; H = W32FINDWINDOW("*Blocco note*")
; FI
; ; Se trovato, seleziona la sua finestra ed invia i dati
; IF NEQ(H, 0)
; W32SETFOCUS(H)
; WHILE QUEUELEN(Q)
; L = DEQUEUE(Q)
; W32SENDKEYS(KTrans(L) "{ENTER}")
; SLEEP(PACE)
; LOOP
; ELSE
; ; Impossibile aprire il blocco notes - dati persi!
; FI
; W32SETFOCUS(HOLD)
; QUEUEFREE(Q)
; FI
FI
LOOP
W32CLOSEHANDLE(HCOM)
ABORT 0
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)
; Apertura seriale con parametri richiesti
hcom = W32CREATEFILE(comport, NOR(_W32_GENERIC_WRITE, _W32_GENERIC_READ), 0, \
_W32_OPEN_EXISTING, 0)
IF EQ(hcom, -1)
CONSOLELN "Impossibile aprire " comport "."
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
CONSOLELN "Errore nell'impostazione della porta (" W32GETLASTERROR() ")."
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
FUNCTION MSRicevi232(r)
q = QUEUENEW()
ct = 0
nacknum = 0
fine = "-0000FF" CHR(13)
firstblock = _EVALFIRSTBLOCK
; Riceve blocco
s = ""
; Riceve un pacchetto, con timeout.
WHILE 1
IF STRLEN(r)
s = s r
IF LT(STRLEN(s), ADD("0x" SUBSTR(s, 2, 2), 8))
; Blocco incompleto: attende la ricezione dei bytes residui
r = W32READCOM(_HCOM, 0)
CONTINUE
FI
s = left(s, ADD("0x" SUBSTR(s, 2, 2), 8))
; Pacchetto completo: lo controllo
IF STREQ(s, fine)
; File ricevuto completamente: invia ACK sulla linea
SendChar(_ACK)
RETURN q
ELSE
IF AckNackCheckPacket(s, ct)
W32PURGECOM(_HCOM, NOR(_W32_PURGE_TXCLEAR, _W32_PURGE_RXCLEAR))
; Controllo ok: invia ACK sulla linea
SendChar(_ACK)
IF NOT(firstblock)
s = SUBSTR(s, 5, SUB(STRLEN(s), 8))
ENQUEUE(q, s)
ELSE
; Ignora il primo blocco (nome del file?)
firstblock = 0
FI
; Resetta il numero di nack
nacknum = 0
s = ""
ELSE
; Verifica che non stia riprovando ad inviare
; il blocco precedente; in caso affermativo,
; lo ignora, perché è già stato salvato
; correttamente ed invia un ACK
ct2 = DEC(ct)
IF LT(ct2, 0)
ct2 = 9
FI
IF AckNackCheckPacket(s, ct2)
W32PURGECOM(_HCOM, NOR(_W32_PURGE_TXCLEAR, _W32_PURGE_RXCLEAR))
; Controllo ok: invia ACK sulla linea
SendChar(_ACK)
; Resetta il numero di nack ed il timeout
nacknum = 0
s = ""
ELSE
; Controllo fallito: invia NACK sulla linea
SendChar(_NAK)
W32PURGECOM(_HCOM, NOR(_W32_PURGE_TXCLEAR, _W32_PURGE_RXCLEAR))
INC(@nacknum)
IF GT(nacknum, 31)
SendChar(REPLICATE(_NAK, _NAKSTREAM))
; Troppi nack: ricezione fallita
QUEUEFREE(q)
RETURN -1
FI
s = ""
FI
FI
FI
FI
IF NOT(W32WAITRXCHAR(_HCOM, _TIMEOUT))
; "Timeout"
SendChar(REPLICATE(_NAK, _NAKSTREAM))
QUEUEFREE(q)
RETURN -1
FI
r = W32READCOM(_HCOM, 0)
LOOP
RETURN
FUNCTION AckNackCheckPacket(blocco, @contatore)
; Verifica se il pacchetto è corretto; in caso affermativo, incrementa il contatore
IF OR(STRNEQ(LEFT(blocco, 1), "-"), \
STRNEQ(RIGHT(blocco, 1), CHR(13)), \
NEQ(LEFT(RIGHT(blocco, 4), 1), contatore), \
STRNEQ(CheckSum(SUBSTR(blocco, 2, SUB(STRLEN(blocco), 4))), LEFT(RIGHT(blocco, 3), 2)))
RETURN 0
ELSE
INC(@contatore)
IF GT(contatore, 9)
contatore = 0
FI
FI
RETURN 1
FUNCTION CheckSum(dati)
; Calcola il check digit
chk = 0
dl = STRLEN(dati)
FOR x = 1 TO dl
ADD(@chk, ASC(SUBSTR(dati, x, 1)))
NEXT
MOD(@chk, 256)
RETURN PFORMAT("02X", chk)
FUNCTION SendChar(s)
; Abbassa il DTR
W32ESCCOMFUNC(_HCOM, _W32_COM_CLRDTR)
SLEEP(_CHARDELAY)
W32WRITEFILE(_HCOM, s)
; Attende che si sia svuotato il buffer di trasmissione
W32WAITTXOVER(_HCOM, 1000)
SLEEP(_DTRDELAY)
; Alza il DTR
W32ESCCOMFUNC(_HCOM, _W32_COM_SETDTR)
RETURN