Thursday, 8 November 2018

Writing Base64 encoded HL7 data to a Binary file

It is common to have Base64 encode data in an OBX-5 field. The most common I've come across are PDF documents, but also .Doc, Docx and other Document formats are common, as are the many image formats - TIFF, PNG, JPG, and BMP.

HL7 Soup is now my go-to HL7 integration engine for binary formats as it's just so simple, while at the same time it's totally flexible.  Let me show you. 

Create a receiver workflow that receives an HL7 message and then writes it out to a file.

In the transformers of the “File Writer” activity, drag the OBX-5 into the transformers list to create a new variable.


Then back in the “File Writer” change the message template to write out the variable (delete the default message template, the right click in message template and insert variable).


Now Just change the message type of the file you want to write to binary and you’ll have a binary file.


And that is it done.  Writing out a binary file automatically decodes the value from base64.  No mucking about with code or anything fiddly like that.  

OK, but what if it wasn’t actually binary data, you just had base64 encoded text for instance?  Well, you also have the option to right click on the variable and tell it to base64 decode the value like so. 




There are lots of different types of encoding available too.  Depending on the Message Type you select, you get different options.  E.g. if I had written out an XML document then right-clicking on the variable would allow me to "XML Encode" the data which replaces the &'s & etc.

Sunday, 12 August 2018

Get Patient Email Address from repeat Fields

The HL7 inbound messages I was receiving were a bit inconsistent on where to find the patient emails.  Basically, I need to look at the PID-14 and all repeating instances of this, one at a time until I find an email address.
It sounded to me like a good opportunity to try out HL7 Soups custom parameters, where I can code up my logic in c#.

Firstly I went through there introduction video on custom transformers, and then grabbed the sample code to base mine transformer on. 

I must say that I love the API, it makes coding with HL7 super easy.  The trick I found is to make sure you typecast the messages to IHL7Message right at the beginning.  It gives you heaps more methods and properties than the basic IMessage type (which is generic because it also works with JSON, CSV, etc).

The only thing I tripped up on was finding the repeat fields themselves.  I thought they may have been accessible on the GetFields() method, so I mucked around there for a bit.  But actually, they are a property of IField.  I just needed to call GetRelatedRepeatFields(), and it gave me an iterator that included the original field and its siblings.

The deployment was easy, and I loved the debugging right in Visual Studio.  So much easier than having to log out each line like in Mirth.

Anyway, here is the code I created, hope it helps.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HL7Soup.Integrations;

namespace CustomTransformersSample
{
    [Variable("Patient Email", "sample@sample.com")]
    [DisplayName("Find Patient Email")]
    public class FindPatientEmail : CustomTransformer
    {
        public override void Transform(IWorkflowInstance workflowInstance, IMessage message, Dictionary<string, string> parameters)
        {
            string email = "";
            //Get the incoming message
            IHL7Message hl7 = workflowInstance.Activities[0].Message as IHL7Message;

            //Find the PID-14
            IHL7Field pid14 = (IHL7Field)hl7.GetPart("PID-14");
            if (pid14 != null)
            {
                //Look at all the PID 14s
                foreach (var repeatField in pid14.GetRelatedRepeatFields())
                {
                    //Get the 4th component (which is email)
                    IHL7Component component = repeatField.GetComponent("4");
                    if (component != null && component.Text != "")
                    {
                        //Found the email.  Return it.
                        email = component.Text;
                        break;
                    }
                }
            }

            workflowInstance.SetVariable("Patient Email", email);
        }
    }

}

Thursday, 10 August 2017

Update HL7 Soup Example messages

When you load up HL7 Soup you are presented with a selection of example HL7 messages.  They are great to get you going, but won't necessarily be what you normally need at start-up.
You may want your own message samples, or maybe you just want a blank page.  The great thing is that you can change these by simply making your changes and clicking save.
You can add your own HL7 messages, delete the ones you don't like, or edit the defaults to your hearts content.  Just click save when you are done, and you'll be confronted with your changes when you next load HL7 Soup.

So where is this stored? Well, I did a bit of hunting and found the file is stored at

%appdata%\hl7soup\Sample HL7 Messages.txt

