HLOTCP ;ALB/CJM- TCP/IP I/O - 10/4/94 1pm ;03/01/2011
 ;;1.6;HEALTH LEVEL SEVEN;**126,131,134,137,138,139,146,153**;Oct 13, 1995;Build 11
 ;Per VHA Directive 2004-038, this routine should not be modified.
 ;
OPEN(HLCSTATE,LOGICAL) ;
 ;This may be called either in the context of a client or a server.
 ;For the server, there are 3 situations:
 ; 1) The server is not concurrent.  In this case the TCP device should be opened.
 ; 2) The server is concurrent, but this process was spawned by the OS
 ;    (via a VMS TCP Service)  In this case, the device should be opened
 ;    via the LOGICAL that was passed in.
 ;  3) The server is concurrent, but this process was spawned by the
 ;     TaskMan multi-listener.  In this case TaskMan already opened the
 ;     device.  This case can be determined by the absence of the LOGICAL
 ;     input parameter.
 ;
 N IP,PORT,DNSFLAG
 ;
 S DNSFLAG=0 ;DNS has not been contacted for IP
 ;
 S:'$G(HLCSTATE("SERVER")) IP=HLCSTATE("LINK","IP")
 S PORT=HLCSTATE("LINK","PORT")
 S HLCSTATE("CONNECTED")=0
 S HLCSTATE("READ HEADER")="READHDR^HLOTCP"
 S HLCSTATE("WRITE HEADER")="WRITEHDR^HLOTCP"
 S HLCSTATE("READ SEGMENT")="READSEG^HLOTCP"
 S HLCSTATE("WRITE SEGMENT")="WRITESEG^HLOTCP"
 S HLCSTATE("END MESSAGE")="ENDMSG^HLOTCP"
 S HLCSTATE("CLOSE")="CLOSE^HLOTCP"
 S HLCSTATE("TCP BUFFER")=""
 S HLCSTATE("TCP BUFFER $X")=0
 ;
 ;spawned by TaskMan multi-listener? If so, the device has already been opened
 I $G(HLCSTATE("SERVER")),$G(HLCSTATE("LINK","SERVER"))="1^M",$G(LOGICAL)="" D  Q
 .S HLCSTATE("DEVICE")=IO(0),HLCSTATE("FLUSH")="!",HLCSTATE("TCP BUFFER SIZE")=512
 .S HLCSTATE("CONNECTED")=1
 ;
 ;if no IP, not a server, give DNS a shot
 I '$G(HLCSTATE("SERVER")),IP="" S DNSFLAG=1,IP=$$DNS(HLCSTATE("LINK","DOMAIN")),HLCSTATE("LINK","IP")=IP Q:IP=""
 ;
RETRY ;
 ;
 I HLCSTATE("SYSTEM","OS")="DSM" D
 .S HLCSTATE("TCP BUFFER SIZE")=512
 .I $G(LOGICAL)]"" S HLCSTATE("DEVICE")=LOGICAL
 .E  S HLCSTATE("DEVICE")=PORT
 .S HLCSTATE("FLUSH")="!"
 .I $G(HLCSTATE("SERVER")) D
 ..O:$G(LOGICAL)]"" HLCSTATE("DEVICE"):(TCPDEV,BLOCKSIZE=512):HLCSTATE("OPEN TIMEOUT")
 ..O:$G(LOGICAL)="" HLCSTATE("DEVICE"):(TCPCHAN,BLOCKSIZE=512):HLCSTATE("OPEN TIMEOUT")
 ..I $T D
 ...S HLCSTATE("CONNECTED")=1
 ...U HLCSTATE("DEVICE"):NOECHO
 .E  D  ;client
 ..O HLCSTATE("DEVICE"):(TCPCHAN,ADDRESS=IP,BLOCKSIZE=512):HLCSTATE("OPEN TIMEOUT")
 ..I $T D
 ...S HLCSTATE("CONNECTED")=1
 ...U HLCSTATE("DEVICE"):NOECHO
 E  I HLCSTATE("SYSTEM","OS")="CACHE" D
 .S HLCSTATE("FLUSH")="!"
 .I $G(LOGICAL)]"" S HLCSTATE("DEVICE")=LOGICAL
 .E  S HLCSTATE("DEVICE")="|TCP|"_PORT
 .S HLCSTATE("TCP BUFFER SIZE")=512
 .I $G(HLCSTATE("SERVER")) D
 ..I HLCSTATE("SERVER")="1^S" D  Q
 ...;single server (no concurrent connections)
ZB25 ...;
 ...O HLCSTATE("DEVICE"):(:PORT:"+A-S":::):HLCSTATE("OPEN TIMEOUT")
 ...I $T D
 ....N A
ZB26 ....S HLCSTATE("CONNECTED")=1
 ....U HLCSTATE("DEVICE")
 ....F  R *A:HLCSTATE("READ TIMEOUT") Q:$T  I $$CHKSTOP^HLOPROC S HLCSTATE("CONNECTED")=0 D CLOSE(.HLCSTATE) Q
ZB27 ....;
 ...E  D
