I’ve just upgraded from 7.9 to 8.1, and one major headache is the change to how expression bindings evaluated parameters and speech marks.
In most of my UDTs there are alarmed tags which had an expression on the Display Path. This is now evaluating literally, and i need to change it to show the parameters properly.
The trouble is, a lot of these alarmed tags have been overridden, and i’m struggling to find a good way to detect that automatically…
There doesn’t seem to be anything in the JSON tag export to indicate a tag property has been overridden.
system.tag.getConfiguration also doesn’t seem to help.
Any ideas?
Overwrite shows in the exported files as “something”, I don’t have a reference now but to the remove the overwrite try to set that something to nothing.
I’m afraid I was not clear,
Can you share the file of a tag and another with no overwrites to help explain?
Hi there,
Here is a snippet of a tag that DOES have an override on the AlarmPriority expression tag:
{
"expression": "toInt(tag(\"[.]/In.AlertCurrentSeverity\")) + 1",
"value": "0",
"name": "AlarmPriority",
"tagType": "AtomicTag"
},
And here is an example of a similar tag that DOESN’T have the override on the same tag:
{
"value": "0",
"name": "AlarmPriority",
"tagType": "AtomicTag"
},
So there is a difference i suppose, but it is not clear that it is an override. I’m surprised there is no flag.
How does Ignition know during an import that this differs from the original UDT structure?
I still haven’t found a good solution for this… Does anyone have any ideas?
Specifically after how to detect overrides in scripting… not via tag JSON export.
i’m going through a process of updating some UDTs, of which there are thousands of tag instances.
There may be dozens of instances which have specific attributes overridden, but there is no good way to find these via scripting.
I can search for specific properties i know been modified, but this doesn’t help identify things that might slip through the cracks.
2 Likes
The function below can be copied into the Ignition 8.1 Designer Script Console and called from there. You’ll need to provide an XML export that includes all of your deployed UDT instances. The code will recursively work through the XML export to identify overrides in atomic tags within UDT instances, printing the tag path and overridden tag property for each override found. If you would like to exclude certain tag properties, append them to the propExclustionList list. I currently have the ‘value’ tag property in that list so that overridden memory tag values don’t get printed to the console.
#Provide file path to XML export from Ignition designer. XML export must be generated by right-clicking on site folder within Ignition designer
#and exporting to .xml file. Provide full file path to this file including .xml extension, ex. "C:\\Users\\LHammami\\Documents\\tags.xml"
#Recursively browses XML export for "Tags" elements and prints all UDT instance atomic tag overrides found in XML export to script console.
def getUdtOverrides(filePath):
#Required libraries
import sys
import xml.etree.ElementTree as ET
#Properties that will not throw an error if found overridden in UDT instance
propExclustionList = ['value']
#Check Tags structure for Tag overrides
def checkTagsForTagOverrides(Tags, path, udtCheckCount=0, udtInstance=False):
for child in Tags:
if (child.tag == 'Tag') and (child.attrib != {}):
for key in child.attrib:
if (key == 'type') and child.attrib[key] == 'Folder':
if path == '':
folderPath = child.attrib['name']
else:
folderPath = path + '/' + child.attrib['name']
for subchild in child:
if (subchild.tag == 'Tags'):
udtCheckCount = checkTagsForTagOverrides(subchild, folderPath, udtCheckCount, udtInstance)
elif (key == 'type') and child.attrib[key] == 'UdtInstance':
if path == '':
udtPath = child.attrib['name']
else:
udtPath = path + '/' + child.attrib['name']
for subchild in child:
if (subchild.tag == 'Tags'):
udtCheckCount = checkTagsForTagOverrides(subchild, udtPath, udtCheckCount + 1, True)
elif (key == 'type') and (child.attrib[key] == 'AtomicTag') and udtInstance:
for prop in child:
if prop.attrib['name'] not in propExclustionList:
tagPath = path + '/' + child.attrib['name']
print tagPath
print "Tag path: ", tagPath
print "Override: ", prop.attrib['name']
print ''
elif (child.tag == 'Tags'):
udtCheckCount = checkTagsForTagOverrides(child, path, udtCheckCount, udtInstance)
return udtCheckCount
#Print error to console if file doesn't exist. Else check all UDT instances for overrides.
if system.file.fileExists(filePath):
print 'Checking ' + siteFolder + ' tag export file ' + filePath + ' for UDT instance overrides.'
print ''
tagXml = system.file.readFileAsString(filePath)
element = ET.fromstring(tagXml)
udtCheckCount = checkTagsForTagOverrides(element, '')
print udtCheckCount, "UDT instances checked."
else:
print 'ERROR: FILE NOT FOUND'
2 Likes
@LHammami FYI, as of 8.1.19, Ignition has a tag report tool that can be leveraged for detecting overrides, among other things. This tool can used in both designer and scripting.
3 Likes
@jlandwerlen Thank you for the heads up; I wasn’t aware that a tag report tool had been developed. We will be upgrading one of our site server Ignition deployments to 8.1.19, so I will have an opportunity to work with that tool soon. I am sure it will be more robust, intuitive, and user-friendly than the code I posted.
Thanks for the script @LHammami , I’ll be keen to try it out, as well as this new Tag Report tool in 8.1.19. Hopefully one of these tools will do what i need!
@cmason have you tried this 8.1.19 tag report tool? Might be useful to test on the recent upgrade.
Is there a way with the report to to find X properties that is NOT overwritten?
Not easily, no. this would still be some thing for a script. However the tag report tool isn't great for identifying complex prop overrides either so a script is best for that as well
1 Like
Here is a script I use on my focus window that lets me know when a
UDT tag has overrides. Very simple and nothing complex, using the native ignition system tag functions. I use this script as a script transform on a expression structure tag binding that consist of the tag_path
, tag_config
, and udt_type
. I include them below for simplicity. I'm basically just comparing each member of the raw UDT path of the tag to the tag itself, if there's no overrides then every tag member and its value source should be the same.
You can go as far as showing the script behind the value source by building a map or dictionary that translates the value source types as a key and the key name of the script to look for as the value {"expr": "expression", ...{key: value}}
import re
tag_path = "[PD]Electrical Metering/Bus A 480/Communication Status"
tag_config = system.tag.getConfiguration(basePath=tag_path, recursive=True)[0]
udt_type = tag_config["typeId"]
# Get the entire portion of the string including the brackets using group 0, group 1 will return only the text inside the brackets
tag_provider = re.search(r"\[(.*?)\]", str(tag_config["path"])).group(0)
# Construct the **full path** for the base UDT definition
# _types_ is a known constant path component in Ignition to locate UDT Definitions
udt_full_path = "{0}_types_/{1}".format(tag_provider, udt_type)
# Get all the udt tag members as a list of dictionaries
udt_tag_members = system.tag.getConfiguration(basePath=udt_full_path, recursive=True)[0]["tags"]
tag_members_with_ovrds = []
for member in tag_config["tags"]:
tag_member_val_src = member["valueSource"]
tag_member_name = member["name"]
# Searches for the matching member in the UDT structure. If no match is found, it returns `{}` as the default.
udt_tag_member_config = next((member for member in udt_tag_members if tag_member_name == member["name"]), {})
# Prevent an exception using .get() so if the key exist return the value of key else return an empty string
udt_tag_member_name = udt_tag_member_config.get("name", "")
udt_member_val_src = udt_tag_member_config.get("valueSource", "")
if udt_tag_member_name and tag_member_val_src != udt_member_val_src:
tag_members_with_ovrds.append(member)
if tag_members_with_ovrds:
for member in tag_members_with_ovrds:
print "Tag member '{0}' has changed to '{1}'.".format(member["name"], member["valueSource"])
else:
"The UDT tag has no tag member overrides."
It doesn't look like this script handles tags within folders inside the UDT instance, nor does it handle UDT instances nested within the UDT definition (it can get quite convoluted!)
From a quick scan, I assume the output of this simply tells you whether or not a tag directly within the root UDT instance has any overrides? It doesn't tell you exaclty what properties are overridden?
Run it in a console with a UDT tag with overrides and see what you get based on your situation?
The system.tag.getConfiguration(basePath=tag_path, recursive=True)
will get everything including nested UDTs hence the argument recursive = True
The tag_members_with_ovrds.append(member)
appends the tag member which is a dictionary which includes all of its tag properties. If anything is overridden in a nested tag then its parent tag will be overridden so you'll get that information. Honestly it depends on your need adjust this code if necessary to fit your needs you can easily incorporate other system tag functions fairly easy like system.tag.browse
which has a filter parameter. This script has worked for my customers so far so I thought I help somebody out.

