System.tag.configure incorrectly overriding multiple UDT tag properties

FWIW, I recently forgot about this and inadvertently added property overrides to a large number of UDTInstance tags :confused: So I created a script to remove the prop overrides on tags part of UDT instances that were not included in a list of property names.

The script copies the tag json back into clipboard, so you can review it first before saving as a json and importing it back in.

Main Functions
def removePropOverrides(tags, retainProps=None, tagpath='', insideUDTInstance=False):
	'''
	This will remove property overrides set on tags that are within UDTInstances that are not
	contained within the predefined retain set, or the retain set passed into the function.
	
	Revision History
	===========
	Rev	Date		Author			Comment
	1.0	2022-10-20	Nick Minchin	Original
	
	
	Args
	===========
	tags:				- tag json e.g. copied into the clipboard by using the "Copy JSON" context menu option from the Tag Browser
	retainProps:		- a set with a list of properties to retain, for example {'writePermissions'}
	tagpath:			- INTERNAL USE ONLY. Keeps track of the relative tag path used to print the properties removed along with the tags they were removed from.
	insideUDTInstance:	- INTERNAL USE ONLY. Keeps track of whether or not the current `tags` are within a UDT instance or not. This will determine if tag props 
						  will be removed or not.
						  
	Return
	===========
	Returns the modified tags as a Py object of dicts and lists
	'''
	fn = 'removePropOverrides()'
	this = removePropOverrides
	if isinstance(tags, str) or isinstance(tags, unicode):
		tags = system.util.jsonDecode(tags)
	
	if retainProps is None:
		retainProps = {}
	if not isinstance(retainProps, set):
		retainProps = set(retainProps)
	
	# these props should always exist and should not be removed!
	retainProps.add('name')
	retainProps.add('tagType')
	retainProps.add('tags')
	retainProps.add('value')
	retainProps.add('parameters')
	retainProps.add('typeId')
	
	# remove all props not in the retain list, if the tag is within a UDTInstance
	if isinstance(tags, dict):
		if insideUDTInstance:
			removedKeys = []
			for key in tags:
				if key not in retainProps:
					removedKeys.append(key)
					del tags[key]
			
			if len(removedKeys) > 0:
				print 'Tag: "{}", Removed keys: {}'.format(tagpath, system.util.jsonEncode(removedKeys))
		
		# check if the tags object has child tags and run the function over these as well
		if 'tags' in tags:
			for tag in tags['tags']:
				insideUDTInstance_ = insideUDTInstance or 'typeId' in tag
				this(tag, retainProps, tagpath + '/' + tag['name'], insideUDTInstance_)
	return tags

def removePropOverridesFromClipboard(retainProps=None):
	'''
	This is a wrapper for removePropOverrides which passes in tag json stored in the clipboard.
	'''
	tag_json = shared.util.clipboard.readText()	
	tags = removePropOverrides(tag_json, retainProps)
	
	return tags

tags=removePropOverridesFromClipboard({'writePermissions'})
shared.util.clipboard.writeText(system.util.jsonEncode(tags))
Clipboard Functions
from java.awt.datatransfer import StringSelection
from java.awt.datatransfer import Clipboard
from java.awt import Toolkit 

def setup():
	global toolkit, clipboard
	
	toolkit = Toolkit.getDefaultToolkit()
	clipboard = toolkit.getSystemClipboard()	

def writeText(text):
	setup()
	clipboard.setContents(StringSelection(text), None)
	
def readText():
	setup()
	from java.awt.datatransfer import DataFlavor
	contents = clipboard.getContents(None)
	return contents.getTransferData(DataFlavor.stringFlavor)

For example, the output print statements will return something like below. If you don't get anything printed, then no tags are affected.

Example Print Statement Output
Tag: "/Folder 1/Instance/Sec", Removed keys: ["deadband","opcServer"]
Tag: "/Folder 1/Instance/Month", Removed keys: ["deadband","opcServer"]
Tag: "/Folder 1/Instance/Day", Removed keys: ["dataType","opcServer","valueSource"]
Tag: "/Folder 1/Instance/Year", Removed keys: ["deadband","opcServer"]
Tag: "/Folder 1/Folder 2/Instance/Day", Removed keys: ["dataType","opcServer","valueSource"]
Tag: "/Folder 2/Instance/Day", Removed keys: ["dataType","opcServer","valueSource"]
Tag: "/Folder 2/Folder 1/Instance/Month", Removed keys: ["deadband","opcServer"]
Tag: "/Folder 2/Folder 1/Instance/Day", Removed keys: ["dataType","opcServer","valueSource"]
Tag: "/Folder 2/Folder 1/Instance/Year", Removed keys: ["deadband","opcServer"]
Tag: "/Folder 2/Folder 1/Instance/Sec", Removed keys: ["deadband","opcServer"]
Tag: "/Folder 2/Folder 1/Folder 2/Instance/Day", Removed keys: ["dataType","opcServer","valueSource"]

And if not obvious, use at your own risk. Check the output before actually importing anything! (and take a backup of your tags in case you need to revert)

3 Likes