[613] | 1 | HLCSDL1 ;ALB/MTC/JC - X3.28 LOWER LAYER PROTOCOL 2.2 - 2/28/95 ;08/19/97
|
---|
| 2 | ;;1.6;HEALTH LEVEL SEVEN;**2,34**;Oct 13, 1995
|
---|
| 3 | ;
|
---|
| 4 | ;This is an implemetation of the X3.28 LLP
|
---|
| 5 | ;
|
---|
| 6 | START ;
|
---|
| 7 | N HLIND0,HLIND1,HLNXST,HLTRANS,HLCHK,HLACKBLK,HLDOUT0,HLDOUT1,X,HLRETRY
|
---|
| 8 | N HLNXST,HLLINE,HLXOR,HLTOUT,HLLINE,HLC1,HLC2
|
---|
| 9 | N HLDLX,HLM
|
---|
| 10 | ;S X=10 X ^%ZOSF("PRIORITY")
|
---|
| 11 | S HLM=0,HLNXST=1
|
---|
| 12 | ;-- enter loop for polling for i/o
|
---|
| 13 | D POLL
|
---|
| 14 | ;-- exit and clean-up
|
---|
| 15 | D EXIT
|
---|
| 16 | Q
|
---|
| 17 | ;
|
---|
| 18 | ;
|
---|
| 19 | POLL ;-- This function will check if any messages should be sent
|
---|
| 20 | ; then if anything is in the buffer to read in. If there is data
|
---|
| 21 | ; to write out then the system will bid for master status and if
|
---|
| 22 | ; successful x-mit the message. If the system receives a request to
|
---|
| 23 | ; receive data, then it will attemp to enter a slave mode and read
|
---|
| 24 | ; data in.
|
---|
| 25 | ;
|
---|
| 26 | N HLFLAG
|
---|
| 27 | S HLFLAG=1
|
---|
| 28 | D TRACE^HLCSDL2("Logging IO to ^XTMP('HL',N")
|
---|
| 29 | ;-- enter loop
|
---|
| 30 | F D MONITOR^HLCSDR2("POLLING",5,HLDP) Q:'HLFLAG D
|
---|
| 31 | .; should we still be running
|
---|
| 32 | . I '$$RUN^HLCSDL2 D MONITOR^HLCSDR2("SHUTDOWN",5,HLDP) S HLFLAG=0 Q
|
---|
| 33 | .;-- check for data to read in
|
---|
| 34 | . D TRACE^HLCSDL2("Slave Check"),SLAVE
|
---|
| 35 | . I '$$RUN^HLCSDL2 D MONITOR^HLCSDR2("SHUTDOWN",5,HLDP) S HLFLAG=0 Q
|
---|
| 36 | .;-- check for out going data
|
---|
| 37 | . D TRACE^HLCSDL2("Master Check"),MASTER
|
---|
| 38 | Q
|
---|
| 39 | ;
|
---|
| 40 | SLAVE ;-- this function will check if anything is ready to read in from
|
---|
| 41 | ; the port. If nothing is ready then return to polling, else
|
---|
| 42 | ; start slave process.
|
---|
| 43 | ;
|
---|
| 44 | N HLX
|
---|
| 45 | ;-- check if anything is ready to read in.
|
---|
| 46 | D TRACE^HLCSDL2("Slave Request")
|
---|
| 47 | ;-- read for enq (request for slave)
|
---|
| 48 | I '$$READENQ^HLCSDL2 G SLAVEQ
|
---|
| 49 | ;-- ack0
|
---|
| 50 | D TRACE^HLCSDL2("Slave Ack0")
|
---|
| 51 | D SENDACK^HLCSDL2(0)
|
---|
| 52 | ;-- read data
|
---|
| 53 | D TRACE^HLCSDL2("Slave Read Data")
|
---|
| 54 | D READ
|
---|
| 55 | ;-- exit and return to polling
|
---|
| 56 | SLAVEQ ;
|
---|
| 57 | Q
|
---|
| 58 | ;
|
---|
| 59 | READ ;-- This function will take the incoming data from the device and
|
---|
| 60 | ; store in file 870. After each read an ack will be sent to the
|
---|
| 61 | ; client application. Once an EOT has been received, return to
|
---|
| 62 | ; polling.
|
---|
| 63 | ;
|
---|
| 64 | N HLX,HLI,HLBK,HLETXB,HLLINE,HLDATA,BTERM
|
---|
| 65 | ;-- prepare for incoming data
|
---|
| 66 | S HLLINE=1,HLI=0
|
---|
| 67 | LOOP ;-- main loop for reading in message
|
---|
| 68 | ;
|
---|
| 69 | ;-- update status
|
---|
| 70 | D MONITOR^HLCSDR2("READING",5,HLDP)
|
---|
| 71 | ;-- read block of data
|
---|
| 72 | S HLX=$$READBK^HLCSDL2("HLDATA",.HLLEN,.HLBK,.HLCK,.BTERM)
|
---|
| 73 | ;-- check for TIMEOUT
|
---|
| 74 | I $G(HLDATA)["TIMEOUT" G READQ
|
---|
| 75 | ;-- check for EOT
|
---|
| 76 | I $G(HLDATA)=HLEOT G READQ
|
---|
| 77 | ;-- check if vaild data
|
---|
| 78 | I '$$VALID^HLCSDL2("HLDATA",HLLINE#8,HLLEN,HLBK,HLCK,BTERM) D G LOOP
|
---|
| 79 | .;-- update status
|
---|
| 80 | . D TRACE^HLCSDL2("Slave Write NAK")
|
---|
| 81 | . D MONITOR^HLCSDR2("SEND NAK",5,HLDP)
|
---|
| 82 | .;-- send nak
|
---|
| 83 | . D SENDNAK^HLCSDL2
|
---|
| 84 | ;
|
---|
| 85 | ;-- write data to file 870
|
---|
| 86 | S HLDOUT0=$$ENQUEUE^HLCSQUE(HLDP,"IN"),HLDOUT1=$P(HLDOUT0,U,2),HLDOUT0=+HLDOUT0
|
---|
| 87 | D APPEND^HLCSUTL("HLDATA",HLDOUT0,HLDOUT1)
|
---|
| 88 | S HLLINE=HLLINE+1
|
---|
| 89 | ;
|
---|
| 90 | ;-- If end of text set status
|
---|
| 91 | I +BTERM=+HLETX D
|
---|
| 92 | . D MONITOR^HLCSDR2("P",2,HLDOUT0,HLDOUT1,"IN")
|
---|
| 93 | . D MONITOR^HLCSDR2("A",3,HLDOUT0,HLDOUT1,"IN")
|
---|
| 94 | ;-- ack
|
---|
| 95 | D SENDACK^HLCSDL2(HLBK)
|
---|
| 96 | ;-- read next line of data
|
---|
| 97 | G LOOP
|
---|
| 98 | ;
|
---|
| 99 | READQ Q
|
---|
| 100 | ;
|
---|
| 101 | MASTER ;-- if outgoing messages are present then establish m/s and begin
|
---|
| 102 | ; transmission of message.
|
---|
| 103 | ;
|
---|
| 104 | N HLBID,HLDOUT0,HLDOUT1
|
---|
| 105 | ;-- check queue
|
---|
| 106 | D TRACE^HLCSDL2("Master Check Queue")
|
---|
| 107 | S HLDOUT0=$$DEQUEUE^HLCSQUE(HLDP,"OUT")
|
---|
| 108 | ;-- nothing on queue quit
|
---|
| 109 | I +HLDOUT0<0 D TRACE^HLCSDL2("*Out Queue Empty") G MASTERQ
|
---|
| 110 | S HLDOUT1=$P(HLDOUT0,U,2),HLDOUT0=+HLDOUT0
|
---|
| 111 | ;-- have item in queue to write, bid for master status
|
---|
| 112 | S HLBID=$$BID(5)
|
---|
| 113 | ;-- if attemp fails quit
|
---|
| 114 | I 'HLBID D PUSH^HLCSQUE(HLDOUT0,HLDOUT1) G MASTERQ
|
---|
| 115 | ;-- if successful goto write state
|
---|
| 116 | I HLBID D
|
---|
| 117 | . D WRITE(HLDOUT0,HLDOUT1)
|
---|
| 118 | . D EOT^HLCSDL2
|
---|
| 119 | ;
|
---|
| 120 | MASTERQ Q
|
---|
| 121 | ;
|
---|
| 122 | BID(MAXTRY) ;-- This function will bid for Master status MAXTRY times
|
---|
| 123 | ; and return a 1 if succesful, 0 if fails
|
---|
| 124 | ; INPUT - MAXTRY - Maximum number of attemps before failing
|
---|
| 125 | ; OUTPUT - 1 for ok; 0 fails
|
---|
| 126 | ;
|
---|
| 127 | N RESULT,HLTRIES,HLDLX
|
---|
| 128 | S RESULT=0,HLTRIES=0
|
---|
| 129 | ;-- update status
|
---|
| 130 | D MONITOR^HLCSDR2("BIDDING",5,HLDP)
|
---|
| 131 | BIDRET ;-- bid for master status
|
---|
| 132 | D TRACE^HLCSDL2("Master Bid")
|
---|
| 133 | D ENQ^HLCSDL2
|
---|
| 134 | ;-- update status
|
---|
| 135 | D TRACE^HLCSDL2("Master Bid Wait Ack0")
|
---|
| 136 | D MONITOR^HLCSDR2("WAIT ACK",5,HLDP)
|
---|
| 137 | ;-- if read ack if block 0 OK else fail
|
---|
| 138 | I $$READACK^HLCSDL2(0) S RESULT=1 G BIDQ
|
---|
| 139 | ;-- if nak or timeout
|
---|
| 140 | S HLTRIES=HLTRIES+1
|
---|
| 141 | I HLTRIES>(MAXTRY-1) G BIDQ
|
---|
| 142 | G BIDRET
|
---|
| 143 | BIDQ ;-- exit
|
---|
| 144 | Q RESULT
|
---|
| 145 | ;
|
---|
| 146 | WRITE(HLDOUT0,HLDOUT1) ;-- This function will take the message contained
|
---|
| 147 | ; in file 870 specified by HLDOUT0 and HLDOUT1 and write the data out.
|
---|
| 148 | ; after each write the system will wait for an ack.
|
---|
| 149 | ; INPUT : HLDOUT0 - IEN of file #870
|
---|
| 150 | ; HLDOUT1 - IEN of out queue multiple
|
---|
| 151 | ;
|
---|
| 152 | N HLHEAD,HLTEXT1,HLFOOT,HLX1,HLX2,HLX3,HLTEMP
|
---|
| 153 | ;-- loop to process message
|
---|
| 154 | S HLX1="",HLX2="HLTEXT1"
|
---|
| 155 | F HLI=1:1 K HLTEXT1 S HLX1=$$NEXTLINE^HLCSUTL(HLDOUT0,HLDOUT1,HLX1,HLX2,"OUT") Q:'HLX1 D I '$$SEND(HLX2,HLHEAD,HLFOOT,5,HLI#8) Q
|
---|
| 156 | . S HLX3=$$NEXTLINE^HLCSUTL(HLDOUT0,HLDOUT1,HLX1,"HLTEMP","OUT")
|
---|
| 157 | . D BUILD^HLCSDL2(HLX2,HLI,$S(HLX3:HLETB,1:HLETX),.HLHEAD,.HLFOOT)
|
---|
| 158 | ;
|
---|
| 159 | WRITEQ Q
|
---|
| 160 | ;
|
---|
| 161 | SEND(HLTEXT,HLHEAD,HLFOOT,HLRETRY,HLBK) ;-- This function will write the X3.28 formatted
|
---|
| 162 | ; string out the port and wait for an ack. If this function fails
|
---|
| 163 | ; 0 will be returned, else 1.
|
---|
| 164 | ;
|
---|
| 165 | ; Input - HLTEXT - Array containing segment to send
|
---|
| 166 | ; - HLHEAD - Block header <STX><BLK><LEN>
|
---|
| 167 | ; - HLFOOT - Block footer <ETX or ETB><BCC><TERM>
|
---|
| 168 | ; - HLRETRY- Maximum retries before failure
|
---|
| 169 | ; - HLBK - Current block 0-7
|
---|
| 170 | ; Output- 0 Fails, 1 = OK
|
---|
| 171 | ;
|
---|
| 172 | N RESULT,HLTRY,X
|
---|
| 173 | S RESULT=1,HLTRY=0
|
---|
| 174 | RETRY ;-- write data
|
---|
| 175 | ;-- update status
|
---|
| 176 | D TRACE^HLCSDL2("Master Write")
|
---|
| 177 | D MONITOR^HLCSDR2("WRITING",5,HLDP)
|
---|
| 178 | ;
|
---|
| 179 | U IO
|
---|
| 180 | ;-- write header
|
---|
| 181 | W HLHEAD
|
---|
| 182 | D LOG(HLHEAD,"WRITE: ")
|
---|
| 183 | S X="" F S X=$O(@HLTEXT@(X)) Q:'X W @HLTEXT@(X) D LOG(@HLTEXT@(X),"Write: ")
|
---|
| 184 | ;-- write footer
|
---|
| 185 | W HLFOOT D LOG(HLFOOT,"WRITE: ")
|
---|
| 186 | ;-- Wait for ack
|
---|
| 187 | D TRACE^HLCSDL2("Master Wait for Ack"_HLBK)
|
---|
| 188 | D MONITOR^HLCSDR2("WAITING ACK",5,HLDP)
|
---|
| 189 | ;-- if ack
|
---|
| 190 | I $$READACK^HLCSDL2(HLBK) S RESULT=1 D MONITOR^HLCSDR2("D",2,HLDP,HLDOUT1,"OUT") G SENDQ
|
---|
| 191 | ;-- if nak then retry
|
---|
| 192 | S HLTRY=HLTRY+1
|
---|
| 193 | I HLTRY>(HLRETRY-1) S RESULT=0 G SENDQ
|
---|
| 194 | G RETRY
|
---|
| 195 | SENDQ ;-- exit
|
---|
| 196 | Q RESULT
|
---|
| 197 | ;
|
---|
| 198 | EXIT ;-- Cleanup
|
---|
| 199 | Q
|
---|
| 200 | ;
|
---|
| 201 | LOG(ST1,OP) ;Log reads/writes (translates ctrls)
|
---|
| 202 | ;ST1=string to file
|
---|
| 203 | ;OP=operation "read" or "write"
|
---|
| 204 | I $G(HLTRACE) D
|
---|
| 205 | .N X S X=$G(^XTMP("HL",0)),$P(X,U)=DT+1,$P(X,U,2)=DT
|
---|
| 206 | .S $P(X,U,3)="HL7 Debug Log",HLLOG=$P(X,U,4)
|
---|
| 207 | .S HLN=$$TRANS(ST1)
|
---|
| 208 | .S HLLOG=HLLOG+1,^XTMP("HL",HLLOG)=OP_HLN,$P(X,U,4)=HLLOG
|
---|
| 209 | .S ^XTMP("HL",0)=X
|
---|
| 210 | Q
|
---|
| 211 | TRANS(ST) ;Translate controls in string
|
---|
| 212 | ;ST=String containing embedded x3.28 control characters
|
---|
| 213 | S ST2="" F I=1:1:$L(ST) S J=$E(ST,I) D
|
---|
| 214 | .I $D(HLCTRL($A(J))) S J=HLCTRL($A(J))
|
---|
| 215 | .S ST2=$G(ST2)_J
|
---|
| 216 | Q ST2
|
---|