ZB28 ....;
 ..;multi-server spawned by OS - VMS TCP Services
 ..O HLCSTATE("DEVICE")::HLCSTATE("OPEN TIMEOUT") I '$T S HLCSTATE("CONNECTED")=0 Q
 ..S HLCSTATE("CONNECTED")=1
 ..U HLCSTATE("DEVICE"):(::"-S")
 ..;
 .E  D  ;client
 ..S HLCSTATE("TCP BUFFER SIZE")=512
 ..O HLCSTATE("DEVICE"):(IP:PORT:"-S":::):HLCSTATE("OPEN TIMEOUT")
 ..I $T D
 ...S HLCSTATE("CONNECTED")=1
 E  D  ;any other system but Cache or DSM
 .S HLCSTATE("TCP BUFFER SIZE")=256
 .D CALL^%ZISTCP(IP,PORT,HLCSTATE("OPEN TIMEOUT"))
 .S HLCSTATE("CONNECTED")='POP
 .I HLCSTATE("CONNECTED") S HLCSTATE("DEVICE")=IO
 ;
 ;if not connected, not the server, give DNS a shot if not tried already
 I '$G(HLCSTATE("SERVER")),'HLCSTATE("CONNECTED"),'DNSFLAG S DNSFLAG=1,IP=$$DNS(HLCSTATE("LINK","DOMAIN")) I IP]"",IP'=HLCSTATE("LINK","IP") S HLCSTATE("LINK","IP")=IP Q:IP=""  G RETRY
 ;
 I HLCSTATE("CONNECTED"),DNSFLAG S $P(^HLCS(870,HLCSTATE("LINK","IEN"),400),"^")=IP
 Q
 ;
DNS(DOMAIN) ;
 Q $P($$ADDRESS^XLFNSLK(DOMAIN),",")
 ;
WRITEHDR(HLCSTATE,HDR) ;
 ;
 ;insure that package buffer is empty
 K HLCSTATE("BUFFER")
 S HLCSTATE("BUFFER","BYTE COUNT")=0
 S HLCSTATE("BUFFER","SEGMENT COUNT")=0
 ;
 ;Start the message with <SB>, then write the header
 N SEG
 S SEG(1)=$C(11)_HDR(1)
 S SEG(2)=HDR(2)
 Q $$WRITESEG(.HLCSTATE,.SEG)
 ;
WRITESEG(HLCSTATE,SEG) ;
 N I,LAST
 S HLCSTATE("BUFFER","SEGMENT COUNT")=HLCSTATE("BUFFER","SEGMENT COUNT")+1
 S I=0,LAST=$O(SEG(99999),-1)
 F  S I=$O(SEG(I)) Q:'I  D
 .I HLCSTATE("BUFFER","BYTE COUNT")>HLCSTATE("SYSTEM","BUFFER") D FLUSH
 .I I=LAST S SEG(I)=SEG(I)_$C(13)
 .S HLCSTATE("BUFFER",HLCSTATE("BUFFER","SEGMENT COUNT"),I)=SEG(I),HLCSTATE("BUFFER","BYTE COUNT")=HLCSTATE("BUFFER","BYTE COUNT")+$L(SEG(I))+20
 Q HLCSTATE("CONNECTED")
 ;
