SocketReader for HL7 and MLLP -- Version 1.2.4
http://sourceforge.net/projects/hl7socketreader/
License: GPLv3.0 -- see gpl-3.0.txt which is included in this archive.
Copyright: Sebastian Weigmann (s_weigmann[at]web.de), All Rights Reserved.
This project uses python-hl7 by John Paulett. More info is available at:
http://johnpaulett.com/2009/01/10/parsing-hl7-with-python/
You can help to improve software quality by submitting bug reports at:
http://sourceforge.net/tracker/?group_id=316605&atid=1331725
Please feel free to submit any other feedback to this site:
http://sourceforge.net/tracker/?group_id=316605&atid=2080710
Special Thanks:
Richard Erlichman and everybody else involved at AOD Software for their
great contribution and patience.
0. Contents:
1. About this software
2. Features
3. Usage
4. Config File Options
5. Deamonizing
6. DISCLAIMER
1. About this software:
Many health care software products have excellent features aiding doctors to
document patient examinations. Yet, many of them miss efficient
means to communicate their results. Many of these programs use protocols based
on file transfer for that purpose.
SocketReader may be placed in front of the medical software product,
serving as a "translator" which transforms socket streams into files
containing one message each and vice versa.
SocketReader is implemented in Python 2.7 to make development easy while
ensuring multi-platform compatibility. It is multi-threaded and supports
socket server and socket client modes. When operating it as socket
server, it is capable of maintaining multiple client connections, accepting
messages from each connection. Messages will be written out to places of your
choice on the local file system.
When transmitting HL7 2.x messages, SocketReader will send HL7-ACKs
to signal successful transmission. Pure ASCII text messages will be wrapped
in MLLP and SocketReader will send MLLP-ACKs upon successful transmission.
SocketReader is available for 32Bit and 64Bit OSes and was tested on
Debian Linux, Ubuntu Linux, Windows XP and Windows 7.
2. Features:
SocketReader is capable of sending/receiving messages via TCP/IP as well as
reading/writing messages from/to files on the local hard disk using dedicated
connectors for each task. SocketReader supports m:n routing between
the supported connectors which effectively enables parallel multi-channel
message transfer (see illustrations below).
This is a single-channel scenario using one connector on the input and
one on the output side:
INPUT internal Queue OUTPUT
socket-in Queue file-out
>>-------------------------XXX----------------------------->>
Multi-channel scenarios use input and output connectors. Incoming messages
from each input connector will be duplicated and then sent to every output
connector on the same queue. For any given queue, message streams on all
output connectors are the same. Multiple queues may be used in the same
SocketReader instance. See the three examples below:
INPUT Connector internal Queue OUTPUT Connector
file-in_1 Queue_1 file-out_1
>>-------------------------XXX----------------------------->>
file-in_2 XXX socket-out_1
>>-------------------------XXX----------------------------->>
socket-in_1 XXX
>>-------------------------XXX
socket-in_2 Queue_2 socket-out_2
>>-------------------------XXX----------------------------->>
XXX file-out_2
XXX----------------------------->>
file-in_3 Queue_3 socket-out_3
>>-------------------------XXX----------------------------->>
You may choose from this set of currently supported connectors:
Connector Type: Transmission technique: Direction:
file local file system in
file local file system out
socket server MLLP-wrapped socket stream with MLLP-ACK in
socket server MLLP-wrapped socket stream with MLLP-ACK out
socket client MLLP-wrapped socket stream with MLLP-ACK in
socket client MLLP-wrapped socket stream with MLLP-ACK out
socket server MLLP-wrapped socket stream with HL7-ACK in
socket server MLLP-wrapped socket stream with HL7-ACK out
socket client MLLP-wrapped socket stream with HL7-ACK in
socket client MLLP-wrapped socket stream with HL7-ACK out
3. Usage:
socketReader uses a config file to read its configuration from and a Unix-like
command line interface to provide the full path for it.
Command line arguments in square brackets are optional.
Synopsis:
socketReader [-h | --help] [--version] [--configfile CONFIGFILE]
and:
socketReaderService install | (start | debug) [--configfile CONFIGFILE] | stop | remove
NOTE: When using the socketReader binary, socketReader.py or socketReader.exe
and leaving the -c/--configfile option blank, the config file will be
expected to be named socketReader.cfg and located in the current
working directory.
NOTE: When starting socketReaderService the first time, you MUST provide the
full path name of your config file by using the -c/--configfile option.
Examples: --configfile /etc/socketReader.cfg
-c "c:/Program Files/socketReader/socketReader.cfg"
WARNING:
The SocketReaderService binaries, the config and log files, the queue
database files and the paths for incoming and outgoing messages-as-file
must not be located on a network drive when running the service. All
resources need to be available on locally attached drives.
Other configurations are likely to cause undefined behavior, most
probably data loss and denial of service conditions. The reason
is that Windows service processes run within another context than
processes started from within an open user session. Device mappings
might therefore not be available to the services.
--version
Show program's version number and exit.
-h or --help
Show a help message and exit.
4. Config File Options:
The socketReader config file layout is organized in sections. In the
socketReader config file, you will need to define multiple sections along with
their options. Each section name defines a unique connection. Connections are
configured by the options stated within their section. Sections are stated in
square brackets, for example: [connection-name].
NOTE: Please see the example.cfg config file for templates.
4.1. The [general] section:
The [general] section has a special meaning. Options defined here apply
globally.
These options are allowed within the [general] section:
logfile = FILE (defaults to the empty string)
By using this option, you enable logging to FILE. FILE must contain an
existing path and the log file name itself
Example: logfile = "d:/logs/logfile.log" where d:/logs/ is an existing
directory.
The log file format is: time stamp - thread name - log level - log message
loglvl = critical | error | warning (default) | prod | info | debug
Here you may optionally define a log level. These log levels are available,
sorted from high to low priority:
critical, error, warning, prod, info, debug.
For example, if you define info, all log levels but debug will be printed.
If you want to see all messages with their contents in your log files, you
need to define debug. The default log level warning will print errors and
critical log entries only to allow for small log files. Log level prod
adds to the warning level log entries indicating unsuccessful message
transfer. Also, the full content of these messages will be printed.
useconnections = CONNECTION
There can be many connections defined in the config file. Here you state the
ones you actually want to use. You need to give a comma separated list of
connection names (section names without square brackets) here.
Example: useconnections = socket-in_2, socket-out_2, file-out_2
4.2. Defining connections:
All other sections differ in their supported options according to the connector
type you define there. There is a subset of options common to all connections
which allows to define the connector type and the queue a connection will use.
These options are:
transtech = file | socket
Here you define which technology will be used for data transmission. At this
time, socketReader supports file and socket connectors. Each of them needs to
be further configured stating additional options.
transdir = in | out
Direction of data transfer. A connection may be an incoming (in) or an
outgoing (out) connection. Bidirectional data transfer within a single
connection is not yet supported.
usehl7 = True | False (default)
SocketReader always acknowledges the messages it receives via a socket
connection. Your messages may be of any kind, for example base64 coded binary
files. If your messages are non-hl7, you must not specify this option.
socketReader will send MLLP acknowledgements. If your messages are hl7 and
your communications server needs to see hl7 acknowledgements for each of its
messages, then you must state this option. SocketReader will then answer
using hl7 acknowledgements. The default behavior is to just send MLLP
acknowledgements.
Using this option on file type connections enables the use of the hl7prefix
option.
queuename = QUEUENAME
This option states the name of the queue the connection will use. This
binds connections to routes. At least one incoming and one outgoing
connection needs to state the same queue name here in order to form a
functional route.
4.2.1. Socket connector options:
queuedb = PATHNAME
Give a full path and filename here. The connection will store undelivered
queue contents there when it is asked to shut down. You MUST use slashes
"/" as separators within the path name, even if you use Windows!
NOTE: This option applies to outgoing socket connections only.
useack = True (default) | False
Emit / expect acknowledgment messages or not. If set to True, HL7 / MLLP ack
messages will be generated or expected. If set to False, no acks are sent or
expected.
createserversocket = True (default) | False
Create a TCP/IP server socket which is the default behavior or create a
TCP/IP client socket otherwise. Note that in the latter case you must also
specify the ipaddr option!
ipaddr = ADDR (defaults to 127.0.0.1)
If you start a socket server, this option will bind your socket to the
IP address or hostname you specified. If you start a socket client, it
will try to connect to the remote IP provided by ipaddr.
NOTE: the argument ADDR may be one of the following:
- a fully qualified domain name. Example: commserver.yoursite.com
- a host name your machine can resolve. Example: commserver
- an IP address. Example: 192.168.3.154
port = PORT (defaults to 22222)
When establishing a socket, you need to specify a port number for it to
operate on (range: 1024..65535). Just replace PORT with an integer of the
given range. If omitted, port 22222 will be used.
sob = chr(ASCIICODE), eod = chr(ASCIICODE) and eob = chr(ASCIICODE)
The MLLP protocol is used as an envelope to your message while it is being
transferred via socket. These options default to the standard MLLP delimiter
characters which are 0x0b for SOB, 0x1c for EOD and 0x0d for EOB.
You may redefine these delimiters if you need to. You must at all costs avoid
defining delimiters for SOB and EOB which may occur within your messages.
Otherwise socketReader will not be able to tell your messages apart from each
other.
NOTE: To define non-printable characters, you need to use the Python chr()
function. You may define sob = chr(0x0b) or sob = chr(11) which is
equivalent to each other. You may also define strings as delimiters,
for example: sob = "START-BLOCK", eod = "END-DATA", eob = "END-BLOCK".
4.2.2. File connector options:
Incoming and outgoing file connections have very different configuration needs.
These will be explained in the two paragraphs 4.2.2.1. and 4.2.2.2. The
common options are explained below.
messagedir = PATH
State the full path to the place socketReader shall write to / read from.
Windows users be aware that you do MUST use slashes ("/") instead of
backslashes ("\") in your path names. Also, it is strongly recommended to
enclose your path name in quotes. This option is always mandatory and does
not default to anything useful. If it is omitted, socketReader will refuse
to start.
newline = True | False (default)
Some subsystems may not digest messages read from files if the message is
tailed by a newline character. Other subsystems do expect a tailing newline
character. This option tells socketReader to add a newline at the end of a
message when writing it to a file, respectively removing it if messages are
read from files. If this option is not given, no newline character - the
plain message only - will be written to file / will be expected at the end
of a file.
4.2.2.1. Options for incoming file connections:
filemask = FILEMASK
Specify a file naming pattern to search for in the path provided by
messagedir. You may use the same syntax as you would on a operating system's
console. This option defaults to * (all files in the directory).
NOTE: File name sorting does NOT use natural sorting! If you have a list of
files "f_1.dat, f_2.dat, f_10.dat", they will be read in the following
order: f_1.dat, f_10.dat, f_2.dat. This might lead to an out-of-order
import of messages. To avoid this, you will need to ensure files are
named in an unambiguous way. For example, this list of files would be
imported in the order they are stated: "f_01.dat, f_02.dat, f_10.dat".
NOTE: Files will be deleted (not moved) after they have been read! If you
want them to be moved to another directory, you need to add a separate
file output connection to the same queue.
Examples: filemask = tokis_*_.hl7
filemask = *.dat
cycletime = SECONDS
Time interval for searches for new files. The default value is 120.
maxfiles = MAXCOUNT
When finding a set of files in messagedir, do at most read maxfiles files.
This prevents socketReader from getting stuck when it is set on a directory
containing very many files. You may specify any number between 1 and 1000.
The default value is 50.
4.2.2.2. Options for outgoing file connections:
hl7prefix = True | False (default)
Each HL7 message carries its own type identifier within field MSH-9. You can
use this identifier as part of a prefix for your output file names and thus
for example easily distinguish observation results from patient data by
filtering for file names containing "ORU" or "ADT". Additionally, the
contents of MSH-3 to MSH-6 are used, so you can collect messages of the same
type from multiple sources and for multiple target applications running on
your machine without having them mixed together indistinguishable. The
output files will look like:
SRCSYSTEM_SRCFACILITY_DSTSYSTEM_DSTFACILITY_MSGTYPE_timestamp_iterator.suffix
for example:
ORBIS_UKD_OPUSL_UKD_ADT_20101102124510_00000.hl7
Apparently, this works for HL7 messages only, so the option usehl7 must be
set to True, too.
prefix = PRE
Specify a static string to precede the dynamically generated part of the
message file names. The options hl7prefix and prefix are mutually exclusive.
The default prefix is the empty string ("").
suffix = SUF
Here you may specify suffixes for the message files written to your output
directory. The default suffix is .dat.
4.3. Debug options:
This sort of options exists to help integrate socketReader into your networks.
Every connector type has its own valid set of debug options. All of them are
given on a single line within the respective sections. For flexibility reasons,
these options need to be given in a Python dictionary syntax:
debug = {"option1":"value", "option2":"value", "option3":False, "option4":True}
If not given, all debug options default to be empty and will have no effect on
existing configurations.
Please refer to the examples.cfg for a full summary of available options.
5. Deamonizing:
For sure, the user would like to run this application as a Linux daemon /
Windows service. This section will explain how to deamonize this application.
5.1. Daemon for Linux:
Three goals are to achieve here:
(1) Start the tool and keep it running while its spawning process dies.
(2) Log any output.
(3) Stop the process so it won't loose any data (messages not written
to disk yet).
5.1.1. Starting a daemon:
To start socketReader in daemon mode, type:
nohup /path/to/socketReader --configfile /path/to/SR.cfg >> ./stdout.log &
The first part of the command line (nohup) will ensure the application will
keep running even if the user launching it logs off. The second part starts
socketReader in your favorite mode. The third part (>> ./stdout.log) dumps
all console output to the file stdout.log in the directory you were when you
started socketReader. You may want to state another directory here (just
replace the "." with your favorite path). The last part (the &) will send the
whole command to the current shell process' background (which means you can
start typing other commands right after pressing Enter). Now you have a
running daemon instance of socketReader.
5.1.2. Stopping a daemon:
To stop all deamonized socketReader processes at once, type:
killall socketReader
This will send a SIGTERM to all open instances of socketReader. The application
will then write out all pending messages and then exit cleanly. To kill a
single instance of socketReader, you need to know its process id (pid) and
issue "kill pid".
To forcibly shutdown all instances of socketReader, type:
killall -9 socketReader
NOTE: socketReader will be forced to exit immediately! You may loose messages
socketReader has already accepted via socket but not yet delivered.
To forcibly shutdown a single instance, type "kill -9 pid".
You may want to wrap the whole procedure into init or upstart scripts.
This task is beyond the scope of this document and left to you.
5.2. Service for Windows:
The Windows service API imposes several extra constraints on an application,
especially on one which is designed to be written once and run anywhere. I had
to write some extra stuff resulting in a special Windows Service Release of
socketReader. You will find a file "socketReaderService.exe" bundled with every
Windows release which you must use to set up the socketReader service.
5.2.1. Setting up a Windows Service:
First, you need to find out how you want to run socketReader, i.e. which
options to configure in the config file. Use the standard "socketReader.exe"
via Command Shell for that task. Once you have figured out your favorite
settings, you are ready to install the service.
Step 1:
Place the "socketReaderService.exe" and all resources it needs on a locally
attached drive, for example here:
C:\Program Files\socketReader\socketReaderService.exe
NOTE: Any files and paths which are meant to be accessed by a Windows
service must NOT reside on a mapped drive (network drive)!
Step 2:
Open a Command Shell and dive into your socketReader directory.
Step 3:
Type the following to install the service:
socketReaderService.exe install
NOTE: You might want to install your service with special service arguments.
You can get a list of available options by just typing:
socketReaderService.exe
NOTE: You can remove "socketReaderService.exe" from the list of Windows
services by typing:
socketReaderService.exe remove
NOTE: You do not configure the socketReader parameters here! Not yet... :)
Step 4:
Test your newly created service by running it in debug mode with your
config file as parameter. Let's assume your config file is located at
C:\Program Files\SR\SR.cfg
You need to type:
socketReaderService.exe debug --configfile "C:\Program Files\SR\SR.cfg"
NOTE: You will see the same console output as you are used to when running
"socketReader.exe". Pressing Crtl+C will shut down the windows service.
NOTE: The parameters you just used are now saved to the windows registry, so
"socketReaderService.exe" will automatically use them from now on. In
other words, issuing another "socketReaderService.exe debug" will use
the arguments it now can fetch from the registry.
If you need to point socketReader to another config file, just do
Step 4 again.
NOTE: Changing the socketReader configuration is as easy as editing the
config file in use by socketReader and then restarting the service.
Your new settings will be activated upon service restart.
Step 5:
Test the socketReader service in productive mode. Type:
socketReaderService.exe start
You will get a notification that the service has been started. Search the
processes tab from your task manager for processes called
"socketReaderService.exe". Now stop the service by typing:
socketReaderService.exe stop
All "socketReaderService.exe" processes should be gone now and a message
issued to the console window that the service has been shut down.
Congratulations! You successfully installed socketReader as a Windows
service.
NOTE: Stuff written to the Windows registry can be fund here:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\hl7socketreader_configfile
5.2.2. Starting the Service:
You can now configure and start your service from the services.msc plugin or
by issuing "sc start hl7socketreader_configfile" or
"net start hl7socketreader_configfile" from the console. After you started
the service, you should persistently see one or more instances of
"socketReaderService.exe" in your task manager's processes tab.
5.2.3. Stopping the Service:
Issue a "net stop hl7socketreader_configfile" or
"sc stop hl7socketreader_configfile" or stop it from services.msc.
You should see no instances of "socketReaderService.exe" anymore.
5.2.4. Removing the Service:
From the command line window, issue the following command:
path\to\sc.exe delete hl7socketreader_configfile
or dive into the socketReader directory and type:
socketReaderService.exe remove
This will delete the registry entries associated with this service but leave
your binary files on your hard drive unattended. You can still launch
"socketReader.exe" manually from the command line or reinstall the service at
any time.
6. DISCLAIMER:
THE SOFTWARE IS DELIVERED FREE OF CHARGE AND 'AS IS' WITHOUT WARRANTY OF ANY
KIND. THE ENTIRE RISK AS TO THE RESULTS AND PERFORMANCE OF THE SOFTWARE IS
ASSUMED BY YOU / THE USER. THE AUTHORS SHALL NOT BE LIABLE FOR LOSS OF DATA,
LOSS OF PRODUCTION, LOSS OF PROFIT, LOSS OF USE, LOSS OF HEALTH OR LIFE, LOSS
OF CONTRACTS OR FOR ANY OTHER CONSEQUENTIAL, ECONOMIC OR INDIRECT LOSS
WHATSOEVER IN RESPECT OF DELIVERY, USE OR DISPOSITION OF THE SOFTWARE.
SocketReader for MLLP and HL7 Files
Brought to you by:
jackkane