uasyncio/aswitch : problem cancelling coros on the throw of a switch.

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
Jim.S
Posts: 80
Joined: Fri Dec 18, 2015 7:36 pm

uasyncio/aswitch : problem cancelling coros on the throw of a switch.

Post by Jim.S » Sun May 13, 2018 10:13 am

I am having problems cancelling a coro using await asyn.Cancellable.cancel_all()) from a aswitch close_func() call

I have successfully used aswitch to (a) send an event to a coro to get it to continue running (b) set a parameter to terminate a while loop, i.e.

Code: Select all

        loop=asyncio.get_event_loop()
        self.consumer_on_event=asyn.Event()
        pin11 = Pin('X11', Pin.IN, Pin.PULL_UP)
        switch11=sw.Switch(pin11)##3
        switch11.close_func(self.consumer_on_event.set)
and

Code: Select all

        pin12= Pin('X12', Pin.IN, Pin.PULL_UP)
        switch12=sw.Switch(pin12)##4
        switch12.close_func(self.set_parameter,(False,))       #set_ parameter is just a simple class method to set a boolan flag 
So I am reasonably confident I can get aswitch and uasyncio to work together nicely. I also can cancel my coros belonging to the consumer group using

Code: Select all

await asyn.Cancellable.cancel_all(group='consumer')
Now I want to use switch12 to cancel a cancellable task belonging to a group (identified by the string consumer) i.e.

Code: Select all

switch12.close_func(asyn.Cancellable.cancel_all,(group='consumer',) )
however, this line is syntactially incorrect, So I tried the following

Code: Select all

switch12.close_func(asyn.Cancellable.cancel_all(group='consumer'))
This line throws a TypeError: 'generator' object is not callable exception

What am I doing wrong?

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

Re: uasyncio/aswitch : problem cancelling coros on the throw of a switch.

Post by pythoncoder » Mon May 14, 2018 5:48 am

This is a classic red and blue function problem. The aswitch class expects a function as an argument whereas cancel_all is a coroutine. In uasyncio coroutines are implemented as generators, hence your error message. You need to do something like this

Code: Select all

async def cancellation():
    await asyn.Cancellable.cancel_all(group='consumer')

def terminator():  # This is a function
    loop = asyncio.get_event_loop()
    loop.create_task(cancellation())

switch12.close_func(terminator)
Note I haven't tested this but I'm pretty confident that this points to a solution.

Historical note. I wrote the aswitch module to use my old scheduler in the days before uasyncio was usable. When uasyncio matured I abandoned my now inferior scheduler and ported aswitch to uasyncio. I may be able to fix aswitch so that it can accept a coroutine or a function as an argument. But at present it expects a function.
Peter Hinch

Jim.S
Posts: 80
Joined: Fri Dec 18, 2015 7:36 pm

Re: uasyncio/aswitch : problem cancelling coros on the throw of a switch.

Post by Jim.S » Mon May 14, 2018 7:50 pm

Thanks Peter, I would never have been able to figure that out.

Jim.S
Posts: 80
Joined: Fri Dec 18, 2015 7:36 pm

Re: uasyncio/aswitch : problem cancelling coros on the throw of a switch.

Post by Jim.S » Fri Jun 01, 2018 8:48 pm

Eventually found the time to get this working.

1. I moved to using NamedTask as this is neater than using Cancellable with groups (at least for my case).
2. Working with classes, and making the 'cancelling function' a bound method gives a neat function/method to pass to the aswitch objects. (see the Indicator.stop method in the example below)

Code: Select all

import uasyncio as asyncio
import pyb
import asyn
import micropython
import aswitch as sw
from machine import Pin


class Indicator(object):
    # class to flash a pyboard led
    def __init__(self,color,sequence):
        #
        led_dict={'green':pyb.LED(2),'red':pyb.LED(1),'orange':pyb.LED(3),'blue':pyb.LED(4)}
        self.led=led_dict[color]
        self.color=color
        self.sequence=sequence
        
    @asyn.cancellable
    async def flash(self):
        #where sequence is a list of times in ms for the on off on etc 
        self.led.off()
        i=0
        l=len(self.sequence)
        while True:
            t_ms=self.sequence[i]
            self.led.toggle()
            await asyncio.sleep_ms(t_ms)
            #modulo thing
            i=(i+1)%l        
        
    def start(self):
        # this creates the task
        loop=asyncio.get_event_loop()
        #the color is the name of the NamedTask
        loop.create_task(asyn.NamedTask(self.color,self.flash)())

    def stop(self):
        # this terminates the task
        await asyn.NamedTask.cancel(self.color)
        self.led.off()

async def terminator(t):
    print('terminator sleep')
    await asyncio.sleep(t)
    print('terminator wakes')
    
print('main start')
loop=asyncio.get_event_loop()
green=Indicator('green',[100,100,300,300])
green.start()
pin12=Pin('X12', Pin.IN, Pin.PULL_UP)
switch12=sw.Switch(pin12)##4
switch12.close_func(green.stop)
loop.run_until_complete(terminator(10))
loop.close()
print('stop')


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

Re: uasyncio/aswitch : problem cancelling coros on the throw of a switch.

Post by pythoncoder » Sat Jun 02, 2018 6:50 am

I'm glad you've got it working and are finding my async code useful.

I revisited the Switch class: it turns out I had already implemented the change which allows these objects to trigger callbacks or schedule coroutines interchangeably. The dubious joys of old age mean that you do things then promptly forget you've done them :?

So your original code should have worked with

Code: Select all

switch12.close_func(asyn.Cancellable.cancel_all, ('consumer',))
I think the error was in the argument passing rather than (as I wrongly stated) the fact that you were passing a coroutine. You need to pass a callback/coro by name (i.e. not using function call syntax) followed by an optional tuple of positional args.

I'll amend the docs to clarify this.
Peter Hinch

Post Reply