; FORMULA
;
; Program for Proteus
;
; (C) 2004 Simone Zanella Productions
;
; Emulates keyboard (wedge emulation) by entering data coming from a serial device using Datalogic Sysnet protocol
; (Formula terminals).
; 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 possible to specify:
; - TERMADDR: address for polled terminal (1, by default);
; - PACE: Delay to wait after sending each line in keyboard emulation (in seconds);
; - POLLINTERVAL: number of seconds after which the program polls the terminal;
; - TIMEOUT: timeout (ms) for receiving a packet from the terminal.
;
; 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 = "M"
; Data bits
COMDATA = 7
; Stop bits
COMSTOP = 1
; Flowcontrol (Rts/cts, Xon/xoff, E [both], None: only uppercase letter)
COMFLOW = "N"
; Terminal address
TERMADDR = 1
; Delay to wait after sending each line in keyboard emulation (in seconds)
PACE = 0.02
; Poll interval
POLLINTERVAL = 1
; Timeout (ms) for receiving a package from the terminal
TIMEOUT = 1500
; Terminator (default = "{ENTER}")
POSTFIX = "{ENTER}"
; ---------------------------
HCOM = OpenCom(COMPORT, COMSPEED, COMPARITY, COMDATA, COMSTOP, COMFLOW)
ASCII_STX = 2
ASCII_ETX = 3
WHILE 1
; Wait the specified interval
SLEEP(POLLINTERVAL)
Q = CheckTerminal(TERMADDR)
IF NEQ(Q, -1)
WHILE QUEUELEN(Q)
L = DEQUEUE(Q)
; If a specific window is to be selected, the following function can be used:
; W32SETFOCUS(W32FINDWINDOW("*ULTRAEDIT-32*"))
W32SENDKEYS(KTrans(L) POSTFIX)
SLEEP(PACE)
LOOP
QUEUEFREE(Q)
FI
; Alternate version which saves/restores current window and selects Notepad before wedging data
; IF NEQ(Q, -1)
; ; Saves current window
; HOLD = W32GETFOCUS()
;
; ; Look for Notepad
; H = W32FINDWINDOW("*Notepad*")
; IF EQ(H, 0)
; ; Not found - open it
; W32SHELL("NOTEPAD.EXE")
; ; Wait 1 second for it to become available
; SLEEP(1)
; ; Look up again its window
; H = W32FINDWINDOW("*Notepad*")
; FI
; ; If found, select its window and wedge data
; IF NEQ(H, 0)
; W32SETFOCUS(H)
; WHILE QUEUELEN(Q)
; L = DEQUEUE(Q)
; W32SENDKEYS(KTrans(L) "{ENTER}")
; SLEEP(PACE)
; LOOP
; ELSE
; ; Notepad could not be opened - data lost!
; FI
; W32SETFOCUS(HOLD)
; QUEUEFREE(Q)
; FI
LOOP
W32CLOSEHANDLE(HCOM)
ABORT 0
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)
CONSOLELN "Port " comport " could not be opened."
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 "Error configuring port (" 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 CheckTerminal(staddr)
; Check if terminal is available
q = QUEUENEW()
W32READCOM(_HCOM, 0)
s = MKPacket(CHR(27) "0*" CHR(27), staddr)
W32WRITEFILE(_HCOM, s)
status = 0
RxPack(s, status, 1)
IF EQ(status, 0)
; Packet received
rec = INT(RIGHT(s, 3))
bigrec = EQ(rec, 999)
; Loop until we run out of attempts
s = ""
WHILE STRNEQ(s, "<EOT>")
SLEEP(0.06)
RxPack(s, status, 0)
IF EQ(status, -1)
BREAK
ELSE
IF AND(STRNEQ(s, "<EOF>"), STRNEQ(s, "<EOT>"))
ENQUEUE(q, s)
DEC(@rec)
ELSE
IF STREQ(s, "<EOT>")
BREAK
FI
FI
FI
LOOP
IF EQ(status, 0)
; Send request to delete data in memory
SLEEP(1)
s = MKPacket(CHR(27) "1*" CHR(27), staddr)
W32WRITEFILE(_HCOM, s)
RxPack(s, status, 0)
IF STREQ(s, "<DEL>")
status = 0
ELSE
status = 1
FI
FI
FI
IF EQ(status, 0)
RETURN q
FI
QUEUEFREE(q)
RETURN -1
FUNCTION RxPack(@s, @status, firstpoll)
; Receive a packet, with timeout.
WHILE 1
IF firstpoll
; Wait efficiently only during polling phase
IF NOT(W32WAITRXCHAR(_HCOM, _TIMEOUT))
s = ""
status = -1
RETURN
FI
FI
r = ""
t1 = ADD(W32GETTICKCOUNT(), _TIMEOUT)
WHILE 1
r2 = W32READCOM(_HCOM, 0)
IF STRLEN(r2)
r = r r2
IF STREQ(RIGHT(r, 1), Chr(13))
BREAK
ELSE
t1 = ADD(W32GETTICKCOUNT(), _TIMEOUT)
FI
ELSE
IF GT(W32GETTICKCOUNT(), t1)
; "Timeout"
s = ""
status = -1
RETURN
FI
FI
LOOP
; Sync start of packet
n = STRSTR(r, CHR(2))
IF NEQ(n, 0)
RESTFROM(@r, n)
FI
IF ChkPacket(r)
; "ACK"
W32WRITEFILE(_HCOM, Chr(6))
s = SUBSTR(r, 3, SUB(STRLEN(r), 6))
status = 0
RETURN
ELSE
; "NAK"
W32READCOM(_HCOM, 0)
W32WRITEFILE(_HCOM, Chr(21))
FI
LOOP
RETURN
FUNCTION ChkPacket(packet)
; Returns 1 if packet is correct (verify checksum)
RETURN STREQ(IdChecksum(LEFT(packet, SUB(STRLEN(packet), 3))), LEFT(RIGHT(packet, 3), 2))
FUNCTION IdChecksum(data)
; Calculate checksum for a Sysnet packet
r = 0
dl = STRLEN(data)
FOR x = 1 TO dl
ADD(@r, ASC(SUBSTR(data, x, 1)))
NEXT
MOD(@r, 256)
RETURN PFORMAT("02X", r)
FUNCTION MKPacket(txline, staddr)
; Create a packet for specified address
chk = 0
tlen = STRLEN(txline)
for i = 1 to tlen
ADD(@chk, ASC(SUBSTR(txline, i, 1)))
next
ADD(@chk, _ASCII_STX, staddr, _ASCII_ETX)
MOD(@chk, 256)
RETURN CHR(_ASCII_STX) CHR(staddr) txline CHR(_ASCII_ETX) PFORMAT("02X", chk) "\r"