Marc Mundt · Mar 26, 2020 go to post

"Insert(^content)" doesn't work because you can't store an object reference in a global. When you do "set ^content=citasPrimarias.datos.GetAt(j)", it converts the object reference to a string before storing it in the global.

Marc Mundt · Mar 26, 2020 go to post

In BPL, you can use a "Trace" activity to log a value to the trace log.

Marc Mundt · Mar 26, 2020 go to post

Yone, what is the data type of the property "datos" in Mensajes.Response.HistoriaClinica.ConsultaCitasResponse. I assume it's a list of a custom class that has two properties named fecha and hora?

Property datos as List of my.class.dato;

If so, you would do something like this:

set dato=##class(my.class.dato).%New()
set dato.fecha=fecha
set dato.hora=hora
do target.datos.InsertAt(dato,indice)
Marc Mundt · Mar 23, 2020 go to post

When the delay expires, Ens.Alarm will send a message back to the business process. You can query to find any messages that have been sent to Ens.Alarm but for which it hasn't sent a message back. This is based on the value of status: the status will be "Delivered" until Ens.Alarm sends the message back to the BP, at which point it will change to "Completed".

SELECT * FROM ens.messageheader WHERE status='Delivered' AND sourceconfigname='MyBPL' AND targetconfigname='Ens.Alarm'

As Eduard mentioned, it would be good to understand the use case as there may be another way to accomplish it.

Marc Mundt · Mar 10, 2020 go to post

Right now you're deleting items from the same list you're iterating through. As an alternative you could loop through the original list but in the loop you build a new list. Only insert an item into the new list if it doesn't contain the characters you're trying to remove.

Marc Mundt · Mar 6, 2020 go to post

Putty has options to take certain actions when it receives a BELL character (  $c(7)  ). Can you change your routine to return a BELL when it completes?

Marc Mundt · Mar 5, 2020 go to post

Yone, have you tried setting ContentCharset to "UTF-8" instead of "utf-8"?

Marc Mundt · Feb 28, 2020 go to post

The following works for me:

TESTING>write ##class(Ens.Util.FunctionSet).ConvertDateTime("20200228210000",  "%K(0)%q",  "%K(-5)%q")
20200228160000

Marc Mundt · Feb 25, 2020 go to post

You're right, this code will not work as-is with fields that exceed the max string size (~ 3 MB). If you expect large fields/segments (such as attachments in OBX:5), there are stream-handling methods that you can use.

If OBX:5 is smaller than the max string size then I don't see any problem regardless of what type of data it contains as the OBX:5 content shouldn't include "^".

Marc Mundt · Feb 25, 2020 go to post

I just did a test with .Find() and was able to compile with no error and the rule executes correctly depending on the values in the inbound record. Here's what my rule looks like:

Is your router receiving the Record object from the record map or the batch object?

Since Find() doesn't meet your use case, it sounds like you'll need to loop through all of the values in that field. Routing rules can't do loops, so the right way to do this would be to create a custom function and pass it either the main Document or Document.ObservationValue. In your custom function you can iterate through the entire list and return a boolean.

Docs on creating a custom function:
https://docs.intersystems.com/healthconnect20191/csp/docbook/Doc.View.c…

Marc Mundt · Feb 20, 2020 go to post

Hi Neil,

Can you provide some more details?

Is this record map being used for pulling data into the system or for outputting data?
Which component is logging the error -- business service, router/business process, business operation?
What is the error message?
Can you give an example of a record that causes the error?
Can you show the record map definition?

-Marc

Marc Mundt · Feb 19, 2020 go to post

As far as I know, there is no configuration option for automatically removing trailing delimiters. So your post-processing approach seems like the way to go.

It looks like Randy's example only removes trailing carats at the end of the segment. I've seen a different version of this code (also by Randy) that includes this additional piece which removes trailing carats from each field in the segment.

       ; Remove trailing up carats within a field
       set tIndex = 0
       while $FIND(tSegment,"^|",tIndex) > 0 {
           set tSegment = $Replace(tSegment,"^|","|")
           do pHL7.SetValueAt(tSegment,tCount)
       }
Marc Mundt · Feb 19, 2020 go to post

Neerav, have you tried logging the session ID that you're passing to the query in the BPL? I only suggest this in order to rule out the possibility that the BPL code is failing to get the correct session ID and that's why the query is not returning any results.

Marc Mundt · Feb 18, 2020 go to post

It's not clear what logic you want to use to evaluate the list properties, but if you want to, for example, check if any of the items in the list contain a particular value, you could use the Find() method in the class %ListOfDataTypes.

In that case the condition in your routing rule would be something like this:

Document.ResultText.Find("MyValue") '= ""
Marc Mundt · Feb 18, 2020 go to post

Yone,

I think there's a problem with your Write() method. It is outputting data from the "stream" object, but "stream" is always set to a new (empty) stream and populated with data from "writer", which is also a new (empty) %XML.Writer object. So I don't see how this method could ever output any data.

Regarding the CSP error when requesting the WSDL, have you checked the application log to see what the actual error is?

-Marc

Marc Mundt · Feb 13, 2020 go to post

"it is quite curious to see it being replied from the Web Service to the Operation, where we can LOGINFO its propierties, and then, suddenly when we pass it to the Process, it is empty."

This is happening because when you receive it from the web service it is only stored in memory. In order to send it from the operation back to the business process it needs to be persisted in the database. This should be handled automatically by the parent (response) class if the child class is a %SerialObject -- it will automatically serialize the child object and store it under the parent's global.

We can demonstrate this with these two classes:

Class Demo.Obj.ParentClass Extends Ens.Response
{

Property parentProp1 As %String;

Property childList As list Of Demo.Obj.ChildClass;

ClassMethod populate()
{
    s parent=##class(Demo.Obj.ParentClass).%New()
    s parent.parentProp1=$ZDT($h)
    f i=1:1:10 {
        s child=##class(Demo.Obj.ChildClass).%New()
        s child.prop1="child.prop1."_i
        s child.prop2="child.prop2."_i
        zw child
        w "    ",parent.childList.Insert(child),!
    }
    w parent.%Save(), ":", parent.%Id(),!
}
}
Class Demo.Obj.ChildClass Extends (%SerialObject, %XML.Adaptor)
{

Property prop1 As %String;

Property prop2 As %String;
}

It's not clear to me why this isn't working in your example -- possible the storage definition needs to be reset since you changed it from %RegisteredObject to %SerialObject.

https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=G…

Marc Mundt · Feb 13, 2020 go to post

You can't directly store an object in a global like this. In your code, you're actually just storing a text description of the object reference in the global. The way to store an object in a global is to either (1) define it as a persistent class and let the system handle storing it automatically, (2) make the list of objects a property of a parent class which is itself persistent, or (3) serialize the object into a string that can be stored directly in the global.

Can you explain why you need to store copyList in this manner? If you just want to persist the objects in copyList, you can consider making RSPK21QUERYRESPONSE a persistent class. 

https://docs.intersystems.com/healthconnect20191/csp/docbook/DocBook.UI…
https://docs.intersystems.com/healthconnect20191/csp/docbook/Doc.View.c…
https://docs.intersystems.com/healthconnect20191/csp/docbook/Doc.View.c…

Also, it's worth noting that when you define the property RSPK21QUERYRESPONSE  as a list of objects where the child object extends %SerialObject,  then that entire list will be stored in the same global as the parent class.

One other note:

At the top of your code example, you set RSPK21QUERYRESPONSE as a %ListOfDataTypes:
SET RSPK21QUERYRESPONSE = ##class(%ListOfDataTypes).%New()

While later you try to clone it into a %ListOfObjects, which is not equivalent:
set copyList = ##class(%ListOfObjects).%New()
set copyList = pResponse.RSPK21QUERYRESPONSE.%ConstructClone(1)

Marc Mundt · Feb 13, 2020 go to post

Try changing EsquemasDatos.Gasometros.hl7.RSPK21.QUERYRESPONSE.CONTENT to extend %SerialObject:

Class EsquemasDatos.Gasometros.hl7.RSPK21.QUERYRESPONSE.CONTENT Extends (%SerialObject, %XML.Adaptor)
Marc Mundt · Feb 12, 2020 go to post

The source for this JSON seems to think that "data" holds a string rather than an object. Still, we can convert that back to a proper object using %DynamicObject's %FromJSON() method:

USER>set myJSONObj={"app_id":"5cf57b56-c3b4-4a0d-8938-4ac4466f93af","headings":{"en":"Cita Atención Primaria","es":"Cita Atención Primaria"},"subtitle":{"en":"C.P. ISORA","es":"C.P. ISORA"},"contents":{"en":"Aqui el contenido del mensaje si aplicase","es":"Aqui el contenido del mensaje si aplicase"},"data":"{\"centro\":\"C.P. ISORA\",\"fecha\":\"yyy/mm/dd\",\"hora\":\"hh:mm\",\"profesional\":\"nombre del profesional\",\"nomUsuario\":\"nombre de usuario\",\"codcita\":\"idCita\",\"sepuedeborrar\":\"1\"}","include_player_ids":["c2917a6f-6ecf-4f45-8b31-9b72538580fd"]}
 
