Jeffrey Drumm · Feb 21, 2018 go to post

One place you can't use persistent classes is where you need to obtain such things as a count of repeating elements in a Routing Rule, at least while using "native" rule functionality. EnsLib.EDI.XML.Document supports that in a way that's consistent with other document types.

Jeffrey Drumm · Feb 7, 2018 go to post

This isn't an Atelier-specific issue, it's Eclipse.

The Java editor for Eclipse has an option for changing the behavior of Ctrl-Left/Ctrl-Right, but as far as I know that's the only one that changes the default.

This is something ISC could change, but I suspect there would have to be significant demand for it.

Jeffrey Drumm · Feb 6, 2018 go to post

Looking at the error you're getting, I'm suspecting that the vendor of the external application that feeds your service intended to include a \r (carriage return) as the EOD character and somehow managed to omit the backslash. That would explain the 'r' character ... it's always an 'r' in the error that's generated, right?

Jeffrey Drumm · Feb 6, 2018 go to post

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 ]
Jeffrey Drumm · Feb 5, 2018 go to post

This error is usually indicative of a framing issue. Have you looked at what, specifically, this non-HL7 data consists of?

Jeffrey Drumm · Feb 2, 2018 go to post

The issue with the "[" option is that it doesn't compare the MRNs individually; it treats the entire PID:3 field (which can contain multiple MRNs) as the value to locate in MRG:1. You'd have to first locate the PID:3 iteration that contains the "CKS" facility identifier, then check the MRG:1 field to see if it's contained within it.

Jeffrey Drumm · Feb 2, 2018 go to post

Hi Richard,

The issue in this case was that there could be a variable number of repetitions in the PID:3 and MRG:1 fields, and that there was no guarantee that the MRNs for each facility would appear in the same order. So the actual need was to be able to compare each iteration of the MRN in PID:3 with each iteration of the MRN in MRG:1. This is cumbersome/difficult without a loop construct in the Rules editor, and would require at least as many sequential comparisons as there are repetitions in PID:3.

The example you provided compares only the first iteration of PID:3 with the first iteration of MRG:1, so if the desired match appeared in any subsequent iteration of either of those fields the desired outcome would not be achieved. This is one of those cases where some ObjectScript is a more streamlined solution.

Jeffrey Drumm · Jan 30, 2018 go to post

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.

Jeffrey Drumm · Jan 30, 2018 go to post

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 ...

Jeffrey Drumm · Jan 29, 2018 go to post

I think the intent is to limit configuration control of business hosts in an Ensemble production based on user role. Effectively, prevent a Cache/Ensemble user from changing the IP address or port number of a TCP/IP Business Operation through the Management Console without restricting the ability to view the settings.

Jeffrey Drumm · Jan 26, 2018 go to post

So how is this an ACTUAL solution if you're going to get cursed at for using it in a Production environment? devil

Jeffrey Drumm · Jan 25, 2018 go to post

And if there will always be a fixed number of iterations, you could do "1."_$CASE(k1,1:"a",2:"b",3:"c",4:"d",5:"e",6:"f",:""). It's hard to say which solution is less intuitive, though.

Another variation ... "1."_$EXTRACT("abcdefghijklmnopqrstuvwxyz",k1).  The possibilities are endless! laugh

Jeffrey Drumm · Jan 24, 2018 go to post

Do you have access to the Cache terminal?

NAMESPACE> Do $System.SQL.Shell()

I know it's not exactly what you asked for, but it doesn't time out ...

Jeffrey Drumm · Jan 24, 2018 go to post

Hmm. You could do something like "1."_$C(96 + k1) for the value of that field, assuming k1 is the iterator.

Things are gonna get funky when you hit the 27th iteration, though wink

Jeffrey Drumm · Jan 24, 2018 go to post

If the value you've fetched from the table is stored in a context variable, you can simply refer to the context variable as context.<variablename> in  DTL invoked from within the BPL.

Jeffrey Drumm · Jan 22, 2018 go to post

Thank you, Eduard.

I'm still a bit concerned that this violates the abstraction layer, but I'm coming to the conclusion that there's just no other way.

Jeffrey Drumm · Jan 19, 2018 go to post

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.

Jeffrey Drumm · Jan 18, 2018 go to post

