python - loop at exact time intervals - python-3.x

I want to run a piece of code at exact time intervals (of the order of 15 seconds)
Initially I used time.sleep(), but then the problem is the code takes a second or so to run, so it will get out of sync.
I wrote this, which I feel is untidy because I don't like using while loops. Is there a better way?
import datetime as dt
import numpy as np
iterations = 100
tstep = dt.timedelta(seconds=5)
for i in np.arange(iterations):
startTime = dt.datetime.now()
myfunction(doesloadsofcoolthings)
while dt.datetime.now() < startTime + tstep:
1==1

Ideally one would use threading to accomplish this. You can do something like
import threading
interval = 15
def myPeriodicFunction():
print "This loops on a timer every %d seconds" % interval
def startTimer():
threading.Timer(interval, startTimer).start()
myPeriodicFunction()
then you can just call
startTimer()
in order to start the looping timer.

Consider tracking the time it takes the code to run (a timer() function), then sleeping for 15 - exec_time seconds after completion.
start = datetime.now()
do_many_important_things()
end = datetime.now()
exec_time = end - start
time.sleep(15-exec_time.total_seconds())

You can use a simple bash line:
watch -n 15m python yourcode.py

Related

Do Something At a Consistent Frequency in Python

I have a python program that reads from a serial port, accesses registers then writes data to the CSV. Id like to perform a writing operation to the csv at a set frequency of 100Hz. Below is sample code related to the timing which attempts to limit writing to the csv. For simplicity it will only print to console rather than write to csv.
import datetime
from datetime import timezone
import time
FREQ = 5
CYCLETIME = 1/FREQ
def main():
while(1):
### Code that will read message bytes from a port
# Time start for Hz
start = time.monotonic()
delta = time.monotonic() - start
if delta < CYCLETIME:
time.sleep(CYCLETIME - delta)
# in the full program we write to a csv but in this simple program we will just print it
milliseconds_since_epoch = datetime.datetime.now(timezone.utc)
print(milliseconds_since_epoch)
if __name__ == "__main__":
main()
2020-08-24 18:57:15.572637+00:00
2020-08-24 18:57:15.773183+00:00
2020-08-24 18:57:15.973637+00:00
2020-08-24 18:57:16.174117+00:00
2020-08-24 18:57:16.374569+00:00
2020-08-24 18:57:16.575058+00:00
2020-08-24 18:57:16.775581+00:00
2020-08-24 18:57:16.976119+00:00
2020-08-24 18:57:17.176627+00:00
2020-08-24 18:57:17.377103+00:00
2020-08-24 18:57:17.577556+00:00
The output seems consistent for 5Hz but if I change it to 100Hz it seems inconsistent. It sounds like this could be a time accuracy drift associated to time.monotonic. Is this a good approach? Is time.montonic appropriate?
My knowledge with pythons thread library is limited but in the near future, I plan to create 2 child threads for each task. One which constantly reads from serial and another that will write (or in our case print to console every 100Hz).
Edit:
I took the solution below and modified it. The original solution seemed to only print once. Here is my new attempt:
import datetime
from datetime import timezone
import time
FREQ = 5
CYCLETIME = 1/FREQ
def main():
t0 = time.perf_counter() # Time ref point in ms
time_counter = t0 # Will be incremented with CYCLETIME for each iteration
while 1:
### Code that will read message bytes from a port
now = time.perf_counter()
elapsed_time = now - time_counter
if elapsed_time < CYCLETIME:
target_time = CYCLETIME - elapsed_time
time.sleep(target_time)
# In the full program we write to a csv but in this simple program we will just print it
milliseconds_since_epoch = datetime.datetime.now(timezone.utc)
print(milliseconds_since_epoch)
time_counter += CYCLETIME
if __name__ == "__main__":
main()
Output:
I used matplot lib to determine the frequency to create this output. I take the rolling window difference of the current and previous value and inverse it since frequency=1/(time diff).
There are 5 problems in your algorithm:
Using time.sleep() has low accuracy, sometimes with an error around 10-13ms, depending on your system. (ref)
start and delta variables do not track the time spent on print(), as delta is set right after start.
start and delta variables use two different time.monotonic(). You should call the function only once, and pass the value to the other variable to make sure the same time value is being used.
Tick rate of time.monotonic() is 64 per second. Use time.perf_counter() instead. (ref)
You are doing time.sleep() on a delta time, which has low accuracy. If you have two threads running together, the cycles will hardly be synchronized. You need a time tracker which makes it sleep based on elapsed time.
Fix:
The following code make use of time.perf_counter(), and uses time_counter to keep track of the previous loop iteration timestamp.
The next loop time is the previous loop timestamp + cycle time - elapsed time. Thus, we will make sure the time.sleep() sleeps the program until then.
def main():
t0 = time.perf_counter() # Time ref point
time_counter = t0 # Will be incremented with CYCLETIME for each iteration
while 1:
### Code that will read message bytes from a port
now = time.perf_counter()
elapsed_time = now - t0
target_time = time_counter + CYCLETIME
if elapsed_time < target_time:
time.sleep(target_time - elapsed_time)
# In the full program we write to a csv but in this simple program we will just print it
milliseconds_since_epoch = datetime.datetime.now(timezone.utc)
print(milliseconds_since_epoch)
time_counter += CYCLETIME

