· Oct 29, 2020

Reply with custom ACK

Hi,i have created a simple production with:

Business Service-EnsLib.HL7.Adapter.TCPInboundAdapter

Business  Process - EnsLib.HL7.MsgRouter.RoutingEngine

Business  Operation - EnsLib.SQL.OutboundAdapter

the sending application requires a  ACK wich is different than the ACK i choose from in the AckMode settings of the Business Service.

How do i create a custom ACK?


Discussion (4)0
Log in or sign up to continue


The way that I have dealt with this in the past is as follows:

1) Create a DTL that accepts the incoming HL7 message as its source and  a target class of EnsLib.HL7.Message with a message structure of 2.5.1:ACK. I am using HL7 2.5.1 but it will work with any version of HL7 from HL7 2.3 upwards (probably earlier versions as well but I have not worked with versions earlier that 2.3)

2) when you invoke the Transform() method of a DTL you will notice that there are three parameters

  1. pRequest (which is your source message)
  2. pResponse (which is the generated target message)
  3. aux

If you read the documentation, if the transform i invoked from a Business Rule then aux is an object and contains information about bthe Rule that invoked the Transform and a couple of other properties. However if you are invoking the transform from Cache ObjectScript then aux can be an instance of a class you create.

The way that I use 'aux' is as a mechanism for getting information into the DTL that is not present in either the sourc or target objects. In this example I want to send in the ACKCode and the ACKMessage.

So my DTL looks like this: (I apologise for having to paste in the class code but I have never found a way to attach classes to a Community Reply) so here goes.

My DTL Class reads as follows:

Class Example.Transformations.CreateNACKDTL Extends Ens.DataTransformDTL [ DependsOn = EnsLib.HL7.Message ]


Parameter REPORTERRORS = 1;


XData DTL [ XMLNamespace = "" ]
<transform sourceClass='EnsLib.HL7.Message' targetClass='EnsLib.HL7.Message' sourceDocType='2.5.1:ADT_A01' targetDocType='2.5.1:ACK' create='new' language='objectscript' >
<assign value='source.{MSH:FieldSeparator}' property='target.{MSH:FieldSeparator}' action='set' />
<assign value='source.{MSH:EncodingCharacters}' property='target.{MSH:EncodingCharacters}' action='set' />
<assign value='source.{MSH:SendingApplication.NamespaceID}' property='target.{MSH:SendingApplication.NamespaceID}' action='set' />
<assign value='source.{MSH:SendingApplication.UniversalID}' property='target.{MSH:SendingApplication.UniversalID}' action='set' />
<assign value='source.{MSH:SendingApplication.UniversalIDType}' property='target.{MSH:SendingApplication.UniversalIDType}' action='set' />
<assign value='source.{MSH:SendingFacility.NamespaceID}' property='target.{MSH:SendingFacility.NamespaceID}' action='set' />
<assign value='source.{MSH:SendingFacility.UniversalID}' property='target.{MSH:SendingFacility.UniversalID}' action='set' />
<assign value='source.{MSH:SendingFacility.UniversalIDType}' property='target.{MSH:SendingFacility.UniversalIDType}' action='set' />
<assign value='source.{MSH:ReceivingApplication.NamespaceID}' property='target.{MSH:ReceivingApplication.NamespaceID}' action='set' />
<assign value='source.{MSH:ReceivingApplication.UniversalID}' property='target.{MSH:ReceivingApplication.UniversalID}' action='set' />
<assign value='source.{MSH:ReceivingApplication.UniversalIDType}' property='target.{MSH:ReceivingApplication.UniversalIDType}' action='set' />
<assign value='source.{MSH:ReceivingFacility.NamespaceID}' property='target.{MSH:ReceivingFacility.NamespaceID}' action='set' />
<assign value='source.{MSH:ReceivingFacility.UniversalID}' property='target.{MSH:ReceivingFacility.UniversalID}' action='set' />
<assign value='source.{MSH:ReceivingFacility.UniversalIDType}' property='target.{MSH:ReceivingFacility.UniversalIDType}' action='set' />
<assign value='$tr($zdt($h,3),",: ","")' property='target.{MSH:DateTimeOfMessage}' action='set' />
<assign value='source.{MSH:Security}' property='target.{MSH:Security}' action='set' />
<assign value='source.{MSH:MessageControlID}' property='target.{MSH:MessageControlID}' action='set' />
<assign value='"ACK"' property='target.{MSH:MessageType.MessageCode}' action='set' />
<assign value='source.{MSH:MessageType.TriggerEvent}' property='target.{MSH:MessageType.TriggerEvent}' action='set' />
<assign value='"ACK"' property='target.{MSH:MessageType.MessageStructure}' action='set' />
<assign value='source.{MSH:ProcessingID}' property='target.{MSH:ProcessingID}' action='set' />
<assign value='source.{MSH:VersionID}' property='target.{MSH:VersionID}' action='set' />
<assign value='source.{MSH:SequenceNumber}' property='target.{MSH:SequenceNumber}' action='set' />
<assign value='source.{MSH:ContinuationPointer}' property='target.{MSH:ContinuationPointer}' action='set' />
<assign value='source.{MSH:AcceptAcknowledgmentType}' property='target.{MSH:AcceptAcknowledgmentType}' action='set' />
<assign value='source.{MSH:ApplicationAcknowledgmentTyp}' property='target.{MSH:ApplicationAcknowledgmentTyp}' action='set' />
<assign value='source.{MSH:CountryCode}' property='target.{MSH:CountryCode}' action='set' />
<assign value='source.{MSH:PrincipalLanguageOfMessage}' property='target.{MSH:PrincipalLanguageOfMessage}' action='set' />
<assign value='source.{MSH:AltCharsetHandlingScheme}' property='target.{MSH:AltCharsetHandlingScheme}' action='set' />
<assign value='aux.ACKCode' property='target.{MSA:AcknowledgmentCode}' action='set' />
<assign value='aux.ACKMessage' property='target.{MSA:TextMessage}' action='set' />


