Authors: Sam Habiel. Thanks to David Whitten for help with VistA; to Loyd Bittle for teaching me how to use Mirth.
Last updated in Januray 2019.
Before proceeding with this section, you should have completed Initializing VistA; and MAS Parameter Entry/Edit (you only need to do section 1, starting with
MEDICAL CENTER NAME
and ending withABBREVIATED PATIENT INQUIRY
This tutorial will help you set-up both outgoing and incoming connections to HL7 in VistA via TCP only. The tutorial will be very thin on theoretical content on HL7 itself. For a fuller treatment of the subject matter, consult the manuals in the VistA Documentation Library, in particular, this manual. There is a newer HL7 messaging system in VistA called "HL7 Optimized (HLO)" --we won't be discussing that here as HLO is not widely used.
HL7 means Health Level 7, the number 7 corresponding to the Application Layer in the OSI Model. In brief, it is a messaging format used in Medicine. It's important because when deploying VistA in a healthcare setting, you almost always have to talk to other machines, such as lab instruments, ECG/EKG instruments, X-ray machines, and the list goes on forever. The way to talk to all of these instruments is using HL7.
When we speak of HL7 in this document, we specifically mean HL7 versions 2.1, 2.2, 2.3, and 2.4, which are all similar to each other. We will not talk about HL7 v.3 and later, which include CDA documents and the like as these are not used for interfacing with Medical Devices.
The following description is taken from this manual.
An HL7 message is the atomic unit for transferring data between systems in the HL7 standard. Each message has a header segment composed of a number of fields, including a field containing the message type and (HL7 versions 2.2 and above) event type. These are each three-character codes, defined by the HL7 standard. The type of a transaction is defined by the message type/event type pair (again for HL7 versions 2.2 and above). Rules for constructing message headers and messages are provided in the "Control/Query" chapter of the HL7 standard.
An HL7 message consists of one or more HL7 segments . A segment is similar to a record in a file. Each segment consists of one or more fields separated by a special character called the field separator . The field separator character is defined in the Message Header (MSH) segment of an HL7 message. The MSH segment is always the first segment in every HL7 message (except for batch HL7 messages, which begin with BHS or FHS segments).
In addition to the field separator character, four other special characters, called encoding characters, are used as delimiters. Encoding characters are also defined in the MSH segment. Each encoding character must be unique, and serves a specific purpose. None of the encoding characters can be the same as the field separator character. The four delimiters for which there are encoding characters are:
Here's an example of an HL7 message:
MSH|^~\&|INFOLIO TEST|BOSTON VAMC|IB TEST|500|19900314130405|ORU^R01|523123|D|2.3|
PID||7777790^2^M10||HL7Patient^One||||||||123456789|
OBR||2930423.08^1^L||199304230800|||||||DERMATOLOGY|
OBX|CE|10040|OV|1^0^0^0^0|
OBX|CE|11041|PR|
OBX|CE|216.6|P|
OBX|ST|VW^WEIGHT^L||120|KG
OBX|ST|VB^BLOOD PRESSURE^L||120/80|MM HG
OBX|ST|VT^TEMPERATURE^L||99|C
OBX|ST|VP^PULSE^L||75|/MIN
Most HL7 messages are unsolicited. They are sent to VistA; or VistA sends them; without either party knowing beforehand that they are getting a message.
HL7 messages typically require an acknowledgement message to be sent. A sample acknowledgement looks like this:
MSH|^~\&|CATH|StJohn|AcmeHIS|StJohn|20061019172719||ACK^O01|MSGID12349876|P|2.3
MSA|AA|MSGID12349876
The second piece of the MSA segment is either:
There is one more thing that's important to mention: The messages above are just the contents. The actual message transmitted over TCP contains several control characters to delimit the start and the end of the message. (TCP is a stream protocol; so you must provide either message lengths or delimiters to delineate the beginning and end of a message). This is called the HL7 Minimal Lower Layer Protocol (MLLP). So a full HL7 message over TCP looks like this:
<ASCII VERTICAL TAB - $C(11)/0X0B>
HL7 MESSAGE
<ASCII FILE SEPARATOR - $C(28)/0X1C>
<ASCII CARRIAGE RETURN - $C(13)/0X0D>
There are some persistent Taskman tasks that need to be created in order for the HL7 system to initialize itself. The tasks are HL AUTOSTART LINK MANAGER
and HL TASK RESTART
, which need to be set to start-up persistent. On a production system, you must schedule HL PURGE TRANSMISSIONS
nightly to ensure that you don't run out of disk space.
Here's a screen capture in Fileman
FOIA201805>D P^DI MSC FileMan 22.1060 Select OPTION: ENTER OR EDIT FILE ENTRIES Input to what File: OPTION SCHEDULING// <enter> (18 entries) EDIT WHICH FIELD: ALL//<enter> Select OPTION SCHEDULING NAME: HL AUTOSTART LINK MANA GER Autostart Link Manager Are you adding 'HL AUTOSTART LINK MANAGER' as a new OPTION SCHEDULING (the 16TH)? No// Y (Yes) QUEUED TO RUN AT WHAT TIME:<enter> DEVICE FOR QUEUED JOB OUTPUT:<enter> OTHER DEVICE PARAMETERS:<enter> QUEUED TO RUN ON VOLUME SET:<enter> RESCHEDULING FREQUENCY:<enter> SPECIAL QUEUEING: SP Startup Persistent Select VARIABLE NAME:<enter> USER TO RUN TASK: ^ Select OPTION SCHEDULING NAME: HL TASK RESTART Restart/Start All Links and Filers Are you adding 'HL TASK RESTART' as a new OPTION SCHEDULING (the 17TH)? No// Y (Yes) QUEUED TO RUN AT WHAT TIME: ^SPECIAL QUEUEING SPECIAL QUEUEING: SP Startup Persistent Select VARIABLE NAME: ^ Select OPTION SCHEDULING NAME: HL,PUR 1 HL PURGE JOB REVIEW Purge jobs monitoring 2 HL PURGE QUEUE (TCP) Purge Outgoing Queue (TCP Only) 3 HL PURGE TRANSMISSIONS Purge Messages CHOOSE 1-3: 3 HL PURGE TRANSMISSIONS Purge Messages Are you adding 'HL PURGE TRANSMISSIONS' as a new OPTION SCHEDULING (the 18TH)? No// Y (Yes) QUEUED TO RUN AT WHAT TIME: T+1@0100 (DEC 28, 2018@01:00) DEVICE FOR QUEUED JOB OUTPUT: OTHER DEVICE PARAMETERS: QUEUED TO RUN ON VOLUME SET: RESCHEDULING FREQUENCY: 1D SPECIAL QUEUEING: ^
Taskman does not "catch" the new tasks unless it's started from scratch (which involves stopping Taskman and then starting with DO ^ZTMB
. We can tell it about them using DO STRTUP^%ZTM0
on direct mode. If you run the system status from direct mode, you should see a bunch of HL7 tasks now running (italicized).
FOIA201805>X ^%ZOSF("SS") GT.M System Status users on 27-DEC-18 21:30:16 PID PName Device Routine Name CPU Time 1136 mumps BG-0 STARTOUT+17^HLCSOUTPOSTMASTER 18:44:51 1816 mumps BG-0 GETTASK+3^%ZTMS1 Sub 1816 21:00:32 4776 mumps BG-0 LOOP+2^HLCSMM1 POSTMASTER 18:44:51 5960 mumps BG-0 LOOP+2^HLCSMM1 POSTMASTER 18:44:52 6512 mumps BG-0 GETTASK+3^%ZTMS1 Sub 6512 18:44:51 8756 mumps BG-0 GO+12^XMTDT POSTMASTER 18:44:52 8800 mumps BG-0 LOOP+7^HLCSLM POSTMASTER 18:44:50 9456 mumps BG-0 LOOP+2^HLCSMM1 POSTMASTER 18:44:51 9580 mumps BG-0 GETTASK+3^%ZTMS1 Sub 9580 18:44:52 10020 mumps BG-0 GETTASK+3^%ZTMS1 Sub 10020 20:44:58 10132 mumps BG-0 STARTIN+28^HLCSIN POSTMASTER 18:44:51 10220 mumps BG-0 IDLE+3^%ZTM Taskman ROU 1 18:44:49 10764 mumps BG-0 GO+26^XMKPLQ POSTMASTER 18:44:52 12096 mumps BG-0 GETTASK+3^%ZTMS1 Sub 12096 18:45:08 12388 mumps BG-0 GETTASK+3^%ZTMS1 Sub 12388 21:00:33 12476 mumps /dev/pty0 INTRPTALL+8^ZSY 사용자,하나 17:56:44 12616 mumps BG-0 GETTASK+3^%ZTMS1 20:00:31 12876 mumps BG-0 GETTASK+3^%ZTMS1 Sub 12876 20:44:56 12968 mumps BG-S9000 LGTM+25^%ZISTCPS POSTMASTER 18:44:50 13308 mumps BG-0 GETTASK+3^%ZTMS1 18:44:51
What I will show here is a typical scenario of sending messages out to other systems. VistA sends messages at specific events. The messages sent and the event at which a message is sent is typically documented in the technical manual for a package on the VDL; however, for older packages, there is no good documentation and the only way to discover the interfaces is by inspecting the code base. The event is something that happens inside of VistA, such as the registration of a patient, the availability of lab results, or the scheduling of an appointment. The example we will use is patient registration, upon which VistA can send an HL7 ADT/A04 message, which is a patient registration message.
This part is confusing, mainly because the way an outgoing message is processed is almost identical to how an incoming message is processed. So we will discuss this again in the context of incoming messages.
This section is for illustration; you don't need to configure anything here.
To send an outgoing message from VistA, you need to create an EVENT DRIVER protocol (it actually lives in a file called PROTOCOL in Fileman). The EVENT DRIVER is also known as a SERVER (which I personally find confusing, but oh well). Here's the one we will use: (NB: to reach this form, you need to navigate to EVE > HL7 Main Menu > Interface Developer Options > Protocol Edit and select VAFC ADT-A04 SERVER
.)
| HL7 INTERFACE SETUP PAGE 1 OF 2
--------------------------------------------------------------------------------
NAME: VAFC ADT-A04 SERVER
DESCRIPTION (wp): + [This server protocol fires when a patient is re]
ENTRY ACTION:
EXIT ACTION:
TYPE: event driver
_______________________________________________________________________________
Exit Save Refresh Quit
On the second page of the form (to reach it, cursor down to "TYPE" field and press enter), you will see the list of subscribers (we have two in this case: VBECS ADT-A04 CLIENT
and HMP ADT-A04 CLIENT
).
| HL7 EVENT DRIVER PAGE 2 OF 2
VAFC ADT-A04 SERVER
--------------------------------------------------------------------------------
SENDING APPLICATION: VAFC PIMS
TRANSACTION MESSAGE TYPE: ADT EVENT TYPE: A04
MESSAGE STRUCTURE:
PROCESSING ID: P VERSION ID: 2.3
ACCEPT ACK CODE: NE APPLICATION ACK TYPE: NE
RESPONSE PROCESSING RTN:
SUBSCRIBERS
VBECS ADT-A04 CLIENT
HMP ADT-A04 CLIENT
_______________________________________________________________________________
Exit Save Previous Page Refresh Quit
So basically, when VistA calls VAFC ADT-A04 SERVER
, VistA will send the message to the subscribers VBECS ADT-A04 CLIENT
and HMP ADT-A04 CLIENT
. For the curious, the registration HL7 message is generated in routine VAFCA04
using this line of code: D GENERATE^HLMA("VAFC ADT-A04 SERVER","LM",1,.HLRST,"",.HL)
.
There are a couple more details we need to talk about. If you move your cursor to one of the subscribers, and press enter, you will see a bunch of fields, three of which are important.
| HL7 EVENT DRIVER PAGE 2 OF 2
┌──────────────────────────HL7 SUBSCRIBER────────────────────────────────┐
---│ VBECS ADT-A04 CLIENT │---
│------------------------------------------------------------------------│
TR│ RECEIVING APPLICATION: VBECS ADT │
│ │
│ RESPONSE MESSAGE TYPE: ACK EVENT TYPE: A04 │
│ │
│SENDING FACILITY REQUIRED?: RECEIVING FACILITY REQUIRED?: │
RE│ │
│ SECURITY REQUIRED?: │
V│ │
H│ LOGICAL LINK: VBECSPTU │
│ │
│ PROCESSING RTN: │
│ ROUTING LOGIC: │
└────────────────────────────────────────────────────────────────────────┘
_______________________________________________________________________________
The important fields we need to look at are LOGICAL LINK
, PROCESSING RTN
, and ROUTING LOGIC
. The way they are used is confusing. They actually override each other, in this order:
ROUTING LOGIC
is specified, that is executed; and nothing else is.LOGICAL LINK
is specified, the message is delivered to the IP address/domain name on the logical link and then we stop.LOGICAL LINK
nor ROUTING LOGIC
is specified, then the message is assumed to be an internal VistA to itself message, and the code for PROCESSING RTN
is used. If PROCESSING RTN
is not filled out, that's an error condition.For most users, using a LOGICAL LINK
to send a message out to an external system is the correct thing to do; but some advanced VistA code uses ROUTING LOGIC
to route messages.
Here are the steps for setting up to send a message from VistA to the outside:
In this example, what I will do is receive the message using the netcat program, which is a generic socket listener; and then we will download Mirth and use it to receive a message. Mirth is an HL7 (and other formats) integration engine -- and it's a realistic target to receive HL7 messages.
In real life, you will have a destination machine with an IP/domain name and port number you need to communicate to. For the purposes of this demonstration, I will initially set-up a netcat
listener on my local machine on port 6661. That means that my new logical link will call 127.0.0.1 port 6661. I will call my link MEMPHIS
. Logical links are not typically namespaced. A namespace is a place to place your code in M; and a numberspace is where you need to put your files and your new fields. To create a new logical link, go to EVE > HL7 Main Menu > Interface Developer Options > Link Edit [EL]
Select HL LOGICAL LINK NODE: MEMPHIS Are you adding 'MEMPHIS' as a new HL LOGICAL LINK (the 77TH)? No// Y HL7 LOGICAL LINK -------------------------------------------------------------------------------- NODE: MEMPHIS DESCRIPTION: INSTITUTION: MAILMAN DOMAIN: AUTOSTART: QUEUE SIZE: 10 LLP TYPE: DNS DOMAIN: _______________________________________________________________________________ Exit Save Refresh Quit
On the Screenman form, scroll to "LLP TYPE" and type "TCP". Fill in the fields as shown in bold below:
HL7 LOGICAL LINK -------------------------------------------------------------------------------- ┌──────────────────────TCP LOWER LEVEL PARAMETERS─────────────────────────┐ │ MEMPHIS │ │ │ │ TCP/IP SERVICE TYPE: CLIENT (SENDER) │ │ TCP/IP ADDRESS: 127.0.0.1 │ │ TCP/IP PORT: 6661 │ │ TCP/IP PORT (OPTIMIZED): │ │ │ │ ACK TIMEOUT: 1 RE-TRANSMISION ATTEMPTS: │ │ READ TIMEOUT: 1 EXCEED RE-TRANSMIT ACTION: │ │ BLOCK SIZE: SAY HELO: │ │ TCP/IP OPENFAIL TIMEOUT: │ │STARTUP NODE: PERSISTENT: │ │ RETENTION: UNI-DIRECTIONAL WAIT: │ └─────────────────────────────────────────────────────────────────────────┘ _______________________________________________________________________________ Close Refresh
Type C for Close when you are at the command window; and then type S to Save, then E to exit. You will be given the message: "If you shut down this link to edit, please remember to restart if appropriate." That's what we are going to do next.
To enable the link we just created, we go to HL7 Main Menu > Filer and Link Management Options > Start/Stop Links [SL].
Select Filer and Link Management Options Option: SL Start/Stop Links This option is used to launch the lower level protocol for the appropriate device. Please select the node with which you want to communicate Select HL LOGICAL LINK NODE: MEMPHIS This LLP has been enabled!
The easiest way to add a client to the EVENT DRIVER VAFC ADT-A04 SERVER
is to edit the event driver and add a subscriber to the multiple on the second page. Go to HL7 Main Menu > Interface Developer Options > Protocol Edit [EP], and then select VAFC ADT-A04 SERVER
.
| HL7 INTERFACE SETUP PAGE 1 OF 2
--------------------------------------------------------------------------------
NAME: VAFC ADT-A04 SERVER
DESCRIPTION (wp): + [This server protocol fires when a patient is re]
ENTRY ACTION:
EXIT ACTION:
TYPE: event driver
_______________________________________________________________________________
Exit Save Refresh Quit
Cursor down to "TYPE" field and press enter to reach the second page of the form.
| HL7 EVENT DRIVER PAGE 2 OF 2
VAFC ADT-A04 SERVER
--------------------------------------------------------------------------------
SENDING APPLICATION: VAFC PIMS
TRANSACTION MESSAGE TYPE: ADT EVENT TYPE: A04
MESSAGE STRUCTURE:
PROCESSING ID: P VERSION ID: 2.3
ACCEPT ACK CODE: NE APPLICATION ACK TYPE: NE
RESPONSE PROCESSING RTN:
SUBSCRIBERS
RG ADT-A04 TRIGGER
VBECS ADT-A04 CLIENT
HMP ADT-A04 CLIENT
_______________________________________________________________________________
Exit Save Previous Page Refresh Quit
On the second page of the form, move your cursor down to the end of the list of the subscribers (there are normally 3 in FOIA, so you should be at the 4th position, which should be empty). Start typing a namespaced name of your client (a namespace is a place where you put your code; if you don't have one use ZZ) -- which will be "ZZ ADT-A04 CLIENT". You will be asked:
Once you do that, you will see this:
| HL7 EVENT DRIVER PAGE 2 OF 2
┌──────────────────────────HL7 SUBSCRIBER────────────────────────────────┐
---│ ZZ ADT-A04 CLIENT │---
│------------------------------------------------------------------------│
TR│ RECEIVING APPLICATION: │
│ │
│ RESPONSE MESSAGE TYPE: EVENT TYPE: │
│ │
│SENDING FACILITY REQUIRED?: RECEIVING FACILITY REQUIRED?: │
RE│ │
│ SECURITY REQUIRED?: │
V│ │
H│ LOGICAL LINK: │
Z│ │
│ PROCESSING RTN: │
│ ROUTING LOGIC: │
└────────────────────────────────────────────────────────────────────────┘
_______________________________________________________________________________
c CLOSE
r REFRESH
Fill in the RECEIVING APPLICATION, RESPONSE MESSAGE TYPE, EVENT TYPE, and LOGICAL LINK.
This is what you will see for the new RECEIVING APPLICATION:
--┌────────────────────Receiving Application Edit───────────────────────────┐---
│ │
T│ NAME: NETCAT ACTIVE/INACTIVE: ACTIVE │
│ │
│FACILITY NAME: COUNTRY CODE: │
│ │
│ MAIL GROUP: │
R└─────────────────────────────────────────────────────────────────────────┘
This is the final display.
| HL7 EVENT DRIVER PAGE 2 OF 2
┌──────────────────────────HL7 SUBSCRIBER────────────────────────────────┐
---│ ZZ ADT-A04 CLIENT │---
│------------------------------------------------------------------------│
TR│ RECEIVING APPLICATION: NETCAT │
│ │
│ RESPONSE MESSAGE TYPE: ACK EVENT TYPE: A04 │
│ │
│SENDING FACILITY REQUIRED?: RECEIVING FACILITY REQUIRED?: │
RE│ │
│ SECURITY REQUIRED?: │
V│ │
H│ LOGICAL LINK: MEMPHIS │
Z│ │
│ PROCESSING RTN: │
│ ROUTING LOGIC: │
└────────────────────────────────────────────────────────────────────────┘
_______________________________________________________________________________
Go to the command area, type "C" for close, and then "E" for exit.
At this point, we should be theoretically ready to send an HL7 message to the MEMPHIS channel.
One last thing before moving on. If you completed Initializing VistA, you would have assigned yourself a 3 digit station number; in the guide we used 999. To prevent a crash during patient registration, we need to edit the station number on the VAFC PIMS Application in the field "FACILITY NAME" to be match our exact station number.
If you go to direct mode, we can get our station number using $$SITE^VASITE:
FOIA201812>D DT^DICRW W $$SITE^VASITE
4304^PALM DESERT HOSPITAL^999
The station number is the third piece: 999. Go to EVE > HL7 Main Menu > Interface Developer Options > Application Edit [EA], and select VAFC PIMS
, and change the facility name field to be your station number:
| HL7 APPLICATION EDIT
--------------------------------------------------------------------------------
NAME: VAFC PIMS ACTIVE/INACTIVE: ACTIVE
FACILITY NAME: 999 COUNTRY CODE: USA
HL7 FIELD SEPARATOR: HL7 ENCODING CHARACTERS:
MAIL GROUP: VAFH ADT/HL7
_______________________________________________________________________________
Exit Save Refresh Quit
Enter a COMMAND, or "^" followed by the CAPTION of a FIELD to jump to.
Now, we should check that the Link Manager to make sure there are no messages on the MEMPHIS Logical Link. Check it by going to HL7 Main Menu > Systems Link Monitor. This is what you will see, and it is what we expect.
| SYSTEM LINK MONITOR for PLATINUM (P System)
MESSAGES MESSAGES MESSAGES MESSAGES DEVICE
NODE RECEIVED PROCESSED TO SEND SENT TYPE STATE
LISTENER 236 235 903 903 MS 2 server
MCAR OUT 10 Shutdown
ROR SEND 1 1 5 1 NC Shutdown
XUMF ACK 1738 1738 1035 1035 NC Enabled
XUMF FORUM 3 3 Enabled
XUMF TEST 4 4 Enabled
Incoming filers running => 1 TaskMan running
Outgoing filers running => 1 Link Manager running
Monitor current [next job 1.0 hr]
Select a Command:
(N)EXT (B)ACKUP (A)LL LINKS (S)CREENED (V)IEWS (Q)UIT (?) HELP:
Quit (Q) out of this, and exit the menu system and go back to the direct mode in VistA. We need to run a simple test with a patient we registered earlier.
NB: There is a recently introduced bug in HLCSTCP3 (patched up to 157 on the second line), line 69, which says:
Q:(HLOS'["VMS")&(HLOS'["UNIX") X "U IO:(::""-M"")"
This line is incorrect in many regards: it assumes all Cache systems run on VMS or UNIX; and it assumes that all UNIX systems will be Cache. Neither of these assumptions are correct.
It's safe to comment this line out. A more proper fix which takes into account other M systems can be found here. You need to comment the line out or get the new copy of the routine before proceeding any further.
$ mumps -dir
FOIA201805>S DUZ=1
FOIA201805>D ^XUP
Setting up programmer environment
This is a TEST account.
Terminal Type set to: C-VT220
Select OPTION NAME:
FOIA201805>N % S %=$$EN^VAFCA04(1,$$NOW^XLFDT)
If you crash, read this note:
If you crash with this error: OBX+10^RGADTP, Undefined local variable: HL(SFN), you need to go to EVE > HL7 Main Menu > Interface Developer Options > Application Edit [EA], choose
VAFC PIMS
, and change theFACILITY NAME
from 050 to your station number (which is the third piece of the output ofWRITE $$SITE^VASITE
. This error comes from the subscriberRG ADT-A04 TRIGGER
, which you may have seen when editing the subscribers for EVENT DRIVERVAFC ADT-A04 SERVER
. Or, you can just go to the subscribers again, move the cursor toRG ADT-A04 TRIGGER
, and then type "@" to remove it.
If we go back to the System Link Monitor (DO ^XUP, type EVE, choose 1, then navigate to HL7 Main Menu > Systems Link Monitor), we will see that MEMPHIS now shows up as open. It will switch between Open and Openfail as we haven't opened a server socket yet.
| SYSTEM LINK MONITOR for PLATINUM (P System)
MESSAGES MESSAGES MESSAGES MESSAGES DEVICE
NODE RECEIVED PROCESSED TO SEND SENT TYPE STATE
LISTENER 236 235 903 903 MS 2 server
MCAR OUT 10 Shutdown
MEMPHIS 1 NC Open
ROR SEND 1 1 5 1 NC Shutdown
VBECSPTU 0 0 1 0 NC Shutdown
XUMF ACK 1738 1738 1035 1035 NC Enabled
XUMF FORUM 3 3 Enabled
XUMF TEST 4 4 Enabled
Incoming filers running => 1 TaskMan running
Outgoing filers running => 1 Link Manager running
Monitor current [next job 0.8 hr]
Select a Command:
(N)EXT (B)ACKUP (A)LL LINKS (S)CREENED (V)IEWS (Q)UIT (?) HELP:
In another window, type the following
nc -l 6661 >> hl7_msg.txt
Go back to the Link Monitor. You will see that MEMPHIS switches from being Open to Retention to Inactive; and the column for MESSAGES SENT becomes 1.
Back to the netcat window, type CTRL-C to stop the listener, and then dump the file using the cat
command on Linux or type
command on Windows. What you will see would be similar to this:
$ cat -v hl7_msg.txt
^KMSH^~|\&^VAFC PIMS^50^NETCAT^^20181228121041-0400^^ADT~A04^505356^P^2.3^^^NE^NE^USA^MEVN^A04^20181228121041-0400^^^1~M-lM-^BM-,M-lM-^ZM-)M-lM-^^M-^P~M-mM-^UM-^XM-kM-^BM-^X^MPID^1^500000001V075322^1~8~M10^1155P^M-kM-'M-^HM-lM-^ZM-0M-lM-^JM-$~M-kM-/M-8M-mM-^BM-$^""^19551111^M^^""~~0005~""~~CDC^ M-fM-^]M-1M-dM-:M-,M-dM-8M--M-eM-$M-.M-iM-^CM-5M-dM->M-?M-eM-1M-^@~ M-fM-^]M-1M-dM-:M-,M-iM-^CM-=M-dM-8M--M-eM-$M-.M-eM-^LM-:M-eM-^EM-+M-iM-^GM-^MM-fM-4M-2M-dM-8M-^@M-dM-8M-^AM-gM-^[M-.5M-gM-^UM-*3M-eM-^OM-7 ~M-fM-^]M-1M-dM-:M-,M-dM-8M--M-eM-$M-.M-iM-^CM-5M-dM->M-?M-eM-1M-^@~M-fM-^]M-1~100-8994~JAPAN~P~""~""|""~""~""~""~""~~VACAE~""~""~~~""&""|""~""~""~""~""~~VACAA~""~""~~~""&""|""~""~""~""~""~~VACAC~""~""~~~""&""|""~""~""~""~""~~VACAM~""~""~~~""&""|""~""~""~""~""~~VACAO~""~""~~~""&""^^""^""^^""^29^^505111155P^^^""~~0189~""~~CDC^ ^MPD1^^^PLATINUM~~050^""^MPV1^1^O^""^^^^^^^^^^^^^^^NON-VETERAN (OTHER)^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^104^MOBX^1^MZPD^1^""^""^""^""^""^""^""^""^""^0^""^""^""^""^0^""^0^""^""^""^MZSP^1^0^""^""^""^""^""^""^^""^""^MZEL^1^""^""^""^""^""^""^0^NON-VETERAN (OTHER)^""^""^""^""^""^""^""^""^""^""^""^""^""^^^^MZCT^1^1^""^""^""^""^""^""^""^MZEM^1^1^""^""^""^""^""^""^^MZFF^2^^MZIR^^MZEN^1^M^\^M
Now we are going to set-up Mirth to receive a message. Mirth is what is known as an integration engine. An integration engine is essentially a store, transform and forward software for messages between different systems. Mirth is open source software and is frequently used with VistA in production implementations. It can be downloaded from here.
I won't cover how to install Mirth. You are on your own for that. It's not as simple as it used to be due to changes on how Java applications can be launched. The main install should also install the program "Mirth Connect Administrator Launcher", which is what you need to launch in order to launch Mirth Connect.
Once you turn on the "Administrator Launcher", this is what you should see:
Click on "Launch". After that, you see the login for Mirth Connect:
Login with the default username/password (unless you have changed them) of admin/admin. Mirth Connect Administrator will be launched, and you will be greeted with a welcome screen. Fill that in appropriately and click Finish.
At last, you will get the main screen for Mirth Connect Administrator:
Note that all the "hot buttons" are on the left hand side. To edit Channels, we need to click on "Channels".
Click on "Channels". The left hand side will get a new drop down called "Channel Tasks". Click on "New Channel". This is what you will see:
We are currently on the summary tab. All we have to do here is put a name, like "VistA HL7 Receiver". The data type on the channel is by default HL7 2.x, so we don't need to modify that. Now click on the "Source Tab". You will initially see this:
Change the connector type (first drop down) to "TCP Listener", and review the setting you see here.
The only thing you may want to change is the Local Port, in order for it to match VistA. I already chose 6661 for VistA, so we should be good to go. If you are running VistA in UTF-8 encoding, or another custom encoding, you need to double check the encoding matches the data you will be sending from VistA.
On the right hand side, click on Channel Tasks > Save Changes. Then click on Channel Tasks > Deploy Channel, and confirm that you really want to deploy it.
Now you will see the Dashboard with the enabled channel:
Run the test again that we ran before:
$ mumps -dir
FOIA201805>S DUZ=1
FOIA201805>D ^XUP
Setting up programmer environment
This is a TEST account.
Terminal Type set to: C-VT220
Select OPTION NAME:
FOIA201805>N % S %=$$EN^VAFCA04(1,$$NOW^XLFDT)
In a few moments, the Mirth Dashboard will now show that you have a new message:
To view the message, double click on the VistA HL7 Receiver row, and you will be taken to the Channel Messages view.
Click on the top row (the one saying "TRANSFORMED). Once you do that, you will see the full contents of the message that VistA sent.
Once we have confirmed that the system to send HL7 messages for patient registrations seems to work, let's turn it on. You need to change field SEND PIMS HL7 V2.3 MESSAGES
in file MAS PARAMETERS
to SEND
. I think it comes set that way by default in FOIA VistA.
FOIA201805>D P^DI MSC FileMan 22.1060 Select OPTION: ENTER OR EDIT FILE ENTRIES Input to what File: PROTOCOL// MAS PARAMETERS (0 entries) EDIT WHICH FIELD: ALL// SEND 1 SEND PIMS HL7 V2.2 MESSAGES 2 SEND PIMS HL7 V2.3 MESSAGES CHOOSE 1-2: 2 SEND PIMS HL7 V2.3 MESSAGES THEN EDIT FIELD: <enter> Select MAS PARAMETERS ONE: `1 SEND PIMS HL7 V2.3 MESSAGES: SEND// ? Choose from: 1 SEND 0 STOP 2 SUSPEND SEND PIMS HL7 V2.3 MESSAGES: SEND// 1 SEND Select MAS PARAMETERS ONE:<enter>
Now it's time to register a patient, and see the HL7 come across. This time log-in into the front door using ^ZU. On GT.M/YottaDB, that's $gtm_dist/mumps -r ZU
; on Cache, that's csession <instance> -U <namespace> ZU
. Use the access and verify codes you set-up in Initialize VistA.
I should note that if you do not finish all the registration steps, an A04 message won't get generated. Instead, an A08 message (patient update) is generated if you exit early. That doesn't make any good sense to me, but that's the way the code is written.
Note the ^Register a Patient
. A ^ in front of a menu means search all the menu system for that option and run it.
Volume set: ROU:memphis UCI: VAH Device: /dev/pty0 ACCESS CODE: ****** VERIFY CODE: ******** Good afternoon 사용자,하나 You last signed on today at 16:27 Checking POSTMASTER mailbox. POSTMASTER has 681 new messages. (681 in the 'IN' basket) Core Applications ... Device Management ... Menu Management ... Programmer Options ... Operations Management ... Spool Management ... Information Security Officer Menu ... Taskman Management ... User Management ... Application Utilities ... Capacity Planning ... HL7 Main Menu ...Select Systems Manager Menu Option: ^Register a Patient CPT (CPT is a registered trademark of the American Medical Association) codes, descriptions and other data are copyright 1966, 1970, 1973, 1977, 1981, 1983-2017 American Medical Association. CPT is commercial technical data developed exclusively at private expense by Contractor/Manufacturer American Medical Association, AMA Plaza, 330 N. Wabash Ave., Suite 39300, Chicago, IL 60611-5885. The provisions of this Agreement between AMA and VA prevail, including prohibiting creating derivative works and providing CPT to any third parties outside of the Facilities. Press any key to continue <enter> Select PATIENT NAME: HLSEVEN,TEST ARE YOU ADDING 'HLSEVEN,TEST' AS A NEW PATIENT (THE 20TH)? No// Y (Yes) PATIENT SEX: M MALE PATIENT DATE OF BIRTH: 11/11/20 (NOV 11, 1920) PATIENT SOCIAL SECURITY NUMBER: P 703111120P PATIENT PSEUDO SSN REASON: N NO SSN ASSIGNED PATIENT TYPE: NON-VETERAN (OTHER) PATIENT VETERAN (Y/N)?: N NO PATIENT SERVICE CONNECTED?: N NO PATIENT MULTIPLE BIRTH INDICATOR:<enter> ...searching for potential duplicates No potential duplicates have been identified. ...adding new patient...new patient added Patient name components-- FAMILY (LAST) NAME: HLSEVEN//<enter> GIVEN (FIRST) NAME: TEST//<enter> MIDDLE NAME:<enter> PREFIX:<enter> SUFFIX:<enter> DEGREE:<enter> Press ENTER to continue<enter> Please verify or update the following information: MOTHER'S MAIDEN NAME:<enter> PLACE OF BIRTH [CITY]:<enter> PLACE OF BIRTH [STATE]:<enter> Select ALIAS:<enter> Attempting to connect to the Master Patient Index in Austin... If no SSN or inexact DOB or common name, this request may take some time, please be patient... Could not connect to MPI or Timed Out, assigning local ICN (if not already assig ned)... Insurance data retrieval has been initiated. HLSEVEN,TEST; 703-11-1120P NOV 11,1920 ============================================================================= Permanent Mailing Address: Temporary Mailing Address: STREET ADDRESS UNKNOWN NO TEMPORARY MAILING UNK. CITY/STATE County: UNSPECIFIED From/To: NOT APPLICABLE Phone: UNSPECIFIED Phone: NOT APPLICABLE Office: UNSPECIFIED Cell: UNSPECIFIED E-mail: UNSPECIFIED Bad Addr: Confidential Address: Confidential Address Categories: NO CONFIDENTIAL ADDRESS From/To: NOT APPLICABLE POS: UNSPECIFIED Claim #: UNSPECIFIED Relig: UNSPECIFIED Birth Sex: MALE Race: UNANSWERED Ethnicity: UNANSWERED Type <Enter> to continue or '^' to exit:<enter> HLSEVEN,TEST; 703-11-1120P NOV 11,1920 ============================================================================= Language Date/Time: UNANSWERED Preferred Language: UNANSWERED Combat Vet Status: NOT ELIGIBLE Primary Eligibility: UNSPECIFIED Other Eligibilities: Unemployable: NO Permanent & Total Disabled: NO Status : PATIENT HAS NO INPATIENT OR LODGER ACTIVITY IN THE COMPUTER Type <Enter> to continue or '^' to exit:<enter> HLSEVEN,TEST; 703-11-1120P NOV 11,1920 ============================================================================= Future Appointments: NONE Remarks: Date of Death Information Date of Death: Source of Notification: Updated Date/Time: Last Edited By: Health Benefit Plans Currently Assigned to Veteran: None Do you want to enter Patient Data? Yes// <enter> (Yes) PATIENT DEMOGRAPHIC DATA, SCREEN >1< HLSEVEN,TEST; 703-11-1120P NON-VETERAN (OTHER) =============================================================================== [1] Name: HLSEVEN,TEST SS: 703-11-1120P DOB: NOV 11,1920 PSSN Reason: No SSN Assigned Family: HLSEVEN Birth Sex: MALE MBI: UNANSWERED Given: TEST [2] Alias: < No alias entries on file > Middle: Prefix: Suffix: Degree: Self-Identified Gender Identity: UNANSWERED [3] Remarks: NO REMARKS ENTERED FOR THIS PATIENT [4] Permanent Mailing Address: [5] Temporary Mailing Address: STREET ADDRESS UNKNOWN NO TEMPORARY ADDRESS UNK. CITY/STATE County: UNANSWERED County: NOT APPLICABLE Phone: UNANSWERED Phone: NOT APPLICABLE Office: UNANSWERED From/To: NOT APPLICABLE Bad Addr: <RET> to CONTINUE, 1-5 or ALL to EDIT, ^N for screen N or '^' to QUIT:<enter> ADDITIONAL PATIENT DEMOGRAPHIC DATA, SCREEN <1.1> HLSEVEN,TEST; 703-11-1120P NON-VETERAN (OTHER) =============================================================================== [1]Confidential Address NO CONFIDENTIAL ADDRESS From/To: NOT APPLICABLE [2] Cell Phone: UNANSWERED Pager #: UNANSWERED Email Address: UNANSWERED [3] Language Date/Time: UNANSWERED Preferred Language: UNANSWERED <RET> to CONTINUE, 1-3 or ALL to EDIT, ^N for screen N or '^' to QUIT:<enter> PATIENT DATA, SCREEN <2> HLSEVEN,TEST; 703-11-1120P NON-VETERAN (OTHER) =============================================================================== [1] Marital: UNANSWERED POB: UNANSWERED Religion: UNANSWERED Father: UNANSWERED SCI: UNANSWERED Mother: UNANSWERED Mom's Maiden: UNANSWERED [2] Previous Care Date Location of Previous Care ------------------ ------------------------- NONE INDICATED NONE INDICATED [3] Ethnicity: UNANSWERED Race: UNANSWERED <4> Date of Death Information Date of Death: Source of Notification: Updated Date/Time: Last Edited By: [5] Emergency Response: <RET> to CONTINUE, 1,2,3,5 or ALL to EDIT, ^N for screen N or '^' to QUIT:<enter> EMERGENCY CONTACT DATA, SCREEN <3> HLSEVEN,TEST; 703-11-1120P NON-VETERAN (OTHER) =============================================================================== [1] NOK: UNANSWERED [2] NOK-2: UNANSWERED Relation: UNANSWERED Relation: UNANSWERED Phone: UNANSWERED Phone: UNANSWERED Work Phone: UNANSWERED Work Phone: UNANSWERED [3] E-Cont.: UNANSWERED [4] E2-Cont.: UNANSWERED Relation: UNANSWERED Relation: UNANSWERED Phone: UNANSWERED Phone: UNANSWERED Work Phone: UNANSWERED Work Phone: UNANSWERED [5] Designee: UNANSWERED Relation: UNANSWERED Phone: UNANSWERED Work Phone: UNANSWERED <RET> to CONTINUE, 1-5 or ALL to EDIT, ^N for screen N or '^' to QUIT:<enter> APPLICANT/SPOUSE EMPLOYMENT DATA, SCREEN <4> HLSEVEN,TEST; 703-11-1120P NON-VETERAN (OTHER) =============================================================================== [1] Employer: UNANSWERED <2> Spouse's: NOT APPLICABLE Occupation: UNANSWERED Status: UNANSWERED Retired Dt.: NOT APPLICABLE <RET> to CONTINUE, 1 or ALL to EDIT, ^N for screen N or '^' to QUIT:<enter> INSURANCE DATA, SCREEN <5> HLSEVEN,TEST; 703-11-1120P NON-VETERAN (OTHER) =============================================================================== [1] Covered by Health Insurance: NOT ANSWERED Insurance COB Subscriber ID Group Holder Effective Expires =========================================================================== No Insurance Information [2] Eligible for MEDICAID: UNANSWERED [3] Medicaid Number: <RET> to CONTINUE, 1-3 or ALL to EDIT, ^N for screen N or '^' to QUIT:<enter> ELIGIBILITY STATUS DATA, SCREEN <7> HLSEVEN,TEST; 703-11-1120P NON-VETERAN (OTHER) =============================================================================== [1] Patient Type: NON-VETERAN (OTHER) Veteran: NO Svc Connected: N/A SC Percent: N/A Rated Incomp.: UNANSWERED Claim Number: UNANSWERED Folder Loc.: UNANSWERED [2] Aid & Attendance: UNANSWERED Housebound: UNANSWERED VA Pension: UNANSWERED VA Disability: UNANSWERED Total Check Amount: NOT APPLICABLE GI Insurance: UNANSWERED Amount: UNANSWERED [3] Primary Elig Code: UNANSWERED Other Elig Code(s): NO ADDITIONAL ELIGIBILITIES IDENTIFIED Period of Service: UNANSWERED <4> Service Connected Conditions as stated by applicant --------------------------------------------------- NONE STATED <RET> to CONTINUE, 1-3 or ALL to EDIT, ^N for screen N or '^' to QUIT:<enter> ELIGIBILITY VERIFICATION DATA, SCREEN <11> HLSEVEN,TEST; 703-11-1120P NON-VETERAN (OTHER) =============================================================================== [1] Eligibility Status: NOT VERIFIED Status Date: NOT APPLICABLE Status Entered By: NOT APPLICABLE Interim Response: UNANSWERED (NOT REQUIRED) Verif. Method: NOT APPLICABLE Verif. Source: NOT AVAILABLE [2] Money Verified: NOT VERIFIED [3] Service Verified: NOT VERIFIED [4] Rated Disabilities: NOT APPLICABLE - NOT A VETERAN [5] Health Benefit Plan: (None Specified) <RET> to CONTINUE, 1-5 or ALL to EDIT, ^N for screen N or '^' to QUIT:<enter> ADMISSION INFORMATION, SCREEN <12> HLSEVEN,TEST; 703-11-1120P NON-VETERAN (OTHER) =============================================================================== NO ADMISSION DATA ON FILE FOR THIS PATIENT!! <RET> to CONTINUE, ^N for screen N or '^' to QUIT:<enter> APPLICATION INFORMATION, SCREEN <13> HLSEVEN,TEST; 703-11-1120P NON-VETERAN (OTHER) =============================================================================== NO APPLICATION DATA ON FILE FOR THIS PATIENT! <RET> to CONTINUE, ^N for screen N or '^' to QUIT:<enter> APPOINTMENT INFORMATION, SCREEN <14> HLSEVEN,TEST; 703-11-1120P NON-VETERAN (OTHER) =============================================================================== <1> Enrollment Clinics: NOT ACTIVELY ENROLLED IN ANY CLINICS AT THIS TIME <2> Pending Appt's: NO PENDING APPOINTMENTS ON FILE <RET> to CONTINUE, ^N for screen N or '^' to QUIT:<enter> SPONSOR DEMOGRAPHIC INFORMATION, SCREEN <15> HLSEVEN,TEST; 703-11-1120P NON-VETERAN (OTHER) =============================================================================== [1] Sponsor Information: No Sponsor Information available. *** Team Information *** -- No team assignment information found -- <RET> to QUIT, 1 or ALL to EDIT, ^N for screen N or '^' to QUIT:<enter> CONSISTENCY CHECKER TURNED OFF!! Patient is exempt from Copay. Is the patient currently being followed in a clinic for the same condition? N (No) Is the patient to be examined in the medical center today? Yes// <enter> (Yes) Registration login date/time: NOW// <enter> (DEC 28,2018@16:40) TYPE OF BENEFIT APPLIED FOR: 1 HOSPITAL TYPE OF CARE APPLIED FOR: 1 DENTAL REGISTRATION ELIGIBILITY CODE: HUMANITARIAN EMERGENCY // 6 6 NON-VETERAN Updating eligibility status for this registration... NEED RELATED TO AN ACCIDENT: N NO NEED RELATED TO OCCUPATION: N NO PRINT 10-10EZ? YES// NO PRINT HEALTH SUMMARY? Yes// N (No) ROUTING SLIP? Yes// N (No) PRINT ENCOUNTER FORMS? Yes// N (No) Select PATIENT NAME:
At the point you see Select PATIENT NAME
, it means that your A04 HL7 message just got sent. If you check the Link Manager, you will see an extra message that just got sent; and if you check Mirth or netcat, you will see that you just received an extra message.
What we will do this tutorial is set-up the VistA listener for HL7 first; and then we will send a message to VistA to register a patient and make VistA process it.
There are three ways to set-up the listener in VistA:
The recommended way to configure VistA is to use the Multi Listener via xinetd. Since that's hard to set-up (I have been consulted in more that one instance on setting this up on production systems), I will also show how to set-up the single-listener in VistA, which should be good for experimentation.
First, you need to find out if there is an existing multi-threaded listener on FOIA. It turns out that there is one. You can find its internal entry number in the LOGICAL LINK file by looking in the "E","M" index of the file:
FOIA201805>WRITE $ORDER(^HLCS(870,"E","M",0))
4
If we inquire into entry #4 in file 870 in Fileman, this is the information that we get:
FOIA201805>S DUZ=1 FOIA201805>D Q^DI MSC FileMan 22.1060 Select OPTION: INQUIRE TO FILE ENTRIES Output from what File: KERNEL SYSTEM PARAMETERS// 870 HL LOGICAL LINK (77 entries) Select HL LOGICAL LINK NODE: `4 LISTENER Another one: Standard Captioned Output? Yes// <enter> (Yes) Include COMPUTED fields: (N/Y/R/B): NO// <enter>- No record number (IEN), no Computed Fields NODE: LISTENER LLP TYPE: TCP DEVICE TYPE: Multi-threaded Server STATE: 2 server AUTOSTART: Enabled MAILMAN DOMAIN: FOIA.DOMAIN.EXT TIME STOPPED: APR 12,2018@13:59:35 SHUTDOWN LLP ?: YES QUEUE SIZE: 10 RE-TRANSMISSION ATTEMPTS: 5 READ TIMEOUT: 600 ACK TIMEOUT: 600 EXCEED RE-TRANSMIT ACTION: shutdown TCP/IP ADDRESS: 127.0.0.1 TCP/IP PORT: 5030 TCP/IP SERVICE TYPE: MULTI LISTENER IN QUEUE BACK POINTER: 236 IN QUEUE FRONT POINTER: 235 OUT QUEUE BACK POINTER: 903 OUT QUEUE FRONT POINTER: 903
The piece of data we are interested in is the "TCP/IP PORT" number. In this case, it's 5030. So, in the following xinetd configurations, you should substitute the port number with 5030.
On Cache, here's the xinetd definition:
service scd_hlst
{
type = UNLISTED
disable = no
flags = REUSE
socket_type = stream
protocol = tcp
port = {port number}
bind = xx.xx.xx.xx
wait = no
user = {cache user that you need to set-up for OS Authentication on Cache}
env = port={port number}
server = /usr/local/cachesys/devfey/bin/csession
server_args = {instance} -ci -U {namespace} PORT^HLCSTCPA
instances = UNLIMITED
per_source = UNLIMITED
}
On GT.M/YottaDB, you need a combination of an xinetd script and a shell script. It looks as follows:
service foia201805-hl7
{
port = {port}
socket_type = stream
protocol = tcp
type = UNLISTED
user = Hp
server = {shell path to script}
wait = no
disable = no
per_source = UNLIMITED
instances = UNLIMITED
}
The shell script looks as follows:
#!/bin/sh
. /var/db/foia201805/env.vista
LOG=$vista_home/log/hl7.log
echo "$$ Job begin `date`" >> ${LOG}
echo "$$ ${gtm_dist}/mumps -run GTMLNX^HLCSGTM" >> ${LOG}
${gtm_dist}/mumps -run GTMLNX^HLCSGTM 2>> ${LOG}
echo "$$ HL7 Listner stopped with exit code $?" >> ${LOG}
echo "$$ Job ended `date`"
To test that the connection works, use netcat or a similar tool to to connect to the port. Make sure the connection stays open. If it opens and then closes, then you have an problem. You should check the error trap with D ^XTER
if that happens.:
$ nc -v localhost 5030
Connection to localhost 5030 port [tcp/*] succeeded!
Go to EVE > HL7 Main Menu > Filer and Link Management Options > Link Edit. Create an entry called SLISTENER
on the first page, and mark the LLP type as TCP
:
| HL7 LOGICAL LINK
--------------------------------------------------------------------------------
NODE: SLISTENER DESCRIPTION:
INSTITUTION:
MAILMAN DOMAIN:
AUTOSTART:
QUEUE SIZE: 10
LLP TYPE: TCP
DNS DOMAIN:
_______________________________________________________________________________
Exit Save Refresh Quit
Once you hit enter after typing TCP, you will see the second page. Set the TCP/IP SERVICE TYPE
to SINGLE LISTENER
, and the port to a reasonable number (but not 5000, 5001, 5030, 5031, as these are reserved for the multi-listeners on production/test for HL7/HLO.
| HL7 LOGICAL LINK
--------------------------------------------------------------------------------
┌──────────────────────TCP LOWER LEVEL PARAMETERS─────────────────────────┐
│ SLISTENER │
│ │
│ TCP/IP SERVICE TYPE: SINGLE LISTENER │
│ TCP/IP ADDRESS: │
│ TCP/IP PORT: 5032 │
│ TCP/IP PORT (OPTIMIZED): │
│ │
│ ACK TIMEOUT: RE-TRANSMISION ATTEMPTS: │
│ READ TIMEOUT: EXCEED RE-TRANSMIT ACTION: │
│ BLOCK SIZE: SAY HELO: │
│ TCP/IP OPENFAIL TIMEOUT: │
│STARTUP NODE: PERSISTENT: │
│ RETENTION: UNI-DIRECTIONAL WAIT: │
└─────────────────────────────────────────────────────────────────────────┘
_______________________________________________________________________________
Close Refresh
Enter a COMMAND, or "^" followed by the CAPTION of a FIELD to jump to.
COMMAND: Close Press <F1>H for help Insert
Once you do that, start the listener using Start/Stop Links [SL] on the same menu:
Select Filer and Link Management Options Option: SL Start/Stop Links
This option is used to launch the lower level protocol for the
appropriate device. Please select the node with which you want
to communicate
Select HL LOGICAL LINK NODE: SLISTENER
Job was queued as 8139.
To test that the connection works, use netcat or a similar tool to to connect to the port. Make sure the connection stays open. If it opens and then closes, then you have an problem. You should check the error trap with D ^XTER
if that happens (netcat gives a weird output below with native listeners, saying connection refused then succeeded -- I think that's because it tries IPv6 first and then IPv4 (i.e. ::1 and then 127.0.0.1)):
$ nc -v localhost 5032
nc: connect to localhost port 5032 (tcp) failed: Connection refused
Connection to localhost 5032 port [tcp/*] succeeded!
Now that we have a working listener, we have to find an example on how to use it. In many of the cases, you will be hooking up to existing HL7 clients in VistA. I couldn't find an easy standalone example, so I came up with a small example in which we can send a ADT/A04 HL7 message to VistA, and have it register the patient from the message. It's not production quality code, but it will do the job for this tutorial.
Do you remember this confusing section when we talked about sending messages out? It's confusing here too.
The trick is realizing that since all messages go through the same pipe, the only way VistA will know how to route a message is based on its contents.
Like sending a message, we need an EVENT DRIVER (aka SERVER) protocol. The second page of the EVENT DRIVER protocol contains the fields that VistA uses to match a message with the proper EVENT DRIVER. Here's a screen scrape for discussion:
| HL7 EVENT DRIVER PAGE 2 OF 2
ZZ ADT-A04 SERVER
--------------------------------------------------------------------------------
SENDING APPLICATION: MIRTH TRANSACTION MESSAGE TYPE: ADT EVENT TYPE: A04
MESSAGE STRUCTURE:
PROCESSING ID: VERSION ID: 2.4
ACCEPT ACK CODE: AL APPLICATION ACK TYPE: SU
RESPONSE PROCESSING RTN:
SUBSCRIBERS
ZZ ADT-A04 CLIENT2
_______________________________________________________________________________
Exit Save Previous Page Refresh Quit
To find the EVENT DRIVER, VistA parses the MSH segment of the incoming message and matches the following:
Once it does that, it needs to match with the SUBSCRIBERs. It does that using the RECEIVING APPLICATION field:
| HL7 EVENT DRIVER PAGE 2 OF 2
┌──────────────────────────HL7 SUBSCRIBER────────────────────────────────┐
---│ ZZ ADT-A04 CLIENT │---
│------------------------------------------------------------------------│
TR│ RECEIVING APPLICATION: MIRTH-VISTA │
│ │
│ RESPONSE MESSAGE TYPE: ADT EVENT TYPE: A04 │
│ │
│SENDING FACILITY REQUIRED?: RECEIVING FACILITY REQUIRED?: │
RE│ │
│ SECURITY REQUIRED?: │
O│ │
│ LOGICAL LINK: │
│ │
│ PROCESSING RTN: D ADTA04^OSEHL7 │
│ ROUTING LOGIC: │
└────────────────────────────────────────────────────────────────────────┘
_______________________________________________________________________________
Close Refresh
Remember we talked about how the fields ROUTING LOGIC
, LOGICAL LINK
, and PROCESSING RTN
interact with each other for outgoing messages? With an incoming message, it's much simpler: only the PROCESSING RTN
is looked at and executed.
I wrote this tiny routine. to register a patient once an appropriate HL7 message has been received. It's a .m file. If you cannot import .m files, you can copy and paste it into an appropriate editor and save the routine.
The first order of business is to create the sending and receiving applications. As previously described, the SENDING APPLICATION is used to match the event driver; and the RECEIVING APPLICATION is used to match the subscriber.
Go to EVE > HL7 Main Menu > Interface Developer Options > Application Edit [EA]. Create two applications, one called MIRTH
for the sending side, and one called MIRTH-VISTA
so that they look as follows:
| HL7 APPLICATION EDIT
--------------------------------------------------------------------------------
NAME: MIRTH ACTIVE/INACTIVE: ACTIVE
FACILITY NAME: COUNTRY CODE: USA
HL7 FIELD SEPARATOR: HL7 ENCODING CHARACTERS:
MAIL GROUP:
_______________________________________________________________________________
Exit Save Refresh Quit
...and:
| HL7 APPLICATION EDIT
--------------------------------------------------------------------------------
NAME: MIRTH-VISTA ACTIVE/INACTIVE: ACTIVE
FACILITY NAME: COUNTRY CODE: USA
HL7 FIELD SEPARATOR: HL7 ENCODING CHARACTERS:
MAIL GROUP:
_______________________________________________________________________________
Exit Save Refresh Quit
Go to EVE > HL7 Main Menu > Interface Developer Options > Protocol Edit [EP]. Create a new protocol as follows:
You will get to the first page. Cursor down to "TYPE" an type event driver
.
Once you hit enter after typing event driver
, you will get to the second page. Fill in everything here as shown (don't fill in SUBSCRIBERS yet--that's the next step):
| HL7 EVENT DRIVER PAGE 2 OF 2
ZZ ADT-A04 SERVER
--------------------------------------------------------------------------------
SENDING APPLICATION: MIRTH
TRANSACTION MESSAGE TYPE: ADT EVENT TYPE: A04
MESSAGE STRUCTURE:
PROCESSING ID: VERSION ID: 2.4
ACCEPT ACK CODE: APPLICATION ACK TYPE:
RESPONSE PROCESSING RTN:
SUBSCRIBERS
_______________________________________________________________________________
Exit Save Previous Page Refresh Quit
By the way, we don't need to put the ACK types here; as the ACK behavior will be dictated by what the message has for ACK types.
Fill in the subscriber as follows:
Then fill in the following page as follows:
| HL7 EVENT DRIVER PAGE 2 OF 2
┌──────────────────────────HL7 SUBSCRIBER────────────────────────────────┐
---│ ZZ ADT-A04 CLIENT2 │---
│------------------------------------------------------------------------│
TR│ RECEIVING APPLICATION: MIRTH-VISTA │
│ │
│ RESPONSE MESSAGE TYPE: ADT EVENT TYPE: A04 │
│ │
│SENDING FACILITY REQUIRED?: RECEIVING FACILITY REQUIRED?: │
RE│ │
│ SECURITY REQUIRED?: │
O│ │
│ LOGICAL LINK: │
│ │
│ PROCESSING RTN: D ADTA04^OSEHL7 │
│ ROUTING LOGIC: │
└────────────────────────────────────────────────────────────────────────┘
_______________________________________________________________________________
Close Refresh
There should be nothing here that is surprising given our discussions so far.
We need to create a sample message that VistA will process. Since all that we do in ADTA04^OSEHL7 is grab the name, gender, and date of birth, all we need in a sample message is the PID segment, pieces 5 (Name), 7 (DOB), and 8 (Gender).
Accordingly, here's a sample message:
MSH^~|\&^MIRTH^^MIRTH-VISTA^^20181230192022-0400^^ADT~A04^10000^P^2.4^^^AL^SU
PID^^^^^HLSEVEN~INCOMING^^19571111^M
A few important points, as the message header is SO important:
Here are two websites where you can paste your HL7 message and get a breakdown of the pieces in it - HL7 Inspector and ParseHog.
Due to the fact the message retention in VistA is based on date, you should change the message date/time to be for today's date, so that your message won't be purged if it is too old. For example, today (as I am writing this) is 4 Jan 2019; so the date would be 20190104103022-0400 etc.
To send a message to VistA, we need to do the following steps in Mirth:
I hope you would have Mirth open by now. On the left hand side, first box from the top, click on "Channels", and then under Channel Tasks in the second box, click on "New Channel".
On the Summary Tab, set the name to be "VistA HL7 Sender". Then click on the Destinations Tab. Change the "Connector Type" to "TCP Sender", and change the IP address and port under the TCP Sender Settings, like this:
You should click on "Test Connection" to see if there is somebody listening on the other end.
There is also an encoding parameter which you need to scroll down to reach. If you are not dealing with ASCII, you should probably explicitly set that.
Click on "Save Changes" under Channel Tasks, and then Deploy Channel. You will be taken to the Dashboard.
Click on "VistA HL7 Sender", and then on the left hand side, under "Dashboard Tasks", click on "Send Message". Paste the message we just create, and then click on Process Message.
If you wait a moment, you will see the "Connection Log" at the bottom update; and you will also see that Received and Sent Column on the Dashboard will show up as "1":
On the right hand side, click on "View Messages" under "Dashboard Tasks", then click on the row that says "Destination 1". You will see your message in the bottom pane:
In the bottom pane, where you see the radio buttons that says "Raw/Encoded/Sent/Response", click on Response. You may need to resize the Window to see the ACK message:
This ACK message is the ACK sent by VistA (CA = Commit Accept). This says that VistA received the message. This is because we asked for ACCEPT ACK CODE in the message of AL. But let's say we want an application acknowledgement, i.e., that VistA PROCESSED, not just received, the message. We can ask for that by changing the message field "Accept Acknowledgement Type" to "NE", and leaving "Application Acknowledgement Type" to "SU". We do that in the next HL7 message we will send, where the last two pieces are NE^SU rather than AL^SU.
The new message looks like this (I adjusted the time and message counter so that this message isn't treated as a duplicate):
MSH^~|\&^MIRTH^^MIRTH-VISTA^^20181230192023-0400^^ADT~A04^10001^P^2.4^^^NE^SU
PID^^^^^HLSEVEN~INCOMING^^19571111^M
Send this using the same mechanism as before. The ACK will now be an application ack:
The AA says that the DFN is 23. Let's see if you we indeed have that patient registered with a DFN of 23:
FOIA201805>D Q^DI
MSC FileMan 22.1060
Select OPTION: INQUIRE TO FILE ENTRIES
Output from what File: HL LOGICAL LINK// 2 PATIENT (23 entries)
Select PATIENT NAME: `23 HLSEVEN,INCOMING, 11-11-57 303111159P **Pseu
do SSN** NO NON-VETERAN (OTHER)
Another one:
Standard Captioned Output? Yes// (Yes)
Include COMPUTED fields: (N/Y/R/B): NO// - No record number (IEN), no Computed
Fields
Display Audit Trail? No// NO
NAME: HLSEVEN,INCOMING, SEX: MALE
DATE OF BIRTH: 11/11/1957 SOCIAL SECURITY NUMBER: 303111159P
WHO ENTERED PATIENT: 사용자,하나 DATE ENTERED INTO FILE: JAN 2,2019
CHECK FOR DUPLICATE: YES SERVICE CONNECTED?: NO
INTEGRATION CONTROL NUMBER: 500000022
ICN CHECKSUM: 892175 FULL ICN: 500000022V892175
NAME COMPONENTS: 2
PSEUDO SSN REASON: SSN UNKNOWN/FOLLOW-UP REQUIRED
TYPE: NON-VETERAN (OTHER) VETERAN (Y/N)?: NO
Okay. Things look good. That's it for sending.
The first step in any debugging is to see if the Link Monitor shows any messages being sent/received. There is an example in Creating a Test Message section.
Messages can be viewed using EVE > HL7 Main Menu > Message Management Options > View Transmission Log (TCP only) [LOG]. Note that the log has a cut-off that is 96 hours by default; I think if you send messages with date/times older than that in the message header, they get deleted after they get processed. So in the sample messages presented in this tutorial, you probably want to update the date/time in the MSH to be today and adjust the time accordingly.
The Messages Viewer is tricky to navigate. Press Tab to move between messages (shift-tab does not work); and press enter to view a message; press Left-Arrow to exit a message and back into the messages view. Press F1-E to exit the messages viewer. Here's a screen scrape:
Select Message Management Options Option: LOG View Transmission Log (TCP only) Search Transmission Log Select one of the following: M Message Search P Pending Transmissions E Error Listing Q Quit (also uparrow, or) Selection: Message Search Start/Stop Time Selection Enter START Date and Time. Date is required. Enter a date and optional time: T// <enter> (JAN 02, 2019) Enter END Date and Time. Date is required. Enter a date and optional time: NOW// <enter> (JAN 02, 2019@10:59:59) Message Criteria for Search Select Status Code for Report: ALL//<enter> Select Logical Link for Report: ALL//<enter> Select Message Type for Report: ALL//<enter> Select Event Type for Report: ALL//<enter> . . . PLEASE WAIT, THIS CAN TAKE AWHILE . . . MESSAGE ID # D/T Entered Log Link Msg:Evn IO Sndg Apl Rcvr Apl 505375 010219.112307 VBECSPTU ADT:A04 OT VAFC PIM VBECS AD 505376 010219.112307 MEMPHIS ADT:A04 OT VAFC PIM NETCAT HYPER-TXT|Press H for help| Line> 2 of 2 Screen> 1 of 1 MESSAGE Record #: 5376 Message #: 505376 D/T Entered: 010219.112307 D/T Processed: 010219.11231 Logical Link: MEMPHIS Ack To MSG#: 505376 D/T STATUS: 010219.11231 STATUS: SUCCESSFULLY COMPLETED ERR MSG: ERR TYPE: Sending Appl: VAFC PIMS Receiving Appl: NETCAT Message Type: ADT Event Type: A04 MESSAGE HEADER: MSH^~|\&^VAFC PIMS^50^NETCAT^^20190102112307-0400^^ADT~A04^505376^P^2.3^^^NE^NE^USA MESSAGE TEXT: EVN^A04^20190102112307-0400^^^1~사용자~하나 PID^1^500000001V075322^1~8~M10^1155P^마우스~미키^""^19551111^M^^""~~0005~""~~CDC^ 東京中央郵便局~ 東京都中央区八重洲一丁目5番3号 ~東京中央郵便局~東~100-8994~JAPAN~P~""~""|""~""~""~""~""~~VACAE~""~""~~~""&""|""~""~""~""~""~~VACAA~""~""~~| ""~""~""~""~""~~VACAM~""~""~~~""&""|""~""~""~""~""~~VACAO~""~""~~~""&""^^""^""^^""^29^^505111155P^^^""~~0189~""~~CDC^ PD1^^^PLATINUM~~050^"" PV1^1^O^""^^^^^^^^^^^^^^^NON-VETERAN (OTHER)^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^113 OBX^1 HYPER-TXT|Press H for help| Line> 22 of 38 Screen> 1 of 2
You can use $$REPROC^HLUTIL(message ien,"processing routine")
to re-process an incoming HL7 message. This is useful when developing processing code. Here's an example of re-processing an incoming message. I got the message number by following the instructions in Viewing the Messages in VistA. The field containing the message IEN is "Record #".
FOIA201805>ZB ADTA04^OSEHL7
FOIA201805>ZWRITE ^HLMA(5380,*)
^HLMA(5380,0)="5390^10003^I^^^5380^79^5105^^^241^242^6^4"
^HLMA(5380,2)="^3190102.114008"
^HLMA(5380,"MSH",0)="^^1^1^3190102^"
^HLMA(5380,"MSH",1,0)="MSH^~|\&^MIRTH^^MIRTH-VISTA^^20190102140000-0400^^ADT~A04
^10003^P^2.4^^^NE^SU"
^HLMA(5380,"P")="3^3190102.114008"
^HLMA(5380,"S")="3190102.114008^^^^^3190102.114008^3190102.114008"
FOIA201805>W $$REPROC^HLUTIL(5380,"ADTA04^OSEHL7")
%GTM-I-BREAKZBA, Break instruction encountered during ZBREAK action
At M source location ADTA04+2^OSEHL7
FOIA201805>ZST INTO
F I=1:1 X HLNEXT Q:HLQUIT'>0 D
%GTM-I-BREAKZST, Break instruction encountered during ZSTEP action
At M source location ADTA04+3^OSEHL7
FOIA201805>ZST INTO
K HLNODE
%GTM-I-BREAKZST, Break instruction encountered during ZSTEP action
At M source location HLNEXT+8^HLCSUTL
FOIA201805>ZST INTO
N HLI,HLDONE,HLX
%GTM-I-BREAKZST, Break instruction encountered during ZSTEP action
At M source location HLNEXT+9^HLCSUTL
FOIA201805>ZST OUTOF
. I $P(HLNODE,HL("FS"))="PID" D
%GTM-I-BREAKZST, Break instruction encountered during ZSTEP action
At M source location ADTA04+4^OSEHL7
FOIA201805>W HLNODE
MSH^~|\&^MIRTH^^MIRTH-VISTA^^20190102140000-0400^^ADT~A04^10003^P^2.4^^^NE^SU
FOIA201805>ZC
303111259P0
Before running any of this, you need to turn off all background messaging tasks, as these will process your messages before you have a chance to touch them. Do that using EVE > HL7 Main Menu > Filer and Link Management Options > Stop All Messaging Background Processes [SA] and then TCP Link Manager Start/Stop [LM].
If you are using xinetd, you need to stop the xinetd as well (or at least disable the HL7 listener).
Most of these debug methods rely on you knowing the IEN of the Logical Link that will receive the messages. Inquire into file 'HL LOGICAL LINK' and ask for the record numbers. Here are my numbers:
FOIA201805>D P^DI
MSC FileMan 22.1060
Select OPTION: INQUIRE TO FILE ENTRIES
Output from what File: HL LOWER LEVEL PROTOCOL TYPE// HL LOGICAL LINK
(78 entries)
Select HL LOGICAL LINK NODE: MEMPHIS
Another one: SLISTENER
Another one: LISTENER
Another one:
Standard Captioned Output? Yes// (Yes)
Include COMPUTED fields: (N/Y/R/B): NO// Record Number (IEN)
NUMBER: 78 NODE: MEMPHIS
LLP TYPE: TCP DEVICE TYPE: Non-Persistent Client
STATE: Shutdown TIME STOPPED: JAN 2,2019@12:01:47
SHUTDOWN LLP ?: YES QUEUE SIZE: 10
READ TIMEOUT: 1 ACK TIMEOUT: 1
TCP/IP ADDRESS: 127.0.0.1 TCP/IP PORT: 6661
TCP/IP SERVICE TYPE: CLIENT (SENDER) IN QUEUE BACK POINTER: 1
IN QUEUE FRONT POINTER: 1 OUT QUEUE BACK POINTER: 7
OUT QUEUE FRONT POINTER: 7
NUMBER: 79 NODE: SLISTENER
LLP TYPE: TCP DEVICE TYPE: Single-threaded Server
STATE: Shutdown TIME STOPPED: JAN 2,2019@12:02:10
SHUTDOWN LLP ?: YES QUEUE SIZE: 10
TCP/IP PORT: 5032 TCP/IP SERVICE TYPE: SINGLE LISTENER
IN QUEUE BACK POINTER: 5 IN QUEUE FRONT POINTER: 5
OUT QUEUE BACK POINTER: 5 OUT QUEUE FRONT POINTER: 5
NUMBER: 4 NODE: LISTENER
LLP TYPE: TCP DEVICE TYPE: Multi-threaded Server
STATE: 2 server AUTOSTART: Enabled
MAILMAN DOMAIN: FOIA.DOMAIN.EXT TIME STOPPED: APR 12,2018@13:59:35
SHUTDOWN LLP ?: YES QUEUE SIZE: 10
RE-TRANSMISSION ATTEMPTS: 5 READ TIMEOUT: 600
ACK TIMEOUT: 600 EXCEED RE-TRANSMIT ACTION: shutdown
TCP/IP ADDRESS: 127.0.0.1 TCP/IP PORT: 5030
TCP/IP SERVICE TYPE: MULTI LISTENER IN QUEUE BACK POINTER: 236
IN QUEUE FRONT POINTER: 235 OUT QUEUE BACK POINTER: 903
OUT QUEUE FRONT POINTER: 903
First, run the code that will create the message and put it in the VistA HL7 Engine (typically via GENERATE^HLMA
). Once that's done, you can debug the sending of the message by setting HLDP to the IEN of the Logical Link, and then running D ^HLCSTCP
. Set a breakpoint in the appropriate place to debug. This example creates an ADT-A04 for DFN #20 and steps through sending it for the logical link "MEMPHIS".
$ mumps -dir
FOIA201805>S DUZ=1
FOIA201805>D ^XUP
Setting up programmer environment
This is a TEST account.
Terminal Type set to: C-VT220
Select OPTION NAME:
FOIA201805>N % S %=$$EN^VAFCA04(20,$$NOW^XLFDT)
FOIA201805>ZB ^HLCSTCP
FOIA201805>S HLDP=78
FOIA201805>D ^HLCSTCP
%GTM-I-BREAKZBA, Break instruction encountered during ZBREAK action
At M source location HLCSTCP+7^HLCSTCP
FOIA201805>ZST INTO
L +^HLCS("HLTCPLINK",HLDP):5 I '$T D Q
%GTM-I-BREAKZST, Break instruction encountered during ZSTEP action
At M source location HLCSTCP+9^HLCSTCP
This is very similar to Sending, except that you will put a breakpoint on the code that will process the message. The start of the processing is in SERVER^HLCSTCP
, but almost always you will want to start with PROCESS^HLCSTCP1
, as you may not receive the message in full because the other end will time out.
FOIA201805>S DUZ=1
FOIA201805>D ^XUP
Setting up programmer environment
This is a TEST account.
Terminal Type set to: C-VT220
Select OPTION NAME:
FOIA201805>zb SERVER^HLCSTCP
FOIA201805>S HLDP=79
FOIA201805>D ^HLCSTCP ; Now, send the message from outside.
%GTM-I-BREAKZBA, Break instruction encountered during ZBREAK action
At M source location SERVER^HLCSTCP
%GTM-W-NOTPRINCIO, Output currently directed to device SCK$95524
Not really; we will just fake out a new listener by writing some new code. This is a bit harder, so you may be better off using the Single Listener, which doesn't require any code changes. This example is for GT.M; the process is similar for Cache, but this exact code won't work. Add this code in HLCSGTM:
DEBUG ;Entry point for debug, Build a server to get the connect
W !,"IP Socket to Listen on: " R SOCK:300,! Q:'$T!(SOCK["^")
;Use %ZISTCP to do a single server
D LISTEN^%ZISTCP(SOCK,"SERV^HLCSGTM")
U $P W !,"Done"
Q
;
SERV ;
S U="^",$ZT="",$ET="D ^%ZTER HALT" ;Setup the error trap
; GTM specific code
X "U IO:(nowrap:nodelimiter:IOERROR=""TRAP"")" ;Setup device
S @("$ZINTERRUPT=""I $$JOBEXAM^ZU($ZPOSITION)""")
K ^TMP($J) ZSHOW "D":^TMP($J)
F %=1:1 Q:'$D(^TMP($J,"D",%)) S X=^(%) Q:X["LOCAL"
S IO("IP")=$P($P(X,"REMOTE=",2),"@"),IO("PORT")=+$P($P(X,"LOCAL=",2),"@",2)
S %=$P($ZTRNLNM("SSH_CLIENT")," ") S:%="" %=$ZTRNLNM("REMOTEHOST")
S HLDP=$ORDER(^HLCS(870,"E","M",0))
D LISTEN^HLCSTCP
QUIT
As before, you should probably put a breakpoint at PROCESS^HLCSTCP1
. Then run D DEBUG^HLCSGTM
, and put it any port number (as long as you configure Mirth to send to it):
FOIA201805>D DEBUG^HLCSGTM
IP Socket to Listen on: 5035
%GTM-I-BREAKZBA, Break instruction encountered during ZBREAK action
At M source location PROCESS+2^HLCSTCP1
%GTM-W-NOTPRINCIO, Output currently directed to device SCK$95524
Typically an HL7 message is sent due to an event happening in the VistA system. There are already a large amount of HL7 event protocols configured in the system; they are typically there for the operation of a specific package (e.g. lab sending and receiving data to lab instruments). They are typically documented in the Technical manuals for the specific package which you can find on the VistA Documentation Library. Just to be clear, in this case you do not have to create any HL7 messages. VistA already creates them for you; and you typically only have to fill out the correct IP address and port in the HL7 link and do some package configuration and then you will start receiving them.
In order to demonstrate creating a new HL7 message, You can attach to existing events in VistA to watch for changes in the patient record and then send an HL7 message based on that change. An event in VistA is actually an extended action type entry in the PROTOCOL file. Here's a reasonably complete list of the important patient record events that happen in VistA, listed by the name of the PROTOCOL entry that lets its subscribers know that the event happened. This list is accurate as of February 2019.
PROTOCOL | Description |
---|---|
DG FIELD MONITOR | DG Field Monitor |
DGPM MOVEMENT EVENTS | MOVEMENT EVENTS v 5.0 |
GMPL EVENT | Problem List Update Event |
GMRA ENTERED IN ERROR | Reaction Data Entered in Error |
GMRA SIGN-OFF ON DATA | Sign-off on Reaction Data |
GMRC EVSEND OR | Consults event sent to OE/RR |
IBCN NEW INSURANCE EVENTS | IB New Insurance Event Driver |
LR7O CH EVSEND OR | LAB => OE/RR ORDER MESSAGE EVENT |
OR EVSEND FH | OE/RR => DIET MESSAGE EVENT |
OR EVSEND GMRC | OE/RR => CONSULTS MESSAGE EVENT |
OR EVSEND LRCH | OE/RR => LAB MESSAGE EVENT |
OR EVSEND ORG | OE/RR => GENERIC MESSAGE EVENT |
OR EVSEND PS | OE/RR => PHARMACY MESSAGE EVENT |
OR EVSEND RA | OE/RR => RAD/NM MESSAGE EVENT |
OR EVSEND VPR | OE/RR => VPR MESSAGE |
PS EVSEND OR | Send Pharmacy orders to CPRS. |
PXK VISIT DATA EVENT | VISIT RELATED DATA |
RA EVSEND OR | Radiology event sent to OE/RR |
SCMC PATIENT TEAM CHANGES | PCMM Patient Team Update Event Driver |
SCMC PATIENT TEAM POSITION CHANGES | PCMM Patient Team Position Update Event Driver |
SDAM APPOINTMENT EVENTS | Appointment Event Driver |
It actually turns out that CPRS sends HL7-like messages to various packages when orders are placed in CPRS. We will use these HL7 segments rather than construct our own for the following demo. This is the list of the protocols in which this happens:
PROTOCOL | Description |
---|---|
OR EVSEND FH | OE/RR => DIET MESSAGE EVENT |
OR EVSEND GMRC | OE/RR => CONSULTS MESSAGE EVENT |
OR EVSEND LRCH | OE/RR => LAB MESSAGE EVENT |
OR EVSEND PS | OE/RR => PHARMACY MESSAGE EVENT |
OR EVSEND RA | OE/RR => RAD/NM MESSAGE EVENT |
The HL7 message CPRS sends looks like this (this is a pharmacy message):
MSH|^~\&|ORDER ENTRY|1|PHARMACY|1|20190415120624-0400||ORM
PID|||3||COYOTE,WILEY
PV1||O|3|||||||||||||||||||||||||||||||||||||||||
ORC|NW|31;1^OR|||||325&MG&1&TABLET&325MG&2^QID PRN^^^^R^^325MG^~||201904151206-0400|1||1|||20190415120624-0400|E^ELECTRONICALLY ENTERED^99ORN^^^
RXO|^^^2^ACETAMINOPHEN TAB ^99PSP|||||||||1338.6642^^99NDF^2^^99PSD|12||0||||D3
NTE|7|P|FOR PAIN
RXR|^^^1^ORAL^99PSR
ZRX||E|N|W
ZSC||||||||
So what we will do in this example is take all the lines except the MSH line (which VistA generates automatically) and send it over to Mirth, like we did in earlier examples.
This is a summary. More detail can be found in this VDL manual, Chapter 8 page 101ff.
HL7 Application Entries
Write the M code HL7 code that will use the Event Driver to send the message.
INIT^HLFNC2
with the name of your Event Driver.The last step is to call the previous M code from another place in the system. Typically this is workflow specific; and a call will be inserted in a specific part of an application or at an event point in VistA. As mentioned above, in our sample we will attach to an event point that happens when you place certain kinds of orders from CPRS.
This example is based on the excellent example that can by found in this VDL manual, Chapter 8, page 101ff. We will be following the manual for all the steps except for the code, which we will author ourselves.
Remember that HL7 Applications, Logical Links, and Protocols can be edited from the menu EVE > HL7 Main Menu ... [HL MAIN MENU] > Interface Developer Options ... [HL MENU INTERFACE TK]. We won't go over in detail how to enter items; take a look at the previous sections or at the referenced manual for that.
| HL7 APPLICATION EDIT
--------------------------------------------------------------------------------
NAME: VIST-AC ACTIVE/INACTIVE: ACTIVE
FACILITY NAME: COUNTRY CODE: KOR
HL7 FIELD SEPARATOR: HL7 ENCODING CHARACTERS:
MAIL GROUP:
_______________________________________________________________________________
Exit Save Refresh Quit
| HL7 APPLICATION EDIT
--------------------------------------------------------------------------------
NAME: ACME ACTIVE/INACTIVE: ACTIVE
FACILITY NAME: COUNTRY CODE: KOR
HL7 FIELD SEPARATOR: HL7 ENCODING CHARACTERS:
MAIL GROUP:
_______________________________________________________________________________
Exit Save Refresh Quit
(page 1) HL7 LOGICAL LINK
--------------------------------------------------------------------------------
NODE: ACME TCP DESCRIPTION:
INSTITUTION:
MAILMAN DOMAIN:
AUTOSTART:
QUEUE SIZE: 10
LLP TYPE: TCP
DNS DOMAIN:
_______________________________________________________________________________
(page 2) HL7 LOGICAL LINK
--------------------------------------------------------------------------------
┌──────────────────────TCP LOWER LEVEL PARAMETERS─────────────────────────┐
│ ACME TCP │
│ │
│ TCP/IP SERVICE TYPE: CLIENT (SENDER) │
│ TCP/IP ADDRESS: 127.0.0.1 │
│ TCP/IP PORT: 6661 │
│ TCP/IP PORT (OPTIMIZED): │
│ │
│ ACK TIMEOUT: RE-TRANSMISION ATTEMPTS: │
│ READ TIMEOUT: EXCEED RE-TRANSMIT ACTION: │
│ BLOCK SIZE: SAY HELO: │
│ TCP/IP OPENFAIL TIMEOUT: │
│STARTUP NODE: PERSISTENT: │
│ RETENTION: UNI-DIRECTIONAL WAIT: │
└─────────────────────────────────────────────────────────────────────────┘
_______________________________________________________________________________
Close Refresh
| HL7 INTERFACE SETUP PAGE 1 OF 2
--------------------------------------------------------------------------------
NAME: VSIT-AC ORM-O01 EVENT
DESCRIPTION (wp): (empty)
ENTRY ACTION:
EXIT ACTION:
TYPE: event driver
_______________________________________________________________________________
Enter a code from the list.
Choose from:
E event driver
S subscriber
HL7 EVENT DRIVER PAGE 2 OF 2
VSIT-AC ORM-O01 EVENT
--------------------------------------------------------------------------------
SENDING APPLICATION: VIST-AC
TRANSACTION MESSAGE TYPE: ORM EVENT TYPE: O01
MESSAGE STRUCTURE:
PROCESSING ID: debug VERSION ID: 2.3
ACCEPT ACK CODE: AL APPLICATION ACK TYPE: NE
RESPONSE PROCESSING RTN:
SUBSCRIBERS
| HL7 SUBSCRIBER PAGE 2 OF 2
ACME ORM-O01 SUBS
--------------------------------------------------------------------------------
RECEIVING APPLICATION: ACME
RESPONSE MESSAGE TYPE: ACK EVENT TYPE: O01
SENDING FACILITY REQUIRED?: NO RECEIVING FACILITY REQUIRED?: NO
SECURITY REQUIRED?:
LOGICAL LINK: ACME TCP
PROCESSING RTN:
ROUTING LOGIC:
_______________________________________________________________________________
VOHL7MSG ;OSEHRA/SMH - Orders HL7 Processing Example;Apr 16, 2019@16:07
;;0.0;EXAMPLE;;
;
EN(MSG) ; -- main entry point protocol VO OR HL7 MONITOR where MSG contains HL7
msg
; This protocol is designed to be attached to the following event protocols
; used by CPRS ordering that emit an HL7 message when an order is placed.
; - OR EVSEND RA
; - OR EVSEND LRCH
; - OR EVSEND FH
; - OR EVSEND PS
; - OR EVSEND GMRC
;
; The action protocol should call it like this: D EN^VOHL7MSG(.XQORMSG)
;
N VOMSG,MSH
; ; Get message array name
S VOMSG=$S($L($G(MSG)):MSG,1:"MSG"),MSH=0 ;MSG="NAME" or MSG(#)
;
; Don't process messages that are not MSH type
N SEG S SEG=0
F S SEG=$O(@VOMSG@(SEG)) Q:SEG'>0 Q:$E(@VOMSG@(SEG),1,3)="MSH"
Q:'SEG ;no message/header
;
; Initialize Destination Server
N HL,HLA D INIT^HLFNC2("VSIT-AC ORM-O01 EVENT",.HL)
I $G(HL)'="" D APPERROR^%ZTER("VOHL7MSG - VAFC A04 SERVER NOT DEFINED PROPERLY") QUIT
;
; Now extract HL7 message
; At this point SEG is at the MSH segment. We don't want that as VistA HL7
; generates its own MSH segment based on the protocol configuration. So we
; start looping to the first segment after MSH.
N CNT S CNT=1
F S SEG=$O(@VOMSG@(SEG)) Q:SEG'>0 S HLA("HLS",CNT)=@VOMSG@(SEG),CNT=CNT+1
;
; Fix the CPRS Field Separator and Component Separator to match the message's
; definition.
; CPRS hardcodes | and ^ for these.
F CNT=0:0 S CNT=$O(HLA("HLS",CNT)) Q:'CNT S HLA("HLS",CNT)=$TR(HLA("HLS",CNT),"|^",HL("FS")_$E(HL("ECH")))
;
; Send the HL7 message
N HLRST
D GENERATE^HLMA("VSIT-AC ORM-O01 EVENT","LM",1,.HLRST)
I $P(HLRST,U,2) D APPERROR^%ZTER("VOHL7MSG - HL7 MESSAGE SEND FAILED")
QUIT
NAME: VO OR HL7 MONITOR
ITEM TEXT: Sample Order HL7 Messages Receiver
TYPE: action
DESCRIPTION: Sample code to show how to receive HL7 messages for events
related to ordering.
ENTRY ACTION: D EN^VOHL7MSG(.XQORMSG)
The following is the screenscrape of steps 7 and 8:
FOIA201805>S DUZ=1 D P^DI
MSC FileMan 22.1061
Select OPTION: ENTER OR EDIT FILE ENTRIES
Input to what File: PROTOCOL// (4593 entries)
EDIT WHICH FIELD: ALL// NAME
THEN EDIT FIELD: ITEM TEXT
THEN EDIT FIELD: TYPE
THEN EDIT FIELD: DESCRIPTION (word-processing)
THEN EDIT FIELD: ENTRY ACTION
THEN EDIT FIELD:
STORE THESE FIELDS IN TEMPLATE:
Select PROTOCOL NAME: VO OR HL7 MONITOR
Not a known package or a local namespace.
Are you adding 'VO OR HL7 MONITOR' as a new PROTOCOL (the 4594TH)? No// Y
(Yes)
PROTOCOL ITEM TEXT: Sample Order HL7 Messages Receiver
PROTOCOL IDENTIFIER:
ITEM TEXT: Sample Order HL7 Messages Receiver Replace
TYPE: action action
DESCRIPTION:
THERE ARE NO LINES!
Edit? NO// y YES
==[ WRAP ]==[INSERT ]==============< DESCRIPTION >====[Press <F1>H for help]====
Sample code to show how to receive HL7 messages for events related to
ordering.
<=======T=======T=======T===[Use control-E to exit]=====T=======T=======T>======
ENTRY ACTION: D EN^VOHL7MSG(.XQORMSG)
Select PROTOCOL NAME:
Select OPTION: entER OR EDIT FILE ENTRIES
Input to what File: PROTOCOL// (4594 entries)
EDIT WHICH FIELD: ALL// ITEM
1 ITEM (multiple)
2 ITEM TEXT
CHOOSE 1-2: 1 ITEM (multiple)
EDIT WHICH ITEM SUB-FIELD: ALL//
THEN EDIT FIELD:
Select PROTOCOL NAME: OR EVSEND RA OE/RR => RAD/NM MESSAGE EVENT
Select ITEM: HMP NA EVENTS// VO OR HL7 MONITOR Sample Order HL7 Messages R
eceiver
MNEMONIC: ^
Select ITEM: ^
Select PROTOCOL NAME: OR EVSEND LRCH OE/RR => LAB MESSAGE EVENT
Select ITEM: HMP NA EVENTS// VO OR HL7 MONITOR Sample Order HL7 Messages R
eceiver
MNEMONIC: ^
Select ITEM: ^
Select PROTOCOL NAME: OR EVSEND FH OE/RR => DIET MESSAGE EVENT
Select ITEM: HMP NA EVENTS// VO OR HL7 MONITOR Sample Order HL7 Messages R
eceiver
MNEMONIC: ^
Select ITEM: ^
Select PROTOCOL NAME: OR EVSEND PS OE/RR => PHARMACY MESSAGE EVENT
Select ITEM: PSIVARH PHARMACY// VO OR HL7 MONITOR Sample Order HL7 Message
s Receiver
MNEMONIC: ^
Select ITEM: ^
Select PROTOCOL NAME: OR EVSEND GMRC OE/RR => CONSULTS MESSAGE EVENT
Select ITEM: HMP NA EVENTS// VO OR HL7 MONITOR Sample Order HL7 Messages R
eceiver
MNEMONIC: ^
Select ITEM: ^
Select PROTOCOL NAME:
After all of this, now whenever you place Radiology, Lab Chemistry, Dietetics, any Pharmacy, and Consults in CPRS, you will get HL7 messages showing up in Mirth. This is nice as it happens almost instantly after placing an order in CPRS. Here are the HL7 messages I got.
Lab Order:
MSH^~|\&^VIST-AC^^ACME^^20190416160925-0400^^ORM~O01^505447^D^2.3^^^AL^NE^KOR
PID^^^14^^田中,誠,
PV1^^O^3^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ORC^NW^37;1~OR^^^^^~~~20190416~^^201904161609-0400^1^^1^^^20190416160925-0400^E~ELECTRONICALLY ENTERED~99ORN~~~
OBR^^^^~~~229~AMITRIPTYLINE~99LRT^^^^^^^1^^^^0X500;SERUM;SNM;1;BLOOD ;99LRS~~~72;SERUM;99LRX^^^^^^^^^^^^~~~~~R;9
Medication Order:
MSH^~|\&^VIST-AC^^ACME^^20190416161236-0400^^ORM~O01^505449^D^2.3^^^AL^NE^KOR
PID^^^5^^간,서준
PV1^^O^3^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ORC^NW^38;1~OR^^^^^325&MG&1&TABLET&325MG&2~QID~~~~R~~325MG~~^^201904161612-0400^1^^1^^^20190416161236-0400^E~ELECTRONICALLY ENTERED~99ORN~~~
RXO^~~~2~ACETAMINOPHEN TAB ~99PSP^^^^^^^^^1338.6642~~99NDF~2~~99PSD^360^^0^^^^D90
NTE^7^P^FOR PAIN
RXR^~~~1~ORAL~99PSR
ZRX^^E^N^W
ZSC^^^^^^^^
Consult:
MSH^~|\&^VIST-AC^^ACME^^20190416161418-0400^^ORM~O01^505451^D^2.3^^^AL^NE^KOR
PID^^^5^^간,서준
PV1^^O^3^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ORC^NW^39;1~OR^^^^^~~~20190417~~R^^201904161614-0400^1^^1^^^20190416161418-0400^E~ELECTRONICALLY ENTERED~99ORN~~~
OBR^^^^~~~46~DERMATOLOGY~99CON^^^^^^^^^^^^^^OC^63
ZSV^~~~46~DERMATOLOGY~99CON^
OBX^1^TX^2000.02~REASON FOR REQUEST~AS4^^test2
OBX^2^TX^~PROVISIONAL DIAGNOSIS~^^test
Radiology Order:
MSH^~|\&^VIST-AC^^ACME^^20190416161529-0400^^ORM~O01^505453^D^2.3^^^AL^NE^KOR
PID^^^5^^간,서준
PV1^^O^3^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ORC^NW^40;1~OR^^^^^~~~20190417~~R^^201904161615-0400^1^^1^^^20190416161529-0400^E~ELECTRONICALLY ENTERED~99ORN~~~
OBR^^^^71020~~CPT4~58~CHEST 2 VIEWS PA&LAT~99RAP^^^^^^^^^^^^^^^^^^^^^^^^^^WALK^~test
OBX^1^TX^2000.02~CLINICAL HISTORY~AS4^1^test2
OBX^1^TX^2000.33~PREGNANT~AS4^^NO
Diet:
MSH^~|\&^VIST-AC^^ACME^^20190416161759-0400^^ORM~O01^505457^D^2.3^^^AL^NE^KOR
PID^^^2^^RUNNER,ROAD
PV1^^I^4^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ORC^NW^41;1~OR^^^^^~~~201904161617-0400~^^201904161617-0400^1^^1^^^20190416161759-0400^E~ELECTRONICALLY ENTERED~99ORN~~~
ODS^D^^~~~FH-5~NPO~99OTH^
To be written at a future date.