How to make python script update in realtime (run continuously)

Hello I am a beginner in python and I just learned the fundamentals and basics of programming in python. I wanted to make this program that will notify me when my class ends, however, once I run my program it only executes it once
When I loop it, it does not continuously access my date and time (instead it takes the time from when the code is executed). Any suggestion on how to resolve this problem?
import win10toast
import datetime
currentDT = datetime.datetime.now()
toaster = win10toast.ToastNotifier()
while (1):
def Period_A():
if currentDT.hour == 7 and currentDT.minute == 30:
toaster.show_toast('Shedule Reminder', 'It is Period A time!', duration=10)
I wanted the code to run in the background and update the value of the date and time continuously so that the notification will appear on the desired time not the time when the code is executed ;).
The line currentDT = datetime.datetime.now() is only called once throughout the whole program, and therefore stays constant to approximately the time you ran the script.
Since you're want to continuously check the time to compare it to a set time, you'll have to move that line to inside the loop.
Secondly, you define a function Period_A in the loop, which by itself does nothing because you don't call the function. If you don't need the abstraction a function gives you, there is no point in making one just to call it once.
import datetime
import win10toast
toaster = win10toast.ToastNotifier()
while 1:
currentDT = datetime.datetime.now()
if currentDT.hour == 7 and currentDT.minute == 30:
toaster.show_toast('Shedule Reminder', 'It is Period A time!', duration=10)

python speedup a simple function

I try to find a simple way to "speed up" simple functions for a big script so I googled for it and found 3 ways to do that.
but it seems the time they need is always the same.
so what I am doing wrong testing them?
file1:
from concurrent.futures import ThreadPoolExecutor as PoolExecutor
from threading import Thread
import time
import os
import math
#https://dev.to/rhymes/how-to-make-python-code-concurrent-with-3-lines-of-code-2fpe
def benchmark():
start = time.time()
for i in range (0, 40000000):
x = math.sqrt(i)
print(x)
end = time.time()
print('time', end - start)
with PoolExecutor(max_workers=3) as executor:
for _ in executor.map((benchmark())):
pass
file2:
#the basic way
from threading import Thread
import time
import os
import math
def calc():
start = time.time()
for i in range (0, 40000000):
x = math.sqrt(i)
print(x)
end = time.time()
print('time', end - start)
calc()
file3:
import asyncio
import uvloop
import time
import math
#https://github.com/magicstack/uvloop
async def main():
start = time.time()
for i in range (0, 40000000):
x = math.sqrt(i)
print(x)
end = time.time()
print('time', end - start)
uvloop.install()
asyncio.run(main())
every file needs about 180-200 sec
so i 'can't see' a difference.
I googled for it and found 3 ways to [speed up a function], but it seems the time they need is always the same. so what I am doing wrong testing them?
You seemed to have found strategies to speed up some code by parallelizing it, but you failed to implement them correctly. First, the speedup is supposed to come from running multiple instances of the function in parallel, and the code snippets make no attempt to do that. Then, there are other problems.
In the first example, you pass the result benchmark() to executor.map, which means all of benchmark() is immediately executed to completion, thus effectively disabling parallelization. (Also, executor.map is supposed to receive an iterable, not None, and this code must have printed a traceback not shown in the question.) The correct way would be something like:
# run the benchmark 5 times in parallel - if that takes less
# than 5x of a single benchmark, you've got a speedup
with ThreadPoolExecutor(max_workers=5) as executor:
for _ in range(5):
executor.submit(benchmark)
For this to actually produce a speedup, you should try to use ProcessPoolExecutor, which runs its tasks in separate processes and is therefore unaffected by the GIL.
The second code snippet never actually creates or runs a thread, it just executes the function in the main thread, so it's unclear how that's supposed to speed things up.
The last snippet doesn't await anything, so the async def works just like an ordinary function. Note that asyncio is an async framework based on switching between tasks blocked on IO, and as such can never speed CPU-bound calculations.

