Jeffrey Drumm · Jul 3, 2019 go to post

I'm reasonably confident this will fix the issue without any side effects ...

TIE> Set ^Ens.Queue("ClinicomMsgRouter",0,"count")=0
TIE> Set ^Ens.Queue("PathologyRouter",0,"count")=0

Question though ... do you have the pool size for these routers set higher than 1? I'm concerned about the number of job entries  under each queue. This could have an impact on FIFO, and may have something to do with the erroneous queue counts.

Jeffrey Drumm · Jul 2, 2019 go to post

From the Cache prompt in  your production's namespace, what do you see when you type:

PROD> zw ^Ens.Queue("QueueName")

?

Replace QueueName with the name of the queue in question ...

Jeffrey Drumm · Jul 2, 2019 go to post

Hi Ed,

If you have the Show Query option enabled in Message Viewer, you can build your criteria for extracting the message data there, then click the button to see the SQL it generates. The default EnsLib.HL7.SearchTable indexes the Patient ID, but it includes PID:2.1, PID:3.1 (all iterations) and PID:4.1 (all iterations). If you're searching for specific IDs or ranges you may get unintended duplicates.

You don't need a custom SearchTable to filter the messages, but it sure helps.

If you don't have it enabled, you can turn it on for your production's namespace:

USER > zn "PROD"
PROD > set ^Ens.Debug("UtilEnsMessages","sql")=1

The Message Viewer is a powerful tool and gives you access for query and display of every HL7 field. The SQL it generates should put you on the right track.

Jeffrey Drumm · Jul 2, 2019 go to post

So when you select the queue in the Queues page, it shows no messages in the Queue Contents column?

Jeffrey Drumm · Jul 1, 2019 go to post

The "N" parameter always creates a new file. If you want to open a file (optionally creating it if it doesn't exist), use "E" instead,  and if you intend to Append to it, include the "A" parameter: open file:("EWA"):1. You don't need the "R" parameter unless you intend to read from it using the same device.

Depending on your Cache configuration, you may not even need the "E" parameter. The default behavior in my Windows installation is to create the file if it doesn't exist, with or without the "E" parameter (the documentation refers to "E" as  "UNIX-only", FYI).

Jeffrey Drumm · Jun 21, 2019 go to post

Adding categories will  have no direct impact on message processing. As you noted, there may be a tiny incremental increase in overhead temporarily if you query for them, and you may see a slight delay in the drop-down list ... but only if  you're talking about multiple hundreds of them.

If the intent is to better document your interfaces, though, take a look at the Business Partner configuration. It's an often-overlooked option to keep details regarding the vendor, its contacts, and other information associated with each interface. This may help make the number of categories more manageable.

Jeffrey Drumm · Jun 20, 2019 go to post

Yes, the fact that the Output window lets you interactively enter commands makes "hacking" a bit easier, but so do the "Run" and "Debug" menu options laugh

I'm curious to know if there's a way to limit a user to writing COS while prohibiting compilation or execution ...

And a server -side configuration option that makes the Output window read-only devil

Jeffrey Drumm · Jun 20, 2019 go to post

Wow, I would think this is a potential security risk. I'm assuming the smart IRIS/Cache Administrator would remove any permissions to create or modify web app settings ...

The answer, while very clever, also assumes access to tools other than Studio. Can the requested solution be provided with Studio alone, and, more importantly, will it be possible for the administrator to prevent this sort of access?

Jeffrey Drumm · Jun 18, 2019 go to post

 The other thing that just occurred to me is that the vendor is dumping a stack trace that may contain print control characters such as carriage returns, and the operation is simply treating the first \x0d as the end of the segment. That would effectively exclude the rest of the stack trace from the message body for conventional parsing purposes. The data is likely still there, but it can't be parsed out as part of MSA;3 with the parsing rules defined in the ACK's schema.

Jeffrey Drumm · Jun 18, 2019 go to post

I haven't had the need to do this yet, but my guess is you're going to have to create a custom operation that modifies the received NAK via the OnReplyDocument() method. If the stack trace is really big, it may be stored as a Stream Object rather than a %String. And if that's the case, you're going to have to scan the Stream for the string you need, and replace the contents of the MSA:3 with that string.

I'd whip up an example for you but I'm headed out the door and will be OoO for the day. I'll check back tonight to see if anyone's beaten me to it :D

Jeffrey Drumm · May 20, 2019 go to post

As @Eduard.Lebedyukpointed out, the method should return a status as indicated by its signature, so at the very minimum you should have a "Return $$$OK" at the end of the method.

That said, %FromJSON() throws an exception when it fails, and the generic error you'll get in the Event Log won't tell you (or your support team) much. You may want to wrap a try/catch around the call to %FromJSON() so that you can return a status that provides a little more insight:

    ...

    Try {
        set tJSON = {}.%FromJSON(pInput)
    } Catch {
        Return $$$ERROR($$$GeneralError,"Badly formed JSON")
    }

...

    Return $$$OK
 }
Jeffrey Drumm · May 19, 2019 go to post

The only reason I can think of doing it the way it was done is that there was no expectation by the original author of the code that the JSON object would be larger than 32KB. It's also possible that he/she was unfamiliar with the types of objects that %FromJSON() works with and always assumed it was a simple string ... which is what the ReadLine() method returns.

Here's the documentation for for the %FromJSON() method.

Jeffrey Drumm · May 19, 2019 go to post

