MAG7UP ;WOIFO/MLH,PMK - Imaging - HL7 - utilities - break out message into a parse tree ;25 May 2017 2:30 PM
 ;;3.0;IMAGING;**11,51,183**;MAR 19, 2002;Build 11;JUN 16, 2006
 ;; Per VHA Directive 2004-038, this routine should not be modified.
 ;; +---------------------------------------------------------------+
 ;; | Property of the US Government.                                |
 ;; | No permission to copy or redistribute this software is given. |
 ;; | Use of unreleased versions of this software requires the user |
 ;; | to execute a written test agreement with the VistA Imaging    |
 ;; | Development Office of the Department of Veterans Affairs,     |
 ;; | telephone (301) 734-0100.                                     |
 ;; | The Food and Drug Administration classifies this software as  |
 ;; | a medical device.  As such, it may not be changed in any way. |
 ;; | Modifications to this software may result in an adulterated   |
 ;; | medical device under 21CFR820, the use of which is considered |
 ;; | to be a violation of US Federal Statutes.                     |
 ;; +---------------------------------------------------------------+
 ;;
 Q
 ;
PARSE(XMSG,XTREE) ; break the HL7 message lines into a parse tree
 ;
 ; INPUT:  The single-dimensional array of message lines
 ;
 ; OUTPUT:  The parse tree, in the structure
 ;   @XTREE@(NSEG,0)                    segment name
 ;   @XTREE@(NSEG,NFLD,NREP,NCMP,NSCM)  element data
 ;   @XTREE@("B",SEGID,NSEG)          null
 ;
 N I,J,K,L,M,X,Z ; --------------------- scratch vars
 N IMSG ; ------------------------------ message array index
 N ISUBSEG ; --------------------------- index of continuation data for long segs
 N ISUCC ; ----------------------------- successor element index
 N FERR ; ------------------------------ error flag
 N SEG ; ------------------------------- segment data
 N SEGTAG ; ---------------------------- segment ID ("0th" piece)
 N UFS,UCS,URS,UEC,USS ;---------------- HL7 delimiters (universal)
 N ENC ;-------------------------------- HL7 encoding characters
 N UFSESC,UCSESC,URSESC,UECESC,USSESC ;- HL7 escape sequences for delimiters (universal)
 N PATTERN ; --------------------------- pattern match for spanned record
 N NSEG ; ------------------------------ segment number in the parse tree
 N NSEGINPT ; -------------------------- segment number of input HL7 data
 N NFLD ; ------------------------------ field number in the segment
 N FLD ; ------------------------------- field data
 ;
 S FERR=0 ; assume no error
 S IMSG=""
 ;
 ; process MSH segment
 ; If there's a message problem, return it in an NTE segment.
 ;
 S IMSG=$O(@XMSG@(IMSG)) ; array sent?
 I IMSG="" D  Q FERR ; no
 . S FERR=-1
 . ; have to use default field separator
 . S @XMSG@(0)="NTE|1||"_FERR_";no input array found"
 . Q
 S SEG=$G(@XMSG@(IMSG)) Q:$E(SEG,1,3)'="MSH" -2 ; an HL7 message?
 I $E(SEG,1,3)'="MSH" D  Q FERR ; no
 . S FERR=-2
 . S ISUCC=$O(@XMSG@(IMSG)) S:'ISUCC ISUCC=IMSG+1
 . ; have to use default field separator
 . S @XMSG@(IMSG+ISUCC/2)="NTE|1||"_FERR_";invalid HL7 message (1st 3 chars must be MSH)"
 . Q
 ;
 ; set up delimiters and escape sequences
 S UFS=$E(SEG,4),@XTREE@(1,1,1,1,1)=UFS
 S ENC=$P(SEG,UFS,2),@XTREE@(1,2,1,1,1)=ENC
 S UCS=$E(ENC),URS=$E(ENC,2),UEC=$E(ENC,3),USS=$E(ENC,4)
 S UFSESC=UEC_"F"_UEC,UCSESC=UEC_"S"_UEC,URSESC=UEC_"R"_UEC,UECESC=UEC_"E"_UEC,USSESC=UEC_"T"_UEC
 S PATTERN="1A2AN1"""_UFS_""""
 S @XTREE@(1,0)="MSH",@XTREE@("B","MSH",1)=""
 F NFLD=3:1:$L(SEG,UFS) S FLD=$P(SEG,UFS,NFLD) D
 . I FLD]"" D PROCFLD(XTREE,1,NFLD,FLD)
 . Q
 ; process the remaining segments
 S SEG="" ; SEG will be a concatenated series of spanned records
 S NSEG=2 ; next segment in the parse tree will be #2
 F NSEGINPT=2:1 S IMSG=$O(@XMSG@(IMSG)) Q:IMSG=""  D  Q:FERR
 . S SEG=$G(@XMSG@(IMSG)) Q:SEG=""
 . S ISUBSEG="" ; prepare to handle very long HL7 segments (up to 32K)
 . F  S ISUBSEG=$O(@XMSG@(IMSG,ISUBSEG)) Q:ISUBSEG=""  D
 . . S SEG=SEG_$G(@XMSG@(IMSG,ISUBSEG))
 . . Q
 . S SEGTAG=$P(SEG,UFS) I SEGTAG'?1U2.3UN S FERR=-3 Q
 . S @XTREE@(NSEG,0)=SEGTAG,@XTREE@("B",SEGTAG,NSEG)=""
 . F NFLD=2:1:$L(SEG,UFS) D
 . . S FLD=$P(SEG,UFS,NFLD)
 . . I FLD]"" D PROCFLD(XTREE,NSEG,NFLD-1,FLD)
 . . Q
 . S SEG="" ; reinitialize SEG for the next possible concatenation
 . S NSEG=NSEG+1 ; increment counter for next segment in the parse tree
 . Q
 Q FERR
 ;
PROCFLD(XTREE,XNSEG,XNFLD,XFLD) ; process a field
 ;
 ; input:  XTREE   name of MUMPS array for parse tree ($NA format)
 ;         XNSEG   segment number for parse tree
 ;         XNFLD   field number for parse tree
 ;         XFLD    field data
 ;
 N SG ; ------ segment name
 N NREP ; ---- repetition (occurrence) number
 N REP ; ----- repetition data
 N NCMP ; ---- component number
 N CMP ; ----- component data
 N NSCM ; ---- subcomponent number
 N SCM ; ----- subcomponent data
 ;
 S SG=@XTREE@(XNSEG,0)
 ; Per DICOM meeting 2004-02-24, reaffirmed that data may need to be
 ; retrieved above the subcomponent level, and that those data will
 ; need to be de-escaped because the receiving application won't have
 ; access to the delimiters from the original message.
 S @XTREE@(XNSEG,XNFLD)=$$DEESC(XFLD)
 ;
 ; Break out to the lowest delimiter level too.  This is not strictly an
 ; HL7 parse because it does not take actual HL7 (or realm constraining)
 ; data types into account.
 ;
 F NREP=1:1:$L(XFLD,URS) S REP=$P(XFLD,URS,NREP) I REP]"" D
 . F NCMP=1:1:$L(REP,UCS) S CMP=$P(REP,UCS,NCMP) I CMP]"" D
 . . ; Per DICOM meeting 2004-02-24, reaffirmed that data may need to be
 . . ; retrieved above the subcomponent level, and that those data will
 . . ; need to be de-escaped because the receiving application won't have
 . . ; access to the delimiters from the original message.
 . . S @XTREE@(XNSEG,XNFLD,NREP,NCMP)=$$DEESC(CMP)
 . . F NSCM=1:1:$L(CMP,USS) S SCM=$P(CMP,USS,NSCM) I SCM]"" D
 . . . S @XTREE@(XNSEG,XNFLD,NREP,NCMP,NSCM)=$$DEESC(SCM)
 . . . Q
 . . Q
 . Q
 Q
 ;
DEESC(XSCM) ; replace escape sequences with delimiter characters
 ;
 ; input:  XSCM   element data before replacement
 ;
 ; expects:  UFSESC, UCSESC, URSESC, UECESC, USSESC
 ;                delimiter escape sequences
 ;
 ; function return:  element data after replacement
 ;
 N HIT ; need another pass after each hit
 F  D  Q:'$D(HIT)
 . K HIT
 . I XSCM[UFSESC S XSCM=$P(XSCM,UFSESC)_UFS_$P(XSCM,UFSESC,2,99999),HIT=1
 . I XSCM[UCSESC S XSCM=$P(XSCM,UCSESC)_UCS_$P(XSCM,UCSESC,2,99999),HIT=1
 . I XSCM[URSESC S XSCM=$P(XSCM,URSESC)_URS_$P(XSCM,URSESC,2,99999),HIT=1
 . I XSCM[UECESC S XSCM=$P(XSCM,UECESC)_UEC_$P(XSCM,UECESC,2,99999),HIT=1
 . I XSCM[USSESC S XSCM=$P(XSCM,USSESC)_USS_$P(XSCM,USSESC,2,99999),HIT=1
 . Q
 Q $E(XSCM,1,510)
 ;
 
--- Routine Detail   --- with STRUCTURED ROUTINE LISTING ---[H[J[2J[HMAG7UP   6835     printed  Sep 23, 2025@19:35:36                                                                                                                                                                                                      Page 2
MAG7UP    ;WOIFO/MLH,PMK - Imaging - HL7 - utilities - break out message into a parse tree ;25 May 2017 2:30 PM
 +1       ;;3.0;IMAGING;**11,51,183**;MAR 19, 2002;Build 11;JUN 16, 2006
 +2       ;; Per VHA Directive 2004-038, this routine should not be modified.
 +3       ;; +---------------------------------------------------------------+
 +4       ;; | Property of the US Government.                                |
 +5       ;; | No permission to copy or redistribute this software is given. |
 +6       ;; | Use of unreleased versions of this software requires the user |
 +7       ;; | to execute a written test agreement with the VistA Imaging    |
 +8       ;; | Development Office of the Department of Veterans Affairs,     |
 +9       ;; | telephone (301) 734-0100.                                     |
 +10      ;; | The Food and Drug Administration classifies this software as  |
 +11      ;; | a medical device.  As such, it may not be changed in any way. |
 +12      ;; | Modifications to this software may result in an adulterated   |
 +13      ;; | medical device under 21CFR820, the use of which is considered |
 +14      ;; | to be a violation of US Federal Statutes.                     |
 +15      ;; +---------------------------------------------------------------+
 +16      ;;
 +17       QUIT 
 +18      ;
PARSE(XMSG,XTREE) ; break the HL7 message lines into a parse tree
 +1       ;
 +2       ; INPUT:  The single-dimensional array of message lines
 +3       ;
 +4       ; OUTPUT:  The parse tree, in the structure
 +5       ;   @XTREE@(NSEG,0)                    segment name
 +6       ;   @XTREE@(NSEG,NFLD,NREP,NCMP,NSCM)  element data
 +7       ;   @XTREE@("B",SEGID,NSEG)          null
 +8       ;
 +9       ; --------------------- scratch vars
           NEW I,J,K,L,M,X,Z
 +10      ; ------------------------------ message array index
           NEW IMSG
 +11      ; --------------------------- index of continuation data for long segs
           NEW ISUBSEG
 +12      ; ----------------------------- successor element index
           NEW ISUCC
 +13      ; ------------------------------ error flag
           NEW FERR
 +14      ; ------------------------------- segment data
           NEW SEG
 +15      ; ---------------------------- segment ID ("0th" piece)
           NEW SEGTAG
 +16      ;---------------- HL7 delimiters (universal)
           NEW UFS,UCS,URS,UEC,USS
 +17      ;-------------------------------- HL7 encoding characters
           NEW ENC
 +18      ;- HL7 escape sequences for delimiters (universal)
           NEW UFSESC,UCSESC,URSESC,UECESC,USSESC
 +19      ; --------------------------- pattern match for spanned record
           NEW PATTERN
 +20      ; ------------------------------ segment number in the parse tree
           NEW NSEG
 +21      ; -------------------------- segment number of input HL7 data
           NEW NSEGINPT
 +22      ; ------------------------------ field number in the segment
           NEW NFLD
 +23      ; ------------------------------- field data
           NEW FLD
 +24      ;
 +25      ; assume no error
           SET FERR=0
 +26       SET IMSG=""
 +27      ;
 +28      ; process MSH segment
 +29      ; If there's a message problem, return it in an NTE segment.
 +30      ;
 +31      ; array sent?
           SET IMSG=$ORDER(@XMSG@(IMSG))
 +32      ; no
           IF IMSG=""
               Begin DoDot:1
 +33               SET FERR=-1
 +34      ; have to use default field separator
 +35               SET @XMSG@(0)="NTE|1||"_FERR_";no input array found"
 +36               QUIT 
               End DoDot:1
               QUIT FERR
 +37      ; an HL7 message?
           SET SEG=$GET(@XMSG@(IMSG))
           if $EXTRACT(SEG,1,3)'="MSH"
               QUIT -2
 +38      ; no
           IF $EXTRACT(SEG,1,3)'="MSH"
               Begin DoDot:1
 +39               SET FERR=-2
 +40               SET ISUCC=$ORDER(@XMSG@(IMSG))
                   if 'ISUCC
                       SET ISUCC=IMSG+1
 +41      ; have to use default field separator
 +42               SET @XMSG@(IMSG+ISUCC/2)="NTE|1||"_FERR_";invalid HL7 message (1st 3 chars must be MSH)"
 +43               QUIT 
               End DoDot:1
               QUIT FERR
 +44      ;
 +45      ; set up delimiters and escape sequences
 +46       SET UFS=$EXTRACT(SEG,4)
           SET @XTREE@(1,1,1,1,1)=UFS
 +47       SET ENC=$PIECE(SEG,UFS,2)
           SET @XTREE@(1,2,1,1,1)=ENC
 +48       SET UCS=$EXTRACT(ENC)
           SET URS=$EXTRACT(ENC,2)
           SET UEC=$EXTRACT(ENC,3)
           SET USS=$EXTRACT(ENC,4)
 +49       SET UFSESC=UEC_"F"_UEC
           SET UCSESC=UEC_"S"_UEC
           SET URSESC=UEC_"R"_UEC
           SET UECESC=UEC_"E"_UEC
           SET USSESC=UEC_"T"_UEC
 +50       SET PATTERN="1A2AN1"""_UFS_""""
 +51       SET @XTREE@(1,0)="MSH"
           SET @XTREE@("B","MSH",1)=""
 +52       FOR NFLD=3:1:$LENGTH(SEG,UFS)
               SET FLD=$PIECE(SEG,UFS,NFLD)
               Begin DoDot:1
 +53               IF FLD]""
                       DO PROCFLD(XTREE,1,NFLD,FLD)
 +54               QUIT 
               End DoDot:1
 +55      ; process the remaining segments
 +56      ; SEG will be a concatenated series of spanned records
           SET SEG=""
 +57      ; next segment in the parse tree will be #2
           SET NSEG=2
 +58       FOR NSEGINPT=2:1
               SET IMSG=$ORDER(@XMSG@(IMSG))
               if IMSG=""
                   QUIT 
               Begin DoDot:1
 +59               SET SEG=$GET(@XMSG@(IMSG))
                   if SEG=""
                       QUIT 
 +60      ; prepare to handle very long HL7 segments (up to 32K)
                   SET ISUBSEG=""
 +61               FOR 
                       SET ISUBSEG=$ORDER(@XMSG@(IMSG,ISUBSEG))
                       if ISUBSEG=""
                           QUIT 
                       Begin DoDot:2
 +62                       SET SEG=SEG_$GET(@XMSG@(IMSG,ISUBSEG))
 +63                       QUIT 
                       End DoDot:2
 +64               SET SEGTAG=$PIECE(SEG,UFS)
                   IF SEGTAG'?1U2.3UN
                       SET FERR=-3
                       QUIT 
 +65               SET @XTREE@(NSEG,0)=SEGTAG
                   SET @XTREE@("B",SEGTAG,NSEG)=""
 +66               FOR NFLD=2:1:$LENGTH(SEG,UFS)
                       Begin DoDot:2
 +67                       SET FLD=$PIECE(SEG,UFS,NFLD)
 +68                       IF FLD]""
                               DO PROCFLD(XTREE,NSEG,NFLD-1,FLD)
 +69                       QUIT 
                       End DoDot:2
 +70      ; reinitialize SEG for the next possible concatenation
                   SET SEG=""
 +71      ; increment counter for next segment in the parse tree
                   SET NSEG=NSEG+1
 +72               QUIT 
               End DoDot:1
               if FERR
                   QUIT 
 +73       QUIT FERR
 +74      ;
PROCFLD(XTREE,XNSEG,XNFLD,XFLD) ; process a field
 +1       ;
 +2       ; input:  XTREE   name of MUMPS array for parse tree ($NA format)
 +3       ;         XNSEG   segment number for parse tree
 +4       ;         XNFLD   field number for parse tree
 +5       ;         XFLD    field data
 +6       ;
 +7       ; ------ segment name
           NEW SG
 +8       ; ---- repetition (occurrence) number
           NEW NREP
 +9       ; ----- repetition data
           NEW REP
 +10      ; ---- component number
           NEW NCMP
 +11      ; ----- component data
           NEW CMP
 +12      ; ---- subcomponent number
           NEW NSCM
 +13      ; ----- subcomponent data
           NEW SCM
 +14      ;
 +15       SET SG=@XTREE@(XNSEG,0)
 +16      ; Per DICOM meeting 2004-02-24, reaffirmed that data may need to be
 +17      ; retrieved above the subcomponent level, and that those data will
 +18      ; need to be de-escaped because the receiving application won't have
 +19      ; access to the delimiters from the original message.
 +20       SET @XTREE@(XNSEG,XNFLD)=$$DEESC(XFLD)
 +21      ;
 +22      ; Break out to the lowest delimiter level too.  This is not strictly an
 +23      ; HL7 parse because it does not take actual HL7 (or realm constraining)
 +24      ; data types into account.
 +25      ;
 +26       FOR NREP=1:1:$LENGTH(XFLD,URS)
               SET REP=$PIECE(XFLD,URS,NREP)
               IF REP]""
                   Begin DoDot:1
 +27                   FOR NCMP=1:1:$LENGTH(REP,UCS)
                           SET CMP=$PIECE(REP,UCS,NCMP)
                           IF CMP]""
                               Begin DoDot:2
 +28      ; Per DICOM meeting 2004-02-24, reaffirmed that data may need to be
 +29      ; retrieved above the subcomponent level, and that those data will
 +30      ; need to be de-escaped because the receiving application won't have
 +31      ; access to the delimiters from the original message.
 +32                               SET @XTREE@(XNSEG,XNFLD,NREP,NCMP)=$$DEESC(CMP)
 +33                               FOR NSCM=1:1:$LENGTH(CMP,USS)
                                       SET SCM=$PIECE(CMP,USS,NSCM)
                                       IF SCM]""
                                           Begin DoDot:3
 +34                                           SET @XTREE@(XNSEG,XNFLD,NREP,NCMP,NSCM)=$$DEESC(SCM)
 +35                                           QUIT 
                                           End DoDot:3
 +36                               QUIT 
                               End DoDot:2
 +37                   QUIT 
                   End DoDot:1
 +38       QUIT 
 +39      ;
DEESC(XSCM) ; replace escape sequences with delimiter characters
 +1       ;
 +2       ; input:  XSCM   element data before replacement
 +3       ;
 +4       ; expects:  UFSESC, UCSESC, URSESC, UECESC, USSESC
 +5       ;                delimiter escape sequences
 +6       ;
 +7       ; function return:  element data after replacement
 +8       ;
 +9       ; need another pass after each hit
           NEW HIT
 +10       FOR 
               Begin DoDot:1
 +11               KILL HIT
 +12               IF XSCM[UFSESC
                       SET XSCM=$PIECE(XSCM,UFSESC)_UFS_$PIECE(XSCM,UFSESC,2,99999)
                       SET HIT=1
 +13               IF XSCM[UCSESC
                       SET XSCM=$PIECE(XSCM,UCSESC)_UCS_$PIECE(XSCM,UCSESC,2,99999)
                       SET HIT=1
 +14               IF XSCM[URSESC
                       SET XSCM=$PIECE(XSCM,URSESC)_URS_$PIECE(XSCM,URSESC,2,99999)
                       SET HIT=1
 +15               IF XSCM[UECESC
                       SET XSCM=$PIECE(XSCM,UECESC)_UEC_$PIECE(XSCM,UECESC,2,99999)
                       SET HIT=1
 +16               IF XSCM[USSESC
                       SET XSCM=$PIECE(XSCM,USSESC)_USS_$PIECE(XSCM,USSESC,2,99999)
                       SET HIT=1
 +17               QUIT 
               End DoDot:1
               if '$DATA(HIT)
                   QUIT 
 +18       QUIT $EXTRACT(XSCM,1,510)
 +19      ;