but I was unable to write to context.A08Msg.

How did you determine that?

Generally it's not a good idea to pass whole objects received from somewhere else, especially if they could be changed down the road. If we're talking about persistent objects then they have ids and all references to persistent objects are stored as ids in the database. At runtime the id is read and the object is loaded into memory as required.

Ensemble BPL process is a state machine that loads and unloads context to/from disk often, so if some other Ensemble host changes the object it would also change in the base BP after reload cycle and that can cause problems.

As a workaround you can assign clones, that is safe:

<assign property='context.A08Msg' value='request.%ConstructClone(1)'/>

My solution and how I got there.

I started with this:

  r=1:1:s{c=1:1:s{r=1!(r=s)!(c=1)!(c=s)!(r=c)!(s=(c+r-1)){"#"}else{" "c=s{!}}}

First improvement was thanks to @Robert.Cemper who suggested moving i c=s{w !}}} into a first for:

  r=1:1:! c=1:1:r=1!(r=s)!(c=1)!(c=s)!(r=c)!(s=(c+r-1)){"#"}else{" "}

Finally got the idea of using $lb/$lf to get my best result of 76:

 f r=1:1:s w ! f c=1:1:s w $s($lf($lb(c,r,r=s,c=s,c=r,r+c-s),1):"#",1:" ")

Some other ideas that didn't pan out.

First of all I thought about replacing $lf($lb)) with $f() but -1 and 1x numbers became a problem:

  f r=1:1:s w ! f c=1:1:s w $s($f(c_r_(r=s)_(c=s)_(c=r)_$replace(r+c-s,-1,""),1):"#",1:" ")

Other idea was using $translate:

  f r=1:1:s w ! f c=1:1:s w $tr(''$lf($lb(c,r,r=s,c=s,c=r,r+c-s),1),10,"# ")

Interestingly if we allow the box to be made of any symbols, some other solutions became possible. For example binary box (63 symbols):

  f r=1:1:s w ! f c=1:1:s w '$lf($lb(c,r,r=s,c=s,c=r,r+c-s),1)

Right. Forgot about it.

You can use ghostscript, here's how. In your case command would probably look like this:

Parameter COMMAND = "%1 -dBATCH -dNOPAUSE -sDEVICE=txtwrite -sOutputFile=%2 %3";

ClassMethod pdf2txt(pdf, txt) As %Status
{
    set cmd = $$$FormatText(..#COMMAND, ..getGS(), txt, pdf)
    return ..execute(cmd)
}

/// Get gs binary
ClassMethod getGS()
{
    if $$$isWINDOWS {
        set gs = "gswin64c"
    } else {
        set gs = "gs"
    }
    return gs
}

Execute method code.

Also note, that PDF can contain only images instead of text. in that case you'd need OCR.

Yes, to skip exporting storage you need to specify compilation flag:

/skipstorage=1

Description

Name: /skipstorage
Description: In class Export, if true do not export storage definition.
Type: logical
Default Value: 0

You can set it:

  • System-wide
  • As a Namespace default
  • For Atelier only: Project -> Compile Configuration

System and namespace defaults could be set via:

Set sc = $System.OBJ.SetQualifiers(qualifiers, system)

If you want to enable/disable/modify several ensemble hosts, it's better to update them without updating production first and after that update production. Maybe your error is caused by racing production updates. Also add longer timeout on production update.

set sc = ##class(Ens.Director).EnableConfigItem("Item1", 1, 0)
write:'sc $System.Status.GetErrorText(sc)
set sc = ##class(Ens.Director).EnableConfigItem("Item2", 1, 0)
write:'sc $System.Status.GetErrorText(sc)
set sc = ##class(Ens.Director).UpdateProduction(60)
write:'sc $System.Status.GetErrorText(sc)

If you want to compare two in-memory objects, you can use method generators, there are several related articles and discussions on that:

Simple comparator on GiitHib - note that it's a runtime comparator, therefore slow. Better solution would be method generators.

If you're comparing objects of different classes you need to find their common ancestor class and compare using that.

If you're comparing stored objects you can calculate hashes and compare that.

All in all it's a very complex topic and you need to determine what requirements do you have:

  • Streams?
  • Lists? Arrays? Position change?
  • Loops/relationships strategy
  • How many levels to compare?
  • Different classes? Do they have common superclass?
  • Do you need to compare dynamic objects/objects from unrelated classes?

And design your comparator based on that.

Here's a simple hasher on GitHub.