This might get you closer to what you want, perhaps called from OnProcessMessage() in the service:

ClassMethod QueueGetOldest(pQueueName As %String, Output pStatus As %Status) As %String
{
    If ##class(Ens.Queue).GetCount(pQueueName) > 0
    {
        Set tStmt = ##class(%SQL.Statement).%New()                                          
        set qSC = tStmt.%PrepareClassQuery("Ens.Queue","EnumerateItem")
        Set tRS = tStmt.%Execute(pQueueName,"")                        
        Do tRS.%Next()                                                
        Set tHdrid = tRS.%Get("MessageId")
        Set tMsghdr = ##class(Ens.MessageHeader).%OpenId(tHdrid)
        Set pStatus = $$$OK
        Return tMsghdr.TimeCreated
    }
    Set pStatus = $$$ERROR($$$GeneralError,"Not found")
    Return ""
}

It returns the time created of the oldest entry in the queue, or the empty string if the queue is empty or doesn't exist.

You could create a variant that would accept a duration argument and return true/false if the duration between the current time and the time of the oldest entry exceeds that.

The tilde character ("~") has special meaning in HL7; it is normally used as the field repetition character. If it is being included in a field value as a literal character, it is often converted to an "escape sequence" so that it can be delivered to a downstream system intact rather than interpreted as a delimiter; the escape sequence for the field repetition character is \R\.

Is your intended use of the ~ character to function as a repetition delimiter, or is it actually a verbatim part of a field value?

Are you inserting these values into an EnsLib.HL7.Message object? If yes, the standard way of doing this is to first make sure the SPM field you're working with is defined as repeating in the message schema (DocType), then iterate through your list of field values to be inserted.

Assuming your message object is tMsg:

Do tMsg.SetValueAt(Modifier1,"SPM:9(1)")
Do tMsg.SetValueAt(Modifier2,"SPM:9(2)")
Do tMsg.SetValueAt(Modifier3,"SPM:9(3)")

This will automatically use the repetition separator to delimit the values in SPM field 9. Note that the path supplied for the SPM segment/field will vary based on your specific schema definition.

I'm thinking that the value you provided for ElementName  in the service's configuration is incorrect. Your class definition also needs to match the structure of the repeating element, not the entire XML document.

Here's an example XML structure:

<Persons>
    <Person>
        <LastName>Drumm</LastName>
        <FirstName>Jeff</FirstName>
        <FavoriteColor>Red</FavoriteColor>
    </Person>
    <Person>
        <LastName>Herlick</LastName>
        <FirstName>Blakely</FirstName>
        <FavoriteColor>Teal</FavoriteColor>
    </Person>
</Persons>

The classname you'd create would be something like User.Custom.Person, with properties FirstName, LastName and FavoriteColor.

In the service's ElementName field, you'd enter Person. When the file is read, each Person element from the XML would end up in a separate message. You can then filter in the routing rule by using the variables Document.FirstName, Document.LastName, etc. and transform it in the DTL by selecting the Persistent Class User.Custom.Person as the source and your HL7 schema as the target.

Make sense?

Hi Blake,

Can you provide a bit more detail? If you need to create an individual HL7 message for each repeating element in the XML document, my answer here is probably the easiest way to get there. If there's a "master/detail" relationship within the XML, though, you'll need to handle that in a BPL, and perhaps still "chunk" the XML before handing it off to the BPL if there are multiple elements with master/detail relationships. You'd do that with the XML Object file service mentioned in the link (assuming you're getting these XML documents as local files ... there's also an FTP version of the service).

There are tutorials on BPLs in ISC's Learning library.

Hi Nora! Long time laugh

Something like this should do the trick, assuming the file to be appended to is named "spoo.txt" and the file from which you're appending is named "fleem.txt":

    Set tOut = ##class(%File).%New()
    Set tIn = ##class(%File).%New()
    Set tSC = 1
    Set tOut.Name = "spoo.txt"
    Set tIn.Name = "fleem.txt"
    Set sc = tOut.Open("WA")
    Set:$$$ISERR(sc) tSC=$$$ADDSC(tSC,sc)
    Set sc = tIn.Open()
    Set:$$$ISERR(sc) tSC=$$$ADDSC(tSC,sc)
    Quit:$$$ISERR(tSC) tSC
    While 'tIn.AtEnd
    {
        Set sc=tOut.Write(tIn.Read())
        Return:$$$ISERR(sc) sc
    }
    Do tIn.Close()
    Do tOut.Close()
    Return $$$OK

