Robert Cemper · Sep 17, 2020 go to post

quick sketch:

  • open file 
  • start loop
  • read line
  • split pieces P1,P2
  • set ^global(p1,p2)=""
  • done  
Robert Cemper · Sep 15, 2020 go to post

If there are queries hanging around then the mayor impact is just on IRISTEMP or CACHETEMP database that might grow.
But it's not worth mentioning just for a query. 

Robert Cemper · Sep 15, 2020 go to post

with complete I mean something starting with

Class.... Extends %Persitent......
all Properties, .... 
until the last line of Storage definition. Just a copy out of Studio
actually at least I just see a little bit here, some part there like a puzzle.

Robert Cemper · Sep 15, 2020 go to post

Have you ever considered asking ADSC for help ?
Do you have some maintenance contract with them as it seems to be their code.?
With a COMPLETE class definition and some sample data, it shouldn't be magic.

Robert Cemper · Sep 15, 2020 go to post

Typically a %ResultSet is an object. It is destroyed as soon as you de-reference it.
This could be a ba killing the referencing variable or by assigning a different value.
there is no need for a %Close or an object destructor.  this happens implicitely.

set rs= ##class(%ResultSet).%New()
. . . . ; work it 
kill rs
; or 
set rs=""
Robert Cemper · Sep 15, 2020 go to post

What you see could be explained best by "model mismatch"
Java Gateway connection stays as long as the connecting process exists.
While CSP.REST services a  request and stops.
To avoid repeated connection times you may run some background process as worker jobs that open Java Gateway
and get its requests from your CSP.Rest.
As you have Ensemble already, you might run your Java Gateway as a Business Service connecting OnInit() an stay connected.
This is of course all asynchronous processing which affects your response time.

If you revert it and a Java process is scanning request using Native API for Java I wouldn't expect more attractive timing. 
 

Robert Cemper · Sep 14, 2020 go to post

BIG THANKS to @Dan Pasco for sharing this example: 

The test is simple - I am running in the 2020R1 instance, namespace USER and there are no classes runnable in this namespace - just a clean, new install. I have several other instances running, all different versions/build numbers. I am testing against my XDBC instance which is currently labeled as 2020.4 (obviously not yet released but the IRIS Native code is essentially the same as in 2020.1). The XDBC instance is listening on port 51780. First, proof this doesn't work locally.

USER>write ##class(Sample.Person).CurrentAge($h-35000)                 

WRITE ##CLASS(Sample.Person).CurrentAge($H-35000)
^
<CLASS DOES NOT EXIST> *Sample.Person

And then, attempt the same function using the IRIS Native connection to the XDBC instance.

USER>set host="localhost",port=51780,namespace="USER",user="_SYSTEM",pwd="SYS" 

USER>set connection = ##class(%Net.DB.DataSource).CreateConnection(host, port, namespace, user, pwd)

USER>set iris = connection.CreateIris()                                                             

USER>write iris.ClassMethodValue("Sample.Person","CurrentAge",$h-35000)                             
95
Robert Cemper · Sep 14, 2020 go to post

Hi, @Dan Pasco 
Since what release does this exist.?
It never crossed my way and obviously nobody else reacted to these questions.

I found a  first trace showing up in Class References of 2020.1 but it is even invisible in IRISlatest docbooks.
Big THANKS for uncovering this secret. yes

Robert Cemper · Sep 14, 2020 go to post

some ideas from the hip:

  • classic SOAP service
  • running a background job over ECP
  • having a listening job that triggers whatever you need
  •  using WebSocket communications

They all run over TCP as there is nothing else between separated servers.
At the bottom line, you rely on some code expecting a request.
"Native API" for various languages, or Studio, or CSP do the same over Super Server Port (1972) 
the quality is different, not the principle.

Robert Cemper · Sep 14, 2020 go to post

IRIS compiles NOW embedded SQL totally different from Caché / Ensemble.  (external Class)
Before you could run in all kind of problems if your host variables were not %*  or declared in the global variable scope.
 

Robert Cemper · Sep 14, 2020 go to post

After a lot of troubles around variable scoping in past I try to avoid embedded SQL wherever possible.

Robert Cemper · Sep 12, 2020 go to post

Fully with you.
There are so many variants to let talk them to each other. 
And on the same server there are enough other options. 

Robert Cemper · Sep 12, 2020 go to post

It's not clear what you mean by "native".
This is one option. Background Jobs over ECP.
But as IRIS Servers also talk to each other over TCP I don't see any advantage over using a SOAP service or WebSockets or similar.

Robert Cemper · Sep 11, 2020 go to post

AHHH!
that sort of comfort isn't foreseen.
You have to type in the variable names.
But you see it when you move the cursor over the variable in the code window as "hoover"-text.
The Watch Window is useful if you have objects, or $LB structures, arrays, ... or obj.property.

Schönen Abend noch aus Wien.

Robert Cemper · Sep 11, 2020 go to post

At first sight, the sequence of your values matches the sequence of your properties.

transforming your input from 

fecha1|nombreProfesional1|centro1|secuencia1|hora1|bloque1|acto1|lug....

to

