Jeffrey Drumm · Mar 12, 2019 go to post

I actually gave  you half of the answer in my previous comment, but it was easy to miss ... laugh

The Target variable of the DTL will contain the value to which you'll set the callrequest property of the <call> activity:

EDIT: You don't really need to use the request builder for this. Just click on the "+" after Request Actions,  select "set", then select the property and value from their respective drop-down lists.

Jeffrey Drumm · Mar 12, 2019 go to post

Yes, it makes sense to set the request class to EnsLib.HL7.Message. However, the response class as Ens.Response is fine since you don't need to create anything to send back to the source business service. Here's a sample Context configuration for the BPL:

To get the segment count for the DG1s, you'd use the GetValueAt() method of the EnsLib.HL7.Message class, and assign it to a context variable. For this you would use the same notation as you would use in the DTL editor to get the segment count:

In COS, a non-zero value is treated as true, so you can use the variable to which you've assigned the count of DG1 segments as the condition for the <if>:

Finally, the context.DG1rec variable from the BPL's Context tab screenshot above was assigned to the record map class used to define the delimited structure. This variable is later used as the Target for the DTL and the value to which the <call> activity's callrequest was set.

I used the same record map class in the Business Operation (BO defined as class type EnsLib.RecordMap.Operation.FileOperation) for the RecordMap field:

This BO will roll over to a new file at midnight each night.

Jeffrey Drumm · Mar 11, 2019 go to post

The usual way of accomplishing this is to combine a DTL with a BPL. The BPL is responsible for calling the DTL and sending a record map message for each DG1 segment in the source message.

The DTL rules would look something like this:

The Main BPL structure would first get the number of DG1 segments in the message and <assign> it to a context variable (ex. context.DG1cnt), check to see if any are present, and if yes loop over them. The top-level BPL might resemble this:

The <while> block would create the record using data from the non-repeating portion of the HL7 message and the current DG1 segment, increment the segment counter, and send the record to the outbound operation:

If you haven't worked with BPLs before, you might want to take a look at the self-paced, on-line course.

Jeffrey Drumm · Mar 7, 2019 go to post

If you abort all 1040 of them, you're just changing the status to Aborted. They don't "go away," they're just no longer eligible for processing.

If you want to process the first 28 messages:

  1. In the Queue Viewer, select the queued destination from the list
  2. Click "Abort All" in the Queue Contents column
  3. In the Message Viewer, select the appropriate Source and Target Business Hosts, select Oldest First as the Sort Order, select Aborted as Status, and Page Size as 28 (you can enter any value in the page size box)
  4. Click the "Select All" checkbox at the top of the selection column (to the left of the '#' column)
  5. Click Resend on the Message Viewer page
  6. Click Resend from the Resend Messages page
Jeffrey Drumm · Mar 7, 2019 go to post

Sorry, can't help you there ... but your screenshot is not a terminal display, so I would not expect the terminal control sequences to do anything useful. HTML (<i>, </i>) might, but I've not spent much time with DeepSee. I'll leave that for someone else to answer.

Jeffrey Drumm · Mar 7, 2019 go to post

Interestingly, $C(27)_"[3m" provides italics when running csession under GNOME Terminal (v3.28.2) on Linux.

Jeffrey Drumm · Mar 7, 2019 go to post

Do you wish to do this under program control or as a global change to your terminal session?

Under program control you can use ANSI escape sequences, but their interpretation may be different based on platform. Unfortunately, italics doesn't seem to be a supported option. Try:

for i=1:1:108 w $C(27)_"["_i_"mText Attribute - "_i_$C(27)_"[0m"_$C(13,10)

to see what you get. On Windows, I don't see any changes after 84, but on Linux (csession) I see additional color options from 90-107.

For the session, using the Windows version of Terminal:

Then:

If you're referring to csession under Unix/Linux, font selection will be a function of your terminal emulator.

Jeffrey Drumm · Feb 25, 2019 go to post

Any application that supports HL7 messages should recognize and translate that value your showing in OBX:5.5 to the appropriate character string. It's a UNC path ... \\sitehopital.org\files\cardio\022018\GE274583.PDF.

Technically it's \E\, not \E that represents the HL7 escape character (the 3rd character in MSH:2).

