HL7 Transform (v2)
The HL7 Transform device is used to modify HL7 messages. This device is referred to as v2 as it supersedes an obsoleted version which only exists for backwards compatibility. All references to the HL7 Transform Device in this documentation refer to v2.
This device has a lot of built-in functionality. It includes:
An expression language for working with HL7.
Map/Lookup tables.
Custom code integration.
Message and segment filtering.
Change preview
This device is built on the concept of Transforms, where there are different types of transforms which can be sequenced together for a desired output. Each transform has a user-defined Condition which determines whether the transform will run. Transform types are:
Expression Transform: Uses the expression language to set the value of an HL7 path.Table Mapping Transform: Reads one or more values from the message and searches a table for a match.Message Filter Transform: Filters a messages if the specified condition is matched.Segment Filter Transform: Specifies segments to be included or excluded in a message.Skip Transforms: A short-circuit which skips any remaining transforms when a condition is met.Custom Function Transform: A transform which calls a user-defined function (C#).
The device UI displays a table of transforms, where each row represents a transform. Each transform has a target HL7 path, a transform UI, a condition, and a description.
The columns are:
Enabled: Unchecked transforms are skipped.
Path: The target path which will be updated, if applicable. Some transforms don’t set a path value (for example, the Segment Filter Transform), and in these cases the target path is hidden.
Transform: The UI for the specified transform. Each transform type has a unique UI.
Condition: The UI for the specified condition. Each condition type has a unique UI.
Description: Auto-generated when the user doesn’t provide a custom description.
Getting Started
This device provides a preview of changes by showing inbound and outbound messages below the Transforms grid. These viewers highlight changes as you configure your device, and are useful to see changes as you build up your collection of transforms.
You can paste an HL7 message into the In viewer, however, the recommended practice is to populate an upstream queue with a few messages - either by ingesting messages with the queue paused, or, manually queuing messages directly into the queue.
Navigate to the queue, and select a message. This message should now be shown in the HL7 Transform device.
Expression Transform
The Expression Transform uses an HL7 expression language to let you easily update an HL7 path based on other fields within the message (as well as user input). There are a number of functions available for simple manipulation.
First, add a new Expression Transform.
After specifying a Path (we’ve chosen MSH-3 below), you should see the message viewers show the updated ‘Change_Me’ value.
The expression language is quite flexible, and can show values from other fields. For example, you can use + to combine multiple expressions. In the first transform below, we’re updating the value of MSH-3 to ‘Change_Me_’ concatenated with the value from MSH-4. The second transform uses the Left function to change the value of MSH-6 to a truncated version of MSH-7. You can click the button to view a list of string and math functions available for expressions.
Expressions are also supported in the Condition column. If you only wish to run your transform under specific conditions, enter an expression which evaluates to true or false. For example, if you only want to execute your transform for ADT^A08 messages, you could add the following expression (note the default expression is Always).
Note that some expression functions only apply to the condition column (the In function, for example).
Show the expression functions / intellisense using ctrl + space. Hit Enter to inject the currently selected function.
Map Tables
Map tables are designed to provide a replacement value based on one or more inputs. Map Tables can have an arbitrary number of columns, with simple of compound indexes for quick lookup. They are not designed to hold very large tables (>100k rows), use a database for large tables.
Global map tables can be used by the HL7 Transform device. Simply define your map table in the Global UI and then select it from the table drop-down menu.
Local Map tables must be configured by clicking the Tables button in the header.
Typically, your table content will come from another source such as Excel. In this case, the easiest way to import the data is via the clipboard. Simply copy from the source, right-click the top-left empty cell, and choose the paste option. The correct number of columns will be automatically created during the paste operation.
Notice the first row of the table contains the column names. You can right click the first row and select the 'Use Current Row As Header'.
The next step is to define your indexes. In addition to improving performance, indexes ensure that value(s) you are searching for within the table are unique. If you wish to search on multiple different columns, you can define multiple indexes. Additionally, if you are searching on multiple columns, you can define 'compound' indexes. For example, in the table above, you may wish to search on 'Modality' and 'Body Part' together and return the corresponding LOINC code ("find the LOINC code where Modality = 'ct' and Body Part = 'brain'"). In this case we must define a compound index which includes both the Modality column and Body Part column. To do this we click on the key icon in the Body Part column and select the compound index option:
Compound indexes show a different key icon in the column header.
The inverse of looking up LOINC codes based on Modality and Body Part would be to look up either Modality or Body Part based on the LOINC Code. In this case we would want to add a single index the LOINC column. To do this, click the key icon in the LOINC Code column header, and select the first option.
Now the key icon in the LOINC Code column indicates a single index.
Using the gear icon displayed in the header of each column (or the corresponding column context menu), you can also modify the structure of the table:
Remove an existing column
Append a new column
Rename an existing column
Once you have configured your map table, you can reference it within the HL7 transform device. Start by adding a new map table transform:
The map table transform UI lets you configure the index, source paths, and output column. You can select a different index by clicking on the red text:
Map Tables are also accessible in custom code (as a data table object). This allows you to perform your own complex queries and package them as Custom Function Transforms:
using System;
using System.Linq;
using System.Text;
using System.IO;
using System.Collections.Generic;
using Connexion.Core;
using Connexion.Core.HL7;
using Connexion.Share;
namespace Connexion.Device
{
// ** This is pseudo-code **
[ClientRunnable(true)]
public partial class CustomFunctions : BaseHL7CustomFunctions
{
public void TableLookup(IMessageContext context)
{
MapTable mt;
if(!MapTablesByName.TryGetValue("New MapTable", out mt))
throw new Exception("Maptable 'New MapTable' not found");
var message = context.GetAsHL7Message();
// put the values to search for in a dictionary where the key is
// the column name and the value is the value to search for.
// In production, cache the dictionary and just change the values.
var lookup = new Dictionary<string, string>();
lookup.Add("Modality", message["MSH-9"]);
lookup.Add("Body Part", message["MSH-10"]);
// set the value of MSH-11 to whatever the map table lookup returned
message["MSH-11"] = mt.Lookup(lookup, "Loinc Part Code");
}
public void DatatableLookup(IMessageContext context)
{
MapTable mt;
if(!MapTablesByName.TryGetValue("New MapTable", out mt))
throw new Exception("Maptable 'New MapTable' not found");
var message = context.GetAsHL7Message();
// SQL syntax - careful this can be slow
// https://msdn.microsoft.com/en-us/library/system.data.datacolumn.expression.aspx
var datatable = mt.DataTable;
var result = datatable.Select($"Modality = '{message["MSH-9"]}' AND [Body Part] Like '{message["MSH-10"]}%'");
var row = result.FirstOrDefault();
if(row == null)
{
message["MSH-11"] = "Not Matched";
}
else
{
message["MSH-11"] = row["LOINC Part Code"].ToString();
}
}
}
}Message Filter Transform
The Message Filter transform is designed to move messages to the Filter queue based on the transform condition. For example, if you only support a specific message type, then all other message types should be filtered from the processing pipeline.
The first parameter specifies whether the message should be moved to the Error queue (true) or Filter queue (false). The second parameter is a string which will be added to the processing history to indicate the reason for filtering or erroring the message.
Segment Filter Transform
The Segment Filter transform is designed to either exclude specific segments from the message, or, include only the specified segments in the message. The first parameter determines the behavior (include / exclude) and the second parameter is a comma-delimited string containing the segment names.
Skip Transform
The Skip transform is designed to skip any remaining transforms (rows below) if the specified condition is met.
Custom Function Transform
The Custom Function transforms are c# functions written using the Custom Code Control. Visit the custom code control page for a detailed look.
Custom Function transforms are C# functions with specific signatures. These functions are displayed on the transform grid and can accept many different types of user input (for example, HL7 paths). These functions have access to the power and flexibility of the custom code control, and therefore can cover almost any scenario.
Within the transform grid, add a Custom Function transform. The new transform is displayed highlighted, waiting for you to choose a function.
By default, some sample/boilerplate functions are available.
When you select a function, the UI will display input fields for each function parameter (if there are any).
The first parameter of every function must be IMessageContext messageContext.
To create a function, navigate to the custom code control in the Code tab.
Functions have three allowed signatures:
public string FunctionName(IMessageContext context, …more parameters)
The above signature returns a string, and will set whatever path is set in the path column to the returned value.
public void FunctionName(IMessageContext context, …more parameters)
The above signature has no return value, and must act directly on the message (which is held on the context.Message property).
public bool FunctionName(IMessageContext context, …more parameters)
The above signature will be available for use in the Condition column.
Functions can optionally specify attributes for [FunctionCategory], [FunctionDescription], and [ParameterDescription]. These are all used to build the Select Method menu displayed to users.
Condition Types
In the examples above, we’ve used Expressions for each condition. You can also use Map Tables and Functions as conditions. Click the cog icon to see other condition types.
InTable: Does the specified value exist in a specific map table column (must be indexed).
NotInTable: The inverse of InTable - returns true when the value is not found.
Function: A custom function that returns true/false.
Transform Step-through
This device supports ‘stepping’ through each transform to visualize how each transform alters the source message. To enable step-through, press F10, or click the step-through button.
The device will highlight each enabled transform, starting at the top, and display the changes in the outbound message window. Press F10 or the step-through button to step to the next transform. Once all transforms have been stepped through, this mode will automatically disabled (or click the stop step-through button).