Julius Kavay · May 21, 2020 go to post

radix 10: mantisse *  10**exponent  (the  ISC  floating format)

radix 2 : mantisse * 2 ** exponent ($double/ IEEE 754 format )

Julius Kavay · May 21, 2020 go to post

The first part is easy... string, integer,... that's not the problem.

The second part, is it a date or not depends on the date format(s) you expect (or allow).

But for the first part, you can use this approach:

/// Return values
/// 0=empty, 1=string, 2=integer, 3=float/radix-10, 4=float/radix-2, 5=object
ClassMethod ValType(var) 
{
  if var="" quit 0
  if $isobject(var) quit 5
  quit $select($length(var)>254:1, 1:$translate($ascii($listbuild(var),2),1245678,1122334))
}

Of course, hardcore people would use just one line

ClassMethod ValType(var) { q $s(var="":0, $isobject(var):5, $l(var)>254:1, 1:$tr($a($lb(var),2),1245678,1122334)) }
Julius Kavay · May 21, 2020 go to post

Just to bring things in the right light

I'm a retired person but one needs some level of activity, that's the reason, why I'm here, do give answers and comments (and if the old customers need "a kind of prolonged help", I'm there too).
In early and mid seventies I programmed some so called "desktop computer", learned Cobol, IBM/360 assembler, Algol60 and Pascal as the preferred language on the university. Then, end of 1977 I met M (DEC's V4B).
Until the nineties thre were no IDEs, the first one I used was Serenji (from GeorgeJames) for MSM and Cache's Studio (with a lovely meat-chopper as the compiler icon). And there were no GitHub.
Anyway, we had great projects and great programms and fun.

The above means, I for myself do not need anything, neither Atelier nor VS-Code not even Studio but in case, I want to edit a class or routine, I prefer Studio.
During my active times, Studio was a great editor (and still it is!), sometimes I had more then one Studio open (at the same time, of course) to different Servers or different namespaces that's the reason why I requested the feature (server defined colors) some decades ago. The light red was the connection into, say a productio system, the green was into  a test namespace and blue, for example was the developmet system. A short glimpse at the display, if I saw red, my brain said, "keep fingers from keyboard", if I saw blue then my brain was ready for switch off... 

But I know several people working since decades with Studio only (and with the line editor before - I hope you know ^% and remember it?). Those people are (roughly) between 50 and 60 years old, worked (and still work) with whatever brand of M-Language (DTM, MSM, ISM, GT-M, Cache etc.) and never had contact to "modern" languages like java, C++, javascript, etc.
That's in my opinion the reason, why Atelier not so widespread as it deserved to be.

De facto, we have a two class society of M-programers

a) the young ones,
- they know everything about the current IDEs (Visual Studio, VS-Code, Eclipse, etc.
- they try to master (more or less) the M-World but
- their heart beats for languages as Java, Javascript, C++, Angular, Scala, etc.
- can't live without Git, GitHub, Bitbucket etc.
- they do not understand the essence/character of M-language (especially Cache/Iris)
- and think, MongoDB is the only NoSql-DB
- work few months (or one or two years) for a company, then change
- in the new company start over with another environment (Java, Angular, whatever)
- many are, I use to say, single-threaded: one project at time only
- etc.

b) and the old ones
- they grew up with the M-language
- they understand the power of M as a unity (language and database)
- most of them never used Git (and similar sites) for version control
- some of them never used a version control at all!
- most of them work with Windows (but this has historical reason)
- a few years before retirement there's no incentive to change to new IDEs

The real problem is not which IDE is in use, the real problem is to get people for M-programming, who then use an IDE. There are, I think, more old(er) then young M-Programer. I know for companies, they search for M-Programers but have dufficulties to find one.
That said, at the end of the day, you have to raise interesse for M-Language and M-Database. So you will need another means and not just a new IDE.
Maybe I'm wrong, but it's like comparing the M-Language and M-Database with a dinner by a start chef.
Nobody asks for the IDE they was in use, nobody asks for the pots and stoves they was in use.
After you leave the restaurant, the only thing that remains is, was the dinner good, were you satisfied.
Same goes the M-Language, does it can all things I need, will I be happy with it.
And yes, my answer is a big yes, definitive.

Julius Kavay · May 20, 2020 go to post

