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