Marc Mundt · May 18, 2023 go to post

One quick/dirty(?) way is to do a "View Code in Namespace" for "%SYS". Under the %SYS namespace the percent classes are not filtered out.

Marc Mundt · Apr 14, 2023 go to post

I found a tokenize method in one of the %DeepSee packages:

USER>w ##class(%DeepSee.extensions.utils.StringMatchUtils).tokenize("this is a string blah blah.",.tokenArray)
1
USER>w

tokenArray=6
tokenArray(1)="this"
tokenArray(2)="is"
tokenArray(3)="a"
tokenArray(4)="string"
tokenArray(5)="blah"
tokenArray(6)="blah"
USER>
Marc Mundt · Mar 22, 2023 go to post

Standalone installers for Caché ODBC drivers are available on the software distribution page in the WRC.

Marc Mundt · Mar 20, 2023 go to post

Hi Rochdi,

You don't need to use %File and then copy it to a file stream. You can just use %Stream.FileCharacter to directly open and read the file:

Set stream=##class(%Stream.FileCharacter).%New()
  Set sc=stream.LinkToFile("c:\myfile.txt")
  While 'stream.AtEnd {
  Set line=stream.Read()
  ; Process the chunk here
  }

And I can confirm that I've also seen %Stream.FileCharacter significantly outperform %File for reads.

-Marc

Marc Mundt · Feb 7, 2023 go to post

I suspect the problem is that you're using a %DynamicObject. BPLs should use persistent/persistable objects because business process execution can in some cases be suspended temporarily and then resumed. Before execution is suspended, context objects are saved into the DB.

%DynamicObjects are not persistent objects, so their values are lost when execution is suspended. You can overcome this by using %ToJSON to serialize the %DynamicObject into a stream property of the context object. Streams are persistable.

The life cycle of a business process requires it to have certain state information saved to disk and restored from disk, whenever the business process suspends or resumes execution. This feature is especially important for long-running business processes, which may take days or weeks to complete.
Marc Mundt · Jan 18, 2023 go to post

I've seen Zen reports used extensively for Chinese content, so they can definitely handle the far reaches of the Unicode realm.

What happens if you do this?

write !,"<PostInfo>My GE: "_$c(8805)_"</PostInfo>"

or this?

write !,"<PostInfo>My GE: "_$zcvt($c(8805),"O","UTF8")_"</PostInfo>"

Some other things to check:

Marc Mundt · Jan 12, 2023 go to post

It's not clear to me what "early binding" means in the context of a data transformation. Do you mean something that happens at the time the DTL is compiled rather than at runtime?

If you can clarify what you mean by "early binding" that will help us get you an answer.

Marc Mundt · Jan 12, 2023 go to post

It would be great to have a cheat-sheet for all of these keyboard shortcuts and other tricks, especially in terms of "if you used to do X in Studio, here's how to do that in VS Code".

A few months ago I happened across the incantation for the VS Code equivalent of Studio's "Find in Files" (free text search of server-side classes) but forgot the key combination and haven't had the time to try to figure out which docs to check and guess at whether it's a standard feature of VS Code or of the ObjectScript plug-in.

Marc Mundt · Dec 6, 2022 go to post

Yes, you can absolutely do that. You separate statements with 2 spaces. Here's an example:

for i=1:1:10 {
    write i,!
    write i*10,!
}

This gives the same output:
for i=1:1:10 {  write i,!  write i*10,!  }

It also works without brackets, but IMHO is less readable:

for i=1:1:10  write i,!  write i*10,!
Marc Mundt · Nov 7, 2022 go to post

Menno,

In your ObjectScript code you never base64 decode the key:

set key="pZR8qfrz7t47G+dboyJCH4NnJRrF+dJbvxq37y/cLUo="
...
Set encrypted=$SYSTEM.Encryption.AESCBCEncrypt($zcvt(text,"O","UTF8"),key,iv)

But you did this in Python:

keyBase64="pZR8qfrz7t47G+dboyJCH4NnJRrF+dJbvxq37y/cLUo="
key = base64.b64decode(keyBase64)
...
cipher = AES.new(key, AES.MODE_CBC)
Marc Mundt · Nov 7, 2022 go to post