You can just put your own file in its place too and it will be picked up as the HL7 Soup default.

One last thing, you might want to revert to the HL7 Soup default messages again.  I found a simple workaround that recovers these.  Just delete the file I mentioned above and when you reload HL7 Soup it will add it back in with the original sample HL7 messages.  Very handy.


Thursday, 3 November 2016

Setting the Encoding in HL7 Soup

I’ve been working on an integration to an old HL7 system where all the messages are encoded with ASCII.  This was causing values like ┬Ámol/L to show as ?mol/L in HL7 Soup.  It also impacts characters like the copyright © symbol.

OK, so there was a very simple fix – I just had to upgrade to the latest version of HL7 Soup, then everything just started working.  However, in the process, I found out some new encoding features.
You can actually set the encoding that is to be used in the HL7 Soup config file.

Navigate to the HL7 Soup Config file found at
C:\Program Files (x86)\Popokey\HL7 Soup\HL7Soup.exe.config
And copy it to your desktop.

Open it up in notepad and locate the Encoding setting

<setting name="Encoding" serializeAs="String">
    <value>Default</value>
</setting>


The Default option suggests that loading or sending HL7 messages will use the default encoding on the PC, which is most likely ISO-8859-1. I think that the Default will be the best for most users, but the option is still there if you need to force a change.


To change the HL7 message encoding to ASCII
<setting name="Encoding" serializeAs="String">
    <value>ASCII</value>
</setting>



To change the HL7 message encoding to UTF-8
<setting name="Encoding" serializeAs="String">
    <value>UTF-8</value>
</setting>


To change the HL7 message encoding to ISO-8859-1
<setting name="Encoding" serializeAs="String">
    <value>UTF-8</value>
</setting>


There is also DefaultUTF-8 option that loads messages using the default encoding on the PC, but once it is sent it will just use UTF-8. I think that this will come in handy for those that have strict UTF-8 requirements for the sending, but always get sent ISO-8859-1 messages.


Once you have made your changes, save the file and copy it back to C:\Program Files (x86)\Popokey\HL7 Soup\
Overwrite the existing file with this edited version and restart HL7 Soup for the changes to take effect.














Monday, 25 July 2016

Mirth CertificateException No name matching found

I keep getting emailed questions about this error, so I thought it would be time to put the answer on the web.

Web Service Sender error
ERROR MESSAGE: Error invoking web servicecom.sun.xml.internal.ws.client.ClientTransportException: HTTP transport error: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching xxxxxxx.com found

You get this message in Mirth Connect when trying to call a web service destination. Here is the problem. Mirth seems to generate the wrong Location URI.

Here you see that I don’t have https in the address, but the Location URI has been generated with the ‘s’

clip_image001

The reverse applies sometimes too.

The work around is simple, just edit the text or the Location URI to match that of the WSDL URL (add or delete an ‘s’). The greyed out text box might make you think this is disabled, but not true – it’s just a poor UI choice.

Here is the full error message.

Web Service Sender error
ERROR MESSAGE: Error invoking web service
com.sun.xml.internal.ws.client.ClientTransportException: HTTP transport error: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching xxxxxxx.com found
                at com.sun.xml.internal.ws.transport.http.client.HttpClientTransport.getOutput(Unknown Source)
                at com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.process(Unknown Source)
                at com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.processRequest(Unknown Source)
                at com.sun.xml.internal.ws.transport.DeferredTransportPipe.processRequest(Unknown Source)
                at com.sun.xml.internal.ws.api.pipe.Fiber.__doRun(Unknown Source)
                at com.sun.xml.internal.ws.api.pipe.Fiber._doRun(Unknown Source)
                at com.sun.xml.internal.ws.api.pipe.Fiber.doRun(Unknown Source)
                at com.sun.xml.internal.ws.api.pipe.Fiber.runSync(Unknown Source)
                at com.sun.xml.internal.ws.client.Stub.process(Unknown Source)
                at com.sun.xml.internal.ws.client.dispatch.DispatchImpl.doInvoke(Unknown Source)
                at com.sun.xml.internal.ws.client.dispatch.DispatchImpl.invoke(Unknown Source)
                at com.mirth.connect.connectors.ws.WebServiceDispatcher$DispatchTask.call(WebServiceDispatcher.java:734)
                at java.util.concurrent.FutureTask.run(Unknown Source)
                at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
                at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
                at java.lang.Thread.run(Unknown Source)
