Unzip .zip files in mirth

Create a source as File Reader. put the zip file directory to read and make sure you enable read as binary radio button. as shown below

unzip files

Now, the logic is that the .zip file will be read via mirth engine and the data inside the zip file will be consumed as base 64 based content and written in to the output folder.

Provide the below code in the transformer editor.

// path at which the ZIP files has to be unzipped
var destinationPath = “C:\\Projects\\NON-EAI-PROJECTS\\tmp”;
var buffer_value = 1024;

// Convert incoming data to a base64 encoded data
var strBase64Data = connectorMessage.getRawData();
var decodedBytes = FileUtil.decode(strBase64Data);

// process all zipped files
var is = new java.io.ByteArrayInputStream(decodedBytes);
var zis = new java.util.zip.ZipInputStream(is);

var entry;
while((entry = zis.getNextEntry()) != null) {

// save file
var count;
var buffer = java.lang.reflect.Array.newInstance(java.lang.Byte .TYPE, buffer_value);

var fileOut = new java.io.File(destinationPath + “\\” + entry.getName());
var fos = new java.io.FileOutputStream(fileOut);

// read byte content from zipped file
while ((count = zis.read(buffer, 0, buffer_value)) != -1) {
fos.write(buffer, 0, count);
}

fos.close();
}
zis.close();

Advertisements

Fetching Data From APIGEE and pushing into Mirth Via RabbitMQ

This will be a interesting blog post. In this post I will explain the following:

  1. Fetching the data from APIGEE (URL re-routing) in JAVA
  2. Pushing the fetched data from APIGEE and inserting to RabbitMQ queue.
  3. Pull the data from Rabbit MQ with Mirthconnect.

1. CREATE APIGEE URL:

APIGEE is available for both enterprise and normal non-payable version. You can sign into Apigee. There are various purposes and use-cases available to use Apigee but i’m currently using this Apigee as a URL re-routing mechanism.

Take any source of JSON data which can be available free. Sign in to APIGEE and click on API Proxies. In the opening page click on +PROXY button on the right hand side top. This will open a new page with the following information

apigee-1

Once the Above screen appears select the first option “REVERSE PROXY” this will act as a URL re-routing mechanism. You will have a actual URL but you will not use that URL for communicating with the clients instead, you will give one URL which which will be mapped to the original URL.

Click next on selecting the first option. Then you will see the below screen as shown:

apigee-2

In the above screen on Proxy Name you have to fill out the name that you wish to give as a proxy name in the first text box in my case I have provided (vibinces-eval-test). In Proxy Base Path  you need to provide a sub-context of your API name I have provided (apigeejsonprofile). In the Existing API you need to provide the full URL path of existing JSON API. Description is optional field, you can either provide it or not.

Once it is created my url looked like this http://vibinces-eval-test.apigee.net/apigeejsonprofile you might get a URL as well with the name of your choice. In the Security  tab, It is advised to select CORS headers on browse. Because it is always possible to get the cross-origin error when you try to access data from browsers which are not verified properly. Im also using no authorization for the API.

apigee-3

In the Next tab you can see how the data provided is converted to your URL. It is also fascinating that APIGEE provides you two types of URL that can be used both in Testing or BETA and as well as one for PROD.

apigee-4

Now your URL re-router is created. That is if  you hit the URL http://vibinces-eval-test.apigee.net/apigeejsonprofile you can see the JSON data that actually belongs to some other URL.

2. JAVA CODE – TO FETCH DATA FROM APIGEE:

I’m going to create the below two classes:

  1. FetchJsonFromApigee.java
  2. PushApigeeDataToRabbitMQ.java

1. FetchJsonFromApigee.java:

import java.io.BufferedReader;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class FetchJsonFromApigee {
public static String call_me() throws Exception {
String url = “http://vibinces-eval-test.apigee.net/apigeejsonprofile”;
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod(“GET”);
con.setRequestProperty(“User-Agent”, “Mozilla/5.0”);
int responseCode = con.getResponseCode();
System.out.println(“Response Code : ” + responseCode);
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in .readLine()) != null) {
response.append(inputLine);
} in .close();
System.out.println(“response : ” + response.toString());
return response.toString();
}
public String sendingMessage() throws Exception {
String pushedJsonMessage = FetchJsonFromApigee.call_me();
return pushedJsonMessage;
}
}

2. PushApigeeDataToRabbitMQ.java:

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

/**
* @author Vibinchander.V
*
*/
public class PushApigeeDataToRabbitMQ {
private final static String QUEUE_NAME = “TestQueuing”;

public static void passMessage(String message) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(“localhost”);
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
channel.basicPublish(“”, QUEUE_NAME, null, message.getBytes());
System.out.println(” [x] Sent ‘” + message + “‘”);
channel.close();
connection.close();
}

public static void main(String[] args) throws IOException, TimeoutException {
FetchJsonFromApigee getData = new FetchJsonFromApigee();
String passMessage = null;
try {
passMessage = getData.sendingMessage();
} catch (Exception e) {
e.printStackTrace();
}
PushApigeeDataToRabbitMQ.passMessage(passMessage);
System.out.println(“Executed Main Method !!!”);
}
}

