web stats
Mirth Tools: User defined functions - Mirth Community

Go Back   Mirth Community > Mirth Connect > Support

Reply
 
Thread Tools Display Modes
  #1  
Old 04-17-2012, 10:32 AM
upstart33 upstart33 is offline
Mirth Guru
 
Join Date: Dec 2010
Location: Chicago, IL.
Posts: 459
upstart33 is on a distinguished road
Default Mirth Tools: User defined functions

Hi,

Out of a desire to learn, partial boredom, and a double-dog dare, I decided to create a thread topic to list some things that I find useful when dealing with Mirth, creating Channels, or fine-tuning the database.

First, I'd figure I would start out with some functions that some may find useful when developing channels.

Feel free to use them, tweak them, or add more to this thread of others to gaze upon. I hope to start other threads with other tools/scripts/code examples to assist in making our daily Mirth Connect experience that more enjoyable and less migraine inducing.

These functions can be defined and called from within a single Channel itself, or placed into the Code Template section for use throughout all of your Channels.

1. Determine a Channel's status
Purpose: To determine the current state of a channel.

Sometimes, you need to know the status of a certain channel, from within another channel. To accomplish this, you can use the following to see if the channel is :
-STARTED
-STOPPED
-PAUSED
-NA (Non-available)

Code:
function GetChannelState(channel_id) {

    var channel_status = "NA";

    var channel_count = parseInt(Packages.com.webreach.mirth.server.controllers.ChannelStatusController.getInstance().getChannelStatusList().size());

    for(var i=0;i<channel_count;i++) {
        if (channel_id == Packages.com.webreach.mirth.server.controllers.ChannelStatusController.getInstance().getChannelStatusList().get(i).getChannelId()) {
            channel_status = Packages.com.webreach.mirth.server.controllers.ChannelStatusController.getInstance().getChannelStatusList().get(i).getState();
        }
    }

    return channel_status;

}
If you do not know your channel_id, you can view it next to the channel_name on the 'Channels' section of your Mirth Connect Administrator.
Reply With Quote
  #2  
Old 04-17-2012, 10:39 AM
upstart33 upstart33 is offline
Mirth Guru
 
Join Date: Dec 2010
Location: Chicago, IL.
Posts: 459
upstart33 is on a distinguished road
Default

2. Quick SELECT/UPDATE/INSERT functions
Purpose: If most of your channels are connecting to the same database, you can use these functions to cut down some lines of code and for re-usability

Let's say that all of your channels are connecting to the same MySQL database. You could create a SELECT function as such:

Code:
function CDRSelect(sql) {

    var dbConn = DatabaseConnectionFactory.createDatabaseConnection("com.mysql.jdbc.Driver","jdbc:mysql://localhost:3306/cdrexample","USER","PASSWORD");
    
    var results = dbConn.executeCachedQuery(sql);

    dbConn.close();

    return results;
}
Now, in your Source or in a javascript transformer, you can execute a SELECT query by calling the following:

Code:
var results= CDRSelect("SELECT * FROM results");
....and for an UPDATE query, it would look like the following:

Code:
function CDRUpdate(sql) {

    var dbConn = DatabaseConnectionFactory.createDatabaseConnection("com.mysql.jdbc.Driver","jdbc:mysql://localhost:3306/cdrexample","USER","PASSWORD");
    var results = dbConn.executeUpdate(sql);
    dbConn.close();

    return results;
}
...which can be referenced by :

Code:
var results= CDRUpdate("UPDATE results set name = 'test'");
Reply With Quote
  #3  
Old 04-17-2012, 10:43 AM
upstart33 upstart33 is offline
Mirth Guru
 
Join Date: Dec 2010
Location: Chicago, IL.
Posts: 459
upstart33 is on a distinguished road
Default

3. Misc. Trim functions
Purpose: Sometimes, you need to do some trimming of strings. If you do not feeling like memorizing Regex, you can use something along the lines of the following.

