Jeffrey Drumm · Nov 29, 2022 go to post

DocTypeCategory and DocTypeName are populated based on the contents of DocType. The DocType property can be changed even though the message is immutable.

TypeVersion is populated based on the value of the MSH:12 field in the message body. If you're attempting to modify the properties of an inbound message received from a business service, I don't think you'll be able to change TypeVersion with "Existing" set in the DTL editor because you can't modify MSH:12.

Are you working with messages newly arrived through a service that haven't undergone any prior transformations?

Jeffrey Drumm · Nov 1, 2022 go to post

%Net.MailMessage has an IsHTML property; set that to a value of 1 and you can use HTML in your WriteLine() calls to format your text.

Jeffrey Drumm · Oct 21, 2022 go to post

Hi Blake,

This might get you started in the right direction:

Set tRuleName = "<rulename>"
Set tTarget = $ORDER(^Ens.Rule.Targets(tRuleName,""))
Set tArr = 0
Set tCnt = 1
While tTarget '= ""
{
    Set tArr(tCnt) = tTarget
    Set tTarget = $ORDER(^Ens.Rule.Targets(tRuleName,tTarget))
    Set tArr = tCnt
    Set tCnt = tCnt + 1
}

Replace <rulename> with the name of the rule as it appears in the router configuration pane.

Jeffrey Drumm · Oct 12, 2022 go to post

Sorry, I was never able to solve this before I finished up with that client. If I have some time I will experiment with one of my local environments.

Jeffrey Drumm · Sep 26, 2022 go to post

There's a self-paced training session on BPLs here; it should help get you started.

Can you describe the routes that the ADT and ORM messages are taking to get to the downstream system? Are they coming in through different services and/or being delivered downstream over different operations?

Jeffrey Drumm · Sep 23, 2022 go to post

A BPL is  a Business Process class, so you would add a new Process to your production and select the name of the BPL you created as the class (rather than, say, a routing engine).

Jeffrey Drumm · Sep 16, 2022 go to post

With some help from a fellow DC member, I wrote the method below. Its intent is to support auto-resolution of managed alerts:

/// Returns the connection status ("AdapterState") of the Business Service or Operation
/// named in <var>pItemName</var>
ClassMethod GetConnectionStatus(pItemName As %String) As %String [ Language = objectscript ]
{
    Set tStatement = ##class(%SQL.Statement).%New()
    Set tStatus = tStatement.%PrepareClassQuery("Ens.Util.Statistics","EnumerateJobStatus")
    If $$$ISERR(tStatus)
    {
        Return "Error in Status Query: "_$system.Status.GetErrorText(tStatus)
    }
    Set tRS = tStatement.%Execute(pItemName)
    If tRS.%SQLCODE = 0
    {
        Do tRS.%Next()
        Return tRS.%Get("AdapterState")
    }
    Return "Status not Found"
}
Jeffrey Drumm · Aug 8, 2022 go to post

Here's a little code snippet that the Management Portal uses to get the Arbiter state:

	Set state = $SYSTEM.Mirror.ArbiterState()
	Set thisConnected = $SELECT($ZB(+state,+$$$ArbiterConnected,1)'=0:1,1:0)
	Set otherConnected = $SELECT($ZB(+state,+$$$ArbiterPeerConnected,1)'=0:1,1:0)
	
	If 'thisConnected {
		Set stateString = $$$Text("This member is not connected to the arbiter")
	} ElseIf 'otherConnected {
		Set stateString = $$$Text("Only this member is connected to the arbiter")
	} Else {
		Set stateString = $$$Text("Both failover members are connected to the arbiter")
	}

You'll need to add an Include statement for %syMirror to use the $$$Arbiter* macros.

Note that the ArbiterState() method is undocumented, and its behavior may change in future releases.

Jeffrey Drumm · Aug 5, 2022 go to post

Does SYS.Mirror.GetFailoverMemberStatus() give you what you want? It has to be executed from %SYS.

%SYS>set sc=##class(SYS.Mirror).GetFailoverMemberStatus(.pri,.alt)

