Yetti
Yetti

Reputation: 331

Using asycio to periodically call a function with a periodicy that depends on IO

I'm building a DJ lighting system with events that are triggered at certain beat intervals (i.e. flash strobe halfway between the next beat). The bpm (beats per minute) changes based on an external application and can never be assumed as fixed.

I would like to schedule a bunch of IO operations (flash strobe, trigger laser) to happen in the next beat by running a scheduling_function a few ms before the start of the next beat.

I know the time, in event_loop time units, when I would like the function to run (returned by get_next_time_to_run()).

I tried using the event_loop.call_at() function, with a recursive call, to continuously schedule the scheduling_function to be run right before the next beat. However, i get a RecursionError.

What might be a better way of setting this up. Here is my current code:

def schedule_next_frame():
    """This function is schedules the next frame (light, motor, lasers). It is trigged to run just before the next beat to give it time to prepare"""
    set_up_frame() #actual scheduling of IO
    frame += 1
    time_to_run = get_next_time_to_run(frame-0.1)
    loop.call_at(time_to_run, schedule_next_frame(frame))

print("Beginning YettiCubes2.0")
frame = get_current_beat()+1 #FIXME if this is really close to a beat boundary, we might tick over a beat before we can setup the next frame
loop.call_soon(schedule_next_frame())
loop.run_forever()

Upvotes: 0

Views: 44

Answers (1)

user4815162342
user4815162342

Reputation: 154846

You want to tell asyncio to run schedule_next_frame(frame) at the specified time. The following code doesn't do that:

loop.call_at(time_to_run, schedule_next_frame(frame))

Instead, it first calls schedule_next_frame recursively, and then passes the result of that call to call_at. Since the recursion is infinite, there never is any result, and you get an exception instead.

The correct way to tell call_at to run schedule_next_frame(frame) is:

loop.call_at(time_to_run, lambda: schedule_next_frame(frame))

The lambda expression will produce a function that, when called without arguments, invokes schedule_next_frame(frame).

Upvotes: 1

Related Questions