In my opinion the very first question is the support. a) Studio

  • it's installed (and updated) with Cache/IRIS
  • if I have a question, there is WRC b) VS-Code
  • it's not installed with Cache/IRIS
  • so I have to care about the installation
  • where to get the suitable files (and updates)
  • and I have to care about the "how to" install it
  • in a case of a problem, who am I contacting?
    1. the WRC? They will say, it's not our product
    2. the community? Are they always (24/7) there?

The current situation with VS-Code and the COS-plugin (my very private opinion) is

  1. it is assumed, that one is an experienced user of VS-Code
  2. there are no installinstuctions and user manuals

ad 1): assume, one day ISC says "VS-Code with COS plugin exists, so we can drop Studio". What should happen to all those COS-programers, how should they master VS-Code? Most of them have never ever heard about VS-Code? Keep in mind, not everybody grew up with mother's breast in one hand and an App in another !

ad 2): yes, there are some YouTube videos. On all videos I saw, VC-Code was already installed

Do not get me wrong, I'm not again VS-Code (the truth is, I would like to use it on my Linux) and I'm not for keeping Studio until eternity (because there is no Studio for Linux). But currently, Studio has some advantage (again, for me):

  • it's installed
  • I can have (and yes, I have) several connections
  • I can give each connection a different (background) color
  • I have the WRC (but I never had a Studio issue)
Julius Kavay · May 2, 2020 go to post

In case, you want some speed, use

set outArray = []

instead of 

set outArray = ##class(%DynamicArray).%New()

Your speed gain: ca. 50%

Julius Kavay · Apr 30, 2020 go to post

The %OpenId() method checks, if the object to be opened ALREADY open (in the current session/job). If it's open then it just returns with an success.  In other case, it loads it from the disk. 

If on your development system, for whatever reason,  there is an open instance  then you save 6.1 million times the "load" operation.

Julius Kavay · Apr 30, 2020 go to post

You are wondering about a SLOW production server and a FAST development machine?

I show you, how to turn your machines (production or development or both) into  a SLOW and FAST systems  by using your own measurement method.

ClassMethod Unclean()
{
   s p=##class(digi.packet).%OpenId("packet||5237")
   d ..runme4()
} 

ClassMethod Clean()
{
   d ..runme4()
}

Login to either of your systems and try the abowe methods in a terminal session. On my local system (of course, with an own persistent class)  the one method is 12 times faster then the other.

Maybe you have an "Unclean" situation on your development system...

Also, sometime it is worth to put a line like

do $system.OBJ.ShowObjects()

as a first line into the runme4() method

Julius Kavay · Apr 16, 2020 go to post

Why do you do make things such complicated?  You neither need  %ListOfDataTypes nor  %ZEN.proxyObject. If I see correctly, at the end of the query you have a characterstream, filled with JSON. Right? So just create this stream on-the-fly!

set json = ##class(%Stream.TmpCharacter).%New(), del=""

  &sql(declare ... )
  &sql(open queryONLWK01)
        
  do json.Write("[")
     for  {
        &sql(fetch queryONLWK01)
        quit:SQLCODE
        do json.Write(del_"{")
        do json.Write("""articlenumber"":" _ articlenumber _ ",") // (*)
        do json.Write("""amount"":" _ amount)
        do json.Write("}")
        set del=","
     }
     do json.WriteLine("]")

(*) if articlenumber is an alphanumeric value then you should do something like this

    do json.Write("""Aarticlenumber"":"_$$json(article)_",")

 where $$json(x) could be something like this ($zconvert() has  no JSON-mode in Cache-2013)
    

json(val) {
   for c="\","/","""",$c(8),$c(9),$c(10),$c(12),$c(13) set val=$replace(val,c,"\"_$tr(c,$c(8,9,10,12,13),"btnfr"))
   for  quit:'$locate(val,"[:cntrl:]",0,j,v)  set $e(val,j-1)="\u"_$e($zh($a(v)+65536),2,5)
   quit """"_$zcvt(val,"O","UTF8")_""""
}
Julius Kavay · Apr 8, 2020 go to post

OK, I installed VSCode together with the objectscript extension on my Linux. Now, I would like to play a little bit with this IDE, merlely for an VSCode beginner it's somewhat difficult. According to https://openexchange.intersystems.com/package/VSCode-ObjectScript there is a  settings file, ".vscode/settings.json",  to make a connection to my  iris server...  I located a file  ".config/Code/User/settings.json". After entering the connection info -this works now. Question, how or where can I enter a second (or even more) Cache/Iris servers?  Next, created a short test routine and saved, but it's not compiled. How to compile it?

I have never used VSCode before, I work since beginning with the Cache-Studio (this means some 20 years or so) and other twenty years before with other editors. Is there somewhere a short tutorial or some other info? It seems, I will need some time to catch this "modern" stuff ;-)

