Issue
I recently have been trying to set event from non async callback and till it set one of my asyncio task is waiting. problem is I am unable to pass asincio.Event variable to non-async callback.
I am getting bellow lookup error.
temp_thing = thing_var.get()
LookupError: <ContextVar name='thing' at 0x7f9ec00bd0>
for making "asyncio.Event" available out of asyncio.run loop. I have used "ContextVar". this was supposed to make my "asyncio.Event" global and hence available for callback to set it. I can print "ContexVar" name mean "ContexVar" is reaching to callback but for some reason it is not passing the "asyncio.Event".
here bellow is relevant code extract.
import asyncio
from contextvars import ContextVar
from SX127x.LoRa import *
from SX127x.LoRaArgumentParser import LoRaArgumentParser
from SX127x.board_config import BOARD
class LoRaRcvCont(LoRa):
server_status = False
def __init__(self, verbose=False):
super(LoRaRcvCont, self).__init__(verbose)
self.set_mode(MODE.SLEEP)
self.set_dio_mapping([0] * 6)
# on receive callback
def on_rx_done(self):
print("LoRa RxDone")
print(f"thing event name: {thing_var.name}")
temp_thing = thing_var.get()
print("tried to send event thing ...")
temp_thing.set()
self.clear_irq_flags(RxDone=1)
# setup lora releted configs
def lora_config_setup():
parser = LoRaArgumentParser("Continous LoRa receiver.")
lora = LoRaRcvCont(verbose=False)
args = parser.parse_args(lora) # lora sanity check
assert (lora.get_agc_auto_on() == 1)
lora.reset_ptr_rx()
lora.set_mode(MODE.RXCONT)
return lora
async def superloop():
# main super loop
thing = asyncio.Event()
thing_var.set(thing)
print("asyncio loop starts ...")
await asyncio.gather(
asyncio.create_task(task_1(thing)),
)
async def task_1(thing):
count = 0
while True:
# acquire the lock to protect the critical section
print("waiting for thing")
await thing.wait()
print("task1: got event thing set")
count = count + 1
await asyncio.sleep(1)
print("task 1: reset event thing set")
thing.clear()
if __name__ == "__main__":
# from here code execution starts
print('code execution started')
thing_var = ContextVar('thing')
BOARD.setup()
lora = lora_config_setup()
print("\nasync superloop code running")
asyncio.run(superloop())
print("code finished")
again output of this code when event callback function executed is as bellow.
code execution started
async superloop code running
asyncio loop starts ...
waiting for thing
LoRa RxDone
thing event name: thing
Traceback (most recent call last):
File "/home/pi/sisig/git_repo_2/nurster-gateway/SX127x/LoRa.py", line 158, in _dio0
self.on_rx_done()
File "/home/pi/sisig/git_repo_2/nurster-gateway/pytest3.py", line 86, in on_rx_done
temp_thing = thing_var.get()
LookupError: <ContextVar name='thing' at 0x7f9ec00bd0>
so if possible do provide info how I should use "ContexVar" or some other methode to pass event to "on_rx_done" event callback.
Solution
A ContextVar seems to be exactly the wrong tool for what you are trying to do. You say you have used it to create a global variable, but that is not what it is for.
You don't need to create a global variable at all. I've re-arranged your code a little bit to create an Event object right at the beginning of superloop. I pass it to the LoRaRcvCont
constructor (through the lora_config_setup method). I also pass it to task_1. That's all you have to do. No global variable necessary.
I didn't change your code except for the necessary changes, with one exception: I replaced your call to asyncio.gather
with a simple await expression. Gathering the result from one task, although it works, seems to me like poor style.
I could not, of course, run this program so my apologies for any stupid mistakes. But I hope you get the idea.
import asyncio
from SX127x.LoRa import *
from SX127x.LoRaArgumentParser import LoRaArgumentParser
from SX127x.board_config import BOARD
class LoRaRcvCont(LoRa):
server_status = False
def __init__(self, event, verbose=False): # changed
self.event = event # changed
super(LoRaRcvCont, self).__init__(verbose)
self.set_mode(MODE.SLEEP)
self.set_dio_mapping([0] * 6)
# on receive callback
def on_rx_done(self):
print("LoRa RxDone")
print(f"thing event name: {thing_var.name}")
self.event.set() # changed
print("tried to send event thing ...")
self.clear_irq_flags(RxDone=1)
# setup lora releted configs
def lora_config_setup(event): # changed
parser = LoRaArgumentParser("Continous LoRa receiver.")
lora = LoRaRcvCont(event, verbose=False) # changed
args = parser.parse_args(lora) # lora sanity check
assert (lora.get_agc_auto_on() == 1)
lora.reset_ptr_rx()
lora.set_mode(MODE.RXCONT)
return lora
async def superloop():
# main super loop
ev = asyncio.Event() # changed
lora = lora_config_setup(ev) # changed
print("asyncio loop starts ...")
await task_1(ev) # changed
# removed call to gather
async def task_1(event): # changed thing to event
count = 0
while True:
# acquire the lock to protect the critical section
print("waiting for thing")
await event.wait()
print("task1: got event thing set")
count = count + 1
await asyncio.sleep(1)
print("task 1: reset event thing set")
event.clear()
if __name__ == "__main__":
# from here code execution starts
print('code execution started')
BOARD.setup()
print("\nasync superloop code running")
asyncio.run(superloop())
print("code finished")
I suspect that this might not solve your real problem. Does the callback to on_rx_done
occur in the main thread? If not, you need to (1) save the main event loop in the LoRaRcvCont constructor, and (2) set the Event flag using the cross-threaded asyncio method self.loop.call_soon_threadsafe(self.event.set)
. Let me know if you need me to describe this further.
Answered By - Paul Cornelius
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.