]> granicus.if.org Git - esp-idf/commitdiff
idf_monitor: use cancellation and Console.getkey from pyserial 3.3.0+
authorIvan Grokhotkov <ivan@espressif.com>
Tue, 15 Aug 2017 08:46:22 +0000 (16:46 +0800)
committerIvan Grokhotkov <ivan@espressif.com>
Mon, 21 Aug 2017 07:58:28 +0000 (15:58 +0800)
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.

tools/idf_monitor.py

index b813d83e18a2fbc3a94d6537ed4aae40be095639..7ddee831f2cb08b42cbfc85268b2008ee4745088 100755 (executable)
@@ -43,6 +43,8 @@ import serial
 import serial.tools.miniterm as miniterm
 import threading
 import ctypes
+import types
+from distutils.version import StrictVersion
 
 key_description = miniterm.key_description
 
@@ -157,13 +159,17 @@ class ConsoleReader(StoppableThread):
             self.console.cleanup()
 
     def _cancel(self):
-        if hasattr(self.console, "cancel"):
-            self.console.cancel()
-        elif os.name == 'posix':
-            # this is the way cancel() is implemented in pyserial 3.1 or newer,
-            # older pyserial doesn't have this method, hence this hack.
+        if os.name == 'posix':
+            # this is the way cancel() is implemented in pyserial 3.3 or newer,
+            # older pyserial (3.1+) has cancellation implemented via 'select',
+            # which does not work when console sends an escape sequence response
+            # 
+            # even older pyserial (<3.1) does not have this method
             #
             # 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
             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.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.console_reader = ConsoleReader(self.console, self.event_queue)
         self.serial_reader = SerialReader(self.serial, self.event_queue)