Question
· Mar 22

HL7 OBX 5 modification

I need to make changes to  OBX 5 which shows as immutable

I have tried ConstructClone, ThrowOnError, and Streams but I can't get the syntax correct

Example

OBX|1|TX|2000.02^REASON FOR REQUEST^AS4|142|REASON FOR REQUEST:      Total Cost:           0.00||||||O
                                        ^^^^ remove "REASON FOR REQUEST"                                                                            ^^ add cr/lf so down stream reports can be formatted more easily

 

I have the code done to parse out the "REASON FOR REQUEST" but I need to make the OBX 5 show the change.

Before:

OBX|1|TX|2000.02^REASON FOR REQUEST^AS4|142|REASON FOR REQUEST:      Total Cost:           0.00||||||O
OBX|1|TX|2000.02^REASON FOR REQUEST^AS4|143|Special Instructions: ||||||O
OBX|2|CE|^PROVISIONAL DIAGNOSIS|1|R52^Pain, unspecified^I10|O|||||||||||test|\0x0A\0x0D\

After

OBX|1|TX|2000.02^REASON FOR REQUEST^AS4|142|      Total Cost:           0.00||||||O

blank line added
OBX|1|TX|2000.02^REASON FOR REQUEST^AS4|143|Special Instructions: ||||||O
OBX|2|CE|^PROVISIONAL DIAGNOSIS|1|R52^Pain, unspecified^I10|O|||||||||||test|\0x0A\0x0D\

FYI

Set pRequest = ##class(EnsLib.HL7.Message).%OpenId(284)
Set pOutput = pRequest.%ConstructClone()
zw pOutput
pOutput=14@EnsLib.HL7.Message ; <OREF>

+----------------- general information ---------------
| oref value: 14
| class name: EnsLib.HL7.Message

| reference count: 2
+----------------- attribute values ------------------
| %ClonedId = 284
| %Concurrency = 1 <Set>
|%maps("runtimeIndex") = 149
|%maps("runtimeIndex",0) = ""
| AutoBuildMap = 0
| BuildMapStatus = ""
| CacheSegsGotten = 1
| DocType = "2.3:ORM_O01" <Set>
| DocTypeCategory = 2.3
| DocTypeName = "ORM_O01"
| Envelope = ""
| IsMutable = 1 <Set>     <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

I tried concantonating cr/lf.

The do statement doesn't work

 If (tItemNumberCount > 1) && (tTotalCostSegment > 0) {

                Set tOBXSegment = pRequest.GetSegmentAt("ORCgrp(1).OBRuniongrp.OBXgrp("_tTotalCostSegment_").OBX", .tStatus)

                Set tOBXText = tOBXSegment.GetValueAt(5)_"\X0D\\X0A\"  // Append escaped CR/LF representation

                Do tOBXSegment.SetValueAt(tOBXText, 5)     <<<<<<<<<<<<<<<<<<<<<<<<<<

            }

Product version: IRIS 2021.2
Discussion (16)4
Log in or sign up to continue

%ConstructClone() has a property of "deep" that is defaulted to 0, and it controls whether or not the new copy is a complete copy of the source, or if it merely copies the top level of the source object and then creates references to any sub objects contained within the source object. See more here.

My guess (without testing) is that the EnsLib.HL7.Message at a top level is being cloned with your code, but deep being false means that it's then still referencing the original EnsLib.HL7.Segment objects contained within the source EnsLib.HL7.Message object.

In your case, try changing to:

Set pOutput = pRequest.%ConstructClone(1)

Also, from a HL7 perspective, trying to add line breaks between your first and second OBX lines may not translate as you're expecting. Any receiving system trying to parse the HL7 will either ignore the break or will see it as malformed HL7.

Assuming that whatever system is receiving the HL7 is producing an output from the OBX:5 values, then you'll want to try adding in a new line and leave the OBX.5 blank, or if your receiving system supports line breaks, then this would usually be with adding a "/.br/" to the end of the OBX.5 in your first OBX (some systems may expect a different value to represent a line break, so your milage may vary).

Finally, as a general point, I would personally move this into a DTL. If there are actions going on in an ObjectScript Business Process leading up to the need to manipulate the message, then you can call the DTL from within your ObjectScript rather easily.

The code sample you've provided above is pulling the OBX segment from pRequest, not the clone. pRequest is immutable so you can't make changes to it. You should be using the pOutput object for your SetValueAt() calls, and since it's a clone you can use it for the GetValueAt() calls as well.

I provided pretty much everything you need to do this via a DTL in your other post on this subject. If you're worried about keeping things simple for ongoing support, the DTL solution is likely the better option.

I have the changes getting made using the $ConstructClone but the changes aren't getting saved to the message.

