| 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 | 
|---|