USER>write myJSONObj.data
{"centro":"C.P. ISORA","fecha":"yyy/mm/dd","hora":"hh:mm","profesional":"nombre del profesional","nomUsuario":"nombre de usuario","codcita":"idCita","sepuedeborrar":"1"}

USER>set dataObj=##class(%DynamicObject).%FromJSON(myJSONObj.data)
 
USER>write dataObj
11@%Library.DynamicObject

USER>set myJSONObj.data = dataObj
 
USER>write myJSONObj.%ToJSON()
{"app_id":"5cf57b56-c3b4-4a0d-8938-4ac4466f93af","headings":{"en":"Cita Atención Primaria","es":"Cita Atención Primaria"},"subtitle":{"en":"C.P. ISORA","es":"C.P. ISORA"},"contents":{"en":"Aqui el contenido del mensaje si aplicase","es":"Aqui el contenido del mensaje si aplicase"},"data":{"centro":"C.P. ISORA","fecha":"yyy/mm/dd","hora":"hh:mm","profesional":"nombre del profesional","nomUsuario":"nombre de usuario","codcita":"idCita","sepuedeborrar":"1"},"include_player_ids":["c2917a6f-6ecf-4f45-8b31-9b72538580fd"]}
Marc Mundt · Feb 11, 2020 go to post

Yone, does Mensajes.Response.GestionPacientes.operacionResponse extend Ens.Request or %Persistent? Does it extend %XML.Adapter?

It would be helpful to see the code for Mensajes.Response.GestionPacientes.operacionResponse and the business process.

Marc Mundt · Feb 11, 2020 go to post

The error suggests that the object received by the VDocRoutingEngine is not a VDoc. Try using EnsLib.MsgRouter.RoutingEngine instead.

Marc Mundt · Feb 6, 2020 go to post

One possibility:

You can handle the two files with two separate interfaces:

The interface for the tracking file would just load it into a record map and then stop. This has the effect of saving the information as a row in a database table.

The BPL for the PO file interface would just query the tracking file record map table to find the relevant entry, open it, and add the necessary info to the PO. To avoid timing issues where a PO file is processed before the corresponding tracking file you could add a check in the BPL: if a row doesn't exist in the tracking table for this PO, delay for 5 seconds using the BPL "Delay" action and try again before failing with an error.

Marc Mundt · Feb 4, 2020 go to post

What happens if you remove "do result.%Display()"?

I suspect that %Display() is iterating through the result set, so by the time it reaches "$$$LOGINFO("resultado siguiente: "_result.%Next())" it is already on the last row.

Marc Mundt · Jan 28, 2020 go to post

Apache Tika is another option. Without writing any code, it can be run from the command-line and output an XLSX as a tab-separated file.

https://tika.apache.org/

java -jar tika-app-1.23.jar -t sample.xlsx > sample.tsv
Marc Mundt · Jan 21, 2020 go to post

Have a look at the StayConnected setting under Connection Settings. By default, it is set to -1 which means the adapter expects to always have an active connection and will throw an error if it doesn't.

Setting StayConnected to 0 would mean the remote system can connect and disconnect as needed without triggering an error.

Stay Connected

Applies to all TCP adapters.

If StayConnected is a positive value, the adapter stays connected to the remote system for this number of seconds between input events. A zero value means to disconnect immediately after every input event. The default of –1 means to stay permanently connected, even during idle times. Adapters are assumed idle at startup and therefore only auto-connect if they are configured with a StayConnected value of –1.

The value of StayConnected controls how the TCP adapter treats disconnections. If StayConnected
has a value of –1, the TCP adapter treats a disconnection as an error. If it has a value of 0 or a positive integer, the TCP adapter does not consider a disconnection an error.
Marc Mundt · Jan 17, 2020 go to post

$TRANSLATE might be a possibility. It accepts a list of characters and replaces them either with other characters or just removes them. You could compare the length of the original column with the length of the column after using $TRANSLATE to remove illegal characters. For rows without illegal characters the length will match.


This would identify rows that have tilde (~), pipe (|), or backtick (`) in MyField:

SELECT * FROM MyTable WHERE CHAR_LENGTH($TRANSLATE(MyField,'~`|'))  <  CHAR_LENGTH(MyField)

It's worth noting that a statement like this can't make use of indices, so it will have to scan every row in the table.

Marc Mundt · Jan 10, 2020 go to post

I would be tempted to, as a next troubleshooting step, take the BPL and JDBCGateway out of the equation and test the stored procedure calls using the same JDBC driver from a Java-based SQL query tool such as Squirrel.

It's not SQL Server specific, but this step-by-step walkthrough for using Squirrel to connect to Caché might save some time.

[Edit: should have said "queries of the views" rather than "stored procedure calls"]