This part here though isn't recursive, so it's only looping through the tags in the root layer. You would need a recursively called function to handled nested tags
lists, but then you'd also need to handle any nested UDT instances by getting their configuration as you're doing for the initial one. It gets rather complicated quickly 
However for simple cases where you've just got atomic tags within your UDT definitions, then it sounds like this works well to report what atomic tag members have prop overrides on them. I guess it depends on the person's specific needs. For me, I often want to know exactly what properties have been overridden and what their default and overridden values are. It's this case that becomes rather complicated to script
1 Like
Okay then you just turn this into a function with two parameters get_overrides(tag_config, udt_tag_members)
you add this piece of logic and keep everything else the same
if "typeId" in member:
# Construct the nested UDT path
nested_udt_type = member["typeId"]
nested_udt_full_path = "{0}_types_/{1}".format(tag_provider, nested_udt_type)
try:
# Get the nested UDT definition
nested_udt_tag_members = system.tag.getConfiguration(basePath=nested_udt_full_path, recursive=True)[0][
"tags"]
# Recursively check overrides for the nested UDT members
nested_overrides = get_overrides(member, nested_udt_tag_members)
tag_members_with_ovrds.extend(nested_overrides)
except:
print("Error fetching configuration for nested UDT: {}".format(nested_udt_full_path))
# Otherwise, check for regular overrides
elif udt_tag_member_name and tag_member_val_src != udt_member_val_src:
tag_members_with_ovrds.append(member)
Now you start it like this
tag_path = "[PD]Electrical Metering/Bus A 480/Communication Status"
# Get the tag configuration
tag_config = system.tag.getConfiguration(basePath=tag_path, recursive=True)[0]
udt_type = tag_config["typeId"]
# Extract tag provider
tag_provider = re.search(r"\[(.*?)\]", str(tag_config["path"])).group(0)
# Get the UDT definition
udt_full_path = "{0}_types_/{1}".format(tag_provider, udt_type)
udt_tag_members = system.tag.getConfiguration(basePath=udt_full_path, recursive=True)[0]["tags"]
# Check for overrides, including nested UDTs
tag_members_with_ovrds = get_overrides(tag_config, udt_tag_members)
# Output results, this is just for example get the props you need based on how much information you want to show
if tag_members_with_ovrds:
for member in tag_members_with_ovrds:
print("Tag member '{0}' has changed to '{1}'.".format(member["name"], member["valueSource"]))
else:
print("The tag has no tag member overrides.")
Anyways still nothing complicated just need to carefully think through your situation. You have everything you need with the system.tag functions
No, not particularly complicated once you grasp the concept of recursive functions, but still more difficult than a simple function call to extract it
I would class it as an intermediate to advanced function to implement