If you wish the backslash character to be interpreted literally in the HL7 message, the escape character in MSH:2 should be substituted with some other character. I often use the backtick character (`). That will prevent a conformant application from trying to do something special with the backslash. And in making that change, an actual backtick character that should be displayed as such in the destination application would be represented as `E` ...

As Robert pointed out,  the $REPLACE() function can be used for the substitution:

set str = "\E\\E\sitehopital.org\E\files\E\cardio\E\022018\E\GE274583.PDF"

set fixed = $REPLACE(str,"\E\","\")

w  fixed

\\sitehopital.org\files\cardio\022018\GE274583.PDF

Jeffrey Drumm · Feb 25, 2019 go to post

Do these files require any transformation or are they just being copied/moved to the target filesystem? Do they need to be removed from the sftp server once transferred?

Ensemble's stock file transfer business service classes don't persist the directory recursed into as a property of the message object, so COS coding and a custom business service would be required, and likely a custom adapter as well. Assuming the complete file path is populated in the message's Source property, a custom File business operation would be needed to create the target path and file using the same name as the source.

The free WinSCP utility has a scripting interface that will perform a recursive transfer and deletion and could be scheduled to run under the Windows scheduler; if the intent is to simply move the files without transformation, this would likely be much faster to implement.

Jeffrey Drumm · Feb 24, 2019 go to post

I just tested this using the standard HL7 File Service and was able to use multiple wildcards to select files. In my testing I used a File Spec of *Test*.hl7 and the service picked up both RadTest01.hl7 and LabTest01.hl7. I also tried a File Spec of LabTest*.hl7;RadTest*.hl7 with the same net result.

The file adapters all use the same core methods for searching the specified directory path, so it should work unless you're using a custom service/adapter.

What version of Ensemble/HSConnect are you using?

Jeffrey Drumm · Feb 22, 2019 go to post

You'll find the Message Structure as the value after the colon in the message body's DocType property. The Name property is used to look up the associated message structure in the schema (the portion before the colon in the DocType property).

HICG > set msg=##class(EnsLib.HL7.Message).%OpenId(26872)

HICG > w msg.DocType

2.3.1:ADT_A01

HICG > w msg.Name

ADT_A04

Jeffrey Drumm · Feb 16, 2019 go to post

@Eduard Lebedyuk requested that I change the answer to reference the ISC public on-line documentation rather than using a local URL. I assumed he meant that he wanted me to edit my answer, and discovered that I could not.

Jeffrey Drumm · Feb 11, 2019 go to post

The DocType is not automatically set by the ImportFromFile() method. You would need to set it to whatever DocType (i.e. DocTypeCategory:DocTypeName) is required to properly parse the message.

In a Production, the Business Service that receives messages uses the value in the Message Schema Category field to set the DocType for subsequent processing, and if both a DocTypeCategory and DocTypeName are present it will override the automatic selection of the DiocTypeName determined by the HL7 message's MSH:9 value.

Jeffrey Drumm · Feb 11, 2019 go to post

If the message you wish to test is saved in a separate file outside of Ensemble, you can create a new message object with the following:

JEFF > set tMsg=##class(EnsLib.HL7.Message).ImportFromFile("/path/to/file/filename.ext")

You'll need to set the DocType manually; for messages already received by Ensemble, that was likely taken care of by the business service:

JEFF > set tMsg.DocType="2.3.1:ORU_R01"

Something to keep in mind is that methods expecting a message class as an argument (ex. EnsLib.HL7.Message) work on message objects rather than strings. The tMsg variable created by both the %OpenId() and ImportFromFile() methods are such objects. These objects have a rich set of methods for inspecting, transforming and otherwise working with messages.

Jeffrey Drumm · Feb 8, 2019 go to post

The easiest way is to use an existing message that's already in the Ensemble message store. Locate the MessageBodyId value in the header tab of the message viewer, and execute the following commands in the same namespace as the production:

JEFF > Set tMsg = ##class(EnsLib.HL7.Message).%OpenId(BodyID)

JEFF > Write ##class(Package.Name).MethodName(tMsg,"Identifier") 

Substitute the numeric Message Body ID for BodyID, the package name for your class for Package.Name, the method for MethodName and the identifier you want to test with for Identifier. The method you mentioned appears to return a string, so you should see the value displayed once you press enter on the 2nd command.

Jeffrey Drumm · Feb 8, 2019 go to post

If you don't want to modify the existing class, write a new one that extends it:

/// Custom functions for this installation
Class User.Rule.MyFunctionSet Extends Ens.Rule.FunctionSet
{

/// Stupid, redundant method provided as an example /// Accepts a string <var>pString</var> and regular expression pattern <var>pPattern</var> /// as arguments; returns 0 for no match, and a positive integer indicating the match's /// position if there is a match. ClassMethod REMatch(pString As %String, pPattern As %String) As %Integer { Return $LOCATE(pString,pPattern) }

}

As long as you don't define a method that overrides a previous method, you're completely safe.

EDIT: Eduard's example beat me to it and does exactly the same thing. The point, though, is that you're not adding methods to the existing class, you're creating new methods outside of it that are accessible from within your rule definitions.

Jeffrey Drumm · Feb 8, 2019 go to post

If your PID:3 is not defined in your schema as a repeating field, you can obtain a $LIST with:

JEFF > Set tLst = $LISTFROMSTRING(msg.GetValueAt("PID:3"),"^")

JEFF > w $LISTGET(tLst,1)

043622

JEFF > zw tLst

tLst=$lb("043622","","","ZZMC","MR","ZZMC")
Jeffrey Drumm · Feb 8, 2019 go to post

Are you looking for the number of repetitions/components, or the string length for the PID:3 field?

To get the number of repetitions:

JEFF > w msg.GetValueAt("PID:3(*)")

2

To get the number of components in the first repetition:

JEFF > w msg.GetValueAt("PID:3(1).*")

6

To get the string length of the entire field:

JEFF > w $LENGTH(msg.GetValueAt("PID:3"))

46
Jeffrey Drumm · Feb 8, 2019 go to post

I appear to be doing something wrong.

EDIT: Nevermind. I guess Preview doesn't actually preview the final posting format ...

Jeffrey Drumm · Feb 6, 2019 go to post

The answer above is significantly different from what I originally posted. the <foreach> action doesn't interpret a %List as a collection, so I went with an <until> instead. The condition for completion of the <until> is when the iterator equals the count of entries in the list (obtained by $LISTLENGTH()).

Jeffrey Drumm · Feb 5, 2019 go to post

The conventional mechanism for generating multiple outbound messages from a single inbound is via a BPL. It can also be done in a custom BP using ObjectScript, and I've also seen it done using the rule editor (but wouldn't recommend it ... it's not exactly intuitive).

In a BPL, you'd assign a context variable to the list of values extracted from ZCO:2 (using $LISTFROMSTRING()), then iterate over the list with an <until> action. You'll also need to create some other context variables for list length, iteration and element selection.

Inside the <until> you would:

  1. Increment the iterator and obtain the current list value using <assign> actions
  2. Invoke a DTL with a <transform> action that copies the relevant fields/segments from the source message to the target
  3. In the same DTL, use the current list iteration value to populate FT1:7.1
  4. Send the resulting message to the downstream Business Operation with a <call> action
Jeffrey Drumm · Feb 4, 2019 go to post

Have you added the OnGetConnections() method I posted as an answer above to your  BP?

Jeffrey Drumm · Feb 4, 2019 go to post

@Nareev, you're correct when the BP is a BPL. I don't believe that's the case due to the fact that Stephen's BP has a TargetConfigName property. That's not normally generated when the BP is created as a BPL.

Jeffrey Drumm · Feb 4, 2019 go to post

You'll need to override the OnGetConnections method by inserting the snippet below in your custom business process(es):

ClassMethod OnGetConnections(Output pArray As %String, pItem As Ens.Config.Item)
  {
                Do ##super(.pArray,pItem)
                If pItem.GetModifiedSetting("TargetConfigName",.tValue) {
                                For i=1:1:$L(tValue,",") { Set tOne=$ZStrip($P(tValue,",",i),"<>W")  Continue:""=tOne  Set pArray(tOne)="" }
                }
  }
Jeffrey Drumm · Jan 29, 2019 go to post

Have you tried exporting the production from the Management Portal's Production Configuration | Production Settings | Actions tab, then deploying it to the Test environment through Ensemble | Manage | Deployment Changes | Deploy?