none
HL7 ACK / NACK Handling, Consuming , correlate in orchestrations. RRS feed

  • Question

  • Guys,

    I have scenario where i need to accept HL7 ACK / NAK messages back to the business process, in detail i call HIS system for admissions and accept all ACK  -AL and on sucessfull ADT-A01 i need to consume the genrated ACK back to the process to do some more steps.

    i have seen the ACK generated..but getting some routing failure exceptions. i know i need to deal with that message.

    so what would be the best practice to correlate the ACK/NAK or Consume the ACK/NAK back to orchestration.

    Thanks for your help in advance,

    SAM.

    Thursday, March 3, 2011 6:21 PM

Answers

  • I had a similar issue. Sorry for the lengthy description but the solution isn't simple unfortunately.

     

    My solution was to write my own lite HL7 parser called which is called from a 2 way webservice bound orchestration. The parser gets and checks the HL7 message metadata from FHS, BHS and MSH segments. It also constructs an Xml instance to store this data and structure in the database via the Oracle WCF-Custom adapter. By enforcing a unique constraint in your database you can check the FHS3/4, BHS3/4, and MSH 3/4 components(Sending application and facility) and the File Control ID (FHS10), Batch Control ID (BHS10), and Message Control ID (MSH10), and manually generate a NACK HL7 message from a string template with a message indicating the violation.

    Immediately after verifying this and populating the database with all records, I also create one file in a directory for each valid HL7 message part (each MSH in a batch). The filename of each of the messages is the primary unique key of all message parts in the database. The FILE adapter will promote this property as  FILE.ReceivedFileName for use in an orchestration. The FILE adapter has the HL7 DASM Pipeline selected. One orchestration is created for each message type and version, including ACK. 

    This is where you have a couple options. Are you doing any semantic validations? As in are you wanting to cross reference anything before you want this message to proceed downstream? Such as checking that all patient records for minors have guardian information? Or are you just wanting to validate that the message is well formed and send it on? Keep in mind the non-Ack orchestrations must have a filter to check BTAHL7Schemas.ParseError = false. You also must have a send port to pick up the failed HL7 RawString message for when ParseError = true, you can make a simply pipeline component in which the Execute method returns null to do this at minimal cost.

    The DASM generates two messages (1), the ACK and one for the corresponding MessageType. There is a big frustration here that I had to come to terms with, the NACK discards all properties which were assigned to the received message. This means that the ACK no longer has the primary key in the FILE.ReceivedFileName property. But remember that there is a unique combination of MSH 3, 4 and 10 fields in the database. These IDs are used to populate custom properties (2) on the received ACK Xml message  in the orchestration. The ACK Xml message is sent out a 1 way port which is bound to an Oracle WCF-Custom adapter send port. There is a custom pipeline which has first the HL7 ASM component and a custom component. After converting the Xml to the HL7 RawString, this custom component takes the resulting HL7 string and the message properties and creates an Xml instance which conforms so a schema to update the record specified by the MSH3, 4 and 10 properties using the Oracle WCF-Custom adapter.

    Meanwhile, the non-ACK orchestration for this message type/version (which exists only if there was no error) will perform further validations on the message (and overwrite the ACK from the previous paragraph in case of failure) and mark the corresponding record in the database as ready to go as it sends the message on to its next destination. All using the File.ReceivedFileName property to locate the corresponding record in the database.

    Meanwhile STILL, the original receiving orchestration is polling an Oracle function which determines if all valid acknowledgements and other messages (Query responses) are ready for the response to the caller. Once this function returns true the orchestration uses the Oracle WCF-Custom adapter (3) to retrieve the data so each response message part can be reassembled (4) with Xslt mapping it to the response schema to send to the caller.

     

    (1) This is only true because we validate the metadata ahead of time. In doing so this prevents another 3rd behavior where no NACK is generated (because party resolution is impossible) and you have only a RawString instance message which cannot be differentiated from the ACKs we're generating now because there is not a NOT EXISTS filter operator.

    If you do have a situation where you do need to differentiate between unparseable metadata HL7 and other ACKs you can use this pipeline component which changes the MessageClass to identify it separately. http://hl7dasmpropfixer.codeplex.com/

    (2) BTAHL7Schemas.MSH3_xMSH4 and MSH10  (custom property schema)

    (3) Using schemas generated with the LOB adapter pack consume adapter pack wizard

    (4) If you happen to be using Oracle I found it more efficient to aggregate the CLOB messages (each MSH) in the view with a User Defined Collection function.

     

     

    Performance testing shows I'm capable of processing about 50 messages per second with full validations enabled as well as execution of custom validations, I don't know if that is sufficiently fast for your needs.


    Saturday, August 6, 2011 4:56 AM

