go to post Alex Woodhead · Sep 13, 2023 You can check for XML invalid characters by decoding the encoded payload. For example: zw $SYSTEM.Encryption.Base64Decode("VXRpbHMJOyBVdGlsaXR5IE1ldGhvZHM7MjAyMy0wOS0xMSAxNjo1Nzo0MiBBTgoJcSAKY2hrQ3RybChmaXg9MCkKCWsgXmdnRwoJcyB0PSJeUkZHIgoJcyBjdHI9MAoJdyAiUkZHIiwhCglmICBzIHQ9") "Utils"_$c(9)_"; Utility Methods;2023-09-11 16:57:42 AN"_$c(10,9)_"q "_$c(10)_"chkCtrl(fix=0)"_$c(10,9)_"k ^ggG"_$c(10,9)_"s t=""^RFG"""_$c(10,9)_"s ctr=0"_$c(10,9)_"w ""RFG"",!"_$c(10,9)_"f s t=" Look for a $C( ? ) where ? is in 0,1,2,3 .. 30,31. Note: Tab $C(9) and New line ($C(10) and $C(13,10) are fine. Sometimes cut-n-paste from an email / word document will use $C(22) as a quote character.
go to post Alex Woodhead · Aug 16, 2023 Sometimes a sending application can have pauses sending data and this is mitigated by increasing the read-timeout. This is also indicative of the thread / process at the sending system closing the connection. Curious if this always happens on a standard configured description of the particular observation. Or is this free text entered by a particular upstream user (eg: copy and paste from word document, eg: Line breaks not escaped in HL7, ASCII 22 being used for double quote ). Is the input content incompatible with the encoding that is being used to transmit the message and crashes the transmit process. Suggest review the error log on the sending system could be useful.
go to post Alex Woodhead · Jul 11, 2023 Ens.StringResponse is a System class that will be overwritten with each upgrade. If the Ens.StringResponse class was previously customized, this is your opportunity to correct that limitation. Recommend, create a new class not using the "Ens" package.
go to post Alex Woodhead · Jul 10, 2023 Yes. Needs to use an SSL Configuration for HTTPS. Otherwise connection will close as handshake fails. I tend to isolate to test a connection from server context if possible: set request=##class(%Net.HttpRequest).%New() set request.Server="www.intersystems.com" set request.Port=443 set request.SSLConfiguration="TEST" set request.Https=1 set tSC=request.Get("/",2) do $SYSTEM.Status.DisplayError(tSC) Switching the SSL off causes the error: set request.Https=0 set tSC=request.Get("/",2) do $SYSTEM.Status.DisplayError(tSC) ERROR #6097: Error '<READ>Read+28^%Net.HttpRequest.1' while using TCP/IP device '9999'
go to post Alex Woodhead · Jun 29, 2023 ClassMethod help() { set src = "{""name"" : ""greg"", ""weight"" : 220.00 }" set obj = ##class(%Library.DynamicObject).%FromJSON(src) write obj.%Get("name",,"string"),! write obj.%Get("weight",,"string"),! return $$$OK } Output: USER>Do ##class(Test.DynaNumString).help() greg 220.00
go to post Alex Woodhead · Jun 25, 2023 Hi Evgeny, Not saying this is best way but this indirection can be achieved with python eval. For example: $Classmethod equivalent classname="%SYSTEM.SYS" methodname="ProcessID" eval(f"iris.cls(\"{classname}\").{methodname}()") $Property equivalent Instantiating a Python exception and then iterate over properties printing out name and value: myerror=iris.cls("%Exception.PythonException")._New("MyOops",123,"def+123^XYZ","SomeData") for propertyname in ["Name","Code","Data","Location"]: propvalue=eval(f"myerror.{propertyname}") print(f"Property Name {propertyname} has value {propvalue}\n") output was: Property Name Name has value MyOops Property Name Code has value 123 Property Name Data has value SomeData Property Name Location has value def+123^XYZ
go to post Alex Woodhead · Jun 23, 2023 Hi Evgeny, The function $Parameter in conjunction with $This, to access these. For example: value='$Parameter($This, "[Parameter Name]")' For example adding to Stream example yesterday. Vanilla Source class. Class Test.Str2Stream.ExampleIn Extends (%Persistent, %XML.Adaptor) { Property Filename As %String; Property Content As %String; } DTL class. Where the magic happens: Class Test.Str2Stream.Trans Extends Ens.DataTransformDTL [ DependsOn = (Test.Str2Stream.ExampleIn, Ens.StreamContainer) ] { Parameter Salutation = "YeaBuddy"; Parameter Reaction = "LightWeight"; Parameter IGNOREMISSINGSOURCE = 1; Parameter REPORTERRORS = 1; Parameter TREATEMPTYREPEATINGFIELDASNULL = 0; XData DTL [ XMLNamespace = "http://www.intersystems.com/dtl" ] { <transform sourceClass='Test.Str2Stream.ExampleIn' targetClass='Ens.StreamContainer' create='new' language='objectscript' > <assign value='##class(Test.Str2Stream).StringToStream(source.Content,source.Filename)' property='target.Stream' action='set' /> <assign value='target.Stream.WriteLine($Parameter($This,"Salutation"))' property='x' action='set' /> <assign value='target.Stream.WriteLine("These plates are "_$Parameter($This,"Reaction"))' property='x' action='set' /> </transform> } } Testing In: <ExampleIn> <Filename>ABC</Filename> <Content>The session begins... </Content> </ExampleIn> Testing output:
go to post Alex Woodhead · Jun 22, 2023 After a bit of digging I came up with the following equivalents. $Horolog >>> iris.cls("%SYSTEM.SYS").Horolog() '66647,85547' Equivalent access: >>> var=iris.cls("%Library.UTC").NowLocal() >>> var '2023-06-22 23:50:04.386' >>> iris.cls("%Library.UTC").ConvertTimeStampToHorolog(var) '66647,85804.386' $NAMESPACE ($ZNSPACE) >>> iris.cls("%SYSTEM.SYS").NameSpace() 'USER' ZN [Namespace] - aka change namespace Keep your object fingers in the car at all times!! Any created object script references need to be cleared BEFORE changing back. Is it necessary. >>> iris.cls("%SYSTEM.Process").SetNamespace("%SYS") '%SYS' $JOB >>> iris.cls("%SYSTEM.SYS").ProcessID() '1548' $SYSTEM - Instance name >>> iris.cls("%SYS.System").GetInstanceName() 'IRIS123' But you might have same name on different operating system / container so: >>> iris.cls("%SYS.System").GetUniqueInstanceName() 'THEMACHINE.YOURDOMAIN.COM:IRIS123' $ZTIMESTAMP >>> iris.cls("%SYSTEM.SYS").TimeStamp() '66647,81615.3832864' $ZTIMEZONE – Contains the time zone offset from the Greenwich meridian >>> iris.cls("%SYSTEM.SYS").TimeZone() 0 $ZVERSION – Contains a string describing the current version of InterSystems IRIS >>> iris.cls("%SYSTEM.Version").Format(0) 'IRIS for Windows (x86-64) 202x.x.0 (Build xxU) Thu xx 2023 06:22:16 EDT'
go to post Alex Woodhead · Jun 21, 2023 Adding some code collateral to help explore challenge: Class definition top Include (%occInclude, %syConfig) Some code to listing maps in a database ZN "%SYS" set pNamespace="HSCUSTOM" set cns="Map."_pNamespace set map="" set found=0 for { set map=$O(^CONFIG(cns,map),+1,db) quit:map="" set len=$L($P(map,"("),"_") set globalMap=$P(map,"_",len,999) write !,"global match:""",globalMap,""" to Database:",db} } Example output: ... global match:"IRIS.MsgNames("EnsSearchTable")" to Database:ENSLIB global match:"IRIS.MsgNames("EnsWf")" to Database:ENSLIB global match:"IRIS.MsgNames("EnsXPATH")" to Database:ENSLIB global match:"IRIS.MsgNames("EnsebXML")" to Database:ENSLIB global match:"IRIS.MsgNames("Ensemble")" to Database:ENSLIB global match:"IRIS.MsgNames("ITK")" to Database:ENSLIB global match:"IRIS.MsgNames("RuleEditor")" to Database:ENSLIB .... Delete a map set tSC=##Class(Config.MapGlobals).Delete(pNamespace,globalMatch,,$$$CPFSave) Create a map set global="IRIS.MsgNames("EnsSearchTable")" // example like variable "gm" above. kill params sset params("Database")="CUSTOMLIB" // The database you want to use set params("Collation")="" set tSC = ##Class(Config.MapGlobals).Create(pNamespace,global,.params,,$$$CPFSave) // Always apply any pending changes// Always confirm in testing that the configuration "sticks" after a system restart do ##class(Config.CPF).Activate() Alternatives to programmatic approach Consider if using CPF merge is appropriate. See: https://docs.intersystems.com/irisforhealth20231/csp/docbook/DocBook.UI.... Extended global syntax can be useful to copy start / end data between previously mapped and currently mapped database.
go to post Alex Woodhead · Jun 21, 2023 Hi Evgeny, The following is a tool System Management Specialists may use. You can achieve Jobbing and also running arbitrary ObjectScript code by employing RunLegacyTask. For example run arbitrary code: USER>zw ^ABC USER>D $SYSTEM.Python.Shell() Python 3.9.5 (default, Mar 14 2023, 06:58:44) [MSC v.1927 64 bit (AMD64)] on win32 Type quit() or Ctrl-D to exit this shell. >>> myjob=iris.cls("%SYS.Task.RunLegacyTask")._New() >>> myjob.ExecuteCode="Set ^ABC=""123""" >>> res=myjob.OnTask() >>> res 1 >>> quit() USER>zw ^ABC ^ABC=123 For example Jobbing a Routine. Routine (Test.mac) source code: Test Set ^ABC=345 Quit Use legacy Task to Job it off: USER>zw ^ABC ^ABC=123 USER>D $SYSTEM.Python.Shell() Python 3.9.5 (default, Mar 14 2023, 06:58:44) [MSC v.1927 64 bit (AMD64)] on win32 Type quit() or Ctrl-D to exit this shell. >>> myjob=iris.cls("%SYS.Task.RunLegacyTask")._New() >>> myjob.ExecuteCode="Job Test^Test" >>> res=myjob.OnTask() >>> res 1 >>> quit() USER>zw ^ABC ^ABC=345 The status result can be useful to understand problems. For example the Routine line label "Test" was miss-typed as "TEST" >>> myjob=iris.cls("%SYS.Task.RunLegacyTask")._New() >>> myjob.ExecuteCode="Job TEST^Test" >>> res=myjob.OnTask() >>> iris.cls("%SYSTEM.Status").DisplayError(res) ERROR #5001: <NOLINE>zexecuteCode+3^%SYS.Task.RunLegacyTask.11 Enjoy IRIS. No limits :)
go to post Alex Woodhead · Jun 21, 2023 Hi Evgeny, An option is to use a FunctionSet, for example: Class Test.Str2Stream Extends Ens.Rule.FunctionSet { ClassMethod StringToStream(initValue As %String = "undefined", filename As %String = "") As %GlobalCharacterStream { set ret=##class(%GlobalCharacterStream).%New() set ret.Attributes("Filename")=filename do ret.Write(initValue) quit ret } } This will let you see the nice single line "From Source property" and "To Target Property" Where assignment value was: ##class(Test.Str2Stream).StringToStream(source.Content,source.Filename) The Gotcha If you have a test form input message: <ExampleIn> <Filename>ABC</Filename> <Content>ho ho ho</Content> </ExampleIn> And this is not populating output like: <StreamContainer> <OriginalFilename>ABC</OriginalFilename> <Stream><![CDATA[ho ho ho]</Stream> <Type>GC</Type> </StreamContainer> Then go back and check the input message type extends %XML.Adaptor. Class Test.Str2Stream.ExampleIn Extends (%Persistent, %XML.Adaptor) ie: It is a limitation of the Test form that it expects to be able to correlate the content from XML to Test Object. Hope this helps.
go to post Alex Woodhead · Jun 19, 2023 That does sound as if painted into a corner, especially if this is superuser account. The preferences are cached in a Portal Settings global. Can raise an idea on Ideas Portal to facilitate convenient clear / reset of user portal cached settings.
go to post Alex Woodhead · Jun 19, 2023 One way to approach this is with WebServerURLPrefix. The Web Gateway has special handling when it detects the first part of application URL matches the instance name of a connection. Where: https://srv2/[Instance name 1]/csp/sys/UtilHome.csp https://srv2/[Instance name 2]/csp/sys/UtilHome.csp See: https://docs.intersystems.com/irisforhealth20231/csp/docbook/DocBook.UI.... To let Apache know to expect this traffic could use: <Location /instance1> CSP On </Location> <Location /instance2> CSP On </Location> See: https://docs.intersystems.com/irisforhealth20231/csp/docbook/DocBook.UI....
go to post Alex Woodhead · Jun 15, 2023 Hi Brian, Possibly the new size is not calculated if message is not already saved. However, Custom RuleSet functions can be implemented in classes for example: Then the Rule Condition could be: (DocSizeX12(Document))>500000
go to post Alex Woodhead · Jun 9, 2023 I would say Yes in the sense that HL7 messages can be constructed entirely from scratch via code. The main supporting method for this on EnsLib.HL7.Message the method "SetValueAt" can use to add and replace content for an HL7 message.There are various ImportFrom methods (File, Stream, String) where a vanilla base message can be correlated to an HL7 Message.The DocType is assigned by method PokeDocType, after which virtual property paths used by "SetValueAt" method will assign content in the right place as expected.The DTL (Data Transform Language) documents can visually assist converting some other form of structured data into new HL7 messages. They are dynamic in that they can respond not just to a source reference message but leverage Lookup tables to control the content created for output HL7 messages. Then new configuration can be added to lookup tables and this extends the behavior of an existing DTL. For some code example I have a UnitTest framework class that contains code demonstrating correlation of flat content to an HL7 message, setting the DocType and comparing virtual document paths. https://openexchange.intersystems.com/package/UnitTest-DTL-HL7
go to post Alex Woodhead · Jun 7, 2023 I think you hit the utility of the documented approach. To not accidentally delete the databases. If you forgot to delete databases as part of namespace removal, they can be manually tidied up (System Administration->Configuration->System Configuration->Databases). Questions I would also be asking when directed by Customer from a Vanilla "ENSEMBLE" name, to a purposeful named Namespace: Are IRIS.DAT(s) located in the correct disk volume / Drive name. ie: Sufficient capacity for expected use. (Is it supposed to be on the same volume / location as where services are installed). Is there a backup strategy. AND did I retest it yet? Are the databases journal enabled. Check Users don't still have "ENSEMBLE" as default namespace location Check Scheduled Tasks are not expecting "ENSEMBLE" namespace Update documented standard build script One more thought. If you are using Unix / Linux for hosting IRIS, a more recent option is mergeCPF, to update both Namespace, Database and Application settings. Suitable for: migration activity as a testable automated script configuration and setup from vanilla docker images. https://docs.intersystems.com/iris20231/csp/docbook/Doc.View.cls?KEY=ACMF This allows applying updates with a simple text file AND it does both traditional CPF settings AND the [csp web] Application settings in one go. Your question encouraged me to share a little web calculator for CreateApplication settings. So less time needed to figure out Application setting names and values. https://openexchange.intersystems.com/package/MergeCPF-Application-Settings
go to post Alex Woodhead · Jun 7, 2023 Am going to make some assumptions that you have been given an IRIS.DAT file and you want to mount that Database file on a new instance for some evaluation activity. This is likely to not be exactly the situation but can serve as an example to help understand what you need to achieve. A Namespace is a CODE and DATA context that database processes normally operate in. To use the Database file it is "mounted" as a Database configuration. The Database configuration is used by a Namespace, either as the DEFAULT for code and routines OR the data is MAPPED for use by a namespace. As an example I create a copy of an empty database at: C:\InterSystems\somewhere\db\empty2 This directory contains the IRIS.DAT.In System Management Portal I can "Create a Database" (System Administration->Configuration->System Configuration->LocalDatabases) Use the path of new IRIS DAT file: "C:\InterSystems\somewhere\db\empty2" There will be a warning: "Database file, IRIS.DAT, already exists in directory." If you then navigate with System Explorer -> Globals And use "Lookin : Database" for the example is "EMPTY2" Here I added the Global Test If the IRIS.DAT has both code (eg: SQL Table definitions) and Data, possibly you might want to create a new namespace and make the mounted database from earlier as the default database for routine and code for that namespace. So "Namespaces -> Create New Namespace Alternatively, you may want to apply the new Data with code in another existing database for example SQL Table definitions. Here you could decide what data you want to map from the new database file to an existing namespace. (See: Explorer for Globals above) Navigate to Namespaces (System Administration->Configuration->System Configuration->Namespaces) Select "Global Mappings" of the namespace you want to "access data from the new Dat file". Example to map the whole of global "Test" to existing namespace. Note this is will "hide" any data in "Test" that might exist in the default database of the pre-existing namespace. Hint: Be sure to have database files unmounted or instance shut down when first learning to copy / swap database files around. There are advanced ways of doing things. Occasionally you might get an IRIS database file from an operating system with a different Endian composition. The tool cvendian can help translate this to a target operating system endian mode. When moving big DAT files about it can be useful to run an SHA-1 or MD5 checksum hash on the database file before and after copying to ensure there is no corruption. Hope this gave some ideas and helps progress the direction needed for your specific situation.
go to post Alex Woodhead · Jun 6, 2023 Bring up the VSCode command palate.Choose: ObjectScript: Compile current file with specified flags. Use flags: ckb Where:b=Compile Dependent Classesc=Compilek=keep generated source codeu=Skip related upto date classes