How to add a component of randomness to a schedule - python

I am trying to schedule a job to run every 3 minutes on average, with a component of randomness between +/- 30 seconds. So the next job runs anywhere between 2mins 30secs - 3mins 30secs later.
This code works nicely for exactly 3 minutes, but I can't think of a way to introduce the 30 secs of randomness:
import schedule
def job:
print('hi')
schedule.every(3).minutes.do(job)

You need to look up the random module that comes with python.
import schedule
import random
def job():
print('hi')
two_mins_30 = 2 * 60 + 30
schedule.every(two_mins_30 + random.randint(0, 60)).seconds.do(job)
This calculation is: two minutes, 30 plus up to another minute at random.
Update:
It turns out you can directly do this with schedule because the Job class has a to() method:
schedule.every(two_mins_30).to(two_mins_30 + 60).seconds.do(job)

Related

Sleep until a certain amount of time has passed

I'm writing a function in Python that waits for an external motor to finish moving, but the amount of time it takes for the motor to move can be variable. I want to standardize the time in between each move — for example, they should all take 30 seconds.
Is there a way to implement a sleep function so that it sleeps the required amount of time until 30 seconds has passed?
For example:
if the motor takes 23 seconds, the function will wait until 30 seconds have passed, so it will sleep 7 seconds.
It sounds like don't want to sleep for 30 second but rather pad the time it takes to perform an activity with a sleep so that it always takes 30 seconds.
import time
from datetime import datetime, timedelta
wait_until_time = datetime.utcnow() + timedelta(seconds=30)
move_motor()
seconds_to_sleep = (wait_until_time - datetime.utcnow()).total_seconds()
time.sleep(seconds_to_sleep)
if you are going to be doing this in multiple places you can create a decorator that you can apply to any function
import functools
import time
from datetime import datetime, timedelta
def minimum_execution_time(seconds=30)
def middle(func)
#functools.wraps(func)
def wrapper(*args, **kwargs):
wait_until_time = datetime.utcnow() + timedelta(seconds=seconds)
result = func(*args, **kwargs)
seconds_to_sleep = (wait_until_time - datetime.utcnow()).total_seconds()
time.sleep(seconds_to_sleep)
return result
return wrapper
You can then use this like so
#minimum_execution_time(seconds=30)
def move_motor(...)
# Do your stuff
It depends on how you are monitoring the runtime of your motor.
For the sake of example, let's assume you have that value stored in a variable,
slowdown_time
#slowdown_time is the variable that stores the time it took for the motor to slow down.
import time
desired_interval = 30 #seconds
sleep_time = desired_interval - slowdown_time
#sleep for remaining time
time.sleep(sleep_time)
Hope this is helpful!

How to make the threading be executed following a fequencey (say, every 3 sec) at a (relatively) accurate time considering function execution time?

