Thursday, 3 November 2016
Setting the Encoding in HL7 Soup
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’
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
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.
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.
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.
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.
Monday, 23 May 2016
Custom Metadata in Mirth Connect
Have you ever wanted to see more details on Mirth's channel dashboard? It would certainly be handy if you could see the patients Id, but depending on the purpose of your channel there might be all sorts of relevant information - Visit ID, Referring Doctor, Etc.
Fortunately Mirth supports this with the feature they have called "Mirth Custom Meta Data".
It's also pretty easy to use, if a little non intuitive. In your channel, navigate to the summary tab and then add fields to the Custom Metadata section
Here I have added a Patients name variable to the existing two (these two are here by default when the channel is created - they can be removed if you don't like them).
The column name is how you would like it to be named on the dashboard. It is pretty restrictive on allowed characters, so don't expect to put in a space or anything like that - basically you are defining a database column in the D_MCMx table in Mirth's database.
The data type also correlates with the database type, so choose one that suits your data.
The Variable Mapping just creates a variable name. It doesn't accept expressions, so you will have to populate your variable elsewhere, most likely in your source transformer like so.
Here I am mapping the HL7 PID.3.1 to my PatientId variable.
That's it, I've done it. The best thing is that not only can I easily identify patients on my dashboard, but also searching for them is much faster (if you have ever run a search for text in the dashboard you will know how painfully slow it normally is).
Further, I can just as easily add any other fields I find useful.
Tuesday, 17 May 2016
Converting a .NET date to a Mirth date
yyyy-MM-ddTHH:MM:ss
They needed to convert it to a US date format. Here is what they had been trying:
DateUtil.convertDate('yyyy-MM-ddTHH:MM:ss', 'mm/dd/yy', msg['Correspondence']['MedicalDemographics']['Patient']['DateOfBirth'].toString());
This looks fine on the surface, but Mirth doesn't like that "T" sitting in the date. I've never found a workaround other than removing that "T". This is particularly easy to do here because date of birth doesn't have a need for the time anyway. So we just take the first 10 characters and convert with that.
DateUtil.convertDate('yyyy-MM-dd', 'mm/dd/yy', msg['Correspondence']['MedicalDemographics']['Patient']['DateOfBirth'].toString().substring(0, 10));
You can do the same for dates with times, but I will save that for another post.
Friday, 13 May 2016
HL7 Message Types
HL7 Message Types are a key part of the HL7 specification as they inform us what a messages purpose is. I thought it would be helpful to create a beginners guide to introduce the basics that you need to know as you get started out in HL7.
Firstly it’s important to know where to find the message type. The first line of an HL7 message is always the MSH. Count the number of |’s and the message type is always after the 8th |.
The message type is broken into two parts by the ^ character. The first one is the Message Code which categorizes what the message is for (Administration, Scheduling, Orders, etc.) and the second part is the Event Type that signifies what action is being taken (Add, update, delete etc.)
While researching this post I came across this video on HL7 Message Types which uses this very helpful HL7 tool called HL7 Soup. You can just paste in your messages and it tells you the details of the message type – very helpful. I’m using it for the screen shots in this post, so I thought I might share this little bag of goodies with you.
There are dozens of different message types available to be used, but you’ll find that there is just a handful that you will use often, so for the sake of simplicity I'm going to focus just on these.
HL7 Soup lists them all – look at the scroll bar to get an idea of how many Message types there are!
ADT:
The most common message type I work with is ADT which stands for Admission, Discharge, and Transfer. The Event Types available for this are also very numerous, but they are well defined and therefore pretty easy to select
Depending on your use case, the most common message will likely be the A04 for registering an outpatient. This contains the basic administrative details of a patient, their primary health care provider, and details about the visit.
An A08 is sent to update these values, so is also another popular event type.
There is also an inpatient alternative to the A04, the A28.
SIU:
These are scheduling messages for people, equipment and rooms. There aren’t too many Event types for this messages type, and I only ever seem to use a few of them (S12, S13, S14, S15, S17)
I have a little mnemonic for remembering SIU message; I call them See-You messages as they are often used for scheduling appointments with doctors.
ORM:
Order messages are used for all sorts of purposes, and you’ll probably have a need for them for something. It really depends on what area/department you are in as to what the use case would be. Radiology would use them for ordering X-rays, Pathology would use them for specimens etc. The basic thing to keep in mind is that they are requesting something
There is only one Event Type I have ever used for an ORM, and that is the O01.
ORU:
These are providing results from one system to another. All the Event Types are unsolicited, meaning that they are sent once a system produces the results e.g. When the x-rays been examined and a diagnosis has been made.
Within this message are the segments ORC, OBR, and OBX that are used to transfer information of the order about.
The ORC and OBR contain information about the order, while the OBX is the actual values being passed. I’ve seen all sorts of OBX values, from a list of blood test results, to whole medical documents.
Once you have selected the Message Type you can then add each of the Segments you need. Each Event Type has it’s own list of valid Segments that can be added. It’s not uncommon for HL7 to be abused, and for Segments that are not valid for the selected Event Type to be included anyway, after all the HL7 format is very flexible. But I suggest if the design of the message is under your control that you hunt for a different Event Type that better conveys the data you need to transfer. HL7 has been about for such a long time that it’s pretty thorough and it is very unlikely that you are working on something in medical that hasn’t been done before.
When looking for the appropriate available segments the documentation on HL7 is very comprehensive, but it does take a bit of hunting to find exactly what you need. Again I found an HL7 Soup feature that solved my problem.
With a message containing the Message Type you’d like to use, click on the header of the message, and you get a nice drop down that list all the Segments available for the current Event Type. You can then just double click on an item to add one to your message with the correct number of ’|’s.
I like to create a follow up post on this one at some point looking either at more message types, or going into the details of particular ones. I’ll look forward to your feedback.
Transforming repeat fields to single values in Mirth Connect
An obvious example of this is a patients phone number, where the receiving system in the integration only support a single phone number (or more likely one for home, and one for business).
If we create a default transformer variable in Mirth for the phone number it generates the code:
msg['PID']['PID.13']['PID.13.1'].toString()
As we don’t want this to error when the phone number is repeated we need to select only the first entry by adding an index pointer to the first item:
msg['PID']['PID.13'][0]['PID.13.1'].toString()
And there you have it - or so I thought - Welcome the next problem…
Because my patient is used in several destinations, I have this code as a source transformer. This means that the code is executed for every message coming in. But not every message has a PID segment. The code without the 0 index works fine still, but my code that only gets the first instance starts showing errors in Mirth's Dashboard.
ERROR (transformer:?): TypeError: Cannot read property "PID.13.1" from undefined
It doesn't actually stop the channel from working, but nobody wants to see the dashboard filled with errors the whole time.
So now I have to change my code again. I added a check that makes sure that field actually exists, and only if it does will it attempt to get the first value.
(msg['PID']['PID.13'].toString() !='')? msg['PID']['PID.13'][0]['PID.13.1'].toString():null
There you have it. This is my recommended technique for handling repeat fields in Mirth. I recommend that every field you want a single value where HL7 supports repeated values should use this code.
Friday, 6 May 2016
Personal Pronoun Case in Mirth
Inbound
|
22 MAIN STREET
|
|
Becomes :
|
22 Main Street
|
Tuesday, 3 May 2016
Adding minutes to appointment dates in Mirth Connect
Adding appointment duration to start date to get the end date
I've got an inbound HL7 appointment message that sends its appointments with a start date and a duration. However the system I'm integrating into needs to have a start date and an end date.
So, for you HL7 guys out there, that means I need to grab the start date from SCH-11.4 and add the duration mins in the SCH-9. It's probably worth noting here that the duration units in the SCH-10 is always going to be 'min' for me, so I can always treat the SCH-9 as the number of minutes - you might have to handle this too.
I've a .NET background, and if I had C# available this would be solved in a second, but as this is Mirth we're stuck with JavaScript.
After fiddling about for a bit I decided that doing this transformer in a Mapper's single line of expression was just too messy - it's possible, but the line is so long that it's impossible to work with. Instead I created a JavaScript Transformer step, basing it on a the mapper I started with as this provides me with all the try/catch syntax.
So first I grab out the appointment date and convert it from an HL7 string to a proper date type.
//Get the appointment startdate and convert it to a datetime
var appointmentDate = DateUtil.getDate("yyyyMMddHHmmss",msg['SCH']['SCH.11']['SCH.11.4'].toString());
Now I have a date I can manipulate it appropriately. I'll get the appointment duration as an integer too.
//Get the appointment length as an integer
var appointmentLength = parseInt(msg['SCH']['SCH.9']['SCH.9.1'].toString())
Online I found some very handy JavaScript functions for adding dates, GetMinutes and SetMinutes. They basically do what they say on the tin, but I did get tripped up there for a second. In my usual .NET coding I'd expect that dateVar.SetMinutes() would return the result of setting the minutes, but it actually just adds them to dateVar directly - no warnings or errors to help me through it, but I got their in the end.
//increase the start date by the appointment Length
appointmentDate.setMinutes(appointmentDate.getMinutes() + appointmentLength);
So that's it, I have the end date, now I just need to convert it back to and HL7 String (though you might not need too) and put it into a channel variable.
Here is the full transformer code.
var mapping;
try {
//Get the appointment startdate and convert it to a datetime
var appointmentDate = DateUtil.getDate("yyyyMMddHHmmss",msg['SCH']['SCH.11']['SCH.11.4'].toString());
//Get the appointment length as an integer
var appointmentLength = parseInt(msg['SCH']['SCH.9']['SCH.9.1'].toString())
//increase the start date by the appointment Length
appointmentDate.setMinutes(appointmentDate.getMinutes() + appointmentLength);
//Convert the date back to an HL7 Formatted string.
mapping = DateUtil.formatDate("yyyyMMddHHmmss", appointmentDate);
} catch (e) {
logger.error(e);
mapping = '';
}
//put the string into a channel varaiable.
channelMap.put('EndDate', validate( mapping , '', new Array()));
Tuesday, 19 April 2016
The prefix "s" for element "s:Envelope" is not bound
I've always found the way Mirth handles namespaces in xml painful at best - to strip namespaces or not to strip namespaces! It is particularly painful when you come from the .NET world using the Web Service Sender. Here's an error I have come across a few times now.
Transformer error
ERROR MESSAGE: Error evaluating transformer
com.mirth.connect.server.MirthJavascriptTransformerException:
CHANNEL: Hunter outbound HL7
CONNECTOR: Send ORU HL7
SCRIPT SOURCE: TRANSFORMER
SOURCE CODE:
209: var responseMessage = destination.getMessage();
210:
211: //logger.info(responseMessage);
212:
213: // Wrap the SOAP string into an XML object so that we can parse it.
214: var soapMsg = new XML(responseMessage);
215:
216: // Find the XML tag that has the RTF file. It's Base-64 encoded
217: var base64EncodedRtf = soapMsg..*::FileByteStream.toString();
218:
LINE NUMBER: 214
DETAILS: TypeError: The prefix "s" for element "s:Envelope" is not bound.
at 6d9945af-8559-413e-8652-3c6747667665:214 (doTransform)
at 6d9945af-8559-413e-8652-3c6747667665:335 (doScript)
at 6d9945af-8559-413e-8652-3c6747667665:337
at com.mirth.connect.server.transformers.JavaScriptFilterTransformer$FilterTransformerTask.call(JavaScriptFilterTransformer.java:154)
at com.mirth.connect.server.transformers.JavaScriptFilterTransformer$FilterTransformerTask.call(JavaScriptFilterTransformer.java:119)
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)
So in this Channel I have a few destinations doing separate things, but the last two are supposed to pull in a medical document, then insert this into an HL7 message and send it.
It gets the document fine, but then the "Send ORU HL7" throws the above error.
Well, the error certainly looks like a namespace problem, so I jump into the channels Set Date Types dialog and start making adjustments.
As you can see here, I am already not stripping the namespace, but as I try changing to different combinations of stripping and not stripping I still cannot fix the problem. Finally I concede that I need to focus on the destination prior, the "Request Document From Corridor". It is indeed that message that returns the "s:" namespace.
So I adjust it's namespace stripping too without any luck. But then I had a thought - what if I treat it as a different datatype. Even though it is returning a xml message I simply tell Mirth it is HL7 V2.x
That was it. Problem solved. I certainly get why this workaround works, but what I don't really understand is why I had to do it that way.
I'd love any feedback on other peoples experiences here, an I can look at doing a future post following up on this.
Thursday, 14 April 2016
What’s the difference between A04 and A28 message
HL7 seems to have a whole lot of event types that sound similar, and it is often difficult to see through the nuances in the definitions. Today I was asked the difference between an A04 (Register a Patient) and an A28 (Add person information), so I thought I would share it.
An A04 is an event that signifies a patient has arrived or checked in for an outpatient visit, that is they are going in for a one off visit and they are not allocated a bed. An A28 is a transfer of a person’s demographic information between systems. It doesn’t need to be triggered from a patients arrival or anything like that. For instance the patient information may be of no immediate use to the other systems, but knowledge of the patient may come in handy later so they are updated with an A28. Further, an A28 isn’t necessarily about a patients, but could represent other people to other systems e.g. Patient guarantors, or records for a registry database.
Tuesday, 12 April 2016
Character reference “” is an invalid XML character Mirth Error
I was bringing a text document into an HL7 messages today in Mirth when I found I got the following error every time for a particular document.
ER7Serializer error
ERROR MESSAGE: Error converting XML to ER7
org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 47427; Character reference "" is an invalid XML character.
at org.apache.xerces.util.ErrorHandlerWrapper.createSAXParseException(Unknown Source)
at org.apache.xerces.util.ErrorHandlerWrapper.fatalError(Unknown Source)
at org.apache.xerces.impl.XMLErrorReporter.reportError(Unknown Source)
at org.apache.xerces.impl.XMLErrorReporter.reportError(Unknown Source)
at org.apache.xerces.impl.XMLErrorReporter.reportError(Unknown Source)
at org.apache.xerces.impl.XMLScanner.reportFatalError(Unknown Source)
at org.apache.xerces.impl.XMLScanner.scanCharReferenceValue(Unknown Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanCharReference(Unknown Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)
at com.mirth.connect.plugins.datatypes.hl7v2.ER7Serializer.fromXML(ER7Serializer.java:277)
at com.mirth.connect.donkey.server.channel.FilterTransformerExecutor.processConnectorMessage(FilterTransformerExecutor.java:123)
at com.mirth.connect.donkey.server.channel.DestinationConnector.transform(DestinationConnector.java:320)
at com.mirth.connect.donkey.server.channel.DestinationChain.call(DestinationChain.java:151)
at com.mirth.connect.donkey.server.channel.Channel.process(Channel.java:1653)
at com.mirth.connect.donkey.server.channel.Channel.dispatchRawMessage(Channel.java:1156)
at com.mirth.connect.donkey.server.channel.SourceConnector.dispatchRawMessage(SourceConnector.java:192)
at com.mirth.connect.donkey.server.channel.SourceConnector.dispatchRawMessage(SourceConnector.java:170)
at com.mirth.connect.connectors.ws.WebServiceReceiver.processData(WebServiceReceiver.java:238)
at com.mirth.connect.connectors.ws.WebServiceReceiver.processData(WebServiceReceiver.java:207)
at com.mirth.connect.connectors.ws.DefaultAcceptMessage.acceptMessage(DefaultAcceptMessage.java:24)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sun.reflect.misc.Trampoline.invoke(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sun.reflect.misc.MethodUtil.invoke(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.sun.xml.internal.ws.api.server.MethodUtil.invoke(Unknown Source)
at com.sun.xml.internal.ws.api.server.InstanceResolver$1.invoke(Unknown Source)
at com.sun.xml.internal.ws.server.InvokerTube$2.invoke(Unknown Source)
at com.sun.xml.internal.ws.server.sei.SEIInvokerTube.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.server.WSEndpointImpl$2.process(Unknown Source)
at com.sun.xml.internal.ws.transport.http.HttpAdapter$HttpToolkit.handle(Unknown Source)
at com.sun.xml.internal.ws.transport.http.HttpAdapter.handle(Unknown Source)
at com.sun.xml.internal.ws.transport.http.server.WSHttpHandler.handleExchange(Unknown Source)
at com.sun.xml.internal.ws.transport.http.server.WSHttpHandler.handle(Unknown Source)
at com.sun.net.httpserver.Filter$Chain.doFilter(Unknown Source)
at sun.net.httpserver.AuthFilter.doFilter(Unknown Source)
at com.sun.net.httpserver.Filter$Chain.doFilter(Unknown Source)
at sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(Unknown Source)
at com.sun.net.httpserver.Filter$Chain.doFilter(Unknown Source)
at sun.net.httpserver.ServerImpl$Exchange.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)
So, this looks like it’s caused by a funny character – the "" or Form Feed. I found the transformer that was bringing in the value and tried at first to try/catch it out, but quickly found that my JavaScript code wasn’t the problem, it was actually internal Mirth code getting all tripped up by the character.
That means that the only way to fix it now is to remove the character, but how? I had actually written out the value to the logging window, where it was visible as a funny square box. Very handy this as I was actually able to copy the value, and then do a string comparison to filter it out.
Wednesday, 6 April 2016
Mirth destinations not waiting for previous destination
It had three Web Server Sender destinations in a row that had to be executed in order. AddPatient, AddVisit, and AddAppointment. No problem I thought, "Wait for previous destination" was checked on all of them.
However I later received a report that the customer was getting errors of the sort that could only happen if the destinations had not been executed in the right order. The appointment was being added without a visit, and sometimes without a patient.
I tried replicating the problem locally, but had no luck, so I gained remote access to the site. I first located an instance of the problem and checked that there wasn't an error in the first couple of destinations - there wasn't.
Sure enough though, when I look at the timing of the message, all the destinations trigger at more or less the same time, while the responses happen much later.
So I opened the channel and checked that the "Wait for previous destination" was checked. Sure enough all looked good, but then I noticed the problem. Someone had changed my pattern to queue the destinations.
Mirth Destination |
Tuesday, 5 April 2016
Sending < and > symbols to a Web Service Sender in Mirth
I have a Mirth channel interface that receives an HL7 message, and then the values are extracted and passed on to a web service via a Web Service Sender.
Mostly it worked well, but for a few messages I got the following error.
Web Service Sender error
ERROR MESSAGE: Error creating web service dispatch
com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: Error during saving a multipart message
at com.sun.xml.internal.messaging.saaj.soap.MessageImpl.saveChanges(Unknown Source)
at com.mirth.connect.connectors.ws.WebServiceDispatcher.send(WebServiceDispatcher.java:514)
at com.mirth.connect.donkey.server.channel.DestinationConnector.handleSend(DestinationConnector.java:738)
at com.mirth.connect.donkey.server.channel.DestinationConnector.process(DestinationConnector.java:436)
at com.mirth.connect.donkey.server.channel.DestinationChain.call(DestinationChain.java:155)
at com.mirth.connect.donkey.server.channel.Channel.process(Channel.java:1653)
at com.mirth.connect.donkey.server.channel.Channel.dispatchRawMessage(Channel.java:1156)
at com.mirth.connect.donkey.server.channel.SourceConnector.dispatchRawMessage(SourceConnector.java:192)
at com.mirth.connect.donkey.server.channel.SourceConnector.dispatchRawMessage(SourceConnector.java:170)
at com.mirth.connect.connectors.tcp.TcpReceiver$TcpReader.call(TcpReceiver.java:650)
at com.mirth.connect.connectors.tcp.TcpReceiver$TcpReader.call(TcpReceiver.java:485)
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: com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: Unable to get header stream in saveChanges:
... 15 more
Caused by: com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: SOAP exception while trying to externalize:
If this was failing all the time, it probably means that the Soap’s XML structure has been corrupted while I was adding variables, but as this was only with certain messages I was pretty sure that the data was corrupting the XML structure.
Looking at the Mirth Dashboard, I navigated to the ‘sent message’ and found the culprit
<win:UniversalServiceText>Type < 3</win:UniversalServiceText>
Yep, that would do it. So how do you convert this to:
<win:UniversalServiceText>Type < 3</win:UniversalServiceText>
I’ve always felt that Mirth should do this for you in the transformers, but every time I try to run a Soap message through the transformers I get errors that I just can’t seem to fix. Besides, I’m not even sure that this would work; it doesn’t very well with HL7 messages.
Instead, I’m back to coding the answer again, each variable at a time. So when you create your variable just include the following like so.
msg['OBR']['OBR.4']['OBR.4.2'].toString().replace('<', '<').replace('>', '>')
I’d love to get feedback about how others get around this issue.
Monday, 4 April 2016
Escaping HL7 ampersands with Mirth Connect (NextGen Connect)
HL7 uses &'s to separate the subcomponents in a message, So if you need to include an '&' in a field, such as the address "Corner of High & Main St" you are going to run into problems.
MSH|^~\&|App1|Fac1|App2|Fac2|20160315121250||ADT^A31|1000|P|2.3.1
PID|1||10000||Smith^John^^^MR^^||19790708|M|||Corner of High & Main St^^City^New York^^UK|
Fortunately HL7 comes with escape characters, but they are a little bit "unique" in my eyes. Here is a great, comprehensive place to find all the escape codes. The one we are interested in for the ampersand is \T\.
MSH|^~\&|App1|Fac1|App2|Fac2|20160315121250||ADT^A31|1000|P|2.3.1
PID|1||10000||Smith^John^^^MR^^||19790708|M|||Corner of High \T\ Main St^^City^New York^^UK|
Mirth Connect actually supports automatically transforming the escaped characters for you, providing you turn on the 'Use Strict Parser' setting.
However, this then turns Mirth into an HL7 Nazi, constantly rejecting messages due to fields not containing the right values. I don't know about you, but I don't think I've worked on an HL7 interface that didn't have some slight breaking of the rules somewhere.
For this reason I recommend leaving the Strict Parser off and editing your source transformer code so that values are transformed with the escape characters replace.
You just need to navigate to the transformers for the fields you’d like to escape and append the following code to the string.
().replace(/\\T\\/g, "\&");
e.g. the Doctors address line one becomes
msg['ZPD']['ZPD.2']['ZPD.2.1'].toString().replace(/\\T\\/g, "\&");
(Update: Yep, I use Integration Host now by HL7 Soup so this is all just a right-click option without pain. If all this is in the too hard basket, do the same...)
Saturday, 2 April 2016
Mirth Connect using SQL Server
The first thing I do with Mirth now on a fresh install is to configure it to point to SQL Server.
Fortunately this is pretty easy to do.
Load up Mirth Connect Server Manager, then navigate to it in your task bar.
Right click it, and select "Show Manager"
Navigate across to the "Database" tab and fill it out with the settings and connection string to your SQL Server.
Now just restart the Mirth service from the Server Manager and Mirth will build and prepare the database for you.
If you need to migrate an existing database, and keep all the data then I suggest you take a look at this link I found.