openrvdas.logger.readers.serial_reader

No module-level documentation available.
  1#!/usr/bin/env python3
  2
  3import logging
  4
  5# Don't freak out if pyserial isn't installed - unless they actually
  6# try to instantiate a SerialReader
  7try:
  8    import serial
  9    SERIAL_MODULE_FOUND = True
 10except ModuleNotFoundError:
 11    SERIAL_MODULE_FOUND = False
 12
 13from logger.readers.reader import Reader  # noqa: E402
 14
 15
 16################################################################################
 17class SerialReader(Reader):
 18    """
 19    Read records from a serial port.
 20    """
 21
 22    def __init__(self,  port, baudrate=9600, bytesize=8, parity='N',
 23                 stopbits=1, timeout=None, xonxoff=False, rtscts=False,
 24                 write_timeout=None, dsrdtr=False, inter_byte_timeout=None,
 25                 exclusive=None, max_bytes=None, eol=None, allow_empty=False,
 26                 encoding='utf-8', encoding_errors='ignore', **kwargs):
 27        """If max_bytes is specified on initialization, read up to that many
 28        bytes when read() is called. If eol is not specified, read() will
 29        read up to the first newline it receives. In both cases, if
 30        timeout is specified, it will return after timeout with as many
 31        bytes as it has succeeded in reading.
 32
 33        By default, the SerialReader will read until it encounters a newline character.
 34        This behavior may be overwritten by specifying
 35
 36        max_bytes - if specified, and write_timeout is None, read this many bytes per record.
 37                If write_timeout is not None, it may return fewer bytes.
 38
 39        eol - if specified, read up until encountering the specified eol
 40
 41        By default, the SerialReader will assume that records are encoded in UTF-8, and will
 42        ignore non unicode characters it encounters. These defaults may be changed by specifying
 43
 44        allow_empty - If True, preserve and return empty records
 45
 46        encoding - 'utf-8' by default. If empty or None, do not attempt any decoding
 47                and return raw bytes. Other possible encodings are listed in online
 48                documentation here:
 49                https://docs.python.org/3/library/codecs.html#standard-encodings
 50
 51        encoding_errors - 'ignore' by default. Other error strategies are 'strict',
 52                'replace', and 'backslashreplace', described here:
 53                https://docs.python.org/3/howto/unicode.html#encodings
 54
 55        command line example:
 56        ```
 57          # Read serial port ttyr05 expecting a LF as end of record
 58          logger/listener/listen.py  --serial port=/dev/ttyr05,eol='\r'
 59        ```
 60        config example:
 61        ```
 62          class: SerialReader
 63          kwargs:
 64            baudrate: 4800
 65            port: /dev/ttyr05
 66            eol: \r
 67        ```
 68        """
 69        super().__init__(encoding=encoding, encoding_errors=encoding_errors, **kwargs)
 70
 71        if not SERIAL_MODULE_FOUND:
 72            raise RuntimeError('Serial port functionality not available. Please '
 73                               'install Python module pyserial.')
 74        try:
 75            self.serial = serial.Serial(port=port, baudrate=baudrate,
 76                                        bytesize=bytesize, parity=parity,
 77                                        stopbits=stopbits, timeout=timeout,
 78                                        xonxoff=xonxoff, rtscts=rtscts,
 79                                        write_timeout=write_timeout, dsrdtr=dsrdtr,
 80                                        inter_byte_timeout=inter_byte_timeout,
 81                                        exclusive=exclusive)
 82        except (serial.SerialException, serial.serialutil.SerialException) as e:
 83            logging.fatal('Failed to open serial port %s: %s', port, e)
 84            raise
 85
 86        self.max_bytes = max_bytes
 87        self.encoding = encoding
 88        self.allow_empty = allow_empty
 89        self.encoding_errors = encoding_errors
 90
 91        # 'eol' comes in as a (probably escaped) string. We need to
 92        # unescape it, which means converting to bytes and back.
 93        #
 94        # NOTE: This block is different from SerialWriter because we use
 95        #       readline() in here, which already looks for trailing '\n' and
 96        #       handles encoding itself.
 97        #
 98        if eol is not None and self.encoding:
 99            eol = self._encode_str(eol, unescape=True)
