idf_monitor: use cancellation and Console.getkey from pyserial 3.3.0+

Between 3.1.0 and 3.3.0, pyserial had thread cancellation implemented
using a select, which blocked on the stdin and an auxiliary pipe. When
thread had to be cancelled, a byte would be sent into the pipe,
unblocking stdin.

Unfortunately, this method suffers from a problem with using select on
a StreamReader (which represents the decoder wrapped around stdin).
In some cases, when the TTY sends an escape sequence in response to
an escape sequence received from serial, this escape sequence will not
be read from stdin until some key is pressed.

In https://github.com/pyserial/pyserial/commit/cab3dab, this method
was replaced with an TIOCSTI ioctl. This change makes sure we use the
new cancellation method even if the script is running with older
pyserial.
This commit is contained in:
Ivan Grokhotkov 2017-08-15 16:46:22 +08:00
parent 489c523870
commit d7d6f1e376

View file

@ -43,6 +43,8 @@ import serial
import serial.tools.miniterm as miniterm import serial.tools.miniterm as miniterm
import threading import threading
import ctypes import ctypes
import types
from distutils.version import StrictVersion
key_description = miniterm.key_description key_description = miniterm.key_description
@ -157,13 +159,17 @@ class ConsoleReader(StoppableThread):
self.console.cleanup() self.console.cleanup()
def _cancel(self): def _cancel(self):
if hasattr(self.console, "cancel"): if os.name == 'posix':
self.console.cancel() # this is the way cancel() is implemented in pyserial 3.3 or newer,
elif os.name == 'posix': # older pyserial (3.1+) has cancellation implemented via 'select',
# this is the way cancel() is implemented in pyserial 3.1 or newer, # which does not work when console sends an escape sequence response
# older pyserial doesn't have this method, hence this hack. #
# even older pyserial (<3.1) does not have this method
# #
# on Windows there is a different (also hacky) fix, applied above. # on Windows there is a different (also hacky) fix, applied above.
#
# note that TIOCSTI is not implemented in WSL / bash-on-Windows.
# TODO: introduce some workaround to make it work there.
import fcntl, termios import fcntl, termios
fcntl.ioctl(self.console.fd, termios.TIOCSTI, b'\0') fcntl.ioctl(self.console.fd, termios.TIOCSTI, b'\0')
@ -221,6 +227,16 @@ class Monitor(object):
self.console.output = ANSIColorConverter(self.console.output) self.console.output = ANSIColorConverter(self.console.output)
self.console.byte_output = ANSIColorConverter(self.console.byte_output) self.console.byte_output = ANSIColorConverter(self.console.byte_output)
if StrictVersion(serial.VERSION) < StrictVersion('3.3.0'):
# Use Console.getkey implementation from 3.3.0 (to be in sync with the ConsoleReader._cancel patch above)
def getkey_patched(self):
c = self.enc_stdin.read(1)
if c == unichr(0x7f):
c = unichr(8) # map the BS key (which yields DEL) to backspace
return c
self.console.getkey = types.MethodType(getkey_patched, self.console)
self.serial = serial_instance self.serial = serial_instance
self.console_reader = ConsoleReader(self.console, self.event_queue) self.console_reader = ConsoleReader(self.console, self.event_queue)
self.serial_reader = SerialReader(self.serial, self.event_queue) self.serial_reader = SerialReader(self.serial, self.event_queue)