Eduard Lebedyuk · Aug 20, 2021 go to post

Code must be executed in the Business Operation process, I can't offload it to another process.

Eduard Lebedyuk · Aug 20, 2021 go to post

Looks like, that you have some data generated, which you should care about, but with some delay.  

Yes.

how about creating some custom adaptor,  

How would this adapter be triggered every X seconds?

Eduard Lebedyuk · Aug 19, 2021 go to post

Ok, but how does it work?

The P command suppresses terminal screen display. It suppresses all terminal display, including displaying the terminal prompt.
Specifying P with no operand toggles display suppression.

Docs.

Eduard Lebedyuk · Aug 18, 2021 go to post

Alternatively, you could create a function that writes the alerts to an internal table, and then poll that table every x minutes to generate a single detailed alert message?

Sounds promising. I don't know - just wanted to use alerts as that seemed like a tool for a job but maybe a custom solution would be better.

Eduard Lebedyuk · Aug 13, 2021 go to post

What do you want your $list to look like?

If you want the end result to be like this:

$lb("das", "is", "wp", "dsa", "nmk")

try this code (assumes dir is shorter that 3 641 144 chars):

ClassMethod test(dir = "test.txt")
{
    set file = ##class(%Stream.FileCharacter).%New()
    set file.LineTerminator = $c(1)
    do file.LinkToFile(dir)
    
    set str = file.Read($$$MaxStringLength)
    
    kill file
    
    set separator = $c(13,10)
    do {
        set newstr = str
        set str = $replace(str, separator _ separator, separator)
    } while newstr'=str
    
    set:$e(str, 1, $l(separator))=separator str=$e(str, 1 + $l(separator), *)
    set:$e(str, *-$l(separator)+1, *)=separator str=$e(str, 1, *-$l(separator))
    set list = $lfs(str, separator)
    quit list
}

This is a naive implementation assuming you don't care about the speed.

Faster solution would go through file line by line.

Eduard Lebedyuk · Aug 12, 2021 go to post

You have to include a slash before EmergencyId :

It depends on OS. In Linux there's no slash. Windows is with slash.

Eduard Lebedyuk · Aug 8, 2021 go to post

You need to:

  • Open Training.NewProduction in the Management portal
  • Start Training.NewProduction  
  • Stop Training.NewProduction  
  • Open your new production in the Management portal 

After that you'll be able to start your new production.

Eduard Lebedyuk · Aug 4, 2021 go to post

There are two parts to it.

1. Create a trigger after INSERT/UPDATE/DELETE. Triggers can work for both sql and object access:

Trigger NewTrigger1 [ Event = INSERT/UPDATE/DELETE, Foreach = row/object, Language = objectscript, Time = AFTER ]
{
    set ^dbg={fieldname*N}
}

2. In the trigger code send an SMS. You can either use an API (a lot of them available) or talk to a GSM modem.

Eduard Lebedyuk · Aug 4, 2021 go to post

First you need to create corresponding classed. You can do that by either importing the XSD or manually.

Here's a manual approach:

Class test.mensajeWS Extends (%RegisteredObject, %XML.Adaptor) {

Parameter NAMESPACE = "https://wslexnet.webservices.lexnet/3.22";

Property respuestaEnvioMensaje As respuestaEnvioMensaje;
}


Class test.respuestaEnvioMensaje Extends (%RegisteredObject, %XML.Adaptor) {

Property idEnvio As %Integer;

Property tamBloque As %Integer;

Property bytesMIME As %VarString;
}

After that in your BS convert incoming XML string like this:

set reader = ##class(%XML.Reader).%New()
set sc = reader.OpenString(pRequest.EnviarIniciadoresGeneralOut)
quit:$$$ISERR(sc) sc

do reader.Correlate("mensajeWS","test.mensajeWS")

do reader.Next(.mensajeWSObj,.sc)
quit:$$$ISERR(sc) sc

// Process mensajeWSObj
Eduard Lebedyuk · Aug 2, 2021 go to post

