Framework

How the framework works

Using tasko (now called CircuitPyton Async) a set of tasks read from a config file are run at set intervals. This means while a certain task is waiting for a response from something like the radio, other tasks are run.

A state machine is also defined in the config file. Each state has its own set of tasks to run (and their coresponding intervals). All the details for transitions are handled by the state machine.

One can also define exit and enter functions for each state.

State Machine

class state_machine.StateMachine[source]

Singleton State Machine Class

start(start_state: str)[source]

Starts the state machine

Args: :param start_state: The state to start the state machine in :type start_state: str

stop_all()[source]

Stops all running tasko processes

switch_to(state_name: str, force=False)[source]

Switches the state of the cubesat to the new state

Args: :param state_name: The name of the state to switch to :type state_name: str

Tasko

tasko.get_loop(debug=False)[source]

Returns the singleton event loop

tasko.add_task(awaitable_task, priority)

Add a concurrent task (known as a coroutine, implemented as a generator in CircuitPython) Use:

scheduler.add_task( my_async_method() )

Parameters

awaitable_task – The coroutine to be concurrently driven to completion.

tasko.run_later(seconds_to_delay, awaitable_task, priority)

Add a concurrent task, delayed by some seconds. Use:

tasko.run_later( seconds_to_delay=1.2, my_async_method() )

Parameters
  • seconds_to_delay – How long until the task should be kicked off?

  • awaitable_task – The coroutine to be concurrently driven to completion.

tasko.schedule(hz: float, coroutine_function, priority, *args, **kwargs)

Describe how often a method should be called.

Your event loop will call this coroutine on the hz schedule. Only up to 1 instance of your method will be alive at a time.

This will use sleep() internally when there’s nothing to do and scheduled, waiting functions consume no cpu so you should feel pretty good about using scheduled async functions.

usage:
async def main_loop:

await your_code()

scheduled_task = get_loop().schedule(hz=100, coroutine_function=main_loop) get_loop().run()

Parameters
  • hz – How many times per second should the function run?

  • coroutine_function – the async def function you want invoked on your schedule

  • event_loop – An event loop that can .sleep() and .add_task. Like BudgetEventLoop.

tasko.schedule_later(hz: float, coroutine_function, priority, *args, **kwargs)

Like schedule, but invokes the coroutine_function after the first hz interval.

See schedule api for parameters.

async tasko.sleep(seconds)

From within a coroutine, this suspends your call stack for some amount of time.

NOTE: Always await this! You will have a bad time if you do not.

Parameters

seconds – Floating point; will wait at least this long to call your task again.

tasko.suspend()

For making library functions that suspend and then resume later on some condition E.g., a scope manager for SPI

To use this you will stash the resumer somewhere to call from another coroutine, AND you will await suspender to pause this stack at the spot you choose.

:returns (async_suspender, resumer)

tasko.run()
Use:
async def application_loop():

pass

def run():

main_loop = Loop() loop.schedule(100, application_loop) loop.run()

if __name__ == ‘__main__’:

run()

The crucial StopIteration exception signifies the end of a coroutine in CircuitPython. Other Exceptions that reach the runner break out, stopping your app and showing a stack trace.