Code:
function trim(in_str) {
    return new String(in_str).replace(/^\s+|\s+$/g,"");
}
Code:
function ltrim(in_str) {
    return new String(in_str).replace(/^\s+/,"");
}
Code:
function rtrim(in_str) {
    return new String(in_str).replace(/\s+$/,"");
}

Now, the function can be called within a transformer or mapper step:
Code:
var fname= trim(msg['patient_first'].toString());
Reply With Quote
  #4  
Old 04-17-2012, 11:07 AM
upstart33 upstart33 is offline
Mirth Guru
 
Join Date: Dec 2010
Location: Chicago, IL.
Posts: 459
upstart33 is on a distinguished road
Default

4. Word-Wrap
Purpose: If for some reason, you need to perform some Work Wrapping, then this function emulates PHP's 'wordwrap'.

-The string to be wrapped.
-The column width (a number, default: 75)
-The character(s) to be inserted at every break. (default: \n)
-The cut: a Boolean value (false by default).

Code:
function wordwrap( str, width, brk, cut ) {
 
  brk = (brk ? brk :\n);
  width = (width ? width : 75);
  cut = (cut ? cut : false);
 
    if (!str) { return str; }
 
    var regex = '.{1,' +width+ '}(\\s|$)' + (cut ? '|.{' +width+ '}|.+$' : '|\\S+?(\\s|$)');
 
    return str.match( RegExp(regex, 'g') ).join( brk );
 
}
The function can be called by:
Code:
wordwrap('The quick brown fox jumped over the lazy dog.', 20, '<br/>\n');
---------
The quick brown fox <br/>
jumped over the lazy <br/>
dog.
Reply With Quote
  #5  
Old 04-17-2012, 11:20 AM
upstart33 upstart33 is offline
Mirth Guru
 
Join Date: Dec 2010
Location: Chicago, IL.
Posts: 459
upstart33 is on a distinguished road
Default

5: Local Timzones
Purpose: Sometimes, your not running Mirth locally and you need the correct timezone.

Code:
function getCurrentLocalTimestamp() {
   var formatter = new Packages.java.text.SimpleDateFormat("yyyyMMddhhmmss"); 
   // your local TZ
   formatter.setTimeZone(Packages.java.util.TimeZone.getTimeZone("EST")); 
   return formatter.format(new Packages.java.util.Date()); 
}
The function can be used to get the local server date/time by the following:
Code:
tmp['MSH']['MSH.7']['MSH.7.1'] = getCurrentLocalTimestamp();
Reply With Quote
  #6  
Old 04-17-2012, 12:02 PM
narupley's Avatar
narupley narupley is online now
Mirth Employee
 
Join Date: Oct 2010
Posts: 7,002
narupley is on a distinguished road
Default

Quote:
UPDATE: I've created a public GitHub repository to track these example channels, code templates, scripts, or whatever else!

https://github.com/nextgenhealthcare/connect-examples

To start with I only added the ones I wrote, because I didn't want to presume and add code from others without their explicit permission. Pull requests welcome!
Haha, I'll play along:

xmlToHL7

Deserializes an E4X XML node into HL7 v2.x. This allows you to pass any E4X node of a message and convert it into HL7 v2.x correctly. Here's the code (I've also attached it as a code template):

