Replies

Hi,

We have an Ensemble Service class that extends " EnsLib.SOAP.Service" and provides a web method with a parameter that is a class that includes a property of " %Stream.FileBinary", plus all the meta data in other properties. This allows the source application, written in .net, to send us documents fairly easily, as all the translation back and forth into xml, etc. is done for you (I assume it is also easy to do at the .net end). There is not a lot of code needed to define the class and web service, then it just needs to build a message object with that same input class as a property, and send that onwards as usual.

(Unfortunately, we then have to convert the file into base 64 encoded chunks and insert into segments in one of those MDM^T02 messages like you do. But that's another story.)

Mike

A lot of the code we have to deal with is like the "One Line" method, but then some of it was written back in the days when there was very little space in the partition for the program, and the source code was interpreted directly, so it had to be kept small. What is different is that while the code to handle the traverse is all kept on one line, which I think makes the scanning method clear, other code for pulling and writing stuff is usually on lower "dotted" lines, like this:

S Sub1="" F  S Sub1=$O(^Trans(Sub1)) Q:Sub1=""  D
.W !,"Sub1: ",Sub1
.S Sub2="" F  S Sub2=$O(^Trans(Sub1,Sub2)) Q:Sub2=""  D
.. ; etc

And yes, it uses the short version of commands (as you did for the Quit, I notice). I'm not saying this is the best style to use, obviously, as things have moved on, but we do still tend to use it when amending the old code.

I think it is useful to be aware of all these variations, as you may come across old code using them. Also, sometimes the array structure can be better or faster than objects and SQL, whether in a global arrays, or in temporary local arrays. Its a very neat and flexible storage system.

Mike

 

 

Hi Jenny,

The FOREACH has to iterate over something, but there is only one input field, so I don't think there is any other way than a CODE action in a DTL (at least on v2012). I could have done the whole loop in the one CODE, or called an outside function, but I preferred to use the GUI to set up the ASSIGN actions for me, to provide the extra validation and visual links in the DT Builder. I have used DTL whenever possible in other areas like in BPL to extract data from messages into temporary variables. Maybe not always the quickest or easiest way, but I think it makes the system more transparent.

For the loop the DT is actually called from a routing rule. The source is a simple message class with a few fields, and the target an HL7 v2 MDM_T02 message. I suppose if a full BP was involved then the loop could have been implemented in BPL in some way, but I think that would get complicated. Perhaps DTL needs a more general WHILE/UNTIL loop action added?

This may be slightly dodgy as it depends on assuming how the DTL will be compile, but I have used them to implement a loop around a set of standard DTL actions.

For one the source message had a stream with a document in it, and it needed to be chopped up into chunks, each one being put in a field in an HL7 v2 OBX segment. So an initial CODE action opens the loop with something like "Do {", gets the next chunk and quits if none. Then there are some normal ASSIGN actions setting up the new segment - easy to put in and appear in the DTL display. And finally there is a second CODE action to end the loop with "}".

It works, but only as long as InterSystems keep the compiled code for the assigns nice and simple.

A similar use was when dealing with xml documents. Again I needed a loop that enclosed normal ASSIGN actions, but this time for things like stepping backwards and deleting unwanted elements, or creating them from an array.

Also, I used a CODE action to work out if a DTL was running inside a business process, or just in the Studio for testing, so that some expected properties could be created to allow the test to run. Just for debugging.

Regards,

Mike

I agree, the Production class is a major problem for Configuration Management. In the past we've tried System Defaults and found it very awkward to use. In the last project we started with separate classes for dev, test and live. The updates to test and live versions were done in a release preparation namespace, with comparisons done to ensure they were in step, as mentioned earlier. Then a release was built from that and installed in test slot and later in live slot.

But it all got harder and harder to handle, and when the system went fully live we stopped doing full releases and went over to releasing only changed classes, etc. and never releasing the production classes. Now the problem is that changes to the production have to be done manually via the front end. Fortunately, there are a lot less of these now.

Regards,

Mike

Hi Steve,

I have done something like you describe. I used BPL, and at the time tried to keep away from using bits of code, but it got complex and in retrospect I'm not sure it was the best way. The diagrams are nice, but I think a bit of well written code might have been easier to follow!

First I created a "TempStore" class with an "MRN" (Medical Records Number) property and no permanent storage. This is used as the target class for a transform that pulls out the patient id and puts it in that property.

In the BPL Process I added an instance of the TempStore class to the BPL context object, and the first activity in the diagram is the transform with Source of "request" and Target "context.TempStore".

With the MRN found, I then use code like the following in the Value of an "assign" activity to put the target stored object into another context property of "context.BNetEpisode" already set to the same class.

##class(...).MRNIndexOpen(context.TempStore.MRN,4)

An "if" activity with a Condition like "$IsObject(context.BNetEpisode)" is used to see if anything was found, and create a new one if required by setting the "context.BNetEpisode.MRN" property equal to "context.TempStore.MRN".

The "context.BNetEpisode" property is then be used as the Target for "transform" activities later on with Create = "existing" used. Ensemble does a save automatically when the Process completes.

I hope this makes sense. (I cannot provide the full code as it belongs to the customer, and anyway it gets a lot more complex as there are 3 types of inbound message, one of which was an HL7 v3 document, and it was actually using an xml document inside the stored object to hold much of the data - but that's another story).

Mike

Hi Andrew,

Thanks for posting this. I am in the process of coding a similar automated statistics email, so I am reading your code with interest and may well use some of it. My only comment is that since this is for monitoring Ensemble, I actually decided to use Ensemble to implement it. So I have a message class with properties like "Subject" etc. and this is sent to an Operation that uses the Ensemble email adapter to send it. There is also a Service that runs once a day to pull the required information and builds the email message.

Mike

Great idea. (I find SharePoint frustrating. All I want is a simple link to a folder in a library, but it's really hard to get.)

Yep, that's the first thing I noticed as well. I often go from one search to another and do not want to step back to the start each time. Search is probably the most important facility for me. (Thanks to interSystems for opening it up for comment, by the way.)

There may be arguments in favour of either solution, depending on the types of data involved and programmer preference, but if you are to embrace the full Ensemble "model" then I think the second option is far better.

By putting the non-HL7 into a message sent to a business process,  it gets stored and becomes visible in Ensemble in it's raw form (or as close as you can make it) on the  message queue into that Process. This makes support much easier as you can see before and after messages in the Ensemble GUI. Also, a Business Service should do a minimum of work so that messages are input as fast as possible.

Using DTL is also the cleanest option, since it is meant to transform messages, but I admit that sometimes this is more effort than it is worth. I have had to deal with complex xml documents, and ended up writing methods in my custom message class to make extraction easier to understand in the DTL.

Mike