%SYS>zw pri
pri=$lb("SERVERA.FOO.BAR.ORG/STAGE","SERVERA.foo.bar.org|2188","Primary","Active","172.31.33.69|1972","SERVERA.foo.bar.org|1972")

%SYS>zw alt
alt=$lb("SERVERB.FOO.BAR.ORG/STAGE","SERVERB.foo.bar.org|2188","Backup","Active","172.31.33.70|1972","SERVERB.foo.bar.org|1972")
Jeffrey Drumm · Jul 21, 2022 go to post

Not sure why yours is showing OpenSSL v3. I'm running IRIS for Health 2022.1.0.209.0 on a Ubuntu 20.04.4 LTS (GNU/Linux 5.4.0-121-generic x86_64) physical host and I had no issues with installation.

My guess is that it isn't really complaining about openssl, but that libssl isn't at least version 1.1.1.

I'd try running (as root):

# apt install openssl
# apt install libssl1.1

These commands should install pre-compiled binaries. The first one should automatically install openssl 1.1.1f and the 2nd the same version of the libssl libraries.

And yes, while I haven't specifically used VirtualBox, I am a long-time user of VMWare on multiple platforms, with multiple Linux guests and versions of Caché/IRIS. Virtualization has, so far, very rarely been an issue.

Jeffrey Drumm · Jul 20, 2022 go to post

It's a bug, and InterSystems is aware. It's still present in 2022.1, but I expect it will be fixed in an upcoming release.

For now, you'll need to change the resource associated with the database(s) to %DB_%DEFAULT (or whatever you prefer) after the creation of the namespace. That can be done through the management portal, under System Administration | System Configuration | Local Databases.

Jeffrey Drumm · Jul 13, 2022 go to post

At some point I encoutered a problem that required I also run this command:

setsebool -P httpd_unified 1

I can't remember what it fixed, but I do know by the fact that I made a note of it that it fixed something ... laugh

Jeffrey Drumm · Jul 13, 2022 go to post

EDIT: Sorry, didn't notice that Alex Woodhead had already provided this answer ...

If SELinux is enabled, you may need to run the following commands as root:

# semanage fcontext -a -t httpd_sys_rw_content_t '/opt/webgateway/logs/CSP.log'
# restorecon -v '/opt/webgateway/logs/CSP.log'

Note that the '#' character represents the root account prompt and is not part of the command.

Jeffrey Drumm · Jul 6, 2022 go to post

I've been troubleshooting issues with WebTerminal that resulted in either long delays after login before the IRIS prompt displayed, or license count exhaustion (which generated the lost connection errors you've experienced). The change that made the most immediate difference was to switch the MPM module used by Apache from event to worker. The latter allows pre-allocation of resources that seem to better support websockets.

On RedHat Linux, the MPM module is configured through /etc/conf.modules.d/00-mpm.conf:

Replace

LoadModule mpm_event_module modules/mod_mpm_event.so

with

LoadModule mpm_worker_module modules/mod_mpm_worker.so
ServerLimit     20
StartServers    10
MaxRequestWorkers 500
MinSpareThreads 75
MaxSpareThreads 250
ThreadsPerChild 25

The values above are likely overkill for many purposes; they're borrowed from @Mark Bolinsky's excellent article on HS web server configuration found here: https://community.intersystems.com/post/apache-httpd-web-server-configuration-healthshare
 It does increase httpd's memory footprint by 30-40% over the "stock" configuration, but unless you're very tight on resources it shouldn't be an issue.

I also heard from @John Murray that there will be another WebTerminal release shortly that addresses the IRIS/Caché version interrogation issue along with being kinder to your license count laugh

Jeffrey Drumm · Jun 23, 2022 go to post

I see that the extension documentation discusses the issue I mentioned in my previous comment. So it looks like this may not work with a standalone web gateway server that hosts multiple instances using the conventional implementation method (instance prefixes). I'd love to solve this issue, but it's a bit outside of my wheelhouse.

Jeffrey Drumm · Jun 23, 2022 go to post

