andreasoie

Serial Reader

Apr 5th, 2021
471
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. class ReaderThread(threading.Thread):
  2.     """\
  3.    Implement a serial port read loop and dispatch to a Protocol instance (like
  4.    the asyncio.Protocol) but do it with threads.
  5.    Calls to close() will close the serial port but it is also possible to just
  6.    stop() this thread and continue the serial port instance otherwise.
  7.    """
  8.  
  9.     def __init__(self, serial_instance, protocol_factory):
  10.         """\
  11.        Initialize thread.
  12.        Note that the serial_instance' timeout is set to one second!
  13.        Other settings are not changed.
  14.        """
  15.         super(ReaderThread, self).__init__()
  16.         self.daemon = True
  17.         self.serial = serial_instance
  18.         self.protocol_factory = protocol_factory
  19.         self.alive = True
  20.         self._lock = threading.Lock()
  21.         self._connection_made = threading.Event()
  22.         self.protocol = None
  23.  
  24.     def stop(self):
  25.         """Stop the reader thread"""
  26.         self.alive = False
  27.         if hasattr(self.serial, 'cancel_read'):
  28.             self.serial.cancel_read()
  29.         self.join(2)
  30.  
  31.     def run(self):
  32.         """Reader loop"""
  33.         if not hasattr(self.serial, 'cancel_read'):
  34.             self.serial.timeout = 1
  35.         self.protocol = self.protocol_factory()
  36.         try:
  37.             self.protocol.connection_made(self)
  38.         except Exception as e:
  39.             self.alive = False
  40.             self.protocol.connection_lost(e)
  41.             self._connection_made.set()
  42.             return
  43.         error = None
  44.         self._connection_made.set()
  45.         while self.alive and self.serial.is_open:
  46.             try:
  47.                 # read all that is there or wait for one byte (blocking)
  48.                 data = self.serial.read(self.serial.in_waiting or 1)
  49.             except serial.SerialException as e:
  50.                 # probably some I/O problem such as disconnected USB serial
  51.                 # adapters -> exit
  52.                 error = e
  53.                 break
  54.             else:
  55.                 if data:
  56.                     # make a separated try-except for called user code
  57.                     try:
  58.                         self.protocol.data_received(data)
  59.                     except Exception as e:
  60.                         error = e
  61.                         break
  62.         self.alive = False
  63.         self.protocol.connection_lost(error)
  64.         self.protocol = None
  65.  
  66.     def write(self, data):
  67.         """Thread safe writing (uses lock)"""
  68.         with self._lock:
  69.             return self.serial.write(data)
  70.  
  71.     def close(self):
  72.         """Close the serial port and exit reader thread (uses lock)"""
  73.         # use the lock to let other threads finish writing
  74.         with self._lock:
  75.             # first stop reading, so that closing can be done on idle port
  76.             self.stop()
  77.             self.serial.close()
  78.  
  79.     def connect(self):
  80.         """
  81.        Wait until connection is set up and return the transport and protocol
  82.        instances.
  83.        """
  84.         if self.alive:
  85.             self._connection_made.wait()
  86.             if not self.alive:
  87.                 raise RuntimeError('connection_lost already called')
  88.             return (self, self.protocol)
  89.         else:
  90.             raise RuntimeError('already stopped')
  91.  
  92.     # - -  context manager, returns protocol
  93.  
  94.     def __enter__(self):
  95.         """\
  96.        Enter context handler. May raise RuntimeError in case the connection
  97.        could not be created.
  98.        """
  99.         self.start()
  100.         self._connection_made.wait()
  101.         if not self.alive:
  102.             raise RuntimeError('connection_lost already called')
  103.         return self.protocol
  104.  
  105.     def __exit__(self, exc_type, exc_val, exc_tb):
  106.         """Leave context: close port"""
  107.         self.close()
RAW Paste Data

Adblocker detected! Please consider disabling it...

We've detected AdBlock Plus or some other adblocking software preventing Pastebin.com from fully loading.

We don't have any obnoxious sound, or popup ads, we actively block these annoying types of ads!

Please add Pastebin.com to your ad blocker whitelist or disable your adblocking software.

×