UDT Instance - Parameter Editing

Hi all,

I am building an administrative tool to push user inputs back to the parameters on a UDT instance from Perspective. As of now, I have completed all of the relevant functionality, but one bug remains.

When passing data back to the UDT instance, any user input is treated as a String value (rather than either detecting the type of the user input or coercing to the inherited type expected by the parameter). User inputs are compiled to a dictionary in a view custom property e.g. {"param1": "StringVal", "param2": Int, "param3": Float} and then passed to a library script which manages tag provider interactions.

I have found the documented limitation of the system.tag.configure() function that leads to this String conversion behavior and have since tried to use system.tag.writeBlocking() directly for each parameter instead, but the issue persists. Any ideas on how to either autodetect or manually coerce the type of a UDT parameter when updating within a script?

Thanks!

Are you using merge mode in the tag config?

system.tag.configure(full_base_path, tags, "m")

Yes, I used the merge overwrite option when I was working with system.tag.configure()to alleviate the issue of activating some inactive parameters on the UDT instance. It didn't change the datatype behavior at all, still just pushed updated values in as Strings regardless of inherited type or value type (ex: ParamName = 5, Integer gets updated to ParamName = 6, String).

Hmm, that's odd. What version of Ignition? I build all my UDTs from an excel macro gen without issue. For example this below sets opcOffset and opcDatablock to integer values and exporting the tag JSON they remain as integers. This works in 8.3.8

Note: The excess code for folders and such is because this is used to bulk create all the tags from a list of tags in payload but it works for one tag too. Perhaps it'll help you with the format of the tags payload.

# 1. SETUP - Provider Name
provider = "[default]" 

# 2. PASTE DATA (Column A from Excel)
tags_payload = [

{"name": "siteName123/Alarms/Alarm0_0", "tagType": "UdtInstance", "typeId": "[default]siteFolder/udtDigitalAlarm", "parameters": {"desc": "Alarm #001:  Building Shutdown Button Pressed", "opcTag": "Alarm", "opcDatablock": 1, "opcOffset": 7, "enableAlarm": 1, "alarmPriority": "Medium"}},

]

# --- 3. FOLDER SPLITTING & GROUPING ---
folder_groups = {}

for config in tags_payload:
    full_name = config["name"]
    
    # Check if a folder path is included in the name 
    if "/" in full_name:
        # Split by the LAST slash
        path_parts = full_name.rsplit("/", 1)
        folder = path_parts[0].strip("/") 
        leaf_name = path_parts[1]         
        
        # Update the config to strictly use the leaf name
        config["name"] = leaf_name 
    else:
        folder = "" # Root folder
        
    # Group the tag into the correct folder dictionary
    if folder not in folder_groups:
        folder_groups[folder] = []
    
    folder_groups[folder].append(config)

# --- 4. EXECUTE CREATION ---
for folder, tags in folder_groups.items():
    
    # Build the target path
    if folder:
        full_base_path = provider + folder
    else:
        full_base_path = provider
        
    print "Creating {} tags in folder: {}".format(len(tags), full_base_path)
    
    # "m" = Merge mode
    system.tag.configure(full_base_path, tags, "m")

print "Done!"

I ended up writing some typechecking into the script when the user commits a cell edit to the table to reject inputs that don't match the inherited type of the parameter. This seems to be a better pattern for our use case anyway, since a user shouldn't really be overwriting a parameter with a different datatype in the first place. Still not sure how I would have handled the backend issue otherwise, though...