Giter Site home page Giter Site logo

dib0 / nhapitools Goto Github PK

View Code? Open in Web Editor NEW
85.0 17.0 59.0 116.76 MB

The NHapiTools are tools that will make using NHapi (the open source .Net HL7 implementation) easier. NHapi has a steep learning curve and not everything works as easy as it should. NHapiTools aims to improve that without tampering with NHapi itselves.

License: MIT License

C# 100.00% Batchfile 0.01%

nhapitools's Introduction

Build Status

NHapiTools

The NHapiTools are tools that will make using NHapi (the open source .Net HL7 implementation) easier. NHapi has a steep learning curve and not everything works as easy as it should. NHapiTools aims to improve that without tampering with NHapi itselves.

Key Benefits

  • Source generation that will generate extension methods on NHapi assemblies. (some generated assemblies are provided)
  • Generic extention methods.
  • Easy IO tools for reading multiple files, implementing MLLP and filtering Base64 data reducing memory use and increasing speed.
  • Tools to generate ACK messages based on any message
  • Tools to implement custom (standard) segments (which is not possible with NHapi)
  • Default validation rules to be used by the NHapi parser
  • Two sets of context implementation to easily add all or configurable validation rules.

Requirements

NHapiTools currently targets version 4.5 of the .NET Framework

Getting Started

The easiest way to get started using nHapi is to use the NuGet package 'NHapiTools':

Using the package manager console withing visual studio, simply run the following command:

PM > Install-Package NHapiTools

Introduction