When you run the first class you can see that the data is fetched from APIGEE and pushed into RabbitMQ message queue.

3. Write a JAR file that will pull data from RabbitMQ:

I’m writing the below class. this class will be used inside the Mirth tool which will act as a consumer to pull the data out of RabbitMQ.

import java.io.IOException;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.QueueingConsumer;
/**
 *
@author Vibinchander.V
 *
 */
public class QueueConsumer {
 public String returnMessage(String queueName) throws IOException, TimeoutException {
  ConnectionFactory factory = new ConnectionFactory(); 
factory.setHost(“localhost”); 
Connection connection = factory.newConnection(); 
Channel channel = connection.createChannel(); 
channel.queueDeclare(queueName, false, false, false, null); 
boolean noAck = false; 
@SuppressWarnings(“deprecation”) 
QueueingConsumer consumerVal = new QueueingConsumer(channel);  channel.basicConsume(queueName, noAck, consumerVal); 
boolean runInfinite = true; 
QueueingConsumer.Delivery delivery = null; 
//while (runInfinite) {  //QueueingConsumer.Delivery delivery; 

try {   
delivery = consumerVal.nextDelivery(); 
} catch (InterruptedException ie)
{   // continue; 
}  //System.out.println(“Message received” + new String(delivery.getBody()));  channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false); 
//} 
channel.close(); 
connection.close();
  return new String(delivery.getBody()); 
}
}

Make the above program in a JAR file put this inside the custom lib folder of Mirth. you might have to include other JAR files as well. Inside the Mirth source connector Javascript reader write the below code:

var queueConsumer = new org.envision.queuing.QueueConsumer();
msg = queueConsumer.returnMessage(“TestQueuing”);
logger.debug(msg);
return msg;

When you run the first FetchDataFromApigee.java you can see that data will be fetched from Apigee and pushed to RabbitMQ queue and immediately pulled by Mirth consumer.

Happy Integrations!!!!!

 

Parse responses in response tab

Imagine that we are sending certain data to external sources and we are getting response from those source. Now the received response has to be parsed inside the response tab.

APPROACH 1:

Imagine we are receiving an HTML response like this as provided below:

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”&gt;
<html xmlns=”http://www.w3.org/1999/xhtml”&gt;
<head>
<meta http-equiv=”Content-Type” content=”text/html; charset=iso-8859-1″/>
<title>401 – Unauthorized: Access is denied due to invalid credentials.</title>
<style type=”text/css”>
<!–
body{margin:0;font-size:.7em;font-family:Verdana, Arial, Helvetica, sans-serif;background:#EEEEEE;}
fieldset{padding:0 15px 10px 15px;}
h1{font-size:2.4em;margin:0;color:#FFF;}
h2{font-size:1.7em;margin:0;color:#CC0000;}
h3{font-size:1.2em;margin:10px 0 0 0;color:#000000;}
#header{width:96%;margin:0 0 0 0;padding:6px 2% 6px 2%;font-family:”trebuchet MS”, Verdana, sans-serif;color:#FFF;
background-color:#555555;}
#content{margin:0 0 0 2%;position:relative;}
.content-container{background:#FFF;width:96%;margin-top:8px;padding:10px;position:relative;}
–>
</style>
</head>
<body>




401 – Unauthorized: Access is denied due to invalid credentials.


You do not have permission to view this directory or page using the credentials that you supplied.



</div>
</body>
</html>

We need to get the status that we are receiving on this and make business logic accordingly. For example,  here on this HTML response we are supposed to parse the status value 401, and if it is 401 then we need to perform actions accordingly.

This can be achieved by using external library called JSOUP that is used for parsing HTML via JAVA. download the jsoup jar library and then test it in the JAVA first using any version of eclipse. Find the JAVA code below:

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;

public class TestHtml {
public void htmlDataReader() {
String htmlData =””; // your html string
Document doc = Jsoup.parse(htmlData);
Document doc = Jsoup.parse(htmlData);
Elements tds = doc.getElementsByTag(“h2”);
String text = tds.text();
System.out.println(“Data : “+ text.substring(0,3));
}
public static void main (String args[]) {
TestHtml val =  new TestHtml();
val.htmlDataReader();
}
}

Output : Data: 401

The corresponding Mirth code for the above JAVA code will be as follows:

importPackage(org.jsoup);
var doc = Jsoup.parse(msg);
var tds = doc.getElementsByTag(“h2”);
var text = tds.text();
var responseStatusValue = text.substring(0,3);
if(responseStatusValue==’401′){
logger.debug(“Data : “+ text.substring(0,3));
// Business logic below of your choice
}

APPROACH 2:

If we are going to parse the HTML data through mirth itself without using any external library, then it is little challenging because the tag <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”> is expected to come in HTML, but ex4 will not be considering this element as a XML tag. So mirth will not take this data as a XML and allow us to parse the message as normal XML.

Make your source listener as RAW instead of XML then only it will be consumed inside Mirth. In the pre-processor of the mirth write the following code:

var pattern = $gc(‘pattern’);
if (!pattern) {
pattern = java.util.regex.Pattern.compile(‘^(\\s*(<\\?xml.*\\?>)?\\s*(<!DOCTYPE[^\\[>]*(\\[\\s*(<![^>]*>\\s*)*\\])?[^>]*>)?\\s*)’);
$gc(‘pattern’, pattern);
}
var prolog = ”;
var matcher = pattern.matcher(message);
if (matcher.find()) {
prolog = matcher.group(1);
message = message.substring(0, matcher.start(1)) + message.substring(matcher.end(1));
}
$c(‘prolog’, prolog);
return message;

Inside the source transformer provide the following code. you can use ‘msg’ inside the source transformer since, those DOCTYPE! will be removed inside the pre-processor. Your pre-processor is the first place where your code reaches at first followed by other stages of mirth interface engine.

var responseData = msg[‘body’][‘div’][1][‘div’][‘fieldset’][‘h2’].toString();
var responseData = msg[‘body’][‘div’][1][‘div’][‘fieldset’][‘h2’].toString();
logger.debug(msg[‘body’][‘div’][1][‘div’][‘fieldset’][‘h2’].toString());
var responseDataStatus = responseData.substring(0,3);
if(responseDataStatus==’401′){
logger.debug(“RESPONSE DATA : “+responseDataStatus);
// Your business Logic
}

APPROACH 3:

You can also perform this activity in the summary of the channel. As shown in the picture below:

summary javascript

Click on the properties tab, and provide the following code. This code is basically the replace code that we will be using to remove the incoming namespace.

message.replace(‘<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”>&#8217;,”);
return message;

Similar to the scripts tab, we should be using message here instead of msg. From 3.4 version mirth has made this javascript properties tab available in the summary of the channel, which will be executed at first instead of the pre-processor.

Inside transformer we can use the code that we have used for Approach 2 directly. it will work fine.

APPROACH 4:

If we want the same to be achieved in response tab, then we cannot use approach 2 or approach 3. In that case we can use the inbuilt function $(‘responseStatusLine’) which will contain the corresponding HTTP responses such as 500,401 or 200.

We can call this function directly and use it in response transformer as provided below:

if($(‘responseStatusLine’).contains(‘401’)){
//Your business logic goes here…
}

Mirth – DB Reader

In the Mirth tool, often people face many issues when it comes to connecting to DB either fetching data or updating/inserting data. This blog post will help you understand what are functions used for what purposes.

Starting with the Database reader concept, there are multiple misconception that needs to be clarified. Not everything are understood as the name portrays, the tool has its own definition of most of the areas and buttons.

polling settings

The option Poll Once on Start means that the database channel will start to poll the database once it is deployed. In-case if NO is selected for this option then, the channel will start to fetch the data from the database in the specified interval of time.

batchprocess

The option Process Batch is used, when the data are coming in batches and there is a necessity to split the data into separate batches. You may wonder, how data will come in batch from the database?. Seems there is a option available to make such polling of data from the DB. In that case you will select YES for the Process Batch and the split the incoming. In usual case, the option NO has to be selected.

connectionstring

Please note that the URL tab for SQL Server will be different than other traditional relational databases. Instead of IP & port in the connection string we may need to use the SQL server name or the system name. When you assign the special or custom user (sa in this example) specific to certain database then your port number will not be the usual default SQL server database port (i.e 1443). To check the port number that you are accessing the data use the following query.

USE master
GO
xp_readerrorlog 0, 1, N’Server is listening on’
GO

Apart from this, the other major misconception is the usage of Javascript. This will be either YES or NO. But in either case we just have to remember one thing that, this will be the triggering action that will be performed on the database. Mirth will poll the database by using the query that we write in the below area.

sql javascript

You can either use this SQL javascript area just for triggering the database by using a query, and perform the logic inside the transformers. Else some people prefer to write logic in the connector area itself by pressing YES on the using Javascript radio button.

But, if you decide to write logic in the SQL javascript area in the source, you will have to face certain limitations. Like the following:

  1. The variable used inside the SQL javascript text area cannot be used in the next javascript text area (i.e  Post-processor javascript). So just in-case you have to update a data using the data fetched previously the SQL javascript area, it will not work well.
  2. You do not have the liberty of using channelMap variable inside the SQL javascript editor area, neither the globalMap. In both the cases it will throw error.
  3. Said that, the result set value will be returned to the source transformer which can be used to process the data of our interest.

Most, misconception of all is how can we fetch data (i.e process). Mirth will fetch data from the Database only in the one-by-one row manner. There is no such possibility available in mirth to fetch 10 or 15 or 100 rows in single stretch. It’s Mirth a tool, not hulk. 🙂

Hope it clarifies some questions.

Remove Segments from CCDA

Aim :

Let us consider that you want to remove certain segments from the incoming CCDA message structure, which your end client thinks that it is not needed for them to receive. How can we remove those specific segments from all  the incoming CCDA files?

Pre-requisite :

  • MirthConnect : 3.4.1.8057 (latest version) — Any previous versions also OK

Channel setup :

  1. Create a New channel name it as your wish Here I have named CCDA Mofification
  2. Click on Set Data Types button set the source connector inbound to XML
  3. Outbound and Destination 1 to XML
     – that’s it the channel setup is done for it.

Source Transformer :

  • Go to the source transformer and right click to create a new step
  • Name the step as Remove segment
  • Select the drop-down of  the step that you have created and select Javascript

 

let us assume that we don’t require the Vital signs segment from the incoming CCDA, though Vital signs segment is very important, I’m just using this to show demo in a very clear and easier format.

Write the following code in the transformer area:

var messageLength = msg[‘component’][‘structuredBody’][‘component’].length();
for(itr=0;itr<messageLength;itr++)
{
if(msg[‘component’][‘structuredBody’][‘component’][itr][‘section’][‘templateId’][‘@root’]==”2.16.840.1.113883.10.20.22.2.4.1″)
{
logger.info(“incoming Message : “+msg);
var removedSeg = msg[‘component’][‘structuredBody’][‘component’][itr][‘section’];
channelMap.put(“removedSegment”,removedSeg);
delete msg[‘component’][‘structuredBody’][‘component’][itr][‘section’];
logger.info(“outgoing Message : “+msg);
}

}

Consider that you provide the following sample data into mirth.

<ClinicalDocument xmlns=”urn:hl7-org:v3″ xmlns:cda=”urn:hl7-org:v3″ xmlns:sdtc=”urn:hl7-org:sdtc” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”&gt;
<realmCode code=”US”/>
<typeId extension=”POCD_HD000040″ root=”2.16.840.1.113883.1.3″/>
<templateId root=”2.16.840.1.113883.10.20.22.1.1″/>

………………………………………….
………………………………………….
………………………………………….
<component>
<section>
<templateId root=”2.16.840.1.113883.10.20.22.2.14″/>
<id extension=”Functional Status” root=”2.201″/>
<code code=”47420-5″ codeSystem=”2.16.840.1.113883.6.1″ codeSystemName=”LOINC” displayName=”FUNCTIONAL STATUS”/>
<title>Functional Status</title>
<text>
<table>
<thead>
<tr>
<th>Functional Condition</th>
<th>Effective Dates</th>
<th>Condition Status</th>
</tr>
</thead>
<tbody>
<tr>
<td/>
<td>null</td>
<td>completed</td>
</tr>
</tbody>
</table>
</text>
<entry typeCode=”DRIV”>
<observation classCode=”OBS” moodCode=”EVN”>
<templateId root=”2.16.840.1.113883.10.20.22.4.68″/>
<id root=”87347b49-c2ce-4e3e-9bf4-8fe09e9332db”/>
<code code=”” codeSystem=”2.16.840.1.113883.6.96″ displayName=”completed” xsi:type=”CE”/>
<statusCode code=”completed”/>
<effectiveTime>
<low value=”null”/>
<high value=”null”/>
</effectiveTime>
<value code=”” codeSystem=”2.16.840.1.113883.6.96″ displayName=”” xsi:type=”CD”/>
</observation>
</entry>
</section>
</component>
<component>
<section>
<templateId root=”2.16.840.1.113883.10.20.22.2.3.1″/>
<code code=”30954-2″ codeSystem=”2.16.840.1.113883.6.1″ codeSystemName=”LOINC” displayName=”RESULTS”/>
<title>Results</title>
<text>
<table>
<thead>
<tr>
<th>Test name</th>
<th>Date</th>
<th>Lab Name</th>
<th>Measure</th>
<th>Units</th>
<th>Reference Range</th>
<th>Lab Value</th>
</tr>
</thead>
<tbody>
<tr>
<td/>
<td>20141015</td>
<td>
<name>Century Hospital</name>
<name>Century Hospital</name>
<name>Century Hospital</name>
<name>Century Hospital</name>
</td>
<td>10^3/ul</td>
<td>10^3/ul</td>
<td> – </td>
<td>220.00</td>
</tr>
</tbody>
</table>
</text>
<entry>
<organizer classCode=”BATTERY” moodCode=”EVN”>
<templateId root=”2.16.840.1.113883.10.20.22.4.1″/>
<id root=”e673e7ec-effc-445c-8d4d-b723ad8327aa”/>
<code code=”43789009″ codeSystem=”2.16.840.1.113883.6.96″ codeSystemName=”SNOMED-CT” displayName=”CBC WO Differential” xsi:type=”CE”/>
<statusCode code=”completed”/>
<component>
<observation classCode=”OBS” moodCode=”EVN”>
<templateId root=”2.16.840.1.113883.10.20.22.4.2″/>
<id root=”c6a6491d-c11e-425a-ac72-028cb6624783″/>
<code code=”” codeSystem=”2.16.840.1.113883.6.1″ codeSystemName=”LOINC” displayName=””/>
<statusCode code=”completed”/>
<effectiveTime value=”20141015″/>
<value unit=”10^3/ul” value=”220.00″ xsi:type=””/>
<author>
<time value=””/>
<assignedAuthor>
<id root=”c6a6491d-c11e-425a-ac72-028cb6624783″/>
<representedOrganization>
<name>&lt;name xmlns=”urn:hl7-org:v3″&gt;Century Hospital&lt;/name&gt;&lt;name xmlns=”urn:hl7-org:v3″&gt;Century Hospital&lt;/name&gt;&lt;name xmlns=”urn:hl7-org:v3″&gt;Century Hospital&lt;/name&gt;&lt;name xmlns=”urn:hl7-org:v3″&gt;Century Hospital&lt;/name&gt;</name>
</representedOrganization>
</assignedAuthor>
</author>
<referenceRange>
<observationRange>
<value xsi:type=””>
<low unit=”” value=””/>
<high unit=”” value=””/>
</value>
</observationRange>
</referenceRange>
</observation>
</component>
</organizer>
</entry>
</section>
</component>
<component>
<section>
<templateId root=”2.16.840.1.113883.10.20.22.2.17″/>
<id extension=”SocialHistory” root=”17eae709-d72c-4b04-a75e-03facbe243d9″/>
<code code=”29762-2″ codeSystem=”2.16.840.1.113883.6.1″ codeSystemName=”LOINC” displayName=”Social history”/>
<title>Social History</title>
<text>
<table>
<thead>
<tr>
<th>Description</th>
<th>Date</th>
</tr>
</thead>
<tbody>
<tr>
<td>Current every day smoker</td>
<td>20141120</td>
</tr>
</tbody>
</table>
</text>
<entry typeCode=”DRIV”>
<observation classCode=”OBS” moodCode=”EVN”>
<templateId root=”2.16.840.1.113883.10.20.22.4.78″/>
<id extension=”12345″ root=”2.16.840.1.113883.19″/>
<code code=”ASSERTION” codeSystem=”2.16.840.1.113883.5.4″/>
<statusCode code=”completed”/>
<effectiveTime>
<low value=”20141120″/>
<high value=”null”/>
</effectiveTime>
<value code=”449868002″ codeSystem=”2.16.840.1.113883.6.96″ displayName=”Current every day smoker” xsi:type=”CD”/>
</observation>
</entry>
</section>
</component>
<component>
<section>
<templateId root=”2.16.840.1.113883.10.20.22.2.4.1″/>
<id extension=”VitalSigns” root=”17eae709-d72c-4b04-a75e-03facbe243d9″/>
<code code=”8716-3″ codeSystem=”2.16.840.1.113883.6.1″ codeSystemName=”LOINC” displayName=”Vital signs”/>
<title>Vital Signs</title>
<text>
<table>
<thead>
<tr>
<th>Date / Time</th>
<th>Height</th>
<th>Weight</th>
<th>BMI</th>
<th>BP</th>
</tr>
</thead>
<tbody>
<tr>
<td>20140923</td>
<td>190 in</td>
<td>200 lbs</td>
<td>3.89 NI</td>
<td>220/120</td>
</tr>
</tbody>
</table>
</text>
<entry typeCode=”DRIV”>
<organizer classCode=”CLUSTER” moodCode=”EVN”>
<templateId root=”2.16.840.1.113883.10.20.22.4.26″/>
<id root=”14c09c70-b5b1-470d-9fa4-c5a1e0562681″/>
<code code=”46680005″ codeSystem=”2.16.840.1.113883.6.96″ codeSystemName=”SNOMED-CT” displayName=”Vital signs”/>
<statusCode code=”completed”/>
<effectiveTime value=”20140923″/>
<component>
<observation classCode=”OBS” moodCode=”EVN”>
<templateId root=”2.16.840.1.113883.10.20.22.4.27″/>
<id root=”14c09c70-b5b1-470d-9fa4-c5a1e0562681″/>
<code code=”8302-2″ codeSystem=”2.16.840.1.113883.6.1″ codeSystemName=”LOINC” displayName=”Height”/>
<statusCode code=”completed”/>
<effectiveTime value=”20140923″/>
<value unit=”in” value=”190″ xsi:type=””/>
</observation>
</component>
<component>
<observation classCode=”OBS” moodCode=”EVN”>
<templateId root=”2.16.840.1.113883.10.20.22.4.27″/>
<id root=”14c09c70-b5b1-470d-9fa4-c5a1e0562681″/>
<code code=”3132″ codeSystem=”2.16.840.1.113883.6.1″ codeSystemName=”LOINC” displayName=”Weight”/>
<statusCode code=”completed”/>
<effectiveTime value=”20140923″/>
<value unit=”lbs” value=”200″ xsi:type=””/>
</observation>
</component>
<component>
<observation classCode=”OBS” moodCode=”EVN”>
<templateId root=”2.16.840.1.113883.10.20.22.4.27″/>
<id root=”14c09c70-b5b1-470d-9fa4-c5a1e0562681″/>
<code code=”39151″ codeSystem=”2.16.840.1.113883.6.1″ codeSystemName=”LOINC” displayName=”Body Mass Index”/>
<statusCode code=”completed”/>
<effectiveTime value=”20140923″/>
<value unit=”NI” value=”3.89″ xsi:type=””/>
</observation>
</component>
<component>
<observation classCode=”OBS” moodCode=”EVN”>
<templateId root=”2.16.840.1.113883.10.20.22.4.27″/>
<id root=”14c09c70-b5b1-470d-9fa4-c5a1e0562681″/>
<code code=”” codeSystem=”2.16.840.1.113883.6.1″ codeSystemName=”LOINC” displayName=””/>
<statusCode code=”completed”/>
<effectiveTime value=”20140923″/>
<value unit=”NI” value=”220″ xsi:type=””/>
</observation>
</component>
<component>
<observation classCode=”OBS” moodCode=”EVN”>
<templateId root=”2.16.840.1.113883.10.20.22.4.27″/>
<id root=”14c09c70-b5b1-470d-9fa4-c5a1e0562681″/>
<code code=”” codeSystem=”2.16.840.1.113883.6.1″ codeSystemName=”LOINC” displayName=””/>
<statusCode code=”completed”/>
<effectiveTime value=”20140923″/>
<value unit=”NI” value=”120″ xsi:type=””/>
</observation>
</component>
</organizer>
</entry>
</section>
</component>
</structuredBody>
</component>
</ClinicalDocument>

Note : This is a sample data taken from an online open source. You can just google it to find any simple open source CCDA files.

You will get an output like this as follows :

<ClinicalDocument xmlns=”urn:hl7-org:v3″ xmlns:cda=”urn:hl7-org:v3″ xmlns:sdtc=”urn:hl7-org:sdtc” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”&gt;
<realmCode code=”US”/>
<typeId extension=”POCD_HD000040″ root=”2.16.840.1.113883.1.3″/>
<templateId root=”2.16.840.1.113883.10.20.22.1.1″/>

………………………………………….
………………………………………….
………………………………………….

<component>
<section>
<templateId root=”2.16.840.1.113883.10.20.22.2.14″/>
<id extension=”Functional Status” root=”2.201″/>
<code code=”47420-5″ codeSystem=”2.16.840.1.113883.6.1″ codeSystemName=”LOINC” displayName=”FUNCTIONAL STATUS”/>
<title>Functional Status</title>
<text>
<table>
<thead>
<tr>
<th>Functional Condition</th>
<th>Effective Dates</th>
<th>Condition Status</th>
</tr>
</thead>
<tbody>
<tr>
<td/>
<td>null</td>
<td>completed</td>
</tr>
</tbody>
</table>
</text>
<entry typeCode=”DRIV”>
<observation classCode=”OBS” moodCode=”EVN”>
<templateId root=”2.16.840.1.113883.10.20.22.4.68″/>
<id root=”87347b49-c2ce-4e3e-9bf4-8fe09e9332db”/>
<code code=”” codeSystem=”2.16.840.1.113883.6.96″ displayName=”completed” xsi:type=”CE”/>
<statusCode code=”completed”/>
<effectiveTime>
<low value=”null”/>
<high value=”null”/>
</effectiveTime>
<value code=”” codeSystem=”2.16.840.1.113883.6.96″ displayName=”” xsi:type=”CD”/>
</observation>
</entry>
</section>
</component>
<component>
<section>
<templateId root=”2.16.840.1.113883.10.20.22.2.3.1″/>
<code code=”30954-2″ codeSystem=”2.16.840.1.113883.6.1″ codeSystemName=”LOINC” displayName=”RESULTS”/>
<title>Results</title>
<text>
<table>
<thead>
<tr>
<th>Test name</th>
<th>Date</th>
<th>Lab Name</th>
<th>Measure</th>
<th>Units</th>
<th>Reference Range</th>
<th>Lab Value</th>
</tr>
</thead>
<tbody>
<tr>
<td/>
<td>20141015</td>
<td>
<name>Century Hospital</name>
<name>Century Hospital</name>
<name>Century Hospital</name>
<name>Century Hospital</name>
</td>
<td>10^3/ul</td>
<td>10^3/ul</td>
<td> – </td>
<td>220.00</td>
</tr>
</tbody>
</table>
</text>
<entry>
<organizer classCode=”BATTERY” moodCode=”EVN”>
<templateId root=”2.16.840.1.113883.10.20.22.4.1″/>
<id root=”e673e7ec-effc-445c-8d4d-b723ad8327aa”/>
<code code=”43789009″ codeSystem=”2.16.840.1.113883.6.96″ codeSystemName=”SNOMED-CT” displayName=”CBC WO Differential” xsi:type=”CE”/>
<statusCode code=”completed”/>
<component>
<observation classCode=”OBS” moodCode=”EVN”>
<templateId root=”2.16.840.1.113883.10.20.22.4.2″/>
<id root=”c6a6491d-c11e-425a-ac72-028cb6624783″/>
<code code=”” codeSystem=”2.16.840.1.113883.6.1″ codeSystemName=”LOINC” displayName=””/>
<statusCode code=”completed”/>
<effectiveTime value=”20141015″/>
<value unit=”10^3/ul” value=”220.00″ xsi:type=””/>
<author>
<time value=””/>
<assignedAuthor>
<id root=”c6a6491d-c11e-425a-ac72-028cb6624783″/>
<representedOrganization>
<name>&lt;name xmlns=”urn:hl7-org:v3″&gt;Century Hospital&lt;/name&gt;&lt;name xmlns=”urn:hl7-org:v3″&gt;Century Hospital&lt;/name&gt;&lt;name xmlns=”urn:hl7-org:v3″&gt;Century Hospital&lt;/name&gt;&lt;name xmlns=”urn:hl7-org:v3″&gt;Century Hospital&lt;/name&gt;</name>
</representedOrganization>
</assignedAuthor>
</author>
<referenceRange>
<observationRange>
<value xsi:type=””>
<low unit=”” value=””/>
<high unit=”” value=””/>
</value>
</observationRange>
</referenceRange>
</observation>
</component>
</organizer>
</entry>
</section>
</component>
<component>
<section>
<templateId root=”2.16.840.1.113883.10.20.22.2.17″/>
<id extension=”SocialHistory” root=”17eae709-d72c-4b04-a75e-03facbe243d9″/>
<code code=”29762-2″ codeSystem=”2.16.840.1.113883.6.1″ codeSystemName=”LOINC” displayName=”Social history”/>
<title>Social History</title>
<text>
<table>
<thead>
<tr>
<th>Description</th>
<th>Date</th>
</tr>
</thead>
<tbody>
<tr>
<td>Current every day smoker</td>
<td>20141120</td>
</tr>
</tbody>
</table>
</text>
<entry typeCode=”DRIV”>
<observation classCode=”OBS” moodCode=”EVN”>
<templateId root=”2.16.840.1.113883.10.20.22.4.78″/>
<id extension=”12345″ root=”2.16.840.1.113883.19″/>
<code code=”ASSERTION” codeSystem=”2.16.840.1.113883.5.4″/>
<statusCode code=”completed”/>
<effectiveTime>
<low value=”20141120″/>
<high value=”null”/>
</effectiveTime>
<value code=”449868002″ codeSystem=”2.16.840.1.113883.6.96″ displayName=”Current every day smoker” xsi:type=”CD”/>
</observation>
</entry>
</section>
</component>
<component>
<section>
<templateId root=”2.16.840.1.113883.10.20.22.2.4.1″/>
<id extension=”VitalSigns” root=”17eae709-d72c-4b04-a75e-03facbe243d9″/>
<code code=”8716-3″ codeSystem=”2.16.840.1.113883.6.1″ codeSystemName=”LOINC” displayName=”Vital signs”/>
<title>Vital Signs</title>
<text>
<table>
<thead>
<tr>
<th>Date / Time</th>
<th>Height</th>
<th>Weight</th>
<th>BMI</th>
<th>BP</th>
</tr>
</thead>
<tbody>
<tr>
<td>20140923</td>
<td>190 in</td>
<td>200 lbs</td>
<td>3.89 NI</td>
<td>220/120</td>
</tr>
</tbody>
</table>
</text>
<entry typeCode=”DRIV”>
<organizer classCode=”CLUSTER” moodCode=”EVN”>
<templateId root=”2.16.840.1.113883.10.20.22.4.26″/>
<id root=”14c09c70-b5b1-470d-9fa4-c5a1e0562681″/>
<code code=”46680005″ codeSystem=”2.16.840.1.113883.6.96″ codeSystemName=”SNOMED-CT” displayName=”Vital signs”/>
<statusCode code=”completed”/>
<effectiveTime value=”20140923″/>
<component>
<observation classCode=”OBS” moodCode=”EVN”>
<templateId root=”2.16.840.1.113883.10.20.22.4.27″/>
<id root=”14c09c70-b5b1-470d-9fa4-c5a1e0562681″/>
<code code=”8302-2″ codeSystem=”2.16.840.1.113883.6.1″ codeSystemName=”LOINC” displayName=”Height”/>
<statusCode code=”completed”/>
<effectiveTime value=”20140923″/>
<value unit=”in” value=”190″ xsi:type=””/>
</observation>
</component>
<component>
<observation classCode=”OBS” moodCode=”EVN”>
<templateId root=”2.16.840.1.113883.10.20.22.4.27″/>
<id root=”14c09c70-b5b1-470d-9fa4-c5a1e0562681″/>
<code code=”3132″ codeSystem=”2.16.840.1.113883.6.1″ codeSystemName=”LOINC” displayName=”Weight”/>
<statusCode code=”completed”/>
<effectiveTime value=”20140923″/>
<value unit=”lbs” value=”200″ xsi:type=””/>
</observation>
</component>
<component>
<observation classCode=”OBS” moodCode=”EVN”>
<templateId root=”2.16.840.1.113883.10.20.22.4.27″/>
<id root=”14c09c70-b5b1-470d-9fa4-c5a1e0562681″/>
<code code=”39151″ codeSystem=”2.16.840.1.113883.6.1″ codeSystemName=”LOINC” displayName=”Body Mass Index”/>
<statusCode code=”completed”/>
<effectiveTime value=”20140923″/>
<value unit=”NI” value=”3.89″ xsi:type=””/>
</observation>
</component>
<component>
<observation classCode=”OBS” moodCode=”EVN”>
<templateId root=”2.16.840.1.113883.10.20.22.4.27″/>
<id root=”14c09c70-b5b1-470d-9fa4-c5a1e0562681″/>
<code code=”” codeSystem=”2.16.840.1.113883.6.1″ codeSystemName=”LOINC” displayName=””/>
<statusCode code=”completed”/>
<effectiveTime value=”20140923″/>
<value unit=”NI” value=”220″ xsi:type=””/>
</observation>
</component>
<component>
<observation classCode=”OBS” moodCode=”EVN”>
<templateId root=”2.16.840.1.113883.10.20.22.4.27″/>
<id root=”14c09c70-b5b1-470d-9fa4-c5a1e0562681″/>
<code code=”” codeSystem=”2.16.840.1.113883.6.1″ codeSystemName=”LOINC” displayName=””/>
<statusCode code=”completed”/>
<effectiveTime value=”20140923″/>
<value unit=”NI” value=”120″ xsi:type=””/>
</observation>
</component>
</organizer>
</entry>
</section>
</component>
</structuredBody>
</component>
</ClinicalDocument>

This is a generalized code, that will remove the vital signs status for any incoming CCDA files. You can just change the template ID of the above provided code and remove any data that you want to remove to achieve this.

 

 

 

Status Monitor in Mirth

Consider this scenario,

You are having Mirth in your system. Which is actively running at-least 5 to 10 channels deployed. Each and every channel performs multiple operations within, and they are independent of each other.

Mirth allows a concept of setting alerts if there is a channel that is error, has messages filled in queue, has stopped due to heap space issues or halted. Mirth community edition will trigger an email to your personal mailing system if you run into any of these troubles.

Different Thought :

How about deploying a channel that will act as a tool, which will primarily monitor all the deployed channel and accumulates the status of those channels now and then and form that as an API.

How will it Help?

If you have a multiple mirth instances, which runs multiple channels, this channel can be deployed in each and every mirth instance and that will monitor  status of all the channels been deployed. Using this status accumulated API we can call it in a mobile or any web application to develop as a generalized reporting tool.

Pre-requisite:

  • MirthConnect : 3.4.1.8057 (latest version)

Channel Setup:
Source : Javascript Reader

  1. Create a New channel name it as your wish Here I have named MirthMonitor
  2. Click on Set Data Types button set the source connector inbound to Raw
  3. Outbound and Destination 1 to Raw
     – that’s it the channel setup is done for it.

Destination : Channel Writer

Source Transformer Code:

/*Get Deployed Channel ID’s*/
var getDeployedChannelIds = ChannelUtil.getDeployedChannelIds();
var getDeployedChannelIdsCount=getDeployedChannelIds.size();
/*Initialize Array*/
var Totaldata=[];
/*InitializeObject*/
var monitorObject={};
/*JSON Header*/
monitorObject.Date= DateUtil.getCurrentDate(“yyyyMMdd”);
monitorObject.ClientKey=UUIDGenerator.getUUID();
/*LoopIndependentChannelStatistics*/
for (var index = 0; index < getDeployedChannelIds.size(); index++) {
var ChannelID = getDeployedChannelIds.get(index);
monitorObject.ChannelStats={};
monitorObject.ChannelStats.Id=ChannelID;
monitorObject.ChannelStats.ChannelName=ChannelUtil.getChannelName(ChannelID);
monitorObject.ChannelStats.ReceivedCount=ChannelUtil.getReceivedCount(ChannelID);
monitorObject.ChannelStats.FilterCount=ChannelUtil.getFilteredCount(ChannelID);
monitorObject.ChannelStats.QueuedCount=ChannelUtil.getQueuedCount(ChannelID);
monitorObject.ChannelStats.ErrorCount=ChannelUtil.getErrorCount(ChannelID);
monitorObject.ChannelStats.SentCount=ChannelUtil.getSentCount(ChannelID);
Totaldata.push(monitorObject.ChannelStats);
}
monitorObject.ChannelStats=Totaldata;
/*PrintJsonData*/
var output= JSON.stringify(monitorObject);
logger.info(output);
channelMap.put(‘jsonOutput’,output);

Let us consider that we have deployed three channels in the mirth and are actually running actively. We are now deploying this channel which will run and monitor the status of other channels and produce the outputs in the JSON format.

channel monitor

Output of this channel monitor will be a JSON data as shown below, Here I have deployed three channels and the outputs are monitored and produced below in real-time  for every 5 seconds.

{
“Date”: “20160831”,
“ClientKey”: “1792cdf1-1926-4438-9d4b-715ac3e45c63”,
“ChannelStats”: [{
“Id”: “f9d9d3ed-59ce-46dd-93cf-309adf0709bb”,
“ChannelName”: “JSON to HL7V2”,
“ReceivedCount”: 31,
“FilterCount”: 0,
“QueuedCount”: 0,
“ErrorCount”: 10,
“SentCount”: 21
}, {
“Id”: “97968dc3-78ba-40e9-960d-b4bee41e961e”,
“ChannelName”: “MirthMonitor-Version2”,
“ReceivedCount”: 4,
“FilterCount”: 0,
“QueuedCount”: 0,
“ErrorCount”: 3,
“SentCount”: 0
}, {
“Id”: “9c1bb1e3-6a4f-4213-b8de-49c16f7ae7e4”,
“ChannelName”: “QRDA – Generator”,
“ReceivedCount”: 25,
“FilterCount”: 0,
“QueuedCount”: 0,
“ErrorCount”: 3,
“SentCount”: 22
}]
}

 

 

 

Blog at WordPress.com.

Up ↑