Code:
function xmlToHL7(node,fieldSeparator,componentSeparator,repetitionMarker,subcomponentSeparator) {
	// Data validation
	if (!node) return '';
	// This shouldn't really ever happen with E4X anyway, but just in case.
	String.prototype.replaceAmp = function() {return this.replace(/&amp;/g,'&');};
	// If we're just dealing with a simple node, then just return its contents.
	if (node.hasSimpleContent()) return node.toString().replaceAmp();
	// Used for StringUtils
	importPackage(org.apache.commons.lang);
	
	// Defaults to standard HL7 encoding characters
	var fs = fieldSeparator || '|';
	var cs = componentSeparator || '^';
	var rm = repetitionMarker || '~';
	var ss = subcomponentSeparator || '&';
	var cr = '\x0D';

	// What will actually be returned
	var output = '';
	// Get the XML name of the node (in the case of an XMLList of repeating fields, the first one will be returned, since they all have the same name anyway)
	var qname = node[0].name().toString();
	// Use the HL7 dot notation to find what level we're at
	var level = StringUtils.countMatches(qname,'.');

	// If the name is HL7Message, we're at the root node
	if (qname == 'HL7Message')
		// Recursively append serialization for each segment
		for each (segment in node.children())
			output += xmlToHL7(segment,fs,cs,rm,ss);

	// If we're at the segment level
	else if (level == 0) {
		// If the node is an XMLList of multiple segments
		if (node.length() > 1) {
			// Recursively append serialization for each segment
			for each (segment in node)
				output += xmlToHL7(segment,fs,cs,rm,ss);
		}
		else {
			// Add the segment name to the output
			output += qname;
			// Initialize name placeholder
			var prevName = '';
			// Iterate through each field in the segment
			for each (field in node.children()) {
				// Get the QName of the field
				var fieldName = field.name().toString();
				// If we're dealing with the special cases of MSH.1/2, then just add the field contents
				if (fieldName in {'MSH.1':1,'MSH.2':1})
					output += field.toString().replaceAmp();
				// Otherwise add the recursive serialization of the field
				else
					// If we're on a field repetition, then prepend a repetition marker, otherwise prepend a field separator
					output += (prevName==fieldName?rm:fs) + xmlToHL7(field,fs,cs,rm,ss);
				// Update the field name placeholder
				prevName = fieldName;
			}
			// Add a carriage return to the end of the segment
			output += cr;
		}
	}

	// If we're at the field level
	else if (level == 1) {
		// If the node is an XMLList of multiple fields
		if (node.length() > 1) {
			// Recursively append serialization for each field
			for each (field in node)
				output += xmlToHL7(field,fs,cs,rm,ss) + rm;
			// Remove the final repetition marker
			output = StringUtils.chomp(output,rm);
		}
		else {
			// Recursively append serialization for each component
			for each (component in node.children())
				// Append a component separator to the end
				output += xmlToHL7(component,fs,cs,rm,ss) + cs;
			// Remove the last component separator
			output = StringUtils.chomp(output,cs);
		}
	}

	// If we're at the component level
	else if (level == 2) {
		// Recursively append serialization for each subcomponent
		for each (subcomponent in node.children())
			// Append a subcomponent separator to the end
			output += xmlToHL7(subcomponent,fs,cs,rm,ss) + ss;
		// Remove the last subcomponent separator
		output = StringUtils.chomp(output,ss);
	}

	// If we're at the subcomponent level
	else
		// Just add the contents of the node
		output = node.toString().replaceAmp();

	// Return the final output
	return output;
}
Examples

Deserialize the entire message:
Code:
logger.info(xmlToHL7(msg));
Deserialize just the PID segment:
Code:
logger.info(xmlToHL7(msg.PID));
Deserialize just the PID.3 segment (even if there are repetitions):
Code:
logger.info(xmlToHL7(msg.PID['PID.3']));
Attached Files
File Type: xml xmlToHL7.xml (4.6 KB, 442 views)

Last edited by narupley; 06-08-2018 at 10:34 AM.
Reply With Quote
  #7  
Old 04-17-2012, 02:26 PM
glenn71 glenn71 is offline
OBX.3 Kenobi
 
Join Date: Nov 2010
Location: Sydney Australia
Posts: 129
glenn71 is on a distinguished road
Default

Function to Stop a channel if it reaches a particular error count. Also sends an alert message via another channel (this sends a sms via our monitoring system).

