Sending An HL7 Message, Receiving It Using A Listener And Sending An Acknowledgement Back

What is HL7

HL7 (Health Level Seven) is a set of standards, formats, and definitions for the exchange, integration, sharing, and retrieval of electronic health information. The HL7 standards are produced by the HL7 International, the international standard-developing organization. These standards are adopted by other standard-issuing bodies such as the American National Standards Institute and International Organization for Standardization. HL7 has compiled a collection of message formats and related clinical standards that define an ideal presentation of clinical information. HL7 also promotes global interoperability in healthcare IT by providing guidance about how to implement its standards.

HL7 Message

HL7 Messages are used to transfer electronic data between disparate healthcare systems. HL7 messages are divided up into Segments of related information, and these are always separated by a carriage return. Each HL7 message sends information about a particular event such as patient admission.

A Sample HL7 Message

MSH|^~\&|SENDING_APPLICATION|SENDING_FACILITY|RECEIVING_APPLICATION|RECEIVING_FACILITY|80440613081417||ADT^A04|294576460110672083618|P|2.3||||
EVN|A04|20110613083617|||
PID|1||135769||WATSON^JAMES^||19281118|M|||123 Main St.^^RIVER SAN MARINA^FL^32830||(506)523-6289^^^[email protected]|||||1719|55331188||||||||||||||||||||
PV1|1|O|||||7^John^Carter^^MD^^^^|||||||||||||||||||||||||||||||||||||||||||||

Determining the HL7 Message Type

Each HL7 message is of a particular type describing an event. For identifying the type of message, we just need to examine the message header segment – MSH. Usually, the 9th ninth field of the MSH describes the message type. In the above example, the 9th filed is ADT^A04, which Patient Registration.

What is MLP

MLP stands for Minimal Lower-Layer Protocol. The protocol specifies how you wrap an HL7 message with a header and footer to ensure you know where a message starts, where a message stops, and where the next message starts.

HL7 messages are transferred using the TCP/IP protocol. TCP/IP data is sent as a stream of bytes. So, multiple HL7 messages may be sent as a continuous stream. So, how will we find out the starting and ending of an HL7 message? MLP is used for this purpose. It helps us to find out starting an ending of a message by adding a header and a footer which are non-printable.

To transport an HL7 message using the MLLP protocol, add a vertical tab (VT) character (hexadecimal value 0x0b) at the start of the message. Add a file separator character (FS) (hexadecimal value 0x1c) and a carriage return (CR) (hexadecimal value 0x0d) at the end of the message.

Creating an HL7 listener

If we need an application to process HL7 messages, we first need to receive the HL7 message. For that, we need to create an HL7 listener. A listener is nothing but a TCP listener. We also need to create another thread that sends an HL7 message so that the listener can receive it.

Once the message is received by the listener, we will send an acknowledgment back. A sample HL7 Acknowledge (ACK) message will look like below:

MSH|^~\&|LABADT|DH|EPICADT|DH|201301011228||ACK^A01^ACK |HL7ACK00001|P|2.3

MSA|AA|HL7MSG00001

Let us create a very simple application to demonstrate how to receive an HL7 message and send back an acknowledgment. I am using Visual Studio 2017 to create the sample application. You can download the sample and try it out yourself.

Step 1. Create a console application

File -> New -> Project.

HL7

Choose Console App

Name it HL7Listener and click OK.

Step 2. Create a Message class

For the message object, let us create a new class and name it Message.

HL7

An HL7 message may have several segments like MSH, PID, etc.

Step 3. Create a Segment class

So, we need another class to hold the segment information.

Let’s create a new class and name it Segment.

HL7

Well, we have the Segment class now. An HL7 segment will have multiple fields separated by a separator.

Step 4. Add fields, Constructors, and methods to manipulate Segment class

We can use a dictionary object to hold the fields.

Since the fields in a specific version of the HL7 message will have a specific location in a segment, I am using a dictionary with Dictionary<int, String> structure. Since I know the position index of a field in a segment, I can use its index as the key for the dictionary object.

private Dictionary<int, String> fields; 

We can initialize the dictionary object in the default constructor.

public Segment() {  
    fields = new Dictionary < int, string > (100);  
}

I use an overloaded constructor to set the name of the segment.

Now, I would like to add a name property for the segment.

/// <summary>  
/// First field of a segment is the name of the segment  
/// </summary>  
public string Name {  
    get {  
        if (!fields.ContainsKey(0)) {  
            return String.Empty;  
        }  
        return fields[0];  
    }  
}

We can also add a method to add fields to the segment and a method to retrieve fields by key (index of the field). Then, we may need a method to split the fields of a segment and add them to the fields dictionary.

Note

Since you can download the code which is available with this article, I am not giving the full method definition here.