I wanted to try it, but it appears the bug/incompatibility with instance prefixes is still on the issues list (at least for the main branch of WebTerminal). Does this extension include a fix/workaround for that?

Jeffrey Drumm · Jun 21, 2022 go to post

HL7 Message bodies are also referenced from the IO Log tables (ACKs/NAKs). Technically, their headers exist, but not in Ens.MessageHeader ;)

Jeffrey Drumm · Jun 8, 2022 go to post

Thanks, @Jolyon Smith, that does give me something to go on. This is the error I'm seeing on each connection attempt:

Error: unable to verify the first certificate
    at TLSSocket.onConnectSecure (node:_tls_wrap:1530:34)
    at TLSSocket.emit (node:events:390:28)
    at TLSSocket._finishInit (node:_tls_wrap:944:8)
   at TLSWrap.ssl.onhandshakedone (node:_tls_wrap:725:12)

The cert shows no issues when using Chrome, but what I've read in researching this error is that AV software may be a contributor. Unfortunately, I don't have the ability to turn it off on the system I'm testing with, so I'll need to work with the customer's PC support team to see if that makes a difference. I've already tried setting http.proxyStrictSSL and http.systemCertificates to false, and set http.proxySupport to off.

Jeffrey Drumm · Jun 6, 2022 go to post

Yes, in retrospect I should have realized that the DocType Structure was what the constraint required rather than the calculated value in the Name property. Sorry for any head-scratching I might have caused wink

Jeffrey Drumm · Jun 6, 2022 go to post

You shouldn't need to ...

DEV>Set msg = ##class(EnsLib.HL7.Message).%OpenId(29485840)

DEV>Write msg.Name
ADT_A11
DEV>Write msg.GetValueAt("MSH:9")
ADT^A11
DEV>Do msg.SetValueAt("A08","MSH:9.2")

DEV>Write msg.GetValueAt("MSH:9")
ADT^A08
DEV>Write msg.Name
ADT_A08

But I guess it wouldn't hurt wink

Is it possible that there's an action somewhere in the DTL that clears MSH:9?

Jeffrey Drumm · Jun 6, 2022 go to post

The Name property in a message object is actually calculated from the contents of MSH:9 and is what the constraint editor evaluates. Is MSH:9 in the target message being set using some unconventional method in the Target message?

EDIT: The constraint editor uses the DocTypeName, not the Name property, as Scott points out in followup comments.

Jeffrey Drumm · Jun 6, 2022 go to post

If you're using New as the Create selection in the Transform tab, the target message should be set to the Target Doc Type specified in that same tab. If you're using Copy or Existing, the Target's Doc Type will be the same as the Source's.

Are you seeing instances where the Doc Type is not set at all, or is it set to the wrong Doc Type?

Jeffrey Drumm · Jun 2, 2022 go to post

There are a number of examples at Defining and Using Stored Procedures | Using InterSystems SQL | InterSystems IRIS Data Platform 2021.2. The SP I wrote for fetching the message body using its object ID probably won't be much help, since it doesn't actually execute an SQL query ... it's just an ObjectScript classmethod that returns a string, and it's called for every row returned in the containing query. It's available at https://hl7spy.ca/downloads/HICG_HL7.xml if you want to look at it.

Jeffrey Drumm · Jun 1, 2022 go to post

I've written a query for HL7Spy's SQL Loader that extracts ACKs/NAKs from the IOLog:

Here's the SQL in text form. The method HICG.GetMsg() is an ObjectScript stored procedure written specifically for HL7Spy that returns the message body as a stream for delivery via ODBC. You could adapt the SQL for use with the %SQL.Statement class to return a result set containing the message body IDs, Next() your way through that, open each message with %OpenId() and write it out to a file. Getting just the NAKs will require a bit of message parsing.

SELECT  
  head.ConfigName As ConfigName,
  body.DocType As DocumentType,
  body.Name As BodyName,
  HICG.GetMsg(body.ID) As Message
FROM
  Ens_Util.IOLogObj head
INNER JOIN
  EnsLib_HL7.Message body
ON
  head.InObjectId = body.%ID
