[FEATURE] Add option to system.tag.getConfiguration to match output of tag browser `copy JSON`

When you use system.tag.getConfiguration to get the configuration of UDt instances, it retrieves all non-default configuration of the UDT definition’s tags by deep recursively searching.
If you compare this to the Tag Browser’s copy JSON functionality, this only copies the properties of the UDT definition tags that are overridden at face value, as well as of the name and tagType.

When using getConfiguration with configure, the output from copy JSON is required otherwise you will essentially be overriding the UDT Instance tags with all of the non-default properties that have been set on the tags in the UDT Definition, thus defeating the purpose of a UDT.

For example, take this UDT Definition:

{
  "name": "TEST",
  "tagType": "UdtType",
  "tags": [
    {
      "valueSource": "memory",
      "name": "New Tag",
      "tooltip": "This is a great tag!",
      "value": 999,
      "tagType": "AtomicTag",
      "engUnit": "bob"
    }
  ]
}

This is the output from Copy JSON copying an instance of this UDT:

{
  "name": "Test Instance",
  "typeId": "_TESTING/TEST",
  "tagType": "UdtInstance",
  "tags": [
    {
      "name": "New Tag",
      "tagType": "AtomicTag"
    }
  ]
}

And this is the output from system.tag.getConfiguration:

[{
        "path": "[default]_TESTING/Test Instance",
        "tagType": "UdtInstance",
        "name": "Test Instance",
        "typeId": "_TEST/TEST",
        "tags": [{
                "path": "New Tag",
                "tooltip": "This is a great tag!",
                "tagType": "AtomicTag",
                "name": "New Tag",
                "engUnit": "bob",
                "valueSource": "memory",
                "value": "999"
            }
        ]
    }
]

If I was to use configure with the getConfiguration output, I would apply all of that tag config as overrides in the instance, thus practically unlinking all of the tag config from the UDT definition

I’m requesting that an argument be added into the getConfiguration function to determine what configuration to get: all non-default properties using a deep recursive search, or just the non-defaults at face value.

Vote here:

6 Likes

I am needing this exact feature. Below is my temporary fix. Definitely not the most efficient, but works for me.

def getTagConfigStr(tagPath):
	filePath = system.file.getTempFile('.json')
	system.tag.exportTags(filePath, [tagPath])
	tagConfigStr = system.file.readFileAsString(filePath)
	
	return tagConfigStr


def getTagConfigObj(tagPath):
	return system.util.jsonDecode(getTagConfigStr(tagPath))	
2 Likes

Update: Thanks to FrankP, no IO required!

def getTagConfigStr(tagPath):
	return system.tag.exportTags(tagPaths = [tagPath])
	
def getTagConfigObj(tagPath):
	return system.util.jsonDecode(getTagConfigStr(tagPath))	
1 Like

Does it surprise anyone that the alarm "Enabled" property is not behaving correctly when using this method?
I was super stoked to find this method and use it for returning Alarm overridden properties.
But see the following:

Screenshot of a parent UDT definition "Basic/Bool" which has alarm enabled set to FALSE:

Screenshot of a UDT instance where the alarm has been overridden to have a MEDIUM priority, and Enabled set to TRUE (amongst other things):

Output of tag right click "Copy JSON". Note that no "enabled" property comes back...:

{
  "tagGroup": {
    "bindType": "parameter",
    "binding": "{Device}_Direct"
  },
  "eventScripts": [
    {
      "eventid": "alarmAcked",
      "script": "\tsystem.tag.writeBlocking([\"[.]Value\"],  [0])"
    }
  ],
  "alarms": [
    {
      "setpointA": 1.0,
      "AlarmType": "Process",
      "name": "Alarm",
      "priority": "Medium",
      "TagType": "Bool",
      "TagName": {
        "bindType": "UDTParameter",
        "value": "{InstanceName}"
      },
      "TagPath": {
        "bindType": "Expression",
        "value": "{itemPath}"
      },
      "displayPath": {
        "bindType": "Expression",
        "value": "{InstanceName} + \": \" + {Description}"
      },
      "RoleString": {
        "bindType": "UDTParameter",
        "value": "{RoleString}"
      }
    }
  ],
  "name": "Value",
  "tagType": "AtomicTag"
}

,
I also notice that ANY bound "Associated Data" properties are returned iwth the "Copy JSON" function as well, even when they are exactly the same bindings / properties to the parent UDT....

Pretty sad to discover this to be honest, and weeping a bit that it is so difficult to deal with alarm tags and overrides and re-configuring with scripting..

:cold_sweat:
that doesn't sound ideal...

Beginning to think that it is because at some point between UDT instance being created, and now, the parent UDT definition was changed, where Alarm enabled was changed from TRUE to FALSE.

Which could explain this behavour. Which implies the tags config is static somewhat and not updated realtime.
And may need some kind of refresh to get updated.
Will do some testing and see what i find..

Has this changed recently? Because I'm having the opposite problem...when I call system.tag.getConfiguration with a tag based on a UDT, I do get the UDT binding info, but I really want the actual values (for display to the user). Really need to have a flag you can pass in to specify what output you actually want.

