tcl scripting help

Clovertech Forums Read Only Archives Cloverleaf Cloverleaf tcl scripting help

  • Creator
    Topic
  • #51303
    Tom Arrowsmith
    Participant

      I need to write a script that goes something like this:

      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

    Viewing 16 reply threads
    • Author
      Replies
      • #69599
        Robert Kersemakers
        Participant

          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

        • #69600
          Keith McLeod
          Participant

            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

            }

          • #69601
            Tom Rioux
            Participant

              If you are using an xlate, this can also be done inside the xlate IF statement using “ct” for contains.   Then if it meets your criteria, just suppress the message.  I believe I have used this somewhere and can find an example if you like.

              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

            • #69602
              Tom Arrowsmith
              Participant

                Thanks Tom,

                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

              • #69603
                Robert Kersemakers
                Participant

                  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

                • #69604
                  Tom Arrowsmith
                  Participant

                    I would love to see an example.

                    Thanks!

                  • #69605
                    Vince Angulo
                    Participant

                      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.

                    • #69606
                      Mason Miller
                      Participant

                        here is a screenshot from xlate[/code]

                      • #69607
                        Mason Miller
                        Participant

                          attachment did not work here it is

                        • #69608
                          Troy Morton
                          Participant

                            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.

                            Code:

                            ################################################################################
                            # 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

                            }

                          • #69609
                            Russ Ross
                            Participant

                              Using a trxID proc for killing messages can also be a handy way when faced with multiple complex conditions.

                              I posted an example I ended up doing for a pharmacy integration at the following URL:

                              https://usspvlclovertch2.infor.com/viewtopic.php?t=1562” class=”bbcode_url”>https://usspvlclovertch2.infor.com/viewtopic.php?t=1562

                              Russ Ross
                              RussRoss318@gmail.com

                            • #69610
                              Jim Kosloskey
                              Participant

                                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.

                              • #69611
                                Mason Miller
                                Participant

                                  try this it should work for what you are tyring to do.

                                  Code:


                                  ######################################################################
                                  # 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
                                  }

                                • #69612
                                  Tom Arrowsmith
                                  Participant

                                    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

                                  • #69613
                                    Mason Miller
                                    Participant

                                      if you are saying that if OBR4.2 contains EKG or ECG you may use someting like this

                                      Code:

                                      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

                                    • #69614
                                      Troy Morton
                                      Participant

                                        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.

                                        Code:

                                        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.   😀

                                      • #69615
                                        Todd Lundstedt
                                        Participant

                                          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.

                                          Code:

                                          ######################################################################
                                          # 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

                                      Viewing 16 reply threads
                                      • The forum ‘Cloverleaf’ is closed to new topics and replies.