Following advice of Dmitry Grigoryev, instead of just opening 'ttyACMx' use
_fptr_thpusb = serial.Serial(self._fname_thpusb, timeout);
Also do not read from the file descriptor _fptr_thpusb
unless it has data as determined by
ready,_,_ = select.select([self._fd], [], [], timeout);
See code marked by "#solution"; this does not block because read always has data;
class AsyncReader(threading.Thread):
def __init__(self, fd, queue1, label):
threading.Thread.__init__(self)
self._fd = fd
self._queue = queue1
self._running = False
self.setDaemon(True)
self.setName(label)
def run(self):
self._running = True
# for line in iter(self._fd.readline, ''):
while self._running:
# added --------
timeout = 10
# this is the solution
ready,_,_ = select.select([self._fd], [], [], timeout)
if not ready:
continue
line = self._fd.readline()
# added --------
line = line.rstrip()
# all our files are binary
line = line.decode()
if not line:
continue
self._queue.put(line)
def stop_running(self):
self._running = False
class ProcManager(object):
def startup(self, fname_thpusb, cmd, path=None, ld_library_path=None):
self._process = subprocess.Popen(cmd.split(' '),
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
# self._fptr_thpusb = open(self._fname_thpusb, "rb")
timeout = 5
# this is the solution
self._fptr_thpusb = serial.Serial(self._fname_thpusb, timeout)
self.thpusb_reader = AsyncReader(
self._fptr_thpusb, self.merge_out_queue, "thp-thread")
self.thpusb_reader.start()
self.stdout_reader = AsyncReader(
self._process.stdout, self.merge_out_queue, "stdout-thread")
self.stdout_reader.start()
self.stderr_reader = AsyncReader(
self._process.stderr, self.stderr_queue, 'stderr-thread')
self.stderr_reader.start()
def shutdown(self):
self._process.kill()
# originally close() was before the shutdown of the readers
self.thpusb_reader.stop_running()
self.thpusb_reader.join(0.5)
self.stdout_reader.stop_running()
self.stdout_reader.join(0.5)
self.stderr_reader.stop_running()
self.stderr_reader.join(0.5)
# originally this was before the shutdown of the readers
self._fptr_thpusb.close()
self._process.stdout.close()
self._process.stderr.close()
if self._process.poll() is None:
logerr('process did not respond to kill, shutting down anyway')
self._process = None
if self.thpusb_reader.is_alive():
loginf('timed out waiting for %s' % self.thpusb_reader.getName())
self.thpusb_reader = None
if self.stdout_reader.is_alive():
loginf('timed out waiting for %s' % self.stdout_reader.getName())
self.stdout_reader = None
if self.stderr_reader.is_alive():
loginf('timed out waiting for %s' % self.stderr_reader.getName())
self.stderr_reader = None
loginf('shutdown complete')
def get_stderr(self):
lines = []
while not self.stderr_queue.empty():
# lines.append(self.stderr_queue.get().decode())
lines.append(self.stderr_queue.get())
return lines
def get_stdout(self):
lines = []
while self.running():
try:
# Fetch the output line. For it to be searched, Python 3
# requires that it be decoded to unicode. Decoding does no
# harm under Python 2:
# line = self.merge_out_queue.get(True, 3).decode()
line = self.merge_out_queue.get(True, 3)
m = ProcManager.TS.search(line)
if m and lines:
yield lines
lines = []
lines.append(line)
except queue.Empty:
yield lines
lines = []
yield lines