Python execution timeout issue

Posted on
Mon Nov 18, 2019 4:59 am
Mark offline
User avatar
Posts: 262
Joined: Apr 21, 2005
Location: California

Python execution timeout issue

What am I missing?

Because I was getting an execution timeout error from one of my python scripts, I moved it to an external py file. Which all works fine. But I still get the same error:

Code: Select all
   Action Group                    Cams All Preset 2
   Error                           because embedded scripts execute sequentially they must complete their execution within 10 seconds.
   Error                           modify the script to exit quickly or convert it to an external file (not embedded).
   Stopping embedded script executor host (pid 18026)
   Module Execution                indigo_cams.py executed: all_preset, 2
   Stopped "embedded script executor host"


Here's how I'm calling the external py script, from within an Action Group's Execute Script Type:
Code: Select all
import indigo_cams
indigo_cams.sendCommandToCams("all_preset","2")


Is Indigo still waiting for the external py file to complete, which is throwing the same error? Is there a way for an embedded py script to call an external py script, without waiting for it to complete?

Posted on
Mon Nov 18, 2019 6:40 am
DaveL17 offline
User avatar
Posts: 6753
Joined: Aug 20, 2013
Location: Chicago, IL, USA

Re: Python execution timeout issue

Because they run one at a time, as I understand it, Indigo will always put the time limit on embedded scripts regardless of what they do.

Why not just have the action call the external script directly?

I came here to drink milk and kick ass....and I've just finished my milk.

[My Plugins] - [My Forums]

Posted on
Mon Nov 18, 2019 10:16 am
jay (support) offline
Site Admin
User avatar
Posts: 18220
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Python execution timeout issue

