class Thread(threading.Thread): def __init__(self, tty=TTY, baud_rate=BAUD_RATE, logger=None, *args, **kwargs): if logger is None: self.logger = logging.getLogger('blipper.thread') else: self.logger = logger super(Thread, self).__init__(*args, **kwargs) self.tty = tty self.baud_rate = baud_rate self.ser = Serial(self.tty, self.baud_rate) self.ser.nonblocking() self.daemon = True self._handlers = defaultdict(list) self._should_exit = False self.waiting_for_packet = False def on(self, packet_type, handler): """Register a handler for the given packet type. If there are multiple handlers for a packet type, at most one should return a response. If no handler returns a response, a default OK response will be sent back. """ self._handlers[packet_type].append(handler) def off(self, packet_type, handler): packet_handlers = self._handlers.get(packet_type, []) new_handlers = [h for h in packet_handlers if h != handler] self._handlers[packet_type] = new_handlers def send(self, packet): self.ser.write(bytes(packet)) self.logger.debug('sent %r', packet) def get_packet(self): self.waiting_for_packet = True data = self.ser.read(Header.length) header = unpack_header(data) body = self.ser.read(header.body_length) cs = self.ser.read(1) self.waiting_for_packet = False cs_cmp = get_checksum(body) if cs != cs_cmp: raise PacketError("invalid body checksum") packet = packet_types[header.cmd].from_body(body) self.logger.debug('got %r', packet) return packet def run(self): while True: try: packet = self.get_packet() response = None packet_handlers = self._handlers.get(type(packet), []) for handler in packet_handlers: handler_response = handler(packet) if handler_response: if response: raise MultipleResponses else: response = handler_response if response is None: response = packet.default_response() if response is not None: self.ser.write(bytes(response)) except Exception as e: if self._should_exit: # exception expected break else: raise e def join(self, timeout=None): self._should_exit = True self.ser.close() super(Thread, self).join(timeout)