Time.sleep function in Ignition

Hi All,

Anyone here have tried to to use time.sleep function of time module in Ignition?

I tried to use this function but the result is not what I wanted. Below is my sample script:

[code]import time

for i in range(1, 6):
print i
time.sleep(3)[/code]

In Ignition this will print: 1,2,3,4,5 at the same time. What I wanted is that there will be delay of 3 second for every print. This script works outside Ignition, I wonder why Ignition is not doing the same thing.

Anyone can help me with this?

It seems like the sleep is freezing the thread before it can print.

The Ignition Designer console is not the same thing as other Python consoles.

This code works:

import time def async(): for i in range(1, 6): def later(): print i system.util.invokeLater(later) time.sleep(3) system.util.invokeAsynchronous(async)“async” is executed in the background and “later” updates the GUI.

Best,

2 Likes

[quote=“dlaki”]Anyone here have tried to to use time.sleep function of time module in Ignition?[/quote]Nick hints at this, but let me be more explicit:
Never, ever, use time.sleep or any other delay function in Ignition unless it is on its own asynchronous thread. Delays on the event dispatch thread (darn near everything in a client) will freeze the user interface for the duration of the delay.
Just say no to any form of sleep. When you need delays, always use system.util.invokeLater() with its delay parameter. If you don’t like the built-in function, nor Nick’s module, consider the alternatives in later.py I posted in this topic.

2 Likes

FYI - dlaki’s code works if executed in a tag change script.

Don’t do it. You can freeze the whole tag change event subsystem.

2 Likes

Pardon my resurrecting an old post, but I’m needing to do something similar and I’ve run into some issues.

My case is that I post a record to a SQL table requesting some action from an OEM’s equipment.
That equipment then responds to the request in a different table using the Request ID as a foreign key in the Response table.
I need to monitor that Response table until I see the response to my request, or I hit a timeout threshold. My plan essentially is to enter a while loop conditioned on the elapsed time and the status of finding the response. If I execeed the timeout threshold I drop out of the loop. If I find the response I drop out of the loop. I then process the response data.

NOTE: This will not interact with the GUI.

It would look something like this:

import time
reqIdent = 0
foundResp = 0

def asyncResponseFind():
    global reqIdent, foundResp 
    foundResp = 0
    query = "SELECT IDENT_CURRENT('tblRequest ') AS reqIdent"
    pyResults = system.db.runQuery(query, 'MyDBConn')
    reqIdent = pyResults[0][0]
    def laterFindResp():
        global reqIdent, foundResp
        start = system.date.now()
        diffSec = system.date.secondsBetween(start, system.date.now())
        while diffSec <= 120 and foundResp == 0:
            time.sleep(5)
            query = 'SELECT [Status], [Message] FROM tblResponse WHERE [RequestID] = ' + str(reqIdent )
            pyResults = system.db.runQuery(query, 'MyDBConn')
            if pyResults.rowCount > 0:
                foundResp = 1
            diffSec = system.date.secondsBetween(start, system.date.now())
    system.util.invokeLater(laterFindResp)
    if foundResp ==1:
        'take found actions'
    else:
         'take timeout actions'
                
def sendRequest():
    query = "INSERT INTO tblRequest (Command, Data) VALUES('CommandedAction', 'CommandData')"
    result = system.db.runUpdateQuery(query, 'MyDBConn')
    system.util.invokeAsynchronous(asyncResponseFind)

I’m having trouble getting this to work and monitoring what’s going on inside the routines.
Using a logger (created with system.util.getLogger) doesn’t seem to put anything in the log.
Also, I know you can’t pass parameters to the Async routine, so I’m guessing using global variables may not work either.
I’ve tried using tags instead but it seems that the update of variables/tags using tag reads/writes is sometimes hit or miss.
Any ideas on doing this in a way that won’t block other threads?
BTW, this will be triggered with a Gateway Tag Change Event script.
Ignition version is 7.9.6.
Please ignore any typos or obvious syntactical errors in my hand typed example above. :slight_smile:

Thanks,
Mike

Run your table polling in a gateway timer event script. Use a memory tag as a state variable, possibly with the ID you are looking for. When zero, don’t execute. Set to zero when the response is found. Have the tag change event script set up the state tag.

Just say no to any form of sleep.

4 Likes

When you talk about using a “state variable” to control execution using a gateway timer event script, are you meaning to do a check on this variable at the outset of the script run by the timer event, and if it is 0 terminate, non-zero execute the table poll?

I’ve not used timer events before but looking at the configuration it is fairly straight forward. But it does not have a way to configure the state tag in the settings. Hence I’m assuming your reference is as stated above.

Would this timer event be the “best practice” way to accomplish a task like this?

Also, each execution of the timed event script would loose all the local variable data being used. I would then have to create tags to house all that information between executions, correct? Things like start time of the first iteration for a new search ID, as I want to search for a maximum period and then timeout with error messages. So say I set my timeout to 120 sec. The start time of the initial execution for Request ID 10 would have to be stored in a tag, yes? I’d prefer not to have to create a bunch of tags that would only be used to house the various statuses I need to check for between “loops” of this test. Are their other means to do this?

Another question. If I were to do this like I described in my first post here, what would the sleep commands affect? What processes would be blocked? Etc.?

The intent is to run the aforementioned script off of a Gateway Tag Change Event. That event would call the above script which would be a shared (Gateway) level script.

Thanks.

Most execution of code in the gateway is handled by Java Executor implementations. These use thread pools to work through a queue of tasks to perform. Any sleeping function, whether java or python, will block the pool thread it is running in, reducing the parallelism of the gateway. You don’t know how many threads are available to a pool (though the gateway web interface allows you to look around). A single sleep operation in one script in your gateway won’t be noticed. But once you have one, and it appears to work, there’s a temptation to put them in other event scripts. Multiple sleeps running concurrently can hurt unrelated operations.

Don’t do it.

Can you give some recommendations to my other questions? Passing status and other data into the timer event, would that REQUIRE tags? Best Practice? Etc.?

If I do the “looping” in the script contained in the timer event, I could get away with fewer tags for status and control data, but then I’d have to pause the loop as I wouldn’t want to have SQL queries triggering as fast as the loop can execute. There really needs to be a cleaner way to do something so apparently simple.

As I understand Phil's suggestion:

1 - Create a state tag, possibly with ID you are looking for (could then use this to compare in your function)
2 - Create gateway timer event script like:

if system.tag.read(state).value > 0:
   #do your checks here, set response = True when response found
   if response:
      system.tag.write("state", 0)

3 - Your tag change event script sets the state variable to non-zero to enable checks for response via gateway timer event script

Yes, you'd need to add something more to this to track timeout.

You may find this helpful for other options:
https://docs.inductiveautomation.com/display/DOC79/Adding+a+Delay+to+a+Script
Excerpt:

If a sleep() or While Loop function must be used, then they should be called from system.util.invokeAsynchronous to prevent blocking any other scripts.

Yep, that is what I said in my assumption statement to his post.
Of course it really isn’t that simple in the real world!

Yes, it’s always simple until implementation :slight_smile: I’m not an expert on this end of things, but by my reading I think system.util.invokeAsynchronous may be the solution you’re looking for.

Either a memory tag or a value stored in a script global scope dictionary.

No loop. You have a timer event that is going to "loop" for you. That event examines the current state and either does nothing, or it does one query. Then updates the state based on the results from the query.

No pauses in the code. No unbounded loops in the code. Treat your code like a task in a PLC -- do everything necessary at each "scan". Anything not ready is re-examined on the next "scan". The "scan" provides the looping.

Who's Pat? I haven't seen a comment by anyone named Pat.

Good question... me neither. I'll just quietly go back and edit that post now. Sorry Phil! I'm feeling a little sheepish having done this twice now :neutral_face: Seems I should stick with programming and leave the commenting to others :grinning:

1 Like

Thanks for the comments and ideas. I’ve got some test code working. Now to put meat on the bones!