def __init__(self, buff, verbose=False): '''Open a serial connection and wait for the ready signal''' self.verbose = verbose self.last_received = 0 self.serial = buff # Create the logger self.logger = TimedLogger(self.serial.start_time, textcolor=self.LOGGING_COLOR) self.serial.start() if not self.wait_for_ready(): exit(1)
def __init__(self, device, verbose=False, baud=9600): """Open a serial connection and wait for the ready signal""" self.device = device self.verbose = verbose self.baud = baud self.last_received = 0 # Create the serial buffer and start it up self.serial = SerialBuffer(device, baud) # Create the logger self.logger = TimedLogger(self.serial.start_time, textcolor=self.LOGGING_COLOR) self.serial.start() if not self.wait_for_ready(): exit(1)
def __init__(self, options): # Connect to serial connection self.ser = HMTLSerial(options.device, verbose=options.verbose, baud=options.baud) self.logger = TimedLogger(self.ser.serial.start_time, textcolor=self.LOGGING_COLOR) self.address = (options.address, options.port) self.terminate = False # TODO: Is this needed at all? If so should the SerialBuffer handle synchronization? self.serial_cv = threading.Condition() self.conn = None self.listener = None
def __init__(self, address='localhost', port=6000, hmtladdress=None, verbose=False, logger=True, authenticate=True): self.logger = TimedLogger() if not logger: self.logger.disable() self.address = hmtladdress self.verbose = verbose address = (address, port) try: if authenticate: authkey = b'secret password' else: authkey = None self.conn = Client(address, authkey=authkey) except Exception as e: raise Exception("Failed to connect to '%s'" % (str(address))) random.seed() print("HMTLClient initialized")
def __init__(self, serial_device, address, device_scan=False, logger=True): self.ser = serial_device self.address = address self.logger = TimedLogger(self.ser.serial.start_time, textcolor=self.LOGGING_COLOR) if not logger: self.logger.disable() self.terminate = False # TODO: Is this needed at all? If so should the SerialBuffer handle synchronization? self.serial_cv = threading.Condition() self.conn = None self.listener = None if device_scan: self.scanner = DeviceScanner(self, True) self.scanner.start() else: self.scanner = None
def __init__(self, server, verbose=True, period=60.0): threading.Thread.__init__(self) self.server = server self.verbose = verbose self.logger = TimedLogger(self.server.ser.serial.start_time, textcolor=TimedLogger.MAGENTA) self.logger.log("Scanner initialized") # Period between scans self.scan_period = period # Period between address self.address_period = 0.25 # TODO: This range is arbitrary for scanning purposes self.address_range = [x for x in range(120, 150)] # Set as a daemon so that this thread will exit correctly # when the parent receives a kill signal self.daemon = True self.devices = {}
class HMTLSerial(): # Default logging color LOGGING_COLOR = TimedLogger.WHITE # How long to wait for the ready signal after connection MAX_READY_WAIT = 10 def __init__(self, buff, verbose=False): '''Open a serial connection and wait for the ready signal''' self.verbose = verbose self.last_received = 0 self.serial = buff # Create the logger self.logger = TimedLogger(self.serial.start_time, textcolor=self.LOGGING_COLOR) self.serial.start() if not self.wait_for_ready(): exit(1) def get_message(self, timeout=None): """Returns the next line of text or a complete HMTL message""" item = self.serial.get(wait=timeout) if not item: return None self.last_received = time.time() return item # Wait for data from device indicating its ready for commands def wait_for_ready(self): """Wait for the Arduino to send its ready signal""" self.logger.log("***** Waiting for ready from Arduino *****") start_wait = time.time() while True: item = self.get_message(1.0) # After connecting to the serial device there is sometimes data # from before the module resets, so wait a bit for that to clear. if (time.time() - start_wait) < 0.5: continue if item and (item.data == HMTLprotocol.HMTL_CONFIG_READY): self.logger.log("***** Recieved ready *****") return True if (time.time() - start_wait) > self.MAX_READY_WAIT: raise Exception("Timed out waiting for ready signal") # Send terminated data and wait for (N)ACK def send_and_confirm(self, data, terminated, timeout=10): """Send a command and wait for the ACK""" self.serial.write(data) if (terminated): self.serial.write(HMTLprotocol.HMTL_TERMINATOR) start_wait = time.time() while True: item = self.get_message() if item.data == HMTLprotocol.HMTL_CONFIG_ACK: return True if item.data == HMTLprotocol.HMTL_CONFIG_FAIL: raise HMTLConfigException("Configuration command failed") if (time.time() - start_wait) > timeout: raise Exception("Timed out waiting for ACK signal") # XXX: Here we need a method of getting data back from poll or the like # Send a text command def send_command(self, command): self.logger.log("send_command: %s" % (command)) self.send_and_confirm(command, True) # Send a binary config update def send_config(self, type, config): self.logger.log("send_config: %-10s %s" % (type, hexlify(config))) self.send_and_confirm(config, True)
class HMTLServer(): address = ('localhost', 6000) # Default logging color LOGGING_COLOR = TimedLogger.RED def __init__(self, serial_device, address, device_scan=False, logger=True): self.ser = serial_device self.address = address self.logger = TimedLogger(self.ser.serial.start_time, textcolor=self.LOGGING_COLOR) if not logger: self.logger.disable() self.terminate = False # TODO: Is this needed at all? If so should the SerialBuffer handle synchronization? self.serial_cv = threading.Condition() self.conn = None self.listener = None if device_scan: self.scanner = DeviceScanner(self, True) self.scanner.start() else: self.scanner = None def get_connection(self): try: self.logger.log("Waiting for connection") self.listener = Listener(self.address, authkey='secret password') self.conn = self.listener.accept() self.logger.log("Connection accepted from %s:%d" % self.listener.last_accepted) except KeyboardInterrupt: print("Exiting") self.terminate = True def handle_msg(self, item): if item.data == SERVER_EXIT: self.logger.log("* Received exit signal *") self.conn.send(SERVER_ACK) self.close() elif item.data == SERVER_DATA_REQ: self.logger.log("* Recieved data request") item = self.get_data_msg() if item: self.conn.send(item.data) else: self.conn.send(None) else: # Forward the message to the device self.serial_cv.acquire() self.send_data(item.data) self.serial_cv.release() # Reply with acknowledgement self.conn.send(SERVER_ACK) def send_data(self, data): self.serial_cv.acquire() self.ser.send_and_confirm(data, False) self.serial_cv.release() # Wait for and handle incoming connections def listen(self): self.logger.log("Server started") self.get_connection() while not self.terminate: try: data = self.conn.recv() item = InputItem.from_data(data) self.logger.log("Received: %s" % item) self.handle_msg(item) self.logger.log("Acked: %s" % item) except (EOFError, IOError): # Attempt to reconnect self.logger.log("Lost connection") self.listener.close() self.get_connection() except Exception as e: # Close the connection on uncaught exception self.logger.log("Exception during listen") self.close() raise e def close(self): self.listener.close() if self.conn: self.conn.close() self.terminate = True def get_data_msg(self, timeout=0.25): """Listen on the serial device for a properly formatted data message""" self.serial_cv.acquire() self.logger.log("Starting data request") time_limit = time.time() + timeout item = None while True: # Try to get a message with low timeout item = self.ser.get_message(timeout=0.1) if item and item.is_hmtl: self.logger.log("Received response: %s:\n%s" % (item, HMTLprotocol.decode_data(item.data))) break # Check for timeout if time.time() > time_limit: self.logger.log("Data request time limit exceeded") item = None break self.serial_cv.release() return item
class DeviceScanner(threading.Thread): """ This class performs a background scan for HMTL devices and maintains a list of discovered devices """ def __init__(self, server, verbose=True, period=60.0): threading.Thread.__init__(self) self.server = server self.verbose = verbose self.logger = TimedLogger(self.server.ser.serial.start_time, textcolor=TimedLogger.MAGENTA) self.logger.log("Scanner initialized") # Period between scans self.scan_period = period # Period between address self.address_period = 0.25 # TODO: This range is arbitrary for scanning purposes self.address_range = [x for x in range(120, 150)] # Set as a daemon so that this thread will exit correctly # when the parent receives a kill signal self.daemon = True self.devices = {} def get_devices(self): return self.devices def log(self, msg): if self.verbose: self.logger.log(msg) def stop(self): self._Thread__stop() def run(self): self.log("Scanner started") while True: self.log("Starting scan") for address in self.address_range: self.log("Polling address %d" % address) msg = HMTLprotocol.get_poll_msg(address) try: self.server.send_data(msg) item = self.server.get_data_msg() if item: (text, msg) = HMTLprotocol.decode_msg(item.data) if (isinstance(msg, HMTLprotocol.PollHdr)): self.log("Poll response: %s" % (msg.dump())) if self.devices[address]: # A device previously responded to this address self.devices[address].update(msg) else: # Create a new device on this address self.devices[address] = HMTLModule(msg) else: self.log("XXX: Wrong message type? %s" % (msg.dump())) elif self.devices[address]: # There was no response for a module we previously had configured self.log("No response for known address %d" % address) self.devices[address].set_active(False) except Exception as e: print("Exception: %s" % e) pass time.sleep(self.address_period) self.log("Current devices:") for deviceid in self.devices.keys(): self.log(" %s" % (self.devices[deviceid].dump())) time.sleep(self.scan_period)
class HMTLClient(): address = HMTLprotocol.BROADCAST verbose = False def __init__(self, address='localhost', port=6000, hmtladdress=None, verbose=False, logger=True, authenticate=True): self.logger = TimedLogger() if not logger: self.logger.disable() self.address = hmtladdress self.verbose = verbose address = (address, port) try: if authenticate: authkey = b'secret password' else: authkey = None self.conn = Client(address, authkey=authkey) except Exception as e: raise Exception("Failed to connect to '%s'" % (str(address))) random.seed() print("HMTLClient initialized") def close(self): self.conn.close() def send(self, msg): if (self.verbose): self.logger.log(" - Sending %s" % (hexlify(msg))) self.conn.send(msg) def get_ack(self): msg = self.conn.recv() if (self.verbose): self.logger.log(" - Received: '%s' '%s'" % (msg, hexlify(msg))) if (msg == server.SERVER_ACK): return True else: return False def send_and_ack(self, msg, expect_response=False): self.send(msg) # Wait for message acknowledgement if self.verbose: self.logger.log(" - Waiting on ack") # Wait for an ack from the server has_ack = False while True: if self.get_ack(): has_ack = True break if has_ack and expect_response: # If a response is expected then request the response data from the # server, repeating until the message indicates that there is no # more data. messages = [] headers = [] more_data = True while more_data: more_data = False # Request response data msg = self.get_response_data() messages.append(msg) if self.verbose: if msg: self.logger.log(" - Received data response: '%s':\n%s" % (hexlify(msg), HMTLprotocol.decode_data(msg))) else: self.logger.log(" - Failed to receive data response") try: # Attempt to decode the message decoded_msg = HMTLprotocol.msg_to_headers(msg) if decoded_msg: headers.append(decoded_msg) # Check if the message indicate that there are # additional messages expected. hdr = decoded_msg[0] if isinstance(hdr, HMTLprotocol.MsgHdr): if hdr.more_data(): # Request another message more_data = True else: headers.append(None) except Exception as e: print("Exception decoding messages: %s" % (str(e))) headers.append(None) return messages, headers return [None, None] def get_response_data(self): '''Request and attempt to retrieve response data''' self.conn.send(server.SERVER_DATA_REQ) #TODO: This should pickle some object with parameters like timeout msg = self.conn.recv() return msg def send_exit(self): self.send_and_ack(server.SERVER_EXIT)