My AUX class definition looks like this:

Class Example.Transformations.CreateNACKDTL.AUX Extends %Persistent

Property ACKCode As %String;

Property ACKMessage As %String(MAXLEN = 200);


To generate the HL7 ACK my code reads:

classmethod GenerateACKMessage(pRequest as EnsLib.HL7.Message, byref pResponse as EnsLib.HL7.Message, pACKCode as %String(VALUELIST=",CA,CE,CR,AA,AE,AR")="AA", pACKMessage as %String(MAXLEN=500)="") as %Status

set tSC=$$$OK

try {

    set aux=##class(Example.Transformations.CreateNACKDTL.AUX).%New()

    set aux.ACKCode=pACKCode,aux.ACKMessage=pACKMessage

    set tSC=##class(Example.Transformations.CreateNACKDTL).Transform(pRequest,.pResponse,.aux) if 'tSC quit


catch ex {set tSC=ex.AsStatus()}

quit tSC


Notice that the DTL takes the SendingFaclity, SendingApplication, ReceivingFacility and ReceivingApplication and swaps them around in the DTL.

You should keep the MessageControlID the same as the incoming HL7 Message MessageControlId so that the ACK can be linked to the original HL7 request. 

The MessageTimeStamp can be updated to current Date/Time

The 'aux' mechanism is very useful. Unfortunately the documentation has one line of comment that says "if the transform is called from ObjectScript aux can contain anything you want" or words to that effect.

So I tested it in a simple example like the one above and it does indedd work and I now use it in the 30+ DTL's I amworking with at the moment.



The reason I created this DTL was as follows: I had created an Interface that sent HL7 Messages to an HTTP Operation and provided the message reaches the target server it will respond with an HL7 ACK message (There HL7 HTTP server is working in the equivalent of 'ACK Immediate" mode as opposed to "application" mode. The OnRequest() method in my Business Process calls the HTTP Operation and then calls the FILE Operation. The HTTP Operation will return an HL7 ACK message and I process that in the OnResponse() method of the Business Process. The File Operation writes the HL7 Message, that I sent to the HTTP Operation, to file and by default thats all the File Operation will do but the reason I have the file operation at all is to simulate what should happen when i send the message to HTTP and so I generate an HL7 ACK message to return to the Business Process. That is why I have the DTL to transform the source HL7 Message into a corresponding HL7 ACK Message, and I think that I randomly generate a NACK code, again to be able to test what to do if I were to get an NACK code from the HL7 HTTP Operation. So back in my Business Process OnResponse method I have code to test the response HL7 ACK message and if there is an error I handle it.

With regards the question can it be used for any message structure. Strictly speaking, Yes because the source is an EnsLib.HL7.Message and the response is an EnsLib.HL7.Message and I basically copy the MSH from the source to the target, I swap the Sending and Receiving fields around and I think I generate a new timestamp. The message Control ID remains the same. Any fields that I need to change I pass in using the AUX object. I basically have an AUX class for ever DTL I create when ever I need some runtime value that is not part of the source message in the DTL so the Event Type of the MSH:MessageType is ACK and the Message Structure is just ACK. Then the Code and the Error Text if there is one are assigned in the MSA Segment. I have noticed a mistake in my DTL. When I create the HL7 message that I am sending to the external application the Message Structure is computed based on the Message Type and Trigger Event and I have a lookup table that maps the various Trigger Events to their base Message Structure. For instance an ADT_A08 where the Message Type i ADT and the Trigger Event is A08 then the Base Message Structure for an ADT_A08 is ADT_A01 and I have a method that I call in my business Process that determines the actual message structure from the Message Type and Trigger Event but that is not being called in the DTL so you will see that I am just assigning ACK to the Message structure where the assign should actually read "ACK"_target.{MSH:MessageType.TriggerEvent}

That would give you ACK_A08