Jeffrey Drumm · Apr 18, 2023 go to post

Well, I learned something new too! I've always created classes in the Explorer pane and never realized that the Interoperability class types were templated out as file types. As someone who works primarily with Health Connect/IRIS for Health/HealthShare, that's pretty cool!

Jeffrey Drumm · Apr 18, 2023 go to post

I don't think so.

UpdateProduction (I think that's what you meant) is attempting to obtain state information for all of the business hosts and likely won't complete until they're all down. Calling it at the end should still be faster than having it enabled for each EnableConfigItem() call.

The reality is that you appear to have a lot of processes that are dependent on polling rates and/or getting the appropriate responses back from external systems on notification they're terminating connections.

If you need to shut down the interfaces fast, you really can only do it at the expense of graceful connection termination.

Have you considered creating separate namespaces and compartmentalizing interfaces to keep your productions at a more manageable size? Business hosts in multiple smaller productions benefit from parallelism when performing administrative tasks like stopping/starting interfaces in bulk.

Jeffrey Drumm · Apr 18, 2023 go to post

That should probably be a choice for completeness' sake.

But you can create an ObjectScript class by right-clicking the server or one of its packages in the Explorer pane and entering the package/class name with a .cls extension. Use slashes rather than periods as package/class delimiters (if you use periods, it may display differently in Explorer until you refresh the window).

Jeffrey Drumm · Apr 17, 2023 go to post

What's the use case for this function?

ISC's Interoperability business rule editor has some quirks; it does not allow the passing of variables by reference to custom methods and that will cause objects to be passed as the OREF string in some cases. I was wondering if there was a way to work around that by obtaining another reference to the object by its OREF string, since that's what i ended up with.

I haven't tried this yet ... it may not work for the specific scenario I encountered. But this discussion has certainly helped me understand how things work under the covers a bit better.

Jeffrey Drumm · Apr 16, 2023 go to post

Hah! As always, you're THE MANyes

Now, why isn't $ZOBJREF() in the documentation? I sure would have noticed that ...

Jeffrey Drumm · Apr 16, 2023 go to post

But is there method/function that will create a reference to the instantiated object from the sequence number and classname?

So ... no? wink

Jeffrey Drumm · Apr 16, 2023 go to post

Thanks! I figured it was something like that. But is there method/function that will create a reference to the instantiated object from the sequence number and classname?

Jeffrey Drumm · Apr 15, 2023 go to post

So I forgot that there's a Pad() method in the DTL function list that would likely work better for your purposes than $EXTRACT() and $JUSTIFY(). You can use it to zero or space fill the fields to the required width. The first argument is the value to pad, the 2nd the width (positive numbers for pad right, negative for left), and the 3rd is the pad character to fill with.

Your update to the requirements is incomplete; it doesn't specify what, if anything, goes in the 2nd (and subsequent) row(s) of the output after the ItemCodeExternal.Identifier value, whether the fractional value is in the Quantity field is right or left justified zero-filled, or whether the UnitofMeasure and DateNeeded values are padded to make line length consistent across all records.

Here's an example of what it might look like and will need to be adjusted to accommodate your vendor's spec:

The code rules that write the records to the stream would need to be adjusted to eliminate the "|" delimiters and insert the renamed/added variables:

This should get you to where you need to be.

Jeffrey Drumm · Apr 14, 2023 go to post

Hi Doug,

Looks like only two lines need to be changed in what I posted previously; they're numbered 20 and 26 in the screen shot. You'd use a combination of string concatenation and $EXTRACT(), along with $JUSTIFY() to line things up according to the specification. Alternately you can set the variables to the justified/aligned/padded version of the values extracted from the HL7 message and then just concatenate them without the pipe characters in lines 20 and 26.

If I have time over the weekend I'll take a stab at it, but if you figure it out beforehand, please post your solution here.

Jeffrey Drumm · Apr 14, 2023 go to post

I went ahead and created a DTL that appears to do what you requested and does not require a custom File Operation to work; It assumes you're using EnsLib.File.PassthroughOperation as the outbound operation class.

The filename is created using the value set for target.OriginalFilename in Ens.StreamContainer in the DTL, so you could base it on something from the HL7 message itself or just set it to a static value (as I've done). You can use date/time tokens in the outbound operation's File Name field to aggregate multiple messages per file, or just let it create uniquely named files for each message with the default pattern.

Here's the DTL Configuration:

And the rules:

To test, I created a HL7 file with repeating ORC groups based on the sample provided in your post, but the DTL will work whether it's repeating or not:

The Filename pattern I used in the outbound operation:

This file was created:

And contained this output:

Hope this helps.

Jeffrey Drumm · Apr 13, 2023 go to post

If the 3rd argument to EnableConfigItem() is 1, the method will update the production on each call. That can be time consuming, so it might be worth considering setting that to 0 and then call Ens.Director.UpdateProduction() after the loop completes.

The other issue is that simply disabling a Production Config Item will only shut it down at the next polling interval or completion of the currently-processing request. This is a generally a good thing, but can take time for some interfaces.

For @Eduard Lebedyuk's benefit ... the regex @Scott Roth referred to is most likely to allow the selective shutdown of interfaces by name pattern to accommodate outages/upgrades for external systems. Alternately to be able to disable inbound interfaces before outbound interfaces to prevent queued messages.

Jeffrey Drumm · Apr 13, 2023 go to post

Hi Doug,

Are there multiple ORC groups with a subordinate RQD segment that need to be included in the same output file, thus requiring a loop over the repeating ORC group?

And will the resulting file contain the data from multiple HL7 messages, or are you expected to produce one file per HL7 message?

Jeffrey Drumm · Apr 9, 2023 go to post

DBeaver offers this via Export Data, using the SQL option. Seems to work fine with IRIS. Doesn't seem to be an IRIS native feature, though.

Jeffrey Drumm · Apr 9, 2023 go to post

or

[SQL]USER>> run filename.sql

You can also save the current query

[SQL]USER>>save filename.sql
Jeffrey Drumm · Mar 31, 2023 go to post

Well I guess there IS a setting (thanks, @Eduard Lebedyuk!) laugh

The parameter Undefined specifies the behavior when ObjectScript attempts to fetch the value of a variable that has not been defined. The value of Undefined may be 0, 1, or 2:

  • 0 - Always throw an <UNDEFINED> error. (default)
  • 1 - If the undefined variable has subscripts, return a null string, but if the undefined variable is single-valued, throw an <UNDEFINED> error.
  • 2 - Always return a null string.

You can change that setting in System Administration | System Configuration | Additional Settings | Compatibility.

Jeffrey Drumm · Mar 30, 2023 go to post

What does your method's argument list look like? If it's something like this:

Method Encrypt(pVarA As %String = "", pVarB As %String = "", pVarC As %String = "") As %Status

The pVar* variables above should automatically default to empty strings when the method is called as provided in your first example.

I'm not aware of any system setting that would affect the behavior of unsupplied values for method arguments when they're not defined with an initial value (unlike those in my snippet above).

That doesn't mean that there isn't one, though ...

Jeffrey Drumm · Mar 30, 2023 go to post

Not sure what version of Caché or IRIS you're on; for future reference it's helpful to include that information. In IRIS 2021.2, you can do this from the IRIS SQL Shell:

JEFF>do $system.SQL.Shell()
SQL Command Line Shell
----------------------------------------------------
The command prefix is currently set to: <<nothing>>.
Enter <command>, 'q' to quit, '?' for help.

[SQL]JEFF>>set displaypath /home/jeff/tmp/
displaypath = /home/jeff/tmp/

[SQL]JEFF>>set displayfile sqlout
displayfile = sqlout

[SQL]JEFF>>set displaymode csv
displaymode = csv

[SQL]JEFF>>set selectmode display
selectmode = display

[SQL]JEFF>>select top 100 * from Ens_Util.Log
13.     select top 100 * from Ens_Util.Log

/home/jeff/tmp/sqlout.csv
/home/jeff/tmp/sqloutMessages.txt

statement prepare time(s)/globals/cmds/disk: 0.0002s/6/831/0ms
          execute time(s)/globals/cmds/disk: 0.0035s/467/20822/0ms
                          cached query class: %sqlcq.JEFF.cls115
---------------------------------------------------------------------------

The default delimiter is comma, but you can change that. For example, the tab character:

[SQL]JEFF>>set displaydelimiter = $C(9)
Jeffrey Drumm · Mar 30, 2023 go to post

A Business Process Component is the BPL analogue of a subroutine or function and is called exclusively from a BPL. The idea is that they can be reusable components applicable to potentially multiple, different business processes. I don't think that's really what you're looking for.

If you don't have a FIFO concern with this database processing and are thinking that increasing the number of parallel processes performing these database activities might improve performance, you could try increasing the pool size for the BP.

Jeffrey Drumm · Mar 27, 2023 go to post

There are methods for dealing with what are essentially embedded streams in HL7 Objects. See the methods GetFieldStreamRaw() and StoreFieldStreamRaw() in class EnsLib.HL7.Message; these are useful for copying streams from one message to another. If the need is to extract the Base64 stream as a binary stream for writing to a file, there's also GetFieldStreamBase64() in the same class; the stream obtained from it can be used with file-based streams to write to a disk file.

Jeffrey Drumm · Mar 22, 2023 go to post

I'm not sure whether this will work in Ensemble 2018.1, but it does seem to work fine in IRIS for Health Interoperability 2022.2.

I'm testing with a simple JSON file that looks like this:

{
    "mrn": 12345678,
    "name": "Johann Smythe",
    "firstname": "Johann",
    "lastname": "Smythe",
    "dob": "1989-03-21 14:20:00",
    "phone": "(555) 555-4917",
    "mobile": "(555) 555-6401",
    "email": "johann@smythe.com",
    "address": "123 Anystreet St",
    "city": "Anytown",
    "state": "ME",
    "zip": "04121"
}

I've used the File Passthrough Service (EnsLib.File.PassthroughService) to read the JSON document into a stream, message class Ens.StreamContainer. Because this isn't an HL7 object, my router is based on the "General Message Routing Rule" rule type, and my constraint consists of the source service name with a message class of Ens.StreamContainer.

In the DTL called by the send action, I use Ens.StreamContainer as the source message type. The target message type is EnsLib.HL7.Message with whatever Document Category and Type is needed.

The first rule in the DTL is this:

After setting a number of default values for the target HL7 message (Event Type/Trigger, Date/Time of message, etc.) I populate the PID fields as follows:

And I now have an HL7 message created from JSON.

This isn't going to work with a batch of patient records in a JSON array; you'd need to create a BPL to process that. But for input that consists of a simple structure like the example I used, you can accomplish what you need without building a custom service or creating a BPL.

Jeffrey Drumm · Mar 21, 2023 go to post

Are you using LDAP for authentication? I seem to remember running into this when the web applications created as part of enabling Ensemble/Interoperability weren't set to support LDAP.

Compare the settings for the web applications created for your new namespace in Security | Applications | Web Applications with those from other (working) Ensemble-enabled namespaces.

Jeffrey Drumm · Mar 18, 2023 go to post

For those that use Interoperability/HealthConnect, nc/netcat is also an excellent tool for verifying that remote ports are accessible for HL7 MLLP, HTTP or other protocols that require a TCP socket client connection.

And while this thread is specifically for Unix/Linux, there's a Windows PowerShell analogue named Test-NetConnection (alias tnc) that provides a subset of nc's features.

Jeffrey Drumm · Mar 7, 2023 go to post

Something like this, perhaps?

Class User.Util.StringFunctions Extends Ens.Util.FunctionSet
{

ClassMethod ReReplace(pStr As %String, pPat As %String, pRepl As %String = "") As %String
{
    Set tStrt = $LOCATE(pStr,pPat,,tEnd) - 1
    // in case the pattern isn't found, return source string
    Return:(tStrt < 0) pStr
    Set tPrefix = $EXTRACT(pStr,1,tStrt)
    Set tSuffix = $EXTRACT(pStr,tEnd,*)
    Return tPrefix_pRepl_tSuffix
}

}
USER> set mystr = "REASON->Blood(1.23)"
USER> set newstr = ##class(User.Util.StringFunctions).ReReplace(mystr,"->\w+")
USER> write newstr
REASON(1.23)
USER> set altstr =  ##class(User.Util.StringFunctions).ReReplace(mystr,"->\w+","-CODE")
USER> write altstr
REASON-CODE(1.23)
Jeffrey Drumm · Mar 5, 2023 go to post

The included IsRecentManagedAlert() method expects a recent alert to have 100% identical SourceConfigName and AlertText values. Probably not suitable for your application.

Unfortunately, I haven't run into a scenario such as you describe where errors from multiple business hosts must be aggregated into one.

I can envision a solution where you would identify this group of business hosts under a single Managed Alert Group, log activity for alerts in that group to a table or global via Ens.Alert's routing rule, and check the table/global for prior activity from that alert group in the in that same rule for the desired time span. Matches could then be suppressed.

Since Managed Alert Groups aren't a property of Ens.AlertRequest, you would need to interrogate the business host (production item) for its membership from the rule.

 So you'd need to create a table/global, write some methods (in a class extending Ens.Rule.FunctionSet) to query your custom table/global for prior alerts and log the current alert, then configure a routing rule to check for the existence of prior activity on the selected Alert Group, log the current activity, and suppress or send the alert based on your criteria.

Jeffrey Drumm · Mar 1, 2023 go to post

Check the Learning link at the top of the page. All of the official InterSystems training courses can be found there. Many are free.

Jeffrey Drumm · Feb 10, 2023 go to post

Is it a password user?

Yes

Try disabling OS Delegated auth.

That worked! Thanks laugh

Jeffrey Drumm · Feb 10, 2023 go to post

Does the version of Studio you're using match the version of Ensemble/Caché? Older versions of Studio will fail to connect to newer versions of Caché/IRIS.

Jeffrey Drumm · Dec 8, 2022 go to post

I tried running it with Wine a few years ago and it almost worked. Perhaps Wine has improved since then, but I'm now firmly in the VS Code camp laugh