INHVTMT2 ; KAC ; 02 Nov 1999 17:54 ; Multi-threaded TCP/IP socket utilities
;;3.01;BHL IHS Interfaces with GIS;;JUL 01, 2001
;COPYRIGHT 1991-2000 SAIC
Q
;
RECEIVE(INCHNL,INIP,INERR,INMEM) ; Function - Read messages from
; TCP bfr and process into GIS. Once in this routine, reading is
; atomic (the transceiver may be told to stop, but RECEIVE will
; finish to a logical conclusion instead of throwing away data
; in the buffer or INREC).
;
; Called by: INHVTMT
;
; Input:
; INCHNL - (req) Socket from which to read
; INIP - (pbr) Array of parameters
; INERR - (pbr) Error array
; INMEM - not used (placeholder. %INET secondary memory)
;
; Function returns:
; 0=ok
; 1=no response at all
; 2=failure in middle of receive
; 3=remote system disconnected
;
; Note: The check for remote system disconnect is based on a string
; match from utility routine %INET. If that utility is changed, this
; must also be changed.
;
N I,INBFR,INCLRMSG,INDELIM,INDONE,INEOL,INEOM,INFIRST,INFRMPOS,INFS,INIVBLD,INLOGMSG,INMSG,INMSGCNT,INNORESP,INNULLRD,INPOS,INREC,INRECCNT,INRSTATE,INSMIN,INSOD,INSOM,INV,REC,X,Y,Z
;
; Constants
S INEOL=INIP("EOL"),INSOM=INIP("SOM"),INEOM=INIP("EOM")
S:$D(INIP("SOD")) INSOD=INIP("SOD")
S:$D(INIP("FS")) INFS=INIP("FS")
S INSMIN=$S($P($G(^INRHSITE(1,0)),U,14):$P(^(0),U,14),1:2500)
S INDELIM=$$FIELD^INHUT()
;
; Variables
S (INBFR,INIVBLD,INMSG)="",(INDONE,INMSGCNT,INNORESP,INNULLRD,INRECCNT)=0
S INREC="REC" K @INREC
S INRSTATE="SOM" ; looking for SOM
S INFIRST=1 ; 1st time thru decryptor per msg
S INCLRMSG='$G(INIP("CRYPT")) ; 1 = do not decrypt rcv'd msg
;
; Main Loop
F D Q:INDONE!INNORESP ; til no response or disconnect
. D:$G(INDEBUG) LOG^INHVCRA1("Reading from buffer.",5)
.; read input bfr (up to 512 bytes)
. D RECV^%INET(.INBFR,.INCHNL,INIP("RTO"),1)
.;
.; Disconnect
. I $G(INBFR(0))["Remote end disconnect" D Q
.. S INDISCNT=INDISCNT+1
.. I INIP("DTRY")'>INDISCNT D Q ; shutdwn xceiver
... S INRUNMT=0,INDONE=1
... S INLOGMSG="Disconnect retries exceeded for background process "_INBPNM
... D:$G(INDEBUG) LOG^INHVCRA1(INLOGMSG,7)
... D ENR^INHE(INBPN,INLOGMSG)
.. S INDONE=3 ; retries not exceeded
.;
.; No response (on 1st or subsequent reads, therefore a 1 or 2 error)
.; Read retries prevent dropping 1st part of msg cos 2nd part was delayed
. I INBFR="" D Q
..; stop reading if not expecting any responses
..; pend que empty, not expecting HB response, not in middle of building msg
.. I '$D(^INLHDEST(INDSTR,"PEND",INBPN)),(INSTATE'="HB"),'INRECCNT,'$D(INRSTATE("SOMFRAG")) S INPEND=0,INDONE=1 Q
.. S INNULLRD=INNULLRD+1
.. I INIP("RTRY")'>INNULLRD S INNORESP=1 Q ; read tries exceeded
.. D WAIT^INHUVUT2(INBPN,INIP("RHNG"),"Waiting "_INIP("RHNG")_" seconds to read buffer",.INRUNMT)
.. S INRUNMT='INRUNMT ; wait rtns opposite
..; if signalled to stop xceiver, stop if not in middle of building msg
.. I 'INRUNMT,'INRECCNT,'$D(INRSTATE("SOMFRAG")) S INDONE=1 Q
. S (INNULLRD,INDISCNT)=0 ; reset upon receipt
.;
.; Ck for bfr continuation (SOM frags). If no frags found, continue
.; search for current state in this new bfr.
. I $D(INRSTATE("SOMFRAG")) D ; SOM frags exist from prev read
.. S X=$L(INSOM)-$L(INRSTATE("SOMFRAG")) ; # SOM chars expected @ start of in this bfr
.. I (INRSTATE("SOMFRAG")_$E(INBFR,1,X)=INSOM) D
...; found frag completion at start of new bfr
... D:$G(INRECCNT) DATAFRAG^INHVTMT3(@INREC@(INRECCNT),0,$L(@INREC@(INRECCNT))) ; log msg in progress as data frag
... S INBFR=$E(INBFR,X+1,999999) ; remove SOM from msg
... S INRSTATE="SOD" ; SOM found - look for SOD
.. K INRSTATE("SOMFRAG")
.;
.; create array (INFRMPOS) of framing chars in this bfr subscripted by position
. K INFRMPOS
. D GETFRAME^INHVTMT3(INBFR,INSOM,.INFRMPOS)
. D:$D(INSOD) GETFRAME^INHVTMT3(INBFR,INSOD,.INFRMPOS)
. D GETFRAME^INHVTMT3(INBFR,INEOM,.INFRMPOS)
.;
. S INPOS=0,INPOS("PREV")=0 ; must be remembered while reading bfr
.;
. F D Q:'INPOS ; scan for multiple msgs til bfr exhausted
..;
..; Scan bfr for single msg (or portion of msg)
.. F I="SOM","SOD","EOM" S INPOS(I)=0
.. F S INPOS=$O(INFRMPOS(INPOS)) Q:'INPOS D Q:INPOS("EOM")
... I (INRSTATE="SOM"),(INFRMPOS(INPOS)=INSOM) D Q
.... S INPOS("SOM")=INPOS
.... S INRSTATE=$S($D(INSOD):"SOD",1:"EOM") ; SOD may not be used
... I $D(INSOD),(INRSTATE="SOD"),(INFRMPOS(INPOS)=INSOD) D Q
.... S INPOS("SOD")=INPOS
.... S INRSTATE="EOM"
... I (INRSTATE="EOM"),(INFRMPOS(INPOS)=INEOM) D Q
.... S INPOS("EOM")=INPOS
.... S INRSTATE="SOM"
....; last position this bfr after which to log data frags
.... S INPOS("PREV")=INPOS
...;
...; Data Fragmentation (possibly across bfrs)
... D DATAFRAG^INHVTMT3(INBFR,INPOS("PREV")+1,INPOS)
... S INPOS("PREV")=INPOS
...; incorrectly placed SOM may be start of good msg
... I INFRMPOS(INPOS)=INSOM D Q
.... S INPOS("SOM")=INPOS
.... S INRSTATE=$S($D(INSOD):"SOD",1:"EOM") ; SOD may not be used
...;
..; Scanning complete - ck for bfr continuation or SOM fragments
..; Scan returns incomplete msg
.. I 'INPOS D Q ; reached end of bfr during scan before finding complete msg
...; SOM frags @ end of bfr?
... I $L(INSOM)>1,($E(INBFR,$L(INBFR)-$L(INSOM)+1,$L(INBFR))'=INSOM) D
.... S X=$L(INBFR)+1,Y=$L(INSOM)-1
.... F I=1:1:Y S Z=$E(INBFR,X-I,$L(INBFR)) I Z=$E(INSOM,1,I) S INRSTATE("SOMFRAG")=Z Q ; save frags
...;
... I "^SOD^EOM^"[(U_INRSTATE_U) D Q ; bfr continuation
.... S INMSG=$E(INBFR,INPOS("SOM"),$L(INBFR))
.... D PUTINREC^INHVTMT3(INMSG)
...;
...; If looking for SOM, log data frag (if any) thru end of bfr
... I (INRSTATE="SOM") D Q
.... S X=INPOS("PREV"),Y=$L(INBFR)-$L($G(INRSTATE("SOMFRAG")))
.... D:((Y-X)>0) DATAFRAG^INHVTMT3(INBFR,INPOS("PREV")+1,Y)
..;
..; Scan returns complete msg (EOM found)
.. I INPOS D
... I $G(INRECCNT) D ; multiple-bfrs of data in INREC
.... S INMSG=$E(INBFR,1,INPOS("EOM"))
.... D PUTINREC^INHVTMT3(INMSG)
... I '$G(INRECCNT) D ; msg all in 1 bfr (includes SOM frags - $E expects INPOS("SOM")=0)
.... S INMSG=$E(INBFR,INPOS("SOM"),INPOS("EOM"))
.... D PUTINREC^INHVTMT3(INMSG)
..;
.. Q:'INRECCNT ; no complete, raw msg to parse/validate
..;
..; Process msg @INREC
.. S INV="INDATA" K @INV
.. D PARSE^INHUVUT1 ; put INREC into INV
.. K @INREC S INREC="REC",INRECCNT=0
.. D EVAL^INHVTMT3(INV) ; submit msg for validation
.. K @INV
.. S INMSGCNT=INMSGCNT+1 ; # msgs read since call to $$RECEIVE
;
;
; Finished reading - return final state of connection
;
; Remote end disconnected
I INDONE=3 S INERR=$G(INBFR(0)) K @INREC Q 3
;
; Remote system timed-out / log error
I INRECCNT,INNORESP D Q 2
. S INERR="Remote system on "_INBPNM_" timed out during transmission of message"
. D DATAFRAG^INHVTMT3($G(@INREC@(1)),0,$L($G(@INREC@(1)))) ; log msg in progress as data frag
;
; No message received
I 'INMSGCNT,INNORESP S INERR="No message received from remote system on transceiver "_INBPNM K @INREC Q 1
;
Q 0
;
;
INHVTMT2 ; KAC ; 02 Nov 1999 17:54 ; Multi-threaded TCP/IP socket utilities
+1 ;;3.01;BHL IHS Interfaces with GIS;;JUL 01, 2001
+2 ;COPYRIGHT 1991-2000 SAIC
+3 QUIT
+4 ;
RECEIVE(INCHNL,INIP,INERR,INMEM) ; Function - Read messages from
+1 ; TCP bfr and process into GIS. Once in this routine, reading is
+2 ; atomic (the transceiver may be told to stop, but RECEIVE will
+3 ; finish to a logical conclusion instead of throwing away data
+4 ; in the buffer or INREC).
+5 ;
+6 ; Called by: INHVTMT
+7 ;
+8 ; Input:
+9 ; INCHNL - (req) Socket from which to read
+10 ; INIP - (pbr) Array of parameters
+11 ; INERR - (pbr) Error array
+12 ; INMEM - not used (placeholder. %INET secondary memory)
+13 ;
+14 ; Function returns:
+15 ; 0=ok
+16 ; 1=no response at all
+17 ; 2=failure in middle of receive
+18 ; 3=remote system disconnected
+19 ;
+20 ; Note: The check for remote system disconnect is based on a string
+21 ; match from utility routine %INET. If that utility is changed, this
+22 ; must also be changed.
+23 ;
+24 NEW I,INBFR,INCLRMSG,INDELIM,INDONE,INEOL,INEOM,INFIRST,INFRMPOS,INFS,INIVBLD,INLOGMSG,INMSG,INMSGCNT,INNORESP,INNULLRD,INPOS,INREC,INRECCNT,INRSTATE,INSMIN,INSOD,INSOM,INV,REC,X,Y,Z
+25 ;
+26 ; Constants
+27 SET INEOL=INIP("EOL")
SET INSOM=INIP("SOM")
SET INEOM=INIP("EOM")
+28 IF $DATA(INIP("SOD"))
SET INSOD=INIP("SOD")
+29 IF $DATA(INIP("FS"))
SET INFS=INIP("FS")
+30 SET INSMIN=$SELECT($PIECE($GET(^INRHSITE(1,0)),U,14):$PIECE(^(0),U,14),1:2500)
+31 SET INDELIM=$$FIELD^INHUT()
+32 ;
+33 ; Variables
+34 SET (INBFR,INIVBLD,INMSG)=""
SET (INDONE,INMSGCNT,INNORESP,INNULLRD,INRECCNT)=0
+35 SET INREC="REC"
KILL @INREC
+36 ; looking for SOM
SET INRSTATE="SOM"
+37 ; 1st time thru decryptor per msg
SET INFIRST=1
+38 ; 1 = do not decrypt rcv'd msg
SET INCLRMSG='$GET(INIP("CRYPT"))
+39 ;
+40 ; Main Loop
+41 ; til no response or disconnect
FOR
Begin DoDot:1
+42 IF $GET(INDEBUG)
DO LOG^INHVCRA1("Reading from buffer.",5)
+43 ; read input bfr (up to 512 bytes)
+44 DO RECV^%INET(.INBFR,.INCHNL,INIP("RTO"),1)
+45 ;
+46 ; Disconnect
+47 IF $GET(INBFR(0))["Remote end disconnect"
Begin DoDot:2
+48 SET INDISCNT=INDISCNT+1
+49 ; shutdwn xceiver
IF INIP("DTRY")'>INDISCNT
Begin DoDot:3
+50 SET INRUNMT=0
SET INDONE=1
+51 SET INLOGMSG="Disconnect retries exceeded for background process "_INBPNM
+52 IF $GET(INDEBUG)
DO LOG^INHVCRA1(INLOGMSG,7)
+53 DO ENR^INHE(INBPN,INLOGMSG)
End DoDot:3
QUIT
+54 ; retries not exceeded
SET INDONE=3
End DoDot:2
QUIT
+55 ;
+56 ; No response (on 1st or subsequent reads, therefore a 1 or 2 error)
+57 ; Read retries prevent dropping 1st part of msg cos 2nd part was delayed
+58 IF INBFR=""
Begin DoDot:2
+59 ; stop reading if not expecting any responses
+60 ; pend que empty, not expecting HB response, not in middle of building msg
+61 IF '$DATA(^INLHDEST(INDSTR,"PEND",INBPN))
IF (INSTATE'="HB")
IF 'INRECCNT
IF '$DATA(INRSTATE("SOMFRAG"))
SET INPEND=0
SET INDONE=1
QUIT
+62 SET INNULLRD=INNULLRD+1
+63 ; read tries exceeded
IF INIP("RTRY")'>INNULLRD
SET INNORESP=1
QUIT
+64 DO WAIT^INHUVUT2(INBPN,INIP("RHNG"),"Waiting "_INIP("RHNG")_" seconds to read buffer",.INRUNMT)
+65 ; wait rtns opposite
SET INRUNMT='INRUNMT
+66 ; if signalled to stop xceiver, stop if not in middle of building msg
+67 IF 'INRUNMT
IF 'INRECCNT
IF '$DATA(INRSTATE("SOMFRAG"))
SET INDONE=1
QUIT
End DoDot:2
QUIT
+68 ; reset upon receipt
SET (INNULLRD,INDISCNT)=0
+69 ;
+70 ; Ck for bfr continuation (SOM frags). If no frags found, continue
+71 ; search for current state in this new bfr.
+72 ; SOM frags exist from prev read
IF $DATA(INRSTATE("SOMFRAG"))
Begin DoDot:2
+73 ; # SOM chars expected @ start of in this bfr
SET X=$LENGTH(INSOM)-$LENGTH(INRSTATE("SOMFRAG"))
+74 IF (INRSTATE("SOMFRAG")_$EXTRACT(INBFR,1,X)=INSOM)
Begin DoDot:3
+75 ; found frag completion at start of new bfr
+76 ; log msg in progress as data frag
IF $GET(INRECCNT)
DO DATAFRAG^INHVTMT3(@INREC@(INRECCNT),0,$LENGTH(@INREC@(INRECCNT)))
+77 ; remove SOM from msg
SET INBFR=$EXTRACT(INBFR,X+1,999999)
+78 ; SOM found - look for SOD
SET INRSTATE="SOD"
End DoDot:3
+79 KILL INRSTATE("SOMFRAG")
End DoDot:2
+80 ;
+81 ; create array (INFRMPOS) of framing chars in this bfr subscripted by position
+82 KILL INFRMPOS
+83 DO GETFRAME^INHVTMT3(INBFR,INSOM,.INFRMPOS)
+84 IF $DATA(INSOD)
DO GETFRAME^INHVTMT3(INBFR,INSOD,.INFRMPOS)
+85 DO GETFRAME^INHVTMT3(INBFR,INEOM,.INFRMPOS)
+86 ;
+87 ; must be remembered while reading bfr
SET INPOS=0
SET INPOS("PREV")=0
+88 ;
+89 ; scan for multiple msgs til bfr exhausted
FOR
Begin DoDot:2
+90 ;
+91 ; Scan bfr for single msg (or portion of msg)
+92 FOR I="SOM","SOD","EOM"
SET INPOS(I)=0
+93 FOR
SET INPOS=$ORDER(INFRMPOS(INPOS))
IF 'INPOS
QUIT
Begin DoDot:3
+94 IF (INRSTATE="SOM")
IF (INFRMPOS(INPOS)=INSOM)
Begin DoDot:4
+95 SET INPOS("SOM")=INPOS
+96 ; SOD may not be used
SET INRSTATE=$SELECT($DATA(INSOD):"SOD",1:"EOM")
End DoDot:4
QUIT
+97 IF $DATA(INSOD)
IF (INRSTATE="SOD")
IF (INFRMPOS(INPOS)=INSOD)
Begin DoDot:4
+98 SET INPOS("SOD")=INPOS
+99 SET INRSTATE="EOM"
End DoDot:4
QUIT
+100 IF (INRSTATE="EOM")
IF (INFRMPOS(INPOS)=INEOM)
Begin DoDot:4
+101 SET INPOS("EOM")=INPOS
+102 SET INRSTATE="SOM"
+103 ; last position this bfr after which to log data frags
+104 SET INPOS("PREV")=INPOS
End DoDot:4
QUIT
+105 ;
+106 ; Data Fragmentation (possibly across bfrs)
+107 DO DATAFRAG^INHVTMT3(INBFR,INPOS("PREV")+1,INPOS)
+108 SET INPOS("PREV")=INPOS
+109 ; incorrectly placed SOM may be start of good msg
+110 IF INFRMPOS(INPOS)=INSOM
Begin DoDot:4
+111 SET INPOS("SOM")=INPOS
+112 ; SOD may not be used
SET INRSTATE=$SELECT($DATA(INSOD):"SOD",1:"EOM")
End DoDot:4
QUIT
+113 ;
End DoDot:3
IF INPOS("EOM")
QUIT
+114 ; Scanning complete - ck for bfr continuation or SOM fragments
+115 ; Scan returns incomplete msg
+116 ; reached end of bfr during scan before finding complete msg
IF 'INPOS
Begin DoDot:3
+117 ; SOM frags @ end of bfr?
+118 IF $LENGTH(INSOM)>1
IF ($EXTRACT(INBFR,$LENGTH(INBFR)-$LENGTH(INSOM)+1,$LENGTH(INBFR))'=INSOM)
Begin DoDot:4
+119 SET X=$LENGTH(INBFR)+1
SET Y=$LENGTH(INSOM)-1
+120 ; save frags
FOR I=1:1:Y
SET Z=$EXTRACT(INBFR,X-I,$LENGTH(INBFR))
IF Z=$EXTRACT(INSOM,1,I)
SET INRSTATE("SOMFRAG")=Z
QUIT
End DoDot:4
+121 ;
+122 ; bfr continuation
IF "^SOD^EOM^"[(U_INRSTATE_U)
Begin DoDot:4
+123 SET INMSG=$EXTRACT(INBFR,INPOS("SOM"),$LENGTH(INBFR))
+124 DO PUTINREC^INHVTMT3(INMSG)
End DoDot:4
QUIT
+125 ;
+126 ; If looking for SOM, log data frag (if any) thru end of bfr
+127 IF (INRSTATE="SOM")
Begin DoDot:4
+128 SET X=INPOS("PREV")
SET Y=$LENGTH(INBFR)-$LENGTH($GET(INRSTATE("SOMFRAG")))
+129 IF ((Y-X)>0)
DO DATAFRAG^INHVTMT3(INBFR,INPOS("PREV")+1,Y)
End DoDot:4
QUIT
End DoDot:3
QUIT
+130 ;
+131 ; Scan returns complete msg (EOM found)
+132 IF INPOS
Begin DoDot:3
+133 ; multiple-bfrs of data in INREC
IF $GET(INRECCNT)
Begin DoDot:4
+134 SET INMSG=$EXTRACT(INBFR,1,INPOS("EOM"))
+135 DO PUTINREC^INHVTMT3(INMSG)
End DoDot:4
+136 ; msg all in 1 bfr (includes SOM frags - $E expects INPOS("SOM")=0)
IF '$GET(INRECCNT)
Begin DoDot:4
+137 SET INMSG=$EXTRACT(INBFR,INPOS("SOM"),INPOS("EOM"))
+138 DO PUTINREC^INHVTMT3(INMSG)
End DoDot:4
End DoDot:3
+139 ;
+140 ; no complete, raw msg to parse/validate
IF 'INRECCNT
QUIT
+141 ;
+142 ; Process msg @INREC
+143 SET INV="INDATA"
KILL @INV
+144 ; put INREC into INV
DO PARSE^INHUVUT1
+145 KILL @INREC
SET INREC="REC"
SET INRECCNT=0
+146 ; submit msg for validation
DO EVAL^INHVTMT3(INV)
+147 KILL @INV
+148 ; # msgs read since call to $$RECEIVE
SET INMSGCNT=INMSGCNT+1
End DoDot:2
IF 'INPOS
QUIT
End DoDot:1
IF INDONE!INNORESP
QUIT
+149 ;
+150 ;
+151 ; Finished reading - return final state of connection
+152 ;
+153 ; Remote end disconnected
+154 IF INDONE=3
SET INERR=$GET(INBFR(0))
KILL @INREC
QUIT 3
+155 ;
+156 ; Remote system timed-out / log error
+157 IF INRECCNT
IF INNORESP
Begin DoDot:1
+158 SET INERR="Remote system on "_INBPNM_" timed out during transmission of message"
+159 ; log msg in progress as data frag
DO DATAFRAG^INHVTMT3($GET(@INREC@(1)),0,$LENGTH($GET(@INREC@(1))))
End DoDot:1
QUIT 2
+160 ;
+161 ; No message received
+162 IF 'INMSGCNT
IF INNORESP
SET INERR="No message received from remote system on transceiver "_INBPNM
KILL @INREC
QUIT 1
+163 ;
+164 QUIT 0
+165 ;
+166 ;