示例#1
0
 def __init__(self, device, baudrate=None, parity=None, rtscts=False,
              debug=False):
     self._terminal = Terminal()
     self._device = device
     self._baudrate = baudrate or self.DEFAULT_BAUDRATE
     self._port = self._open_port(self._device, self._baudrate, parity,
                                  rtscts, debug)
     self._resume = False
     self._silent = False
     self._rxq = deque()
     self._rxe = Event()
     self._debug = debug
     register(self._cleanup)
示例#2
0
class MiniTerm:
    """A mini serial terminal to demonstrate pyserial extensions"""

    DEFAULT_BAUDRATE = 115200

    def __init__(self,
                 device,
                 baudrate=None,
                 parity=None,
                 rtscts=False,
                 debug=False):
        self._terminal = Terminal()
        self._device = device
        self._baudrate = baudrate or self.DEFAULT_BAUDRATE
        self._port = self._open_port(self._device, self._baudrate, parity,
                                     rtscts, debug)
        self._resume = False
        self._silent = False
        self._rxq = deque()
        self._rxe = Event()
        self._debug = debug
        register(self._cleanup)

    def run(self,
            fullmode=False,
            loopback=False,
            silent=False,
            localecho=False,
            autocr=False):
        """Switch to a pure serial terminal application"""

        self._terminal.init(fullmode)
        print('Entering minicom mode @ %d bps' % self._port.baudrate)
        stdout.flush()
        self._resume = True
        # start the reader (target to host direction) within a dedicated thread
        args = [loopback]
        if self._device.startswith('ftdi://'):
            # with pyftdi/pyusb/libusb stack, there is no kernel buffering
            # which means that a UART source with data burst may overflow the
            # FTDI HW buffer while the SW stack is dealing with formatting
            # and console output. Use an intermediate thread to pop out data
            # out from the HW as soon as it is made available, and use a deque
            # to serve the actual reader thread
            args.append(self._get_from_source)
            sourcer = Thread(target=self._sourcer)
            sourcer.setDaemon(1)
            sourcer.start()
        else:
            # regular kernel buffered device
            args.append(self._get_from_port)
        reader = Thread(target=self._reader, args=tuple(args))
        reader.setDaemon(1)
        reader.start()
        # start the writer (host to target direction)
        self._writer(fullmode, silent, localecho, autocr)

    def _sourcer(self):
        try:
            while self._resume:
                data = self._port.read(4096)
                if not data:
                    continue
                self._rxq.append(data)
                self._rxe.set()
        except Exception as ex:
            self._resume = False
            print(str(ex), file=stderr)
            interrupt_main()

    def _get_from_source(self):
        while not self._rxq and self._resume:
            if self._rxe.wait(0.1):
                self._rxe.clear()
                break
        if not self._rxq:
            return bytearray()
        return self._rxq.popleft()

    def _get_from_port(self):
        try:
            return self._port.read(4096)
        except OSError as ex:
            self._resume = False
            print(str(ex), file=stderr)
            interrupt_main()
        except Exception as ex:
            print(str(ex), file=stderr)
            return bytearray()

    def _reader(self, loopback, getfunc):
        """Loop forever, processing received serial data in terminal mode"""
        try:
            # Try to read as many bytes as possible at once, and use a short
            # timeout to avoid blocking for more data
            self._port.timeout = 0.050
            while self._resume:
                if self._silent:
                    sleep(0.25)
                    continue
                data = getfunc()
                if data:
                    stdout.write(data.decode('utf8', errors='replace'))
                    stdout.flush()
                if loopback:
                    self._port.write(data)
        except KeyboardInterrupt:
            return
        except Exception as exc:
            print("Exception: %s" % exc)
            if self._debug:
                print(format_exc(chain=False), file=stderr)
            interrupt_main()

    def _writer(self, fullmode, silent, localecho, crlf=0):
        """Loop and copy console->serial until EOF character is found"""
        while self._resume:
            try:
                char = self._terminal.getkey()
                if fullmode and ord(char) == 0x2:  # Ctrl+B
                    self._cleanup(True)
                    return
                if self._terminal.IS_MSWIN:
                    if ord(char) in (0, 224):
                        char = self._terminal.getkey()
                        self._port.write(self._terminal.getch_to_escape(char))
                        continue
                    if ord(char) == 0x3:  # Ctrl+C
                        raise KeyboardInterrupt('Ctrl-C break')
                if silent:
                    if ord(char) == 0x6:  # Ctrl+F
                        self._silent = True
                        print('Silent\n')
                        continue
                    if ord(char) == 0x7:  # Ctrl+G
                        self._silent = False
                        print('Reg\n')
                        continue
                if localecho:
                    stdout.write(char.decode('utf8', errors='replace'))
                    stdout.flush()
                if crlf:
                    if char == b'\n':
                        self._port.write(b'\r')
                        if crlf > 1:
                            continue
                self._port.write(char)
            except KeyError:
                continue
            except KeyboardInterrupt:
                if fullmode:
                    if self._terminal.IS_MSWIN:
                        self._port.write(b'\x03')
                    continue
                self._cleanup(True)

    def _cleanup(self, *args):
        """Cleanup resource before exiting"""
        if args and args[0]:
            print('%sAborting...' % linesep)
        try:
            self._resume = False
            if self._port:
                # wait till the other thread completes
                sleep(0.5)
                try:
                    rem = self._port.inWaiting()
                except IOError:
                    # maybe a bug in underlying wrapper...
                    rem = 0
                # consumes all the received bytes
                for _ in range(rem):
                    self._port.read()
                self._port.close()
                self._port = None
                print('Bye.')
        except Exception as ex:
            print(str(ex), file=stderr)
        finally:
            if self._terminal:
                self._terminal.reset()
                self._terminal = None

    @staticmethod
    def _open_port(device, baudrate, parity, rtscts, debug=False):
        """Open the serial communication port"""
        try:
            from serial.serialutil import SerialException
            from serial import PARITY_NONE
        except ImportError as exc:
            raise ImportError("Python serial module not installed") from exc
        try:
            from serial import serial_for_url, VERSION as serialver
            version = tuple([int(x) for x in serialver.split('.')])
            if version < (3, 0):
                raise ValueError
        except (ValueError, IndexError, ImportError) as exc:
            raise ImportError("pyserial 3.0+ is required") from exc
        # the following import enables serial protocol extensions
        if device.startswith('ftdi:'):
            try:
                from pyftdi import serialext
                serialext.touch()
            except ImportError as exc:
                raise ImportError("PyFTDI module not installed") from exc
        try:
            port = serial_for_url(device,
                                  baudrate=baudrate,
                                  parity=parity or PARITY_NONE,
                                  rtscts=rtscts,
                                  timeout=0)
            if not port.is_open:
                port.open()
            if not port.is_open:
                raise IOError('Cannot open port "%s"' % device)
            if debug:
                backend = port.BACKEND if hasattr(port, 'BACKEND') else '?'
                print("Using serial backend '%s'" % backend)
            return port
        except SerialException as exc:
            raise IOError(str(exc)) from exc