def message_loop(self): loop_start_at = datetime.now() loop_last_print_at = datetime.now() while True: # Two parts to loop body: 1) look for and handle any # incoming messages, and 2) send out any outgoing # messages. # Hacky flag variables to avoid spinning fast if there is # nothing coming in and nothing going out (the common # case...) no_inputs = True no_outputs = True # # Handle any synthetic messages and loop them back to us. # if not self.fake_rx_queue.empty(): no_inputs = False msg = self.fake_rx_queue.get() self.logger.debug("Received synthetic message") # Don't need to confirm checksum as we computed it # ourselves! self.handle_message(msg) # # Handle incoming messages. # if self.serial_interface.wait_for_message_start() == MSG_START: no_inputs = False msg_ok = True try: msg = self.serial_interface.read_next_message() except CommException, ex: self.send_nak() self.logger.error(repr(ex)) continue msg_time = datetime.now() if len(msg) < 3: # Message too short, need at least length byte, # command byte, and checksum byte. self.send_nak() self.logger.error("Message too short: %r" % encode_message_to_ascii(msg)) if validate_message_checksum(msg): self.send_ack() self.handle_message(msg) else: # Bad checksum self.send_nak() self.logger.error("Bad checksum for message %r" % encode_message_to_ascii(msg)) # TODO: check here if there is pending input and handle it # by looping again, before worrying about sending out any # commands. # # If there is a pending message awaiting ack, see if it needs # to be resent. If there is no pending message (or the # pending message timed-out), send what's on the transmit # queue. # if self.tx_pending is not None and self.tx_timeout_exceded(): no_outputs = False self.maybe_resend_message("timeout") if self.tx_pending is None and not self.tx_queue.empty(): no_outputs = False msg = self.tx_queue.get() if msg == STOP: # Close the serial port once all the pending # messages have been sent. Because we close it, # we can't rerun message_loop(); we have to create # a new AlarmPanelInterface instance. self.serial_interface.close() return self.send_message(msg) # If there was nothing to do on this pass through the # loop, take a nap... if no_inputs and no_outputs: time.sleep(self.timeout_secs) secs_since_print = total_secs(datetime.now() - loop_last_print_at) if secs_since_print > 20: self.logger.debug_verbose("Looping %d" % \ total_secs(datetime.now() - loop_start_at)) loop_last_print_at = datetime.now()
def tx_timeout_exceded(self): assert self.tx_pending is not None elapsed = datetime.now() - self.tx_time return total_secs(elapsed) > ACK_TIMEOUT_INBOUND