Thank you.
 

Julius Kavay · Apr 2, 2020 go to post

You could try this way:

Class My.Table2 Extends %Persistent
{

Property Name As %String;

Property Age As %Numeric;

Property City As %String;

Property Phone As %String;

ClassMethod Test()
{
    // This is your Data-Object...
    set data=[]
    do data.%Push({"Name":"Joe", "Age":44, "City":"Boston", "Phone":"1-234-4567"})
    do data.%Push({"Name":"Ron", "Age":48, "City":"Dallas", "Phone":"1-234-5678"})
    do data.%Push({"Name":"Eve", "Age":40, "City":"Miami",  "Phone":"1-234-4567"})
    
    do data.%Push($lb("Tommy", 50, "New York", "1-345-6789"))
    do data.%Push($lb("Alexa", 35, "Portland", "1-567-8901"))

    // Now insert all the above data into your table...
    if 'data.%Size() quit
    
    set cnt=0, size=data.%Size()
    while $i(cnt)<=size {
        set rowData=data.%Get(cnt-1)
        &sql(
            INSERT INTO My.Table2 (Name,Age,City,Phone)
            VALUES (My.Table2_DataProvider(:rowData), :row(2), :row(3), :row(4))
        )
    }
} 

ClassMethod DataProvider(rowData) As %String [ PublicList = row, SqlProc ]
{
    kill row
    if $isobject(rowData) {
        set it=rowData.%GetIterator()
        while it.%GetNext(,.val) { set row($i(row))=val } 

    } else {
        for i=1:1:$ll(rowData) { set row(i)=$lg(rowData,i) }
    }
    quit row(1)
}

Storage Default
{
<Data name="Table2DefaultData">
<Value name="1">
<Value>%%CLASSNAME</Value>
</Value>
<Value name="2">
<Value>Name</Value>
</Value>
<Value name="3">
<Value>Age</Value>
</Value>
<Value name="4">
<Value>City</Value>
</Value>
<Value name="5">
<Value>Phone</Value>
</Value>
</Data>
<DataLocation>^My.Table2D</DataLocation>
<DefaultData>Table2DefaultData</DefaultData>
<IdLocation>^My.Table2D</IdLocation>
<IndexLocation>^My.Table2I</IndexLocation>
<StreamLocation>^My.Table2S</StreamLocation>
<Type>%Library.CacheStorage</Type>
} }

Create your data for insert and then

INSERT into yourtable (Prop1, Prop2, ...)

VALUES (sqlProcForTheFirstValue(), :localVarForOtherValues(2), :localVatForOtherValues(3),...)

see the above example.

Take care of the sequence of INSERT names and row(i) values.

Julius Kavay · Apr 1, 2020 go to post

Oh, I just see now, I forgot to add the two test lines

Enter into browser : http://localhost:52773/csp/user/DC.Upload.cls?pswd=123&rpt=456

Enter into Terminal:

IDEV:USER>write ##class(DC.Download).GetFile(123,456,"/tmp/inpfiles/")  ==> 1
Julius Kavay · Apr 1, 2020 go to post

Sorry, but it's not clear to me what you want to achieve.

If you want to provide data if somebody connected to your server,  then use the DC.Upload class (see below).

If somebody has data for you and you want to programatically download this data, use the DC.Download class (see below).

The examples below does not handle the case, where the (Cache/IRIS)Server needs an user authentication (my IRIS  System > Security Management > Web Applications is set to "Unauthenticated")

Server side (Upload)

/// Upload (i.e. provide) data to a remote party
Class DC.Upload Extends %CSP.Page
{
ClassMethod OnPage() As %Status
{
   if %request.Data("myData")]"" {
      do %request.Data("myData").OutputToDevice()
      do %request.Data("myData").%Close() }
   else {
      write "<html><head></head><body>",!
      write "Please provide a correct Password and ReportId<br>",!
      write "</body></html>",!
   }
   quit $$$OK
}

