Cyclic increment value in a script

I would like to increment a value every 1s.

Here's the script running on script console.

class tagType:
	#sets all initial values for the pid library
	def __init__(self):
		self.i = 0
from time import sleep
newTag = tagType
newTag.i = 10
while True:
	print(newTag.i)
	newTag.i = newTag.i + 1
	sleep(1)

I placed it in Gateway Events, as timer events & 1,000ms.
The above "print" script is replaced to write to a memory tag.

From the above script, the instance "newTag" is created every time the script is called, I thought when the above script is running in Gateway, the print value will remain at 10 because each time the script runs, the new instance will be created, but the value does increase every one second, "10,11,12,13,14.....".

I am a bit confused about how the gateway run the script. It seems that when the instance "newTag" is generated from the class, it retains the last scanned value when the new scan starts at 1second interval.

Then I tried to use the same script in a now(1000) tag, when the tag value changes every 1 second, It will execute the above script. Under this case, the value "newTag" remain at 10, as what I expected.

Now my question is: why the script result from the gateway 1 second timer event is different from the 1 second tag event?

Any comment is appreciated.

You're trying to do something every one second, but you're using two different tools to accomplish the repeating.

  1. A gateway timer event script at 1000ms will run whatever code exists in its script every second.
  2. The while loop in your script will run forever and execute its logic every 10 seconds.

In your gateway timer script, you've essentially established one infinite loop every second. I imagine this would have performance implications eventually. If each infinite loop is trying to write to a tag at roughly the same time, I would expect that to be racey and would consider the behavior to be undefined.

As general advice for Ignition projects, I recommend not:

  1. using time.sleep() in any gateway script (there may be limited exceptions in perspective)
  2. using unbreakable infinite loops

I would remove all of these from your gateway, restart the gateway (to clean up the threads that are probably still running these infinite loops), and write a script that doesn't use these patterns.

2 Likes

Never sleep or use long-running loops in tag event scripts. Event scripts must complete quickly or they block the thread pool for other tags.