Problem with Timeit - Processing time - Weird Number

Basicly i have a file who have the code to calculate the time of some process. But it`s not working right. Just work if i put the contents of functions inside my main.
Wrong calculate from file util.py
import timeit
start_process = 0
finish_process = 0
def starting_process():
start_process = timeit.default_timer()
def time_process():
finish_process = timeit.default_timer()
time_total = finish_process - start_process
# output running time in a nice format.
mins, secs = divmod(time_total, 60)
hours, mins = divmod(mins, 60)
return "Total running time: %d:%d:%d" % (hours, mins, secs)
In a process who spend 4 seconds, it show me like this: 238:23:55
If i put this code in my main without functions it shows right: 0:0:4
Anyone can help in why it is happening?
Thanks,
Lucas Antoni.
This function:
def starting_process():
start_process = timeit.default_timer()
creates a local variable start_process, sets a value, then returns None (since there is no explicit return statement). The value is lost when the function ends. The function does not affect the global variable start_process.
So when time_process() is called, the expression which uses start_process sees that there is no local variable of that name in time_process, but instead finds the global variable which is set to 0.
One way to fix this would be to use
def starting_process():
global start_process
start_process = timeit.default_timer()
to tell starting_process that the variable start_process is the global variable.

Python string time [duplicate]

How can I run a function in Python, at a given time?
For example:
run_it_at(func, '2012-07-17 15:50:00')
and it will run the function func at 2012-07-17 15:50:00.
I tried the sched.scheduler, but it didn't start my function.
import time as time_module
scheduler = sched.scheduler(time_module.time, time_module.sleep)
t = time_module.strptime('2012-07-17 15:50:00', '%Y-%m-%d %H:%M:%S')
t = time_module.mktime(t)
scheduler_e = scheduler.enterabs(t, 1, self.update, ())
What can I do?
Reading the docs from http://docs.python.org/py3k/library/sched.html:
Going from that we need to work out a delay (in seconds)...
from datetime import datetime
now = datetime.now()
Then use datetime.strptime to parse '2012-07-17 15:50:00' (I'll leave the format string to you)
# I'm just creating a datetime in 3 hours... (you'd use output from above)
from datetime import timedelta
run_at = now + timedelta(hours=3)
delay = (run_at - now).total_seconds()
You can then use delay to pass into a threading.Timer instance, eg:
threading.Timer(delay, self.update).start()
Take a look at the Advanced Python Scheduler, APScheduler: http://packages.python.org/APScheduler/index.html
They have an example for just this usecase:
http://packages.python.org/APScheduler/dateschedule.html
from datetime import date
from apscheduler.scheduler import Scheduler
# Start the scheduler
sched = Scheduler()
sched.start()
# Define the function that is to be executed
def my_job(text):
print text
# The job will be executed on November 6th, 2009
exec_date = date(2009, 11, 6)
# Store the job in a variable in case we want to cancel it
job = sched.add_date_job(my_job, exec_date, ['text'])
Might be worth installing this library: https://pypi.python.org/pypi/schedule, basically helps do everything you just described. Here's an example:
import schedule
import time
def job():
print("I'm working...")
schedule.every(10).minutes.do(job)
schedule.every().hour.do(job)
schedule.every().day.at("10:30").do(job)
schedule.every().monday.do(job)
schedule.every().wednesday.at("13:15").do(job)
while True:
schedule.run_pending()
time.sleep(1)
Here's an update to stephenbez' answer for version 3.5 of APScheduler using Python 2.7:
import os, time
from apscheduler.schedulers.background import BackgroundScheduler
from datetime import datetime, timedelta
def tick(text):
print(text + '! The time is: %s' % datetime.now())
scheduler = BackgroundScheduler()
dd = datetime.now() + timedelta(seconds=3)
scheduler.add_job(tick, 'date',run_date=dd, args=['TICK'])
dd = datetime.now() + timedelta(seconds=6)
scheduler.add_job(tick, 'date',run_date=dd, kwargs={'text':'TOCK'})
scheduler.start()
print('Press Ctrl+{0} to exit'.format('Break' if os.name == 'nt' else 'C'))
try:
# This is here to simulate application activity (which keeps the main thread alive).
while True:
time.sleep(2)
except (KeyboardInterrupt, SystemExit):
# Not strictly necessary if daemonic mode is enabled but should be done if possible
scheduler.shutdown()
I've confirmed the code in the opening post works, just lacking scheduler.run(). Tested and it runs the scheduled event. So that is another valid answer.
>>> import sched
>>> import time as time_module
>>> def myfunc(): print("Working")
...
>>> scheduler = sched.scheduler(time_module.time, time_module.sleep)
>>> t = time_module.strptime('2020-01-11 13:36:00', '%Y-%m-%d %H:%M:%S')
>>> t = time_module.mktime(t)
>>> scheduler_e = scheduler.enterabs(t, 1, myfunc, ())
>>> scheduler.run()
Working
>>>
I ran into the same issue: I could not get absolute time events registered with sched.enterabs to be recognized by sched.run. sched.enter worked for me if I calculated a delay, but is awkward to use since I want jobs to run at specific times of day in particular time zones.
In my case, I found that the issue was that the default timefunc in the sched.scheduler initializer is not time.time (as in the example), but rather is time.monotonic. time.monotonic does not make any sense for "absolute" time schedules as, from the docs, "The reference point of the returned value is undefined, so that only the difference between the results of consecutive calls is valid."
The solution for me was to initialize the scheduler as
scheduler = sched.scheduler(time.time, time.sleep)
It is unclear whether your time_module.time is actually time.time or time.monotonic, but it works fine when I initialize it properly.
dateSTR = datetime.datetime.now().strftime("%H:%M:%S" )
if dateSTR == ("20:32:10"):
#do function
print(dateSTR)
else:
# do something useful till this time
time.sleep(1)
pass
Just looking for a Time of Day / Date event trigger:
as long as the date "string" is tied to an updated "time" string, it works as a simple TOD function. You can extend the string out to a date and time.
whether its lexicographical ordering or chronological order comparison,
as long as the string represents a point in time, the string will too.
someone kindly offered this link:
String Comparison Technique Used by Python
had a really hard time getting these answers to work how i needed it to,
but i got this working and its accurate to .01 seconds
from apscheduler.schedulers.background import BackgroundScheduler
sched = BackgroundScheduler()
sched.start()
def myjob():
print('job 1 done at: ' + str(dt.now())[:-3])
dt = datetime.datetime
Future = dt.now() + datetime.timedelta(milliseconds=2000)
job = sched.add_job(myjob, 'date', run_date=Future)
tested accuracy of timing with this code:
at first i did 2 second and 5 second delay, but wanted to test it with a more accurate measurement so i tried again with 2.55 second delay and 5.55 second delay
dt = datetime.datetime
Future = dt.now() + datetime.timedelta(milliseconds=2550)
Future2 = dt.now() + datetime.timedelta(milliseconds=5550)
def myjob1():
print('job 1 done at: ' + str(dt.now())[:-3])
def myjob2():
print('job 2 done at: ' + str(dt.now())[:-3])
print(' current time: ' + str(dt.now())[:-3])
print(' do job 1 at: ' + str(Future)[:-3] + '''
do job 2 at: ''' + str(Future2)[:-3])
job = sched.add_job(myjob1, 'date', run_date=Future)
job2 = sched.add_job(myjob2, 'date', run_date=Future2)
and got these results:
current time: 2020-12-10 19:50:44.632
do job 1 at: 2020-12-10 19:50:47.182
do job 2 at: 2020-12-10 19:50:50.182
job 1 done at: 2020-12-10 19:50:47.184
job 2 done at: 2020-12-10 19:50:50.183
accurate to .002 of a second with 1 test
but i did run a lot of tests and accuracy ranged from .002 to .011
never going under the 2.55 or 5.55 second delay
#everytime you print action_now it will check your current time and tell you should be done
import datetime
current_time = datetime.datetime.now()
current_time.hour
schedule = {
'8':'prep',
'9':'Note review',
'10':'code',
'11':'15 min teabreak ',
'12':'code',
'13':'Lunch Break',
'14':'Test',
'15':'Talk',
'16':'30 min for code ',
'17':'Free',
'18':'Help ',
'19':'watever',
'20':'watever',
'21':'watever',
'22':'watever'
}
action_now = schedule[str(current_time.hour)]

Resources