I created a new user with the %Developer role and verified that the class could be debugged successfully. I then added the %SecureBreak role to that user and encountered an issue similar, but not identical to the one reported by @Rueben Van Mol and @Donna.Goodall:

<COMMAND>zDebugStub+30^%Debugger.System.1
Invalid debugger target: ##class(Blue.App.Handler.Absolut).TestConnection()
Target has exited debugger

I've copied your class into my local environment and am not getting any errors using the Studio debugger.

Cache for Windows (x86-64) 2018.1.2 (Build 309U) Mon Mar 4 2019 15:05:44 EST [HealthShare Modules:Core:15.032.9020 + Linkage Engine:15.032.9020]

Class Blue.App.Handler.Absolut Extends %RegisteredObject
{

ClassMethod TestConnection()
{
    w "ok"
    q
}

}


Debugger executing '##class(Blue.App.Handler.Absolut).TestConnection()'
Executing ##class(Blue.App.Handler.Absolut).TestConnection()
ok
Program has terminated.
Target has exited debugger

Scott,

The challenge here is defining what is "long enough enough for a response." One option is to wait forever, but that will have the effect of processing no further messages until the call completes. That can lead to resource contention and race conditions that could be unacceptable in a healthcare setting.

Better to set a timeout and then take some action when the operation fails to complete. Here's a COS example that could be used in a <code> activity in place of a <call>:

set callrequest.ResearchID = context.RschMRN
do process.SendRequestSync("PatientBillingDBPoll", callrequest, .callresponse, 60)
if ('$ISOBJECT(callresponse)) {
   // handle the timeout in here; suspend the message, perhaps?
} else {
   set context.ClarityRs = callresponse
}

This will wait 60 seconds for a response, and if none is received perform the actions in the 'if' consequence.

No, you can't use operands for date math with the "stock" file business service.

I'm assuming that you want to guarantee that only completed txt files are picked up by the business service. In that case, you'll probably want to use the EnsLib.RecordMap.Operation.BatchFileOperation Business Operation class, and update your Record Map to include (and generate) an "empty" batch class.

You can then specify an intermediate file path in the operation's config that will hold the current day's records until the rollover schedule criteria is met. At the scheduled time, the accumulated records in the intermediate file path will be moved to the destination file path and name specified in the config.

Batch class definition:

Sample configuration for EnsLib.RecordMap.Operation.BatchFileOperation, with additional settings required to enable batch rollover:

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.

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.

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.

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

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.

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

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.

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?

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

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.

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.