private string Field(int key) {  
    -- -- -- -- -- --  
    -- -- -- -- -- --  
}  
public void Field(int key, string value) {  
    -- -- -- -- -- --  
    -- -- -- -- -- --  
} 

Now, we may need a method to split the fields into a segment and add them to the fields dictionary.

Here is the method signature:

public void DeSerializeSegment(string segment) {  
    -- -- -- -- -- --  
    -- -- -- -- -- --  
}

Finally, we need a method to create an HL7 message string out of the segments dictionary for sending the acknowledgment message.

I named the method - SerializeSegment() :

string SerializeSegment()  
{  
    -- -- -- -- -- --  
    -- -- -- -- -- --  
}

Our Segment class is se, now let us go back to the Message class and finish it off.

We need a constant variable to check if the field is the MSH header.

private const string MSH = "MSH";

We now need a list to store the segments of the message and the definition goes here:

private LinkedList<Segment> _segments;  

That’s it for the fields of the class.

I am going to add methods to various Message operations.

For adding a segment to the list:

public void Add(Segment segment) {  
    -- -- -- -- -- --  
    -- -- -- -- -- --  
}

For adding the segments to the message object:

public void DeSerializeMessage(string msg)  
{  
    -- -- -- -- -- --  
    -- -- -- -- -- --  
} 

For serializing the message object to a string:

public string SerializeMessage()  
{  
    -- -- -- -- -- --  
    -- -- -- -- -- --  
}

With that, we are ready with our message object.

Step 5. Add logic to send message

For a listener to receive some message, there should be a sender to which the listener to subscribe. So, go to the Program.cs class and add some logic to do some.

We can listen to any available server and port. I am here using the localhost and port 7777.

private static readonly byte[] Localhost = { 127, 0, 0, 1 };  
private const int Port = 7777; 

Let us create a separate thread to implement the sender so that both the sender and listener work parallelly.

First, create an IP address object and an IPEndPoint object using classes from the System.Net namespace.

System.Net.IPAddress address = new IPAddress(Localhost);  
System.Net.IPEndPoint endPoint = new IPEndPoint(address, Port); 

I am going to use the power of threading for the listener to work independently, I mean without blocking.

ystem.Threading.Thread listnerThread = new Thread(new ThreadStart(Listen));  

We can create another thread to implement the message sending logic.

Thread senderThread = new Thread(new ThreadStart(Send));  

You just need to start the threads and this is all that we need to do in the Program.cs

So, what goes inside Listen and Send methods? Before explaining that, we can move the logic of message listening and message sending to two new classes.

Step 6. Create Subscriber class

Create a new class and name it Subscriber. This is our class which handles the message listening part.

We already discussed that the listener is just a TCP listener. So, let us create a listener using by creating a new Socket object.

System.Net.Sockets.Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

Then you need to bind the listener to the endpoint of our interest and start listening.

listener.Bind(endPoint);  
listener.Listen(3);  

Once you have received the message, you can handle it in the way you wish. Then you should acknowledge the sender that you have received the message.

The message you get will be packaged in an MLLP frame in the format <VT> data <FS><CR>. You need to remove these special characters before processing them.

  • VT – Vertical Tab
  • FS – File Separator
  • CR – Carriage Return

Acknowledgment message has a format and you should follow it. It is an MSH segment with acknowledged time, version info etc. Finally, the message should be wrapped inside an MLLP frame.

Since we have moved the listener logic to a class, the thread declaration will change like below:

listnerThread = new Thread(new ThreadStart(subscriber.Listen));  

Step 7. Create a Publisher class

Create a new class and name it Publisher. This is our class which handles the message sending part.

The sender will be sending the message to a port. The applications which are subscribed to the server will be listening to this port.

So where is the HL7 message?

I have attached a sample HL7 message file (SampleHL7.hl7) to the project.

What I am going to do is just reading the message from the file and sending it across the TCP socket so that whichever applications are listening to the same address and port will be able to capture the data.

As I have mentioned earlier, the HL7 is transmitted over TCP/IP in MLLP frames to identify the start and end of a message.

So before, sending the message, we need to wrap it like this: <VT> data <FS><CR>

I will once again create a socket for sending.

Socket sender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);  

Next thing, read the data from the file as an array of bytes.

Add VT character (hexadecimal value 0x0b) at the beginning and FS (hexadecimal value 0x1c) and CR (hexadecimal value 0x0d) at the end of the bytes.

sender.Connect(endPoint);  

establishes a connection to the port in the host address.

sender.Send(dataToSend);

will send the data (Hl7 message) as a stream of bytes.

Since we have moved the message sending logic to a class, the thread declaration will change like below:

senderThread = new Thread(new ThreadStart(publisher.Send));  

Congratulations, we have managed to send and receive an HL7 message.

And here is your output:

HL7