go to post Jeffrey Drumm · Aug 28, 2023 You can explicitly exclude '%' schemas with the pattern "\%*" in the Exclude list:
go to post Jeffrey Drumm · Aug 21, 2023 As part of the encryption negotiation process, there's an exchange of supported cypher suites between the client and server. If there's no match, no connection can be established. No need to force a specific cypher site; all available should be presented by the client during connection negotiation. If upgrading to a current version of HealthShare/Health Connect is not an option, you could script the transfers outside of the production (batch/powershell/Python/Perl script running under Windows' Scheduler or called from ObjectScript in a Scheduled Task via $ZF(-100) ) and then use a File service/operation to pick them up for processing or drop them off for delivery.
go to post Jeffrey Drumm · Jul 24, 2023 I had previously posted a comment with a solution here, but received a request to turn it into a question/article. You can find it at this link.
go to post Jeffrey Drumm · Jul 20, 2023 I've worked with a number of integration solutions over the last few decades and not a single one of them would work with this vendor's perverse interpretation of HL7 communications out-of-the-box. Health Connect / HealthShare can accommodate pretty much anything if you're willing to dip your toes into ObjectScript and write custom services and operations. But the OOB classes won't handle the vendor's requirements without subclassing/extending. Acknowledgements There are certainly variances in the types of acknowledgements received and even cases where acknowledgements are received out-of-band (Sunrise Clinical Manager, I'm looking at you). I've even seen implementations where multiple HL7 messages are sent, streamed in a batch, with envelope characters wrapped around the whole batch rather than the individual messages. In that case no ACK was expected. I wrote a custom service to handle that one. Fundamentally, Health Connect is a request/response messaging system and violating that design presents some interesting challenges. Receiving the ACKs back asynchronously over the same connection is theoretically possible, but I wouldn't encourage the vendor to work that way ... every single future customer will be problematic for them. Message Control IDs I've never seen an engine that increments the message control ID automatically, although it's something that can be coded/customized in multiple solutions. The message control ID is defined by the sending application, not the middleware; setting the MSH:10 in the engine is usually an extraordinary measure. HL7.org defines it as such: This field contains a number or other identifier that uniquely identifies the message. If the same message was re-sent with a different MSH:10 value, it would not be uniquely identified. In my opinion, at least.
go to post Jeffrey Drumm · Jul 11, 2023 Depends on your platform (although that may have changed since the last time I configured an LDAP setup). With Caché/IRIS running on *nix variants, the only option is STARTTLS, which is encrypted but uses the "standard" port 389. With Windows, I believe "LDAP over SSL" (aka LDAPS) is also an option, on port 686 by default. Both will require that whatever certificate is served is valid for the load balancer. This is usually accomplished via a certificate Subject Alternative Name value.
go to post Jeffrey Drumm · Jul 4, 2023 Not with this code, no. I interpreted your original request literally, assuming that you wanted to send the email directly from within a custom service. If you wish to have an Email operation in the production that handles the delivery of messages from routing rules, that's a bit more work to create, but in the end more versatile and easier to support. If you don't care about having an operation, you could create a class that extends Ens.Rule.FunctionSet that wraps the original class in such a way that you can send email from a rule: Class User.Util.FunctionSet Extends Ens.Rule.FunctionSet { ClassMethod SendEmail(pToAddr As %String, pSubject As %String, pMessageBody As %String) As %Status { Set tMail=##class(User.Mail).%New() Set tSC = tMail.Send(pToAddr, pSubject, pMessageBody) } } You could then call it from a rule (the assign simply lets you call it and optionally do something with the returned status):
go to post Jeffrey Drumm · Jun 20, 2023 The RecordMap services have options for handling errors: I'm not sure whether they do actual datatype validation on fields in the RecordMap (wouldn't make much sense to let you specify datatypes without supporting validation though). But if your extra field didn't trigger some sort of warning or error in the Event Log 🤷♂️
go to post Jeffrey Drumm · Jun 1, 2023 @Eduard Lebedyuk is correct (yeah, he's always correct 😁), you can't use a variable for the target in a business rule. You can do this in a custom BP (COS or Python) or a BPL-based BP, though. The BPL <call> action specifically supports a context variable as a destination: The variable would be assigned the name of the BH to send to prior to invoking the call.
go to post Jeffrey Drumm · May 31, 2023 It's not clear from your post whether you're using any of the healthcare-related variants of the InterSystems suite. If you're using HealthShare, Health Connect or IRIS for Health, support is included for receiving HL7 messages and storing them via multiple mechanisms. TCP/IP MLLP, HTTP, and file based services are all supported natively. When configuring a Business Service to receive HL7 messages via TCP/IP MLLP, you would select the EnsLib.HL7.Service.TCPService class and configure the Message Schema Category to "2.5" or "2.5.1" depending on your specific HL7 version. You would also need to configure the port on which to receive the messages, the target Business Process or Operation to act on them, and possibly a few other settings. Messages received through that service will then be inserted into the Interoperability message store, regardless of HL7 message type; message headers are created to provide tracking/status information and the messages are databased. If your need is to index and retrieve those messages based on content (patient name, account number, gender, etc), there are additional steps to fetch the specific data elements needed from the messages themselves and populate associated fields in a database structure that addresses those needs. That's something you would need to design; you would populate it using interoperability production components tailored to your filtering requirements and database design.
go to post Jeffrey Drumm · May 22, 2023 Can you try the code I posted above, substituting appropriate paths/filenames in the calls to the LinkToFile() methods? Any file will do for the in stream, as long as file/directory permissions permit. This at least would tell us whether the issue is with the key file or the JWT you're attempting to encrypt. I've tried this on I4H 2023.1 and Health Connect 2021.1.2 and the RSASHASign() method has not failed to generate a signature unless the key was passphrase-protected or not readable (due to file ownership/permissions) by the process opening it.
go to post Jeffrey Drumm · May 5, 2023 Ok, answering my own question ... Ens.MonitorService calls the macro $$$SetHostMonitor() when the InactivityTimeout is reached, which does this: Set $$$EnsHostMonitorRoot($namespace,%host,%prop)=%val,$$$EnsHostMonitorRoot($namespace,%host,$$$eMonitorLastActivity)=$$$timeUTC And that certainly appears to be updating the LastActivity time.
go to post Jeffrey Drumm · May 2, 2023 If you set the ENSLIB database to mount read/write, it will compile. I'm wondering if, after recompilation, it will work ...
go to post Jeffrey Drumm · Apr 27, 2023 Web services normally use an HTTP status code; for example, an ACK would be 200 OK for REST/HTTP and would be available through the %Net.HttpResponse Object in the StatusCode/StatusLine properties. SOAP usually provides some sort of payload along with the status code, and that would be found in the Data property. The type of response would likely be identified in the source/target system's WSDL for the SOAP interface.
go to post Jeffrey Drumm · Apr 26, 2023 This is something I wrote a long time ago; it extracts all business hosts and their settings. I've learned some things since I wrote it and would probably do a few things differently these days. It should be enough to give you some ideas, though ... ClassMethod GetConfigs(pProduction As %String = {$G(^Ens.Runtime("Name"),$G(^Ens.Suspended,$G(^Ens.Configuration("csp","LastProduction"))))}, pFile As %String = {$System.Util.GetEnviron("HOME")_"/"_$NAMESPACE_"_hostconfigs.csv"}) As %Status { Set tPrd = ##class(Ens.Config.Production).%OpenId(pProduction) Set tOut = ##class(%File).%New() Set tOut.Name = pFile Set tSC = tOut.Open("RWN") if '$$$ISERR(tSC) { Set tSC = vOut.WriteLine("""Type"",""Name"",""ClassName"",""Adapter"",""Enabled"",""ConfigName"",""ConfigValue""") } Quit:$$$ISERR(tSC) tSC If $ISOBJECT(tPrd) { For i=1:1:tPrd.Items.Count() { Set tHost = tPrd.Items.GetAt(i) Set tName = tHost.Name Set tClassName = tHost.ClassName Set tType = $CASE(tHost.BusinessType(),0:"Unknown",1:"Service",2:"Process",3:"Operation",4:"Actor",:"Huh?") Set tAdapter = $CLASSMETHOD(tClassName,"%GetParameter","ADAPTER") Set tEnabled = tHost.Enabled Set tCategory = tHost.Category Set tLine = """"_tType_""","""_tName_""","""_tClassName_""","""_tAdapter_""","""_tEnabled_""",""" Do tOut.WriteLine(tLine_"Category"","""_tCategory_"""") For l=1:1:tHost.Settings.Count() { Set tCfg = tHost.Settings.GetAt(l) Set tCfgName = tCfg.Name Set tCfgVal = tCfg.Value Set tSC = vOut.WriteLine(tLine_tCfgName_""","""_tCfgVal_"""") Return:$$$ISERR(tSC) tSC } } Do tOut.Close() } Else { Return $$$ERROR(0,"Production Not Found in this namespace") } Return $$$OK }
go to post Jeffrey Drumm · Apr 18, 2023 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).
go to post Jeffrey Drumm · Apr 15, 2023 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.
go to post Jeffrey Drumm · Apr 14, 2023 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.
go to post Jeffrey Drumm · Apr 9, 2023 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.
go to post Jeffrey Drumm · Mar 31, 2023 Well I guess there IS a setting (thanks, @Eduard Lebedyuk!) 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.
go to post Jeffrey Drumm · Mar 30, 2023 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)