OPC tag script using UDT parameters

I'm trying to create a UDT for an OPC tag that uses a script. I've found some documentation and topics similar, but I'm struggling to have the script update the desired tags using the parameters from the UDT definition. Any help or guidance on the topic would be great.

script below:

def valueChanged(tag, tagPath, previousValue, currentValue, initialChange, missedEvents):
# Retrieve UDT parameters
	station_id = system.tag.readBlocking(["[Tillsonburg]ShotClockMemory/015_FAST_Trigger_udt_test/StationId"])[0].value
	station_number = system.tag.readBlocking(["[Tillsonburg]ShotClockMemory/015_FAST_Trigger_udt_test/StationNumber"])[0].value
	asset_number = system.tag.readBlocking(["[Tillsonburg]ShotClockMemory/015_FAST_Trigger_udt_test/AssetNumber"])[0].value

	if currentValue.value == True:  # if trigger is activated
	    last_fault_description_tag = "[Tillsonburg]ShotClockMemory/{}_Last_Fault_description".format(station_id)
	    last_fault_description = system.tag.readBlocking([last_fault_description_tag])[0].value
	    values = 0
	    unknown = bool(system.tag.readBlocking(["[Tillsonburg]ShotClockMemory/Unkown_Fault"])[0].value)
	
	    if unknown == False:
	        for x in range(20):
	            fault_val_tag = "[Tillsonburg]ShotClockMemory/Sta{}/Fault{}".format(station_number, x + 1)
	            valuer_tag = "[Tillsonburg]ShotClockMemory/Sta{}/Value{}".format(station_number, x + 1)
	            fault_val = system.tag.readBlocking([fault_val_tag])[0].value.strip()
	            valuer = system.tag.readBlocking([valuer_tag])[0].value
	
	            if last_fault_description == fault_val:
	                if valuer <= 2:
	                    values = 0
	                elif valuer < 5:
	                    values = 2
	                elif valuer < 10:
	                    values = 5
	                elif valuer < 15:
	                    values = 10
	                elif valuer < 20:
	                    values = 15
	                elif valuer < 25:
	                    values = 20
	                elif valuer < 30:
	                    values = 25
	                elif valuer < 35:
	                    values = 30
	                elif valuer <= 40:
	                    values = 35
	                break  # Exit the loop as we found the matching fault
	
	        write_tag = "[Tillsonburg]ShotClockMemory/Sta{}_Number_of_Minutes_Down_predicted".format(asset_number)
	        system.tag.writeAsync([write_tag], [values])
	    else:
	        # Reset unknown fault tag to false if not in the table
	        system.tag.writeBlocking(["[Tillsonburg]ShotClockMemory/Unkown_Fault"], [False])
	else:
	    # Reset values if the current value is not true
	    values = 0
	    write_tag = "[Tillsonburg]ShotClockMemory/Sta{}_Number_of_Minutes_Down_predicted".format(asset_number)
	    system.tag.writeAsync([write_tag], [values])

Desired Flow: Set UDT parameters, update OPC tag from parameters, when OPC tag is triggered the specified tags should update.

The script works with everything hard coded in a standard OPC tag, struggling to use UDT parameters.

Please post code, not pictures of code. Tip: use the </> code formatting button to preserve code indentation and apply syntax highlighting. There’s an edit button (pencil icon) below your post so you can fix it.

That aside, please explain the expected flow with more detail, ie: When tag foo has value bar, tag knick should have value knack written to it. We can't see enough of the code to figure out exactly what you are trying to accomplish.

3 Likes

When I want to use UDT Parameters in the event scripting, I do this:

station_id = tag["parameters"]["StationId"]
station_number = tag["parameters"]["StationNumber"]
asset_number = tag["parameters"]["AssetNumber"]
1 Like

Thanks for the pro tip!

Another pro-tip. This script is not appropriate for a Tag ValueChange script. At best you are performing 7 blocking reads, at worse you are doing 45.

Tag ValueChange scripts, operate in a shared Thread Pool, and should execute in 10's of ms single digit ms. I highly doubt this script performs that well. The limit on that pool is three concurrent threads, once that limit is reached, you will start to miss events which will result in things not working the way they should.

You should always try to reduce the number of calls to system.tag.read*() to as few as possible. From what I can tell you're trying to do in the script, the best you can do is 3 separate reads. Which, IMO is still too many.

A Gateway Tag Change event, is more appropriate, but might not be the best solution. What is the actual goal of this tag data structure?

EDIT: Thanks @pturmel, that's what I was meaning, but was too lazy to go look it up. That's what I get.

2 Likes

This is marginal advice, applicable to small system. A few thousand tags, perhaps, of which only a modest percentage have scripts.

Get much bigger and you will definitely want your tag events consistently running in low single-digit milliseconds, or perhaps even less than a millisecond. Tag events don't scale.

It is very common for Ignition systems to start small and then grow wildly. Don't get caught with crappy code.

3 Likes

Thanks for the help folks. My project will be a small scale project for a very specific part of the plant. My brain was off for a few days and I forgot simple programming. The method I used to get the parameters in the script is below.

	station_id = system.tag.readBlocking(["[.]Parameters.StationId"])[0].value
	station_number = system.tag.readBlocking(["[.]Parameters.StationNumber"])[0].value
	asset_number = system.tag.readBlocking(["[.]Parameters.AssetNumber"])[0].value

There are probably more efficient ways to get the job done than readBlocking, but for an MVP it demonstrates what I want to accomplish with my project.

There's a more efficient way to get the job done with readBlocking.

station_id,station_number,asset_number = [qv.value for qv in system.tag.readBlocking(["[.]Parameters." + tag for tag in ["StationId","StationNumber","AssetNumber"]])]

Would still probably avoid doing it in a Tag Value Change script.

system.tag.readBlocking() doesn't create any external traffic when called in gateway scope, so is fine in tag value change events. The one to fear is writeBlocking.

2 Likes

Good to know, more concerned with "blocking" and execution time. Hadn't really considered external traffic.