WHERE  -- specify start and end time for search here
  head.ID >= (SELECT TOP 1 ID FROM Ens_Util.IOLogObj WHERE TimeReceived >='2022-06-01 00:00:00.000' ORDER BY TimeReceived ASC)
  AND head.ID <= (SELECT TOP 1 ID FROM Ens_Util.IOLogObj WHERE TimeReceived <='2022-06-02 00:00:00.000' ORDER BY TimeReceived DESC)
  AND head.IsInbound = 0
  AND head.InObjectClass = 'EnsLib.HL7.Message'
  AND head.ConfigName = 'To_Outbound_Operation' -- substitute operation name here
Jeffrey Drumm · Apr 29, 2022 go to post

Assuming you cut and pasted from your DTL, the double-quote characters around the 2nd dash are incorrect. They appear to be the distinct open and close quote characters that Word automatically substitutes for the "standard" double-quote character:

set            tSSN                                                                  source.{PID:SSNNumberPatient}                               
set            target.{PID:SSNNumberPatient}                  $E(tSSN,1,3)_"-"_$E(tSSN,4,5)_-_$E(tSSN,6,9)

Jeffrey Drumm · Apr 29, 2022 go to post

Or you could use $E()! laugh

To @John Klahn, My reason for suggesting the classmethod is that this type of thing is something that's done somewhat frequently, so having a reusable method can be a timesaver.

Alas, it does take a bit of investment in time to learn ObjectScript and Studio, but it can be very beneficial to your productivity even though you spend most of your time working on integrations through the management console.

Jeffrey Drumm · Apr 28, 2022 go to post

If you create the following class in IRIS Studio, a "FormatSSN()" function will be available in the DTL editor's expression editor, and you can use it in your set rules:

Class Misc.Util.StringFunction Extends Ens.Rule.FunctionSet
{ 
ClassMethod FormatSSN(pStr As %String) As %String
{
   Set tStr = $ZSTRIP(pStr,"*AWP")
   Return $E(tStr,1,3)_"-"_$E(tStr,4,5)_"-"_$E(tStr,6,9)
}
}

The method strips all non-numeric characters from the value passed to it, then formats it per the SSN format. It doesn't verify that the proper number of digits are present, but that's something that can be easily added.

In a DTL action, it would look like this:

Jeffrey Drumm · Apr 27, 2022 go to post
<assign value='$PIECE($ZCVT(source.{ibex_medical_chart.patient_info.admdoc.name},"i","XML"),",",1)' property='target.{PV1:AdmittingDoctor(1).FamilyName}' action='set' />
<assign value='$PIECE($ZCVT(source.{ibex_medical_chart.patient_info.admdoc.name},"i","XML"),",",2)' property='target.{PV1:AdmittingDoctor(1).GivenName}' action='set' />

Or a bit more efficiently:

<assign value='$ZCVT(source.{ibex_medical_chart.patient_info.admdoc.name},"i","XML")' property='tFullName' action='set' />
<assign value='$PIECE(tFullName,",",1)' property='target.{PV1:AdmittingDoctor(1).FamilyName}' action='set' />
<assign value='$PIECE(tFullName,",",2)' property='target.{PV1:AdmittingDoctor(1).GivenName}' action='set' />

Note that this isn't actually replacing the comma character so much as it's splitting the full name value supplied in the source path on that character and assigning the individually extracted values to their associated HL7 components in the Admitting Doctor field.

Jeffrey Drumm · Apr 27, 2022 go to post

Assuming Caché Terminal is working, you should be able to access these settings via the ^SECURITY menu.

Sign on via Terminal with an administrative account (SuperUser,  cacheusr,  _SYSTEM) using the password you provided during installation.

Change to the %SYS namespace using the command zn "%SYS"

Issue the command do ^SECURITY at the %SYS> prompt.

Choose System Parameter Setup

Choose Edit system options

Press Enter until "SSLServer connect type" displays with 3 options

Choose 1 to disable SSL (the current setting may be displayed near the end of the prompt)

Press Enter to step through the remaining prompts, and confirm changes to the security parameters.

Exit from the menu, and type h followed by Enter to exit the session.