[
   {
      "u""historyEnabled":true,
      "u""historyProvider":"u""US_KS_GAR01",
      "u""path":[US_KS_GAR01]"TestInstanceTag/TestParamTag",
      "u""tagType":"AtomicTag",
      "u""name":"u""TestParamTag",
      "u""deadband":{
         "bindType=parameter",
         "binding="{
            "Value_Deadband"
         }
      },
      "u""historyTimeDeadband":10,
      "u""engUnit":{
         "bindType=parameter",
         "binding="{
            "Value_EngUnit"
         }
      },
      "u""valueSource":"u""memory",
      "u""historyMaxAge":60,
      "u""value":3
   }
]

I assume you're talking about getting the actual values of the bound props? (e.g. engUnit). In that case, this has always returned what you're seeing; it's never returned the resultant values. Although that would be super useful to be able to get.

If you want to get the actual values at the moment, you'll need to read them with system.tag.readBlocking

e.g.

system.tag.readBlocking(['tag/path/pump01/status.engUnit'])

Yep, exactly. It's just a bit painful this way - you've always got to check whether a binding or an actual value is returned for each property, then look up the value. Would be great if you could just ask it to give you what you wanted in the first place!

Seems pretty clear to me. Read for values, getConfiguration for, well, configuration. :man_shrugging:

1 Like

I mean, I see what you're saying :slight_smile:

BUT - system.tag.readBlocking is just reading a specific tag (or list of tags), right? I was using system.tag.getConfiguration to get the full recursive list of tags, with all of their details. If readBlocking had a recursion option that would work nicely, but as it stands it takes some extra work.

(Not the end of the world, just a little inconvenient)

I would argue that tag configuration properties are configuration, regardless if they're bound or not. If you want the configuration property values of a tag, currently it's far more work than should be needed to get this when you have bound properties

1 Like

To make it simpler, I would just compile a list of all tagpaths of properties returned from getConfiguration, and just read them all again with readBlocking.
That is, if the configuration returns:

[
   {
      "u""historyEnabled":true,
      "u""historyProvider":"u""US_KS_GAR01",
      "u""path":[US_KS_GAR01]"TestInstanceTag/TestParamTag",
      "u""tagType":"AtomicTag",
      "u""name":"u""TestParamTag",
      "u""deadband":{
         "bindType=parameter",
         "binding="{
            "Value_Deadband"
         }
      },
      "u""historyTimeDeadband":10,
      "u""engUnit":{
         "bindType=parameter",
         "binding="{
            "Value_EngUnit"
         }
      },
      "u""valueSource":"u""memory",
      "u""historyMaxAge":60,
      "u""value":3
   }
]

then compile a list of all of the tag properties from this:

[US_KS_GAR01]"TestInstanceTag/TestParamTag.historyEnabled
[US_KS_GAR01]"TestInstanceTag/TestParamTag.historyProvider
[US_KS_GAR01]"TestInstanceTag/TestParamTag.deadband
[US_KS_GAR01]"TestInstanceTag/TestParamTag.historyTimeDeadband
[US_KS_GAR01]"TestInstanceTag/TestParamTag.engUnit
....

and then read the whole lot with readBlocking. It's a bit inefficient because you're re-reading property values that you've already got from getConfiguration, but it's less code than trying to only read the bound props and combining it with the values you've already got from getConfiguration.

I'm wondering if this behavior is because the default value for an alarm IS 'true'. If you change your Path/To/Tag/Alarms/Alarm.Enabled property to 'false' and then copy JSON again, does the property show up?

(Edit to add my experience):
I performed a 'Copy JSON' from an existing tag of an instance UDT, pasted to notepad, no alarm objects appeared in the JSON:

{
  "name": "TEST_TAG",
  "tagType": "AtomicTag"
}

I then Edited the tag, and clicked to override the single alarm that was configured:

{
  "alarms": [
    {
      "mode": "Inequality",
      "ackPipeline": "MyPipeline",
      "activePipeline": "MyPipeline",
      "name": "Alarm",
      "clearPipeline": "MyPipeline",
      "priority": "High",
      "label": {
        "bindType": "Expression",
        "value": "{pMyParameter}"
      },
      "displayPath": {
        "bindType": "Expression",
        "value": "{pMyParameter}"
      }
    }
  ],
  "name": "TEST_TAG",
  "tagType": "AtomicTag"
}

I then modified the value of the alarm.enabled property and copied JSON again:

{
  "alarms": [
    {
      "mode": "Inequality",
      "ackPipeline": "MyPipeline",
      "activePipeline": "MyPipeline",
      "name": "Alarm",
      "clearPipeline": "MyPipeline",
      "priority": "High",
      "enabled": false,
      "label": {
        "bindType": "Expression",
        "value": "{pMyParameter}"
      },
      "displayPath": {
        "bindType": "Expression",
        "value": "{pMyParameter}"
      }
    }
  ],
  "name": "TEST_TAG",
  "tagType": "AtomicTag"
}