Tag Change Scripts are not using current Script Module Edits after a Save Event

Ignition 8.1.14

I am working with a team on numerous projects that inherit scripts from the same global project.
The Customer has decided to use Tag Change Scripts (not Gateway Events) to execute certain functions, and these are currently being called on Asynchronous threads.

My issue arises after I edit the global scripts, then I update and save the Global Project to publish the changes.
Now, if I run my tag change script, it will end up using an older version of the script module (i.e. I am not seeing a logging message in my database that I had JUST added to the script call.)

Here is some further information that may help:

  • We usually have anywhere from 1 to 15 designers open across our team on the same Gateway.
  • The Development server we are using is integrated with GitHub for version tracking.
  • My specific Tag Provider is not the default provider for the global project.
  • Our “Gateway Scripting Project” is a layer BELOW the global project, so everything in global is accessible, but maybe not updating in the secondary project? (example: “GlobalProject” > “SiteSpecificProject” > [tag change event script calls])

Any thoughts? Thanks.

An asynchronous thread carries references to the complete interpreter in which they live. You must kill them off and start new ones when the code they are running (including anything called in other script modules) changes.

Not only is your failure to kill these off preventing them from calling your updated code, but they are leaking huge gobs of gateway heap memory.

(This is true of async threads no matter what kind of event starts them--gateway vs. tag is moot.)

Some guidance:

Also, don’t use import on any script module defined in an Ignition project. Use fully qualified names.

So, would Gateway Events be safer or better performing than Tags? And in what way?
I know I prefer using Gateway Events, and I have seen you post else where about this, but are there any specifics I should point out to the customer?

Also, Would you be able to suggest edits for my tag change script?
This is what I have in a UDT Definition:

def valueChanged(...):

	import sys
	import traceback
		
	### This Function will be called on a new thread
	def delayExecution(params):
		packagesite.moduleexample.udtScriptCreateMESMaterial(params, currentValue, tagPath, missedEvents)
		
	### Actual Tag Change Event
	try:		
		params = {}
		if not initialChange and currentValue.quality.isGood() and bool(currentValue.value):
			oThread = system.util.invokeAsynchronous(delayExecution, [params])

	except:
		exType, exValue, exTraceback = sys.exc_info()
		packageglobal.exceptionhandling.exceptionLog(exType, exValue, exTraceback)

They are different. I prefer Gateway Events in a project over Tag Events in most cases. Factors to consider:

  • Gateway Tag Change events have no queue limit--they don't lose events short of total gateway crashes. Tag Events are discarded when five pending events exist for the tag (by default).
  • Gateway Tag Change events are singulated--all of the events for all of the tags listed are executed one event at a time. No parallelism eliminates some need for locking. But any slow execution can block action for other tags. By contrast, Tag events can run in parallel (three at a time by default).
  • Gateway Tag Change events, as part of a project, are disabled if the project is disabled, and can use any script resources in that project. Their existence is independent of the tags they monitor. Tags in a monitor list are guaranteed to run the same code for the event. By contrast, Tag Events are part of the tag, can only use resources from the Global Scripting Project, and edits to individual tags can change the one tag's script. Deleting the tag deletes any trace of the tag's scripts, too.

If you need parallelism, then delegating to another thread is necessary for Gateway Tag Change events. Launching a dedicated thread is pretty heavyweight, though. I recommend creating a ThreadPoolExecutor and pushing your runnables to that. (See later.py for an example.)

Any such delegated event should still run straight through--no sleeps or infinite loops--to completion.

Your code launches a thread per event. Kinda' heavy, but not bad in and of itself. The question is whether the launched thread finishes (exits) quickly. The launched thread will not have access to updated script resources after it starts, and will hold its interpreter in memory until it exits.