100        self.eol = eol
101
102    ############################
103    def read(self):
104        try:
105            if self.eol:
106                record = self.serial.read_until(expected=self.eol, size=self.max_bytes)
107                # read_until()'s record includes a trailing 'eol', strip it off
108                #
109                # NOTE: But don't use rstrip which just looks explicitly for
110                #       whitespace
111                #
112                record = record.rsplit(self.eol)[0]
113            elif self.max_bytes:
114                # no stripping on this one, just use exactly what we got
115                record = self.serial.read(size=self.max_bytes)
116            else:
117                # readline()'s record includes the trailing '\n', strip it off
118                record = self.serial.readline().rstrip()
119
120            return self._decode_bytes(record, self.allow_empty)
121
122        except KeyboardInterrupt as e:
123            raise e
124        except serial.serialutil.SerialException as e:
125            logging.error(str(e))
126            return None
class SerialReader(logger.readers.reader.Reader):
 18class SerialReader(Reader):
 19    """
 20    Read records from a serial port.
 21    """
 22
 23    def __init__(self,  port, baudrate=9600, bytesize=8, parity='N',
 24                 stopbits=1, timeout=None, xonxoff=False, rtscts=False,
 25                 write_timeout=None, dsrdtr=False, inter_byte_timeout=None,
 26                 exclusive=None, max_bytes=None, eol=None, allow_empty=False,
 27                 encoding='utf-8', encoding_errors='ignore', **kwargs):
 28        """If max_bytes is specified on initialization, read up to that many
 29        bytes when read() is called. If eol is not specified, read() will
 30        read up to the first newline it receives. In both cases, if
 31        timeout is specified, it will return after timeout with as many
 32        bytes as it has succeeded in reading.
 33
 34        By default, the SerialReader will read until it encounters a newline character.
 35        This behavior may be overwritten by specifying
 36
 37        max_bytes - if specified, and write_timeout is None, read this many bytes per record.
 38                If write_timeout is not None, it may return fewer bytes.
 39
 40        eol - if specified, read up until encountering the specified eol
 41
 42        By default, the SerialReader will assume that records are encoded in UTF-8, and will
 43        ignore non unicode characters it encounters. These defaults may be changed by specifying
 44
 45        allow_empty - If True, preserve and return empty records
 46
 47        encoding - 'utf-8' by default. If empty or None, do not attempt any decoding
 48                and return raw bytes. Other possible encodings are listed in online
 49                documentation here:
 50                https://docs.python.org/3/library/codecs.html#standard-encodings
 51
 52        encoding_errors - 'ignore' by default. Other error strategies are 'strict',
 53                'replace', and 'backslashreplace', described here:
 54                https://docs.python.org/3/howto/unicode.html#encodings
 55
 56        command line example:
 57        ```
 58          # Read serial port ttyr05 expecting a LF as end of record
 59          logger/listener/listen.py  --serial port=/dev/ttyr05,eol='\r'
 60        ```
 61        config example:
 62        ```
 63          class: SerialReader
 64          kwargs:
 65            baudrate: 4800
 66            port: /dev/ttyr05
 67            eol: \r
 68        ```
 69        """
 70        super().__init__(encoding=encoding, encoding_errors=encoding_errors, **kwargs)
 71
 72        if not SERIAL_MODULE_FOUND:
 73            raise RuntimeError('Serial port functionality not available. Please '
 74                               'install Python module pyserial.')
 75        try:
 76            self.serial = serial.Serial(port=port, baudrate=baudrate,
 77                                        bytesize=bytesize, parity=parity,
 78                                        stopbits=stopbits, timeout=timeout,
 79                                        xonxoff=xonxoff, rtscts=rtscts,
 80                                        write_timeout=write_timeout, dsrdtr=dsrdtr,
 81                                        inter_byte_timeout=inter_byte_timeout,
 82                                        exclusive=exclusive)
 83        except (serial.SerialException, serial.serialutil.SerialException) as e:
 84            logging.fatal('Failed to open serial port %s: %s', port, e)
 85            raise
 86
 87        self.max_bytes = max_bytes
 88        self.encoding = encoding
 89        self.allow_empty = allow_empty
 90        self.encoding_errors = encoding_errors
 91
 92        # 'eol' comes in as a (probably escaped) string. We need to
 93        # unescape it, which means converting to bytes and back.
 94        #
 95        # NOTE: This block is different from SerialWriter because we use
 96        #       readline() in here, which already looks for trailing '\n' and
 97        #       handles encoding itself.
 98        #
 99        if eol is not None and self.encoding:
