My issue is that I do not know how to create / modify the BoundValue datatype. Setting the opcItemPath equal a string of the desired path does not work. I have to manually go back into the UDT and apply the change.
There’s not any way to construct bindings in scripts, for any part of Ignition. You would have to instantiate an expression parser with a carefully constructed execution context, and none of the necessary pieces are exposed, not even in the SDK. It is theoretically possible, though. But totally unsupported.
I know its an old thread, but if you were still wondering, I found that (in 8.3) if you import the BoundValue type you can create a new BoundValue and assign it to a tag's opcItemPath property.
eg. if you want to bind a parameter named opcRootPath:
I just looked at this (and Ignition) for the first time in years. It turns out all that was needed was a .getValue() on the parameter. No need for messing with BoundValue.
Here's a script that dynamically goes through a specified path and automatically adds the nested name to the OPC_Item_Path custom parameter.
Going off the initial example, the OPC_Item_Path variable was FV_102.
Now the OPC_Item_Path for FV_102/FullStall will be FV_102.FullStall
edited to a much more optimized implementation using Gemini
def propagateParamsChildren(startPth):
"""
Robust hybrid approach:
1. Finds all top-level UDT instances directly under the starting path.
2. Sequentially reads child configs inside error boundaries to prevent script crashes.
3. Extracts raw primitive values to preserve explicit datatypes and prevent JSON parsing type-flips.
4. Batches writes per folder to maintain great performance.
"""
startPth = str(startPth).rstrip('/')
topLevelUdts = []
# --- STEP 1: Find the Top-Level Master UDTs (Subfolder Aware) ---
try:
startConfig = system.tag.getConfiguration(startPth, False)
if startConfig and str(startConfig[0].get('tagType', '')) == 'UdtInstance':
topLevelUdts.append(startPth)
else:
# Grab EVERY UDT instance anywhere down the tree recursively
results = system.tag.browse(startPth, {"tagType": "UdtInstance", "recursive": True}).getResults()
allFoundPaths = [str(item.get('fullPath', '')) for item in results if item.get('fullPath')]
# Sort paths shortest to longest so parents are evaluated before their own children
allFoundPaths.sort(key=lambda x: len(x))
# Filter the list so we only keep UDTs that don't have a parent UDT above them
for path in allFoundPaths:
is_child_udt = False
for existing_master in topLevelUdts:
if path.startswith(existing_master + "/"):
is_child_udt = True
break
if not is_child_udt:
topLevelUdts.append(path)
except Exception as e:
print "Error finding top-level UDTs at '%s': %s" % (startPth, str(e))
return
if not topLevelUdts:
print "No UDT instances found to process at: %s" % startPth
return
print "Found %d independent UDT trees to process." % len(topLevelUdts)
# --- STEP 2: Process each UDT tree one by one ---
for masterPath in topLevelUdts:
print "Processing UDT Tree: %s" % masterPath
try:
# Safely fetch the Master UDT configuration
masterConfig = system.tag.getConfiguration(masterPath, False)
if not masterConfig:
continue
masterParams = masterConfig[0].get('parameters', {})
if not masterParams:
continue
# Find all nested child UDTs under this master tree
childResults = system.tag.browse(masterPath, {"tagType": "UdtInstance", "recursive": True}).getResults()
childPaths = [str(item.get('fullPath')) for item in childResults if item.get('fullPath')]
if not childPaths:
print " No nested children found under this UDT."
continue
# Group writes by folder for this specific tree
configsToWrite = {}
# Process child configurations safely one-by-one to isolate path parsing errors
for path in childPaths:
try:
childConfigList = system.tag.getConfiguration(path, False)
if not childConfigList:
continue
config = childConfigList[0]
except Exception as pathEx:
print " Skipping child tag due to read path format error: %s" % path
continue
node_name = config.get('name', '')
childParams = config.get('parameters', {})
modified = False
for k, v in masterParams.items():
if k in childParams:
# FIX: Always extract the raw python/java primitive value out of the wrapper object
parent_val = v.getValue() if hasattr(v, 'getValue') else v
if k == 'OPC_Item_Path':
childParams[k] = "%s.%s" % (parent_val, node_name)
else:
# Pass the clean, raw type (e.g. raw str, int, bool) without the Java object casing
childParams[k] = parent_val
modified = True
if modified:
write_config = {
"name": node_name,
"tagType": "UdtInstance",
"parameters": childParams
}
parent_pth = path.rsplit('/', 1)[0]
if parent_pth not in configsToWrite:
configsToWrite[parent_pth] = []
configsToWrite[parent_pth].append(write_config)
# Bulk write updates back out for this specific UDT tree
for parent_folder, configs in configsToWrite.items():
try:
system.tag.configure(parent_folder, configs, 'o')
print " Successfully wrote batch of %d updates to %s" % (len(configs), parent_folder)
except Exception as writeEx:
print " Error writing batch to folder %s: %s" % (parent_folder, str(writeEx))
except Exception as treeEx:
print " Error processing UDT tree '%s': %s" % (masterPath, str(treeEx))
print "Parameter propagation complete."