gotthardp / python-mercuryapi

Python wrapper for the ThingMagic Mercury API
MIT License
122 stars 63 forks source link

start_reading callback isn't called when run inside new thread? #109

Closed clickworkorange closed 4 years ago

clickworkorange commented 4 years ago

My multiprocessing fu is weak, and I cannot figure out why the start_reading callback never gets called here:

#!/usr/bin/env python3

from mercury import Reader
from multiprocessing import Process
from time import sleep

class MyProcess(Process):
  def __init__(self):
    Process.__init__(self)
    self.reader = Reader("tmr:///dev/ttyAMA0", baudrate=115200)
    self.reader.set_region("EU3")
    self.reader.set_read_plan([1], "GEN2", read_power=1900)

  def run(self):
    print("Run")
    self.reader.start_reading(self.found)

  def stop(self):
    print("Stop")
    self.reader.stop_reading()

  def found(self, tags):
    print(tags)

if __name__ == '__main__':
  try:
    process = MyProcess()
    process.daemon = True
    process.start()
    while True:
      sleep(0.1)

  except KeyboardInterrupt:
    process.stop()

Is it because self.found() doesn't point where I think it does? I don't get any reference errors though, or any other error - but the found method never gets called. Any ideas?

Edit: It makes no difference if I make the callback a stand-alone method outside the class - but if it's missing altogether I do get a reference error. Mysterious. The mercuryapi wrapper receives but then loses the reference to the callback?

Edit 2: If I move the start_reading call to my process class' __init__ method instead then it works! But I don't like that so much; run() seems like the appropriate place?

Edit 3: Since the synchronous read() function doesn't use a callback it works inside my class' run() method, and though it adds some bulk to the class I can rewrite it to use this function in a loop instead. It's in a separate process anyway, so won't hold up my main thread. Still curious to hear what happened with the missing start_reading() callback though - I might at least learn something new!

HanYangZhao commented 4 years ago

Try this:

  def run(self):
    print("Run")
    self.reader.start_reading(self.found)
    while(True):
        sleep(1)

If you're not doing while(True), then the run function finishes executing and the process terminates, leaving you with just the main process in sleep(0.1)

clickworkorange commented 4 years ago

Thanks! That's interesting. I knew the function would terminate, but not that this would take with it the reader and/or the callback. I thought of run() as being tasked with starting the reader and then getting out of the way, since there is no more work for it to do. Seems obvious now; the thread completes and everything gets garbage collected. Multiprocessing fu++