Method FromCerner(pRequest As EnsLib.HL7.Message) As %Status
{
    #dim tStatus As %Status = $$$OK
    #dim eException As %Exception.AbstractException
    #dim tOBXSegment As EnsLib.HL7.Segment
    #dim tOBXText As %String
    #dim tItemNumberCount As %Integer = 0
    #dim tItemNumberProcessed As %Integer = 0
    #dim tTotalCostSegment As %Integer = 0
    #dim tOrderType As %String
    
    //
    // Set the message
    Set tMessageType = pRequest.GetValueAt("MSH:9.1")
    
    Try {
        // Message Subtype
        Set tMessageSubType = pRequest.GetValueAt("ORCgrp(1).ORC:1")
        //$$$LOGINFO(tMessageSubType _"_" _ "tMessageSubType") // Look at incoming message type tMessageSubType is showing NW
        //
        // Check if OBR:19 contains "Implant Usage (PSAS)" OR "Issued in Clinic (PSAS)"
        Set tOrderType = pRequest.GetValueAt("ORCgrp(1).OBRuniongrp.OBRunion.OBR:19(1).1")
        //$$$LOGINFO(tOrderType_"_"_"tOrderType 19.1") // print message ORC 19 value for troubleshooting
        //
        If ((tOrderType["Implant Usage (PSAS)") || (tOrderType["Issued in Clinic (PSAS)")) {
            //$$$LOGINFO(tOrderType_"_"_"tOrderType"_"_"_"If statemenet for tOrderType")
            //
            // First loop: Count occurrences of "Item Number:"
            Set = 1
            While (pRequest.GetValueAt("ORCgrp(1).OBRuniongrp.OBXgrp("_i_").OBX")'="") {
                Set tOBXSegment = pRequest.GetSegmentAt("ORCgrp(1).OBRuniongrp.OBXgrp("_i_").OBX", .tStatus)
                //$$$LOGINFO(tOBXSegment_"_"_"tOBXSegment Whie loop entered")
                Set tOBXText = tOBXSegment.GetValueAt(5)
                //$$$LOGINFO(tOBXText_"_"_"tOBXText value set OBX 5") /// works
                //
                If (tOBXText [ "Item Number:") {
                    Set tItemNumberCount = tItemNumberCount + 1
                    //$$$LOGINFO(tItemNumberCount_"_"_"tItemNumberCount and shoud be 3")
                }
                
                If (tOBXText[ "Total Cost:") {
                    Set tTotalCostSegment = i
                    //$$$LOGINFO(tTotalCostSegment_"_"_"tTotalCostSegment check Total Cost")
                }
                
                Set = + 1
            }
            
            // Second loop: Modify OBX segments if necessary
            Set = 1
            While (pRequest.GetValueAt("ORCgrp(1).OBRuniongrp.OBXgrp("_i_").OBX")'="") {
                Set tOBXSegment = pRequest.GetSegmentAt("ORCgrp(1).OBRuniongrp.OBXgrp("_i_").OBX", .tStatus)
                //$$$LOGINFO(tOBXSegment_"_"_"tOBXSegment modify OBX to remove text")
                //Text: 63@EnsLib.HL7.Segment_tOBXSegment modify OBX to remove text
                Set tOBXText = tOBXSegment.GetValueAt(5)
                //$$$LOGINFO(tOBXText_"_"_"tOBXText")
                
                If (tOBXText [ "Item Number:") {
                    Set tItemNumberProcessed = tItemNumberProcessed + 1
                }
                
                If (tItemNumberCount > 1) && (tOBXText [ "REASON FOR REQUEST:") {
                    //$$$LOGINFO(tOBXText)
                    //$$$LOGINFO(tOBXSegment_"----"_"segment count")
                    set tOBXKey = i
                    Set tOBXText=$p(tOBXText,":",2,5) /// OBX 5 text parsed to replace in OBX 5 later
                    //// $P({ORCgrp(1).OBRuniongrp.OBRunion.OBR:UniversalServiceID.text},"-",1,2 )_"-"_{MSH:12}
                    //$$$LOGINFO(tOBXText_"--"_"Parsed")
        //
        // Start replacement process
        Set pOutput = pRequest.%ConstructClone(1)
        //$$$LOGINFO(pOutput_" ---- pOutput ConstructClone")
        //
        // pOutput
        Set tOBXSegmentpOutput = pOutput.GetSegmentAt("ORCgrp(1).OBRuniongrp.OBXgrp("_i_").OBX", .tStatus)
        //$$$LOGINFO(tOBXSegmentpOutput_"---- tOBXSegmentpOutput segment using pOutput")
        //
        Set tOBXTexttOBXSegmentpOutput = tOBXSegmentpOutput.GetValueAt(5)
        $$$LOGINFO(tOBXTexttOBXSegmentpOutput_" ---- tOBXTexttOBXSegmentpOutput OBX 5 text using pOutput")
        
        Set tSC=tOBXSegmentpOutput.SetValueAt(pOutput, 5) /// check that clone got created??
        //$$$LOGINFO(tSC_" ---- tSC for ConstructClone with tOBXSegmentpOutput")
        //
        // pRequest
        Set tOBXSegmentpRequest = pRequest.GetSegmentAt("ORCgrp(1).OBRuniongrp.OBXgrp("_i_").OBX", .tStatus)
        //$$$LOGINFO(tOBXSegmentpRequest_" ---- tOBXSegmentpRequest segment using pRequest")
        // Get OBX 5 text using pRequest
        Set tOBXSegmentpRequest= tOBXSegmentpRequest.GetValueAt(5)
        $$$LOGINFO(tOBXSegmentpRequest_"--- tOBXSegmentpRequest OBX 5 text using pRequest ")
        //
        //$$$ThrowOnError(tTransHL7.SetValueAt(tOBXText,"ORCgrp(1).OBRuniongrp.OBXgrp(14).OBX:5"))
        Do pOutput.SetValueAt(tOBXText, 5)
        ///
        Set tOBX5Text = pOutput.GetValueAt(5) // check output of message
        $$$LOGINFO(pOutput_" ---- check if clone is still there....pOutput ConstructClone")
        $$$LOGINFO(tOBX5Text_"--"_"Parsed text added to OBX5")
        //
            }
                     Set = + 1
            }
            
            // Add CR/LF after "Total Cost:" if multiple "Item Number:" exist
            //$$$LOGINFO(tItemNumberCount_" "_tTotalCostSegment_"----cr/lf")
            If (tItemNumberCount > 1) && (tTotalCostSegment > 0) {
                Set tOBXSegment = pRequest.GetSegmentAt("ORCgrp(1).OBRuniongrp.OBXgrp("_tTotalCostSegment_").OBX", .tStatus)
                //$$$LOGINFO(tOBXSegment_"----"_"tOBXSegment from cr/lf")
                Set tOBXText = tOBXSegment.GetValueAt(5)_"\X0D\\X0A\"  // Append escaped CR/LF representation
                //$$$LOGINFO(tOBXText_" cr/lf OBX mod")
                Do tOBXSegment.SetValueAt(tOBXText, 5)
            }
        }
        
        // Process ORM messages
        If tMessageSubType = "RE" {
            Set tResultType = pRequest.GetValueAt("ORCgrp(1).OBRuniongrp.OBXgrp(1).OBX:11")
            If tResultType = "D" {
                $$$ThrowOnError(..ProcessPurgeDocumentToVista(pRequest))
            Else {
                $$$ThrowOnError(..ProcessNewDocumentToVista(pRequest))
            }
        }
        ElseIf (tMessageType="ORM") {
            $$$ThrowOnError(..SendRequestAsync(..VistaTarget, pRequest, 1, "OutputCernerToVistaORM"))
        }
        ElseIf (tMessageType="ORR") || (tMessageType="ACK") {
            $$$ThrowOnError(..SendRequestAsync(..VistaTarget, pRequest, 1, "OutputCernerToVistaORR"))
        }
        Else {
            $$$ThrowOnError($$$ERROR($$$GeneralError, "Error: unable to process message from Cerner. Message type " _ tMessageType _ " received."))
        }
    }
    Catch eException {
        Set tStatus = eException.AsStatus()
        Set tStatus = $$$ADDSC(tStatus, ..CreateAndSendAlert(eException.DisplayString(), pRequest))
    }
    Quit tStatus
}



 

There are a few issues with this code, but the biggest one is that you've created a clone and should be modifying the clone (pOutput) exclusively. However, you're still calling SetValueAt() against a segment pulled from pRequest.

The above notwithstanding, you're still sending the original unchanged message object (pRequest) to the target operation with the SendRequestAsync() call.

Fundamentally, you should:

  1. Create a clone of the request object pRequest (i.e. pOutput)
  2. Modify the clone, which is a mutable copy of the original message
  3. Send the modified clone (pOutput) to the target operation

Somewhere between the %ContstructClone() call and the SetValueAt() call, you'll be using the GetSegmentAt() method of the clone to get the segment object into tOBXSegmentpOutput.

The syntax of the SetValueAt() call should have the string to store as the first argument rather than the pOutput message object.

tOBXSegmentpOutput is a reference to the segment in the clone message. It should not need to be explicitly saved, and I'm pretty sure SaveData() doesn't do what you think it does anyway.

I can't get it to work

Method FromCerner(pRequest As EnsLib.HL7.Message) As %Status
{
    #dim tStatus As %Status = $$$OK
    #dim eException As %Exception.AbstractException
    #dim tOBXSegment As EnsLib.HL7.Segment
    #dim tOBXText As %String
    #dim tItemNumberCount As %Integer = 0
    #dim tItemNumberProcessed As %Integer = 0
    #dim tTotalCostSegment As %Integer = 0
    #dim tOrderType As %String
    
    //
    // Set the message
    Set tMessageType = pRequest.GetValueAt("MSH:9.1")
    
    Try {
        // Message Subtype
        Set tMessageSubType = pRequest.GetValueAt("ORCgrp(1).ORC:1")
        //$$$LOGINFO(tMessageSubType _"_" _ "tMessageSubType") // Look at incoming message type tMessageSubType is showing NW
        //
        // Check if OBR:19 contains "Implant Usage (PSAS)" OR "Issued in Clinic (PSAS)"
        Set tOrderType = pRequest.GetValueAt("ORCgrp(1).OBRuniongrp.OBRunion.OBR:19(1).1")
        //$$$LOGINFO(tOrderType_"_"_"tOrderType 19.1") // print message ORC 19 value for troubleshooting
        //
        If ((tOrderType["Implant Usage (PSAS)") || (tOrderType["Issued in Clinic (PSAS)")) {
            //$$$LOGINFO(tOrderType_"_"_"tOrderType"_"_"_"If statemenet for tOrderType")
            //
            // First loop: Count occurrences of "Item Number:"
            Set = 1
            While (pRequest.GetValueAt("ORCgrp(1).OBRuniongrp.OBXgrp("_i_").OBX")'="") {
                Set tOBXSegment = pRequest.GetSegmentAt("ORCgrp(1).OBRuniongrp.OBXgrp("_i_").OBX", .tStatus)
                //$$$LOGINFO(tOBXSegment_"_"_"tOBXSegment Whie loop entered")
                Set tOBXText = tOBXSegment.GetValueAt(5)
                //$$$LOGINFO(tOBXText_"_"_"tOBXText value set OBX 5") /// works
                //
                If (tOBXText [ "Item Number:") {
                    Set tItemNumberCount = tItemNumberCount + 1
                    //$$$LOGINFO(tItemNumberCount_"_"_"tItemNumberCount and shoud be 3")
                }
                
                If (tOBXText[ "Total Cost:") {
                    Set tTotalCostSegment = i
                    //$$$LOGINFO(tTotalCostSegment_"_"_"tTotalCostSegment check Total Cost")
                }
                
                Set = + 1
            }
            
            // Second loop: Modify OBX segments if necessary
            Set = 1
            While (pRequest.GetValueAt("ORCgrp(1).OBRuniongrp.OBXgrp("_i_").OBX")'="") {
                Set tOBXSegment = pRequest.GetSegmentAt("ORCgrp(1).OBRuniongrp.OBXgrp("_i_").OBX", .tStatus)
                //$$$LOGINFO(tOBXSegment_"_"_"tOBXSegment modify OBX to remove text")
                //Text: 63@EnsLib.HL7.Segment_tOBXSegment modify OBX to remove text
                Set tOBXText = tOBXSegment.GetValueAt(5)
                //$$$LOGINFO(tOBXText_"_"_"tOBXText")
                
                If (tOBXText [ "Item Number:") {
                    Set tItemNumberProcessed = tItemNumberProcessed + 1
                }
                
                If (tItemNumberCount > 1) && (tOBXText [ "REASON FOR REQUEST:") {
                    //$$$LOGINFO(tOBXText)
                    //$$$LOGINFO(tOBXSegment_"----"_"segment count")
                    set tOBXKey = i
                    Set tOBXText=$p(tOBXText,":",2,5) /// OBX 5 text parsed to replace in OBX 5 later
                    //// $P({ORCgrp(1).OBRuniongrp.OBRunion.OBR:UniversalServiceID.text},"-",1,2 )_"-"_{MSH:12}
                    //$$$LOGINFO(tOBXText_"--"_"Parsed")
        //
        // Start replacement process
        Set pOutput = pRequest.%ConstructClone(1)
        $$$LOGINFO(pOutput_" ---- pOutput ConstructClone")
        // pOutput
        Set tOBXSegmentpOutput = pOutput.GetSegmentAt("ORCgrp(1).OBRuniongrp.OBXgrp("_i_").OBX", .tStatus)
        //$$$LOGINFO(tOBXSegmentpOutput_"---- tOBXSegmentpOutput segment using pOutput")
        //Set tOBX5Text = pOutput.GetValueAt(5) // check output of message
        $$$LOGINFO(tOBXText_"--"_"Parsed text to be replaced in OBX5")
        do tOBXSegmentpOutput.SetValueAt(tOBXText, 5)
        //do pOutput.%Save()
        //