On my blog I get a lot of questions on how to set up a complete .Net system for HL7 message integration. In other words: all over the world developers create integration components from scratch to add HL7 integration to their applications. After working for a while with NHapi, the most complete and free component to support HL7 with .Net, I started to miss functionality. To make my life easier (and hopefully the life of other developers, I created the NHapiTools. In this document the functionality that NHapiTools adds to the NHapi component will be explained.

The sources and releases of NHapiTools can be found on Github: https://github.com/dib0/NHapiTools

Also check out HL7Fuse: https://github.com/dib0/HL7Fuse

And, of course, The NHapi Beginner’s Guide: http://www.smashwords.com/books/view/344824

The Structure of NHapiTools

For the most important part NHapiTools is a set of extension methods that extend NHapi. NHapi uses a code generator to generate the HL7 message structure into classes and then assemblies. NHapiTools uses the same mechanism to generate classes with extension methods based on the NHapi assemblies. The generated classes are provided within the source, so you don’t have to do this (of course, if you want to, you can).

The extension methods make it far easier to enumerate through the messages. For example: they will allow you to use the “foreach” statement, which the basic NHapi structure doesn’t allow.

Besides the extension methods and richer validations NHapiTools provide tools for commonly used patterns within HL7 and HL7 integration. For example support for MLLP, a TCP/IP MLLP client, a method of generation ACK messages based on any method and alternative streams that filter out attached Base64 encoded documents that makes parsing large message easier and (much) faster.

Extension Methods

NHapi, probably the part you'll never use, is actually a code generator. Based on the HL7 structure database, that can be obtained through the local HL7 organization, it will generate the classes for the data types, segments and message structures. It also forces the Hl7 standard within these classes.

Annoying about in these NHapi HL7 structures are the way repeating segments or fields are handled. NHapi generates a method to get the number of repetitions with a name like Get{property name}RepetitionsUsed(). With the methods Get{property name}(int repetition) you can get to a specific repeating field. Note that the last one can also be used to add a new repetition if you are creating a new message. The annoying part is that you always have to write the same for-loop to go through the data. I would rather be using the foreach statement, which will give you more readable and maintainable code.

The extention methods in NHapiTools do just that. NHapiTools contains a code generator that generates the extention methods based on the NHapi assemblies. NHapiTools also contain the compiled version, so you don't have to run the generator. That means by using the namespace of the extention assemblies you can use the using statements. So you can turn this:

for (int i=0; i< oMessage.PATIENT_RESULTRepetitionsUsed; i++)
{
    PID pid = oMessage.GetPATIENT_RESULT(i).PATIENT.PID;
    for (int y=0; y < pid.AlternatePatientIDPIDRepetitionsUsed; y++)
    {
        CX alterateId = pid.GetAlternatePatientIDPID(y);
        // do something with the alternate ID
    }
}

Into this:

foreach (ORU_R01_PATIENT_RESULT patient in oMessage.GetAllPATIENT_RESULTRecords())
{
    PID pid = patient.PATIENT.PID;
    
    foreach (CX alternateId in pid.GetAlternatePatientIDPIDRecords())
    {
        // do something with the alternate ID
    }
}

Besides methods to easily use the foreach statement NHapiTools also provide a clearer way to add repeating fields. Instead of the NHapi method to “get” the next (non existing) enumeration NHapiTools provides a simple add extention methods to perform this operation.

Validation

The validation of HL7 messages is embedded in the classes of NHapi. That means that the messages are validated if they are conform the HL7 specification standard. The NHapi parser utilizes a context for parsing. If you choose to use your own context you can influence the parsing. For example, by adding validation rules.

The way these validation rules are implemented and the context you have to create aren't really complicated, but since the rules are version specific and you might have to develop complex rules or many rules, maintaining them can be a hassle. NHapiTools provides two different contexts and a set of generic validation rules.

Automated Context

The automated context allows you to develop you own set of validation rules. You will have to implement the ISpecificEncodingRule, ISpecificMessageRule or the ISpecificPrimitiveRule interface. The automated context will search through every type within a namespace and applies all the rules it can find to the parser. This will save you a lot of time maintaining and building the context. It needs a namespace:

<appSettings>
	<add key="NHapiRulesNamespace" value="TestApp, version=1.0.0.0"/>
</appSettings>

To use the automated context you will need to add the following lines of code:

PipeParser parser = new PipeParser();
AutomatedContext context = new AutomatedContext(parser.ValidationContext);
parser.ValidationContext = context;

Configurable Context

The configurable context also allows you to implement your own validation rules, by implementing the same interfaces. The difference with the automated context is that you can configure which rules must be applied. This will give you far more control over the behavior of you application in different circumstances. To configure rules do the following:

<configSections>
	<sectionGroup name="hl7ValidationRulesGroup">
		<section name="hl7ValidationRules" type="NHapiTools.Base.Configuration.ValidationSection, NHapiTools.Base, version=1.0.0.0"/>
	</sectionGroup>
</configSections>

<hl7ValidationRulesGroup>
	<hl7ValidationRules>
		<EncodingRules>
			<add Name="EncodingRule1" Assembly="TestApp" Type="TestApp.CustomRules.EncodingRule1"/>
		</EncodingRules>
		<MessageRules>
			<add Name="MessageRule" Assembly="TestApp" Type="TestApp.CustomRules.MessageRule"/>
			<add Name="MandatorySegments" Assembly="NHapiTools.Base" Type=" NHapiTools.Base.Validation.Rule.MessageSegmentMandatoryRule"/>
			<add Name="MessageRegExField" Assembly="NHapiTools.Base" Type=" NHapiTools.Base.Validation.Rule.MessageRegExRule"/>
			<add Name="MessageFieldIntOnly" Assembly="NHapiTools.Base" Type=" NHapiTools.Base.Validation.Rule.MessageFieldIntOnlyRule"/>
			<add Name="MessageFieldMandatory" Assembly="NHapiTools.Base" Type=" NHapiTools.Base.Validation.Rule.MessageFieldMandatoryRule"/>
		</MessageRules>
	</hl7ValidationRules>
</hl7ValidationRulesGroup>

To use the configurable context with the parser you will need to add the following code:

PipeParser parser = new PipeParser();
ConfigurableContext context = new ConfigurableContext(parser.ValidationContext);
parser.ValidationContext = context;

Standard Validation Rules

Developing you own set of validation rules can be a hassle on it's own. It would be better if there were a set of configurable rules the will cover the basics (and hopefully all that you will need). NHapiTools provides a set of generic rules that you can use by configuration. These work well with the configurable context.

The following rules are provided:

MessageFieldIntOnlyRule Checks the content of a specific field in the message and allows only integer values.
MessageFieldMandatoryRule Check if a specific field in the message exists and if the field isn't empty.
MessageRegExRule Checks the content of a specific field against a regular expression.
MessageSegmentMandatoryRule Checks if a given segment exists. Which is otherwise quite hard to check.

The various rules require configuration. All rules require a unique name, Hl7 version they apply to, the message type they apply to and the trigger event. All of these option, except for the first one, allow the '*' (multiple unknown characters) and '?' (a single unknown character) as wild cards. If a rule needs a field indicator, this always follows the standard HL7 notation (e.g. “PID-2-1”). The configuration looks like this:

<configSections>
	<sectionGroup name="SpecificRulesGroup">
		<section name="SpecificRules" type="NHapiTools.Base.Configuration.MessageRulesSection, NHapiTools.Base, version=1.0.0.0"/>
	</sectionGroup>
</configSections>

And:

<SpecificRulesGroup>
	<SpecificRules>
		<SegmentMandatoryRule>
			<add Name="MandatoryPIDOn2.1_ADT_A01" Version="2.4" MessageType="ADT" TriggerEvent="A01" MadatorySegment="PID"/>
			<add Name="MandatoryPIDOn*" Version="*" MessageType="*" TriggerEvent="*" MadatorySegment="PID"/>
		</SegmentMandatoryRule>
		<MessageRegExRule>
			<add Name="RegExOn2.1_ADT_A01" Version="2.4" MessageType="ADT" TriggerEvent="A01" FieldIndicator="MSH-9-1" RegEx=""/>
			<add Name="RegExOnMSH-10-1" Version="*" MessageType="*" TriggerEvent="*" FieldIndicator="MSH-10-1" RegEx="^\d+$" AllowNull="false"/>
		</MessageRegExRule>
		<MessageFieldIntOnlyRule>
			<add Name="IntOnlyOnMSH-3-1" Version="*" MessageType="ADT" TriggerEvent="*" FieldIndicator="PID-3-1" AllowNull="true"/>
		</MessageFieldIntOnlyRule>
		<MessageFieldMandatoryRule>
			<add Name="MandatoryFieldADT_MSH-2-1" Version="*" MessageType="ADT" TriggerEvent="*" FieldIndicator="PID-2-1"/>
			<add Name="MandatoryFieldDFT_MSH-2-1" Version="*" MessageType="DFT" TriggerEvent="*" FieldIndicator="PID-2-1"/>
		</MessageFieldMandatoryRule>
	</SpecificRules>
</SpecificRulesGroup>

Utils

There is quite a diversity of tools within the NHapiTools. In this chapter IO tools, network tools and HL7 tooling.

IO

The IO namespace has two stream classes and two enumeration classes. The HL7FilterBase64AttachmentsStream allows you to read a HL7 message, but separate Base64 content. In some cases, for example lab results that contain PDF files, the HL7 message become quite large. These large messages take a lot of time to parse with NHapi. Using this stream will insert a dummy text (thus increasing the parsing speed) and allows you to read the attachments after parsing the message.

The other stream is the MultipleFilesStream. This class is not directly HL7 related, but just allows you to read the content of a complete directory as one stream, so you don’t have to care about which file you are reading or not. Using the OnFileCompleted event (that fires once a file has been completely read) you can perform specific file actions, like archiving or deleting the file.

The HL7InputStreamMessageEnumerator is a port of the Hl7InputStreamMessageIterator class from the Hapi project. This class wasn’t migrated to the NHapi source code, but is can be useful. It allows you to enumerate HL7 messages from a stream (preferably a file stream) and parses the message to an IMessage object. So you don’t have to care about when a HL7 message begins or ends.

The HL7InputStreamMessageStringEnumerator does the same thing, but enumerates the message as a string instead of parsing it to an IMessage object.

Net

The SimpleMLLPClient is a TCP/IP client that allows you to connect to a HL7 server and send a HL7 message using the MLLP protocol. It will return the response as an IMessage object. It uses the MLLP class to perform MLLP actions.

HL7

There are some other interesting tools for handeling HL7 messages. The Ack class is a simple ACK message generator, that generates any possible ACK message based on a IMessage object.

GenericMessageWrapper

A slightly more complicated tool is the GenericMessageWrapper. As you know you can add custom segments to HL7 messages. The parser will automatically recognize these and load the custom segments. The harder part is that you need to override the message structure as well. If you need to override standard segments, for example the PID segment, the only way is to add a custom implementation of the complete message. But if you want to use this custom PID segment on all messages, you’re about to recompile the complete NHapi structure.
On the other hand, making custom changes in standard components should always be prevented!

Using the GenericMessageWrapper you can parse any message to this implementation of an AbstractMessage. This wrapper allows you to cast the message to the right class structure, while maintaining the possibility to easily get to custom implementations of segments etc.
So this wrapper allows you to keep all the standard components standard, but adds enough flexibility to implement your own custom implementations. To use this GenericMessageWrapper:

EnhancedModelClassFactory emch = new EnhancedModelClassFactory();
PipeParser parser = new PipeParser(emch);
emch.ValidationContext = parser.ValidationContext;

IMessage im = parser.Parse(txtMessage);
GenericMessageWrapper gcw = im as GenericMessageWrapper;
if (gcw != null)
    IMessage originalMessage = gcw.Unwrap();

nhapitools's People

Contributors

benmgilman avatar dib0 avatar ianyates avatar joranvar avatar milkshakeuk avatar nick-marnik avatar sschoop avatar stevebering avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nhapitools's Issues

Documentation?

Hi, is there any documentation for this library? I have found some links to your legacy blog which does not exist any more. I tried to follow the examples such as this one:

foreach (ORU_R01_PATIENT_RESULT patient in oMessage.GetAllPATIENT_RESULTRecords())
{
PID pid = patient.PATIENT.PID;

foreach (CX alternateId in pid.GetAlternatePatientIDPIDRecords())
{
    // do something with the alternate ID
}

}

However I am not even sure how to convert a parsed IMessage created like this:

PipeParser parser = new PipeParser();
IMessage m = parser.Parse(stringMessage);

into ORU_R01 class which GetAllPATIENT_RESULTRecords() method needs

Custom segment

Hi,
How does NHapiTools support to

  1. Add non standard z segment
  2. Add custom properties to newly added z segment

can you provide some example ?

Incorrect message structure for Acks generated with MakeACK

There appears to be an issue in the MakeACK method causing it to create invalid Acks when the message structure (MSH-9-3) field is populated in the incoming message.

As the MSH segment is copied from the inbound message to the ACK and MSH-9-3 is not updated Acks generated from an incoming message that had MSH-9-3 populated will also have MSH-9-3 populated. As Ack’s always have a differing structure to the inbound message this in not valid.

SimpleMLLPClient

Hi dib0
I need send OML messages. I bought the book thinking, because I had think this maybe contains an example. I had searched for all documentation of NHapiTools and HL7Fuse, but I found nothing.

Please, would you want to give an example?

note: The blog of you doesn't work

SendHL7Message may never return

Case:
Let's say the destination service may not reply with any bytes if the message you send has incorrect values set in segments; it simply ignore the message you send and ReadByte() return -1

This is a snippet code of function SendHL7Message:

StringBuilder sb = new StringBuilder();
bool messageComplete = false;
while (!messageComplete) {
  int b = streamToUse.ReadByte();
  if (b != -1)
    sb.Append((char) b);

  messageComplete = MLLP.ValidateMLLPMessage(sb);
}

streamToUse.ReadByte() will always return -1 in that case, and function MLLP.ValidateMLLPMessage will always return false since sb.length is not > 3, look at below code:

public static bool ValidateMLLPMessage(StringBuilder sb) {
  bool result = false;

  if (sb.Length > 3) {
    if (((int) sb[0] == MLLP_START_CHARACTER)) {
      if (((int) sb[sb.Length - 2] == MLLP_FIRST_END_CHARACTER) && ((int) sb[sb.Length - 1] == MLLP_LAST_END_CHARACTER))
        result = true;
    }
  }

  return result;
}

What happens is that the code will get stuck in infinite loop, SendHL7Message will never return and the caller will get stuck.

There should be a way to break from this loop, maybe return a custom timeout error if streamToUse.ReadByte() repeatedly returns 0 bytes for some time?

SimpleMLLPClient charachters encoding

Unfortunately, the way the SimpleMLLPClient is written is suitable only in case of ANSI (single byte) encoding of communication. The problem is in receiving answer and no issues in sending. The method “public string SendHL7Message(string message)” takes for granted that incoming message is composed by single byte encoded characters, which IMHO is wrong. Here is the code:

sb.Append((char) b); (line 109, SimpleMLLPClient.cs)

which takes one byte and interprets it as if it is ANSI character. In case of utf-8 for example, non-ANSI characters comes wrongly encoded. I could make some changes, if help is appreciated. I think that SimpleMLLPClient should have one more property – Encoding. Basically, object should capture just bytes, and check for end of message (messageComplete = MLLP.ValidateMLLPMessage(sb);) with ad-hoc appropriate encoding (set by proposed by me new property).

nHapi returns error for valid HL7 files while doing validation concurrently

We are using nHapitool to validate batch of 1000 HL7 files (want to use it for much more files).
We are using Parallel.ForEachAsync loop with degree of parallelism set to 250.
For some of the valid HL7 files we receive errors as below:

  1. An item with the same key has already been added. Key: ^~&
  2. Index was outside the bounds of the array.Exception: System.IndexOutOfRangeException: Index was outside the bounds of the array
  3. Operations that change non-concurrent collections must have exclusive access. A concurrent update was performed on this collection and corrupted its state. The collection's state is no longer correct.
    Please suggest on this, we are kind of blocked on this.

We are using nhapitool version 2.0.3 and .net 6.0

Thanks

Unescaped & or ^ in OBX-5 FT from clinic

I am receiving Unescaped & or ^ in the OBX-5 when a lab is sending Formatted Text (FT) such as links or html link tags (www.medical-website.com?param1=value1&param2=value2)

I know that the clinics should be escaping these characters with \T\ and \S\ but the labs are not doing this.

Is there any way to create a custom parser for OBX-5 that will skip the component and subcomponent delimiter parsing?
(I would prefer to not have to pre-process the file with regex to escape those characters, as that solution seems very fragile)

Or are there any other solutions for this issue?

(I found someone else encounter this issue too: https://stackoverflow.com/questions/8935497/ampersand-character-in-obx-segment-causing-problems-hl7-formatting)

OBX Example

OBX|1|FT|HTML^Display format in HTML^AUSPDI|1|<HTML><P>This report has been forwarded to &quot;DOCTOR, Dr Demo&quot; from &quot;TEST&quot; with the following comments:<br />&quot;TESTING HTML URL&quot;<br /><br /></P><B><A HREF="http://www.healthlink.net">CLICK HERE TO VIEW THE IMAGES (2)</A></B><BR><BR><br><U><STRONG>CHEST X-RAY</STRONG></U><br><br><U><STRONG>HISTORY</STRONG></U><br>Left lateral chest wall pain.  Previous breast surgery and axillary lymph<br>node resection.  <br><br><U><STRONG>FINDINGS</STRONG></U><br>The prior left sided breast and axillary surgery is noted along with<br>insertion of bilateral breast prostheses.  There is also a surgical clip in<br>the left upper quadrant and there may have been a prior splenectomy. <br>Allowing for this, no pulmonary parenchymal mass is seen to suggest a<br>metastatic deposit.  There is minor tenting to the dome of the left<br>hemidiaphragm which would be compatible with postinflammatory scarring<br>(possibly related to prior the prior surgery and/or radiotherapy).  No<br>pleural fluid is seen.  The heart is not enlarged and the hilar complexes<br>have a normal configuration.  There is no apparent widening of the superior<br>mediastinum.  <br>No advanced bony destructive lesion is detected.  <br><br><U><STRONG>COMMENT</STRONG></U><br>No definite evidence of metastatic disease identified but if a skeletal<br>deposit is still clinically suspected then further assessment with an<br>isotope bone scan is suggested.  <br><br>Thank you for referring this patient.<br><br><U><STRONG>Mr Radiol Ogist</STRONG></U><br><br><br></HTML>||||||F

Specifically "forwarded to & quot;DOCTOR, Dr Demo & quot; from"

Nuget version not aligned

Hi,
the nuget version isn't aligned with code.
The nHapi dependency is 2.5.0.6 in nuget site, but nHapi.Core 2.6.0.2 in code for .netStandard 2.0 (this is correct)

Nuget
.NETFramework 4.6.1
nHapi (>= 2.5.0.6)
System.Configuration.ConfigurationManager (>= 5.0.0)
.NETStandard 2.0
NETStandard.Library (>= 2.0.0)
nHapi (>= 2.5.0.6)
System.Configuration.ConfigurationManager (>= 5.0.0)

Code

    <dependencies>
      <group targetFramework=".NETFramework4.6.1">
        <dependency id="nHapi" version="2.5.0.6" />
        <dependency id="System.Configuration.ConfigurationManager" version="5.0.0" />
      </group>
      <group targetFramework=".NETStandard2.0">
        <dependency id="nHapi.Core" version="2.6.0.2" />
        <dependency id="NETStandard.Library" version="2.0.0" />
        <dependency id="System.Configuration.ConfigurationManager" version="5.0.0" />
    </group>
    </dependencies>

Can you publish an updated nuget package?

Thank you.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.