Code:
function stopOnErrorCount(cid, errorCount) {
	var channelStatisticsController = Packages.com.mirth.connect.server.controllers.ChannelStatisticsController.getInstance();
	var channelStatusController = Packages.com.mirth.connect.server.controllers.ChannelStatusController.getInstance();
	var stats = channelStatisticsController.getStatistics(cid);
	// var numOfQueued = stats.queued;
	var numOfErrors = stats.error;

	if(numOfErrors > errorCount )
	{
		//Stop Me
		channelStatusController.stopChannel(cid);
		//Alert msg sent to _SVHALERT channel
		var channelController = Packages.com.mirth.connect.server.controllers.ChannelController.getInstance();
		var channelName = channelController.getDeployedChannelById(cid).getName();
		var alertmsg = 'mirth01 mirth_channel_status codeTemplate_stopOnErrorCount 0 ' +
					   'channel ' + channelName + ' stopped';
		router.routeMessage('_SVHALERT', alertmsg);
	}
}

To use this in the Postprocessor:
Code:
// will stop the current channel when errorcount greater than 10.
stopOnErrorCount(channelId, 10);
return;
Reply With Quote
  #8  
Old 04-18-2012, 06:35 AM
Bostad Bostad is offline
Level 85 Channel Wizard
 
Join Date: Mar 2009
Location: Stratford Ontario
Posts: 712
Bostad is an unknown quantity at this point
Default Converting HL7 DTG to English Date

I'm swamped so can't add much but here's something:

Code:
//presumes YYYYYMMDDHHSS  -although I don't get the time with this one

function DateConvert(strString) {

var months = new Array ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');

var year = strString.substring(0,4);
var mm = strString.substring(5,6);
var day = strString.substring(7,8);

return day + ', ' + months[mm] + ', ' + year;


}

This one does the time as well

Code:
function DTGConvert(strString) {


if (strString.length < 9) {     //user requirement

strString = strString + '0000'

}

var months = new Array ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');


var year = strString.substring(0,4);
var mm = parseInt(strString.substring(4,6),10);
var day = strString.substring(6,8);
var hh = strString.substring(8,10);
var min = strString.substring(10,12);

return day + ', ' + months[(mm-1)] + ', ' + year + ' ' + hh + ':' + min;


}
__________________
I can be reached through gmail and Google Talk using davidrothbauer at gmail dot com
http://www.linkedin.com/pub/david-rothbauer/5/923/518
codeismydrug.wordpress.com
hl7coders.wordpress.com

Test all my code suggestions prior to implementation
Reply With Quote
  #9  
Old 04-18-2012, 10:36 AM
jacobb jacobb is offline
Mirth Employee
 
Join Date: Aug 2006
Location: Irvine, CA
Posts: 1,218
jacobb is an unknown quantity at this point
Default

Thanks for all of the contributions! These seem really useful and they should probably be posted to the wiki so they don't get lost in the forums.
__________________
Jacob Brauer
Director, Software Development
NextGen Healthcare

Reply With Quote
  #10  
Old 04-19-2012, 04:47 AM
narupley's Avatar
narupley narupley is online now
Mirth Employee
 
Join Date: Oct 2010
Posts: 7,002
narupley is on a distinguished road
Default

Quote:
UPDATE: I've created a public GitHub repository to track these example channels, code templates, scripts, or whatever else!

https://github.com/nextgenhealthcare/connect-examples

To start with I only added the ones I wrote, because I didn't want to presume and add code from others without their explicit permission. Pull requests welcome!
Fix the node order of an HL7 v2.x E4X XML object

When you add fields/components/subcomponents out of order in a message, it isn't serialized correctly by Mirth. For example, if you have the following code:

Code:
var nte = <NTE/>;

nte['NTE.1']['NTE.1.1'] = '1';
nte['NTE.3']['NTE.3.1'] = 'comment';
nte['NTE.2']['NTE.2.1'] = 'TX';

msg.appendChild(nte);
then your NTE segment will come out looking like:

Code:
NTE|1||comment|TX
Issue 625 is scheduled to be fixed in 3.0, but in the meantime, here's another way to fix it:

