Something to be aware of and which is perhaps not clear in our documentation ( I will check ) is that the metric values returned must be NUMERIC if you are going to use this API with our forthcoming SAM monitoring product. Values like 'OK 'and 'Normal ' and even '0d 1h 18maren't much use in rule evaluation and display in commonly available tools just as Grafana ( which we leverage in our solution).

You can't do that directly. SAX XML  parsing and XPATH are two completely different, unrelated, technologies. The best you could do would be to subclass the ContentHandler class and record the 'path'  as each element is reported. You would want to declare a transient property to hold the 'path' and then incrementally build it up as startElement() is called and pull it down when endElement() is called. When an error occurs, consult the value of the 'path' property. This won't give you an XPATH path but it will give you something similar to give you an idea of where you are in the document.

ROUTINE ColorJSON

#include %occStatus

    // demonstration of using %SyntaxColor to output coloring information in JSON format
    
    // example code to be syntax checked
    // - lines for the syntax checker must begin with space or tab if there is no label
    Set n=0
    Set code($I(n))=" Set sum=0"
    Set code($I(n))=" For i=1:1 {"
    Set code($I(n))="  Read ""Number (return to finish): "",in,!"
    Set code($I(n))="  If in="""" {Quit}"
    Set code($I(n))="  Set sum=sum+in"
    Set code($I(n))=" }"
    Set code($I(n))=" Write ""Sum is: "",sum,!"
    Set code($I(n))=" Quit"
    
    // list code to current device
    Write !,"Code:",!
    For lineno=1:1:n {
        Write lineno,": ",code(lineno),!
    }
    Write !,"--",!!
    
    // write code to a stream and rewind it for reading
    Set input=##class(%GlobalCharacterStream).%New() If '$IsObject(input) {$$$ThrowStatus(%objlasterror)}
    For lineno=1:1:n {
        $$$THROWONERROR(sc,input.WriteLine(code(lineno)))
    }
    $$$THROWONERROR(sc,input.Rewind())
        
    // stream for coloring-output
    Set output=##class(%GlobalCharacterStream).%New() If '$IsObject(output) {$$$ThrowStatus(%objlasterror)}
    
    // create syntax checker
    Set syn=##class(%SyntaxColor).%New() If '$IsObject(syn) {$$$ThrowStatus(%objlasterror)}
    
    // top-level language (moniker) of source code
    Set language="COS"
    
    // flags for JSON output ("K" can't be used with "C" or "H")
    Set flags="K"
    
    // invoke syntax checker
    // - the Color method returns 1 for OK, 0 for ERROR (same applies to the Languages and Attributes methods) 
    If 'syn.Color(input,output,language,flags,,,,.coloringerrors) {
        Throw ##class(%Exception.General).%New("- error calling %SyntaxColor:Color: "_syn.DLLResultCode) // error details are in the DLLResultCode property
    }

    If coloringerrors {
        Write "(coloring errors)",!!
    }
    
    Do ShowJSON(syn,output)
    
    Quit

    
ShowJSON(syn,stream)
{
    Write "Raw JSON:",!
    $$$THROWONERROR(sc,stream.Rewind())
    While 'stream.AtEnd {
        Write stream.ReadLine(,.sc),! If $$$ISERR(sc) {$$$ThrowStatus(sc)}
    }
    Write !,"--",!!

    Write "Decoded JSON:",!
    Write " - format is: OFFSET(COUNT) LANGUAGE:ATTRIBUTE",!
    $$$THROWONERROR(sc,stream.Rewind())
    Set json={}.%FromJSON(stream.Read(,.sc)) If $$$ISERR(sc) {$$$ThrowStatus(sc)}
    
    // for each line ..
    // (the %GetNext loops could alternatively be done using a For loop starting at 0) 
    Set linesiter=json.%GetIterator()
    While linesiter.%GetNext(.lineno,.linejson) {
    
        Write !,"Line ",lineno+1,! // this array is numbered from 0 so we need to add 1 to get a line number corresponding to the code listing
        
        // for each colored item in the line
        Set onelineiter=linejson.%GetIterator()
        While onelineiter.%GetNext(,.item) {
            Write " ",$$ShowItem(syn,item),!
        }        
    }
    Write !,"--",!!
}


ShowItem(syn,item)
{
    Quit item.p_"("_item.c_") "_$$Language(syn,item.l)_":"_$$Attribute(syn,item.l,item.s)
}


Language(syn,langindex)
{
    // in a real program this should be cached
    If 'syn.Languages(.languages) {Throw ##class(%Exception.General).%New("- error calling %SyntaxColor:Languages: "_syn.DLLResultCode)}
    Quit $List(languages,langindex+1) // first language index is 0 so we need to add 1 to langindex
}


Attribute(syn,langindex,attrindex)
{
    // in a real program this should be cached
    If 'syn.Attributes(langindex,.attributes) {Throw ##class(%Exception.General).%New("- error calling %SyntaxColor:Attributes: "_syn.DLLResultCode)}
    Quit $List(attributes,attrindex+1) // first attribute index is 0 so we need to add 1 to attrindex
}

I think you misunderstand. You cannot just add an arbitrary name as a tag! the reference above of course is invalid because someTag does not appear in the dictionary. Each part of the property reference must appear in the dictionary for this to be valid so 'PatientPrimaryLanguageCodeSequence' and 'someTag' both must appear. For your reference :-

(0010,0101) VR=SQ VM=1 Patient's Primary Language Code Sequence

As regards point 3) T

 

The dictionary does contain the individual elements, for example in our work list demo we do this :-

 

Set tSC=pDocOut.SetValueAt("StationAET","DataSet.ScheduledProcedureStepSequence[1].ScheduledStationAETitle") If $$$ISERR(tSC) Quit

 

ScheduledStationAETitle appears in the dictionary with tag (0040,0001).

 

You will need to consult the NEMA DICOM online documentation to determine which sub-tags are valid for a particular structure, in the particular document you are sending. We (ISC) don't maintain that information ourselves.

 

Hope this helps

1) Use Job per connection. A DICOM service can handle multiple remote AETs including wildcard definitions.

2) That method takes a list of TRANSFER SYNTAXES not SOP classes. Use the UI to create a specific association definition.

3) You don't need to create the SQ explicitly , it will come into being when you set the first member, e.g. Something in the example below

Set tSC = pDocOut.SetValueAt("English", "DataSet.PatientPrimaryLanguageCodeSequence[1].Something")

As the implementor of the BPL engine I would caution against this approach.  There is an awful lot of book keeping you have to do to which is better handled by BPL. A technique which has worked well for me in the past is to create a context class which contains much of your logic and then reference that in a simple BPL which performs the calls asynchronously. This is done by setting the 'contextsuperclass' attribute on the BPL process element in the XDATA block. The BPL compiler will use th e named class as the superclass of the context class which it generates during the compilation phase.