go to post Julius Kavay · Jun 22, 2024 The first part (Base 64 encoding is not able to encode ... unicode (2 byte) characters) is correct. The second part (data-->utf8-->base64 and base64-->utf8-->data) is correct only if there is an agreement beetwen the sender and receiver about the double-encoding (utf8+base64). If I was told, I get a base64 encoded file then I expect a file which is only base64 encoded and not a mix of several encodings including base64. A simple way to encode your document could be something like this: ClassMethod Encode(infile, outfile) { // file-binary reads bytes and not characters set str = ##class(%Stream.FileBinary).%New() set str.Filename = infile set len = 24000 // len #3 must be 0 ! set nonl = 1 // no-newline: do not insert CR+LF do str.Rewind() open outfile:"nwu":0 if $test { use outfile while 'str.AtEnd { write $system.Encryption.Base64Encode(str.Read(len),nonl) } close outfile } quit $test }
go to post Julius Kavay · May 13, 2024 If your system does not support JSON (i.e. pre 2016.2?) then give this "dirty trick" a try: - add a zero-width-space character to your numbers- create the output stream- remove the zero-width-space characters Instead of the zero-width-space you can use any other character too, which does not appear in your data (binary data should be base64 encoded). ClassMethod WithQuotes() { set zwsp = $c(8203) // zero-width-space set obj = ##class(%ZEN.proxyObject).%New() set obj.ID = 1234_zwsp set obj.Number=123.45_zwsp if ##class(%ZEN.Auxiliary.jsonArrayProvider).%WriteJSONStreamFromObject(.tmp,obj) { set json=##class(%Stream.TmpBinary).%New() do tmp.Rewind() while 'tmp.AtEnd { do json.Write($tr(tmp.Read(32000),zwsp)) } } do json.Rewind() write json.Read(json.Size) }
go to post Julius Kavay · Apr 18, 2024 In the above code just insert a Lock and Unlock, and the problem is solved //Buid table if it doesn't exist lock +^LockOutOthers // or any other name you wish // No timeout! At this point everybody has to wait // as long as the current job does a table check/update &SQL( ... ... } lock -^LockOutOthers // let the others in Quit outVisitID
go to post Julius Kavay · Apr 15, 2024 As you wrote, %XML.TextReader is used to read arbtrary XML documents. "A text where in the middle a little bit xml-structure sits" isn't XML! Maybe there is a Pyhton library for extracting XML from a text. If not, probably you have to read char-after-char, count each "<" (+1) and ">" (-1) and if the counter is 0 then between the first "<" and the last ">" probably you have a correct XML structure. Oh, and don't forget for <![CDATA[...]]> sequences, which makes the reading more challenging.
go to post Julius Kavay · Apr 13, 2024 Just came to my mind- Cache-5.0.x is likely to be 32 Bit version, Win-11 is 64 Bit (only) (in case, the application uses some .dll, .ocx, etc.)- unlikely that you use it, but as a hint, LAT is not supported anymore- user database is now provided by ISC. In case your application maintains its own users, you can still use your own user database, but the login process will require some "adaption"
go to post Julius Kavay · Apr 13, 2024 As a first step, I would contact your ISC Sales because Cache-5.0.x licenses neither work with (the latest) Cache nor with IRIS. Second, there was a lot of change between Cache-5.0.x and recent Cache/IRIS versions, so I would check to see if there are any problems to expect. A customer of mine "upgraded" fom Cache-5.0.21 to IRIS some four years ago...
go to post Julius Kavay · Apr 12, 2024 Just for the case, you are lost in the working memory space and desperately searching the spot(s) in your programm where a specific object is once again referenced, here a small handy method which could help you /// find all variables which contain a given object(reference) /// /// I: the OREF you looking for /// /// O: "" if the spool-device can't be opened /// [] if no variables contain the given OREF /// [var1, var2, ... varN] an array of variable names (incl. subscripted and orefs) /// ClassMethod FindObject(obj) { set res=[] if $d(%)#10,%=obj do res.%Push("%") new % set %=obj kill obj lock +^SPOOL("nextID") // adapt this lines open 2:($o(^SPOOL(""),-1)+1):1 // to your method of lock -^SPOOL("nextID") // creating new spool IDs if $t { use 2 set spl=$zb do $system.OBJ.ShowReferences(.%,1) for i=1:1:$za-1 { set x=$p($zstrip(^SPOOL(spl,i),"<=>w",$c(13,10))," ",3) do:x]"%.~" res.%Push(x) } close 2 kill ^SPOOL(spl) } else { set res="" } quit res } Example USER>kill USER>set pers=##class(DC.Person).%OpenId(1) USER>set temp=pers, zz(3)=temp USER>write ##class(DC.Help).FindObject(pers).%ToJSON() ["pers","temp","zz(3)"]
go to post Julius Kavay · Apr 11, 2024 I'm not sure wha you want to achive, so I ask a puzzling question: do you want to create dungling object? "I want to remove the object from memory even if it is still referenced in memory", as I understand, would free the memory used by an object but let the object referenc(es) intact, so the reference now would point into nirvana. Is that what you want to do? Why? Can you a little bit elaborate, what is your target or the background respectively?
go to post Julius Kavay · Apr 11, 2024 The other way is, to put the classname into a variable and set var = "MyPackage.MyClass" do $classmethod(var,"Main") set x=$classmethod(var,"Othermethod",params) // or, if you have an instance set obj=##class(MyClass).%New() do $method(obj,"Main") set x=$method(obj,"Othermethod",params)
go to post Julius Kavay · Mar 28, 2024 If you process thousands of time splits, it's a good idea using %List or just a simple %String instead of JSON - you can have a time savings by factor of about 10! /// old line set res=[] for t=t(0):int:t(1) do res.%Push($zdt(t\86400_","_(t#86400),3,1)) quit res /// new line set res="" for t=t(0):int:t(1) set res=res_$lb($zdt(t\86400_","_(t#86400),3,1)) quit res /// or set res="" for t=t(0):int:t(1) set res=res_","_$zdt(t\86400_","_(t#86400),3,1) quit $e(res,2,*) To see the differences, try loops like this /// with JSON s h=$zh f i=1:1:1E5 { s r=[] f j=1:1:10 { d r.%Push("abcd") } } w $zh-h,! /// with %List s h=$zh f i=1:1:1E6 { s r="" f j=1:1:10 { s r=r_$lb("abcd") } } w $zh-h,! /// with %String s h=$zh f i=1:1:1E6 { s r="" f j=1:1:10 { s r=r_","_"abcd" } } w $zh-h,!
go to post Julius Kavay · Mar 28, 2024 /// start, end: timestamp format /// int : the interval in seconds /// mod : 0 = use the time value as is /// +1 = round-up the timestamp to a multiple of <int> /// -1 = round-down the timestamp to a multiple of <int> /// /// return an JSON array: [time1, time2, ... timeN] /// ClassMethod Intervals(start, end, int = 15*60, mod = 0) As %DynamicArray { set res=[], t(0)=$zdth(start,3,1), t(1)=$zdth(end,3,1) for i=0,1 { set t(i)=t(i)*86400+$p(t(i),",",2) if mod,t(i)#int { set t(i)=t(i)-(t(i)#int) set:mod>0 t(i)=t(i)+int } } for t=t(0):int:t(1) do res.%Push($zdt(t\86400_","_(t#86400),3,1)) quit res }
go to post Julius Kavay · Mar 25, 2024 You can edit (or enhance) the above code to give you all matching elements. Below I share a code with you where you can choose the result data type (%List or %String) and the result scope (all the matching elements or just the first match). /// Find common items of two lists or in two delimited strings /// (as used in a $piece-function) /// /// itm1: first list (as %List or comma-delimited %String) /// itm2: other list (as %List or comma-delimited %String) /// ans : 0 = return a comma-delimited %String with the first match found /// 1 = return a comma-delimited %String with all matches found /// 2 = return a %List with the first match found /// 3 = return a %List with all matches found /// /// return value: according to <ans> argument /// /// Hint: the "$d(var)," part is only needed if the <itm1> argument is /// of %List type and can contain an "undefined" element like the /// second element in $lb(11,,33). /// ClassMethod FindCommonItems(itm1, itm2, ans = 0) { set ptr=0, res="", all=ans#2 set:'$lv(itm1) itm1=$lfs(itm1) set:'$lv(itm2) itm2=$lfs(itm2) while $listnext(itm1,ptr,val) { if $d(val),$lf(itm2,val) { set res=res_$lb(val) quit:'all } } quit $s(ans<2:$lts(res), 1:res) }
go to post Julius Kavay · Jan 31, 2024 You have right, I overlooked the [ character, sorry (usually one posts a piece of code and not a piece of picture!). So the above line would be if jsonobj, jsonobj.statusCode = 200 { for i=0:1:jsonobj.value.labReports.%Size()-1 { set pdf(i)=jsonobj.value.labReports.%Get(i).%Get("pdf",,"stream<base64") } ... // do something with pfd(i) streams }
go to post Julius Kavay · Jan 11, 2024 In general, $extract() and $zstrip() are your friends.If you want to strip ONLY the LAST character, then use this set data="abc,," set $extract(data,*)="" write data --> abc, If you want to strip ALL (same) trailing characters, use this set remove="," set data1="abc," set data2="abc,,," set data3="abc,,-,," set data1=$zstrip(data1,">",remove) set data2=$zstrip(data2,">",remove) set data3=$zstrip(data3,">",remove) write data1 --> abc write data2 --> abc write data3 --> abc,,-
go to post Julius Kavay · Dec 21, 2023 If you know which record is locked (i.e. ^My.Global(123) ) then you can identify the locking process (and therefore the user) in a simple method Class DC.Lock Extends %RegisteredObject { /// For a given (global) reference /// return the (exclusive) locking processID and username /// /// ref: a global reference, for example: $name(^My.Global(1,2,3)) /// /// For other lock types (shared, remote) /// use the infos obtained by info_types OWNER, MODE, FLAGS and COUNTS, see /// https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_slock /// ClassMethod Who(ref) { if ^$LOCK(ref,"MODE")="X" { set pid=^$LOCK(ref,"OWNER") if pid { set job=##class(%SYS.ProcessQuery).%OpenId(pid) quit {"pid":(pid), "usr":($s(job:job.UserName,1:""))} } } else { quit {} } } } For example: set ref=$name(^My.Global(123)) lock +@ref:1 if '$test { // in case, the node is locked, // check up, by who is the node locked set who=##class(DC.Lock).Who(ref) write who.%ToJSON() --> {"pid":"2396","usr":"kav"} }
go to post Julius Kavay · Nov 29, 2023 Just a short update, the problem is fixed in IRIS 2022.1 and later versions.
go to post Julius Kavay · Nov 21, 2023 Take a look on %SYSTEM.OBJ class do $system.OBJ.Compile("your.classname") // or to compile a whole package do $system.OBJ.CompilePackage("your.package")
go to post Julius Kavay · Nov 13, 2023 /// Use the propertydefinition-class: /// Open your.classname || propertyname /// For example: set def=##class(%Dictionary.PropertyDefinition).%OpenId("Sample.Person||Name") if def write def.Type
go to post Julius Kavay · Nov 1, 2023 the above approach ist the right way. And I do not see any problem there: First, in the very first line (of the question) it's stated: "I need to develop a tool ... what data is being consumed by a certain process, ... to build an automated test scenario.", which means, this will be used during a development and/or test phase to gather informations about the touched globals (for automated tests). So the performance is not an issue. Second, the suggestion of Paul Waterman can always run, assuming the process runs with the required right and flag. One can always provide the required conditions.
go to post Julius Kavay · Oct 17, 2023 Some twenty years ago, SOAP was a hype and catchword like today ML, AI and Python are and everybody (includen me) used SOAP.At those times I used tcpTrace (https://www.pocketsoap.com/tcptrace/) for debugging. I can't you help more than that.