fixHL7NodeOrder takes in a single HL7 v2.x node (it can be the root HL7Message, or a single segment/field/component/etc.), and returns the same node, with all children sorted in ascending order as per the HL7 dot notation. So the following node:

Code:
<NTE>
	<NTE.1>
		<NTE.1.1>1</NTE.1.1>
	</NTE.1>
	<NTE.3>
		<NTE.3.1>comment</NTE.3.1>
	</NTE.3>
	<NTE.2>
		<NTE.2.1>TX</NTE.2.1>
	</NTE.2>
</NTE>
would be changed to:

Code:
<NTE>
	<NTE.1>
		<NTE.1.1>1</NTE.1.1>
	</NTE.1>
	<NTE.2>
		<NTE.2.1>TX</NTE.2.1>
	</NTE.2>
	<NTE.3>
		<NTE.3.1>comment</NTE.3.1>
	</NTE.3>
</NTE>
Here's the code, or you can just import the attached code template. Cheers!

Code:
/*
	Author: Nick Rupley
	Date Modified: 4/18/2012

	fixHL7NodeOrder: Returns a new E4X node where the order of all siblings and descendants have been fixed as per the Mirth HL7 dot notation convention.

	Arguments
	---------
		Required
		--------
		node: The node to be fixed. 
*/

function fixHL7NodeOrder(node) {
	// Create output node
	var newNode = new XML();
	// In case the node is an XMLList of multiple siblings, loop through each sibling
	for each (sibling in node) {
		// Create new sibling node
		var newSibling = new XML('<'+sibling.name().toString()+'/>');
		// Iterate through each child node
		for each (child in sibling.children())
			// If the child has its own children, then recursively fix the node order of the child
			if (child.hasComplexContent())
				newSibling.appendChild(fixHL7NodeOrder(child));
			// If the child doesn't have its own children, then just add the child to the new sibling node
			else
				newSibling.appendChild(child);
		// After recursively fixing all of the child nodes, now we'll fix the current node
		newNode += sortHL7Node(newSibling);
	}
	// Return the fixed node
	return newNode;
}

// Helper function for fixHL7NodeOrder
function sortHL7Node(node) {
	// If the node has no children, then there's nothing to sort
	if (node.hasSimpleContent())
		return node;
	// Create new output node
	var newNode = new XML('<'+node.name().toString()+'/>');
	// Iterate through each child in the node
	for each (child in node.children()) {
		// If the child has a QName, then we can sort on it
		if (child.name()) {
			// Get the current "index" of the child. Id est, if the QName is PID.3.1, then the index is 1
			curChildIndex = parseInt(child.name().toString().substring(child.name().toString().lastIndexOf('.')+1),10);
			// Boolean placeholder
			var inserted = false;
			// Iterate through each child currently in the NEW node
			for (var i = 0; i <= newNode.children().length()-1; i++) {
				// Get the index of the child of the new node
				loopChildIndex = parseInt(newNode.child(i).name().toString().substring(newNode.child(i).name().toString().lastIndexOf('.')+1),10);
				// If the child we want to insert has a lower index then the current child of the new node, then we're going to insert the child 
				// right before the current newNode child
				if (curChildIndex < loopChildIndex) {
					// Insert the child
					newNode.insertChildBefore(newNode.children()[i],child);
					// Set our flag, indicating that an insertion was made
					inserted = true;
					// No need to continue iteration
					break;
				}
			}
			// If no insertion was made, then the index of the child we want to insert is greater than or equal to all of the
			// indices of the children that have already been inserted in newNode. So, we'll just append the child to the end.
			if (!inserted)
				newNode.appendChild(child);
		}
	}
	// Return the sorted HL7 node
	return newNode;
}
Attached Files
File Type: xml fixHL7NodeOrder.xml (3.4 KB, 197 views)

Last edited by narupley; 06-08-2018 at 10:34 AM.
Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -8. The time now is 03:19 AM.


Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Mirth Corporation