HLCSDL1 ;ALB/MTC/JC - X3.28 LOWER LAYER PROTOCOL 2.2 - 2/28/95 ;08/19/97 [ 04/02/2003 8:38 AM ]
;;1.6;HEALTH LEVEL SEVEN;**1004**;APR 1, 2003
;;1.6;HEALTH LEVEL SEVEN;**2,34**;Oct 13, 1995
;
;This is an implemetation of the X3.28 LLP
;
START ;
N HLIND0,HLIND1,HLNXST,HLTRANS,HLCHK,HLACKBLK,HLDOUT0,HLDOUT1,X,HLRETRY
N HLNXST,HLLINE,HLXOR,HLTOUT,HLLINE,HLC1,HLC2
N HLDLX,HLM
;S X=10 X ^%ZOSF("PRIORITY")
S HLM=0,HLNXST=1
;-- enter loop for polling for i/o
D POLL
;-- exit and clean-up
D EXIT
Q
;
;
POLL ;-- This function will check if any messages should be sent
; then if anything is in the buffer to read in. If there is data
; to write out then the system will bid for master status and if
; successful x-mit the message. If the system receives a request to
; receive data, then it will attemp to enter a slave mode and read
; data in.
;
N HLFLAG
S HLFLAG=1
D TRACE^HLCSDL2("Logging IO to ^XTMP('HL',N")
;-- enter loop
F D MONITOR^HLCSDR2("POLLING",5,HLDP) Q:'HLFLAG D
.; should we still be running
. I '$$RUN^HLCSDL2 D MONITOR^HLCSDR2("SHUTDOWN",5,HLDP) S HLFLAG=0 Q
.;-- check for data to read in
. D TRACE^HLCSDL2("Slave Check"),SLAVE
. I '$$RUN^HLCSDL2 D MONITOR^HLCSDR2("SHUTDOWN",5,HLDP) S HLFLAG=0 Q
.;-- check for out going data
. D TRACE^HLCSDL2("Master Check"),MASTER
Q
;
SLAVE ;-- this function will check if anything is ready to read in from
; the port. If nothing is ready then return to polling, else
; start slave process.
;
N HLX
;-- check if anything is ready to read in.
D TRACE^HLCSDL2("Slave Request")
;-- read for enq (request for slave)
I '$$READENQ^HLCSDL2 G SLAVEQ
;-- ack0
D TRACE^HLCSDL2("Slave Ack0")
D SENDACK^HLCSDL2(0)
;-- read data
D TRACE^HLCSDL2("Slave Read Data")
D READ
;-- exit and return to polling
SLAVEQ ;
Q
;
READ ;-- This function will take the incoming data from the device and
; store in file 870. After each read an ack will be sent to the
; client application. Once an EOT has been received, return to
; polling.
;
N HLX,HLI,HLBK,HLETXB,HLLINE,HLDATA,BTERM
;-- prepare for incoming data
S HLLINE=1,HLI=0
LOOP ;-- main loop for reading in message
;
;-- update status
D MONITOR^HLCSDR2("READING",5,HLDP)
;-- read block of data
S HLX=$$READBK^HLCSDL2("HLDATA",.HLLEN,.HLBK,.HLCK,.BTERM)
;-- check for TIMEOUT
I $G(HLDATA)["TIMEOUT" G READQ
;-- check for EOT
I $G(HLDATA)=HLEOT G READQ
;-- check if vaild data
I '$$VALID^HLCSDL2("HLDATA",HLLINE#8,HLLEN,HLBK,HLCK,BTERM) D G LOOP
.;-- update status
. D TRACE^HLCSDL2("Slave Write NAK")
. D MONITOR^HLCSDR2("SEND NAK",5,HLDP)
.;-- send nak
. D SENDNAK^HLCSDL2
;
;-- write data to file 870
S HLDOUT0=$$ENQUEUE^HLCSQUE(HLDP,"IN"),HLDOUT1=$P(HLDOUT0,U,2),HLDOUT0=+HLDOUT0
D APPEND^HLCSUTL("HLDATA",HLDOUT0,HLDOUT1)
S HLLINE=HLLINE+1
;
;-- If end of text set status
I +BTERM=+HLETX D
. D MONITOR^HLCSDR2("P",2,HLDOUT0,HLDOUT1,"IN")
. D MONITOR^HLCSDR2("A",3,HLDOUT0,HLDOUT1,"IN")
;-- ack
D SENDACK^HLCSDL2(HLBK)
;-- read next line of data
G LOOP
;
READQ Q
;
MASTER ;-- if outgoing messages are present then establish m/s and begin
; transmission of message.
;
N HLBID,HLDOUT0,HLDOUT1
;-- check queue
D TRACE^HLCSDL2("Master Check Queue")
S HLDOUT0=$$DEQUEUE^HLCSQUE(HLDP,"OUT")
;-- nothing on queue quit
I +HLDOUT0<0 D TRACE^HLCSDL2("*Out Queue Empty") G MASTERQ
S HLDOUT1=$P(HLDOUT0,U,2),HLDOUT0=+HLDOUT0
;-- have item in queue to write, bid for master status
S HLBID=$$BID(5)
;-- if attemp fails quit
I 'HLBID D PUSH^HLCSQUE(HLDOUT0,HLDOUT1) G MASTERQ
;-- if successful goto write state
I HLBID D
. D WRITE(HLDOUT0,HLDOUT1)
. D EOT^HLCSDL2
;
MASTERQ Q
;
BID(MAXTRY) ;-- This function will bid for Master status MAXTRY times
; and return a 1 if succesful, 0 if fails
; INPUT - MAXTRY - Maximum number of attemps before failing
; OUTPUT - 1 for ok; 0 fails
;
N RESULT,HLTRIES,HLDLX
S RESULT=0,HLTRIES=0
;-- update status
D MONITOR^HLCSDR2("BIDDING",5,HLDP)
BIDRET ;-- bid for master status
D TRACE^HLCSDL2("Master Bid")
D ENQ^HLCSDL2
;-- update status
D TRACE^HLCSDL2("Master Bid Wait Ack0")
D MONITOR^HLCSDR2("WAIT ACK",5,HLDP)
;-- if read ack if block 0 OK else fail
I $$READACK^HLCSDL2(0) S RESULT=1 G BIDQ
;-- if nak or timeout
S HLTRIES=HLTRIES+1
I HLTRIES>(MAXTRY-1) G BIDQ
G BIDRET
BIDQ ;-- exit
Q RESULT
;
WRITE(HLDOUT0,HLDOUT1) ;-- This function will take the message contained
; in file 870 specified by HLDOUT0 and HLDOUT1 and write the data out.
; after each write the system will wait for an ack.
; INPUT : HLDOUT0 - IEN of file #870
; HLDOUT1 - IEN of out queue multiple
;
N HLHEAD,HLTEXT1,HLFOOT,HLX1,HLX2,HLX3,HLTEMP
;-- loop to process message
S HLX1="",HLX2="HLTEXT1"
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
. S HLX3=$$NEXTLINE^HLCSUTL(HLDOUT0,HLDOUT1,HLX1,"HLTEMP","OUT")
. D BUILD^HLCSDL2(HLX2,HLI,$S(HLX3:HLETB,1:HLETX),.HLHEAD,.HLFOOT)
;
WRITEQ Q
;
SEND(HLTEXT,HLHEAD,HLFOOT,HLRETRY,HLBK) ;-- This function will write the X3.28 formatted
; string out the port and wait for an ack. If this function fails
; 0 will be returned, else 1.
;
; Input - HLTEXT - Array containing segment to send
; - HLHEAD - Block header <STX><BLK><LEN>
; - HLFOOT - Block footer <ETX or ETB><BCC><TERM>
; - HLRETRY- Maximum retries before failure
; - HLBK - Current block 0-7
; Output- 0 Fails, 1 = OK
;
N RESULT,HLTRY,X
S RESULT=1,HLTRY=0
RETRY ;-- write data
;-- update status
D TRACE^HLCSDL2("Master Write")
D MONITOR^HLCSDR2("WRITING",5,HLDP)
;
U IO
;-- write header
W HLHEAD
D LOG(HLHEAD,"WRITE: ")
S X="" F S X=$O(@HLTEXT@(X)) Q:'X W @HLTEXT@(X) D LOG(@HLTEXT@(X),"Write: ")
;-- write footer
W HLFOOT D LOG(HLFOOT,"WRITE: ")
;-- Wait for ack
D TRACE^HLCSDL2("Master Wait for Ack"_HLBK)
D MONITOR^HLCSDR2("WAITING ACK",5,HLDP)
;-- if ack
I $$READACK^HLCSDL2(HLBK) S RESULT=1 D MONITOR^HLCSDR2("D",2,HLDP,HLDOUT1,"OUT") G SENDQ
;-- if nak then retry
S HLTRY=HLTRY+1
I HLTRY>(HLRETRY-1) S RESULT=0 G SENDQ
G RETRY
SENDQ ;-- exit
Q RESULT
;
EXIT ;-- Cleanup
Q
;
LOG(ST1,OP) ;Log reads/writes (translates ctrls)
;ST1=string to file
;OP=operation "read" or "write"
I $G(HLTRACE) D
.N X S X=$G(^XTMP("HL",0)),$P(X,U)=DT+1,$P(X,U,2)=DT
.S $P(X,U,3)="HL7 Debug Log",HLLOG=$P(X,U,4)
.S HLN=$$TRANS(ST1)
.S HLLOG=HLLOG+1,^XTMP("HL",HLLOG)=OP_HLN,$P(X,U,4)=HLLOG
.S ^XTMP("HL",0)=X
Q
TRANS(ST) ;Translate controls in string
;ST=String containing embedded x3.28 control characters
S ST2="" F I=1:1:$L(ST) S J=$E(ST,I) D
.I $D(HLCTRL($A(J))) S J=HLCTRL($A(J))
.S ST2=$G(ST2)_J
Q ST2
HLCSDL1 ;ALB/MTC/JC - X3.28 LOWER LAYER PROTOCOL 2.2 - 2/28/95 ;08/19/97 [ 04/02/2003 8:38 AM ]
+1 ;;1.6;HEALTH LEVEL SEVEN;**1004**;APR 1, 2003
+2 ;;1.6;HEALTH LEVEL SEVEN;**2,34**;Oct 13, 1995
+3 ;
+4 ;This is an implemetation of the X3.28 LLP
+5 ;
START ;
+1 NEW HLIND0,HLIND1,HLNXST,HLTRANS,HLCHK,HLACKBLK,HLDOUT0,HLDOUT1,X,HLRETRY
+2 NEW HLNXST,HLLINE,HLXOR,HLTOUT,HLLINE,HLC1,HLC2
+3 NEW HLDLX,HLM
+4 ;S X=10 X ^%ZOSF("PRIORITY")
+5 SET HLM=0
SET HLNXST=1
+6 ;-- enter loop for polling for i/o
+7 DO POLL
+8 ;-- exit and clean-up
+9 DO EXIT
+10 QUIT
+11 ;
+12 ;
POLL ;-- This function will check if any messages should be sent
+1 ; then if anything is in the buffer to read in. If there is data
+2 ; to write out then the system will bid for master status and if
+3 ; successful x-mit the message. If the system receives a request to
+4 ; receive data, then it will attemp to enter a slave mode and read
+5 ; data in.
+6 ;
+7 NEW HLFLAG
+8 SET HLFLAG=1
+9 DO TRACE^HLCSDL2("Logging IO to ^XTMP('HL',N")
+10 ;-- enter loop
+11 FOR
DO MONITOR^HLCSDR2("POLLING",5,HLDP)
IF 'HLFLAG
QUIT
Begin DoDot:1
+12 ; should we still be running
+13 IF '$$RUN^HLCSDL2
DO MONITOR^HLCSDR2("SHUTDOWN",5,HLDP)
SET HLFLAG=0
QUIT
+14 ;-- check for data to read in
+15 DO TRACE^HLCSDL2("Slave Check")
DO SLAVE
+16 IF '$$RUN^HLCSDL2
DO MONITOR^HLCSDR2("SHUTDOWN",5,HLDP)
SET HLFLAG=0
QUIT
+17 ;-- check for out going data
+18 DO TRACE^HLCSDL2("Master Check")
DO MASTER
End DoDot:1
+19 QUIT
+20 ;
SLAVE ;-- this function will check if anything is ready to read in from
+1 ; the port. If nothing is ready then return to polling, else
+2 ; start slave process.
+3 ;
+4 NEW HLX
+5 ;-- check if anything is ready to read in.
+6 DO TRACE^HLCSDL2("Slave Request")
+7 ;-- read for enq (request for slave)
+8 IF '$$READENQ^HLCSDL2
GOTO SLAVEQ
+9 ;-- ack0
+10 DO TRACE^HLCSDL2("Slave Ack0")
+11 DO SENDACK^HLCSDL2(0)
+12 ;-- read data
+13 DO TRACE^HLCSDL2("Slave Read Data")
+14 DO READ
+15 ;-- exit and return to polling
SLAVEQ ;
+1 QUIT
+2 ;
READ ;-- This function will take the incoming data from the device and
+1 ; store in file 870. After each read an ack will be sent to the
+2 ; client application. Once an EOT has been received, return to
+3 ; polling.
+4 ;
+5 NEW HLX,HLI,HLBK,HLETXB,HLLINE,HLDATA,BTERM
+6 ;-- prepare for incoming data
+7 SET HLLINE=1
SET HLI=0
LOOP ;-- main loop for reading in message
+1 ;
+2 ;-- update status
+3 DO MONITOR^HLCSDR2("READING",5,HLDP)
+4 ;-- read block of data
+5 SET HLX=$$READBK^HLCSDL2("HLDATA",.HLLEN,.HLBK,.HLCK,.BTERM)
+6 ;-- check for TIMEOUT
+7 IF $GET(HLDATA)["TIMEOUT"
GOTO READQ
+8 ;-- check for EOT
+9 IF $GET(HLDATA)=HLEOT
GOTO READQ
+10 ;-- check if vaild data
+11 IF '$$VALID^HLCSDL2("HLDATA",HLLINE#8,HLLEN,HLBK,HLCK,BTERM)
Begin DoDot:1
+12 ;-- update status
+13 DO TRACE^HLCSDL2("Slave Write NAK")
+14 DO MONITOR^HLCSDR2("SEND NAK",5,HLDP)
+15 ;-- send nak
+16 DO SENDNAK^HLCSDL2
End DoDot:1
GOTO LOOP
+17 ;
+18 ;-- write data to file 870
+19 SET HLDOUT0=$$ENQUEUE^HLCSQUE(HLDP,"IN")
SET HLDOUT1=$PIECE(HLDOUT0,U,2)
SET HLDOUT0=+HLDOUT0
+20 DO APPEND^HLCSUTL("HLDATA",HLDOUT0,HLDOUT1)
+21 SET HLLINE=HLLINE+1
+22 ;
+23 ;-- If end of text set status
+24 IF +BTERM=+HLETX
Begin DoDot:1
+25 DO MONITOR^HLCSDR2("P",2,HLDOUT0,HLDOUT1,"IN")
+26 DO MONITOR^HLCSDR2("A",3,HLDOUT0,HLDOUT1,"IN")
End DoDot:1
+27 ;-- ack
+28 DO SENDACK^HLCSDL2(HLBK)
+29 ;-- read next line of data
+30 GOTO LOOP
+31 ;
READQ QUIT
+1 ;
MASTER ;-- if outgoing messages are present then establish m/s and begin
+1 ; transmission of message.
+2 ;
+3 NEW HLBID,HLDOUT0,HLDOUT1
+4 ;-- check queue
+5 DO TRACE^HLCSDL2("Master Check Queue")
+6 SET HLDOUT0=$$DEQUEUE^HLCSQUE(HLDP,"OUT")
+7 ;-- nothing on queue quit
+8 IF +HLDOUT0<0
DO TRACE^HLCSDL2("*Out Queue Empty")
GOTO MASTERQ
+9 SET HLDOUT1=$PIECE(HLDOUT0,U,2)
SET HLDOUT0=+HLDOUT0
+10 ;-- have item in queue to write, bid for master status
+11 SET HLBID=$$BID(5)
+12 ;-- if attemp fails quit
+13 IF 'HLBID
DO PUSH^HLCSQUE(HLDOUT0,HLDOUT1)
GOTO MASTERQ
+14 ;-- if successful goto write state
+15 IF HLBID
Begin DoDot:1
+16 DO WRITE(HLDOUT0,HLDOUT1)
+17 DO EOT^HLCSDL2
End DoDot:1
+18 ;
MASTERQ QUIT
+1 ;
BID(MAXTRY) ;-- This function will bid for Master status MAXTRY times
+1 ; and return a 1 if succesful, 0 if fails
+2 ; INPUT - MAXTRY - Maximum number of attemps before failing
+3 ; OUTPUT - 1 for ok; 0 fails
+4 ;
+5 NEW RESULT,HLTRIES,HLDLX
+6 SET RESULT=0
SET HLTRIES=0
+7 ;-- update status
+8 DO MONITOR^HLCSDR2("BIDDING",5,HLDP)
BIDRET ;-- bid for master status
+1 DO TRACE^HLCSDL2("Master Bid")
+2 DO ENQ^HLCSDL2
+3 ;-- update status
+4 DO TRACE^HLCSDL2("Master Bid Wait Ack0")
+5 DO MONITOR^HLCSDR2("WAIT ACK",5,HLDP)
+6 ;-- if read ack if block 0 OK else fail
+7 IF $$READACK^HLCSDL2(0)
SET RESULT=1
GOTO BIDQ
+8 ;-- if nak or timeout
+9 SET HLTRIES=HLTRIES+1
+10 IF HLTRIES>(MAXTRY-1)
GOTO BIDQ
+11 GOTO BIDRET
BIDQ ;-- exit
+1 QUIT RESULT
+2 ;
WRITE(HLDOUT0,HLDOUT1) ;-- This function will take the message contained
+1 ; in file 870 specified by HLDOUT0 and HLDOUT1 and write the data out.
+2 ; after each write the system will wait for an ack.
+3 ; INPUT : HLDOUT0 - IEN of file #870
+4 ; HLDOUT1 - IEN of out queue multiple
+5 ;
+6 NEW HLHEAD,HLTEXT1,HLFOOT,HLX1,HLX2,HLX3,HLTEMP
+7 ;-- loop to process message
+8 SET HLX1=""
SET HLX2="HLTEXT1"
+9 FOR HLI=1:1
KILL HLTEXT1
SET HLX1=$$NEXTLINE^HLCSUTL(HLDOUT0,HLDOUT1,HLX1,HLX2,"OUT")
IF 'HLX1
QUIT
Begin DoDot:1
+10 SET HLX3=$$NEXTLINE^HLCSUTL(HLDOUT0,HLDOUT1,HLX1,"HLTEMP","OUT")
+11 DO BUILD^HLCSDL2(HLX2,HLI,$SELECT(HLX3:HLETB,1:HLETX),.HLHEAD,.HLFOOT)
End DoDot:1
IF '$$SEND(HLX2,HLHEAD,HLFOOT,5,HLI#8)
QUIT
+12 ;
WRITEQ QUIT
+1 ;
SEND(HLTEXT,HLHEAD,HLFOOT,HLRETRY,HLBK) ;-- This function will write the X3.28 formatted
+1 ; string out the port and wait for an ack. If this function fails
+2 ; 0 will be returned, else 1.
+3 ;
+4 ; Input - HLTEXT - Array containing segment to send
+5 ; - HLHEAD - Block header <STX><BLK><LEN>
+6 ; - HLFOOT - Block footer <ETX or ETB><BCC><TERM>
+7 ; - HLRETRY- Maximum retries before failure
+8 ; - HLBK - Current block 0-7
+9 ; Output- 0 Fails, 1 = OK
+10 ;
+11 NEW RESULT,HLTRY,X
+12 SET RESULT=1
SET HLTRY=0
RETRY ;-- write data
+1 ;-- update status
+2 DO TRACE^HLCSDL2("Master Write")
+3 DO MONITOR^HLCSDR2("WRITING",5,HLDP)
+4 ;
+5 USE IO
+6 ;-- write header
+7 WRITE HLHEAD
+8 DO LOG(HLHEAD,"WRITE: ")
+9 SET X=""
FOR
SET X=$ORDER(@HLTEXT@(X))
IF 'X
QUIT
WRITE @HLTEXT@(X)
DO LOG(@HLTEXT@(X),"Write: ")
+10 ;-- write footer
+11 WRITE HLFOOT
DO LOG(HLFOOT,"WRITE: ")
+12 ;-- Wait for ack
+13 DO TRACE^HLCSDL2("Master Wait for Ack"_HLBK)
+14 DO MONITOR^HLCSDR2("WAITING ACK",5,HLDP)
+15 ;-- if ack
+16 IF $$READACK^HLCSDL2(HLBK)
SET RESULT=1
DO MONITOR^HLCSDR2("D",2,HLDP,HLDOUT1,"OUT")
GOTO SENDQ
+17 ;-- if nak then retry
+18 SET HLTRY=HLTRY+1
+19 IF HLTRY>(HLRETRY-1)
SET RESULT=0
GOTO SENDQ
+20 GOTO RETRY
SENDQ ;-- exit
+1 QUIT RESULT
+2 ;
EXIT ;-- Cleanup
+1 QUIT
+2 ;
LOG(ST1,OP) ;Log reads/writes (translates ctrls)
+1 ;ST1=string to file
+2 ;OP=operation "read" or "write"
+3 IF $GET(HLTRACE)
Begin DoDot:1
+4 NEW X
SET X=$GET(^XTMP("HL",0))
SET $PIECE(X,U)=DT+1
SET $PIECE(X,U,2)=DT
+5 SET $PIECE(X,U,3)="HL7 Debug Log"
SET HLLOG=$PIECE(X,U,4)
+6 SET HLN=$$TRANS(ST1)
+7 SET HLLOG=HLLOG+1
SET ^XTMP("HL",HLLOG)=OP_HLN
SET $PIECE(X,U,4)=HLLOG
+8 SET ^XTMP("HL",0)=X
End DoDot:1
+9 QUIT
TRANS(ST) ;Translate controls in string
+1 ;ST=String containing embedded x3.28 control characters
+2 SET ST2=""
FOR I=1:1:$LENGTH(ST)
SET J=$EXTRACT(ST,I)
Begin DoDot:1
+3 IF $DATA(HLCTRL($ASCII(J)))
SET J=HLCTRL($ASCII(J))
+4 SET ST2=$GET(ST2)_J
End DoDot:1
+5 QUIT ST2