FLUSH ;flushes the HL7 package buffer, and the system TCP buffer when full
 N SEGMENT,MAX
 S SEGMENT=0
 ;
 S MAX=HLCSTATE("TCP BUFFER SIZE")-2
 ;
 U HLCSTATE("DEVICE") I (HLCSTATE("SYSTEM","OS")="CACHE") S HLCSTATE("CONNECTED")=($ZA\8192#2) I 'HLCSTATE("CONNECTED") D CLOSE(.HLCSTATE)
 F  S SEGMENT=$O(HLCSTATE("BUFFER",SEGMENT)) Q:'SEGMENT  D
 .N I S I=0
 .F  S I=$O(HLCSTATE("BUFFER",SEGMENT,I)) Q:'I  D
 ..N LINE
 ..S LINE=HLCSTATE("BUFFER",SEGMENT,I)
 ..;put the line in the TCP buffer, or as much as will fit - flush the buffer when it gets full
 ..F  Q:LINE=""  D
 ...N INC
 ...;INC is how much space is left in the buffer
 ...S INC=MAX-HLCSTATE("TCP BUFFER $X")
 ...I '($L(LINE)>INC) D
 ....S HLCSTATE("TCP BUFFER")=HLCSTATE("TCP BUFFER")_LINE
 ....S HLCSTATE("TCP BUFFER $X")=HLCSTATE("TCP BUFFER $X")+$L(LINE)
 ....S LINE=""
 ...E  D
 ....S HLCSTATE("TCP BUFFER")=HLCSTATE("TCP BUFFER")_$E(LINE,1,INC)
 ....S HLCSTATE("TCP BUFFER $X")=MAX
 ....S LINE=$E(LINE,INC+1,99999)
 ...I HLCSTATE("TCP BUFFER $X")=MAX D
 ....W HLCSTATE("TCP BUFFER"),@HLCSTATE("FLUSH")
 ....S HLCSTATE("TCP BUFFER")="",HLCSTATE("TCP BUFFER $X")=0
 K HLCSTATE("BUFFER")
 S HLCSTATE("BUFFER","SEGMENT COUNT")=1
 S HLCSTATE("BUFFER","BYTE COUNT")=0
 Q
 ;
READSEG(HLCSTATE,SEG) ;
 ;
 ;Output:
 ;  SEG - returns the segment (pass by reference)
 ;  Function returns 1 on success, 0 on failure
 ;
 K SEG
 ;**START P139 CJM - if the header segment has been read, and <EB> is encountered before the <CR>, then accept whatever came before <EB> as a segment
 Q:HLCSTATE("MESSAGE ENDED") 0
 ;**END P139
 ;
 N SUCCESS,COUNT,BUF
 S (COUNT,SUCCESS)=0
 ;
 ;anything left from last read?
 S BUF=HLCSTATE("READ")
 S HLCSTATE("READ")=""
 I BUF]"" D  ;something was left!
 .S COUNT=1
 .I BUF[$C(13) D  Q
 ..S SEG(1)=$P(BUF,$C(13)),BUF=$P(BUF,$C(13),2,999999)
 ..S SUCCESS=1
 .;**START P139 CJM
 .I HLCSTATE("MESSAGE STARTED"),BUF[$C(28) D  Q
 ..S SEG(1)=$P(BUF,$C(28)),BUF=$P(BUF,$C(28),2,999999)
 ..S SUCCESS=1
 ..S HLCSTATE("MESSAGE ENDED")=1
 .;**END P139 CJM
 .S SEG(1)=BUF,BUF=""
 ;
 ; *** Begin HL*1.6*146 - RBN ***
 ;I 'SUCCESS U HLCSTATE("DEVICE") F  R BUF:HLCSTATE("READ TIMEOUT") Q:'$T D Q:SUCCESS
 I 'SUCCESS U HLCSTATE("DEVICE") F  Q:'$$READ(.BUF)  D  Q:SUCCESS
 .;** End HL*1.6*146 - RBN ***
 .;
 .I BUF[$C(13) S SUCCESS=1,COUNT=COUNT+1,SEG(COUNT)=$P(BUF,$C(13)),BUF=$P(BUF,$C(13),2,999999) Q
 .;
 .;**START P139 CJM
 .I HLCSTATE("MESSAGE STARTED"),BUF[$C(28) S SUCCESS=1,HLCSTATE("MESSAGE ENDED")=1,COUNT=COUNT+1,SEG(COUNT)=$P(BUF,$C(28)),BUF=$P(BUF,$C(28),2,999999) Q
 .;**END P139 CJM
 .;
 .S COUNT=COUNT+1,SEG(COUNT)=BUF
 ;
 I SUCCESS D
 .S HLCSTATE("READ")=BUF ;save the leftover
 .I COUNT>1,SEG(COUNT)="" K SEG(COUNT) S COUNT=COUNT-1
 ;Cache can return the connection status
 E  I (HLCSTATE("SYSTEM","OS")="CACHE") S HLCSTATE("CONNECTED")=($ZA\8192#2) I 'HLCSTATE("CONNECTED") D CLOSE(.HLCSTATE)
 ;
 ;if the <EB> character was encountered, then there are no more segments in the message, set the end of message flag
 I SUCCESS,SEG(COUNT)[$C(28) D
 .K SEG
 .S SUCCESS=0
 .S HLCSTATE("MESSAGE ENDED")=1
 Q SUCCESS
 ;
READHDR(HLCSTATE,HDR) ;
 ;reads the next header segment in the message stream, discarding everything that comes before it
 ;
 N SEG,SUCCESS,J,I
 S SUCCESS=0
 K HDR
 F  Q:'$$READSEG(.HLCSTATE,.SEG)  D  Q:SUCCESS
 .S I=0
 .;look for the <SB>
 .;perhaps the <SB> isn't in the first line
 .F  S I=$O(SEG(I)) Q:'I  D  Q:SUCCESS
 ..I (SEG(I)'[$C(11)) K SEG(I) Q
 ..S SEG(I)=$P(SEG(I),$C(11),2)
 ..S SUCCESS=1
 ..K:SEG(I)="" SEG(I)
 I SUCCESS S (I,J)=0 F  S J=$O(SEG(J)) Q:'J  S I=I+1,HDR(I)=SEG(J)
 Q SUCCESS
 ;
CLOSE(HLCSTATE) ;
 CLOSE HLCSTATE("DEVICE")
 ;**P146 START CJM
 I $G(HLCSTATE("READ TIMEOUT","CHANGED")) D PUTTIME(.HLCSTATE)
 ;**P146 END CJM
 Q
 ;
ENDMSG(HLCSTATE)        ;
 N SEG
 S SEG(1)=$C(28)
 I $$WRITESEG(.HLCSTATE,.SEG) D  Q 1
 .D FLUSH
 .I HLCSTATE("TCP BUFFER $X") D
 ..U HLCSTATE("DEVICE")
 ..W HLCSTATE("TCP BUFFER"),@HLCSTATE("FLUSH")
 ..S HLCSTATE("TCP BUFFER")=""
 ..S HLCSTATE("TCP BUFFER $X")=0
 Q 0
 ;
 ;**P146 START CJM
READ(BUF) ;
 ;Performs a READ to BUF and returns the $T result as the function value.
 ;For client reads the timeout value is dynamically adjusted based
 ;on a random sample. For server reads the timeout is static.
 ;
 ;
ZB31 ;
 N RETURN
 S RETURN=0
 ;for the server the timeout is static
 I $G(HLCSTATE("SERVER")) D
 .R BUF:HLCSTATE("READ TIMEOUT")
 .S RETURN=$T
 ;
 E  D  ;client
 .I ($R(100)<10) D
 ..;measure how long the READ really takes
 .. N T1,T2
 .. S T1=$$NOW^XLFDT
 .. R BUF:100
 ..I $T D
 ...S RETURN=1
 ...S T2=$$NOW^XLFDT
 ...D SETTIME($$FMDIFF^XLFDT(T2,T1,2))
 ..E  D
 ...S RETURN=0
 .E  D
 ..R BUF:HLCSTATE("READ TIMEOUT")
 ..S RETURN=$T
ZB32 ;
 ;
 Q RETURN
 ;
SETTIME(TIME) ;
 ;Re-sets the Read Timeout based on an algorithm that uses the
 ;new read time + the prior 4 historical values.
 ;
 N MAX,I,TEMP
 S HLCSTATE("READ TIMEOUT","HISTORICAL")=TIME_"^"_$P(HLCSTATE("READ TIMEOUT","HISTORICAL"),"^",1,4)
 S MAX=0
 F I=1:1:5 S TEMP=$P(HLCSTATE("READ TIMEOUT","HISTORICAL"),"^",I) I TEMP>MAX S MAX=TEMP
 S TEMP=MAX+5
 I TEMP<20 S TEMP=20
 I TEMP>60 S TEMP=60
 S HLCSTATE("READ TIMEOUT")=TEMP
 S HLCSTATE("READ TIMEOUT","CHANGED")=1
 Q
 ;
GETTIME(HLCSTATE) ;
 ;Gets from ^HLTMP the current read timeout for the client link and the
 ;historical data that the timeout value is based on.
 N DATA
 S DATA=$G(^HLTMP("READ TIMEOUT",HLCSTATE("LINK","NAME")))
 I $P(DATA,"^")<20 D
 .S HLCSTATE("READ TIMEOUT")=20
 E  D
 .S HLCSTATE("READ TIMEOUT")=$P(DATA,"^")
 S HLCSTATE("READ TIMEOUT","HISTORICAL")=$P(DATA,"^",2,6)
 S HLCSTATE("READ TIMEOUT","CHANGED")=0
 Q
 ;
PUTTIME(HLCSTATE) ;
 ;Stores to ^HLTMP the current read timeout for the client link and
 ;the historical data that the timeout value is based on.
 S ^HLTMP("READ TIMEOUT",HLCSTATE("LINK","NAME"))=HLCSTATE("READ TIMEOUT")_"^"_HLCSTATE("READ TIMEOUT","HISTORICAL")
 Q
 ;**P146 END CJM
 
--- Routine Detail   --- with STRUCTURED ROUTINE LISTING ---[H[J[2J[HHLOTCP   10975     printed  Sep 23, 2025@19:35:19                                                                                                                                                                                                     Page 2
HLOTCP    ;ALB/CJM- TCP/IP I/O - 10/4/94 1pm ;03/01/2011
 +1       ;;1.6;HEALTH LEVEL SEVEN;**126,131,134,137,138,139,146,153**;Oct 13, 1995;Build 11
 +2       ;Per VHA Directive 2004-038, this routine should not be modified.
 +3       ;
OPEN(HLCSTATE,LOGICAL) ;
 +1       ;This may be called either in the context of a client or a server.
 +2       ;For the server, there are 3 situations:
 +3       ; 1) The server is not concurrent.  In this case the TCP device should be opened.
 +4       ; 2) The server is concurrent, but this process was spawned by the OS
 +5       ;    (via a VMS TCP Service)  In this case, the device should be opened
 +6       ;    via the LOGICAL that was passed in.
 +7       ;  3) The server is concurrent, but this process was spawned by the
 +8       ;     TaskMan multi-listener.  In this case TaskMan already opened the
 +9       ;     device.  This case can be determined by the absence of the LOGICAL
 +10      ;     input parameter.
 +11      ;
 +12       NEW IP,PORT,DNSFLAG
 +13      ;
 +14      ;DNS has not been contacted for IP
           SET DNSFLAG=0
 +15      ;
 +16       if '$GET(HLCSTATE("SERVER"))
               SET IP=HLCSTATE("LINK","IP")
 +17       SET PORT=HLCSTATE("LINK","PORT")
 +18       SET HLCSTATE("CONNECTED")=0
 +19       SET HLCSTATE("READ HEADER")="READHDR^HLOTCP"
 +20       SET HLCSTATE("WRITE HEADER")="WRITEHDR^HLOTCP"
 +21       SET HLCSTATE("READ SEGMENT")="READSEG^HLOTCP"
 +22       SET HLCSTATE("WRITE SEGMENT")="WRITESEG^HLOTCP"
 +23       SET HLCSTATE("END MESSAGE")="ENDMSG^HLOTCP"
 +24       SET HLCSTATE("CLOSE")="CLOSE^HLOTCP"
 +25       SET HLCSTATE("TCP BUFFER")=""
 +26       SET HLCSTATE("TCP BUFFER $X")=0
 +27      ;
 +28      ;spawned by TaskMan multi-listener? If so, the device has already been opened
 +29       IF $GET(HLCSTATE("SERVER"))
               IF $GET(HLCSTATE("LINK","SERVER"))="1^M"
                   IF $GET(LOGICAL)=""
                       Begin DoDot:1
 +30                       SET HLCSTATE("DEVICE")=IO(0)
                           SET HLCSTATE("FLUSH")="!"
                           SET HLCSTATE("TCP BUFFER SIZE")=512
 +31                       SET HLCSTATE("CONNECTED")=1
                       End DoDot:1
                       QUIT 
 +32      ;
 +33      ;if no IP, not a server, give DNS a shot
 +34       IF '$GET(HLCSTATE("SERVER"))
               IF IP=""
                   SET DNSFLAG=1
                   SET IP=$$DNS(HLCSTATE("LINK","DOMAIN"))
                   SET HLCSTATE("LINK","IP")=IP
                   if IP=""
                       QUIT 
 +35      ;
RETRY     ;
 +1       ;
 +2        IF HLCSTATE("SYSTEM","OS")="DSM"
               Begin DoDot:1
 +3                SET HLCSTATE("TCP BUFFER SIZE")=512
 +4                IF $GET(LOGICAL)]""
                       SET HLCSTATE("DEVICE")=LOGICAL
 +5               IF '$TEST
                       SET HLCSTATE("DEVICE")=PORT
 +6                SET HLCSTATE("FLUSH")="!"
 +7                IF $GET(HLCSTATE("SERVER"))
                       Begin DoDot:2
 +8                        if $GET(LOGICAL)]""
                               OPEN HLCSTATE("DEVICE"):(TCPDEV,BLOCKSIZE=512):HLCSTATE("OPEN TIMEOUT")
 +9                        if $GET(LOGICAL)=""
                               OPEN HLCSTATE("DEVICE"):(TCPCHAN,BLOCKSIZE=512):HLCSTATE("OPEN TIMEOUT")
 +10                       IF $TEST
                               Begin DoDot:3
 +11                               SET HLCSTATE("CONNECTED")=1
 +12                               USE HLCSTATE("DEVICE"):NOECHO
                               End DoDot:3
                       End DoDot:2
 +13      ;client
                  IF '$TEST
                       Begin DoDot:2
 +14                       OPEN HLCSTATE("DEVICE"):(TCPCHAN,ADDRESS=IP,BLOCKSIZE=512):HLCSTATE("OPEN TIMEOUT")
 +15                       IF $TEST
                               Begin DoDot:3
 +16                               SET HLCSTATE("CONNECTED")=1
 +17                               USE HLCSTATE("DEVICE"):NOECHO
                               End DoDot:3
                       End DoDot:2
               End DoDot:1
 +18      IF '$TEST
               IF HLCSTATE("SYSTEM","OS")="CACHE"
                   Begin DoDot:1
 +19                   SET HLCSTATE("FLUSH")="!"
 +20                   IF $GET(LOGICAL)]""
                           SET HLCSTATE("DEVICE")=LOGICAL
 +21                  IF '$TEST
                           SET HLCSTATE("DEVICE")="|TCP|"_PORT
 +22                   SET HLCSTATE("TCP BUFFER SIZE")=512
 +23                   IF $GET(HLCSTATE("SERVER"))
                           Begin DoDot:2
 +24                           IF HLCSTATE("SERVER")="1^S"
                                   Begin DoDot:3
 +25      ;single server (no concurrent connections)
ZB25      ;
 +1                                    OPEN HLCSTATE("DEVICE"):(:PORT:"+A-S":::):HLCSTATE("OPEN TIMEOUT")
 +2                                    IF $TEST
                                           Begin DoDot:4
 +3                                            NEW A
ZB26                                           SET HLCSTATE("CONNECTED")=1
 +1                                            USE HLCSTATE("DEVICE")
 +2                                            FOR 
                                                   READ *A:HLCSTATE("READ TIMEOUT")
                                                   if $TEST
                                                       QUIT 
                                                   IF $$CHKSTOP^HLOPROC
                                                       SET HLCSTATE("CONNECTED")=0
                                                       DO CLOSE(.HLCSTATE)
                                                       QUIT 
ZB27      ;
                                           End DoDot:4
 +1                                   IF '$TEST
                                           Begin DoDot:4
ZB28      ;
                                           End DoDot:4
                                   End DoDot:3
                                   QUIT 
 +1       ;multi-server spawned by OS - VMS TCP Services
 +2                            OPEN HLCSTATE("DEVICE")::HLCSTATE("OPEN TIMEOUT")
                               IF '$TEST
                                   SET HLCSTATE("CONNECTED")=0
                                   QUIT 
 +3                            SET HLCSTATE("CONNECTED")=1
 +4                            USE HLCSTATE("DEVICE"):(::"-S")
 +5       ;
                           End DoDot:2
 +6       ;client
                      IF '$TEST
                           Begin DoDot:2
 +7                            SET HLCSTATE("TCP BUFFER SIZE")=512
 +8                            OPEN HLCSTATE("DEVICE"):(IP:PORT:"-S":::):HLCSTATE("OPEN TIMEOUT")
 +9                            IF $TEST
                                   Begin DoDot:3
 +10                                   SET HLCSTATE("CONNECTED")=1
                                   End DoDot:3
                           End DoDot:2
                   End DoDot:1
 +11      ;any other system but Cache or DSM
          IF '$TEST
               Begin DoDot:1
 +12               SET HLCSTATE("TCP BUFFER SIZE")=256
 +13               DO CALL^%ZISTCP(IP,PORT,HLCSTATE("OPEN TIMEOUT"))
 +14               SET HLCSTATE("CONNECTED")='POP
 +15               IF HLCSTATE("CONNECTED")
                       SET HLCSTATE("DEVICE")=IO
               End DoDot:1
 +16      ;
 +17      ;if not connected, not the server, give DNS a shot if not tried already
 +18       IF '$GET(HLCSTATE("SERVER"))
               IF 'HLCSTATE("CONNECTED")
                   IF 'DNSFLAG
                       SET DNSFLAG=1
                       SET IP=$$DNS(HLCSTATE("LINK","DOMAIN"))
                       IF IP]""
                           IF IP'=HLCSTATE("LINK","IP")
                               SET HLCSTATE("LINK","IP")=IP
                               if IP=""
                                   QUIT 
                               GOTO RETRY
 +19      ;
 +20       IF HLCSTATE("CONNECTED")
               IF DNSFLAG
                   SET $PIECE(^HLCS(870,HLCSTATE("LINK","IEN"),400),"^")=IP
 +21       QUIT 
 +22      ;
DNS(DOMAIN) ;
 +1        QUIT $PIECE($$ADDRESS^XLFNSLK(DOMAIN),",")
 +2       ;
WRITEHDR(HLCSTATE,HDR) ;
 +1       ;
 +2       ;insure that package buffer is empty
 +3        KILL HLCSTATE("BUFFER")
 +4        SET HLCSTATE("BUFFER","BYTE COUNT")=0
 +5        SET HLCSTATE("BUFFER","SEGMENT COUNT")=0
 +6       ;
 +7       ;Start the message with <SB>, then write the header
 +8        NEW SEG
 +9        SET SEG(1)=$CHAR(11)_HDR(1)
 +10       SET SEG(2)=HDR(2)
 +11       QUIT $$WRITESEG(.HLCSTATE,.SEG)
 +12      ;
WRITESEG(HLCSTATE,SEG) ;
 +1        NEW I,LAST
 +2        SET HLCSTATE("BUFFER","SEGMENT COUNT")=HLCSTATE("BUFFER","SEGMENT COUNT")+1
 +3        SET I=0
           SET LAST=$ORDER(SEG(99999),-1)
 +4        FOR 
               SET I=$ORDER(SEG(I))
               if 'I
                   QUIT 
               Begin DoDot:1
 +5                IF HLCSTATE("BUFFER","BYTE COUNT")>HLCSTATE("SYSTEM","BUFFER")
                       DO FLUSH
 +6                IF I=LAST
                       SET SEG(I)=SEG(I)_$CHAR(13)
 +7                SET HLCSTATE("BUFFER",HLCSTATE("BUFFER","SEGMENT COUNT"),I)=SEG(I)
                   SET HLCSTATE("BUFFER","BYTE COUNT")=HLCSTATE("BUFFER","BYTE COUNT")+$LENGTH(SEG(I))+20
               End DoDot:1
 +8        QUIT HLCSTATE("CONNECTED")
 +9       ;
FLUSH     ;flushes the HL7 package buffer, and the system TCP buffer when full
 +1        NEW SEGMENT,MAX
 +2        SET SEGMENT=0
 +3       ;
 +4        SET MAX=HLCSTATE("TCP BUFFER SIZE")-2
 +5       ;
 +6        USE HLCSTATE("DEVICE")
           IF (HLCSTATE("SYSTEM","OS")="CACHE")
               SET HLCSTATE("CONNECTED")=($ZA\8192#2)
               IF 'HLCSTATE("CONNECTED")
                   DO CLOSE(.HLCSTATE)
 +7        FOR 
               SET SEGMENT=$ORDER(HLCSTATE("BUFFER",SEGMENT))
               if 'SEGMENT
                   QUIT 
               Begin DoDot:1
 +8                NEW I
                   SET I=0
 +9                FOR 
                       SET I=$ORDER(HLCSTATE("BUFFER",SEGMENT,I))
                       if 'I
                           QUIT 
                       Begin DoDot:2
 +10                       NEW LINE
 +11                       SET LINE=HLCSTATE("BUFFER",SEGMENT,I)
 +12      ;put the line in the TCP buffer, or as much as will fit - flush the buffer when it gets full
 +13                       FOR 
                               if LINE=""
                                   QUIT 
                               Begin DoDot:3
 +14                               NEW INC
 +15      ;INC is how much space is left in the buffer
 +16                               SET INC=MAX-HLCSTATE("TCP BUFFER $X")
 +17                               IF '($LENGTH(LINE)>INC)
                                       Begin DoDot:4
 +18                                       SET HLCSTATE("TCP BUFFER")=HLCSTATE("TCP BUFFER")_LINE
 +19                                       SET HLCSTATE("TCP BUFFER $X")=HLCSTATE("TCP BUFFER $X")+$LENGTH(LINE)
 +20                                       SET LINE=""
                                       End DoDot:4
 +21                              IF '$TEST
                                       Begin DoDot:4
 +22                                       SET HLCSTATE("TCP BUFFER")=HLCSTATE("TCP BUFFER")_$EXTRACT(LINE,1,INC)
 +23                                       SET HLCSTATE("TCP BUFFER $X")=MAX
 +24                                       SET LINE=$EXTRACT(LINE,INC+1,99999)
                                       End DoDot:4
 +25                               IF HLCSTATE("TCP BUFFER $X")=MAX
                                       Begin DoDot:4
 +26                                       WRITE HLCSTATE("TCP BUFFER"),@HLCSTATE("FLUSH")
 +27                                       SET HLCSTATE("TCP BUFFER")=""
                                           SET HLCSTATE("TCP BUFFER $X")=0
                                       End DoDot:4
                               End DoDot:3
                       End DoDot:2
               End DoDot:1
 +28       KILL HLCSTATE("BUFFER")
 +29       SET HLCSTATE("BUFFER","SEGMENT COUNT")=1
 +30       SET HLCSTATE("BUFFER","BYTE COUNT")=0
 +31       QUIT 
 +32      ;
READSEG(HLCSTATE,SEG) ;
 +1       ;
 +2       ;Output:
 +3       ;  SEG - returns the segment (pass by reference)
 +4       ;  Function returns 1 on success, 0 on failure
 +5       ;
 +6        KILL SEG
 +7       ;**START P139 CJM - if the header segment has been read, and <EB> is encountered before the <CR>, then accept whatever came before <EB> as a segment
 +8        if HLCSTATE("MESSAGE ENDED")
               QUIT 0
 +9       ;**END P139
 +10      ;
 +11       NEW SUCCESS,COUNT,BUF
 +12       SET (COUNT,SUCCESS)=0
 +13      ;
 +14      ;anything left from last read?
 +15       SET BUF=HLCSTATE("READ")
 +16       SET HLCSTATE("READ")=""
 +17      ;something was left!
           IF BUF]""
               Begin DoDot:1
 +18               SET COUNT=1
 +19               IF BUF[$CHAR(13)
                       Begin DoDot:2
 +20                       SET SEG(1)=$PIECE(BUF,$CHAR(13))
                           SET BUF=$PIECE(BUF,$CHAR(13),2,999999)
 +21                       SET SUCCESS=1
                       End DoDot:2
                       QUIT 
 +22      ;**START P139 CJM
 +23               IF HLCSTATE("MESSAGE STARTED")
                       IF BUF[$CHAR(28)
                           Begin DoDot:2
 +24                           SET SEG(1)=$PIECE(BUF,$CHAR(28))
                               SET BUF=$PIECE(BUF,$CHAR(28),2,999999)
 +25                           SET SUCCESS=1
 +26                           SET HLCSTATE("MESSAGE ENDED")=1
                           End DoDot:2
                           QUIT 
 +27      ;**END P139 CJM
 +28               SET SEG(1)=BUF
                   SET BUF=""
               End DoDot:1
 +29      ;
 +30      ; *** Begin HL*1.6*146 - RBN ***
 +31      ;I 'SUCCESS U HLCSTATE("DEVICE") F  R BUF:HLCSTATE("READ TIMEOUT") Q:'$T D Q:SUCCESS
 +32       IF 'SUCCESS
               USE HLCSTATE("DEVICE")
               FOR 
                   if '$$READ(.BUF)
                       QUIT 
                   Begin DoDot:1
 +33      ;** End HL*1.6*146 - RBN ***
 +34      ;
 +35                   IF BUF[$CHAR(13)
                           SET SUCCESS=1
                           SET COUNT=COUNT+1
                           SET SEG(COUNT)=$PIECE(BUF,$CHAR(13))
                           SET BUF=$PIECE(BUF,$CHAR(13),2,999999)
                           QUIT 
 +36      ;
 +37      ;**START P139 CJM
 +38                   IF HLCSTATE("MESSAGE STARTED")
                           IF BUF[$CHAR(28)
                               SET SUCCESS=1
                               SET HLCSTATE("MESSAGE ENDED")=1
                               SET COUNT=COUNT+1
                               SET SEG(COUNT)=$PIECE(BUF,$CHAR(28))
                               SET BUF=$PIECE(BUF,$CHAR(28),2,999999)
                               QUIT 
 +39      ;**END P139 CJM
 +40      ;
 +41                   SET COUNT=COUNT+1
                       SET SEG(COUNT)=BUF
                   End DoDot:1
                   if SUCCESS
                       QUIT 
 +42      ;
 +43       IF SUCCESS
               Begin DoDot:1
 +44      ;save the leftover
                   SET HLCSTATE("READ")=BUF
 +45               IF COUNT>1
                       IF SEG(COUNT)=""
                           KILL SEG(COUNT)
                           SET COUNT=COUNT-1
               End DoDot:1
 +46      ;Cache can return the connection status
 +47      IF '$TEST
               IF (HLCSTATE("SYSTEM","OS")="CACHE")
                   SET HLCSTATE("CONNECTED")=($ZA\8192#2)
                   IF 'HLCSTATE("CONNECTED")
                       DO CLOSE(.HLCSTATE)
 +48      ;
 +49      ;if the <EB> character was encountered, then there are no more segments in the message, set the end of message flag
 +50       IF SUCCESS
               IF SEG(COUNT)[$CHAR(28)
                   Begin DoDot:1
 +51                   KILL SEG
 +52                   SET SUCCESS=0
 +53                   SET HLCSTATE("MESSAGE ENDED")=1
                   End DoDot:1
 +54       QUIT SUCCESS
 +55      ;
READHDR(HLCSTATE,HDR) ;
 +1       ;reads the next header segment in the message stream, discarding everything that comes before it
 +2       ;
 +3        NEW SEG,SUCCESS,J,I
 +4        SET SUCCESS=0
 +5        KILL HDR
 +6        FOR 
               if '$$READSEG(.HLCSTATE,.SEG)
                   QUIT 
               Begin DoDot:1
 +7                SET I=0
 +8       ;look for the <SB>
 +9       ;perhaps the <SB> isn't in the first line
 +10               FOR 
                       SET I=$ORDER(SEG(I))
                       if 'I
                           QUIT 
                       Begin DoDot:2
 +11                       IF (SEG(I)'[$CHAR(11))
                               KILL SEG(I)
                               QUIT 
 +12                       SET SEG(I)=$PIECE(SEG(I),$CHAR(11),2)
 +13                       SET SUCCESS=1
 +14                       if SEG(I)=""
                               KILL SEG(I)
                       End DoDot:2
                       if SUCCESS
                           QUIT 
               End DoDot:1
               if SUCCESS
                   QUIT 
 +15       IF SUCCESS
               SET (I,J)=0
               FOR 
                   SET J=$ORDER(SEG(J))
                   if 'J
                       QUIT 
                   SET I=I+1
                   SET HDR(I)=SEG(J)
 +16       QUIT SUCCESS
 +17      ;
CLOSE(HLCSTATE) ;
 +1        CLOSE HLCSTATE("DEVICE")
 +2       ;**P146 START CJM
 +3        IF $GET(HLCSTATE("READ TIMEOUT","CHANGED"))
               DO PUTTIME(.HLCSTATE)
 +4       ;**P146 END CJM
 +5        QUIT 
 +6       ;
ENDMSG(HLCSTATE) ;
 +1        NEW SEG
 +2        SET SEG(1)=$CHAR(28)
 +3        IF $$WRITESEG(.HLCSTATE,.SEG)
               Begin DoDot:1
 +4                DO FLUSH
 +5                IF HLCSTATE("TCP BUFFER $X")
                       Begin DoDot:2
 +6                        USE HLCSTATE("DEVICE")
 +7                        WRITE HLCSTATE("TCP BUFFER"),@HLCSTATE("FLUSH")
 +8                        SET HLCSTATE("TCP BUFFER")=""
 +9                        SET HLCSTATE("TCP BUFFER $X")=0
                       End DoDot:2
               End DoDot:1
               QUIT 1
 +10       QUIT 0
 +11      ;
 +12      ;**P146 START CJM
READ(BUF) ;
 +1       ;Performs a READ to BUF and returns the $T result as the function value.
 +2       ;For client reads the timeout value is dynamically adjusted based
 +3       ;on a random sample. For server reads the timeout is static.
 +4       ;
 +5       ;
ZB31      ;
 +1        NEW RETURN
 +2        SET RETURN=0
 +3       ;for the server the timeout is static
 +4        IF $GET(HLCSTATE("SERVER"))
               Begin DoDot:1
 +5                READ BUF:HLCSTATE("READ TIMEOUT")
 +6                SET RETURN=$TEST
               End DoDot:1
 +7       ;
 +8       ;client
          IF '$TEST
               Begin DoDot:1
 +9                IF ($RANDOM(100)<10)
                       Begin DoDot:2
 +10      ;measure how long the READ really takes
 +11                       NEW T1,T2
 +12                       SET T1=$$NOW^XLFDT
 +13                       READ BUF:100
 +14                       IF $TEST
                               Begin DoDot:3
 +15                               SET RETURN=1
 +16                               SET T2=$$NOW^XLFDT
 +17                               DO SETTIME($$FMDIFF^XLFDT(T2,T1,2))
                               End DoDot:3
 +18                      IF '$TEST
                               Begin DoDot:3
 +19                               SET RETURN=0
                               End DoDot:3
                       End DoDot:2
 +20              IF '$TEST
                       Begin DoDot:2
 +21                       READ BUF:HLCSTATE("READ TIMEOUT")
 +22                       SET RETURN=$TEST
                       End DoDot:2
               End DoDot:1
ZB32      ;
 +1       ;
 +2        QUIT RETURN
 +3       ;
SETTIME(TIME) ;
 +1       ;Re-sets the Read Timeout based on an algorithm that uses the
 +2       ;new read time + the prior 4 historical values.
 +3       ;
 +4        NEW MAX,I,TEMP
 +5        SET HLCSTATE("READ TIMEOUT","HISTORICAL")=TIME_"^"_$PIECE(HLCSTATE("READ TIMEOUT","HISTORICAL"),"^",1,4)
 +6        SET MAX=0
 +7        FOR I=1:1:5
               SET TEMP=$PIECE(HLCSTATE("READ TIMEOUT","HISTORICAL"),"^",I)
               IF TEMP>MAX
                   SET MAX=TEMP
 +8        SET TEMP=MAX+5
 +9        IF TEMP<20
               SET TEMP=20
 +10       IF TEMP>60
               SET TEMP=60
 +11       SET HLCSTATE("READ TIMEOUT")=TEMP
 +12       SET HLCSTATE("READ TIMEOUT","CHANGED")=1
 +13       QUIT 
 +14      ;
GETTIME(HLCSTATE) ;
 +1       ;Gets from ^HLTMP the current read timeout for the client link and the
 +2       ;historical data that the timeout value is based on.
 +3        NEW DATA
 +4        SET DATA=$GET(^HLTMP("READ TIMEOUT",HLCSTATE("LINK","NAME")))
 +5        IF $PIECE(DATA,"^")<20
               Begin DoDot:1
 +6                SET HLCSTATE("READ TIMEOUT")=20
               End DoDot:1
 +7       IF '$TEST
               Begin DoDot:1
 +8                SET HLCSTATE("READ TIMEOUT")=$PIECE(DATA,"^")
               End DoDot:1
 +9        SET HLCSTATE("READ TIMEOUT","HISTORICAL")=$PIECE(DATA,"^",2,6)
 +10       SET HLCSTATE("READ TIMEOUT","CHANGED")=0
 +11       QUIT 
 +12      ;
PUTTIME(HLCSTATE) ;
 +1       ;Stores to ^HLTMP the current read timeout for the client link and
 +2       ;the historical data that the timeout value is based on.
 +3        SET ^HLTMP("READ TIMEOUT",HLCSTATE("LINK","NAME"))=HLCSTATE("READ TIMEOUT")_"^"_HLCSTATE("READ TIMEOUT","HISTORICAL")
 +4        QUIT 
 +5       ;**P146 END CJM