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