[796] | 1 | TMGKERN2 ;TMG/kst/OS Specific functions ;11/21/09
|
---|
| 2 | ;;1.0;TMG-LIB;**1**;11/21/09
|
---|
| 3 | ;
|
---|
| 4 | ;"TMG KERNEL FUNCTIONS -- 2
|
---|
| 5 | ;"This module is primarly for functions to support a SOCKET
|
---|
| 6 | ;" connection between two different VistA instances. One running
|
---|
| 7 | ;" as a server, and the other as a client.
|
---|
| 8 | ;"I.e. functions that are OS specific.
|
---|
| 9 | ;"Kevin Toppenberg MD
|
---|
| 10 | ;"GNU General Public License (GPL) applies
|
---|
| 11 | ;"11/21/09
|
---|
| 12 | ;
|
---|
| 13 | RUNSERVER(PORT,TMGMSGFN,TMGVERBOSE)
|
---|
| 14 | ;"Purpose: To open up a socket that will listen to requests from a client.
|
---|
| 15 | ;"Input: Port -- the port to listen on
|
---|
| 16 | ;" TMGMSGFN -- the NAME of a function that will handle incoming
|
---|
| 17 | ;" messages. E.g. 'HANDLMSG^MOD1'
|
---|
| 18 | ;" This function will be called as follows:
|
---|
| 19 | ;" xecute "DO "_TMGMSGFN_"(TMGCLIENT)"
|
---|
| 20 | ;" So the function must accept at least 1 parameter.
|
---|
| 21 | ;" TMGVERBOSE -- If 1 then some output will be show to console.
|
---|
| 22 | ;"Results: 1 if successful, -1^Error Message if failed.
|
---|
| 23 | ;"NOTE: This will be messaging protocol.
|
---|
| 24 | ;" #HELLO# will be sent on startup (possibly preceeded by 2 blank lines)
|
---|
| 25 | ;" #BYE# will be sent when server is quitting
|
---|
| 26 | ;" Server will respond to query of #BYE# by quitting.
|
---|
| 27 | ;" Server will turn control over to the message-handler-fn, allowing it to write
|
---|
| 28 | ;" out as many lines as it wants.
|
---|
| 29 | ;" After message-handler-fn returns, the server will send #OK# to signal done.
|
---|
| 30 | ;"
|
---|
| 31 | NEW RESULT,TMGDELIM,TMGTCPDEV,TMGTIMEOUT
|
---|
| 32 | NEW TMGCLIENT,TMGANSWR,TMGCODE
|
---|
| 33 | ;
|
---|
| 34 | SET RESULT=1 ;"Default of success
|
---|
| 35 | IF +$GET(PORT)'>0 DO GOTO RSVRDN
|
---|
| 36 | . SET RESULT="-1^Invalid port number passed. Received: "_$GET(PORT)
|
---|
| 37 | IF $GET(TMGMSGFN)="" DO GOTO RSVRDN
|
---|
| 38 | . SET RESULT="-1^No Message handling function passed."
|
---|
| 39 | IF $TEXT(@TMGMSGFN)="" DO GOTO RSVRDN
|
---|
| 40 | . SET RESULT="-1^Message handler ["_TMGMSGFN_"] appears invalid"
|
---|
| 41 | SET PORT=+$GET(PORT)
|
---|
| 42 | SET TMGDELIM=$CHAR(13)
|
---|
| 43 | SET TMGTCPDEV="server$"_$JOB
|
---|
| 44 | SET TMGTIMEOUT=30
|
---|
| 45 | SET TMGCODE="DO "_TMGMSGFN_"(TMGCLIENT)"
|
---|
| 46 | SET TMGVERBOSE=+$GET(TMGVERBOSE)
|
---|
| 47 | ;
|
---|
| 48 | OPEN TMGTCPDEV:(ZLISTEN=PORT_":TCP":attach="server":DELIMITER=TMGDELIM):TMGTIMEOUT:"SOCKET"
|
---|
| 49 | IF $TEST=0 DO goto RSVRDN
|
---|
| 50 | . SET RESULT="-1^Attempts to open server failed (timedout)"
|
---|
| 51 | USE TMGTCPDEV
|
---|
| 52 | WRITE /listen(1)
|
---|
| 53 | WRITE /wait(TMGTIMEOUT)
|
---|
| 54 | WRITE "#HELLO#",!
|
---|
| 55 | ;
|
---|
| 56 | L1 ;"Main Listen-Reply loop
|
---|
| 57 | NEW TMGCLIENT,TMGI,TMGDONE
|
---|
| 58 | SET TMGDONE=-1,TMGI=1
|
---|
| 59 | FOR DO QUIT:(TMGDONE>0)!(TMGI>100)!(TMGCLIENT="#BYE#")
|
---|
| 60 | . USE TMGTCPDEV
|
---|
| 61 | . READ TMGCLIENT:TMGTIMEOUT
|
---|
| 62 | . IF ($TEST=0)!(TMGCLIENT="") DO QUIT
|
---|
| 63 | . . SET TMGDONE=TMGDONE+1
|
---|
| 64 | . . WRITE "#BYE#",!
|
---|
| 65 | . IF TMGCLIENT="#ENQ#" WRITE "#ACK#",! QUIT
|
---|
| 66 | . IF TMGCLIENT="#BYE#" WRITE "#BYE#",! QUIT
|
---|
| 67 | . SET TMGI=TMGI+1
|
---|
| 68 | . DO
|
---|
| 69 | . . NEW $etrap
|
---|
| 70 | . . SET $etrap="write ""<Error in message handler>"",!,$ZSTATUS,! set $etrap="""",$ecode="""""
|
---|
| 71 | . . XECUTE TMGCODE
|
---|
| 72 | . USE TMGTCPDEV ;"Ensure handler didn't redirect $IO
|
---|
| 73 | . WRITE "#OK#",! ;"Send message to indicate done sending reply (will allow multi line responses)
|
---|
| 74 | . use $P
|
---|
| 75 | . read *TMGDONE:0
|
---|
| 76 | . IF TMGVERBOSE DO
|
---|
| 77 | . . if TMGI#10=1 write "+"
|
---|
| 78 | . . else write "."
|
---|
| 79 | ;
|
---|
| 80 | CLOSE TMGTCPDEV
|
---|
| 81 | ;
|
---|
| 82 | RSVRDN USE $P
|
---|
| 83 | QUIT RESULT
|
---|
| 84 | ;
|
---|
| 85 | ;
|
---|
| 86 | RUNCLIENT(HOST,PORT) ;"NOTE: meant to be run as a background process
|
---|
| 87 | ;"Purpose: Establish a connection with specified server. Then maintain connection,
|
---|
| 88 | ;" sending queries to server, and returning results. Will take as input
|
---|
| 89 | ;" a messaging global ^TMG("TMP","TCP",$J,"TS",<index>)=<query> TS=ToServer
|
---|
| 90 | ;" And replies will be stored in ^TMG("TMP","TCP",$J,"FS",<index>)=<query> FS=FromServer
|
---|
| 91 | ;"Input: HOST -- the IP address, (or name for DNS lookup) of the server.
|
---|
| 92 | ;" PORT -- the port that the server is listening on.
|
---|
| 93 | ;"Result: none
|
---|
| 94 | ;"Output: Results will be stored in ^TMG("TMP","TCP",$J,"RESULT")=<result>
|
---|
| 95 | ;" 1 -- if successful, -1^Error Message if failed.
|
---|
| 96 | ;"
|
---|
| 97 | NEW RESULT,TMGDELIM,TMGTCPDEV,TMGTIMEOUT
|
---|
| 98 | ;"Setup vars
|
---|
| 99 | SET TMGTCPDEV="client$"_$JOB
|
---|
| 100 | SET TMGTIMEOUT=30
|
---|
| 101 | KILL ^TMG("TMP","TCP",$J,"RESULT")
|
---|
| 102 | SET RESULT=1
|
---|
| 103 | ;"Validate input
|
---|
| 104 | IF +$GET(PORT)'>0 DO GOTO RCLDN
|
---|
| 105 | . SET RESULT="-1^Valid port number passed. Received: "_$GET(PORT)
|
---|
| 106 | IF $GET(HOST)="" DO GOTO RCLDN
|
---|
| 107 | . SET RESULT="-1^No Host passed."
|
---|
| 108 | SET PORT=+$GET(PORT)
|
---|
| 109 | IF PORT'>0 DO GOTO RCLDN
|
---|
| 110 | . SET RESULT="-1^Invalid port: ["_PORT_"]"
|
---|
| 111 | ;"Open up the TCP/IP connection
|
---|
| 112 | OPEN TMGTCPDEV:(CONNECT=HOST_":"_PORT_":TCP":ATTACH="client":DELIMITER=$CHAR(13)):TMGTIMEOUT:"SOCKET"
|
---|
| 113 | IF $TEST=0 DO GOTO RCLDN
|
---|
| 114 | . SET RESULT="-1^Error on OPEN of SOCKET"
|
---|
| 115 | USE TMGTCPDEV
|
---|
| 116 | ;"Make sure server is ready to send information.
|
---|
| 117 | NEW TMGI,SRVREPLY
|
---|
| 118 | FOR TMGI=1:1:3 DO QUIT:(SRVREPLY="#HELLO#")
|
---|
| 119 | . READ SRVREPLY:TMGTIMEOUT
|
---|
| 120 | IF SRVREPLY'="#HELLO#" DO GOTO RCLDN
|
---|
| 121 | . SET RESULT="-1^Failed to get a '#HELLO#' from server"
|
---|
| 122 | SET ^TMG("TMP","TCP",$J,"RESULT")=$GET(RESULT)
|
---|
| 123 | ;
|
---|
| 124 | ;"Now process messaging.
|
---|
| 125 | RC1 NEW TSREF SET TSREF=$NAME(^TMG("TMP","TCP",$J,"TS"))
|
---|
| 126 | NEW FSREF SET FSREF=$NAME(^TMG("TMP","TCP",$J,"FS"))
|
---|
| 127 | NEW QUERY SET QUERY=""
|
---|
| 128 | NEW TMGIDLE SET TMGIDLE=0
|
---|
| 129 | FOR DO quit:(QUERY="#BYE#")!(SRVREPLY="#BYE#")
|
---|
| 130 | . SET TMGI=$ORDER(@TSREF@(""))
|
---|
| 131 | . IF TMGI="" DO ;"Start idle handling
|
---|
| 132 | . . SET QUERY=""
|
---|
| 133 | . . SET TMGIDLE=TMGIDLE+1
|
---|
| 134 | . . HANG 0.1
|
---|
| 135 | . . IF TMGIDLE<50 QUIT
|
---|
| 136 | . . SET QUERY="#ENQ#" ;"send an ENQ every 5 seconds of idleness.
|
---|
| 137 | . . SET TMGIDLE=0
|
---|
| 138 | . ELSE DO
|
---|
| 139 | . . SET QUERY=$get(@TSREF@(TMGI))
|
---|
| 140 | . . KILL @TSREF@(TMGI)
|
---|
| 141 | . . SET TMGIDLE=0 ;"Reset idle counter
|
---|
| 142 | . IF QUERY="" QUIT
|
---|
| 143 | . USE TMGTCPDEV
|
---|
| 144 | . WRITE QUERY,! ;"send query to server.
|
---|
| 145 | . FOR DO QUIT:(SRVREPLY="#BYE#")!(SRVREPLY="#OK#")!(SRVREPLY="#ACK#")
|
---|
| 146 | . . READ SRVREPLY:TMGTIMEOUT ;"read reply.
|
---|
| 147 | . . IF ($TEST=0)!(SRVREPLY="") SET SRVREPLY="#BYE#"
|
---|
| 148 | . . IF SRVREPLY="#ACK#" QUIT ;"Don't record ENQ-ACK's
|
---|
| 149 | . . IF SRVREPLY="#BYE#" QUIT ;"Don't record Termination signal.
|
---|
| 150 | . . SET TMGI=+$ORDER(@FSREF@(""),-1)
|
---|
| 151 | . . SET @FSREF@(TMGI+1)=SRVREPLY
|
---|
| 152 | WRITE "#BYE#",!
|
---|
| 153 | CLOSE TMGTCPDEV
|
---|
| 154 | ;
|
---|
| 155 | RCLDN USE $P
|
---|
| 156 | SET ^TMG("TMP","TCP",$J,"RESULT")=$GET(RESULT)
|
---|
| 157 | HALT
|
---|
| 158 | ;
|
---|
| 159 | ;
|
---|
| 160 | MSGCLIENT(JNUM,QUERY,REPLY,ERROR,TIMEOUT)
|
---|
| 161 | ;"Purpose: To send messages to background client. So this will be one function
|
---|
| 162 | ;" that the programmer may interact with. The reason for having the client
|
---|
| 163 | ;" run as a separate job is so that the server and the client can talk back
|
---|
| 164 | ;" and forth with ENQ<-->ACK upon either timing out, to keep the connection
|
---|
| 165 | ;" alive.
|
---|
| 166 | ;"Input: JNUM -- The job number of the background client process
|
---|
| 167 | ;" QUERY -- The message to send to the server.
|
---|
| 168 | ;" REPLY -- PASS BY REFERENCE, AN OUT PARAMETER. Prior data killed.
|
---|
| 169 | ;" REPLY(1)=<a reply line from server>
|
---|
| 170 | ;" REPLY(2)=<a reply line from server>
|
---|
| 171 | ;" REPLY(3)=<a reply line from server>
|
---|
| 172 | ;" ERROR -- PASS BY REFERENCE, AN OUT PARAMETER. Prior data killed.
|
---|
| 173 | ;" If error, filled with -1^Message.
|
---|
| 174 | ;" TIMEOUT -- OPTIONAL. Default=1 (in seconds)
|
---|
| 175 | ;"Result: none
|
---|
| 176 | ;
|
---|
| 177 | KILL ERROR,REPLY
|
---|
| 178 | NEW RESULT SET RESULT=""
|
---|
| 179 | SET JNUM=+$GET(JNUM)
|
---|
| 180 | IF JNUM'>0 SET ERROR="-1^BAD JOB NUMBER" GOTO MSGDN
|
---|
| 181 | SET QUERY=$GET(QUERY)
|
---|
| 182 | IF QUERY="" SET ERROR="-1^NO QUERY PROVIDED" GOTO MSGDN
|
---|
| 183 | SET TIMEOUT=+$GET(TIMEOUT,1)
|
---|
| 184 | NEW NTIME,STIME SET STIME=$PIECE($H,",",2)
|
---|
| 185 | NEW TMGI SET TMGI=+$ORDER(^TMG("TMP","TCP",JNUM,"TS",""),-1)
|
---|
| 186 | SET ^TMG("TMP","TCP",JNUM,"TS",TMGI+1)=QUERY
|
---|
| 187 | IF QUERY="#BYE#" GOTO MSGDN
|
---|
| 188 | NEW REPLYI SET REPLYI=1
|
---|
| 189 | NEW ONELINE SET ONELINE=""
|
---|
| 190 | FOR DO QUIT:(ONELINE="#OK#")
|
---|
| 191 | . SET TMGI=$ORDER(^TMG("TMP","TCP",JNUM,"FS",""))
|
---|
| 192 | . IF TMGI="" DO QUIT
|
---|
| 193 | . . SET NTIME=$PIECE($H,",",2)
|
---|
| 194 | . . IF (NTIME-STIME)'<TIMEOUT DO
|
---|
| 195 | . . . SET ERROR="-1^TIMED OUT WAITING FOR CLIENT TO GET REPLY FROM SERVER"
|
---|
| 196 | . . . SET ONELINE="#OK#"
|
---|
| 197 | . SET ONELINE=$GET(^TMG("TMP","TCP",JNUM,"FS",TMGI))
|
---|
| 198 | . IF ONELINE'="#OK#" SET REPLY(REPLYI)=ONELINE
|
---|
| 199 | . SET REPLYI=REPLYI+1
|
---|
| 200 | . KILL ^TMG("TMP","TCP",JNUM,"FS",TMGI)
|
---|
| 201 | MSGDN QUIT
|
---|
| 202 | ;
|
---|
| 203 | ;
|
---|
| 204 | CLEARBUF(JNUM,ERROR)
|
---|
| 205 | ;"Purpose: To remove all messages from message buffer.
|
---|
| 206 | ;"Input: JNUM -- The job number of the background client process
|
---|
| 207 | ;" ERROR -- PASS BY REFERENCE, AN OUT PARAMETER. Prior data killed.
|
---|
| 208 | ;" If error, filled with -1^Message.
|
---|
| 209 | ;"Result: None
|
---|
| 210 | ;
|
---|
| 211 | KILL ERROR
|
---|
| 212 | SET JNUM=+$GET(JNUM)
|
---|
| 213 | IF JNUM'>0 SET ERROR="-1^BAD JOB NUMBER" GOTO CLBFDN
|
---|
| 214 | NEW TMGI
|
---|
| 215 | FOR DO QUIT:(TMGI="")
|
---|
| 216 | . SET TMGI=$ORDER(^TMG("TMP","TCP",JNUM,"TS",""))
|
---|
| 217 | . IF TMGI="" QUIT
|
---|
| 218 | . KILL ^TMG("TMP","TCP",JNUM,"TS",TMGI)
|
---|
| 219 | FOR DO QUIT:(TMGI="")
|
---|
| 220 | . SET TMGI=$ORDER(^TMG("TMP","TCP",JNUM,"FS",""))
|
---|
| 221 | . IF TMGI="" QUIT
|
---|
| 222 | . KILL ^TMG("TMP","TCP",JNUM,"FS",TMGI)
|
---|
| 223 | ;
|
---|
| 224 | CLBFDN QUIT
|
---|
| 225 | ;
|
---|
| 226 | ;
|
---|
| 227 | ;"===================================================================
|
---|
| 228 | ;"===================================================================
|
---|
| 229 | ;" Delete later...
|
---|
| 230 | ;"===================================================================
|
---|
| 231 | ;"===================================================================
|
---|
| 232 | ;
|
---|
| 233 | HANDLMSG(MESSAGE)
|
---|
| 234 | write "Got: ["_MESSAGE_"]. Server is $JOB="_$J,!
|
---|
| 235 | quit
|
---|
| 236 | ;
|
---|
| 237 | ;
|
---|