I want to run a function every 3 second, and I do have searched similar topics. However, I didn't find any solution that can indeed meet my requirement, and the key issue is that in these solutions, they do not consider the time of executing the function itself. Consider the following code.
import datetime as dt
import time
import threading
def counting():
global num
time_now = dt.datetime.now()
if num > 0:
print(f'count: {num}, time now: {time_now}')
num -= 1
t = threading.Timer(3.0, counting)
t.start()
num = 5
counting()
This prints every 3.0 seconds. The main issue is that in a real case, in stead of print(f'count: {num}, time now: {time_now}') , I will call a function, say, func1(), which will take some time between 1 second and 2.5 seconds. Hence, the real interval time between two calls will be more than 3 seconds (about 4-5.5 seconds). How can I write it to be exactly (of course, very small error is allowed) every 3 seconds between two calls? Thanks!
The way to do it is to use a monotonic clock to find out the current time, and subtract that from the time at which you next want your scheduled function to be called; then you know exactly how long you'll need to sleep for, regardless of how long your func1() took to execute. Here's an example (I removed the threading since the logic is the same regardless of whether it's running in the main thread or some child thread):
import random
import time
def func1():
seconds_to_sleep = random.randrange(1000, 2500) / 1000.0
print("pretending to work for %f seconds" % seconds_to_sleep)
time.sleep(seconds_to_sleep)
def scheduled_function(scheduled_time, now):
if (now > scheduled_time):
print("scheduled_function was called %f seconds late" % (now-scheduled_time))
else:
print("scheduled_function was called %f seconds early" % (scheduled_time-now))
next_call_time = time.monotonic() # schedule the first call to happen right away
while True:
now = time.monotonic()
time_until_call_time = next_call_time-now
if (time_until_call_time > 0.0):
time.sleep(time_until_call_time) # wait until our next scheduled call-time
scheduled_function(next_call_time, time.monotonic())
func1() # sleep for some unpredictable amount of time to simulate a workload
next_call_time = next_call_time + 3.0 # each call should happen ~3 seconds after the previous call
The solution using the Ada programming language is very simple. Ada provides a "delay until" syntax allowing to delay until some future time.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Calendar; use Ada.Calendar;
with Ada.Calendar.Formatting; use Ada.Calendar.Formatting;
procedure Main is
task periodic;
task body periodic is
Now : Time;
Future : Time;
The_Delay : constant duration := 3.0;
begin
for I in 1..10 loop
Now := Clock;
Put_Line("Periodic message at time " & Image(Now));
Future := Now + The_Delay;
delay 1.0;
delay until Future;
end loop;
end periodic;
begin
null;
end Main;
A recent execution of this program results in the following output (Time is the UTC time zone):
Periodic message at time 2021-05-27 02:26:46
Periodic message at time 2021-05-27 02:26:49
Periodic message at time 2021-05-27 02:26:52
Periodic message at time 2021-05-27 02:26:55
Periodic message at time 2021-05-27 02:26:58
Periodic message at time 2021-05-27 02:27:01
Periodic message at time 2021-05-27 02:27:04
Periodic message at time 2021-05-27 02:27:07
Periodic message at time 2021-05-27 02:27:10
Periodic message at time 2021-05-27 02:27:13
The variable The_Delay is a constant value indicating 3.0 seconds. The Now time is the time at the beginning of each iteration. The Future time is Now plus 3.0 seconds. The resulting time is not offset by the task execution as long as that execution does not exceed 3.0 seconds. In order to simulate a long task execution the task delays (sleeps) 1.0 seconds during each iteration. The "delay 1.0;" statement is an absolute delay while the "delay until Future;" statement is a relative delay.
I guess I got the answer myself. We need to use schedule module. See the examples in https://schedule.readthedocs.io/en/stable/examples.html
The following is my test code.
import datetime as dt
import time
import threading
import schedule
def job():
print("I'm running on thread %s" % threading.current_thread())
print(dt.datetime.now())
time.sleep(2)
def run_threaded(job_func):
job_thread = threading.Thread(target=job_func)
job_thread.start()
schedule.every(6).seconds.do(run_threaded, job)
schedule.every(6).seconds.do(run_threaded, job)
schedule.every(6).seconds.do(run_threaded, job)
t_end = dt.datetime.now() + dt.timedelta(seconds = 20)
while dt.datetime.now() < t_end:
schedule.run_pending()
You can see that I am letting it run for every 6 seconds (by applying three multithread parallel computing), and I do let it sleep for 2 seconds in the function job() to replace the real running time. And from the output result, you will see that it runs every 6 seconds instead of 8 seconds!

Schedule task regularly at alternating times in python

I would like to schedule a task at minute :57 and minute :05, alternating every two hours. So the intervall during execution is alternating between 52 minutes and 68 minutes, on average 60 minutes.
For a given hour x the times when I would like to execute code are:
x:05; x:57; (x+2):05, (x+2):57; ...
I currently use the python module schedule and the following code:
schedule.every(2).hours.at(':05').do(job1)
schedule.every(2).hours.at(':57').do(job2)
while True:
schedule.run_pending()
time.sleep(1)
How can I best ensure that the code is not running at x:57 and then again at (x+1):05 the following hour (meaning the intervall is only 8 minutes long)?
Edit: I could add something along the lines of:
minutes = datetime.now().minutes
if not (minutes < 5 or minutes > 57):
time.sleep(60 - minutes)
and then schedule but I would miss 2 executions which is not ideal.

How can I repeat a program every x seconds for x seconds(in my case, every 3 seconds for 30 seconds)

My programming teacher had assigned me a simple task of creating a program that randomly generates a number between 1-100 every 3 seconds for 30 seconds. Here is my program so far:
import random
import time
while True:
print(random.randint(0,100))
time.sleep(3)
At the moment, it only prints every 3 seconds infinitely. I want it to repeat it for only 30 seconds.
Thanks for your consideration!
import random
import time
a=time.clock()
while ((time.clock()-a)<30):
print(random.randint(0,100))
time.sleep(3)
It's an assignment, so we cannot solve it for you. However, we can guide you to the correct module to use, that's sched documented here https://docs.python.org/3.6/library/sched.html
Make use of time.time() function. It will give you the time in seconds since January 1, 1970, 00:00:00 (UTC).
import random
import time
counter = 30
start_time = time.time()
while time.time() - start_time < counter:
print(random.randint(0,100))
time.sleep(3)
Use a for range loop like so:
import time
import random
a=time.clock()
for x in range(10):
print(random.randint(1,100))
time.sleep(3)

Scheduling a task on python

I want to run a program that runs a function every 4 hours.
What is the least consuming way to do so?
Simlest way I can think of (in python since the post is tagged with python):
import time
while True:
do_task()
time.sleep(4 * 60 * 60) # 4 hours * 60 minutes * 60 seconds
You can use sched module
Here are the docs
https://docs.python.org/3.4/library/sched.html
Use the build in timer thread:
from threading import Timer
def function_to_be_scheduled():
"""Your CODE HERE"""
interval = 4 * 60 * 60 #interval (4hours)
Timer(interval, function_to_be_scheduled).start()

Categories

Resources