Python's Timer: from threading import Timer

Hi everyone,

I’ve been using python’s timer for a while now but for some reason it’s failing in a certain script.

The code is fairly simple:

def JustTesting():
	from threading import Timer
	msg = "Starting"
	system.perspective.print(message = msg, destination="client")
	
	def runThisLater(param):
		msg = "Inside Timer"
		system.perspective.print(message = msg, destination="client")
		system.tag.writeBlocking(['[default]Temporal/BIT 1'], [True])
	
	return Timer(5.0, runThisLater, ['Some param']).start()

That is a script is in the Gateway and its being called from a component on a Action Perform event.

I wonder if it will start failing in other places.

Edit # 1:
I would add that I don’t see any errors in the log.

Also, I would like to confirm that the function will be called in a new thread. I’m planning to call timers that will take hours to execute.

Hi

Not sure why that script is not working. There is a standard function for invoking functions on different threads. It might be worth to check that out.
https://docs.inductiveautomation.com/display/DOC81/system.util.invokeAsynchronous

You have this flagged as Perspective, so everything executes in a Gateway Scope, however, where exactly is this script "in the Gateway"? There are some weird legacy scoping issues that could possibly be getting in the way.

Why do it this way, when there are Gateway Timer or Scheduled Events that can already be set up to run on separate threads?

I think I would need to combine both: Python's timer and invokeAsync. Because invokeAsync doesn't have a fixed time as a parameter.

From this post: AttributeError: ‘com.inductiveautomation.ignition.common.script.Imm’ object has no attribute ‘invokeLater’

They both seem the same but probably some minor differences.

Edit #1:
I think it would be nice to call the timer through an invokeAsync to get a description of the thread in the gateway. Maybe I'll create two threads by doing so. Any thoughts on this?

It's in project library.

Are those legacy scoping issues related to Script Hint Scope? I'm not sure how that works yet so I set it to "All" for testing.

Time of execution is dynamic. Like 1 min to 8h (fired by a component in this case)

No. The Script Hint Scope just lets you chose what types of functions it will hint you for. For instance if you set it to Client, then you wont see functions that can only run in the gateway. It's just a "Hint" to what you can use. It doesn't play any role in what scope the script is actually executed in.

Are you testing this in the Designer or in a real Session?

system.util.invokeLater() has a delay parameter.

https://docs.inductiveautomation.com/display/DOC81/system.util.invokeLater

I think you're headed down a perilous road here. How do you plan to manage the life cycle of these threads? I see no way to halt or interrupt the timer. What happens if the gateway service needs to be rebooted? Or the project is saved restarting scripting?

See this for some more pertinent information

1 Like

I'm testing in the designer for now.

You are right but I avoided it when I read this post:

Answering these questions:

There is no way to control them according to my research. If the gateway is rebooted or the scripts are restarted I will lose that execution.

I could use a Gateway Timer to check every XX time or Life Cycle Module. A Timer wouldn't execute in the exact moment and pturmel's module is new to me.

Can I ask what exactly it is you are trying to accomplish? From a business logic perspective. Seems like someone is supposed to press something and that is supposed to write to a tag X minutes later, is that all? If so I don’t see why a message handler wouldn’t suffice.

What if the person presses the button and the client immediately exits, I don’t think that would cancel the execution of the script since it all runs on the gateway but it I can’t say that for sure.

Press the button, send a message to the gateway message handler and that will write to the tag after X minutes (which could be sent in the payload as well), and then this will run whether the client stays open or not. Though I suppose this isn’t so different from your methodology.

Or you could have a memory datetime tag that you write the desired execution time to, another that sees if the current moment now() is past that time, and then have a tag change event on that looks at the expression tag and if true then executes your tag write. You may need another tag to store what value you want to write and what tag you want to write to if its privy to change. If that’s the case I’d suggest the first method. If it’s always going to be writing to the same tag though this method I think would be fine. Then no thread management is really required here.

Edit: If the exact execution time being a couple seconds off is acceptable, different idea would be to use a memory dataset tag (or a database table) that stores 1) What tag to write to 2) When it should be written to and 3) what value should be written to it. Then you could have a gateway timer script read the dataset tag or table every X seconds, determine what tags need to be written to, and it could potentially do more than one tag per cycle. After writing the tag you would delete it from the dataset. You would also be able to view the queue of upcoming tag writes, and so you could display that to clients or use that knowledge to disable your button if you already know that someone else pressed it and there’s one queued up already for instance. I would most likely go this way tbch.

1 Like

Regardless of the client or session, if an action is performed on a component, it will execute a script XX time later.

It would be the same because a MessageHandler in the Gateway will suffer from the same conditions as any other script: Gateway restarts, script restarts, and any other condition that halts scripts.

That is like an expression tag that is always checking for the conditions to write a value. I think that could also do the trick. It wasn't my first approach.

It's something I'm looking at now. Maybe it's acceptable, I will discuss it with the team.

Meantime I will create all the necessary tags that I may use in any case. There is no harm in doing so. Then we can pick a solution.

Thanks @lrose and @bkarabinchak.psi

BTW, does the code from the main post work for you? My case is closed but maybe someone finds their timer is not working. Maybe those timers where just being broken after a save. Are there other conditions to lookout for?

2 Likes

I have not tried, however I will say while the Designer is a pretty good simulation, you should always test in a live client.

I’ve noticed property certain things like property change events/invokeLater don’t always work properly in designer.

Don’t take things not working in designer as things not working in the client. Always test in the client.

1 Like