To preface i have read a lot of the forums and re-read the documentation every time i encounter this. I dont think i've come across this exact scenario mentioned, but am seeking a better understanding of a gateway side's script's scope.
We have a project that is set as the Gateway Scripting Project. In that project i create a script with the below code
logger = system.util.getLogger("TESTLOG")
TestNum = 0
logger.info('Module Created')
def incTestNum(who):
global TestNum
TestNum = TestNum +1;
logger.info('Task %s incrimented value from %d to %d'%(who,TestNum-1,TestNum))
If i make a function call like below. It should incriment the number in the script and log that information and who called it. testscope.incTestNum('Gateway Timer Script')
I put this function call in a Timer gateway events scripts of that same project running every 10 seconds. It increments as it should. If i put it in a in a tag change gateway even scripts, it also works as expected. Both these seems like as explained, these are gateway side scripts and can update information that way.
However, if i put this in a value changed event on a memory tag. The count starts over. If i create multiple memory tags with the script they count like i would expect, however it is independant of the gateway time scripts.
Lifetime is also a bit confusing. When i save the project the values reset for the tag change scripts and i get the new module message. Make sense because the log shows a clearing resource script module message. The gateway events scripts whoever do not always. They seem to do so when i change and save the script code and then the gateway event scripts would reset its count but not necessarily if the file is not modified.
Every project has its own scripting environment, including the gateway scripting project that is used by non-project objects, like tags. What you are seeing is expected behavior.
If you need something that is true global and persists in spite of scripting restarts, you will need to place appropriate objects into the dictionary provided by system.util.getGlobals(). But there are hazards to deal with:
Multiple scripts could attempt to initialize simultaneously. Use python's .setdefault() dictionary method to perform the initial setup in a thread-safe manner.
Objects that aren't native to Ignition (that is, user-defined python classes and class instances) will not be replaced when the project that created them is restarted. Instead, they will continue with the old code, which will otherwise be unreachable, and will leak the memory of the entire interpreter that defined them. So native java and jython object types only.
Multiple scripts can access such objects simultaneously, too. Jython's lists and dictionaries are built with java thread-safe collections (from java.util.concurrent), so they won't be corrupted themselves, but your algorithms on top of them might need locking to be correct.
Thank you for that information. It did seem to be that way, its nice to understand that a bit better.
I understand the system.util.getGlobals() is a bit of an older methodoloyg. I do remember the old scripting and objects being used back in the day. Is that recommended for a new project? or mostly to keep legacy things going? It seems to imply that in the documenation.
I've read some documentation but have not played with the SDK much. Is it right to think this would be a better approach to make a gateway module that had that kind of functionality? I assume that it would be possible and maybe even preferred.
Sort of. It was originally created to expose the shared global namespace at the top level of old script event routines. May still do so in gateway scope (ugly).
But in modern Ignition, it is the one truly global entity in a process.
(For certainty to avoid clashes with legacy scope, consider using system.util.persist() from my life cycle module.)