You can treat a lookup table (Ens.Util.LookupTable) like any SQL table, using the LIKE operator to perform either "contains" ('%strval%') or "StartsWith" ('%strval') searches, but I don't think that's what you're really looking for. I'm thinking that, based on the logic you supplied, you're looking for two tables: One containing keys that you will use as "StartsWith" strings, and the other as "Contains" strings. You'd like to iterate through one table for your "Contains" comparisons, checking each AIL:3.2 against each key. And for your "StartsWith" comparisons, the other table. This would keep it maintainable in the Ensemble section of the Management Console, but you'd only really be populating the KeyName field. Wrapped in a couple of FunctionSet methods, this would simplify your rule considerably.

But here are some examples of "Contains" vs. "StartsWith" queries against Ens.Util.LookupTable, just in case:

ISYSDEV>>select KeyName from Ens_Util.LookupTable where TableName = 'ALLERGY_CODES' and KeyName LIKE '%mon%'
12.     select KeyName from Ens_Util.LookupTable where TableName = 'ALLERGY_CODES' and KeyName LIKE '%mon%'
KeyName
Almonds
almonds
cinnamon
lemons
4 Rows(s) Affected
statement prepare time: 0.1417s, elapsed execute time: 0.0011s.
---------------------------------------------------------------------------

 

ISYSDEV>>select KeyName from Ens_Util.LookupTable where TableName = 'ALLERGY_CODES' and KeyName LIKE 'cin%'
13.     select KeyName from Ens_Util.LookupTable where TableName = 'ALLERGY_CODES' and KeyName LIKE 'cin%'
KeyName
cinnamon
1 Rows(s) Affected
statement prepare time: 0.0006s, elapsed execute time: 0.0003s.
---------------------------------------------------------------------------

Jeffrey Drumm · Jan 18, 2018 go to post

Barry, Source is the string value from the message header's SourceConfigName property, and source.%Source is an error:

Jeffrey Drumm · Jan 17, 2018 go to post

The method takes the message object as its first argument, referenced by the variable Document in the Rule Editor. The call you should be making is, literally, CheckMrnDupForFac(Document,"CKS"). The method will parse the PID:3 and MRG:1 fields for you; you don't need to supply them literally.

Jeffrey Drumm · Jan 17, 2018 go to post

The expression editor will often add a period after "Document" ... can you verify that you removed it?

Jeffrey Drumm · Jan 17, 2018 go to post

Well, it can ... but you would need to contact the WRC for a version of ServerManager.exe that supports it:


Jeffrey Drumm · Jan 15, 2018 go to post

@Thembelani Mlalazi, he's specifically looking to get at the element %Source, which is not a field defined by the user in the RecordMap, and is not visible to the Context of the Rule.

Jeffrey Drumm · Jan 15, 2018 go to post

@Thembelani, the %Source element is not directly accessible, even with the constraint set. That's why I provided the solution below.

Jeffrey Drumm · Jan 13, 2018 go to post

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:

ClassUser.Util.MsgBodyExtendsEns.Rule.FunctionSet

{

 

ClassMethodGetMsgSource(pMsgAsEns.Request)As%String

 {

       ReturnpMsg.%Source

 }

 

}

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

Jeffrey Drumm · Jan 11, 2018 go to post

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:


ClassUser.Util.FunctionSetExtendsEns.Rule.FunctionSet

{

 

ClassMethodCheckMrnDupForFac(pMsgAsEnsLib.HL7.Message,pFacAs%String)As%Boolean

{

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

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

       fori=1:1:tPid3Cnt

       {

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

             {

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

                    forj=1:1:tMrg1Cnt

                    {

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

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

                          {

                                 Return1

                          }

                    }

             }

       }

       Return0

}

 

}

(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.

Jeffrey Drumm · Jan 11, 2018 go to post

Thanks, Marc, that's what I'm doing now, including the creation of some custom functions to get at things like child node counts that aren't normally accessible via EnsLib.MsgRouter.RoutingEngine. An efficient mechanism for conversion of a Cache object to class EnsLib.EDI.XML.Document would have given me access to all of that VDoc's methods with minimal coding, though.

Jeffrey Drumm · Jan 11, 2018 go to post

And if they're not always in the same repetition but the number of repetitions is fixed, it gets a little bit messy; you could do something like:

HL7.{MRG:1.1} CONTAINS HL7.{PID:3(1).1}

OR

HL7.{MRG:1.1} CONTAINS HL7.{PID:3(2).1}

OR

HL7.{MRG:1.1} CONTAINS HL7.{PID:3(3).1}

If they're completely arbitrary in the number of repetitions, you're gonna have to write some COS :)

Jeffrey Drumm · Jan 11, 2018 go to post

Are the identifiers you are trying to compare always at the same position in each field, i.e. 3rd repetition? If yes, you should be able to use HL7.{PID:3(3).1}=HL7.{MRG:1(3).1} to compare them.