uasyncio - sched module, long delay events.

Discussion about programs, libraries and tools that work with MicroPython. Mostly these are provided by a third party.
Target audience: All users and developers of MicroPython.
Post Reply
User avatar
AndreaC
Posts: 4
Joined: Mon Aug 31, 2020 10:11 pm
Location: Italy

uasyncio - sched module, long delay events.

Post by AndreaC » Thu Oct 08, 2020 11:16 am

Hello,
struggling days to understand why a schedule like this (running on a pyboardv1.1 fw:1.13):

Code: Select all

async def mycoro():
    print('hello world')
    await asyncio.sleep(0)

async def main():
    asyncio.create_task(schedule(mycoro, hrs=12))

try:
    loop = asyncio.get_event_loop()
    loop.create_task(main())
    loop.run_forever()
except KeyboardInterrupt:
    pass
finally:
    asyncio.new_event_loop()  # Clear retained state	
instead of everyday at 12am launches mycoro approximately every 16mins and 40secs.
Eventually today, looking at schedule coro definition I discovered the clause:
maxt = 1000 # uasyncio can't handle arbitrarily long delays
Onestly I can't figure out how to achive my goal? should the schedule coro return instead of launch the passed function just after 1000secs?
I am missing something?
Hope I explained myself clearly.
Regards

User avatar
AndreaC
Posts: 4
Joined: Mon Aug 31, 2020 10:11 pm
Location: Italy

Re: uasyncio - sched module, long delay events.

Post by AndreaC » Thu Oct 08, 2020 3:19 pm

AndreaC wrote:
Thu Oct 08, 2020 11:16 am
Hello,
struggling days to understand why a schedule like this (running on a pyboardv1.1 fw:1.13):

Code: Select all

async def mycoro():
    print('hello world')
    await asyncio.sleep(0)

async def main():
    asyncio.create_task(schedule(mycoro, hrs=12))

try:
    loop = asyncio.get_event_loop()
    loop.create_task(main())
    loop.run_forever()
except KeyboardInterrupt:
    pass
finally:
    asyncio.new_event_loop()  # Clear retained state	
instead of everyday at 12am launches mycoro approximately every 16mins and 40secs.
Eventually today, looking at schedule coro definition I discovered the clause:
maxt = 1000 # uasyncio can't handle arbitrarily long delays
Onestly I can't figure out how to achive my goal? should the schedule coro return instead of launch the passed function just after 1000secs?
I am missing something?
Hope I explained myself clearly.
Regards
I have apparently by-passed the problem by modifying the schedule function in order to wait until its time to launch the func, but I am still not sure if my approach is correct.

Code: Select all

async def schedule(func, *args, times=None, **kwargs):
    fcron = cron(**kwargs)
    maxt = 1000  # uasyncio can't handle arbitrarily long delays
    while times is None or times > 0:
        tw = fcron(int(time()))  # Time to wait (s)
        while tw > 0:  # While there is still time to wait
            # *** Added a new variable here tw_***
            tw_ = min(tw, maxt)
            await asyncio.sleep(tw_)
            tw -= tw_
        res = launch(func, args)
        if times is not None:
            times -= 1
        await asyncio.sleep_ms(1200)  # ensure we're into next second
    return res

User avatar
pythoncoder
Posts: 4641
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: uasyncio - sched module, long delay events.

Post by pythoncoder » Fri Oct 09, 2020 10:19 am

See my scheduling module which allows uasyncio tasks to be scheduled over very long periods.

As a general point there is normally no point in using micropython.schedule() with uasyncio. Its purpose is to be called from interrupt service routines.

The uasyncio way is for a coroutine to pause for a period, then trigger the task with asyncio.create_task(). However the way that uasyncio implements sleep() and sleep_ms() means there is an upper limit to the delay. My schedule module overcomes this limitation.
Peter Hinch

User avatar
AndreaC
Posts: 4
Joined: Mon Aug 31, 2020 10:11 pm
Location: Italy

Re: uasyncio - sched module, long delay events.

Post by AndreaC » Fri Oct 09, 2020 12:24 pm

Thank you Peter, but the point is that I am indeed talking about your schedule module not the micropython.schedule().

Code: Select all

# sched.py

# Copyright (c) 2020 Peter Hinch
# Released under the MIT License (MIT) - see LICENSE file

import uasyncio as asyncio
from sched.primitives import launch
from time import time
from sched.cron import cron

async def schedule(func, *args, times=None, **kwargs):
    fcron = cron(**kwargs)
    maxt = 1000  # uasyncio can't handle arbitrarily long delays
    while times is None or times > 0:
       tw = fcron(int(time()))  # Time to wait (s)
        while tw > 0:  # While there is still time to wait
            tw = min(tw, maxt)
            await asyncio.sleep(tw)
            tw -= maxt
        res = launch(func, args)
        if times is not None:
            times -= 1
        await asyncio.sleep_ms(1200)  # ensure we're into next second
    return res
For what I can see, if the value returned by the cron object

Code: Select all

tw = fcron(int(time()))  # Time to wait (s)
is greater than 1000 as in my case, the schedule coro according to

Code: Select all

while tw > 0:  # While there is still time to wait
	tw = min(tw, maxt)
        await asyncio.sleep(tw)
        tw -= maxt
res = launch(func, args)
will launches de func at the 1000th second, so I can not figure out how it can deal with long delays?

User avatar
pythoncoder
Posts: 4641
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: uasyncio - sched module, long delay events.

Post by pythoncoder » Sat Oct 10, 2020 9:26 am

Ah, sorry about the misunderstanding.

You have indeed found a howler of a bug in my code. :(
[EDIT]
I have pushed an update. This is my fix:

Code: Select all

async def schedule(func, *args, times=None, **kwargs):
    fcron = cron(**kwargs)
    maxt = 1000  # uasyncio can't handle arbitrarily long delays
    while times is None or times > 0:
        tw = fcron(int(time()))  # Time to wait (s)
        while tw > 0:  # While there is still time to wait
            await asyncio.sleep(min(tw, maxt))  # Never wait longer than maxt
            tw -= maxt
        res = launch(func, args)
        if times is not None:
            times -= 1
        await asyncio.sleep_ms(1200)  # ensure we're into next second
    return res
I have modified your test script to make it more compliant with the V3 way of doing things (not that this has anything to do with the bug).

Code: Select all

import uasyncio as asyncio
from sched.primitives import launch
from time import localtime
from sched.sched import schedule

async def mycoro():
    print('hello world')
    yr, mo, md, h, m, s, wd = localtime()[:7]  # Provide confirmation of when it ran
    fst = 'Callback {:02d}:{:02d}:{:02d} on {:02d}/{:02d}/{:02d}'
    print(fst.format(h, m, s, md, mo, yr))
    await asyncio.sleep(0)

async def main():
    asyncio.create_task(schedule(mycoro, hrs=11))
    while True:
        await asyncio.sleep(100)  # Keep the scheduler running forever

try:
    asyncio.run(main())  # This has superseded the event loop stuff
finally:
    asyncio.new_event_loop()  # Clear retained state	
Peter Hinch

User avatar
AndreaC
Posts: 4
Joined: Mon Aug 31, 2020 10:11 pm
Location: Italy

Re: uasyncio - sched module, long delay events.

Post by AndreaC » Sat Oct 10, 2020 11:59 am

Thanks Peter, especially for the valuable advice on my code. I would like to congratulate you for the amazing work you did with the uasyncio tutorial. Since I discovered the existence of uasyncio I completely rewrote the code of my project which was previously based on threads. Now I have no more problems regarding the heap fragmentation. :D

Post Reply