Caused by: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching leandrovm2008c.winscribe.com found
                at sun.security.ssl.Alerts.getSSLException(Unknown Source)
                at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source)
                at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
                at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
                at sun.security.ssl.ClientHandshaker.serverCertificate(Unknown Source)
                at sun.security.ssl.ClientHandshaker.processMessage(Unknown Source)
                at sun.security.ssl.Handshaker.processLoop(Unknown Source)
                at sun.security.ssl.Handshaker.process_record(Unknown Source)
                at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
                at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
                at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
                at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
                at sun.net.www.protocol.https.HttpsClient.afterConnect(Unknown Source)
                at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown Source)
                at sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(Unknown Source)
                at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(Unknown Source)
                at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(Unknown Source)
                ... 16 more
Caused by: java.security.cert.CertificateException: No name matching leandrovm2008c.winscribe.com found
                at sun.security.util.HostnameChecker.matchDNS(Unknown Source)
                at sun.security.util.HostnameChecker.match(Unknown Source)
                at sun.security.ssl.X509TrustManagerImpl.checkIdentity(Unknown Source)
                at sun.security.ssl.X509TrustManagerImpl.checkIdentity(Unknown Source)
                at sun.security.ssl.X509TrustManagerImpl.checkTrusted(Unknown Source)
                at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
                ... 29 more

Friday, 24 June 2016

HL7 Soup Review

It might come as no surprise to my readers to hear I’ve started a little love affair with a delightful piece of software. It’s not something that happens often in the world of medical integrations; that software captures your imagination and yet fits so seamlessly with the way you already work. But that’s how I feel about this delicious treat called HL7 Soup.
HL7 has always been tedious to work with, counting pipes, unintuitive codes, and unreadable dates. So by necessity, there have been a number of apps designed to help deliver you through the jungle of an HL7 Integration. But before I discovered HL7 Soup I always found these to either little more than putting a message in a tree view or something nearly as fiddly as HL7 itself.
HL7 Soup just seems to focus on the human element of an integration, providing you depth but still focusing on clarity. By that I mean it is uncluttered, there aren’t buttons and menus everywhere, but it presents your message from several angles all at once in the manner an architectural diagram presents you a house.
HL7 Soup window definitions
The first of these elevations is what they call the interpretations window. I don’t think I like the name much, it’s a bit of a mouthful, but it is a unique view on the message that must be essential for those wanting to learn HL7, and just a big time saver for old dogs like me.
Put simply the software tries its best to read the HL7 message and present you with sentences that represent the topics within. Ultimately it produces a story about the patient and the visit. I’m pretty sure you can picture how this helps the beginner, but what makes this helpful to the expert is that the whole thing is covered with hyperlinks that change the context of every other view when clicked. Click the age of the patient for instance and you can instantly see their date of birth, ready for you to work with inside the raw view.
The raw view of an HL7 message is still where I find myself working most of the time. Because I’ve been working with HL7 for so long I just find it more natural than tree views. But even this view offers a plethora of simplifications to help me work with the message. I love that clicking on a field with a table automatically gives me a drop down, just like the auto-completion I’m used to within Visual Studio. Better still is that it includes the definitions, not just the values like some of the alternative tools I have used. In fact, every character you click on gives you a descriptive tooltip that puts all the useful information right in front of you.
Instead of a tree view, HL7 Soup includes a color-coded grid. Different colors represent different depths in the message. This does a good job of grouping together related values. But as I mentioned earlier, I mostly prefer working in the raw view, so I can’t authoritatively say if the use of a grid is better than the more traditional tree view. But I think it is worth noting that date values in this view actually have a calendar, and tables have lookups.  This might make for the deciding vote when you can actually see and change dates with ease.
The final view is another that just works like you feel it should. It lists all the messages that you have loaded in a list. Sounds simple I know but I just love the way it works with filters. You can create views of relevant messages, and then manipulate just the current ones in bulk. This is incredibly helpful, but mostly I just appreciate how intuitive it all feels.  All other HL7 software I have used always feels so cumbersome in comparison when working with lots of messages.
Intuitive is, therefore, the how I would sum up this app.  Everything is there, but it doesn’t feel hard to find. Right-click in the message and every command you need becomes available. Open a badly formatted message and it just fixes for you. If you type an invalid date it just highlights it and tells you what is wrong.
What this all boils down to is by using HL7 Soup as your HL7 editor you become an instant HL7 expert.  I don’t make a phone call without having it handy now.  I can talk about an integration type I’ve never used before and sound like I’m totally familiar with it.  I have a little laugh to myself when I hear people on the other end of the line counting out the pipes or trying to determine what the PID-25 value is for.
It saves me time and money, and that is totally the point of software like this.  But it is how it connects with the way I work that makes HL7 soup the best HL7 viewer/editor on the market.









