› Clovertech Forums › Read Only Archives › Cloverleaf › Cloverleaf › tcl scripting help
If OBR-4.2 does not contain ECG or EKG then kill the message.
The situation may evolve to include a 3rd or 4th “or”.
I’m used to using scripts that revolve around the “equals” concept (cequal) and have not yet written one to catch a sequence of characters no matter where they occur in the data element. I say “contains” but the keyword may well be different….
Any help would be appreciated.
Thanks!
Tom Arrowsmith
Hi Tom,
I think you are looking for the ‘regexp’ command.
if {![regexp $filter $field]} {
}
Your $filter can look like “ECG|EKG”. This would mean that if the $field contains ECG OR EKG anywhere in the field, then the regexp is valid.
Hope this helps.
Zuyderland Medisch Centrum; Heerlen/Sittard; The Netherlands
Another option might be lcontain meaning list contains.
Syntax: lcontain list element
set your_list {ECG EKG}
If {![lcontain $your_list $your_field]} {
kill message
}
Just a note, when I suppress a message within an xlate, I also include an echo statement that will write to the log file telling me why the message in question was suppressed.
Thanks….
Tom Rioux
Actually, my predecessor who mentored me a year ago when I took this job left me with the impression that the only alternative for killing messages was a tcl script.
This is the second time I’ve heard that messages can be suppressed form within an xlate.
Can you tell me what the pro’s and con’s are of each way – I’d like to learn if using an xlate and suppressing is a methodology I should be taking advantage of.
I would be very interested to see your example – and also interested in how you write it out to a log file.
Please feel free to call me directly if that is easier …
Anyone else who has thoughts on “xlate-suppress” vs “tcl kill”, please chip in!
Tom – (703) 779-5474
I always try to kill the messages before the Xlate. Xlate takes a lot of performance (or so I hear), so it’s better to get rid of messages before they are translated.
But sometimes the conditions under which you want to kill the message are too complicated for a (generic) tclproc, so I have some Xlates where I kill certain messages inside the Xlate.
Zuyderland Medisch Centrum; Heerlen/Sittard; The Netherlands
Thanks!
I often use a basic xlate construct like this:
COPY =true => @SendMsg
COMMENT “++++ Start Exclusion Criteria +++++”
COMMENT “Exclude transactions where visit type is P or event is A05”
IF PV1.00132.[0] eq =P || EVN.00099.[0] eq =A05
>>>> COPY =false => @SendMsg
….other criteria follow….
COMMENT “++++ End Exclusion Criteria +++++”
IF @SendMsg eq =true
>>>> ….construct msg with copies and iterates, etc….
ELSE
>>>> SUPPRESS
Hope this helps.
I agree that Xlate is not the best place to KILL messages. I believe it should be avoided if at all possible (I can’t think of any reason it wouldn’t be possible).
Here is an example script where I’m KILLing the message based on mutliple criteria.
################################################################################
# Name: tps_TRvarianFilter_B_a
# Purpose:
#
# Filters ADT messages
#
# UPoC type: tps
# Args: tps keyedlist containing the following keys:
# MODE run mode (”start”, “run” or “time”)
# MSGID message handle
# ARGS TOFILE
#
#
#
proc tps_TRvarianFilter_B_a { args } {
global HciConnName HciSite
keylget args MODE mode ;# Fetch mode
set dispList {} ;# Nothing to return
switch -exact — $mode {
start {
# Perform special init functions
# N.B.: there may or may not be a MSGID key in args
}
run {
# init variables
set module “tps_TRvarianFilter_B_a”
set hosp “SHEC”
set killreason “”
set msgkill 0
set blnTrcWrite 0
set mh “”
set msgtext “”
set mshseg “”
set pv1seg “”
set eventtype “”
set patclass “”
set patloc “”
set patloc1 “”
set patloc2 “”
set now [clock format [clock seconds] -f “%m/%d/%Y %H:%M:%S”]
# Get arguments
keylget args MSGID mh
# Read message from handle
set msgtext [msgget -cvtnull “” $mh]
# Pull out the fields needed for our filters
set mshseg [getHL7Segment $msgtext MSH]
set pv1seg [getHL7Segment $msgtext PV1]
set cid [getHL7Field $mshseg 9]
set eventtype [getHL7Field $mshseg 8]
set patclass [getHL7Field $pv1seg 2]
set patloc [getHL7Field $pv1seg 3]
set patloc1 [getHL7Comp $pv1seg 3 1]
set patloc2 [getHL7Comp $pv1seg 3 2]
# echo ” cid: $cid”
# echo “eventtype: $eventtype”
# echo ” patclass: $patclass”
# echo ” patloc: $patloc”
# echo ” patloc1: $patloc1″
# echo ” patloc2: $patloc2n”
# Filter 1 : If EventType is A29, only keep if PatClass is “M” and PatLoc is “RCC ”
if { $eventtype == “ADT^A29” && ( $patclass != “M” || ( $patclass == “M” && $patloc1 != “RCC ” ) ) } {
set killreason “If EventType is A29, only keep if PatClass is “M” and Patient Location is “RCC “.”
set msgkill 1
}
# Filter 2 : If EventType is A05 or A08 and PatClass is “B”, only keep if PatLoc2 is “ONCL”
if { ( $eventtype == “ADT^A05” || $eventtype == “ADT^A08” ) && $patclass == “B” && $patloc2 != “ONCL” } {
set killreason “If EventType is A05 or A08 and PatClass is “B”, only keep if Patient Clinic Location is “ONCL”.”
set msgkill 1
}
# Filter 3 : If EventType is not A29 and PatClass is M, only keep if PatLoc is “CLINIC^ONCL”
if { ( $eventtype != “ADT^A29” && $patclass == “M” ) && $patloc != “CLINIC^ONCL” } {
set killreason “If EventType is not A29 and PatClass is “M”, only keep if Patient Location is “CLINIC^ONCL”.”
set msgkill 1
}
# Filter 4 : If EventType is A08, do not send if PatClas is F, G, 3 or 4.
if { $eventtype == “ADT^A08” && [lsearch “F G 3 4” $patclass] != -1 } {
set killreason “If EventType is A08, do not send if PatClas is F, G, 3 or 4.”
set msgkill 1
}
if { $msgkill } {
# Set disposition to KILL
lappend dispList “KILL $mh”
} else {
lappend dispList “CONTINUE $mh”
}
}
time {
# Timer-based processing
# N.B.: there may or may not be a MSGID key in args
}
shutdown {
# Doing some clean-up work
}
}
return $dispList
}
I posted an example I ended up doing for a pharmacy integration at the following URL:
https://usspvlclovertch2.infor.com/viewtopic.php?t=1562https://usspvlclovertch2.infor.com/viewtopic.php?t=1562
Russ Ross
RussRoss318@gmail.com
While I prefer filtering messages outside the Xlate (specifically in the Pre-Xlate Routing), I also understand not every shop has the Tcl skills to do this.
However, the demands do not wait for skill level upgrading.
So in my mind a case can be made for doing filtering in the Xlate.
It probably would be a good idea to make sure the fact that a particular Xlate filters mesages and the filtering conditions should be known outside of the Xlate (like in some integration documentation or perhaps the xlate name itself).
That way one can know without having to investigate that a given Xlate cannot be used in any global fashion.
Also, lot’s of commenting inside the Xlate should be de rigueur as well as trying to place all of the filtering logic together so it is easy to find.
Having said the above, there are some generic filtering procs in the community that can be used to do a lot (if not all) of the message filtering required.
But even then, that may challenge someone who has not yet attained the requisite skill level.
email: jim.kosloskey@jim-kosloskey.com 29+ years Cloverleaf, 59 years IT - old fart.
try this it should work for what you are tyring to do.
######################################################################
# Name: supress_adt_sc
# Purpose: supress all adt transactions from soarian clinicals
# UPoC type: tps
# Args: tps keyedlist containing the following keys:
# MODE run mode (”start”, “run” or “time”)
# MSGID message handle
# ARGS user-supplied arguments:
#
#
# Returns: tps disposition list:
#
#
proc tps_kill_ORR4_2 { args } {
keylget args MODE mode ;# Fetch mode
set dispList {} ;# Nothing to return
switch -exact — $mode {
start {
# Perform special init functions
# N.B.: there may or may not be a MSGID key in args
}
run {
# ‘run’ mode always has a MSGID; fetch and process it
keylget args MSGID mh
set msg [msgget $mh]
set segmentList [split $msg r]
set segment [lindex [lsearch -all -inline $segmentList OBR*]0]
set obr4_2 “”
set fieldSep [string index $msg 3]
set subfieldsep [string index $msg 4]
set fieldList [split $segment $fieldSep]
set obr4 [lindex $fieldList 4]
set obr4_2 [split $obr4 $subfieldsep]
set obr4_2 [lindex $obr4_2 2]
#echo $obr4
#echo $obr4_2
#if OBR4.2 is equal to EKG or ECG kill message!
if { $obr4_2 == “EKG” || $obr4_2 == “ECG” } {
set disp KILL
#echo kill message!!
} else {
set disp CONTINUE
#echo continue
}
lappend dispList “$disp $mh”
}
time {
# Timer-based processing
# N.B.: there may or may not be a MSGID key in args
}
shutdown {
# Doing some clean-up work
}
}
return $dispList
}
I have acheived the desired result by concatenating the OBR-4.3 and OBR-4.1 together to get a unique identifier for the orders, placing the result in the MSH-14, then using a table that identifies which of these resulting values should be recognized as ECG orders and if so, place the value EKG in the MSH-13.
Then I have a more simple tcl kill script that kills any messages that do not have the value EKG in the MSH 13.
So the pressures off….
However, looking at the various solutions that people offered – I want to clarify that the alternative route I was looking to go down was how to kill based on the ECG or EKG values being somewhere embedded in a longer string. It seems that the folks that give the procedures names here have no idea of a naming convention – and the only thing I could of hung my hat on is the fact that somewhere in the procedure name the characters ECG or EKG where there.
So I need the language to do that in a tcl script – but as this was a production issue I decided to pursue the solution I knew would work (Now I have a table to maintain, though!)
So now that the dust has settled, I would love to pursue the other method of identifying characters within a string – so that next time I can have a more elegant solution.
Still open to comments… I am very thankful for the suggestions and help offered here! This forum has often provided me with workable solutions and has help me to progress with my skills.
Thanks!
Tom
if { [string first “EKG” $obr4_2] !=-1 || [string first “ECG” $obr4_2] !=-1 } {
set disp KILL
echo kill message!!
} else {
set disp CONTINUE
#echo continue
To find a string in another string, I use the “string first” command:
string first string1 string2 ?startIndex?
Search string2 for a sequence of characters that exactly match the characters in string1. If found, return the index of the first character in the first such match within string2. If not found, return -1. If startIndex is specified (in any of the forms accepted by the index method), then the search is constrained to start with the character in string2 specified by the index.
string first a 0a23456789abcdef 5
will return 10, but
string first a 0123456789abcdef 11
will return -1.
tcl>set mystring “0a23456789abcdef”
tcl>string first a $mystring
1
tcl>string first a $mystring 5
10
tcl>string first a $mystring 11
-1
tcl>string first “bcd” $mystring
11
Hope this gives you some ideas. 😀
We have a script in use widely here called “kill_hl7_val.tcl” in which you can input any segment, field position, and a list of values. If any of those input values exist in that HL7 field, the message is killed. We have a similar script called “accept_hl7_val.tcl” which will continue on the same criteria. We haven’t had an overwhelming need to kill at a subcomponent level, yet. But, because of your post, and because I had an hour or so left on this Friday afternoon, I decided to go that extra step.
Note… the following script does NOT look at repeating fields… that will be a future need, I am sure.
######################################################################
# Name: kill_hl7_subcom_val
# Purpose: Interrogate any segment/element for value and kill it.
# UPoC type: tps
# Args: tps keyedlist containing the following keys:
# MODE run mode (”start”, “run” or “time”)
# MSGID message handle
# ARGS user-supplied arguments:
# Example:
# {SEG OBR} {SEGELE 4} {SEGSUB 1} {SEGVAL {EKG ECG}}
# code will kill records with OBR-4.2 = EKG or ECG
#
# Returns: tps disposition list:
# KILL or CONTINUE message.
#
proc kill_hl7_subcom_val { args } {
keylget args MODE mode ;# Fetch mode
set dispList {} ;# Nothing to return
switch -exact — $mode {
start {
# Perform special init functions
# N.B.: there may or may not be a MSGID key in args
}
run {
# ‘run’ mode always has a MSGID; fetch and process it
keylget args MSGID mh
keylget args ARGS.SEG SEG
keylget args ARGS.SEGELE SEGele
keylget args ARGS.SEGSUB SEGsub
keylget args ARGS.SEGVAL SEGval
set msg [msgget -cvtnull _ $mh]
set sep [csubstr $msg 3 1]
set subsep [csubstr $msg 4 1]
set repsep [csubstr $msg 5 1]
set segments [split $msg r]
set SEGlistLength [llength $SEGval]
#echo “kill_hl7_subcom_val SEG input =$SEG”
#echo “kill_hl7_subcom_val SEGele input =$SEGele”
#echo “kill_hl7_subcom_val SEGsub input =$SEGsub”
#echo “kill_hl7_subcom_val SEGval input =$SEGval”
#echo “segments is $segments”
lappend dispList “CONTINUE $mh”
foreach seg $segments {
# Check to see if SEGele equals one of the input values
if [cequal $seg “”] {continue} ;# check blank segment
if [cequal [csubstr $seg 0 3] $SEG] { ;# Get SEG input
set segFields [split $seg $sep ] ;# SEG fields
set field [lindex $segFields $SEGele] ;# Get SEG element #
set subcom [lindex [split $field $subsep] $SEGsub] ;# get SUBcomponent
for {set value 0} {$value < $SEGlistLength} {incr value 1} {
if [cequal $subcom [lindex $SEGval $value]] {
set dispList {}
lappend dispList "KILL $mh"
#echo $dispList
return $dispList
}
}
}
}
#echo $dispList
return $dispList
}
time {
# Timer-based processing
# N.B.: there may or may not be a MSGID key in args
}
}
}
As Jim K. indicated, need and desire to improve a personal skill set were the driving factors to the predecessors to the above script. We started out creating very similar kill scripts, one for each particular instance or need (kill_evn1, kill_obr25), and eventually grew into scripts that would accept different values to look for, and finally to the kill_hl7_val script on which the above script is based.
I have done minor testing, and this script is not in use anywhere yet, but I think it will work for you. One thing it does NOT do is tolerate mistakes in the input values… so use them with accuracy.
Example input parms:
kill OBX 3.2 values of Apgar 1 or Apgar 5
{SEG OBX} {SEGELE 3} {SEGSUB 1} {SEGVAL {{Apgar 1} {Apgar 5}}}
kill records with patient home phone area codes of 456, 987 or 147
{SEG PID} {SEGELE 13} {SEGSUB 5} {SEGVAL {456 987 147}}
Remember, if you are looking for text with an imbedded space, you must enclose that value in curly braces.
Hope this helps!
Todd