go to post Robert Barbiaux · Feb 24, 2024 Hi,To summarize the documentation : ReplyCodeActions settings of HL7 operations is a comma-separated list of specifiers in the form <code>=<actions>, where <code> is an expression matching error condition(s) and <actions> is a string of one letter action codes. All codes where <actions> consists of only 'W' (for 'log a Warning') will be evaluated, and a warning will be generated for each matching <code>. Other <code> values will be evaluated in left-to-right order, executing the first matching <code> that has a non-warning <actions> value. As noted in the details for the 'W' flag, an error that only triggers 'W' <actions> will be treated as Completed OK. if ReplyCodeActions is empty, a default setting is used. For HL7 operations, it is : :?R=RF,:?E=S,:~=S,:?A=C,:*=S,:I?=W,:T?=C To match the application reject code in the HL7 ACK^O01 message in the example, and suspend all matching messages, use the following specifier, that matches all ACKs with MSA:1 = "AR" and suspend message, while retaining default behavior for other error conditions : :?R=S,:?E=S,:~=S,:?A=C,:*=S,:I?=W,:T?=C
go to post Robert Barbiaux · Feb 22, 2024 ^SPOOL is not buried 😁, it is well documented, along with %IS and %SPOOL utilities.It is simple and effective when used to it's intended purpose, that is, spooling text written to the currently in use device.
go to post Robert Barbiaux · Feb 22, 2024 As Enrico mentions, HL7 v2 message grammars, segment structures and data type syntaxes are available in IRIS schemas. Complete specifications and semantics are detailed in the normative documents available on HL7.org web site.
go to post Robert Barbiaux · Feb 19, 2024 Yes, ^SPOOL is the simplest way to achieve this. If you need a string rather than a global, you can just get all lines from ^SPOOL, for example : ClassMethod ZWriteToString() As %String { #Dim result as %String #Dim i,lineCount as %Integer kill ^SPOOL($j) open 2:$j use 2 zwrite s result="" s lineCount=$select($data(var):$za-1,1:$za-2) close 2 for i=1:1:lineCount s result=result_^SPOOL($j,i) return result }
go to post Robert Barbiaux · Feb 5, 2024 Thank you David, your excellent remarks are now part of the text.
go to post Robert Barbiaux · Feb 5, 2024 Hi Joseph, To unit test interoperability productions and their business hosts, such as business processes, extend the %UnitTest.TestProduction class.
go to post Robert Barbiaux · Jan 30, 2024 If the intent is to wrap the statements in a new variable scope, why not simply extract the block in a private method ?
go to post Robert Barbiaux · Jan 25, 2024 To execute the routine at scheduled intervals, you can use the task scheduler with %SYS.Task.RunLegacyTask , that is intended to do exactly what you describe, run a routine.
go to post Robert Barbiaux · Jan 24, 2024 EnsLib.HL7.Service.HTTPService may suit your needs : it implements reading HL7 message from incoming HTTP request.
go to post Robert Barbiaux · Jan 23, 2024 To clean pending messages and other suspended production data, you can use Ens.Director.CleanProduction() method, as explained in the documentation. Caution though, this will delete current production state, including removing message headers (Ens.MessageHeader instances) from queues.
go to post Robert Barbiaux · Jan 14, 2024 You can pass the value to the DTL using the transformation auxiliary parameter, as described in the documentation : Working with Rules | Developing Business Rules | InterSystems IRIS for Health 2023.3.To pass the source configuration name : in the business rule, before the send action, add an assign action to set RuleActionUserData to %PrimaryRequestHeader.SourceConfigName in the DTL, use aux.RuleActionUserData to access the value (see https://docs.intersystems.com/irisforhealth20233/csp/docbook/DocBook.UI....)
go to post Robert Barbiaux · Sep 18, 2023 I totally agree.At the moment I have all routines in a git repo, some files are using suffixes for disambiguation, and will gradually reduce entropy by refactoring and applying clear naming conventions as Evgeny suggest. my thanks to you both 😀
go to post Robert Barbiaux · Sep 18, 2023 After enabling case-sensitivity in Windows, it is indeed possible to keep the original storage names.Unfortunately, enabling the flag has no impact on the behaviour of Windows versions of git or vscode, they continue to use case-insensitive comparisons for file names. For example, it is impossible to stage / commit changes to the git repository, as file names differing only case are considered as a single file.
go to post Robert Barbiaux · Jul 21, 2023 Hi, Using out-of-the-box classes in the EnsLib.HL7 package, you can either completely ignore responses, either process them synchronously. More complex scenarios involving multiple responses (ack/nack) are supported (see Important HL7 Scenarios | Routing HL7 Version 2 Messages in Productions | InterSystems IRIS for Health 2023.1), but I think they do not match your supplier requirements.According to the standard, when sending multiple HL7 v2.x messages over MLLP/TCP, the sender (initiating module) must wait for a response before sending the next message. This is described in "Health Level Seven Implementation Support Guide", Appendix C, section C.6.4, item 5. : It is assumed that an initiating module may connect and perform more than one message transaction before disconnecting, but it may not have more than one outstanding message waiting for a response. In other words, the initiating task must wait for the response to a given message before sending another message.This vendor requirement to receive multiple messages before sending responses is thus non-standard. It would require the sender to keep track of sent messages with a pending response, handle asynchronous replies and retry errored messages.While this is possible by writing custom adapter/operation extending the out-of-the-box classes in EnsLib.HL7 package, it would add a lot of complexity and has IMHO, little added value compared to the usual synchronous implementation.
go to post Robert Barbiaux · Jun 19, 2023 Hi Evgeny, If the operation class is using the mechanism in Ens.BusinessOperation to route incoming requests to the OnMessage() method, you can use the XData content to get a list of supported request (just as the Portal testing dialog does). If the operation class implements it’s own custom OnMessage(), there is no simple way to tell what request classes are supported. The XData is described in the documentation. For responses, there is a RESPONSECLASSNAME parameter defined in classes extending Ens.Request. I believe this used by the BPL editor. This is somewhat limited however, since the operation can as well respond with any %RegisteredObject object and accept any kind of request (via OnMessage), e.g. any class extending %RegisteredObject if it is marked as "in process". Anyways, as Robert C. points out, documenting what the operation intent is and it's interface, especially if they are not trivial, helps a lot and IMHO is best practice.
go to post Robert Barbiaux · Jan 24, 2023 For simple headers (and footers), you can use the 'batch class' feature of the record map file batch service (EnsLib.RecordMap.Service.BatchFileService) and operation (EnsLib.RecordMap.Operation.BatchFileOperation), and a class such as EnsLib.RecordMap.SimpleBatch to specify a header string.
go to post Robert Barbiaux · Jan 22, 2023 The record map file operation append records to the output file. The initial value of the 'Filename' setting is '%Q', hence you get one file per timestamp. If you set "Filename" to '%f', the output file name will be the same as the input file name and records from one input file will be appended to an output file with the same name.
go to post Robert Barbiaux · Jan 21, 2023 For a simple message transformation flow example, I would go for record map : you get readymade service (EnsLib.RecordMap.Service.FileService) and operation (EnsLib.RecordMap.Operation.FileOperation) in the library to read and write CSV files and a mecanism that generates an appropriate message class based on a declarative definition (see Using the Record Mapper | Developing Productions | InterSystems IRIS Data Platform 2022) So you can focus on DTL and the whole flow can be done from the administration portal, look ma, no code ;-)
go to post Robert Barbiaux · Jan 16, 2023 You can call the Tranform(source,target) class method implemented by classes that extend Ens.DataTransform : directly in objectscript code through code generated for a BPL or router process ('send' action optionally uses a list of transformation classes that gets applied in sequence to the message before it is sent to target(s)). through code generated by a DTL ('subtransform')
go to post Robert Barbiaux · Jan 16, 2023 If you need to process the entire file and no line filtering, I would go for using pass through file service (EnsLib.File.PassthroughService) to send an instance of stream container (Ens.StreamContainer) to either a message router (EnsLib.MsgRouter.RoutingEngine) or custom (BPL or code) process, and use a transform (class extending Ens.DataTransform) to transform the source stream container into a target stream container and send it to the file pass through operation (EnsLib.File.PassthroughOperation) for output. I would use a custom process over message router if transform needs data source(s) (e.g. response from another process or operation) other than the input file. The transform can pick a suitable target stream class (Extending %Stream.Object) to hold in the Ens.StreamContainer depending on where you want to store the data (database vs file system,…) HTH