A friend of mine asked me to wrote a simple code to play a video each time a motion is detected. The hardwares used are a Raspberry Pi B+ and a Logitech C170 webcam.
This is full code: http://pastebin.com/Z6nS9MXf. Overall, it works almost as expected.
So the issue is the timeout_command part:
def timeout_command(command, timeout):
cmd = command.split(" ")
start = datetime.datetime.now()
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
while process.poll() is None:
time.sleep(0.1)
now = datetime.datetime.now()
if (now - start).seconds > timeout:
os.kill(process.pid, signal.SIGKILL)
os.waitpid(-1, os.WNOHANG)
return None
return process.stdout.read()
It's supossed to do something within a timeout period, and after the period is reached, then the process is stopped.
I wrote this in the code:
timeout_command("omxplayer /home/pi/video/trololo.mp4", 10)
Which is supposed to make omxplayer play the video for only 10 seconds, then close after that. Yet omxplayer plays the video till the end.
How to fix this?
Related
I am trying to record screen events and execute them after for replay.
I wrote a small python script which listens for events,converts them from hexadecimal to decimal,waits for 5 seconds and executes recorded events with adb sendevent.
But for some reason sendevent never executes correctly, sometimes it touches wrong coordinates, sometimes touches for too long also there are problems with delays between touches.
I couldnt understand why this is happening ? What i expect is it should just replay since getevent captured all necessary data needed(?)
import subprocess
import threading
import os
from time import sleep
eventsToSend = []
def eventSender():
while(True):
if(len(eventsToSend) > 200):
print("starting to execute in 5 seconds...")
sleep(5)
for command in eventsToSend:
#with open('output.txt', 'a') as f1:
#f1.write(command+os.linesep)
subprocess.Popen(command.split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
print("done")
break
else:
None
eventSenderStarter = threading.Thread(target = eventSender)
eventSenderStarter.start()
def runProcess(exe):
p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while(True):
# returns None while subprocess is running
retcode = p.poll()
line = p.stdout.readline()
yield line
if retcode is not None or len(eventsToSend)>200:
print("Executing events...")
break
print("Listening for events...")
for line in runProcess('adb shell -- getevent /dev/input/event1'.split()):
myLine = line.decode().strip()
splittedLine = myLine.split(" ")
decimalString = ""
for index,hexadecimal in enumerate(splittedLine):
decimal = int(hexadecimal, 16)
if(index==0):
decimalString = decimalString+str(decimal)
if(index>0):
decimalString = decimalString+" "+str(decimal)
eventsToSend.append("adb shell sendevent /dev/input/event1 "+decimalString)
Just connect your phone to pc then run this script play with your screen after 200 events it will start replay(be careful because it might press wrong coordinates :P ) .In my case it was
/dev/input/event1
so you might need to edit event1 for testing.
Consider adding some small delay between the events you send - time.sleep(0.5). You may have to change the value of 0.5 - try some values until it will work.
I am trying to write a python script which displays a macOS alert and starts an alarm at the same time.
The alarm sound should be stopped after the alert is closed, however it isn't.
def show_alert(message="Flashlight alarm"):
"""Display a macOS dialog."""
message = json.dumps(str(message))
exit_status = os.system("osascript dialog.scpt {0}".format(message))
return exist_status
def play_alarm(file_name = "beep.wav", repeat=3):
"""Repeat the sound specified to mimic an alarm."""
process = subprocess.Popen(['sh', '-c', 'while :; do afplay "$1"; done', '_', file_name], shell=False)
return process
def alert_after_timeout(timeout, message, sound = True):
"""After timeout seconds, show an alert and play the alarm sound."""
time.sleep(timeout)
process = None
if sound:
process = play_alarm()
exit_status = show_alert(message)
if process is not None:
os.killpg(os.getpgid(process.pid), signal.SIGINT)
process.kill()
# also, this below line doesn't seem to open an alert.
show_alert(exit_status)
alert_after_timeout(1, "1s alarm")
The above code should display a macOS alert after starting to loop an alarm sound (in the file beep.wav). When the alert is closed, the alarm sound should instantly stop.
The AppleScript file dialog.scpt triggers the alert, it is only a few lines long:
on run argv
tell application "System Events" to display dialog (item 1 of argv) with icon file (path of container of (path to me) & "Icon.png")
end run
I admit I don't know why you cannot kill your process running in a shell, using subprocess to mimic running as background..., and the fact that no other command runs after that means that there's probably a deadlock somewhere. So let's drop that solution.
Let me propose a more pythonic solution. The audio play part was adapted from how to play wav file in python? but now plays in a loop and works with python 3 as well.
The idea is to start a thread that plays a sound in a loop using only python modules. The thread is aware of a global variable. If the stop_audio variable is set, then the thread knows it has to quit the infinite loop and stop playing.
you control the flag from the other procedure. Once the message has been clicked on, set the flag, audio stops playing immediately.
import pyaudio
import wave
import threading
# global variable used to gently tell the thread to stop playing
stop_audio = False
def show_alert(message="Flashlight alarm"):
"""Display a macOS dialog."""
message = json.dumps(str(message))
exit_status = os.system("osascript dialog.scpt {0}".format(message))
return exit_status
# initialize audio
def play_alarm(file_name = "beep.wav"):
#define stream chunk
chunk = 1024
#open a wav format music
f = wave.open(file_name,"rb")
#instantiate PyAudio
p = pyaudio.PyAudio()
#open stream
stream = p.open(format = p.get_format_from_width(f.getsampwidth()),
channels = f.getnchannels(),
rate = f.getframerate(),
output = True)
while not stop_audio:
f.rewind()
#read data
data = f.readframes(chunk)
#play stream
while data and not stop_audio:
stream.write(data)
data = f.readframes(chunk)
#stop stream
stream.stop_stream()
stream.close()
#close PyAudio
p.terminate()
def alert_after_timeout(timeout, message, sound = True):
"""After timeout seconds, show an alert and play the alarm sound."""
time.sleep(timeout)
process = None
if sound:
t = threading.Thread(target=play_alarm,args=("beep.wav",))
t.start()
exit_status = show_alert(message)
global stop_sound
if sound:
stop_sound = True # tell the thread to exit
t.join()
show_alert(exit_status)
alert_after_timeout(1, "1s alarm")
Note that I've dropped the repeat=3 parameter as it wasn't used and I made no sense of it.
An alternative without using pyaudio would be to call the external player in a loop, replace play_alarm above by this:
def play_alarm(file_name = "beep.wav"):
global stop_sound
while not stop_sound:
subprocess.call(["afplay",file_name])
when stop_sound is True, the sound keeps on playing till the end, but doesn't resume. So the effect is not instantaneous, but it's simple.
And another alternative to cut the sound in a more reactive way:
def play_alarm(file_name = "beep.wav"):
global stop_sound
while not stop_sound:
process = subprocess.Popen(["afplay",file_name])
while not stop_sound:
if process.poll() is not None:
break # process has ended
time.sleep(0.1) # wait 0.1s before testing process & stop_sound flag
if stop_sound:
process.kill() # kill if exit by stop_sound
I build a keypad activated sensor with a Raspberry Pi and Python. Everything seems to go well but after a few minutes and several keypad entries, the Pi has a total crash and switches off immediately - no error messages pop up.
The script will continously wait for a keypad entry and if the code is correct, switch on a sensor, if it's not you have to try again. If the sensor is activated you have to put in the correct numbers to avoid an alarm being triggered after 30s)
Could anyone point me in the direction of what might be the problem? Here are the things I've tried so far without success
1) Exchange Pi to new Pi 2
2) Different OS, both NOOBS and Raspbian Wheezy
3) Different sensor (accelerometer vs IR sensor)
4) Disconnect monitor, keyboard and use SSH connection via Cygwin
5) Get log file until crash - log file was empty after reboot
python bad_script &> full_log.txt
6) Different log file command: causes instant crash and is also empty after reboot:
python script.py >> /logdir/script.py.log 2>&1
The question is: how am I able to crash Linux? If it's a memory problem, isn't there a prevention in linux to stop processed before they appear?
Here is the full script I am running:
import sys
from time import sleep
import threading
import signal
from matrix_keypad import RPi_GPIO1
import RPi.GPIO as GPIO
import smbus
import time
passcode = [1,2,3,4] # this is the correct code you have to enter
kp = RPi_GPIO1.keypad(columnCount = 3)
alarm_active = threading.Event() # shared variable used to control the sensor monitoring thread
alarm_active.clear() # Clear the alarm initially
monitor_thread = None # Global variable used to store the monitoring thread
#Set up all the pins correctio
GPIO.setmode(GPIO.BCM)
PINIR=7
GPIO.setup(7, GPIO.IN) # infrad-sensor
#Now activate the kaypad and listen for keypad inputs
def digit():
r = None
while r == None:
r = kp.getKey()
return r
def get_keycode():
# Function to loop around reading 4 keypresses in a row
# Compare against chosen code
# If match, switch the alarm state
entered = []
while len(entered) < 4:
key = digit()
sleep(0.5)
print key
entered.append( key )
if entered == passcode:
entered = []
print "Code correct"
switch_alarm_state()
else:
# Just clear the keypad buffer if the wrong code went in
# Could say "BAD CODE!" here, or even force immediate alarm perhaps
print "Wrong Code - Try again"
GPIO.output(27, True) # let red LED blink as indicator that code was wrong
time.sleep(1)
GPIO.output(27, False)
entered = []
def switch_alarm_state():
# Function to control the state of the alarm
# If the alarm should be on, run a thread monitoring the sensor
# If the alarm should be off, make sure the thread is stopped
global monitor_thread
if alarm_active.is_set():
# If the alarm is currently set, stop it
print "Alarm was abolished"
GPIO.output(17, False) #switch green LED off
alarm_active.clear() # Signals to the monitor thread that we wish to shut it down
monitor_thread.join() # Wait until the monitor thread has stopped
else:
# Alarm is not currently set, so start the sensor monitor thread going
print "Alarm was activated"
GPIO.output(17, True)
monitor_thread = threading.Thread( target=sensor_monitor )
alarm_active.set()
monitor_thread.start()
def sensor_monitor():
# Function to be run in a separate thread, monitoring the sensor
alarm_timer = None # Variable to hold a timer object that will fire the alarm
while alarm_active.is_set():
#print xrota
if GPIO.input(PINIR):
print "Alarm has been triggered"
if alarm_timer is None:
alarm_timer = threading.Timer( 30.0, fire_alarm )
alarm_timer.start()
sleep(0.5)
# The alarm must have been deactivated to get here
# Stop any outstanding alarms and shutdown
if alarm_timer is not None:
alarm_timer.cancel()
return
def fire_alarm():
# Here you implement the actions to be carried out by an alarm
print "Alarm is send to server"
msg = "Alarm"
publish.single("alarm/demo",msg, hostname="52.17.194.125")
def shutdown_handler( signum, frame ):
# Shut down the child thread nicely
alarm_active.clear()
monitor_thread.join()
if __name__ == '__main__': # This is the Python way to check you are being directly run, and not just imported by another script
signal.signal( signal.SIGINT, shutdown_handler ) # If you stop the script with ctrl+C, make sure it shuts down properly
signal.signal( signal.SIGTERM, shutdown_handler )
try:
while True:
get_keycode()
except KeyboardInterrupt:
GPIO.cleanup()
print "Quit program"
I have a subprocess that encodes a video , and what I would love to do is update at database record with the time it is taking to encode the video (so I can print it out in ajax on a web page)
I am very close - this code I have so far updates the database and encodes the video - but the process/loop gets stuck on the final db.commit() and never exits the while True: loop. Is there a better way to do this? Here is the code I am tinkering with:
time_start = time.time()
try:
p = subprocess.Popen(["avconv" , "-y" , "-t" , "-i" , images , "-i" , music_file , video_filename], universal_newlines=True, stdout=subprocess.PIPE)
while True:
time_now = time.time()
elapsed_time = time_now - time_start
progress_time = "ENCODING TIME" + str(int(elapsed_time)) + " Seconds "
cursor.execute("UPDATE video SET status = %s WHERE id = %s" ,[progress_time , video_id] )
db.commit()
out, err = p.communicate()
retcode = p.wait()
except IOError:
pass
else:
print "] ENCODING OF VIDEO FINISHED:" + str(retcode)
You're right, because you have no way of exiting your infinite loop, it will just spin forever. What you need to do it call check p.poll() to see if the process has exited (it will return none if it hasn't). So, something like:
while True:
if p.poll():
break
... other stuff...
or better yet:
while p.poll() == None:
.... other stuff....
will cause your loop to terminate when the subprocess is complete. then you can call p.communicate() to get the output.
I would also suggest using a sleep or delay in there so that your loop doesn't spin using 100% of your CPU. Only check and update your database every second, not continuously. So:
while p.poll() == None:
time.sleep(1)
...other stuff...
In addition to the infinite loop issue pointed out by #clemej, there is also a possibility of a deadlock because you don't read from p.stdout pipe in the loop despite stdout=subprocess.PIPE i.e., while p.poll() is None: will also loop forever if avconv generates enough output to fill its stdout OS pipe buffer.
Also, I don't see the point to update the progress time in the database while the process is still running. All you need is two records:
video_id, start_time # running jobs
video_id, end_time # finished jobs
If the job is not finished then the progress time is current_server_time - start_time.
If you don't need the output then you could redirect it to devnull:
import os
from subprocess import call
try:
from subprocess import DEVNULL # Python 3
except ImportError:
DEVNULL = open(os.devnull, 'r+b', 0)
start_time = utcnow()
try:
returncode = call(["avconv", "-y", "-t", "-i", images,
"-i", music_file, video_filename],
stdin=DEVNULL, stdout=DEVNULL, stderr=DEVNULL)
finally:
end_time = utcnow()
I run a subprocess using:
p = subprocess.Popen("subprocess",
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE)
This subprocess could either exit immediately with an error on stderr, or keep running. I want to detect either of these conditions - the latter by waiting for several seconds.
I tried this:
SECONDS_TO_WAIT = 10
select.select([],
[p.stdout, p.stderr],
[p.stdout, p.stderr],
SECONDS_TO_WAIT)
but it just returns:
([],[],[])
on either condition. What can I do?
Have you tried using the Popen.Poll() method. You could just do this:
p = subprocess.Popen("subprocess",
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE)
time.sleep(SECONDS_TO_WAIT)
retcode = p.poll()
if retcode is not None:
# process has terminated
This will cause you to always wait 10 seconds, but if the failure case is rare this would be amortized over all the success cases.
Edit:
How about:
t_nought = time.time()
seconds_passed = 0
while(p.poll() is not None and seconds_passed < 10):
seconds_passed = time.time() - t_nought
if seconds_passed >= 10:
#TIMED OUT
This has the ugliness of being a busy wait, but I think it accomplishes what you want.
Additionally looking at the select call documentation again I think you may want to change it as follows:
SECONDS_TO_WAIT = 10
select.select([p.stderr],
[],
[p.stdout, p.stderr],
SECONDS_TO_WAIT)
Since you would typically want to read from stderr, you want to know when it has something available to read (ie the failure case).
I hope this helps.
This is what i came up with. Works when you need and don't need to timeout on thep process, but with a semi-busy loop.
def runCmd(cmd, timeout=None):
'''
Will execute a command, read the output and return it back.
#param cmd: command to execute
#param timeout: process timeout in seconds
#return: a tuple of three: first stdout, then stderr, then exit code
#raise OSError: on missing command or if a timeout was reached
'''
ph_out = None # process output
ph_err = None # stderr
ph_ret = None # return code
p = subprocess.Popen(cmd, shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
# if timeout is not set wait for process to complete
if not timeout:
ph_ret = p.wait()
else:
fin_time = time.time() + timeout
while p.poll() == None and fin_time > time.time():
time.sleep(1)
# if timeout reached, raise an exception
if fin_time < time.time():
# starting 2.6 subprocess has a kill() method which is preferable
# p.kill()
os.kill(p.pid, signal.SIGKILL)
raise OSError("Process timeout has been reached")
ph_ret = p.returncode
ph_out, ph_err = p.communicate()
return (ph_out, ph_err, ph_ret)
Here is a nice example:
from threading import Timer
from subprocess import Popen, PIPE
proc = Popen("ping 127.0.0.1", shell=True)
t = Timer(60, proc.kill)
t.start()
proc.wait()
Using select and sleeping doesn't really make much sense. select (or any kernel polling mechanism) is inherently useful for asynchronous programming, but your example is synchronous. So either rewrite your code to use the normal blocking fashion or consider using Twisted:
from twisted.internet.utils import getProcessOutputAndValue
from twisted.internet import reactor
def stop(r):
reactor.stop()
def eb(reason):
reason.printTraceback()
def cb(result):
stdout, stderr, exitcode = result
# do something
getProcessOutputAndValue('/bin/someproc', []
).addCallback(cb).addErrback(eb).addBoth(stop)
reactor.run()
Incidentally, there is a safer way of doing this with Twisted by writing your own ProcessProtocol:
http://twistedmatrix.com/projects/core/documentation/howto/process.html
Python 3.3
import subprocess as sp
try:
sp.check_call(["/subprocess"], timeout=10,
stdin=sp.DEVNULL, stdout=sp.DEVNULL, stderr=sp.DEVNULL)
except sp.TimeoutError:
# timeout (the subprocess is killed at this point)
except sp.CalledProcessError:
# subprocess failed before timeout
else:
# subprocess ended successfully before timeout
See TimeoutExpired docs.
If, as you said in the comments above, you're just tweaking the output each time and re-running the command, would something like the following work?
from threading import Timer
import subprocess
WAIT_TIME = 10.0
def check_cmd(cmd):
p = subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
def _check():
if p.poll()!=0:
print cmd+" did not quit within the given time period."
# check whether the given process has exited WAIT_TIME
# seconds from now
Timer(WAIT_TIME, _check).start()
check_cmd('echo')
check_cmd('python')
The code above, when run, outputs:
python did not quit within the given time period.
The only downside of the above code that I can think of is the potentially overlapping processes as you keep running check_cmd.
This is a paraphrase on Evan's answer, but it takes into account the following :
Explicitly canceling the Timer object : if the Timer interval would be long and the process will exit by its "own will" , this could hang your script :(
There is an intrinsic race in the Timer approach (the timer attempt killing the process just after the process has died and this on Windows will raise an exception).
DEVNULL = open(os.devnull, "wb")
process = Popen("c:/myExe.exe", stdout=DEVNULL) # no need for stdout
def kill_process():
""" Kill process helper"""
try:
process.kill()
except OSError:
pass # Swallow the error
timer = Timer(timeout_in_sec, kill_process)
timer.start()
process.wait()
timer.cancel()