ClassMethod OnPreHTTP() As %Boolean [ ServerOnly = 1 ]
{
   if ..chkPsw(), ..chkRpt(.file) {
      set data=##class(%FileBinaryStream).%New()
      set size=##class(%File).GetFileSize(file)
      set name=##class(%File).GetFilename(file)
      do data.LinkToFile(file)
      set %response.ContentType="application/pdf"
      do %response.SetHeader("Content-Disposition","attachment;filename="_name)
      do %response.SetHeader("Content-Length",size)
      set time=$h-1
      set %response.Expires=$zd(time,11)_", "_$zd(time,2)_" 00:00:00 GMT"
      set %request.Data("myData")=data
 
      } else {
         set %request.Data("myData")=""
      }
  
      quit $$$OK
}

ClassMethod chkPsw()
{
   set psw=$g(%request.Data("pswd",1))
   if psw]"" quit 1
   quit 0
}

ClassMethod chkRpt(name)
{
   set rpt=$g(%request.Data("rpt",1))
   if rpt]"" {
      set name="/tmp/outfiles/74LS13.pdf"
      quit 1
   }
   quit 0
} 

}

Client side (download)

/// Download (i.e. get) data from remote server
/// 
Class DC.Download Extends %RegisteredObject
{
ClassMethod GetFile(psw, rpt, saveTo = "/tmp/inpfiles/")
{
   set http=##class(%Net.HttpRequest).%New()
   set http.Server="localhost"
   set http.Port=52773
   do http.SetParam("pswd",psw)
   do http.SetParam("rpt",rpt)

   if http.Get("/csp/user/DC.Upload.cls") {
      set file=$piece($g(http.HttpResponse.Headers("CONTENT-DISPOSITION")),"=",2)
      set del=$select($zversion(1)=2:"\", 1:"/")
      set file=saveTo_$s($e(saveTo,*)=del:"",1:del)_$s(file="":"noname.dat",1:file)
      
      open file:"nwu":0
      if $t {
         use file
         do http.HttpResponse.Data.OutputToDevice()
         close file
         quit 1

      } else {
        quit "0,Can't open "_file
      }

   } else {
       quit "0,"_http.HttpResponse.StatusLine
   }
} 
}


Julius Kavay · Apr 1, 2020 go to post

Create a temp table with all properties  you need, store it in a Cachte/IRIS-Temp unde $J of the running job (it could be, your applcation runs in several instances at the same time) an use it in your INSERT / UPDATE.

This is your Table

Class My.Table1 Extends %Persistent
{
Property Name As %String;
Property Age As %Numeric;

ClassMethod Test()
{
  d ##class(My.Temp).%DeleteId($j)
  s tmp=##class(My.Temp).%New()
  s tmp.TempID=$j, tmp.Name="Paul", tmp.Age=69
  d tmp.%Save()
  // or popolate the My.Temp via INSERT...  

  &sql(INSERT INTO My.Table1 (Name,Age)
       SELECT Name,Age FROM My.Temp WHERE TempID=$j
      )
}
}

and this is the Temporary Table

Class My.Temp Extends %Persistent
{
  Parameter DEFAULTGLOBAL = "^CacheTemp.TempTable"; Property TempID As %Integer;
  Property Name As %String;
  Property Age As %Numeric;

  Index main On TempID [ IdKey ];

}
Julius Kavay · Mar 30, 2020 go to post

Do I understand you right, you want to get the Value of a VALUELIST as a return value of a method? If yes, then the answer is yes.

Class Some.Class

{

Property Status As %String  (VALUELIST = ",1,2,3,4"); 

Property Rating As %String(VALUELIST = ",Bad,Good,Excellent");

/// For the Status property only
ClassMethod StatusValues() As %String [ CodeMode = objectgenerator ]
{
 for i=%class.Properties.Count():-1:0 if i,%class.Properties.GetAt(i).Name="Status" quit

 set val=$select(i:%class.Properties.GetAt(i).Parameters.GetAt("VALUELIST"), 1:"")

 do %code.WriteLine($c(9)_"quit """_val_"""")
} 

/// For all properties with a VALUELIST parameter

ClassMethod Values(prop) [ CodeMode = objectgenerator ]
{
 set sel=""
 for i=1:1:%class.Properties.Count() {
     set val=%class.Properties.GetAt(i).Parameters.GetAt("VALUELIST")
     set:val]"" sel=sel_""""_%class.Properties.GetAt(i).Name_""":"""_val_""", "
 }
 do %code.WriteLine($c(9)_"quit $case(prop,"_sel_":"""")")
}

}

And a few examples...

Write ##class(Some.Class).StatusValues()  ==> ,1,2,3

Write ##class(Some.Class).Values("Status") ==> ,1,2,3

Write ##class(Some.Class).Values("Rating") ==> Bad,Good,Excellent

Write ##class(Some.Class).Values("OtherProperty") ==>

Julius Kavay · Mar 30, 2020 go to post

not a built-in, but you can easily create one
 

ClassMethod ParamList() [ CodeMode = objectgenerator ] As %List
{
set params=""
for i=1:1:%class.Parameters.Count() set params=params_$lb(%class.Parameters.GetAt(i).Name)
do %code.WriteLine($c(9)_"quit """_params_"""")
} 

ClassMethod ParamString() As %String
{
quit $listtostring(..ParamList())
}
Julius Kavay · Mar 29, 2020 go to post

as far as I know (subject to verification) you  source code will be compiled into a p-code (pseudo- or portable-code). This means, Cache and IRIS behave (regarding program execution) as virtual machines, like the JVM (Java Virtual Machine).

Julius Kavay · Mar 23, 2020 go to post

$listbuild() and all other functions, like $length(), $char() etc.  are  (I call them) basic  functions of COS and have nothing to do with objects. The language of Cache (and of course IRIS )  handles data as  raw (basic) data and not as objects, like some other (but not all) languages.

For example, in JavaScript you can do

 var  text="Some text";
 alert("'" + text + "' has "+text.length + " characters");
 alert("'Some text' has " + "Some text".length + " characters");

because both, a (text)variable and a (text)string have an (object)property name 'length'.

Beside the raw data type COS also has object data type and most of the standard COS functions will accept an object property as argument, for example:

Class Test.Class Extends %RegisteredObject
{
Property Data as %String;
}

set text="Some text"
set obj = ##class(Test.Class).%New()
set obj.Data = "Some text";
write $length(text),!
write $length(obj.Data),!
Julius Kavay · Mar 19, 2020 go to post

JavaScript, as your code shows, is compiled and executed on client side (in the browser).


The part of the code  #()#  you use is CSP (CacheServerPages), which is compiled  and prepared by the server for  execution by client.

Hence, you can't use the #()# syntax in a pure JavaScript code.

Julius Kavay · Mar 17, 2020 go to post
$lb(data1, data2, data3, ... dataN) is built as a string of item1 item2 item3 ... itemN

itemN:= <len> <type> <dataN>

assuming, you are on a little-endian CPU and

l1 = $length(data)
l2 = l1 + 1 // for type byte
l3 = l2 + 1 // for the length itself

then the length is as follows

if l1<=253 then len: <l3>
elseif l1<=65534 len:= <0> <l2-lowbyte> <l2-highbyte>
else len:= <0> <0> <0> <l2-lowbyte-loworderword> ... <l2-highbyte-highorderword>

And don't forget,
$lb(123) is not the same as $lb(1230/10), hence we have a $ls() function!

Julius Kavay · Mar 15, 2020 go to post

I have no idea how you did your endless loop, but the above code can't do that.

Julius Kavay · Mar 14, 2020 go to post

the functions $order() and $query() are your best friends ;-))

Write, for example, a method like this:

ClassMethod(ref)
{
  if $data(@ref)#10 write ref,"=",@ref,! // do something with this node
  set i=""
  for  set i=$order(@ref@(i)) quit:i=""  do ..Show($name(@ref@(i)))
}

So, for testdata like

set tmp(1,2,3)="Joe"

set tmp(1,2,3,4,1)="Paul"

set tmp(1,2,3,4,2)="Paula"

set tmp(1,2,3,5,6)="Tom"

start some testing


do ##class(yourclass).Show($na(tmp))  // shows all entries

do ##class(yourclass).Show($na(tmp(1,2)))  // also shows all entries

do ##class(yourclass).Show($na(tmp(1,2,3)))  // shows Joe

do ##class(yourclass).Show($na(tmp(1,2,3,4))  // shows Paul and Paula

do ##class(yourclass).Show($na(1,2,3,5)))  // shows Tom

do ##class(yourclass).Show($na(tmp(1,2,3,5,6)))  // shows Tom

do ##class(yourclass).Show($na(tmp(2,3)))  // shows nothing

Now, I hope, you can figure out how to get out your desired data

Julius Kavay · Mar 14, 2020 go to post
 set x="1.2.3", y=$name(tmp)
 for i=1:1:$length(x,".") { set y=$name(@y@($piece(x,".",i))) }
 set @y=""
 kill x,y,i

You can put al the above in one line, if you are in terminal. Or put it in a method

ClassMethod setTemp(x) [ PublicList = tmp]
{
 set y=$name(tmp)
 for i=1:1:$length(x,".") { set y=$name(@y@($piece(x,".",i))) }
 set @y=""
} 

Or you take the shorthand

set x="1.2.3", @("tmp("_$tr(x,".",",")_")=""""")

But this works only as long as x contains number_period_numeber_period_...No leading zeros, no alphabetics...

Julius Kavay · Jan 15, 2020 go to post

Ok, I started my Studio (2018.1.1) --> New Class --> ABC.Try --> Finish.

Then removed the Extends... and I left over with

Class ABC.Try
{

}

Then saved (but no compile) and my test in a terminal:

USER>s aa=##class(%Dictionary.PackageDefinition).GetPackageList()

USER>w aa.Find("ABC")
523
USER>

Julius Kavay · Jan 15, 2020 go to post

In the class definition one can create  persistent-, serial-, registered-, abstract- and data-classes.

All of the above classes are contained in ##class(%Dictionary.PackageDefinition).GetPackageList(). Do you have a example for an class, which is not contained in the above method?

Or there is just a misunderstanding?

Julius Kavay · Jan 15, 2020 go to post

The simple (and documented) answer is something like this:

set name="Test.Package"

if ##class(%Dictionary.PackageDefinition).GetPackageList().Find($zconvert(name,"u")) { w "Yes" } else { w "No" }

@Timothy

using ^oddDEF and/or ^oddCOM works today, we hope, it will work tomorrow too, but there is no garantie

Julius Kavay · Jan 12, 2020 go to post

A warning about long time open transactions will be placed in cconsole.log and alerts.log (both files in $system.Util.InstallDirectory()_"mgr" directory) - at least  this is the case what I see on a customers system. The entries look like this:

04/05/19-02:31:34:470 (21012) 1 [SYSTEM MONITOR] TransOpenSecs Warning: One or more transactions open longer than 10 minutes. Process id  18624 22208 (only top 5 shown)
08/29/19-14:31:03:802 (18576) 1 [SYSTEM MONITOR] TransOpenSecs Warning: One or more transactions open longer than 10 minutes. Process id  4128 (only top 5 shown)
09/04/19-17:16:19:090 (21344) 1 [SYSTEM MONITOR] TransOpenSecs Warning: One or more transactions open longer than 10 minutes. Process id  25872 (only top 5 shown)

So it should be as easy as monitoring those two logfiles. But how it looks like on an ECP server, don't ask me.

Julius Kavay · Dec 21, 2019 go to post

You could try this way (I assume, your image is in *.jpg, *.png, *.gif, *.ico or *.bmp  format and you use a Windows OS):

- save the stream to a temp file
- do $zf(-1,"pathToIrfanView.exe pathToInpFile  /resize=(100,100)  /aspectratio  /convert=pathToOutFile")
- read the output into a stream and proceed as needed

Don't forget, if one of the above path contains a space char, you have to put in quotes the whole path.

/resize=(100,100)  /aspectration  means, the picture will be resized to a max width/height of 100 by maintaning the aspect ratio. Example: input=2000x3000 out =67x100,  input=3000x2000  out=100x67

For Unix exists similar tools to use with $zf(-1,...), for example "identify" to get the dimensions of the picture  and "convert" or "imagemagick" for resizing.

Julius Kavay · Dec 21, 2019 go to post

I'm not that java guy, so just a hint only.

There should be a

public static java.lang.String PetNameLogicalToDisplay(...)

method, so use/call this method to display your pets.