The ReadLine() method of %FileCharacterStream defaults to a length of 32K.

Have you tried simply passing the stream object, pInput, as the argument to %FromJSON()?

Jeffrey Drumm · Apr 16, 2019 go to post

Probably something like this (I'm not sure of the full name of the osuwmc.PatientBilling... class, and you probably want to do some sort of exception handling or at least error checking):

       setcallrequest=##class(osuwmc.PatientBilling.DataStructures.SelectResearchID).%New()

       setcallresponse=##class(EnsLib.SQL.Snapshot).%New()

       setcallrequest.ResearchID=context.RschMRN

       doprocess.SendRequestSync("PatientBillingDBPoll", callrequest, .callresponse)

       setcontext.ClarityRS=callresponse

The default behavior of SendRequestSync is to never timeout, but you can always include a -1 as the 4th parameter to be a bit more descriptive.

Jeffrey Drumm · Apr 16, 2019 go to post

It's explained pretty well in the documentation. It allows the synchronization of the call to be retried repeatedly when the timeout is reached. You specify the <call> to sync, the timeout, and whether resync (retry) is allowed. I'm not sure if there's any benefit to using it over the SendRequestSync() method, though.

Jeffrey Drumm · Apr 12, 2019 go to post

Hi Scott,

I've accomplished this in the past by calling the SQL Operation with the SendRequestSync() method in a <code> action rather than using the <call> action. The default is to wait until the response is received.

This will be problematic if you must maintain FIFO (thus setting the Pool Size to 1), since the BPL will stop processing until the response is received. And increasing the pool size could have undesirable side effects other than loss of FIFO ... so you're likely to see horrible message latency at times given the metrics you're reporting.

EDIT: Stelios of course beat me to it ... I stepped away from the keyboard before hitting the Publish button and only realized it some hours later laugh

Jeffrey Drumm · Apr 12, 2019 go to post

If each Encounter element in the document contains all of the data necessary to create your individual HL7 messages, you could potentially use the EnsLib.XML.Object.Service.FileService (or FTPService) class for a Business Service that will "chunk" the document into individual Encounter documents. Those would then be mapped to HL7 via a routing rule and DTL ... no BPL required. You'll need to create an Encounter class that matches the Encounter element structure; this will be used subsequently for creating routing rules and DTLs.

Jeffrey Drumm · Apr 9, 2019 go to post

I guess it's worth trying, but I have a gut feeling that it won't work. If it doesn't ...

You could accomplish this in a BPL, but you would have to build the response logic from the BO along with the filtering/routing logic.

You could alternately create a custom BO that simply generates NACKs and set the send target in your otherwise to that ... just override the OnMessage() method:

Class HICG.HL7.Operation.NackFactory Extends Ens.BusinessOperation
{

Parameter INVOCATION = "Queue";

Method OnMessage(pRequest As EnsLib.HL7.Message, Output pResponse As EnsLib.HL7.Message) As %Status
{
    Set pResponse = ##class(EnsLib.HL7.Message).%New()
    Set pResponse.DocType = "2.3.1:ACK"
    Do pResponse.SetValueAt(pRequest.GetValueAt("MSH"),"MSH")
    Do pResponse.SetValueAt("ACK","MSH:9")
    Do pResponse.SetValueAt("MSA|AE|"_pRequest.GetValueAt("MSH:10")_"|No Destination","MSA")
    Do pResponse.%Save()
    Quit $$$OK
}

This is obviously not robust code, but it does generate a NACK as a response object for every message sent to it, and if the BP is configured to forward responses from the Production BO created based on this, you'll get what you're looking for.

Jeffrey Drumm · Apr 5, 2019 go to post

Ah, I see. For some reason I assumed you were attempting to NACK based on un-routed messages.

Did you configure the Response From value in the Business Process to specify the Business Operation from which you want the NACKs relayed?

Jeffrey Drumm · Apr 5, 2019 go to post

Are you attempting to do this with a routing rule, a BPL, or some other custom BP type? I don't believe you can do what you want directly with a routing rule.

Jeffrey Drumm · Mar 30, 2019 go to post

I think you really only needed to log out and back in again; at least that worked for me.

Jeffrey Drumm · Mar 28, 2019 go to post

Eduard, are you referring to the Priority property of the Ens.MessageHeader class? That seems to be used exclusively for marking the message for Async vs. Synchronous delivery.

The need for message prioritization within a single message flow (one that would otherwise be FIFO) is valid and has been addressed in other integration products. One healthcare use case that I've encountered in multiple implementations is that the downstream system uses a session-oriented connection ("always on" MLLP) and receives both ADT and lab orders over the same connection. As the patient demographic data and "stat" lab orders should always be prioritized over routine order messages, a "high" priority value would be set for those messages during in-engine routing/transformation processing and would be a primary selection criteria for the process (eWay/Connection Point/Communication Client) to deliver it to the receiving system.

Given modern hardware and software, latency caused by the integration engine itself is rarely an issue. However, not all applications can receive messages as fast as integration engines can send them ...

Jeffrey Drumm · Mar 27, 2019 go to post

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.VanMoland @Donna Goodall:

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

Jeffrey Drumm · Mar 27, 2019 go to post

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

Jeffrey Drumm · Mar 21, 2019 go to post

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.

Jeffrey Drumm · Mar 13, 2019 go to post

You're welcome, but the best way to thank me is to click the "Accepted Answer" button laugh

Jeffrey Drumm · Mar 13, 2019 go to post

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: