The cursor is hovering over the 2nd sub-component of the first component of the field. 3 is the field, 3.1 is the first component of the field, 3.1.1 is the first sub-component of the first component of the field, and 3.1.2 is the second sub-component of the first component of the field. If the field has repetitions (delimited by a ~), the first repetition would be referenced as 3(1), with its components and sub-components referenced as 3(1).1, 3(1).1.1, 3(1).1.2, etc.

You may already understand this, but it bears repeating: If your intent is to include a literal ampersand symbol, you should also change the sub-component separator in MSH-2 in keeping with the formal HL7 spec.

Unfortunately, many vendors don't understand (or bother to handle) HL7 escaped characters, and some of them don't even pay attention to the values in MSH-2 ... leaving us forced to generate what is essentially badly-formed HL7.

If you're unable to purge Message Headers that are beyond the retention period with KeepIntegrity turned off, you may need to clean up your indices.  In a Cache terminal, execute the method below in the namespace where your Ensemble production is defined:

> Do ##class(Ens.MessageHeader).%ValidateIndices("",1)

Depending on the number of message headers, this may take a while to run ... on one system I've worked with it took more than a month.

You may want to also check for orphaned message bodies. You can determine if you have any by issuing the following SQL query from the SQL shell:

> Do $system.SQL.Shell()

>> SELECT COUNT(*) FROM EnsLib_HL7.Message HL7 LEFT JOIN Ens.MessageHeader hdr ON HL7.Id=hdr.MessageBodyId WHERE hdr.MessageBodyId IS NULL

Cleaning up the orphaned message bodies is yet another task, but let's see if you have any first ...

Looks like changing the framing would require a modified Parser anyway. So ...

I think you'll have to create a copy of EnsLib.HL7.Service.Standard (something like OSU.HL7.Service.CrappyFraming) that changes the  %Parser property to be your custom parser:

Property %Parser As OSU.Custom.HL7.CrappyFramingParser [ Internal ];

Then modify the custom service that was developed long, long ago to subclass it:

Class OSU.HL7.Service.TCPService Extends OSU.HL7.Service.CrappyFraming[ ClassType = "", ProcedureBlock, System = 4 ]

Yes, and just recently too.

You very likely need to run an index validation against Ens.MessageHeader.

NAMESPACE>Set sc = $System.OBJ.ValidateIndices("Ens.MessageHeader",,1,1)

Depending on the size of your database, it could take a while ... possibly weeks if you're in the multi-terabyte range. It has very little impact on performance, though. If you think it might take a while, you can create a task class and have it run non-interactively in Task Manager (that's what I did).

Once it's complete, your message purge should remove the old cruft automatically.

Scott, there's a utility called %GSIZE that does just this; it analyzes global storage and reports allocated vs. actual use.

namespace> d ^%GSIZE

It will prompt you for the database directory (defaulted to your current namespace's), whether you want to include all globals (Yes), globals that contain no data (No), and whether it should show details (Yes). Hit Enter for Device: or specify a path/filename if you want the report written to disk, and Enter again for the Right margin.

If your environment is mirrored, you can run it against the mirror. It could take a while to run; an Ensemble database I've worked with recently is 10TB in size and it took a month to complete.

EDIT: Should've paid attention to your qualification, too ... that's something that will take a bit of query development, thinking in terms of message volume between specific source and target config items along with an analysis of message content size. You'd be working with Ens.MessageHeader for that ...

If the Caché account name is the same as the OS account name, select System Administration | Security | System Security | Authentication/CSP Session Options and check Allow Operating System authentication. You'll automatically  be logged on using an account with the same name as the OS account, assuming one has been created in Caché, and will have all permissions set for that account and its roles.

If you don't care who you're logged in as, enable Allow Unauthenticated Access on the same page and make sure the UnknownUser account is enabled. You'll still be prompted for user/password, but you can press enter twice to bypass. You'll then have all permissions that have been set for UnknownUser and its associated roles.

Here's a potential solution. It's a method that will extract the %Source value from the message, and it should work for any Ensemble message type:

Class User.Util.MsgBody Extends Ens.Rule.FunctionSet

{

 

ClassMethod GetMsgSource(pMsg As Ens.Request) As %String

 {

       Return pMsg.%Source

 }

 

}

Since it extends Ens.Rule.FunctionSet, it's available as a function in the rule editor:

So, here's where the ability to write a little custom function in COS for use in the Routing Rule engine comes in handy. You may need to modify the HL7 paths depending on the schema you're using:

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

{

 

ClassMethod CheckMrnDupForFac(pMsg As EnsLib.HL7.Message, pFac As %String) As %Boolean

{

       Set tPid3Cnt = pMsg.GetValueAt("PIDgrp(1).PID:3(*)")

       Set tMrg1Cnt = pMsg.GetValueAt("PIDgrp(1).MRG:1(*)")

       for i=1:1:tPid3Cnt

       {

             If pMsg.GetValueAt("PIDgrp(1).PID:3("_i_").5") = pFac

             {

                    Set tPidMrn = pMsg.GetValueAt("PIDgrp(1).PID:3("_i_").1")

                    for j=1:1:tMrg1Cnt

                    {

                          Set tMrgMrn = pMsg.GetValueAt("PIDgrp(1).MRG:1("_j_").1")

                          if (pMsg.GetValueAt("PIDgrp(1).MRG:1("_j_").5") = pFac) && (tPidMrn '= tMrgMrn)

                          {

                                 Return 1

                          }

                    }

             }

       }

       Return 0

}

 

}

(updated for readability/word-wrap)

The method above will return true only when CKS entries exist in both fields and are different. Your specific needs may vary :)

It will be available in the function drop-down list in the rule editor.

In Studio's Tools menu, select Options, Environment, Documentation and Proxy. Check "Templates and add-ins will use Proxy server for <hostname>." Enter the address (including 'https://' if you're using SSL/TLS) and port number for your standalone web server. Click Apply.

You may have to re-authenticate each time you select a template, but at least you'll be able to get to them (and I'm sure there's a way around that too).

So, putting it all together ...

NOTE: This is NOT production-ready code. There's no error handling or input validation. You get to add that stuff yourself :)

Class User.HL7.Operation.Email extends Ens.BusinessOperation {

 

Parameter ADAPTER = "EnsLib.EMail.OutboundAdapter";

 

Property Adapter As EnsLib.EMail.OutboundAdapter;

 

Parameter INVOCATION = "Queue";

 

Method SimpleMessage(pInput As EnsLib.HL7.Message, Output pOutput As Ens.Response) As %Status

{

    Set email=##class(%Net.MailMessage).%New()

    Set addr = pInput.GetValueAt("PID:13.4")

    Do email.To.Insert(addr)

    Set email.Subject="The subject of your message"

    Do email.TextData.Write("This is the body of your message.")

    Set tSc=..Adapter.SendMail(email)

    //send an empty response message after message is sent

    Set pOutput=##class(Ens.Response).%New()

    Quit $$$OK

}

 

XData MessageMap {

<MapItems>

    <MapItem MessageType="EnsLib.HL7.Message">

        <Method>SimpleMessage</Method>

    </MapItem>

</MapItems>

}

 

}

Do you need to route the message based on criteria supplied in HL7 field values? Is any transformation required?

If the answer to both of those questions is "no," then you really don't need a router ... you can specify the operation(s) in the target config name field for the business service that receives the messages.

If you need to route, though, you'll want to disable BuildMap segment mapping error validation in the routing rule. That's enabled when using 1 as the validation selection. Try "d-m-z" instead, assuming you still want to validate that the messages have a DocType assigned.

Note that you won't be able to (easily) specify routing criteria based on fields in segments starting with the first segment that doesn't match the schema, or (again, easily) work with those segments in DTLs.

If flexibility and maintainability are a requirement, I'd recommend you go ahead and create a custom schema.