All replies

  • hello,

     

    I have found the following links with best practices hopefully that will help, if not let me know and I can look further. 

    http://blog.hl7-info.com/archive/2009/08/15/HL7_Best_Practices.aspx#customschemas
    http://www.microsoft.com/downloads/en/confirmation.aspx?displaylang=en&FamilyID=ce84af68-35fc-4ded-b9f8-91feff05d8d0


    Best Regards, Imelda
    Friday, March 11, 2011 1:40 AM
  • After further investigation, we decided that we will need a lot more information.  It will also take quite a bit of time to provide you a sample.  

    It would be better if you could to open a regular incident

     


    Best Regards, Imelda
    Wednesday, March 16, 2011 10:24 PM
  • I had a similar issue. Sorry for the lengthy description but the solution isn't simple unfortunately.

     

    My solution was to write my own lite HL7 parser called which is called from a 2 way webservice bound orchestration. The parser gets and checks the HL7 message metadata from FHS, BHS and MSH segments. It also constructs an Xml instance to store this data and structure in the database via the Oracle WCF-Custom adapter. By enforcing a unique constraint in your database you can check the FHS3/4, BHS3/4, and MSH 3/4 components(Sending application and facility) and the File Control ID (FHS10), Batch Control ID (BHS10), and Message Control ID (MSH10), and manually generate a NACK HL7 message from a string template with a message indicating the violation.

    Immediately after verifying this and populating the database with all records, I also create one file in a directory for each valid HL7 message part (each MSH in a batch). The filename of each of the messages is the primary unique key of all message parts in the database. The FILE adapter will promote this property as  FILE.ReceivedFileName for use in an orchestration. The FILE adapter has the HL7 DASM Pipeline selected. One orchestration is created for each message type and version, including ACK. 

    This is where you have a couple options. Are you doing any semantic validations? As in are you wanting to cross reference anything before you want this message to proceed downstream? Such as checking that all patient records for minors have guardian information? Or are you just wanting to validate that the message is well formed and send it on? Keep in mind the non-Ack orchestrations must have a filter to check BTAHL7Schemas.ParseError = false. You also must have a send port to pick up the failed HL7 RawString message for when ParseError = true, you can make a simply pipeline component in which the Execute method returns null to do this at minimal cost.

    The DASM generates two messages (1), the ACK and one for the corresponding MessageType. There is a big frustration here that I had to come to terms with, the NACK discards all properties which were assigned to the received message. This means that the ACK no longer has the primary key in the FILE.ReceivedFileName property. But remember that there is a unique combination of MSH 3, 4 and 10 fields in the database. These IDs are used to populate custom properties (2) on the received ACK Xml message  in the orchestration. The ACK Xml message is sent out a 1 way port which is bound to an Oracle WCF-Custom adapter send port. There is a custom pipeline which has first the HL7 ASM component and a custom component. After converting the Xml to the HL7 RawString, this custom component takes the resulting HL7 string and the message properties and creates an Xml instance which conforms so a schema to update the record specified by the MSH3, 4 and 10 properties using the Oracle WCF-Custom adapter.

    Meanwhile, the non-ACK orchestration for this message type/version (which exists only if there was no error) will perform further validations on the message (and overwrite the ACK from the previous paragraph in case of failure) and mark the corresponding record in the database as ready to go as it sends the message on to its next destination. All using the File.ReceivedFileName property to locate the corresponding record in the database.

    Meanwhile STILL, the original receiving orchestration is polling an Oracle function which determines if all valid acknowledgements and other messages (Query responses) are ready for the response to the caller. Once this function returns true the orchestration uses the Oracle WCF-Custom adapter (3) to retrieve the data so each response message part can be reassembled (4) with Xslt mapping it to the response schema to send to the caller.

     

    (1) This is only true because we validate the metadata ahead of time. In doing so this prevents another 3rd behavior where no NACK is generated (because party resolution is impossible) and you have only a RawString instance message which cannot be differentiated from the ACKs we're generating now because there is not a NOT EXISTS filter operator.

    If you do have a situation where you do need to differentiate between unparseable metadata HL7 and other ACKs you can use this pipeline component which changes the MessageClass to identify it separately. http://hl7dasmpropfixer.codeplex.com/

    (2) BTAHL7Schemas.MSH3_xMSH4 and MSH10  (custom property schema)

    (3) Using schemas generated with the LOB adapter pack consume adapter pack wizard

    (4) If you happen to be using Oracle I found it more efficient to aggregate the CLOB messages (each MSH) in the view with a User Defined Collection function.

     

     

    Performance testing shows I'm capable of processing about 50 messages per second with full validations enabled as well as execution of custom validations, I don't know if that is sufficiently fast for your needs.


    Saturday, August 6, 2011 4:56 AM
  • Just finished a second pass optimization. After cleaning up some fluff I was able to increase performance to 900 messages per second. I think this proves the solution is effective enough for enterprise use if there was concern regarding that.

     

    I believe this can be further improved with use of .Net 4 parallelization, but for my needs that is unnecessary. I am working with my client to see if they will allow me to reproduce this code more generically for open sourcing. If I do so I will update this thread and link to the codeplex page for it. 


    Saturday, August 13, 2011 12:12 AM
  • Doing another pass on this app to make it more streamlined, I discovered a far easier way to correlate the HL7 request and the generated acknowledgement. 

    First, create a Receive Validation Pipeline Component.

    Add a self initializing protected property to the class to uniquely identify this correlated pair, I used just 

    ...
    protected Guid uniqueId = Guid.newGuid();
    
    public Microsoft.BizTalk.Message.Interop.IBaseMessage Execute(Microsoft.BizTalk.Component.Interop.IPipelineContext pc, Microsoft.BizTalk.Message.Interop.IBaseMessage inmsg)
            {...

    Then just write that to a context and you can use it for routing or correlation sets which link both your request and generated ack. Should be much more efficient this way.

    This unique id property will be unique to each instance of the pipeline which is created for each received message. 
    Wednesday, April 4, 2012 11:46 PM