'fecha1','nombreProfesional1','centro1','secuencia1','hora1','bloque1','acto1','lug....

in an exercise for beginners.
Next  I would use SQL to insert the record

INSERT  INTO  
EsquemasDatos_DragoAP.ConsultarCitas (fecha1,nombreProfesional1,centro1,secuencia1,hora1,bloque1,acto1,lug....)

VALUES ('fecha1','nombreProfesional1','centro1','secuencia1','hora1','bloque1','acto1','lug....)
 

An that's it!

Listing of the columns is only necessary if you distrust the class compiler
or if you want to prevent problems with later sequence changes in the class definition.
In any case, it should reflect the sequence of your data input, independent of sequence in class.

I'd suggest to use one of the ResultSet classes and avoid embedded SQL.
The whole SQL statement can be composed as a single string  and then processed .

more on INSERT and variations
 

Robert Cemper · Sep 11, 2020 go to post

Confirm. No Caché in containers.
I built one myself a year or more ago. Not real rocket since but quite some effort. 
(late thanks to @Dmitry Maslennikov and  @Luca Ravazzolo )
Now I'd position it as less rocket than before. But not really worth the effort.
I finally grabbed a VM instead for my "exercises". 
Today I would just use WSL2 instead with a default Linux distribution.

Robert Cemper · Sep 11, 2020 go to post

laughI enjoyed it!  reminds me of competitions on how to get some results by a minimum of characters.  eg. list a global.

With expanding / compressing code I Studio [ <ctrl>E, <ctrl><shift>E ]  this is all history

Robert Cemper · Sep 6, 2020 go to post

just reading it:
ERROR <Ens>ErrParsingExpression: Error parsing expression
'GenerateNACK("For NewBorn Msg, PID21 Mother Idendentifier should have MRN number",%ErrorStatus)':
0--------10--------20--------30--------40--------50--------60--------70--------80--^ 83 !!!

ERROR <Ens>ErrInvalidName: Invalid name at offset 83   >  

either variable %ErrorStatus is missing or not an object of type %Status

Robert Cemper · Sep 5, 2020 go to post

simply NO! no OS would allow this.
1 port = 1 listener
but you may build a listener dispatching to various services according to some content / signal you receive.
or using a kind of load balancer to distribute incoming messages

Robert Cemper · Sep 3, 2020 go to post

The Native API connects to IRIS by starting a background JOB
In namespace %SYS you can have access to every fresh started  job.
The routine name is %ZSTART.mac.
You have to create it if not existing yet or to adapt it.
My example is just reduced to your case.
Writing a log in global ^%CHECK is just for testing.
I used Native API and also Studio for testing.

%ZSTART ; User startup routine.
#; cannot be invoked directly
    quit SYSTEM ;
#; InterSystems IRIS starting
    quit
JOB ;
#; JOB'd process begins
    new ipaddr,halt
    set ipaddr=$system.Process.ClientIPAddress()
    set halt=(ipaddr="172.17.0.2")
    set ^%CHECK($I(^%CHECK))=$lb(halt,$job,$ZDT($zts,3,,3),ipaddr,$znspace,$io)
#; add here all checking for allowed IP Address
#; halt if access is not allowed
    if halt halt
    quit
CALLIN ;
#; connect over CALLIN
    quit
LOGIN ;
    quit

This is a test result with Studio and 2 Native API. One  blocked and the other allowed.

^%CHECK=5
^%CHECK(1)=$lb(0,"15044","2020-09-03 16:22:54.667","192.168.0.9","USER","|TRM|:|15044")
^%CHECK(2)=$lb(0,"17100","2020-09-03 16:23:41.247","192.168.0.9","%SYS","|TCP|51773|17100")
^%CHECK(3)=$lb(0,"9880","2020-09-03 16:24:20.612","192.168.0.9","%SYS","|TCP|51773|9880")
^%CHECK(4)=$lb(1,"19544","2020-09-03 16:24:41.925","172.17.0.2","%SYS","|TCP|51773|19544")
^%CHECK(5)=$lb(0,"3268","2020-09-03 16:25:27.691","172.17.0.3","%SYS","|TCP|51773|3268") 
Robert Cemper · Sep 3, 2020 go to post

let's have  your  binary  input in variable data 

JAVA                                  ObjectScript
byte[] data = new byte[1024];                                            ;
Bytebuf buf  = new ByteBuf(data)      set buf=data              ; make your work copy  
byte type = buf.read()                set type=$extract(buf)    ; if type = character 
                                      set type=$ascii(buf)      ; if type = binary
int len = buf.ReadInt()               set len=$zlascii($extract(buf,2,5))           ; if order low->high
                                      set len=$zlascii($reverse($extract(buf,2,5))) ; li order high->low
 
byte[] bodyBuff = new byte[len]       
buf.Read(bodyBuff)                    
String str = new String(strBody)      set str=$extract(buf,6,*) ; take rest of buf


from the description there are 2 open points:

  • is type a binary value or a character 
     
  • for length it could be
  • -         low byte first   so 515  => x/02 03 00 00  ;  default
  • -  or high byte first  so  515 => x/00 00 02 03   ;  need to reverse order

therefore I have added both variants