; WEDGEF64
;
; Program for Proteus
;
; (C) 2004 Simone Zanella Productions
;
; Emulates keyboard by entering data coming from a serial device using Datalogic® wedge protocol
; (used by F64/F67 keyboard emulators).
; This program can be installed as a script for Proteus Service.
;
; Communication parameters can be found at the very beginning of the program; the meaning is as follow:
; - COMPORT = communication port ("COM1", "COM2", etc.)
; - COMSPEED = communication speed (1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200 baud)
; - COMPARITY = "N" (none), "E" (even), "O" (odd), "M" (mark), "S" (space)
; - COMDATA = 7 o 8 (data bits)
; - COMSTOP = 1 o 2 (stop bits)
; - COMFLOW = "R" (RTS/CTS or hardware), "X" (XON/XOFF or software), "E" (both), "N" (none)
; - POSTFIX = terminator (usually: "{ENTER}")
;
; It is also possible to specify:
; - PACE: Delay to wait after sending each line in keyboard emulation (in seconds);
; - POLLINTERVAL: number of seconds after which the program polls the terminal (default 0.64);
; - TIMEOUT: timeout (ms) for receiving a packet from the terminal.
;
; You should modify the function EmulateKeyboard to select which information should be sent.
;
; The lines below (commented out) allow for alternate behaviours:
; - bring to the foreground a specific window (Ultra-Edit) and emulate keyboard;
; - open notepad (if it is not running), emulate keyboard and return to previous window.
#!proteus -z -j
!include "win32.prt"
; Communication parameters
; ------------------------
; Serial port
COMPORT = "COM1"
; Baud rate
COMSPEED = 9600
; Parity (None, Odd, Even, Mark, Space: only first letter)
COMPARITY = "E"
; Data bits
COMDATA = 7
; Stop bits
COMSTOP = 1
; Flowcontrol (Rts/cts, Xon/xoff, E [both], None: only uppercase letter)
COMFLOW = "N"
; Delay to wait after sending each line in keyboard emulation (in seconds)
PACE = 0.02
; Poll interval
POLLINTERVAL = 0.64
; Timeout (ms) for receiving a packet from the terminal
TIMEOUT = 500
; Terminator (default = "{ENTER}")
POSTFIX = "{ENTER}"
; ---------------------------
HCOM = OpenCom(COMPORT, COMSPEED, COMPARITY, COMDATA, COMSTOP, COMFLOW)
ACK = CHR(6)
NAK = CHR(21)
NULL = CHR(0)
ESC = CHR(0x1B)
CR = CHR(0x0D)
LF = CHR(0x0A)
ETB = CHR(0x17)
EOT = CHR(0x04)
PACKETENQ = MKPacket(NULL ESC "5$" ESC CR)
WHILE 1
; Wait the specified interval
SLEEP(POLLINTERVAL)
; If it is not a service and the user pressed ESC, exit
!ifndef SERVICE
IF KBDHIT()
IF EQ(GETCH(), 27)
BREAK
FI
FI
!endif
; Send ENQ
W32WRITEFILE(HCOM, PACKETENQ)
NCHAR = W32WAITRXCHAR(HCOM, TIMEOUT)
IF NCHAR
; Characters received
RXWedgeProtocol()
FI
LOOP
W32CLOSEHANDLE(HCOM)
ABORT 0
FUNCTION RXWedgeProtocol()
lastblock = 0
rxabort = 0
nakcount = 0
_Q = QUEUENEW()
lastpacketsent = _PACKETENQ
status = 0
WHILE 1
s = W32READCOM(_HCOM, 0)
WHILE NOT(STRSTR(s, _LF))
n = W32WAITRXCHAR(_HCOM, _TIMEOUT)
IF n
s = s W32READCOM(_HCOM, 0)
nakcount = 0
ELSE
; In case of timeout, send NAK and retry 2 times
INC(@nakcount)
IF LT(nakcount, 2)
s = ""
W32READCOM(_HCOM, 0)
W32WRITEFILE(_HCOM, lastpacketsent)
CONTINUE
ELSE
; Too many NAK: abort (discard all records received)
rxabort = 1
FI
BREAK
FI
LOOP
IF rxabort
BREAK
FI
p = STRSTR(s, _LF)
s = LEFT(s, p)
record = ""
result = CheckFrame(s, @record, @lastblock)
W32READCOM(_HCOM, 0)
IF result
SWITCH status
ON 0
; Check if it has received [xx]<RUN>
IF STREQ(RIGHT(record, 5), "<RUN>")
W32WRITEFILE(_HCOM, _ACK)
; Reset record pointer
lastpacketsent = MKPacket(_NULL _ESC "0#" _ESC _CR)
W32WRITEFILE(_HCOM, lastpacketsent)
INC(@status)
ELSE
; Record ok: add it
lastpacketsent = _NAK
W32WRITEFILE(_HCOM, _ACK)
status = 2
IF NOT(lastblock)
ENQUEUE(_Q, record)
FI
; W32WRITEFILE(_HCOM, _NAK)
FI
ON 1
IF STREQ(LEFT(record, 5), "<ACK>")
W32WRITEFILE(_HCOM, _ACK)
; Request first 100 records
lastpacketsent = MKPacket(_NULL _ESC "0#100" _ESC _CR)
W32WRITEFILE(_HCOM, lastpacketsent)
INC(@status)
ELSE
; Record ok: add it
lastpacketsent = _NAK
W32WRITEFILE(_HCOM, _ACK)
INC(@status)
IF NOT(lastblock)
ENQUEUE(_Q, record)
FI
; W32WRITEFILE(_HCOM, _NAK)
FI
ON 2
; Record ok: add it
lastpacketsent = _NAK
W32WRITEFILE(_HCOM, _ACK)
IF NOT(lastblock)
ENQUEUE(_Q, record)
FI
OFF
ELSE
; Record not valid
W32WRITEFILE(_HCOM, _NAK)
FI
IF lastblock
IF EQ(lastblock, 1)
; ETB received, wait for EOT
lastblock = 0
; Request another 100 records (if available)
lastpacketsent = MKPacket(_NULL _ESC "0#100" _ESC _CR)
W32WRITEFILE(_HCOM, lastpacketsent)
status = 2
ELSE
BREAK
FI
FI
LOOP
IF NOT(rxabort)
EmulateKeyboard(_Q)
FI
QUEUEFREE(_Q)
RETURN
FUNCTION MKPacket(s)
RETURN s CalcLRC(s) _LF
FUNCTION CheckFrame(s, record, lastblock)
; Start of frame is invalid?
IF STRNEQ(LEFT(s, 2), _NULL _NULL)
IF STRNEQ(LEFT(s, 1), _NULL)
RETURN 0
ELSE
s = _NULL s
FI
FI
; Verify End Of Block
IF STRNEQ(RIGHT(s, 1), _LF)
RETURN 0
FI
; Cut End Of Block
s = LEFT(s, -1)
; Isolate LRC
lrc = RIGHT(s, 1)
; Cut LRC
s = LEFT(s, -1)
IF STRNEQ(RIGHT(s, 1), _CR)
RETURN 0
FI
; Verify LRC
IF NOT(VerifyLRC(lrc, s))
RETURN 0
FI
; Cut initial NULLs
s = RESTFROM(s, 3)
; Cut ending CR
s = LEFT(s, -1)
; Check if lastblock and update parameter
SWITCH LEFT(s, 1) STREQ
ON _EOT
lastblock = 1
ON _ETB
lastblock = 2
OTHER
lastblock = 0
OFF
; Store record
record = s
RETURN 1
FUNCTION CalcLRC(s)
acc = 0
l = STRLEN(s)
; XOR all characters in s
FOR x = 1 to l
NXOR(@acc, ASC(SUBSTR(s, x, 1)))
NEXT
RETURN CHR(acc)
FUNCTION VerifyLRC(lrc, s)
RETURN STREQ(lrc, CalcLRC(s))
FUNCTION EmulateKeyboard(q)
; Alternate version which saves/restores current window and selects Notepad before wedging data
; ; Saves current window
; hOld = W32GETFOCUS()
;
; ; Look for Notepad
; hNotepad = W32FINDWINDOW("*Notepad*")
; IF EQ(hNotepad, 0)
; ; Not found - open it
; W32SHELL("NOTEPAD.EXE")
; ; Wait 1 second for it to become available
; SLEEP(1)
; ; Look up again its window
; hNotepad = W32FINDWINDOW("*Notepad*")
; FI
; ; If found, select its window and wedge data
; IF EQ(hNotepad, 0)
; W32SETFOCUS(hOld)
; RETURN
; FI
; W32SETFOCUS(hNotepad)
WHILE QUEUELEN(q)
W32SENDKEYS(KTrans(DEQUEUE(q)) _POSTFIX)
SLEEP(_PACE)
LOOP
; Alternate version - restore old window
; W32SETFOCUS(hOld)
RETURN
FUNCTION KTrans(s)
l = STRLEN(s)
r = ""
FOR x = 1 TO l
c = SUBSTR(s, x, 1)
; Special characters: characters which are not available on the keyboard
; require special treatment (Alt + 3 digits) - useful for foreign keyboards
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)
; Open serial port with specified parameters
hcom = W32CREATEFILE(comport, NOR(_W32_GENERIC_WRITE, _W32_GENERIC_READ), 0, \
_W32_OPEN_EXISTING, 0)
IF EQ(hcom, -1)
!ifndef SERVICE
CONSOLELN "Port " comport " could not be opened."
!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 "Error configuring port (" 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