Can't set python event flag

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP



Can't set python event flag



I have been struggling the whole day with this, I'm pretty certain it's a thread problem, but I just can't figure out what's wrong. Basically, My main.py creates an instance of "Neopixel". That neopixel starts a thread that runs a led ring. Some ring animations are running for as long as a condition is met, unlike simple led blinking that don't cause problems.



To handle those conditions, well, I use an event flag. It's set to true when it's a long animation, and when another led state kicks in, it's unset so it should theoretically stop the animation as it is in a while event.is_set() loop. But... It is never cleared, even though I do clear it.



Some code:


def __init__(self):
self._logger = logging.getLogger('ProjectAlice')
self._logger.info('Initializing Project Alice')

self._leds = NeoPixels()
self._leds.onStart()
....
self._logger.info('Project Alice started')
self._leds.onConnecting()
self.greetAlice()
....
elif message.topic == self._SUB_GREETING_BACK:
self._state = State.REGISTERED
self._logger.info('- Alice greeted back, module registered')
self._leds.onConnected()



I've remove irrelevant parts but left the led part. As we see, it starts, creates an instance of Neopixels, calls onStart() for the leds, then calls onConnect(), then tries to reach the main server through mqtt. When the main server replies, I call onConnected(). But the leds always stay in "onConnect()" and never go to the "onConnected()" state. There's a print('Done') in there, that never shows, but when I ctrl-c the program, which also then executes the "onConnected" animation



Somehow, the



self._animation.clear()



in



def onConnected(self):



does not register, the animation loop never ends but this



self._logger.info('- Alice greeted back, module registered')



prints, meaning onConnected() is called



class NeoPixels(object):


def __init__(self):
self._running = True

self._ring = Adafruit_NeoPixel(num=config.settings['ringLedCount'], pin=config.settings['ringLedPin'], brightness=125, strip_type=ws.SK6812_STRIP_RGBW)
self._ring.begin()

self._queue = Queue.Queue()
self._animation = threading.Event()

threading.Thread(target=self._run).start()

def onStart(self):
self._running = True
self._animation.clear()

self._queue.put(self._start)

def onConnecting(self):
self._animation.clear()
self._queue.put(self._connecting)


def onConnected(self):
self._animation.clear()
self._queue.put(self._connected)

def _run(self):
while self._running:
func = self._queue.get()
func()

def _start(self):
for i in range(self._ring.numPixels()):
self._setPixelColorRGB(i, 255, 0, 0)
self._ring.show()
time.sleep(10 / 1000.0)

for i in range(self._ring.numPixels()):
self._setPixelColorRGB(i, 0, 0, 0)
self._ring.show()
time.sleep(10 / 1000.0)

time.sleep(0.25)

for i in range(self._ring.numPixels()):
self._setPixelColorRGB(i, 255, 0, 0)
self._ring.show()
time.sleep(1 / 1000.0)

def _connecting(self):
self._animation.set()
while self._animation.is_set():
for i in range(self._ring.numPixels()):
self._setPixelColorRGB(i, 255, 0, 0)
self._ring.show()
time.sleep(20 / 1000.0)
threading.Timer(interval=20 / 1000.0, function=self._setPixelColorRGB, args=[i, 0, 0, 0]).start()
print('done')

def _connected(self):
for i in range(self._ring.numPixels()):
self._setPixelColorRGB(i, 0, 128, 0)
self._ring.show()

time.sleep(1)
self._clear()




1 Answer
1



This very much looks like a timing issue with your queue. If the call to onConnected happens too quickly after onConnecting has been called, onConnected clears the event before onConnecting -> _connecting sets it.


onConnected


onConnecting


onConnected


onConnecting


_connecting



This is how I stripped down your class to run and test it (I removed all the LED ring stuff and added print statements to onConnected and _run)


onConnected


_run


import time, threading, queue

class NeoPixels:
def __init__(self):
self._running = True
self._queue = queue.Queue()
self._animation = threading.Event()
threading.Thread(target=self._run)

def onStart(self):
self._running = True
self._animation.clear()
self._queue.put(self._start)

def onConnecting(self):
self._animation.clear()
self._queue.put(self._connecting)

def onConnected(self):
print("called onConnected")
self._animation.clear()
self._queue.put(self._connected)


def _run(self):
while self._running:
func = self._queue.get()
print("now running ".format(func.__qualname__))
func()

def _start(self):
time.sleep(.5)

def _connecting(self):
self._animation.set()
while self._animation.is_set():
for i in range(5):
time.sleep(20 / 1000.0)
print('done')

def _connected(self):
for i in range(1):
print("connected")
time.sleep(1)



And here's how I run it:


leds = NeoPixels()
leds.onStart()
leds.onConnecting()
leds.onConnected()



Here's the output:


called onConnected
now running NeoPixels._start
now running NeoPixels._connecting



As you can see, onConnected is called and clears the event before _run even fetched _start from the queue and processed it. So when onConnecting is executed, _connecting sets the event and since nothing clears it after that, it keeps running indefinitely.


onConnected


_run


_start


onConnecting


_connecting



So, changing onConnected to wait for the queue to clear, like so:


onConnected


def onConnected(self):
while not self._queue.empty():
time.sleep(.1)
print("called onConnected")
self._animation.clear()
self._queue.put(self._connected)



will lead to the following output:


now running NeoPixels._start
now running NeoPixels._connecting
called onConnected
done
now running NeoPixels._connected
connected



Out of curiosity, how do you actually terminate/join the thread? You create it anonymously, so I don't see how you would get a handle on it to close it once done.





Thank you for your answer! I will try that as soon as I get back home, could it be that easy? But wanted to answer the question of yours: I made a light version to test separately and striped down the whole thing to post here. On the project, this is run through a ThreadManager that handles threads terminating when needed.
– Psychokiller1888
Aug 8 at 10:43





Ah, now that makes sense. I constantly had to kill the process manually following the implementation you presented here and was wondering about the witchcraft you must have come up with to grab and terminate that thread :D
– shmee
Aug 8 at 13:13





Dude! Really? I did not see that? I was printing lines and lines to see what was hanging and I did not notice anything! Many many thanks for pointing it out!
– Psychokiller1888
Aug 8 at 22:08






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Firebase Auth - with Email and Password - Check user already registered

Dynamically update html content plain JS

How to determine optimal route across keyboard