Here's a method that, once created and compiled, will appear in your dropdown list of functions in the rule editor:

Class User.Util.DateTime Extends Ens.Rule.FunctionSet
{

ClassMethod DaysPrior(pDays As %Integer) As %String
{
    Return $ZDATE($H - pDays,8)
}

}

You'd use it in your rule like this:

Caveat: The birthdate is assumed to be valid and 8 characters in length. If there's a possibility that you would get an invalid or missing date, the ">" comparison will not be valid.

GetAdapterSettingValue() may give you what you want. The Type parameter isn't needed.

Set archiveFilePath=##class(Ens.Director).GetAdapterSettingValue("ReadPDFFileService","ArchivePath",.status)
 

There's a corresponding GetHostSettingValue() method for non-adapter settings that works similarly.

Both seem to supply the System Default Setting when the setting is unvalued in the production.

The schema doesn't seem to match the structure of the message you've supplied; the OBRuniongrp group should be a repeating group and indicate such with parentheses (i.e. OBRuiniongrp()). Assuming that's fixed in the schema, you should be able to get at the fields in question in repeating OBR segments with something like:

Set tStudy = 0
// Get count of OBR segments
Set tOBRCnt = request.GetValueAt("ORCgrp(1).OBRuniongrp(*)")
// Loop through OBRs and evaluate field contents
For tIter = 1:1:tOBRCnt
{
   If request.GetValueAt("ORCgrp(1).OBRuniongrp("_tIter_").OBRunion.OBR:UniversalServiceIdentifier.Identifier") = "match_value"
   {
      Set tStudy = 1
   }
}
If tStudy
{
   ...insert action to take here...
}

The above may need to be modified to use context variables if it's being used in a BPL.

Rather than:

s target.{ORCgrp(k1).OBRgrp(k2).OBXgrp(k3).NTE(iNteCnt)} sTmp

Try:

d target.SetValueAt(iNteCnt, "ORCgrp("_k1_").OBRgrp("_k2_").OBXgrp("_k3_").NTE("_iNteCnt_"):1")
d target.SetValueAt(sTmp, "ORCgrp("_k1_").OBRgrp("_k2_").OBXgrp("_k3_").NTE("_iNteCnt_"):3")

Assuming you want the value in the 3rd field of the NTE. You should also change the value stored in sNTE to:

source.{ORCgrp(k1).OBRgrp(k2).OBXgrp(k3).NTE(1):3}

If there's only one NTE segment in each OBXgrp of the the inbound message.

Building an interface in Ensemble would require essentially emulating the printing protocol used by Meditech (lpr/lpd?). While that might be fun, I don't know that it's the best use of your time smiley

Depending on your OS platform, it might be possible to route Meditech's printer output to files. This would be done by configuring a custom printer definition on either the Ensemble host or a host that Ensemble has file (ftp/sftp/cifs/etc.) access to. For Unix/Linux, this isn't terribly hard to do with lpd, and I imagine it can be done with CUPS as well.

I know Windows can function as an lpd server, but I'm not sure how you would get its output routed to a file.

Once the printed output is in file form, you could then create a file service in Ensemble to pick up and process it.

I set up a test scenario based on your criteria. I created a service, HL7 router process and 3 operations:

Service: HL7 MLLP, target HL7 Router, AckMode=Application

Process: HL7 Router, ForceSyncSend: True, ReplyCodeActions: E=F

Target Operation 1: HL7 File Out

Target Operation 2: Custom Operation that returns an HL7 NAK, sets IsError to 1, logs an error code of 5001 with text "Forced NAK", ReplyCodeActions: E=F

Target Operation 3: HL7 File Out

Observations:

  1. Message arrives in router process
  2. Routes to Operation 1, completes successfully
  3. Routes to Operation 2, receives error
  4. Router responds to Service with Error; shuts down ... no message delivered to Operation 3
  5. Service returns NAK to sending application
  6. Message remains queued for Router

Isn't this what you would want to happen?