Tuesday, 21 June 2016

Sending and Receiving HL7 Messages

HL7 Messages are normally sent using the MLLP protocol.  For those that are interested, that just means there are a couple of characters at the front of the message to signify the start, and a couple of characters at the end the signal the conclusion of the message.  Other than that it is really just plain text.  For the most part though you don’t really have to worry about MLLP at all as most HL7 software is just going to handle it for you.  You’ll see in my illustrations that I’m using HL7 Soup which is probably perfect for most people too.

I’ll start by creating a test where I just send the message from HL7 Soup back to the same instance of HL7 Soup.

Start by configuring the receiver by clicking on the drop down for the “Receive” button and selecting “Create New”.  You’ll then be presented with the Receiver configuration dialog.

clip_image001

Here you configure the receiving address and port for you message.  For the more technical readers, you’ll notice that this configuration is for TCP communication.  MLLP sits on top of this. 

Because we are sending to ourselves we will use the server address of 127.0.0.1, which is just a relay address according to TCP\IP.  It will send the message straight back to the sender.  The default port HL7 Soup uses is 22222, but you are welcome to change this as required.

So that’s it.  Basically you can just load up this dialog and accept the defaults and you are done.  Your welcome to change the Name if you like, but then just click ok and your receiver is created.

If only creating the sender was that easy.  Actually, only joking – it is exactly the same for creating the sender.  Just click the “Send” button, the send dialog loads, accept the defaults, click ok and you’re done.

clip_image002[1]

You can now send a test message by clicking “Start Receiving”, the clicking the “Send” button a few times.  You’ll see the message going through, and the responses coming back too.  If not then see further on in this document for the trouble shooting section.

HL7 Message once received should always return a response to the sender that lets them know everything went ok. An Application Accepted response message (AA) signifies the message was received and processed without problems.  If things went wrong then an (AE) is sent, and if the message was rejected it’s an ER.  HL7 Soup shows these in different colors so that they are easy to spot. You also have the option to load only successful, errored, and/or rejected messages into the UI, and you can also configure the receiver to respond in a fixed manner to help with testing. 

Sending to another computer on the same network is exactly the same process, just the addresses are a bit different.  It also can be a bit fiddler because of firewalls etc.

All you need to do is adjust the Server IP address for both the Sender and Receiver configurations to be the IP address of the Receiver.  Yep, they will both have the same IP address, and they will both have the same port.

So how do you get the IP address I hear you ask?  Well when you are on the Receiving computer just load up a command prompt and type ipconfig.

Here you will find the IP address that should go into the configuration.  Note that you don’t need to use IP addresses, and it works perfectly well with DNS too.  That means the Server text box can just be the name of the computer.

That’s it, you should now be able to just start the receiver, and click send a few times to watch you messages fire across.

Troubleshooting:

Although it’s normally pretty straight forward there are a number of things that could go wrong, so here is a list of things worth checking:

1. Make sure that the ports are the same on the sender and the receiver.

2. Make sure the Server address is the same on the sender and the receiver.

3. If you are using IP Addresses in the Server text box then change to the computers network name, or vice versa.

4. Ping the receiver from the sender to make sure they can talk over the network.

5. Turn off the windows firewall on both computers (or configure HL7 Soup to be allowed to use the desired port by whatever means your firewall allows)

Please let me know if there is anything else that should be added to this list and I will add it to help others.