Adding the additional logging helped. Looking at it now, it looks like it is working as coded:

9413847-9428572 BEGENING OF LOOP FOR: XAL465.1110.A0
9413847-9428572 START RECURSIVE
9421986-9428572 Has Value
9413847-9428572 BEGENING OF LOOP FOR: XAL465.1110.A1

The call for 9413847 is looping through all results of the query. It finds that 9421986 has a composition so it calls itself recursively. The call for 9421986 finds that it already has a value so it returns control to the 9413847 loop which continues iterating.

If you want all super and sub loops to quit as soon as any sub-loop finds "has value" then you will need to check for this condition inside the loop.

Marc Mundt · Nov 4, 2022 go to post

It's a bit hard to follow the output. I think things will become clearer if you add "mainArticle" and "article" to every WRITE statement, for example:

W !, $G(mainArticle),"-",$G(article)," Has Value"
...
W !, $G(mainArticle),"-",$G(article)," BEGENING OF LOOP FOR: "_rs.articleCode
...
W !, $G(mainArticle),"-",$G(article)," START RECURSIVE"
...
W !, $G(mainArticle),"-",$G(article)," END OF LOOP"
Marc Mundt · Oct 18, 2022 go to post

The ORC group is repeating, and all of it's child segments are optional, so you can iterate through the ORC group in order to get at the OBRs that it contains.

But... you can also just set the DTL create mode to "copy" and all of the segments will get copied over automatically without needing to iterate through each ORC.

Marc Mundt · Oct 12, 2022 go to post

Are you just calling out to a command-line utility -- like calling PGP to decrypt a file? Or are you trying to launch a GUI application that the user will interact with?

Marc Mundt · Sep 16, 2022 go to post

Hopefully this can save you some work. It uses a much larger chunk size (which is a multiple of 57) and works with or without CR/LFs (set the argument pAddCRLF):

Class Example.B64.Util Extends %RegisteredObject
{

/// Be cautious if changing CHUNKSIZE. Incorrect values could cause the resulting encoded data to be invalid.
/// It should always be a multiple of 57 and needs to be less than ~2.4MB when MAXSTRING is 3641144
Parameter CHUNKSIZE = 2097144;

ClassMethod B64EncodeStream(pStream As %Stream.Object, pAddCRLF As %Boolean = 0) As %Stream.Object
{
    set tEncodedStream=##class(%Stream.GlobalCharacter).%New()
    
    do pStream.Rewind()
    
    while ('pStream.AtEnd) {
        set tReadLen=..#CHUNKSIZE
        set tChunk=pStream.Read(.tReadLen)
        
        do tEncodedStream.Write($System.Encryption.Base64Encode(tChunk,'pAddCRLF))
        if (pAddCRLF && 'pStream.AtEnd) {
            do tEncodedStream.Write($c(13,10))
        }
    }
    
    do tEncodedStream.Rewind()
    
    quit tEncodedStream
}

}
Marc Mundt · Jul 21, 2022 go to post

My experience with Zebras was quite a few years ago, so this may or may not still apply... Using Zen reports at the time, the print server would end up rendering the label as a bitmap and sending that over to the Zebra. ZPL code to print an equivalent label was much smaller than a bitmap, so ZPL labels tended to print faster than those rendered by a report.

Marc Mundt · Jun 14, 2022 go to post

Ok, so the flow would look roughly like this:

  • The GenericService accepts an inbound REST request, populates a GenericMessage, and sends it to your business process
  • Business process extracts the JSON payload from the GenericMessage, and pulls out any relevant details needed for the call to Athena
  • Business process creates a new GenericMessage, populates any items needed by Athena, uses %SYS.OAuth2 (and the OAuth client profile you created) to request an OAuth token and adds it to the GenericMessage, and passes the new GenericMessage to the business operation.
  • Business operation makes the outbound REST call to Athena, and returns a new GenericMessage containing the response to your business process.
  • Business process extracts JSON payload from the GenericMessage, uses a transformation to create the payload required by your internal REST client.
  • Business process creates a new GenericMessage, populates it with the response payload, and returns it to the GenericService
  • GenericService returns response to REST client