100            eol = self._encode_str(eol, unescape=True)
101        self.eol = eol
102
103    ############################
104    def read(self):
105        try:
106            if self.eol:
107                record = self.serial.read_until(expected=self.eol, size=self.max_bytes)
108                # read_until()'s record includes a trailing 'eol', strip it off
109                #
110                # NOTE: But don't use rstrip which just looks explicitly for
111                #       whitespace
112                #
113                record = record.rsplit(self.eol)[0]
114            elif self.max_bytes:
115                # no stripping on this one, just use exactly what we got
116                record = self.serial.read(size=self.max_bytes)
117            else:
118                # readline()'s record includes the trailing '\n', strip it off
119                record = self.serial.readline().rstrip()
120
121            return self._decode_bytes(record, self.allow_empty)
122
123        except KeyboardInterrupt as e:
124            raise e
125        except serial.serialutil.SerialException as e:
126            logging.error(str(e))
127            return None

Read records from a serial port.

SerialReader( port, baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=None, xonxoff=False, rtscts=False, write_timeout=None, dsrdtr=False, inter_byte_timeout=None, exclusive=None, max_bytes=None, eol=None, allow_empty=False, encoding='utf-8', encoding_errors='ignore', **kwargs)
 23    def __init__(self,  port, baudrate=9600, bytesize=8, parity='N',
 24                 stopbits=1, timeout=None, xonxoff=False, rtscts=False,
 25                 write_timeout=None, dsrdtr=False, inter_byte_timeout=None,
 26                 exclusive=None, max_bytes=None, eol=None, allow_empty=False,
 27                 encoding='utf-8', encoding_errors='ignore', **kwargs):
 28        """If max_bytes is specified on initialization, read up to that many
 29        bytes when read() is called. If eol is not specified, read() will
 30        read up to the first newline it receives. In both cases, if
 31        timeout is specified, it will return after timeout with as many
 32        bytes as it has succeeded in reading.
 33
 34        By default, the SerialReader will read until it encounters a newline character.
 35        This behavior may be overwritten by specifying
 36
 37        max_bytes - if specified, and write_timeout is None, read this many bytes per record.
 38                If write_timeout is not None, it may return fewer bytes.
 39
 40        eol - if specified, read up until encountering the specified eol
 41
 42        By default, the SerialReader will assume that records are encoded in UTF-8, and will
 43        ignore non unicode characters it encounters. These defaults may be changed by specifying
 44
 45        allow_empty - If True, preserve and return empty records
 46
 47        encoding - 'utf-8' by default. If empty or None, do not attempt any decoding
 48                and return raw bytes. Other possible encodings are listed in online
 49                documentation here:
 50                https://docs.python.org/3/library/codecs.html#standard-encodings
 51
 52        encoding_errors - 'ignore' by default. Other error strategies are 'strict',
 53                'replace', and 'backslashreplace', described here:
 54                https://docs.python.org/3/howto/unicode.html#encodings
 55
 56        command line example:
 57        ```
 58          # Read serial port ttyr05 expecting a LF as end of record
 59          logger/listener/listen.py  --serial port=/dev/ttyr05,eol='\r'
 60        ```
 61        config example:
 62        ```
 63          class: SerialReader
 64          kwargs:
 65            baudrate: 4800
 66            port: /dev/ttyr05
 67            eol: \r
 68        ```
 69        """
 70        super().__init__(encoding=encoding, encoding_errors=encoding_errors, **kwargs)
 71
 72        if not SERIAL_MODULE_FOUND:
 73            raise RuntimeError('Serial port functionality not available. Please '
 74                               'install Python module pyserial.')
 75        try:
 76            self.serial = serial.Serial(port=port, baudrate=baudrate,
 77                                        bytesize=bytesize, parity=parity,
 78                                        stopbits=stopbits, timeout=timeout,
 79                                        xonxoff=xonxoff, rtscts=rtscts,
 80                                        write_timeout=write_timeout, dsrdtr=dsrdtr,
 81                                        inter_byte_timeout=inter_byte_timeout,
 82                                        exclusive=exclusive)
 83        except (serial.SerialException, serial.serialutil.SerialException) as e:
 84            logging.fatal('Failed to open serial port %s: %s', port, e)
 85            raise
 86
 87        self.max_bytes = max_bytes
 88        self.encoding = encoding
 89        self.allow_empty = allow_empty
 90        self.encoding_errors = encoding_errors
 91
 92        # 'eol' comes in as a (probably escaped) string. We need to
 93        # unescape it, which means converting to bytes and back.
 94        #
 95        # NOTE: This block is different from SerialWriter because we use
 96        #       readline() in here, which already looks for trailing '\n' and
 97        #       handles encoding itself.
 98        #
 99        if eol is not None and self.encoding:
100            eol = self._encode_str(eol, unescape=True)
101        self.eol = eol

If max_bytes is specified on initialization, read up to that many bytes when read() is called. If eol is not specified, read() will read up to the first newline it receives. In both cases, if timeout is specified, it will return after timeout with as many bytes as it has succeeded in reading.

By default, the SerialReader will read until it encounters a newline character. This behavior may be overwritten by specifying

max_bytes - if specified, and write_timeout is None, read this many bytes per record. If write_timeout is not None, it may return fewer bytes.

eol - if specified, read up until encountering the specified eol

By default, the SerialReader will assume that records are encoded in UTF-8, and will ignore non unicode characters it encounters. These defaults may be changed by specifying

allow_empty - If True, preserve and return empty records

encoding - 'utf-8' by default. If empty or None, do not attempt any decoding and return raw bytes. Other possible encodings are listed in online documentation here: https://docs.python.org/3/library/codecs.html#standard-encodings

encoding_errors - 'ignore' by default. Other error strategies are 'strict', 'replace', and 'backslashreplace', described here: https://docs.python.org/3/howto/unicode.html#encodings

command line example:

  # Read serial port ttyr05 expecting a LF as end of record
  logger/listener/listen.py  --serial port=/dev/ttyr05,eol='
'

config example:

  class: SerialReader
  kwargs:
    baudrate: 4800
    port: /dev/ttyr05
    eol: 
max_bytes
encoding
allow_empty
encoding_errors
eol
def read(self):
104    def read(self):
105        try:
106            if self.eol:
107                record = self.serial.read_until(expected=self.eol, size=self.max_bytes)
108                # read_until()'s record includes a trailing 'eol', strip it off
109                #
110                # NOTE: But don't use rstrip which just looks explicitly for
111                #       whitespace
112                #
113                record = record.rsplit(self.eol)[0]
114            elif self.max_bytes:
115                # no stripping on this one, just use exactly what we got
116                record = self.serial.read(size=self.max_bytes)
117            else:
118                # readline()'s record includes the trailing '\n', strip it off
119                record = self.serial.readline().rstrip()
120
121            return self._decode_bytes(record, self.allow_empty)
122
123        except KeyboardInterrupt as e:
124            raise e
125        except serial.serialutil.SerialException as e:
126            logging.error(str(e))
127            return None

read() should return None when there are no more records.