Great article.

Some comments on formatting:

  1. Move large code blocks under spoiler to improve readability using button.
  2. There are a lot of empty lines in code blocks, they can be safely removed.

On the article itself my only issue is you create 5 methods per class:

  • GET /class
  • POST /class
  • GET /class/object
  • PUT /class/object
  • DELETE /class/object

That's a lot of code even for 4 classes, but what about a case where there are 10 classes? 50? 100? Generally I would recommend writing 5 methods which would process objects of any allowed class (see RESTForms2).

Eduard Lebedyuk · Jul 30, 2021 go to post

I think the preferred way is to use $SYSTEM.OBJ.Export (or even better - VCS) instead of direct global manipulation to export code.

Eduard Lebedyuk · Jul 29, 2021 go to post

Set in the BP/BPL classes:

Parameter SKIPMESSAGEHISTORY = 1;

it would improve journaling.

Eduard Lebedyuk · Jul 28, 2021 go to post

The BP (and the entire production) is down for the entire modification/compilation/update.

The issue is that for existing instances ResponseHandlers should be updated after BP compilation to point to the new values.

Eduard Lebedyuk · Jul 28, 2021 go to post

For the case where only a new OnResponse method is added the workaround is executing these update queries:

UPDATE process.Context__ResponseHandlers
SET  "_ResponseHandlers" = 'OnResponseXYZ'
WHERE "_ResponseHandlers" = 'OnResponseABC'

Where ABC is an old method name, XYZ is a new method name.

In a case of several new methods they should be executed from the largest number first.

Eduard Lebedyuk · Jul 27, 2021 go to post

Unable to reproduce. Here's my test routine:

ByRef
    kill a
    set a = 1
    set a(1) = 2
    do Test(.a)
    zw a

Test(a)
    set a(1) = a(1) + 1
    set a(2) = -1

And here's an invocation result:

>d ^ByRef
a=1
a(1)=3
a(2)=-1

as you can see a new subscript has been added successfully.

Eduard Lebedyuk · Jul 23, 2021 go to post

Datatype classes are used to define object properties, they allow to:

  • Validate values both generic (for example any defined %Integer property by default checks that it's value is an integer) and based on datatype parameters (for example you can have an %Integer(MINVAL=0, MAXVAL=9) property which in addition to checking that it's value is an integer would check that integer value is between 0 and 9)
  • Provide different representations based on context, specifically:
    • Logical (what's stored in globals)
    • Display (what's shown in object)
    • XSD (for XML export/import)
    • ODBC (returned in SQL)
  • Provide generators for utility methods. Article on some examples.

Docs.

Eduard Lebedyuk · Jul 22, 2021 go to post

Here's an example:

Class User.Assert Extends Ens.BusinessProcess [ ClassType = persistent, Language = objectscript ]
{

Method OnRequest(pRequest As Ens.Request, Output pResponse As Ens.Response) As %Status
{
    Set pResponse = ##class(Ens.Response).%New()
    
    $$$LOGINFO("$$$ASSERT(1)")
    $$$ASSERT(1) // skipped
    
    $$$LOGINFO("$$$LOGASSERT(1)")
    $$$LOGASSERT(1)
    
    $$$LOGINFO("$$$ASSERT(0)")
    $$$ASSERT(0)
    
    $$$LOGINFO("$$$LOGASSERT(0)")
    $$$LOGASSERT(0)

    Quit $$$OK
}
}
Eduard Lebedyuk · Jul 22, 2021 go to post

This looks more like a WRC issue, but I'd wager a guess that the first and second query use different indices.

To be more specific the second query does not use some index due to the need to traverse all RPE.Veterinario rows due to the vet.numConselho > 0 condition.

To solve this issue try to rebuild all indices for these 3 tables.

Eduard Lebedyuk · Jul 22, 2021 go to post

It's a special type of event which is only written to the database if the assert is false. It accepts one argument which must evaluate into 0 or 1. Check the docs for more info.