Dave's explanation is correct: your embedded script is just loading the module and running the method, still within the context of the embedded script (that's how Python modules are loaded, into their existing context). Running it as an external script (in it's own process with no time restrictions) means putting it in a separate file and selecting file as the script execution type.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Mon Nov 18, 2019 12:01 pm
Mark offline
User avatar
Posts: 262
Joined: Apr 21, 2005
Location: California

Re: Python execution timeout issue

OK, I get it (regarding why the error is still happening). I guess I don't know how to run an external py script. I did it all the time with AppleScript. Is there an example somewhere I can look at? I was using the file that includes the note:
Code: Select all
In this file you can insert any methods and classes that you define.
They will be shared by all Python scripts - you can even import the
IOM (as shown below) but if you do then you'll only be able to import
this script in Python processes started by Indigo. If you don't need
the IOM then skip the import and it'll work in any Python script
no matter where it's run from.


And I had defined "sendCommandToCams" there (is that a method or a class, still a little foggy on that)?

sendCommandToCams is basically just using Python "requests" to send URLs to IP cams (their built-in webservers). But the script also "talks" to Indigo to write to the Indigo log. I don't have to have the logging, but it's nice to see the action in the log, as well as for troubleshooting. Can that be done from an external PY script?

So do I use the "Open File" type in the Action Group, instead of the "Execute Script" type? If so, I just need an example of how to format a py script that will run when opened...

Posted on
Mon Nov 18, 2019 12:11 pm
Mark offline
User avatar
Posts: 262
Joined: Apr 21, 2005
Location: California

Re: Python execution timeout issue

Also, I was passing parameters to my script. How would I do that using a py external script? :(

Alternately, would anyone happen to know how to send a Python request to a web server without waiting for the reply? Maybe that's what taking so long. Then I could keep everything as it is, and just fire off all the URLs. Without waiting for the web server, maybe that would take less than 10 seconds...

Posted on
Mon Nov 18, 2019 1:47 pm
jay (support) offline
Site Admin
User avatar
Posts: 18220
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Python execution timeout issue

Why don't you just post the actual script - then we'll have a more complete understanding of what you're trying to do.

In terms of how to do an external script, it's the same way you do an external AppleScript. You write your Python script then you point to that script file in the Execute Script action.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Mon Nov 18, 2019 1:53 pm
jay (support) offline
Site Admin
User avatar
Posts: 18220
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Python execution timeout issue

Mark wrote:
Also, I was passing parameters to my script. How would I do that using a py external script? :(


I think this is where we need to see the full script and how you were calling it.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Mon Nov 18, 2019 2:33 pm
Mark offline
User avatar
Posts: 262
Joined: Apr 21, 2005
Location: California

Re: Python execution timeout issue

I have a bunch of cams. I'm using Python to send the same command, via URL, to all the cams at the same time: turn on all IR lights, turn 'em all off, move all cams to preset position 1, preset 2, reboot all cams, etc. Which is why I am using parameters, to tell the script which commands to send out. And then I log what happened, including errors. I built a list of IP addresses (the cams' web server), and loop through the list, sending a URL to each cam. I don't process the result of the URL, so I don't need the response from the cam's server, so I don't need to wait for it. Is there a parameter I can add to the "requests.get" line to avoid the wait for the response ?

Is the script taking x amount of time to connect, and/or x amount of time waiting for a reply, costing me more than 10 seconds? There are eight or nine cams in play, so that many URL requests, depending on the command. Or is the logging taking too much time? I'm not logging each loop, just once after the loop is done, or once if there was an error.

I could build a standalone external script for each command, to eliminate the need for the parameters, but this is so clean using one script like this. I'd rather figure out how to send parameters to the py file, or just speed up what I've got.

Worst case, the script is working, the URLs find the cams and execute. The only real issue is the errors in the Indigo log. I no likely the red text!!! :D

Here's the py script in an attachment file::
Code: Select all
try:
    import indigo
except ImportError:
    print "Attachments can only be used from within Indigo"
    raise ImportError

import requests
from requests.auth import HTTPDigestAuth
   
def sendCommandToCams(mCommand,mOption):
   if mCommand == "all_ir":
      ipAddresses = ['xxx.xxx.xxx.xx1','xxx.xxx.xxx.xx2','xxx.xxx.xxx.xx3','xxx.xxx.xxx.xx4','xxx.xxx.xxx.xx5','xxx.xxx.xxx.xx6','xxx.xxx.xxx.xx7','xxx.xxx.xxx.xx8','xxx.xxx.xxx.xx9']
      cgi = 'configManager.cgi?action=setConfig&Lighting[0][0].Mode='+mOption
   elif mCommand == "all_preset":
      ipAddresses = ['xxx.xxx.xxx.xx1','xxx.xxx.xxx.xx2','xxx.xxx.xxx.xx3','xxx.xxx.xxx.xx4','xxx.xxx.xxx.xx5','xxx.xxx.xxx.xx6','xxx.xxx.xxx.xx7','xxx.xxx.xxx.xx8']
      cgi = 'ptz.cgi?action=start&channel=0&code=GotoPreset&arg1=0&arg2='+mOption+'&arg3=0'
   elif mCommand == "all_reboot":
      ipAddresses = ['xxx.xxx.xxx.xx1','xxx.xxx.xxx.xx2','xxx.xxx.xxx.xx3','xxx.xxx.xxx.xx4','xxx.xxx.xxx.xx5','xxx.xxx.xxx.xx6','xxx.xxx.xxx.xx7','xxx.xxx.xxx.xx8','xxx.xxx.xxx.xx9']
      cgi = 'magicBox.cgi?action=reboot'
   else:
      indigo.server.log("indigo_cams error: sendCommandToCams method", type="Module", isError=True)
      indigo.server.log("mCommand parameter not defined", type="Module", isError=True)
   try:
      for ipAddress in ipAddresses:
         tURL = 'http://'+ipAddress+'/cgi-bin/'+cgi
         requests.get(tURL, auth=HTTPDigestAuth('username', 'super_dooper_strong_password'))
      indigo.server.log("indigo_cams.py executed: "+mCommand+", "+mOption, type="Module Execution")
   except:
      indigo.server.log("indigo_cams error: sendCommandToCams method", type="Module", isError=True)
      indigo.server.log("something went wrong with the http request", type="Module", isError=True)

And within an Action Group I use:
Code: Select all
import indigo_cams
indigo_cams.sendCommandToCams("all_ir","Off")


Side note: my error detection isn't very robust. If the script reaches that "else" statement, where I log that the parameter was not defined, I should abort the script at that point, before the loop starts,, but I don't know how to do that. Or I could eliminate all the logging and error detection altogether, it's not necessary, I just like having it there.

Posted on
Mon Nov 18, 2019 8:46 pm
Mark offline
User avatar
Posts: 262
Joined: Apr 21, 2005
Location: California

Re: Python execution timeout issue

Well, I made some progress, but I'm not sure how elegant the solution is. I wanted to keep as much of the code in one place as possible, and not have a bunch of near-duplicate code in a bunch of py files, but that meant passing parameters. I also wanted to continue to log to Indigo. So this is what I tried, though I'm not sure why it worked.

I left the indigo_cams.py file in place (in Indigo's attachments folder). Remember, that file has the loop and all the lists of the IP addresses. I created five new .py files (in Indigo's attachments folder), and moved the original Python embedded code from each of the five Action Groups I was using into each of those five new py files. That code calls a method in the indigo_cams code, and passes parameters. Then I altered each of the Action Groups to just open its respective new .py file, instead of running any embedded code.

The new .py files then execute, and they each import the original indigo_cams.py code, and pass to it the parameters. The indigo_cams.py code then executes and even writes to Indigo's log. So while I have to have a .py file for each Action Group, the code in those is very short and simple. All the work, and the bulk of the code, is still in the original indigo_cams.py file. So when I need to alter the Python, or add new methods, I'll do so in the indigo_cams.py file. Sweet. (By the way, I'm sure I'm using some of these terms incorrectly. I have more studying to do, for sure.)

And even though I can watch the time it all takes, by observing the log, and some tasks are taking longer than 10 seconds, there are no longer any timeout errors in the log. Yay.

So correct me if I'm wrong, but I think I got the best of everything. Is this what's going on? The Action Group launches the first .py file, and then returns control to the Indigo Server right away, So that's especially good. The .py file executes and imports the indigo_cams.py code, passes it parameters and then the indigo_cams.py code executes. Takes as long as necessary to send URLs to the nine cams, including waiting for responses, and when that loop is done, it writes to Indigo's logs. So in terms of the hit on the Indigo Server, it only has to execute the Action Group, and now doesn't have to wait for any Python to finish. But when the external Python does finally finish, and writes to the Indigo log, the Indigo Server is only "bothered" by that small logging task, instead of waiting so long for all the cams to do their thing.

I think I get why the .py scripts are now executing on their own, in their own thread (though I'm not sure what Python interpreter is being used, Indigo's or the OS's). But I'm not clear on why/how those Python scripts in Indigo's attachment are finding each other, and then able to write back to Indigo.

But it's working, so that's cool.

A few extra tidbits: I believe the timeouts were happening because the requests.get response not only had to wait to see if the cam server got the message, I think the cam only sends the response after it actually executes the command. When a cam was asked to turn off its IR lights, when the lights were already off, that happened pretty quick. But to turn them off when they were on took longer. And when the cam was asked to move to a preset position, I believe the cam sends the response back to the script only after it finishes moving. That can take a while, times nine cams! No wonder it was timing out.

I found and tried a different kludge, which was to add a timeout parameter to requests.get, but that didn't work every time. That's how I noticed that certain commands (times 9 cams) executed quickly enough, while others timed out, which led me to believe the cam server doesn't respond after it receives the command, but only responds after it executes the command. Something like that.

If there's a better way to do this, I'm all ears...

Posted on
Mon Nov 18, 2019 9:04 pm
Mark offline
User avatar
Posts: 262
Joined: Apr 21, 2005
Location: California

Re: Python execution timeout issue

Don't mind me, I'm talking to myself, mostly. I just did some interesting tests. I fired off the Action Group that moves all nine cams to preset positions. Then I quickly turned on an Indigo device light in one of the rooms. The light came on before the cams finished moving! So that tells me the Python scripts are executing "on their own."

Then I initiated two Action Groups back to back, one that moves all nine cams, and another that turns on the IR lights of all nine cams. Viola! The cams took several seconds to move. One moves, then the next after the first is done, the the third after the seconds is done, etc. So that tells me the requests.get call doesn't finish until the cam finishes moving. But the IR lights were coming on before all the cams finished moving! I think that means that the two external scripts were both running at the same time, both using their own instance of the master .py script, and the cams were responding to both sets of commands seemingly asynchronously (is that the right term?).

And all of this was going on without the Indigo Server bogging down.

While it is a bit of a kludge, it's definitely working better than the way I had it before...

Posted on
Tue Nov 19, 2019 9:01 am
jay (support) offline
Site Admin
User avatar
Posts: 18220
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Python execution timeout issue

Yep, sure sounds like the camera is waiting. They really should have an option to return automatically IMO.

One way you could probably speed it up is to use Python threads - create a thread for each request.get call which should essentially have each one of those camera moves happen in parallel.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Tue Nov 19, 2019 9:08 am
jay (support) offline
Site Admin
User avatar
Posts: 18220
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Python execution timeout issue

Mark wrote:
While it is a bit of a kludge, it's definitely working better than the way I had it before...


And, that's not a kludge - it's more of a workaround for odd camera behavior.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Tue Nov 19, 2019 9:32 am
matt (support) offline
Site Admin
User avatar
Posts: 21417
Joined: Jan 27, 2003
Location: Texas

Re: Python execution timeout issue

It sounds like you've already discovered this, but FYI external python scripts run in their own processes (so in parallel) and embedded python scripts run in their own shared process (so parallel to other Indigo Server processing, but each embedded python script runs serially/asynchronously relative to other embedded python scripts). At the Indigo Server level for other actions it gets complicated as there are multiple threads and some actions are sent out to plugins to be executed (but those plugins can then block or have to wait on the Indigo Server depending on what they are doing).

Image

Posted on
Tue Nov 19, 2019 10:42 am
Mark offline
User avatar
Posts: 262
Joined: Apr 21, 2005
Location: California

Re: Python execution timeout issue

Hmm, run each cam command in its own thread. I'll have to look into that. I took a peek at your link for that, Jay, a bit beyond my Python skills at this point, but I'll study some more. I think I ran into that, or something similar, when I was searching for the timeout solution, as others have had similar issues trying to speed up Python requests.get calls. I read things like "multiprocessing.dummy.Pool" and "pool.apply_async(requests.get, [params])" but stumbled on exactly how to implement those in my case (if those even apply to my case!).

Matt, you mentioned that Python scripts run parallel to other Indigo Server processing. Does that mean I could turn on a light even while my long-winded embedded python script was running? If so, then why the 10 second limit (and error) for python script execution?

Anyway, it's working well enough for now. So I'll revisit this later.

By the way, it was a pleasant surprise that my .py files find each other (for importing) without any special installation, as long as they're in the same directory. And that they can find Indigo (to be able to import "indigo"), too (for logging). Is that because they're in the attachments folder? Or would a py script be able execute, and be able to import "indigo", no matter where it was located?

So many questions... when I modify a py script, what is the proper way to reintroduce it to Indigo? I can see that something compiles my script (creates a .pyc version). after it first executes. Is Indigo doing that, or the OS's python interpreter? Also, I was using the "Reload Libraries and Attachments" menu command after each edit, but when looking at the log, I'm not convinced that is doing anything for python attachments. So how best to "tell" Indigo I've modified a py attachment?

And thanks for all your support and walking me through this obscure issue...

Posted on
Tue Nov 19, 2019 11:04 am
Mark offline
User avatar
Posts: 262
Joined: Apr 21, 2005
Location: California

Re: Python execution timeout issue

Sorry, one more question. When I upgrade Indigo (like from 7.3 to 7.4), will the installer copy over these py files in the attachment folder for me? And will the 7.4 Action Groups find the copied external files, in the 7.4 folder, or will the Action Groups still be looking for the linked files in the 7.3 folder path? In other words, when upgrading, do I have to mind attachments, or does Indigo figure all that out? So if I modify a 7.4 attachment in the future, 7.4 will use that version, and not the 7.3 version that might still exist in the /Library/Application Support/Perceptive Automation/ directory? (I tend to leave previous installations in place until I know the new version is doing everything correctly and finding everything I modified in the previous Perceptive Automation folder.)

Who is online

Users browsing this forum: No registered users and 17 guests

cron