| 1 | HLOQUE ;ALB/CJM- HL7 QUEUE MANAGEMENT - 10/4/94 1pm ;07/31/2007 | 
|---|
| 2 | ;;1.6;HEALTH LEVEL SEVEN;**126,132,134,137**;Oct 13, 1995;Build 21 | 
|---|
| 3 | ;Per VHA Directive 2004-038, this routine should not be modified. | 
|---|
| 4 | ; | 
|---|
| 5 | INQUE(FROM,QNAME,IEN778,ACTION,PURGE) ; | 
|---|
| 6 | ;Will place the message=IEN778 on the IN queue, incoming | 
|---|
| 7 | ;Input: | 
|---|
| 8 | ;  FROM - sending facility from message header. | 
|---|
| 9 | ;         For actions other than incoming messages, its the specified link. | 
|---|
| 10 | ;  QNAME - queue named by the application | 
|---|
| 11 | ;  IEN778 = ien of the message in file 778 | 
|---|
| 12 | ;  ACTION - <tag^routine> that should be executed for the application | 
|---|
| 13 | ;  PURGE (optional) - PURGE=1 indicates that the purge dt/tm needs to be set by the infiler | 
|---|
| 14 | ;     If PURGE("ACKTOIEN") is set, it indicates that the purge dt/tm of | 
|---|
| 15 | ;     the original message to this application ack also needs to be set. | 
|---|
| 16 | ;Output: none | 
|---|
| 17 | ; | 
|---|
| 18 | I $G(FROM)="" S FROM="UNKNOWN" | 
|---|
| 19 | I '$L($G(QNAME)) S QNAME="DEFAULT" | 
|---|
| 20 | S ^HLB("QUEUE","IN",FROM,QNAME,IEN778)=ACTION_"^"_$G(PURGE)_"^"_$G(PURGE("ACKTOIEN")) | 
|---|
| 21 | I $$INC^HLOSITE($NA(^HLC("QUEUECOUNT","IN",FROM,QNAME))) | 
|---|
| 22 | Q | 
|---|
| 23 | ; | 
|---|
| 24 | OUTQUE(LINKNAME,PORT,QNAME,IEN778) ; | 
|---|
| 25 | ;Will place the message=IEN778 on the out-going queue | 
|---|
| 26 | ;Input: | 
|---|
| 27 | ;  LINKNAME = name of (.01) the logical link | 
|---|
| 28 | ;  PORT (optional) the port to connect to | 
|---|
| 29 | ;  QNAME - queue named by the application | 
|---|
| 30 | ;  IEN778 = ien of the message in file 778 | 
|---|
| 31 | ;Output: none | 
|---|
| 32 | ; | 
|---|
| 33 | N SUB | 
|---|
| 34 | S SUB=LINKNAME | 
|---|
| 35 | I PORT S SUB=SUB_":"_PORT | 
|---|
| 36 | I '$L($G(QNAME)) S QNAME="DEFAULT" | 
|---|
| 37 | S ^HLB("QUEUE","OUT",SUB,QNAME,IEN778)="" | 
|---|
| 38 | I $$INC^HLOSITE($NA(^HLC("QUEUECOUNT","OUT",SUB,QNAME))) | 
|---|
| 39 | Q | 
|---|
| 40 | ; | 
|---|
| 41 | DEQUE(FROMORTO,QNAME,DIR,IEN778) ; | 
|---|
| 42 | ;This routine will remove the message=IEN778 from its queue | 
|---|
| 43 | ;Input: | 
|---|
| 44 | ;  DIR = "IN" or "OUT", denoting the direction that the message is going in | 
|---|
| 45 | ;  FROMORTO = for outgoing: the .01 field of the logical link | 
|---|
| 46 | ;         for incoming: sending facility | 
|---|
| 47 | ;  IEN778 = ien of the message in file 778 | 
|---|
| 48 | ;Output: none | 
|---|
| 49 | ; | 
|---|
| 50 | Q:(FROMORTO="") | 
|---|
| 51 | I ($G(QNAME)="") S QNAME="DEFAULT" | 
|---|
| 52 | D | 
|---|
| 53 | .I $E(DIR)="I" S DIR="IN" Q | 
|---|
| 54 | .I $E(DIR)="O" S DIR="OUT" Q | 
|---|
| 55 | I DIR'="IN",DIR'="OUT" Q | 
|---|
| 56 | Q:'$G(IEN778) | 
|---|
| 57 | D:$D(^HLB("QUEUE",DIR,FROMORTO,QNAME,IEN778)) | 
|---|
| 58 | .K ^HLB("QUEUE",DIR,FROMORTO,QNAME,IEN778) | 
|---|
| 59 | .;don't let the count become negative | 
|---|
| 60 | .I $$INC^HLOSITE($NA(^HLC("QUEUECOUNT",DIR,FROMORTO,QNAME)),-1)<0,$$INC^HLOSITE($NA(^HLC("QUEUECOUNT",DIR,FROMORTO,QNAME))) | 
|---|
| 61 | Q | 
|---|
| 62 | ; | 
|---|
| 63 | STOPQUE(DIR,QUEUE) ; | 
|---|
| 64 | ;This API is used to set a stop flag on a named queue. | 
|---|
| 65 | ;DIR=<"IN" or "OUT"> | 
|---|
| 66 | ;QUEUE - the name of the queue to be stopped | 
|---|
| 67 | ; | 
|---|
| 68 | Q:$G(DIR)="" | 
|---|
| 69 | Q:$G(QUEUE)="" | 
|---|
| 70 | S ^HLTMP("STOPPED QUEUES",DIR,QUEUE)=1 | 
|---|
| 71 | Q | 
|---|
| 72 | STARTQUE(DIR,QUEUE) ; | 
|---|
| 73 | ;This API is used to REMOVE the stop flag on a named queue. | 
|---|
| 74 | ;DIR=<"IN" or "OUT"> | 
|---|
| 75 | ;QUEUE - the name of the queue to be stopped | 
|---|
| 76 | ; | 
|---|
| 77 | Q:$G(DIR)="" | 
|---|
| 78 | Q:$G(QUEUE)="" | 
|---|
| 79 | K ^HLTMP("STOPPED QUEUES",DIR,QUEUE) | 
|---|
| 80 | Q | 
|---|
| 81 | STOPPED(DIR,QUEUE) ; | 
|---|
| 82 | ;This API is used to DETERMINE if the stop flag on a named queue is set. | 
|---|
| 83 | ;Input: | 
|---|
| 84 | ;  DIR=<"IN" or "OUT"> | 
|---|
| 85 | ;  QUEUE - the name of the queue to be checked | 
|---|
| 86 | ;Output: | 
|---|
| 87 | ;  Function returns 1 if the queue is stopped, 0 otherwise | 
|---|
| 88 | Q:$G(DIR)="" 0 | 
|---|
| 89 | Q:$G(QUEUE)="" 0 | 
|---|
| 90 | I $G(^HLTMP("STOPPED QUEUES",DIR,QUEUE)) Q 1 | 
|---|
| 91 | Q 0 | 
|---|
| 92 | ; | 
|---|
| 93 | SQUE(SQUE,LINKNAME,PORT,QNAME,IEN778) ; | 
|---|
| 94 | ;Will place the message=IEN778 on the sequencing queue. This is always done in the context of the application calling an HLO API to send a message. | 
|---|
| 95 | ;Input: | 
|---|
| 96 | ;  SQUE - name of the sequencing queue | 
|---|
| 97 | ;  LINKNAME = name of (.01) the logical link | 
|---|
| 98 | ;  PORT (optional) the port to connect to | 
|---|
| 99 | ;  QNAME (optional) outgoing queue | 
|---|
| 100 | ;  IEN778 = ien of the message in file 778 | 
|---|
| 101 | ;Output: 1 if placed on the outgoing queue, 0 if placed on the sequence queue | 
|---|
| 102 | ; | 
|---|
| 103 | N NEXT,MOVED | 
|---|
| 104 | S MOVED=0 | 
|---|
| 105 | ; | 
|---|
| 106 | ;keep a count of messages pending on sequence queues for the HLO System Monitor | 
|---|
| 107 | I $$INC^HLOSITE($NA(^HLC("QUEUECOUNT","SEQUENCE"))) | 
|---|
| 108 | ; | 
|---|
| 109 | L +^HLB("QUEUE","SEQUENCE",SQUE):200 | 
|---|
| 110 | ; | 
|---|
| 111 | S NEXT=+$G(^HLB("QUEUE","SEQUENCE",SQUE)) | 
|---|
| 112 | Q:NEXT=IEN778 0  ;already queued! | 
|---|
| 113 | ;if the sequence queue is empty and not waiting on a message, then the message can be put directly on the outgoing queue, bypassing the sequence queue | 
|---|
| 114 | I '$O(^HLB("QUEUE","SEQUENCE",SQUE,0)),'NEXT D | 
|---|
| 115 | .S ^HLB("QUEUE","SEQUENCE",SQUE)=IEN778 ;to mean something moved to outgoing but not yet transmitted | 
|---|
| 116 | .L -^HLB("QUEUE","SEQUENCE",SQUE) | 
|---|
| 117 | .D OUTQUE(.LINKNAME,.PORT,.QNAME,IEN778) | 
|---|
| 118 | .S MOVED=1 | 
|---|
| 119 | E  D | 
|---|
| 120 | .;Put the message on the sequence queue. | 
|---|
| 121 | .S ^HLB("QUEUE","SEQUENCE",SQUE,IEN778)="" | 
|---|
| 122 | .L -^HLB("QUEUE","SEQUENCE",SQUE) | 
|---|
| 123 | Q MOVED | 
|---|
| 124 | ; | 
|---|
| 125 | ADVANCE(SQUE,MSGIEN) ; | 
|---|
| 126 | ;Will move the specified sequencing queue to the next message. | 
|---|
| 127 | ;Input: | 
|---|
| 128 | ;  SQUE - name of the sequencing queue | 
|---|
| 129 | ;  MSGIEN - the ien of the message upon which the sequence queue was waiting.  If it is NOT the correct ien, then the sequence queue will NOT be advance. | 
|---|
| 130 | ;Output: | 
|---|
| 131 | ;  Function - 1 if advanced, 0 if not | 
|---|
| 132 | ; | 
|---|
| 133 | N NODE,IEN778,LINKNAME,PORT,QNAME | 
|---|
| 134 | Q:'$L($G(SQUE)) 0 | 
|---|
| 135 | Q:'$G(MSGIEN) 0 | 
|---|
| 136 | L +^HLB("QUEUE","SEQUENCE",SQUE):200 | 
|---|
| 137 | ; | 
|---|
| 138 | ;do not advance if the queue wasn't pending the message=MSGIEN | 
|---|
| 139 | I (MSGIEN'=$P($G(^HLB("QUEUE","SEQUENCE",SQUE)),"^")) L -^HLB("QUEUE","SEQUENCE",SQUE) Q 0 | 
|---|
| 140 | ; | 
|---|
| 141 | I $$INC^HLOSITE($NA(^HLC("QUEUECOUNT","SEQUENCE")),-1)<0,$$INC^HLOSITE($NA(^HLC("QUEUECOUNT","SEQUENCE"))) ;decrement the count of messages pending sequence queues | 
|---|
| 142 | ; | 
|---|
| 143 | S IEN778=0 | 
|---|
| 144 | ;look for the first message on the sequence que.  Make sure its valid, if not remove the invalid entry and keep looking. | 
|---|
| 145 | F  S IEN778=$O(^HLB("QUEUE","SEQUENCE",SQUE,0)) Q:'IEN778  S NODE=$G(^HLB(IEN778,0)) Q:$L(NODE)  D | 
|---|
| 146 | .;message does not exist! Remove from queue and try again. | 
|---|
| 147 | .K ^HLB("QUEUE","SEQUENCE",SQUE,IEN778) | 
|---|
| 148 | .I $$INC^HLOSITE($NA(^HLC("QUEUECOUNT","SEQUENCE")),-1)<0,$$INC^HLOSITE($NA(^HLC("QUEUECOUNT","SEQUENCE"))) ;decrement the count of messages pending sequence queues | 
|---|
| 149 | ; | 
|---|
| 150 | ;IEN778 is the next pending msg on this sequence queue | 
|---|
| 151 | I IEN778 D | 
|---|
| 152 | .; | 
|---|
| 153 | .;parse out info needed to move to outgoing queue | 
|---|
| 154 | .S LINKNAME=$P(NODE,"^",5),PORT=$P(NODE,"^",8),QNAME=$P(NODE,"^",6) | 
|---|
| 155 | .; | 
|---|
| 156 | .S ^HLB("QUEUE","SEQUENCE",SQUE)=IEN778 ;indicates this sequence queue is now waiting for msg=IEN778 before advancing.  The second pieces is the timer, but will not be set until the message=IEN778 is actually transmitted. | 
|---|
| 157 | .K ^HLB("QUEUE","SEQUENCE",SQUE,IEN778) ;remove from sequence queue | 
|---|
| 158 | .L -^HLB("QUEUE","SEQUENCE",SQUE) | 
|---|
| 159 | .S $P(^HLB(IEN778,5),"^",2)=1 | 
|---|
| 160 | .D OUTQUE(.LINKNAME,$G(PORT),$G(QNAME),IEN778) ;move to outgoing queue | 
|---|
| 161 | E  D | 
|---|
| 162 | .K ^HLB("QUEUE","SEQUENCE",SQUE) ;this sequence queue is currently empty and not needed | 
|---|
| 163 | .L -^HLB("QUEUE","SEQUENCE",SQUE) | 
|---|
| 164 | Q 1 | 
|---|
| 165 | ; | 
|---|
| 166 | SEQCHK(WORK) ;functions under the HLO Process Manager | 
|---|
| 167 | ;check sequence queues for timeout | 
|---|
| 168 | N QUE,NOW | 
|---|
| 169 | S NOW=$$NOW^XLFDT | 
|---|
| 170 | S QUE="" | 
|---|
| 171 | F  S QUE=$O(^HLB("QUEUE","SEQUENCE",QUE)) Q:QUE=""  D | 
|---|
| 172 | .N NODE,MSGIEN,ACTION,NODE | 
|---|
| 173 | .S NODE=$G(^HLB("QUEUE","SEQUENCE",QUE)) | 
|---|
| 174 | .Q:'$P(NODE,"^",2) | 
|---|
| 175 | .Q:$P(NODE,"^",2)>NOW | 
|---|
| 176 | .Q:$P(NODE,"^",3) | 
|---|
| 177 | .L +^HLB("QUEUE","SEQUENCE",QUE):2 | 
|---|
| 178 | .;don't report if a lock wasn't obtained | 
|---|
| 179 | .Q:'$T | 
|---|
| 180 | .S NODE=$G(^HLB("QUEUE","SEQUENCE",QUE)) | 
|---|
| 181 | .I '$P(NODE,"^",2) L -^HLB("QUEUE","SEQUENCE",QUE) Q | 
|---|
| 182 | .I ($P(NODE,"^",2)>NOW) L -^HLB("QUEUE","SEQUENCE",QUE) Q | 
|---|
| 183 | .I $P(NODE,"^",3) L -^HLB("QUEUE","SEQUENCE",QUE) Q  ;exception already raised | 
|---|
| 184 | .S MSGIEN=$P(NODE,"^") | 
|---|
| 185 | .I 'MSGIEN L -^HLB("QUEUE","SEQUENCE",QUE) Q | 
|---|
| 186 | .S ACTION=$$EXCEPT^HLOAPP($$GETSAP^HLOCLNT2(MSGIEN)) | 
|---|
| 187 | .S $P(^HLB(MSGIEN,5),"^",3)=1 | 
|---|
| 188 | .S $P(^HLB("QUEUE","SEQUENCE",QUE),"^",3)=1 ;indicates exception raised | 
|---|
| 189 | .L -^HLB("QUEUE","SEQUENCE",QUE) | 
|---|
| 190 | .D  ;call the application to take action | 
|---|
| 191 | ..N HLMSGIEN,MCODE,DUZ,QUE,NOW | 
|---|
| 192 | ..N $ETRAP,$ESTACK S $ETRAP="G ERROR^HLOQUE" | 
|---|
| 193 | ..S HLMSGIEN=MSGIEN | 
|---|
| 194 | ..S MCODE="D "_ACTION | 
|---|
| 195 | ..N MSGIEN,X | 
|---|
| 196 | ..D DUZ^XUP(.5) | 
|---|
| 197 | ..X MCODE | 
|---|
| 198 | ..;kill the apps variables | 
|---|
| 199 | ..D | 
|---|
| 200 | ...N ZTSK | 
|---|
| 201 | ...D KILL^XUSCLEAN | 
|---|
| 202 | Q | 
|---|
| 203 | ERROR ;error trap for application context | 
|---|
| 204 | S $ETRAP="D UNWIND^%ZTER" | 
|---|
| 205 | D ^%ZTER | 
|---|
| 206 | S $ECODE=",UAPPLICATION ERROR," | 
|---|
| 207 | ; | 
|---|
| 208 | ;kill the apps variables | 
|---|
| 209 | D | 
|---|
| 210 | .N ZTSK,MSGIEN,QUEUE | 
|---|
| 211 | .D KILL^XUSCLEAN | 
|---|
| 212 | ; | 
|---|
| 213 | ;release all the locks the app may have set, except Taskman lock | 
|---|
| 214 | L:$D(ZTSK) ^%ZTSCH("TASK",ZTSK):1 | 
|---|
| 215 | L:'$D(ZTSK) | 
|---|
| 216 | ;reset HLO's lock | 
|---|
| 217 | L +^HLTMP("HL7 RUNNING PROCESSES",$J):0 | 
|---|
| 218 | ;return to processing the next message on the queue | 
|---|
| 219 | D UNWIND^%ZTER | 
|---|
| 220 | Q | 
|---|