{ I would say "never use sleep() in Ignition", but there are rare reasonable uses. This isn't one of them. }

3 Likes

Thank you all for the comments.

Basically I would like to use ignition to achieve some PLC functions in a cyclic manner.

For some of the functions, I used tag "now(xxxx)" to trigger the functions in the value change script, the functions are nice and short, which I hope it won't affect the overall gateway performance.

For the other functions, because it will be instances derived from a "Class", which I cannot use the cyclic now"xxxx" tag to trigger the script, otherwise, as mentioned before, the newly created instance will overwrite the existing outcome from the previous cycle. Like the example code below.

So I thought about using the gateway timer function, which I never use before.
It seems work as I want. But for sure if I have 10 cases for each project and with 10 projects running at the same time, the gateway performance will be down.

class tagType:
	#sets all initial values for the pid library
	def __init__(self):
		self.i = 0
from time import sleep
newTag = tagType
newTag.i = 10
while True:
	print(newTag.i)
	newTag.i = newTag.i + 1
	sleep(1)

What's the better way to achieve the cyclic running function?

  1. The script in "now(xxxx)" seems be working for short and nice calculation. I hasn't seen any gateway performance impact, for now.
  2. For the above mentioned instance from class script, the gateway timer script is the only one I found it able to achieve my requirements.

Please advise your comments.

PID control is one function I would like to achieve in Ignition.

"simple-pid" is a python script I used.

In ignition exchange, there's a project utilizing the "simple-pid" to achieve the pid function, but it's using SFC to cyclic run the script.

I never use ignition SFC before.
Is the SFC a better tool to manage the cyclic running script?

Or is ignition a platform to handle cyclic functions?
What's the limit of cyclic script it can handle?
How's the ignition gateway handling the timer script?

Ideally, there are maybe 200 instance of valves&pumps&instruments&switches in one project. I would like to cyclically run them in the script, same as the PLC FB. Then on top of it, there are timer&pid&totalizer blocks I would like to cyclically run in igniton as well, but it has more complicated logic involving instance from class and the values from the outcome of last cycle to be the input to the current cycle.

I would like to have a try, but maybe with all of the effort spent, at the end, ignition won't be able to handle it.

Any comment is appreciated.

Don't use .sleep() !!!
Don't use while True: !!!

Just set a gateway project timer event to run at your desired pace, as if it were a PLC task. Have that single script call all of your cyclic functions. Use a project script module for the functions. Make sure the entire event runs straight through--the gateway timer takes care of running it repeatedly.

3 Likes

wonderful.

Thank you for the advice.

I will have a try.

Still a bit confused about my experiment mentioned before:

From the gateway script,
the instance of the class is created every xx Second, but the outcome value is not overwritten every time the script is called.
But from the tag timer script, it works as I expected.

  1. Why the gateway timer script is different from the tag timer script?
  2. Where is the gateway tag instance stored? in the gateway memory cache?

Your script doesn't make instances of tagType as newTag, it simply makes it a synonym for the class.

  1. No clue. I haven't looked beyond the glaring problems.

  2. Gateway memory tags are held in the java heap under the tag provider's hierarchy, with value backups in the gateway's internal database. But these are mere details that you should not be concerned with.

1 Like

I kind of find the issue.

When running it from the gateway, it looks like the gateway run this script only once, even though it is a 1second timer script.
So the instance was created only once.

This is the gateway timer script, 1second.

class tagType:
	#sets all initial values for the pid library
	def __init__(self):
		self.i = 0
from simple_pid import PID
pid = PID(15, 1, 0, setpoint=1.5)
pv = 1
logger = system.util.getLogger("myLogger")
logger.info(str(pv))
from time import sleep
while True:
	control = pid(pv)
	sleep(1)
	system.tag.writeAsync("[default]ctrlPID/outCV", control)

I got the following log:


And I have the PID loop CV value updating every one second.

Looking through this post,

Actually my main problem is not able the cyclic loop, but more about the instance of the defined class.

In this PID controller issue, I want to create an instance of the PID class, then I want to execute its PID calculation every couple of seconds.

I don't see a way to create a tag as the instance of defined class. But this instance has to be stored in the running project/gateway in some certain form so I can retrieve the values everything I want to run its function.

Maybe my question should be:
How do I create an instance of a defined class and store the data somewhere in the project? So I can run its function whenever I want, either in a cyclic loop or by some event trigger.

Or is there any way I can store the object of the defined class as a ignition global variable, so it can be used at any time?

Remove. The. Loop.

What do you think is going to happen when the loop is reached ?
Here's a rundown:

  • second 0: Script starts. Loop is entered. sleep for 1 second, repeat forever.
  • second 1: New script starts. Loop is entered. sleep for 1 second, repeat forever.
  • second 2: New script starts. Loop is entered. sleep for 1 second, repeat forever.
  • second 3: New script starts. Loop is entered. sleep for 1 second, repeat forever.
  • second 4: New script starts. Loop is entered. sleep for 1 second, repeat forever.
  • second 5: New script starts. Loop is entered. sleep for 1 second, repeat forever.

6 seconds in, you already have 6 infinite loops running. And it's not going to stop.

Let's try and make it clear:
The gateway timer event IS the loop. If you set a timer event to run every 1 second, it WILL RUN every 1 second. You don't need to add your own loop inside.

That's a script that runs ONCE and loops FOREVER, calling a function every second:

while True:
    do_something()
    sleep(1)

Here's a TIMER script that will be run every x second (set up in the GUI) and call a function every time:

do_something()

That's it.
Do not use sleep. Do not use While True without a break condition.
And ESPECIALLY do not do these things in a timer script.

3 Likes

Thanks for the comments. You made the point.

The previously posted script was drafted in a script console to test the class object function and then pasted in the gateway timer script to try out the simple-pid library. It was poorly structured.

I thought the same when I started the gateway script, as per the code, there should be a new script starting every one second, because the previous script will never complete.

But when I checked the gateway "Current running script" status page, there's only one script running, I thought maybe the gateway list only one instance of the duplicated script. But because I write a flag log in the script, if there's duplicated scripts running, there should be one new log every one second. But from the gateway log, there's only one log only.

So I was kind of confused, maybe Ignition is doing something smart to run only one script to prevent the memory from overflowing?

logger = system.util.getLogger("myLogger")
logger.info(str(pv))

I used the gateway script only a handful of times over the years and I had poor experience when using it. For example, in the gateway script, I will always write log to keep track of the running script status. But for quite a few times I found the script was not listed in the current running script page, but the log keep on updating with new status log, which means there's a script running at the backend but not captured by the gateway, the only way to stop it is to restart the gateway.

Anyway, the gateway cyclic script is not the show stopper to my current case.
My real problem is how to create object from class, store the object in gateway or running project, then run the object function.
I have started a new post about it, but since this post attracted more attention, maybe we can continue the discussion in this post. :grinning:

1 Like

Yes. Contrary to @pascal.fragnoud's suggestion, an event that doesn't return will be prevented from running again. Event scripts are effectively "critical sections" that will not run in parallel with themselves. (They can run in parallel with other scripts and events.)

2 Likes

Eh, I didn't know that. Makes sense though.

Let's take a script on a 1 second timer:
Will the next execution run 1 second after the previous one completes ?
Imagine it takes 1.5 seconds to complete, will the next one be 1 seconds afterward, or 0.5 seconds ?

I guess I'm asking if it's a 'fixed' timer, checking every second if it should run, or if the end of an execution triggers a scheduling of the next.

From what I tested, the first script will continue, as I saw the PID CV kept on changing until it reaches 100% or 0%.

Just tested again a new gateway script, same issue happened again.
The timer script is running, because the log kept on updating, but there's no running script listed in the log page.




Yes, because it executes very fast. You'll get one script that executes almost instantly every second.
It's not just a script that keeps on running.

Why do you call that an issue ?

1 Like

That is a good thing. The point of the "running scripts" page is to find the scripts that are misbehaving by not finishing quickly.

1 Like