def __init__(self): # TODO: very simple implementation, need to improve #self.ser_conn = Serial() #self.serial_port_finder() try: # old implementation was: #self.ser_conn = Serial(port='/dev/cu.usbmodemfd121', baudrate=57600) # self.ser_conn = Serial(port=self._port, baudrate=57600) self.ser_conn = Serial() portIndexToOpen = 0 self.serial_port_finder(portIndexToOpen) print("attempting to open " + self._ports[portIndexToOpen]) self.open_serial(self._ports[0], 57600) print("port opened") self.asip = AsipClient(self.SimpleWriter(self)) except Exception as e: sys.stdout.write( "Exception: caught {} while init serial and asip protocols\n". format(e)) try: self.ListenerThread(self.queue, self.ser_conn, True, self.DEBUG).start() self.ConsumerThread(self.queue, self.asip, True, self.DEBUG).start() while self.asip.isVersionOk( ) == False: # flag will be set to true when valid version message is received self.request_info() time.sleep(1.0) self.request_port_mapping() #time.sleep(1) except Exception as e: #TODO: improve exception handling sys.stdout.write( "Exception: caught {} while launching threads\n".format(e))
def __init__(self, Broker, BoardID): try: self.Broker = Broker self._ClientID = "Client" self._SUBTOPIC = "asip/" + BoardID + "/out" self._PUBTOPIC = "asip/" + BoardID + "/in" self.mqtt_client = mqtt.Client(self._ClientID) self.mqtt_client.on_connect = self.on_connect self.connect() self.buffer = "" self.asip = AsipClient(self.SimpleMQTTWriter(self)) except Exception as e: sys.stdout.write( "Exception: caught {} while init tcp socket and asip protocols\n" .format(e)) try: # NOTICE: two request_port_mapping() are required. If this method is not called two times, # the client won't be able to set the pin mapping # time.sleep(0.5) # self.request_port_mapping() # time.sleep(1) # self.request_port_mapping() # time.sleep(1) # ListenerThread is the one that reads incoming messages from mqtt # ConsumerThread is the one that read the queue filled by ListenerThread and call the asip process_input # Sender is the thread that publish messages self.ListenerThread(self.queue, self.mqtt_client, True, self.DEBUG, self._SUBTOPIC).start() self.ConsumerThread(self.queue, self.asip, True, self.DEBUG).start() self.Sender(self).start() self.mqtt_client.loop_start() # starting mqtt loop # TODO: check following code # while self.asip.isVersionOk() == False: # flag will be set to true when valid version message is received # self.request_info() # time.sleep(1.0) while not self.asip.check_mapping(): print("Requesting mapping") self.request_port_mapping() time.sleep(0.25) sys.stdout.write("**** Everything check ****\n") except Exception as e: #TODO: improve exception handling sys.stdout.write( "Exception: caught {} while launching threads\n".format(e))
def __init__(self, ip_address='127.0.0.1', tcp_port=5005): try: sys.stdout.write("Setting tcp: attempting to connect to {} and port {}\n".format(ip_address, tcp_port)) self.__sock_conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.__sock_conn.connect((ip_address, tcp_port)) self.asip = AsipClient(self.SimpleTCPWriter(self.__sock_conn, self.DEBUG)) except Exception as e: sys.stdout.write("Exception caught in init tcp socket and asip protocols {}\n".format(e)) try: # try to close connection self.close_tcp_conn() finally: sys.exit(1) # Listener creation try: self.__threads.append(self.ListenerThread( self.asip, self.__sock_conn, self.__RECV_TIMEOUT, self.__BUFFER_SIZE, self.DEBUG)) sys.stdout.write("Creating Threads: starting\n") self.__threads[0].start() while not self.__threads[0].is_alive(): # checking that listener is alive pass sys.stdout.write("Creating Threads: all threads created and alive\n") except Exception as e: sys.stdout.write("Caught exception in threads launch: {}\n".format(e)) self.thread_killer() sys.exit(1) else: # Running try: # TODO: version checking still missing # flag will be set to true when valid version message is received # while self.asip.isVersionOk() == False: # self.asip.request_info() # time.sleep(1.0) # Checking mapping while not self.asip.check_mapping(): self.asip.request_port_mapping() time.sleep(0.5) self.asip.set_auto_report_interval(0) sys.stdout.write("Creating Threads: Mapping received, auto-report interval set to 0. Running now!\n") # KeyboardInterrupt handling in order to close every thread correctly except KeyboardInterrupt: # KeyboardInterrupt handling in order to close every thread correctly sys.stdout.write("KeyboardInterrupt while checking mapping. Attempting to close listener thread.\n") self.thread_killer() sys.exit() except Exception as e: # killing threads and exiting in case of generic exception sys.stdout.write("Caught generic exception while checking mapping: {}\n".format(e)) self.thread_killer() sys.exit(1)
def __init__(self): # Serial connection creation, AsipClient object creation try: self.__ser_conn = Serial() self.serial_port_finder(self.__PORT_INDEX_TO_OPEN) sys.stdout.write("Setting Serial: attempting to open {}\n".format(self.__ports[self.__PORT_INDEX_TO_OPEN])) self.open_serial(self.__ports[self.__PORT_INDEX_TO_OPEN], self.__BAUD_RATE) sys.stdout.write("Setting Serial: serial port {} opened\n".format(self.__ports[self.__PORT_INDEX_TO_OPEN])) self.asip = AsipClient(self.SimpleWriter(self.__ser_conn, self.DEBUG)) except serial.SerialException as e: sys.stdout.write("Exception while init serial connection: {}\n".format(e)) sys.exit(1) # Listener creation try: self.__threads.append(self.ListenerThread(self.asip, self.__ser_conn, self.DEBUG)) sys.stdout.write("Creating Threads: starting\n") self.__threads[0].start() while not self.__threads[0].is_alive(): # checking that listener is alive pass sys.stdout.write("Creating Threads: all threads created and alive\n") except Exception as e: sys.stdout.write("Caught exception in threads launch: {}\n".format(e)) self.thread_killer() sys.exit(1) else: # Running try: # TODO: version checking still mis # flag will be set to true when valid version message is received # while self.asip.isVersionOk() == False: # self.asip.request_info() # time.sleep(1.0)sing # Checking mapping while not self.asip.check_mapping(): self.asip.request_port_mapping() time.sleep(0.5) self.asip.set_auto_report_interval(100) sys.stdout.write("Creating Threads: Mapping received, auto-report interval set to 0. Running now!\n") except KeyboardInterrupt: # KeyboardInterrupt handling in order to close every thread correctly sys.stdout.write("KeyboardInterrupt while checking mapping. Attempting to close listener thread.\n") self.thread_killer() sys.exit() except Exception as e: # killing threads and exiting in case of generic exception sys.stdout.write("Caught generic exception while checking mapping: {}\n".format(e)) self.thread_killer() sys.exit(1)
def __init__(self): # TODO: very simple implementation, need to improve #self.ser_conn = Serial() #self.serial_port_finder() try: # old implementation was: #self.ser_conn = Serial(port='/dev/cu.usbmodemfd121', baudrate=57600) # self.ser_conn = Serial(port=self._port, baudrate=57600) self.ser_conn = Serial() portIndexToOpen = 0 self.serial_port_finder(portIndexToOpen) sys.stdout.write("attempting to open {}\n".format(self._ports[portIndexToOpen])) self.open_serial(self._ports[0], 57600) sys.stdout.write("port opened\n") self.asip = AsipClient(self.SimpleWriter(self)) except Exception as e: sys.stdout.write("Exception: caught {} while init serial and asip protocols\n".format(e)) try: self.__running = True self.ListenerThread(self.queue, self.ser_conn, self.__running, self.DEBUG).start() self.ConsumerThread(self.queue, self.asip, self.__running, self.DEBUG).start() self.KeyboardListener(self).start() print("****** I am here ******") #while self.asip.isVersionOk() == False: # flag will be set to true when valid version message is received #self.request_info() #time.sleep(1.0) self.request_port_mapping() time.sleep(1) self.request_port_mapping() time.sleep(1) while not self.asip.check_mapping(): self.request_port_mapping() time.sleep(0.1) print("**** Everything check ****") except Exception as e: #TODO: improve exception handling sys.stdout.write("Exception: caught {} while launching threads\n".format(e))
class SerialBoard: # ************ BEGIN CONSTANTS DEFINITION **************** DEBUG = False # Activates debug messages __SERIAL_TIMEOUT = 2 # serial timeout (avoid blocking in case of issues) __PORT_INDEX_TO_OPEN = 0 __BAUD_RATE = 57600 # ************ END CONSTANTS DEFINITION **************** # ************ BEGIN PRIVATE FIELDS DEFINITION **************** # asip: The client for the asip protocol # __ser_conn: self board uses serial communication __ports = [] # serial ports array __threads = [] # List of threads # ************ END PRIVATE FIELDS DEFINITION **************** # Self constructor find the name of an active serial port and it creates a Serial objted def __init__(self): # Serial connection creation, AsipClient object creation try: self.__ser_conn = Serial() self.serial_port_finder(self.__PORT_INDEX_TO_OPEN) sys.stdout.write("Setting Serial: attempting to open {}\n".format( self.__ports[self.__PORT_INDEX_TO_OPEN])) self.open_serial(self.__ports[self.__PORT_INDEX_TO_OPEN], self.__BAUD_RATE) sys.stdout.write("Setting Serial: serial port {} opened\n".format( self.__ports[self.__PORT_INDEX_TO_OPEN])) self.asip = AsipClient( self.SimpleWriter(self.__ser_conn, self.DEBUG)) except serial.SerialException as e: sys.stdout.write( "Exception while init serial connection: {}\n".format(e)) sys.exit(1) # Listener creation try: self.__threads.append( self.ListenerThread(self.asip, self.__ser_conn, self.DEBUG)) sys.stdout.write("Creating Threads: starting\n") self.__threads[0].start() while not self.__threads[0].is_alive( ): # checking that listener is alive pass sys.stdout.write( "Creating Threads: all threads created and alive\n") except Exception as e: sys.stdout.write( "Caught exception in threads launch: {}\n".format(e)) self.thread_killer() sys.exit(1) else: # Running try: # TODO: version checking still mis # flag will be set to true when valid version message is received # while self.asip.isVersionOk() == False: # self.asip.request_info() # time.sleep(1.0)sing # Checking mapping while not self.asip.check_mapping(): self.asip.request_port_mapping() time.sleep(0.5) self.asip.set_auto_report_interval(100) sys.stdout.write( "Creating Threads: Mapping received, auto-report interval set to 0. Running now!\n" ) except KeyboardInterrupt: # KeyboardInterrupt handling in order to close every thread correctly sys.stdout.write( "KeyboardInterrupt while checking mapping. Attempting to close listener thread.\n" ) self.thread_killer() sys.exit() except Exception as e: # killing threads and exiting in case of generic exception sys.stdout.write( "Caught generic exception while checking mapping: {}\n". format(e)) self.thread_killer() sys.exit(1) # ************ BEGIN PUBLIC METHODS ************* # stops and wait for the join for threads in the given pool # TODO: improve in case of timeout of the join def thread_killer(self): for i in self.__threads: try: i.stopper() sys.stdout.write( "Killing Threads: event for {} successfully set\n".format( i)) except Exception as e: sys.stdout.write( "Caught exception while stropping thread {}.\nException is: {}\n" .format(i, e)) time.sleep(0.5) sys.stdout.write("Killing Threads: waiting for join\n") for i in self.__threads: i.join() sys.stdout.write( "Killing Threads: thread {} successfully closed\n".format(i)) self.__threads = [] sys.stdout.write("All threads terminated.\n") return True def get_asip_client(self): return self.asip # ************ END PUBLIC METHODS ************* # ************ BEGIN PRIVATE METHODS ************* def open_serial(self, port, baud_rate): if self.__ser_conn.isOpen(): self.__ser_conn.close() self.__ser_conn.port = port self.__ser_conn.baudrate = baud_rate self.__ser_conn.timeout = self.__SERIAL_TIMEOUT self.__ser_conn.open() # Toggle DTR to reset Arduino self.__ser_conn.setDTR(False) time.sleep(1) # toss any data already received, see self.__ser_conn.flushInput() time.sleep(1) self.__ser_conn.setDTR(True) time.sleep(1) def close_serial(self): self.__ser_conn.close() # This methods retrieves the operating system and set the Arduino serial port """Lists serial ports :raises EnvironmentError: On unsupported or unknown platforms :returns: A list of available serial ports """ # TODO: test needed for linux and windows implementation # TODO: improve try except def serial_port_finder(self, desired_index): system = sys.platform if system.startswith('win'): temp_ports = ['COM' + str(i + 1) for i in range(255)] elif system.startswith('linux'): # this is to exclude your current terminal "/dev/tty" temp_ports = glob.glob('/dev/tty[A-Za-z]*') elif system.startswith('darwin'): temp_ports = glob.glob('/dev/tty.usbmodem*') cp2104 = glob.glob('/dev/tty.SLAB_USBtoUART' ) # append usb to serial converter cp2104 ft232rl = glob.glob('/dev/tty.usbserial-A9MP5N37' ) # append usb to serial converter ft232rl fth = glob.glob( '/dev/tty.usbserial-FTHI5TLH') # append usb to serial cable #new = glob.glob('/dev/tty.usbmodemfd121') #temp_ports = glob.glob('/dev/tty.SLAB_USBtoUART') #temp_ports = glob.glob('/dev/tty.usbserial-A9MP5N37') if cp2104 is not None: temp_ports += cp2104 if ft232rl is not None: temp_ports += ft232rl if fth is not None: temp_ports += fth #if new is not None: # FIXME: REMOVE!!! Only used for tests # temp_ports = new else: raise EnvironmentError('Unsupported platform') for port in temp_ports: try: self.__ser_conn.port = port self.__ser_conn.open() self.__ser_conn.close() self.__ports.append(port) if len(self.__ports) > desired_index: return # we have found the desired port except serial.SerialException: pass if self.DEBUG: sys.stdout.write("DEBUG: available ports are {}\n".format( self.__ports)) # ************ END PRIVATE METHODS ************* # ************ BEGIN PRIVATE CLASSES ************* # As described above, SimpleSerialBoard writes messages to the serial port. # inner class SimpleWriter implements abstract class AsipWriter: class SimpleWriter(AsipWriter): def __init__(self, ser_conn, debug=False): self.ser_conn = ser_conn self.DEBUG = debug # val is a string # TODO: improve try catch, add exit in case of exception too def write(self, val): if self.ser_conn.isOpen(): try: temp = val.encode() self.ser_conn.write(temp) if self.DEBUG: sys.stdout.write( "DEBUG: just wrote in serial {}\n".format(temp)) except (OSError, serial.SerialException) as e: sys.stdout.write( "Caught exception in serial write: {}\n".format(e)) else: raise serial.SerialException # ListenerThread read the serial stream and call process_input class ListenerThread(Thread): # overriding constructor def __init__(self, asip, ser_conn, debug=False): Thread.__init__(self) self.asip = asip self.ser_conn = ser_conn self.DEBUG = debug self._stopper = threading.Event() sys.stdout.write("Listener Thread: thread process created.\n") # if needed, kill will stops the loop inside run method def stopper(self): sys.stdout.write("Listener Thread: now stopping.\n") self._stopper.set() # overriding run method, thread activity def run(self): time.sleep(2) # TODO: maybe reduce this sleep? sys.stdout.write("Listener Thread: now running.\n") ser_buffer = "" while not self._stopper.is_set(): try: c = self.ser_conn.read( ) # attempt to read a character from Serial c = c.decode('utf-8', errors='ignore') if len(c) == 0: # was anything read? pass else: # if self.DEBUG: # sys.stdout.write("DEBUG: Char from serial: {}\n".format(c)) if c == '\n' or c == '\n': if len(ser_buffer) > 0: ser_buffer += '\n' self.asip.process_input(ser_buffer) if self.DEBUG: sys.stdout.write( "DEBUG: Complete message from serial: {}\n" .format(ser_buffer)) ser_buffer = "" else: ser_buffer += c except serial.SerialTimeoutException: continue # Go to next iteration in case of serial timeout except serial.SerialException as e: sys.stdout.write( "Caught SerialException in serial read: {}\nListener Thread will now stop\n" .format(e)) self.stopper() except Exception as e: sys.stdout.write( "Caught exception: {}\nListener Thread will NOT stop\n" .format(e)) #self.stopper() sys.stdout.write("Listener Thread: stopped\n")
class TCPBoard: # ************ BEGIN CONSTANTS DEFINITION **************** DEBUG = True # Activates debug messages __BUFFER_SIZE = 256 # TCP buffer size __RECV_TIMEOUT = 2 # socket receive timeout in second # ************ END CONSTANTS DEFINITION **************** # ************ BEGIN PRIVATE FIELDS DEFINITION **************** # asip: The client for the asip protocol # __sock_conn: tcp/ip socket communication __threads = [] # List of threads # ************ END PRIVATE FIELDS DEFINITION **************** # tcp_port = 6789 is the one used by the java bridge by franco in mirto def __init__(self, ip_address='127.0.0.1', tcp_port=5005): try: sys.stdout.write("Setting tcp: attempting to connect to {} and port {}\n".format(ip_address, tcp_port)) self.__sock_conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.__sock_conn.connect((ip_address, tcp_port)) self.asip = AsipClient(self.SimpleTCPWriter(self.__sock_conn, self.DEBUG)) except Exception as e: sys.stdout.write("Exception caught in init tcp socket and asip protocols {}\n".format(e)) try: # try to close connection self.close_tcp_conn() finally: sys.exit(1) # Listener creation try: self.__threads.append(self.ListenerThread( self.asip, self.__sock_conn, self.__RECV_TIMEOUT, self.__BUFFER_SIZE, self.DEBUG)) sys.stdout.write("Creating Threads: starting\n") self.__threads[0].start() while not self.__threads[0].is_alive(): # checking that listener is alive pass sys.stdout.write("Creating Threads: all threads created and alive\n") except Exception as e: sys.stdout.write("Caught exception in threads launch: {}\n".format(e)) self.thread_killer() sys.exit(1) else: # Running try: # TODO: version checking still missing # flag will be set to true when valid version message is received # while self.asip.isVersionOk() == False: # self.asip.request_info() # time.sleep(1.0) # Checking mapping while not self.asip.check_mapping(): self.asip.request_port_mapping() time.sleep(0.5) self.asip.set_auto_report_interval(0) sys.stdout.write("Creating Threads: Mapping received, auto-report interval set to 0. Running now!\n") # KeyboardInterrupt handling in order to close every thread correctly except KeyboardInterrupt: # KeyboardInterrupt handling in order to close every thread correctly sys.stdout.write("KeyboardInterrupt while checking mapping. Attempting to close listener thread.\n") self.thread_killer() sys.exit() except Exception as e: # killing threads and exiting in case of generic exception sys.stdout.write("Caught generic exception while checking mapping: {}\n".format(e)) self.thread_killer() sys.exit(1) # ************ BEGIN PUBLIC METHODS ************* # stops and waits for the join for threads in the given pool # TODO: improve in case of timeout of the join def thread_killer(self): for i in self.__threads: try: i.stopper() sys.stdout.write("Killing Threads: event for {} successfully set\n".format(i)) except Exception as e: sys.stdout.write("Caught exception while stropping thread {}.\nException is: {}\n".format(i, e)) time.sleep(0.5) sys.stdout.write("Killing Threads: waiting for join\n") for i in self.__threads: i.join() sys.stdout.write("Killing Threads: thread {} successfully closed\n".format(i)) self.__threads = [] try: self.close_tcp_conn() except Exception as e: sys.stdout.write("Caught generic exception while calling close_tcp_conn: {}\n".format(e)) sys.stdout.write("All threads terminated.\n") return True def get_asip_client(self): return self.asip # ************ END PUBLIC METHODS ************* # ************ BEGIN PRIVATE METHODS ************* def close_tcp_conn(self): self.__sock_conn.shutdown(socket.SHUT_RDWR) self.__sock_conn.close() sys.stdout.write("Connection closed.\n") # ************ END PRIVATE METHODS ************* # ************ BEGIN PRIVATE CLASSES ************* # As described above, SimpleTCPBoard writes messages to the tcp stream. # inner class SimpleTCPWriter implements abstract class AsipWriter: class SimpleTCPWriter(AsipWriter): def __init__(self, sock_conn, debug=False): self.sock_conn = sock_conn self.DEBUG = debug # val is a string # TODO: improve try catch def write(self, val): # TODO: insert a way to check weather the connection is still open or not try: self.sock_conn.send(val) if self.DEBUG: sys.stdout.write("DEBUG: sent {}\n".format(val)) except Exception as e: sys.stdout.write("Caught exception in serial write: {}\n".format(e)) # ListenerThread read the tcp/ip stream and call process_input class ListenerThread(Thread): # overriding constructor def __init__(self, asip, sock_conn, timeout, buffer_size=256, debug=False): Thread.__init__(self) self.asip = asip self.sock_conn = sock_conn self.sock_conn.settimeout(timeout) # setting socket recv timeout self.BUFFER_SIZE = buffer_size self.DEBUG = debug self._stopper = threading.Event() sys.stdout.write("Listener Thread: thread process created.\n") # if needed, kill will stops the loop inside run method def stopper(self): sys.stdout.write("Listener Thread: now stopping.\n") self._stopper.set() # overriding run method, thread activity def run(self): time.sleep(2) # TODO: maybe reduce this sleep? sys.stdout.write("Listener Thread: now running.\n") temp_buffer = "" while not self._stopper.is_set(): try: data = self.sock_conn.recv(self.BUFFER_SIZE) # sys.stdout.write("Received data is: {}\n".format(data)) if data != '\r' and data != '\n' and data != ' ' and data is not None: # ignore empty lines if "\n" in data: # If there is at least one newline, we need to process # the message (the buffer may contain previous characters). while "\n" in data and len(data) > 0: # But remember that there could be more than one newline in the buffer temp_buffer += (data[0:data.index("\n")]) temp = temp_buffer.encode() self.asip.process_input(temp) temp_buffer = "" if data[data.index("\n")+1:] == '\n': data = '' break else: data = data[data.index("\n")+1:] if len(data) > 0 and data not in ('\r', '\n', ' '): temp_buffer = data else: temp_buffer += data except socket.timeout as e: err = e.args[0] if err == 'timed out': # socket time out, if the _stop value is set, program will exit continue except Exception as e: sys.stdout.write("Caught exception in listener: {}\nListener will now stop\n".format(e)) self.stopper() sys.stdout.write("Listener Thread: stopped\n") # ************ END PRIVATE CLASSES *************
class SimpleTCPBoard: # ************ BEGIN CONSTANTS DEFINITION **************** DEBUG = True # ************ END CONSTANTS DEFINITION **************** # ************ BEGIN PRIVATE FIELDS DEFINITION **************** sock_conn = None # self board uses socket asip = None # The client for the aisp protocol queue = Queue( 10) # Buffer # TODO: use pipe instead of queue for better performances # FIXME: fix Queue dimension? IPaddress = "" #_TCPport = 6789 # the one used by the java bridge by franco in mirto _TCPport = 5005 # ************ END PRIVATE FIELDS DEFINITION **************** # self constructor takes the IP address # Here the tcp ip listener and the queue reader are started def __init__(self, IPaddress): try: self.IPaddress = IPaddress sys.stdout.write( "Attempting to connect to {} and port {}\n".format( self.IPaddress, self._TCPport)) self.sock_conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock_conn.connect((self.IPaddress, self._TCPport)) self.asip = AsipClient( self.SimpleTCPWriter(self, sock_conn=self.sock_conn)) except Exception as e: sys.stdout.write( "Exception caught in init tcp socket and asip protocols {}\n". format(e)) else: worker = [] try: # NOTICE: two request_port_mapping() are required. If this method is not called two times, # the client won't be able to set the pin mapping worker.append( self.ListenerThread(self.queue, self.sock_conn, True, self.DEBUG)) worker.append( self.ConsumerThread(self.queue, self.asip, True, self.DEBUG)) for i in worker: if self.DEBUG: print("Starting {}".format(i)) i.start() all_alive = False while not all_alive: # cheching that every thread is alive # TODO: improve syntax in following line if worker[0].is_alive() and worker[1].is_alive(): all_alive = True # TODO: check following code # while self.asip.isVersionOk() == False: # flag will be set to true when valid version message is received # self.request_info() # time.sleep(1.0) active_workers = threading.active_count() sys.stdout.write("*** All threads created and alive ***\n") except Exception as e: sys.stdout.write( "Caught exception in thread launch: {}\n".format(e)) self.close_tcp_conn() self.thread_killer(worker) sys.exit(1) else: try: while not self.asip.check_mapping(): self.request_port_mapping() time.sleep(0.25) self.set_auto_report_interval(0) # checking that a thread is not killed by an exception while len(threading.enumerate()) == active_workers: pass # KeyboardInterrupt handling in order to close every thread correctly except KeyboardInterrupt: sys.stdout.write( "KeyboardInterrupt: attempting to close threads.\n") finally: # killing thread in both cases: keyboardinterrupt or exception in one of the thread self.close_tcp_conn() self.thread_killer(worker) sys.stdout.write("All terminated.\n") sys.exit() # ************ BEGIN PUBLIC METHODS ************* # The following methods are just a replica from the asip class. # TODO: add parameter checikng in each function (raise exception?) def digital_read(self, pin): return self.asip.digital_read(pin) def analog_read(self, pin): return self.asip.analog_read(pin) def set_pin_mode(self, pin, mode): self.asip.set_pin_mode(pin, mode) def digital_write(self, pin, value): self.asip.digital_write(pin, value) def analog_write(self, pin, value): self.asip.analog_write(pin, value) def request_info(self): self.asip.request_info() def request_port_mapping(self): self.asip.request_port_mapping() def set_auto_report_interval(self, interval): self.asip.set_auto_report_interval(interval) def add_service(self, service_id, asip_service): self.asip.add_service(service_id, asip_service) def get_asip_client(self): return self.asip # ************ END PUBLIC METHODS ************* # ************ BEGIN PRIVATE METHODS ************* #def open_serial(self, port, baudrate): def close_tcp_conn(self): self.sock_conn.close() # stops and wait for the join for threads in the given pool # TODO: improve in case of timeout of the join def thread_killer(self, thread_pool): for i in thread_pool: i.stop() if self.DEBUG: sys.stdout.write("Event for {} successfully set\n".format(i)) sys.stdout.write("Waiting for join\n") for i in thread_pool: i.join() if self.DEBUG: sys.stdout.write("Thread {} successfully closed\n".format(i)) return True # ************ END PRIVATE METHODS ************* # ************ BEGIN PRIVATE CLASSES ************* # As described above, SimpleTCPBoard writes messages to the tcp stream. # inner class SimpleTCPWriter implements abstract class AsipWriter: class SimpleTCPWriter(AsipWriter): parent = None sock_conn = None def __init__(self, parent, sock_conn): self.parent = parent self.sock_conn = sock_conn # val is a string # TODO: improve try catch def write(self, val): # TODO: insert a way to check weather the connection is still open or not try: #if self.parent.DEBUG: #sys.stdout.write("DEBUG: sending {}\n".format(val)) #temp = self.writeUTF(val) #self.sock_conn.sendall((val+'\n').encode('utf-8')) temp = val #+ '\n' #self.sock_conn.send(b"temp") # temp.encode('utf-8') #self.sock_conn.sendall(bytes(temp,encoding='utf8')) # self.parent.sock_conn.send(val) #temp.encode('utf-8') self.sock_conn.send(temp) if self.parent.DEBUG: sys.stdout.write("DEBUG: sent {}\n".format(temp)) except Exception as e: pass # def writeUTF(self, str): # temp = None # utf8 = str.encode('utf-8') # length = len(utf8) # temp.append(struct.pack('!H', length)) # format = '!' + str(length) + 's' # temp.append(struct.pack(format, utf8)) # return temp # ListenerThread and ConsumerThread are implemented following the Producer/Consumer pattern # A class for a listener that read the tcp ip messages and put incoming messages on a queue # TODO: implement try catch class ListenerThread(Thread): queue = None sock_conn = None running = False DEBUG = False BUFFER_SIZE = 256 # overriding constructor def __init__(self, queue, sock_conn, running, debug): Thread.__init__(self) self.queue = queue self.sock_conn = sock_conn self.running = running self.DEBUG = debug self._stop = threading.Event() if self.DEBUG: sys.stdout.write("DEBUG: listener thread process created \n") # if needed, kill will stops the loop inside run method def stop(self): self._stop.set() # overriding run method, thread activity def run(self): temp_buff = "" time.sleep(2) write_buffer = "" # TODO: implement ser.inWaiting() >= minMsgLen to check number of char in the receive buffer? while not self._stop.is_set(): # data = self.sock_conn.recv(512).decode('utf-8') # #data = data[2:] # if not data: # pass # else: # self.queue.put(data) # print("Received {}\n".format(data)) # if self.DEBUG: # sys.stdout.write("DEBUG: Temp buff is now {}\n".format(temp_buff)) # val = self.sock_conn.recv(2) # if self.DEBUG: # sys.stdout.write("DEBUG: val value when retrieving from tcp ip stream is {}\n".format(val)) # # val = val.decode('utf-8') # time.sleep(0.1) # if self.DEBUG: # sys.stdout.write("DEBUG: val value after decode is {}".format(val)) # if val is not None and val!="\n": # if "\n" in val: # # If there is at least one newline, we need to process # # the message (the buffer may contain previous characters). # # while ("\n" in val and len(val) > 0): # # But remember that there could be more than one newline in the buffer # temp_buff += (val[0:val.index("\n")]) # self.queue.put(temp_buff) # if self.DEBUG: # sys.stdout.write("DEBUG: tcp ip produced {}\n".format(temp_buff)) # temp_buff = "" # val = val[val.index("\n")+1:] # if self.DEBUG: # sys.stdout.write("DEBUG: Now val is {}\n".format(val)) # if len(val)>0: # temp_buff = val # if self.DEBUG: # sys.stdout.write("DEBUG: After internal while buffer is {}\n".format(temp_buff)) # else: # temp_buff += val # if self.DEBUG: # sys.stdout.write("DEBUG: else case, buff is equal to val, so they are {}\n".format(temp_buff)) data = self.sock_conn.recv(self.BUFFER_SIZE) # print("Received data is: {}".format(data)) if data != '\r' and data != '\n' and data != ' ' and data is not None: # ignore empty lines if "\n" in data: # If there is at least one newline, we need to process # the message (the buffer may contain previous characters). while ("\n" in data and len(data) > 0): # But remember that there could be more than one newline in the buffer write_buffer += (data[0:data.index("\n")]) temp = write_buffer.encode() self.queue.put(temp) #print("Inserting in queue {}".format(temp)) write_buffer = "" if data[data.index("\n") + 1:] == '\n': data = '' break else: data = data[data.index("\n") + 1:] if len(data) > 0 and data not in ('\r', '\n', ' '): write_buffer = data else: write_buffer += data # A class that reads the queue and launch the processInput method of the AispClient. class ConsumerThread(Thread): queue = None asip = None running = False DEBUG = False # overriding constructor def __init__(self, queue, asip, running, debug): Thread.__init__(self) self.queue = queue self.asip = asip self.running = running self.DEBUG = debug self._stop = threading.Event() if self.DEBUG: sys.stdout.write("DEBUG: consumer thread created \n") # if needed, kill will stops the loop inside run method def stop(self): self._stop.set() # overriding run method, thread activity def run(self): # global _queue # global asip while not self._stop.is_set(): temp = self.queue.get() # print("Consumer, calling process_input with input: {}\n".format(temp)) self.asip.process_input(temp) self.queue.task_done()
class SimpleMQTTBoard: # ************ BEGIN CONSTANTS DEFINITION **************** DEBUG = True # ************ END CONSTANTS DEFINITION **************** # ************ BEGIN PRIVATE FIELDS DEFINITION **************** mqtt_client = None # self board uses mqtt asip = None # The client for the aisp protocol queue = Queue( 10) # Buffer # TODO: use pipe instead of queue for better performances # FIXME: fix Queue dimension? Broker = "" _TCPport = 1883 _ClientID = "" _SUBTOPIC = "" _PUBTOPIC = "" # ************ END PRIVATE FIELDS DEFINITION **************** # self constructor takes the Broker address # Here the listener and the queue reader are started def __init__(self, Broker, BoardID): try: self.Broker = Broker self._ClientID = "Client" self._SUBTOPIC = "asip/" + BoardID + "/out" self._PUBTOPIC = "asip/" + BoardID + "/in" self.mqtt_client = mqtt.Client(self._ClientID) self.mqtt_client.on_connect = self.on_connect self.connect() self.buffer = "" self.asip = AsipClient(self.SimpleMQTTWriter(self)) except Exception as e: sys.stdout.write( "Exception: caught {} while init tcp socket and asip protocols\n" .format(e)) try: # NOTICE: two request_port_mapping() are required. If this method is not called two times, # the client won't be able to set the pin mapping # time.sleep(0.5) # self.request_port_mapping() # time.sleep(1) # self.request_port_mapping() # time.sleep(1) # ListenerThread is the one that reads incoming messages from mqtt # ConsumerThread is the one that read the queue filled by ListenerThread and call the asip process_input # Sender is the thread that publish messages self.ListenerThread(self.queue, self.mqtt_client, True, self.DEBUG, self._SUBTOPIC).start() self.ConsumerThread(self.queue, self.asip, True, self.DEBUG).start() self.Sender(self).start() self.mqtt_client.loop_start() # starting mqtt loop # TODO: check following code # while self.asip.isVersionOk() == False: # flag will be set to true when valid version message is received # self.request_info() # time.sleep(1.0) while not self.asip.check_mapping(): print("Requesting mapping") self.request_port_mapping() time.sleep(0.25) sys.stdout.write("**** Everything check ****\n") except Exception as e: #TODO: improve exception handling sys.stdout.write( "Exception: caught {} while launching threads\n".format(e)) # ************ BEGIN PUBLIC METHODS ************* # The following methods are just a replica from the asip class. # TODO: add parameter checikng in each function (raise exception?) def digital_read(self, pin): return self.asip.digital_read(pin) def analog_read(self, pin): return self.asip.analog_read(pin) def set_pin_mode(self, pin, mode): self.asip.set_pin_mode(pin, mode) def digital_write(self, pin, value): self.asip.digital_write(pin, value) def analog_write(self, pin, value): self.asip.analog_write(pin, value) def request_info(self): self.asip.request_info() def request_port_mapping(self): self.asip.request_port_mapping() def set_auto_report_interval(self, interval): self.asip.set_auto_report_interval(interval) def add_service(self, service_id, asip_service): self.asip.add_service(service_id, asip_service) def get_asip_client(self): return self.asip # ************ END PUBLIC METHODS ************* # ************ BEGIN PRIVATE METHODS ************* # The callback for when the client receives a CONNACK response from the server. def on_connect(self, client, userdata, flags, rc): if self.DEBUG: sys.stdout.write("DEBUG: Connected with result code {}".format(rc)) # Subscribing in on_connect() means that if we lose the connection and # reconnect then subscriptions will be renewed. #self.mqtt_client.subscribe("$SYS/#") #self.mqtt_client.subscribe(self._SUBTOPIC) def connect(self): if self.DEBUG: sys.stdout.write( "DEBUG: Connecting to mqtt broker {} on port {}".format( self.Broker, self._TCPport)) self.mqtt_client.connect(self.Broker, self._TCPport, 180) def sendData(self, msg): self.mqtt_client.publish(self._PUBTOPIC, msg) def disconnect(self): if self.DEBUG: sys.stdout.write("DEBUG: DEBUG: Disconnected from mqtt") self.mqtt_client.disconnect() # ************ END PRIVATE METHODS ************* # ************ BEGIN PRIVATE CLASSES ************* class Sender(Thread): def __init__(self, parent): Thread.__init__(self) self.parent = parent self.running = True def run(self): while self.running: if not self.parent.buffer: pass else: if self.parent.DEBUG: sys.stdout.write("DEBUG: Sending: {}\n".format( self.parent.buffer[0:self.parent.buffer.index('\n' )])) self.parent.mqtt_client.publish( self.parent._PUBTOPIC, self.parent.buffer[0:self.parent.buffer.index('\n')]) self.parent.buffer = self.parent.buffer[self.parent.buffer. index('\n') + 1:] if self.parent.DEBUG: sys.stdout.write("DEBUG: Rest of buffer {}\n".format( self.parent.buffer)) # As described above, SimpleMQTTBoard writes messages to the tcp stream. # inner class SimpleMQTTWriter implements abstract class AsipWriter: class SimpleMQTTWriter(AsipWriter): parent = None def __init__(self, parent): self.parent = parent # val is a string # TODO: improve try catch def write(self, val): # TODO: insert a way to check weather the connection is still open or not try: # it fills a buffer self.parent.buffer = self.parent.buffer + val + '\n' if self.parent.DEBUG: sys.stdout.write("DEBUG: Just sent {}".format(val)) except Exception as e: pass # ListenerThread and ConsumerThread are implemented following the Producer/Consumer pattern # A class for a listener that read the tcp ip messages and put incoming messages on a queue # TODO: implement try catch class ListenerThread(Thread): queue = None mqtt_client = None running = False DEBUG = False temp_buff = "" # overriding constructor def __init__(self, queue, mqtt_client, running, debug, subtopic): Thread.__init__(self) self.queue = queue self.mqtt_client = mqtt_client self.mqtt_client.on_message = self.on_message # callback self.mqtt_client.subscribe(topic=subtopic) self.running = running self.DEBUG = debug if self.DEBUG: sys.stdout.write("DEBUG: listener thread process created \n") # if needed, kill will stops the loop inside run method def kill(self): self.running = False # The callback for when a PUBLISH message is received from the server. def on_message(self, client, userdata, msg): if not msg.payload: pass else: temp = msg.payload.decode('utf-8') self.queue.put(temp) if self.DEBUG: sys.stdout.write("DEBUG: Received {}\n".format(temp)) # A class that reads the queue and launch the processInput method of the AispClient. class ConsumerThread(Thread): queue = None asip = None running = False DEBUG = False # overriding constructor def __init__(self, queue, asip, running, debug): Thread.__init__(self) self.queue = queue self.asip = asip self.running = running self.DEBUG = debug if self.DEBUG: sys.stdout.write("DEBUG: consumer thread created \n") # if needed, kill will stops the loop inside run method def kill(self): self.running = False # overriding run method, thread activity def run(self): while self.running: temp = self.queue.get() self.asip.process_input(temp) self.queue.task_done()
class SerialBoard: # ************ BEGIN CONSTANTS DEFINITION **************** DEBUG = False # Activates debug messages __SERIAL_TIMEOUT = 2 # serial timeout (avoid blocking in case of issues) __PORT_INDEX_TO_OPEN = 0 __BAUD_RATE = 57600 # ************ END CONSTANTS DEFINITION **************** # ************ BEGIN PRIVATE FIELDS DEFINITION **************** # asip: The client for the asip protocol # __ser_conn: self board uses serial communication __ports = [] # serial ports array __threads = [] # List of threads # ************ END PRIVATE FIELDS DEFINITION **************** # Self constructor find the name of an active serial port and it creates a Serial objted def __init__(self): # Serial connection creation, AsipClient object creation try: self.__ser_conn = Serial() self.serial_port_finder(self.__PORT_INDEX_TO_OPEN) sys.stdout.write("Setting Serial: attempting to open {}\n".format(self.__ports[self.__PORT_INDEX_TO_OPEN])) self.open_serial(self.__ports[self.__PORT_INDEX_TO_OPEN], self.__BAUD_RATE) sys.stdout.write("Setting Serial: serial port {} opened\n".format(self.__ports[self.__PORT_INDEX_TO_OPEN])) self.asip = AsipClient(self.SimpleWriter(self.__ser_conn, self.DEBUG)) except serial.SerialException as e: sys.stdout.write("Exception while init serial connection: {}\n".format(e)) sys.exit(1) # Listener creation try: self.__threads.append(self.ListenerThread(self.asip, self.__ser_conn, self.DEBUG)) sys.stdout.write("Creating Threads: starting\n") self.__threads[0].start() while not self.__threads[0].is_alive(): # checking that listener is alive pass sys.stdout.write("Creating Threads: all threads created and alive\n") except Exception as e: sys.stdout.write("Caught exception in threads launch: {}\n".format(e)) self.thread_killer() sys.exit(1) else: # Running try: # TODO: version checking still mis # flag will be set to true when valid version message is received # while self.asip.isVersionOk() == False: # self.asip.request_info() # time.sleep(1.0)sing # Checking mapping while not self.asip.check_mapping(): self.asip.request_port_mapping() time.sleep(0.5) self.asip.set_auto_report_interval(100) sys.stdout.write("Creating Threads: Mapping received, auto-report interval set to 0. Running now!\n") except KeyboardInterrupt: # KeyboardInterrupt handling in order to close every thread correctly sys.stdout.write("KeyboardInterrupt while checking mapping. Attempting to close listener thread.\n") self.thread_killer() sys.exit() except Exception as e: # killing threads and exiting in case of generic exception sys.stdout.write("Caught generic exception while checking mapping: {}\n".format(e)) self.thread_killer() sys.exit(1) # ************ BEGIN PUBLIC METHODS ************* # stops and wait for the join for threads in the given pool # TODO: improve in case of timeout of the join def thread_killer(self): for i in self.__threads: try: i.stopper() sys.stdout.write("Killing Threads: event for {} successfully set\n".format(i)) except Exception as e: sys.stdout.write("Caught exception while stropping thread {}.\nException is: {}\n".format(i, e)) time.sleep(0.5) sys.stdout.write("Killing Threads: waiting for join\n") for i in self.__threads: i.join() sys.stdout.write("Killing Threads: thread {} successfully closed\n".format(i)) self.__threads = [] sys.stdout.write("All threads terminated.\n") return True def get_asip_client(self): return self.asip # ************ END PUBLIC METHODS ************* # ************ BEGIN PRIVATE METHODS ************* def open_serial(self, port, baud_rate): if self.__ser_conn.isOpen(): self.__ser_conn.close() self.__ser_conn.port = port self.__ser_conn.baudrate = baud_rate self.__ser_conn.timeout = self.__SERIAL_TIMEOUT self.__ser_conn.open() # Toggle DTR to reset Arduino self.__ser_conn.setDTR(False) time.sleep(1) # toss any data already received, see self.__ser_conn.flushInput() time.sleep(1) self.__ser_conn.setDTR(True) time.sleep(1) def close_serial(self): self.__ser_conn.close() # This methods retrieves the operating system and set the Arduino serial port """Lists serial ports :raises EnvironmentError: On unsupported or unknown platforms :returns: A list of available serial ports """ # TODO: test needed for linux and windows implementation # TODO: improve try except def serial_port_finder(self, desired_index): system = sys.platform if system.startswith('win'): temp_ports = ['COM' + str(i + 1) for i in range(255)] elif system.startswith('linux'): # this is to exclude your current terminal "/dev/tty" temp_ports = glob.glob('/dev/tty[A-Za-z]*') elif system.startswith('darwin'): temp_ports = glob.glob('/dev/tty.usbmodem*') cp2104 = glob.glob('/dev/tty.SLAB_USBtoUART') # append usb to serial converter cp2104 ft232rl = glob.glob('/dev/tty.usbserial-A9MP5N37') # append usb to serial converter ft232rl fth = glob.glob('/dev/tty.usbserial-FTHI5TLH') # append usb to serial cable #new = glob.glob('/dev/tty.usbmodemfd121') #temp_ports = glob.glob('/dev/tty.SLAB_USBtoUART') #temp_ports = glob.glob('/dev/tty.usbserial-A9MP5N37') if cp2104 is not None: temp_ports += cp2104 if ft232rl is not None: temp_ports += ft232rl if fth is not None: temp_ports += fth #if new is not None: # FIXME: REMOVE!!! Only used for tests # temp_ports = new else: raise EnvironmentError('Unsupported platform') for port in temp_ports: try: self.__ser_conn.port = port self.__ser_conn.open() self.__ser_conn.close() self.__ports.append(port) if len(self.__ports) > desired_index: return # we have found the desired port except serial.SerialException: pass if self.DEBUG: sys.stdout.write("DEBUG: available ports are {}\n".format(self.__ports)) # ************ END PRIVATE METHODS ************* # ************ BEGIN PRIVATE CLASSES ************* # As described above, SimpleSerialBoard writes messages to the serial port. # inner class SimpleWriter implements abstract class AsipWriter: class SimpleWriter(AsipWriter): def __init__(self, ser_conn, debug=False): self.ser_conn = ser_conn self.DEBUG = debug # val is a string # TODO: improve try catch, add exit in case of exception too def write(self, val): if self.ser_conn.isOpen(): try: temp = val.encode() self.ser_conn.write(temp) if self.DEBUG: sys.stdout.write("DEBUG: just wrote in serial {}\n".format(temp)) except (OSError, serial.SerialException) as e: sys.stdout.write("Caught exception in serial write: {}\n".format(e)) else: raise serial.SerialException # ListenerThread read the serial stream and call process_input class ListenerThread(Thread): # overriding constructor def __init__(self, asip, ser_conn, debug=False): Thread.__init__(self) self.asip = asip self.ser_conn = ser_conn self.DEBUG = debug self._stopper = threading.Event() sys.stdout.write("Listener Thread: thread process created.\n") # if needed, kill will stops the loop inside run method def stopper(self): sys.stdout.write("Listener Thread: now stopping.\n") self._stopper.set() # overriding run method, thread activity def run(self): time.sleep(2) # TODO: maybe reduce this sleep? sys.stdout.write("Listener Thread: now running.\n") ser_buffer = "" while not self._stopper.is_set(): try: c = self.ser_conn.read() # attempt to read a character from Serial c = c.decode('utf-8', errors='ignore') if len(c) == 0: # was anything read? pass else: # if self.DEBUG: # sys.stdout.write("DEBUG: Char from serial: {}\n".format(c)) if c == '\n' or c == '\n': if len(ser_buffer) > 0: ser_buffer += '\n' self.asip.process_input(ser_buffer) if self.DEBUG: sys.stdout.write("DEBUG: Complete message from serial: {}\n".format(ser_buffer)) ser_buffer = "" else: ser_buffer += c except serial.SerialTimeoutException: continue # Go to next iteration in case of serial timeout except serial.SerialException as e: sys.stdout.write( "Caught SerialException in serial read: {}\nListener Thread will now stop\n".format(e)) self.stopper() except Exception as e: sys.stdout.write("Caught exception: {}\nListener Thread will NOT stop\n".format(e)) #self.stopper() sys.stdout.write("Listener Thread: stopped\n")
def __init__(self, broker_ip, target_board, my_name="Client", keepalive=180): # setting mqtt connection and asip protocol try: sys.stdout.write( "Setting mqtt: attempting to connect to broker {}\n".format( broker_ip)) self.__Broker = broker_ip self.__keepalive = keepalive self.__SUBTOPIC = "asip/" + target_board + "/in" self.__PUBTOPIC = "asip/" + target_board + "/out" self.mqtt_client = mqtt.Client(my_name) self.mqtt_client.on_connect = self.on_connect self.connect() self.asip = AsipClient( self.SimpleMQTTWriter(self.mqtt_client, self.__PUBTOPIC, self.DEBUG)) except Exception as e: sys.stdout.write( "Exception caught in init mqtt and asip protocols: {}\n". format(e)) try: # try to close connection self.disconnect() except Exception as e: sys.stdout.write( "Caught generic exception while disconnecting MQTT: {}\n". format(e)) finally: sys.exit(1) # Listener creation try: self.__threads.append( self.ListenerThread(self.asip, self.mqtt_client, self.__SUBTOPIC, self.DEBUG)) sys.stdout.write("Creating Threads: starting\n") self.__threads[0].start() while not self.__threads[0].is_alive( ): # checking that listener is alive pass sys.stdout.write( "Creating Threads: all threads created and alive\n") self.mqtt_client.loop_start() # starting mqtt loop except Exception as e: sys.stdout.write( "Caught exception in threads launch: {}\n".format(e)) self.thread_killer() sys.exit(1) else: # Running try: # TODO: version checking still missing # flag will be set to true when valid version message is received # while self.asip.isVersionOk() == False: # self.asip.request_info() # time.sleep(1.0) # Checking mapping while not self.asip.check_mapping(): self.asip.request_port_mapping() time.sleep(0.5) self.asip.set_auto_report_interval(0) sys.stdout.write( "Creating Threads: Mapping received, auto-report interval set to 0. Running now!\n" ) # KeyboardInterrupt handling in order to close every thread correctly except KeyboardInterrupt: # KeyboardInterrupt handling in order to close every thread correctly sys.stdout.write( "KeyboardInterrupt while checking mapping. Attempting to close listener thread.\n" ) self.thread_killer() sys.exit() except Exception as e: # killing threads and exiting in case of generic exception sys.stdout.write( "Caught generic exception while checking mapping: {}\n". format(e)) self.thread_killer() sys.exit(1)
class MQTTBoard: # ************ BEGIN CONSTANTS DEFINITION **************** DEBUG = True # Activates debug messages __TCP_port = 1883 # MQTT registered port # ************ END CONSTANTS DEFINITION **************** # ************ BEGIN PRIVATE FIELDS DEFINITION **************** # asip: The client for the asip protocol # mqtt_client: self board uses mqtt __threads = [] # List of threads # ************ END PRIVATE FIELDS DEFINITION **************** # self constructor takes the broker address and the target board # client name and keepalive are optional parameters def __init__(self, broker_ip, target_board, my_name="Client", keepalive=180): # setting mqtt connection and asip protocol try: sys.stdout.write( "Setting mqtt: attempting to connect to broker {}\n".format( broker_ip)) self.__Broker = broker_ip self.__keepalive = keepalive self.__SUBTOPIC = "asip/" + target_board + "/in" self.__PUBTOPIC = "asip/" + target_board + "/out" self.mqtt_client = mqtt.Client(my_name) self.mqtt_client.on_connect = self.on_connect self.connect() self.asip = AsipClient( self.SimpleMQTTWriter(self.mqtt_client, self.__PUBTOPIC, self.DEBUG)) except Exception as e: sys.stdout.write( "Exception caught in init mqtt and asip protocols: {}\n". format(e)) try: # try to close connection self.disconnect() except Exception as e: sys.stdout.write( "Caught generic exception while disconnecting MQTT: {}\n". format(e)) finally: sys.exit(1) # Listener creation try: self.__threads.append( self.ListenerThread(self.asip, self.mqtt_client, self.__SUBTOPIC, self.DEBUG)) sys.stdout.write("Creating Threads: starting\n") self.__threads[0].start() while not self.__threads[0].is_alive( ): # checking that listener is alive pass sys.stdout.write( "Creating Threads: all threads created and alive\n") self.mqtt_client.loop_start() # starting mqtt loop except Exception as e: sys.stdout.write( "Caught exception in threads launch: {}\n".format(e)) self.thread_killer() sys.exit(1) else: # Running try: # TODO: version checking still missing # flag will be set to true when valid version message is received # while self.asip.isVersionOk() == False: # self.asip.request_info() # time.sleep(1.0) # Checking mapping while not self.asip.check_mapping(): self.asip.request_port_mapping() time.sleep(0.5) self.asip.set_auto_report_interval(0) sys.stdout.write( "Creating Threads: Mapping received, auto-report interval set to 0. Running now!\n" ) # KeyboardInterrupt handling in order to close every thread correctly except KeyboardInterrupt: # KeyboardInterrupt handling in order to close every thread correctly sys.stdout.write( "KeyboardInterrupt while checking mapping. Attempting to close listener thread.\n" ) self.thread_killer() sys.exit() except Exception as e: # killing threads and exiting in case of generic exception sys.stdout.write( "Caught generic exception while checking mapping: {}\n". format(e)) self.thread_killer() sys.exit(1) # ************ BEGIN PUBLIC METHODS ************* # stops and waits for the join for threads in the given pool # TODO: improve in case of timeout of the join def thread_killer(self): for i in self.__threads: try: i.stopper() sys.stdout.write( "Killing Threads: event for {} successfully set\n".format( i)) except Exception as e: sys.stdout.write( "Caught exception while stropping thread {}.\nException is: {}\n" .format(i, e)) time.sleep(0.5) sys.stdout.write("Killing Threads: waiting for join\n") for i in self.__threads: i.join() sys.stdout.write( "Killing Threads: thread {} successfully closed\n".format(i)) self.__threads = [] try: self.disconnect() except Exception as e: sys.stdout.write( "Caught generic exception while disconnecting MQTT: {}\n". format(e)) sys.stdout.write("All threads terminated.\n") return True def get_asip_client(self): return self.asip # ************ END PUBLIC METHODS ************* # ************ BEGIN PRIVATE METHODS ************* # The callback for when the client receives a CONNACK response from the server. def on_connect(self, client, userdata, flags, rc): if self.DEBUG: sys.stdout.write("Connected with result code: {}\n".format(rc)) def connect(self): self.mqtt_client.connect(self.__Broker, self.__TCP_port, self.__keepalive) sys.stdout.write( "Connected to MQTT broker: {} port: {} keepalive: {} .\n".format( self.__Broker, self.__TCP_port, self.__keepalive)) def disconnect(self): self.mqtt_client.disconnect() sys.stdout.write("Disconnected from MQTT broker.\n") # ************ END PRIVATE METHODS ************* # ************ BEGIN PRIVATE CLASSES ************* # SimpleMQTTBoard writes messages to the MQTT stream. # inner class SimpleMQTTWriter implements abstract class AsipWriter: class SimpleMQTTWriter(AsipWriter): def __init__(self, mqtt_client, pubtopic, debug=False): self.mqtt_client = mqtt_client self.pubtopic = pubtopic self.DEBUG = debug # val is a string # TODO: improve try catch def write(self, val): # TODO: insert a way to check weather the connection is still open or not try: self.mqtt_client.publish(self.pubtopic, val) if self.DEBUG: sys.stdout.write("DEBUG: sent {}\n".format(val)) except Exception as e: sys.stdout.write( "Caught exception in MQTT publish: {}\n".format(e)) # ListenerThread read the mqtt stream and call process_input class ListenerThread(Thread): # overriding constructor def __init__(self, asip, mqtt_client, subtopic, debug=False): Thread.__init__(self) self.asip = asip self.mqtt_client = mqtt_client self.mqtt_client.on_message = self.on_message # callback self.DEBUG = debug self.listener_buffer = "" self.mqtt_client.subscribe(topic=subtopic) sys.stdout.write( "Listener Thread: subscribed to topic: {} .\n".format( subtopic)) self._stopper = threading.Event() sys.stdout.write("Listener Thread: thread process created.\n") # if needed, kill will stops the loop inside run method def stopper(self): sys.stdout.write("Listener Thread: now stopping.\n") self._stopper.set() # The callback for when a PUBLISH message is received from the server. def on_message(self, client, userdata, msg): try: if not msg.payload: pass else: data = msg.payload.decode('utf-8') if data != '\r' and data != '\n' and data != ' ': # ignore empty lines self.listener_buffer += data if self.DEBUG: sys.stdout.write("DEBUG: Received {}\n".format(data)) except Exception as e: sys.stdout.write( "Exception in listener on_message method: {}\nListener will now stop\n" .format(e)) self.stopper() # overriding run method, thread activity def run(self): time.sleep(2) # TODO: maybe reduce this sleep? sys.stdout.write("Listener Thread: now running.\n") while not self._stopper.is_set(): time.sleep(0.001) # TODO: thread concurrency try: # If there is at least one newline, we need to process the message # (the buffer may contain previous characters). while "\n" in self.listener_buffer: temp = self.listener_buffer[0:self.listener_buffer. index("\n")] self.asip.process_input(temp) self.listener_buffer = self.listener_buffer[ self.listener_buffer.index("\n") + 1:] except Exception as e: sys.stdout.write( "Exception in listener run method: {}\nListener will now stop\n" .format(e)) self.stopper() sys.stdout.write("Listener Thread: stopped\n")
def __init__(self): # Serial connection creation, AsipClient object creation try: self.__ser_conn = Serial() self.serial_port_finder(self.__PORT_INDEX_TO_OPEN) sys.stdout.write("Setting Serial: attempting to open {}\n".format( self.__ports[self.__PORT_INDEX_TO_OPEN])) self.open_serial(self.__ports[self.__PORT_INDEX_TO_OPEN], self.__BAUD_RATE) sys.stdout.write("Setting Serial: serial port {} opened\n".format( self.__ports[self.__PORT_INDEX_TO_OPEN])) self.asip = AsipClient( self.SimpleWriter(self.__ser_conn, self.DEBUG)) except serial.SerialException as e: sys.stdout.write( "Exception while init serial connection: {}\n".format(e)) sys.exit(1) # Listener creation try: self.__threads.append( self.ListenerThread(self.asip, self.__ser_conn, self.DEBUG)) sys.stdout.write("Creating Threads: starting\n") self.__threads[0].start() while not self.__threads[0].is_alive( ): # checking that listener is alive pass sys.stdout.write( "Creating Threads: all threads created and alive\n") except Exception as e: sys.stdout.write( "Caught exception in threads launch: {}\n".format(e)) self.thread_killer() sys.exit(1) else: # Running try: # TODO: version checking still mis # flag will be set to true when valid version message is received # while self.asip.isVersionOk() == False: # self.asip.request_info() # time.sleep(1.0)sing # Checking mapping while not self.asip.check_mapping(): self.asip.request_port_mapping() time.sleep(0.5) self.asip.set_auto_report_interval(100) sys.stdout.write( "Creating Threads: Mapping received, auto-report interval set to 0. Running now!\n" ) except KeyboardInterrupt: # KeyboardInterrupt handling in order to close every thread correctly sys.stdout.write( "KeyboardInterrupt while checking mapping. Attempting to close listener thread.\n" ) self.thread_killer() sys.exit() except Exception as e: # killing threads and exiting in case of generic exception sys.stdout.write( "Caught generic exception while checking mapping: {}\n". format(e)) self.thread_killer() sys.exit(1)
class SimpleSerialBoard: # ************ BEGIN CONSTANTS DEFINITION **************** DEBUG = False # ************ END CONSTANTS DEFINITION **************** # ************ BEGIN PRIVATE FIELDS DEFINITION **************** ser_conn = None # self board uses serial communication asip = None # The client for the aisp protocol queue = Queue( 10) # Buffer # TODO: use pipe instead of queue for better performances # FIXME: fix Queue dimension? _port = "" #serial port _ports = [] #serial ports array # ************ END PRIVATE FIELDS DEFINITION **************** # self constructor takes the name of the serial port and it creates a Serial object # Here the serial listener and the queue reader are started def __init__(self): # TODO: very simple implementation, need to improve #self.ser_conn = Serial() #self.serial_port_finder() try: # old implementation was: #self.ser_conn = Serial(port='/dev/cu.usbmodemfd121', baudrate=57600) # self.ser_conn = Serial(port=self._port, baudrate=57600) self.ser_conn = Serial() portIndexToOpen = 0 self.serial_port_finder(portIndexToOpen) print("attempting to open " + self._ports[portIndexToOpen]) self.open_serial(self._ports[0], 57600) print("port opened") self.asip = AsipClient(self.SimpleWriter(self)) except Exception as e: sys.stdout.write( "Exception: caught {} while init serial and asip protocols\n". format(e)) try: self.ListenerThread(self.queue, self.ser_conn, True, self.DEBUG).start() self.ConsumerThread(self.queue, self.asip, True, self.DEBUG).start() while self.asip.isVersionOk( ) == False: # flag will be set to true when valid version message is received self.request_info() time.sleep(1.0) self.request_port_mapping() #time.sleep(1) except Exception as e: #TODO: improve exception handling sys.stdout.write( "Exception: caught {} while launching threads\n".format(e)) # ************ BEGIN PUBLIC METHODS ************* # The following methods are just a replica from the asip class. # TODO: add parameter checikng in each function (raise exception?) def digital_read(self, pin): return self.asip.digital_read(pin) def analog_read(self, pin): return self.asip.analog_read(pin) def set_pin_mode(self, pin, mode): self.asip.set_pin_mode(pin, mode) def digital_write(self, pin, value): self.asip.digital_write(pin, value) def analog_write(self, pin, value): self.asip.analog_write(pin, value) def request_info(self): self.asip.request_info() def request_port_mapping(self): self.asip.request_port_mapping() def set_auto_report_interval(self, interval): self.asip.set_auto_report_interval(interval) def add_service(self, service_id, asip_service): self.asip.add_service(service_id, asip_service) def get_asip_client(self): return self.asip # ************ END PUBLIC METHODS ************* # ************ BEGIN PRIVATE METHODS ************* def open_serial(self, port, baudrate): if self.ser_conn.isOpen(): self.ser_conn.close() self.ser_conn.port = port self.ser_conn.baudrate = baudrate self.ser_conn.open() # Toggle DTR to reset Arduino self.ser_conn.setDTR(False) time.sleep(1) # toss any data already received, see self.ser_conn.flushInput() self.ser_conn.setDTR(True) def close_serial(self): self.ser_conn.close() # This methods retrieves the operating system and set the Arduino serial port """Lists serial ports :raises EnvironmentError: On unsupported or unknown platforms :returns: A list of available serial ports """ # TODO: test needed for linux and windows implementation def serial_port_finder(self, desiredIndex): #system = platform.system() # if self.DEBUG: # sys.stdout.write("DEBUG: detected os is {}\n".format(system)) # if 'linux' in system: # pass # elif 'Darwin' == system: # also 'mac' or 'darwin' may work? # for file in os.listdir("/dev"): # if file.startswith("tty.usbmodem"): # self._port = "/dev/" + file # if self.DEBUG: # sys.stdout.write("DEBUG: serial file is {}\n".format(file)) # break # elif ('win' in system) or ('Win' in system) or ('cygwin' in system) or ('nt' in system): # pass # else: # raise EnvironmentError('Unsupported platform') # if self.DEBUG: # sys.stdout.write("DEBUG: port is {}\n".format(self._port)) system = sys.platform if system.startswith('win'): temp_ports = ['COM' + str(i + 1) for i in range(255)] elif system.startswith('linux'): # this is to exclude your current terminal "/dev/tty" temp_ports = glob.glob('/dev/tty[A-Za-z]*') elif system.startswith('darwin'): temp_ports = glob.glob('/dev/tty.usbmodem*') else: raise EnvironmentError('Unsupported platform') for port in temp_ports: try: self.ser_conn.port = port s = self.ser_conn.open() self.ser_conn.close() self._ports.append(port) if (len(self._ports) > desiredIndex): return # we have found the desired port except serial.SerialException: pass if self.DEBUG: sys.stdout.write("DEBUG: available ports are {}\n".format( self._ports)) # ************ END PRIVATE METHODS ************* # ************ BEGIN PRIVATE CLASSES ************* # As described above, SimpleSerialBoard writes messages to the serial port. # inner class SimpleWriter implements abstract class AsipWriter: class SimpleWriter(AsipWriter): parent = None def __init__(self, parent): self.parent = parent # val is a string # TODO: improve try catch def write(self, val): #print(val), if self.parent.ser_conn.isOpen(): try: self.parent.ser_conn.write(val.encode()) except (OSError, serial.SerialException): pass else: raise serial.SerialException # ListenerThread and ConsumerThread are implemented following the Producer/Consumer pattern # A class for a listener that rad the serial stream and put incoming messages on a queue # TODO: implement try catch class ListenerThread(Thread): queue = None ser_conn = None running = False DEBUG = False # overriding constructor def __init__(self, queue, ser_conn, running, debug): Thread.__init__(self) self.queue = queue self.ser_conn = ser_conn self.running = running self.DEBUG = debug if self.DEBUG: sys.stdout.write("DEBUG: serial thread process created \n") # if needed, kill will stops the loop inside run method def kill(self): self.running = False # overriding run method, thread activity def run(self): temp_buff = "" time.sleep(2) # TODO: implement ser.inWaiting() >= minMsgLen to check number of char in the receive buffer? while self.running: if self.DEBUG: sys.stdout.write( "DEBUG: Temp buff is now {}\n".format(temp_buff)) val = self.ser_conn.readline() if self.DEBUG: sys.stdout.write( "DEBUG: val value when retrieving from serial is {}\n". format(val)) val = val.decode('utf-8') if self.DEBUG: sys.stdout.write( "DEBUG: val value after decode is {}".format(val)) if val is not None and val != "\n": if "\n" in val: # If there is at least one newline, we need to process # the message (the buffer may contain previous characters). while ("\n" in val and len(val) > 0): # But remember that there could be more than one newline in the buffer temp_buff += (val[0:val.index("\n")]) self.queue.put(temp_buff) if self.DEBUG: sys.stdout.write( "DEBUG: Serial produced {}\n".format( temp_buff)) temp_buff = "" val = val[val.index("\n") + 1:] if self.DEBUG: sys.stdout.write( "DEBUG: Now val is {}\n".format(val)) if len(val) > 0: temp_buff = val if self.DEBUG: sys.stdout.write( "DEBUG: After internal while buffer is {}\n". format(temp_buff)) else: temp_buff += val if self.DEBUG: sys.stdout.write( "DEBUG: else case, buff is equal to val, so they are {}\n" .format(temp_buff)) # A class that reads the queue and launch the processInput method of the AispClient. class ConsumerThread(Thread): queue = None asip = None running = False DEBUG = False # overriding constructor def __init__(self, queue, asip, running, debug): Thread.__init__(self) self.queue = queue self.asip = asip self.running = running self.DEBUG = debug if self.DEBUG: sys.stdout.write("DEBUG: consumer thread created \n") # if needed, kill will stops the loop inside run method def kill(self): self.running = False # overriding run method, thread activity def run(self): # global _queue # global asip while self.running: temp = self.queue.get() self.asip.process_input(temp) self.queue.task_done()
class SimpleSerialBoard: # ************ BEGIN CONSTANTS DEFINITION **************** DEBUG = True # ************ END CONSTANTS DEFINITION **************** # ************ BEGIN PRIVATE FIELDS DEFINITION **************** ser_conn = None # self board uses serial communication asip = None # The client for the aisp protocol queue = Queue(10) # Buffer # TODO: use pipe instead of queue for better performances # FIXME: fix Queue dimension? _port = "" #serial port _ports = [] #serial ports array __running = False # ************ END PRIVATE FIELDS DEFINITION **************** # self constructor takes the name of the serial port and it creates a Serial object # Here the serial listener and the queue reader are started def __init__(self): # TODO: very simple implementation, need to improve #self.ser_conn = Serial() #self.serial_port_finder() try: # old implementation was: #self.ser_conn = Serial(port='/dev/cu.usbmodemfd121', baudrate=57600) # self.ser_conn = Serial(port=self._port, baudrate=57600) self.ser_conn = Serial() portIndexToOpen = 0 self.serial_port_finder(portIndexToOpen) sys.stdout.write("attempting to open {}\n".format(self._ports[portIndexToOpen])) self.open_serial(self._ports[0], 57600) sys.stdout.write("port opened\n") self.asip = AsipClient(self.SimpleWriter(self)) except Exception as e: sys.stdout.write("Exception: caught {} while init serial and asip protocols\n".format(e)) try: self.__running = True self.ListenerThread(self.queue, self.ser_conn, self.__running, self.DEBUG).start() self.ConsumerThread(self.queue, self.asip, self.__running, self.DEBUG).start() self.KeyboardListener(self).start() print("****** I am here ******") #while self.asip.isVersionOk() == False: # flag will be set to true when valid version message is received #self.request_info() #time.sleep(1.0) self.request_port_mapping() time.sleep(1) self.request_port_mapping() time.sleep(1) while not self.asip.check_mapping(): self.request_port_mapping() time.sleep(0.1) print("**** Everything check ****") except Exception as e: #TODO: improve exception handling sys.stdout.write("Exception: caught {} while launching threads\n".format(e)) # ************ BEGIN PUBLIC METHODS ************* # The following methods are just a replica from the asip class. # TODO: add parameter checikng in each function (raise exception?) def digital_read(self, pin): return self.asip.digital_read(pin) def analog_read(self, pin): return self.asip.analog_read(pin) def set_pin_mode(self, pin, mode): self.asip.set_pin_mode(pin, mode) def digital_write(self, pin, value): self.asip.digital_write(pin, value) def analog_write(self, pin, value): self.asip.analog_write(pin, value) def request_info(self): self.asip.request_info() def request_port_mapping(self): self.asip.request_port_mapping() def set_auto_report_interval(self, interval): self.asip.set_auto_report_interval(interval) def add_service(self, service_id, asip_service): self.asip.add_service(service_id, asip_service) def get_asip_client(self): return self.asip # ************ END PUBLIC METHODS ************* # ************ BEGIN PRIVATE METHODS ************* def open_serial(self, port, baudrate): if self.ser_conn.isOpen(): self.ser_conn.close() self.ser_conn.port = port self.ser_conn.baudrate = baudrate # self.ser_conn.timeout = None # 0 or None? self.ser_conn.open() # Toggle DTR to reset Arduino self.ser_conn.setDTR(False) time.sleep(1) # toss any data already received, see self.ser_conn.flushInput() self.ser_conn.setDTR(True) def close_serial(self): self.ser_conn.close() # This methods retrieves the operating system and set the Arduino serial port """Lists serial ports :raises EnvironmentError: On unsupported or unknown platforms :returns: A list of available serial ports """ # TODO: test needed for linux and windows implementation def serial_port_finder(self, desiredIndex): #system = platform.system() # if self.DEBUG: # sys.stdout.write("DEBUG: detected os is {}\n".format(system)) # if 'linux' in system: # pass # elif 'Darwin' == system: # also 'mac' or 'darwin' may work? # for file in os.listdir("/dev"): # if file.startswith("tty.usbmodem"): # self._port = "/dev/" + file # if self.DEBUG: # sys.stdout.write("DEBUG: serial file is {}\n".format(file)) # break # elif ('win' in system) or ('Win' in system) or ('cygwin' in system) or ('nt' in system): # pass # else: # raise EnvironmentError('Unsupported platform') # if self.DEBUG: # sys.stdout.write("DEBUG: port is {}\n".format(self._port)) system = sys.platform if system.startswith('win'): temp_ports = ['COM' + str(i + 1) for i in range(255)] elif system.startswith('linux'): # this is to exclude your current terminal "/dev/tty" temp_ports = glob.glob('/dev/tty[A-Za-z]*') elif system.startswith('darwin'): temp_ports = glob.glob('/dev/tty.usbmodem*') cp2104 = glob.glob('/dev/tty.SLAB_USBtoUART') # append usb to serial converter cp2104 ft232rl = glob.glob('/dev/tty.usbserial-A9MP5N37') # append usb to serial converter ft232rl fth = glob.glob('/dev/tty.usbserial-FTHI5TLH') # append usb to serial cable # new = glob.glob('/dev/tty.usbmodemfa131') #temp_ports = glob.glob('/dev/tty.SLAB_USBtoUART') #temp_ports = glob.glob('/dev/tty.usbserial-A9MP5N37') if cp2104 is not None: temp_ports += cp2104 if ft232rl is not None: temp_ports += ft232rl if fth is not None: temp_ports += fth #if new is not None: # FIXME: REMOVE!!! Only used for tests # temp_ports = new else: raise EnvironmentError('Unsupported platform') for port in temp_ports: try: self.ser_conn.port = port s = self.ser_conn.open() self.ser_conn.close() self._ports.append(port) if(len(self._ports) > desiredIndex): return # we have found the desired port except serial.SerialException: pass if self.DEBUG: sys.stdout.write("DEBUG: available ports are {}\n".format(self._ports)) # ************ END PRIVATE METHODS ************* # ************ BEGIN PRIVATE CLASSES ************* # As described above, SimpleSerialBoard writes messages to the serial port. # inner class SimpleWriter implements abstract class AsipWriter: class SimpleWriter(AsipWriter): parent = None def __init__(self, parent): self.parent = parent # val is a string # TODO: improve try catch def write(self, val): #print(val), if self.parent.ser_conn.isOpen(): try: temp = val.encode() self.parent.ser_conn.write(temp) if self.parent.DEBUG: sys.stdout.write("DEBUG: just wrote in serial {}\n".format(temp)) except (OSError, serial.SerialException): pass else: raise serial.SerialException class KeyboardListener(Thread): def __init__(self, parent): Thread.__init__(self) self.parent = parent self.running = True # if needed, kill will stops the loop inside run method def kill(self): self.running = False def run(self): while self.running: if self.heardEnter(): sys.stdout.write("*** Closing ***hty\n") self.parent.__running = False time.sleep(0.5) self.parent.close_serial() self.running = False def heardEnter(self): i,o,e = select.select([sys.stdin],[],[],0.0001) for s in i: if s == sys.stdin: input = sys.stdin.readline() return True return False # ListenerThread and ConsumerThread are implemented following the Producer/Consumer pattern # A class for a listener that rad the serial stream and put incoming messages on a queue # TODO: implement try catch class ListenerThread(Thread): queue = None ser_conn = None running = False DEBUG = False # overriding constructor def __init__(self, queue, ser_conn, running, debug): Thread.__init__(self) self.queue = queue self.ser_conn = ser_conn self.running = running self.DEBUG = debug if self.DEBUG: sys.stdout.write("DEBUG: serial thread process created \n") # if needed, kill will stops the loop inside run method def kill(self): self.running = False # overriding run method, thread activity def run(self): temp_buff = "" time.sleep(2) # TODO: implement ser.inWaiting() >= minMsgLen to check number of char in the receive buffer? serBuffer = "" while self.running: # #if self.DEBUG: # # sys.stdout.write("DEBUG: Temp buff is now {}\n".format(temp_buff)) # time.sleep(0.1) # val = self.ser_conn.readline() # #val = self.ser_conn.read() # if self.DEBUG: # sys.stdout.write("DEBUG: val value when retrieving from serial is {}\n".format(val)) # val = val.decode('utf-8', errors= 'ignore') # if self.DEBUG: # sys.stdout.write("DEBUG: val value after decode is {}".format(val)) # if val is not None and val!="\n" and val!=" ": # if "\n" in val: # # If there is at least one newline, we need to process # # the message (the buffer may contain previous characters). # # while ("\n" in val and len(val) > 0): # # But remember that there could be more than one newline in the buffer # temp_buff += (val[0:val.index("\n")]) # self.queue.put(temp_buff) # if self.DEBUG: # sys.stdout.write("DEBUG: Serial produced {}\n".format(temp_buff)) # temp_buff = "" # val = val[val.index("\n")+1:] # if self.DEBUG: # sys.stdout.write("DEBUG: Now val is {}\n".format(val)) # if len(val)>0: # temp_buff = val # if self.DEBUG: # sys.stdout.write("DEBUG: After internal while buffer is {}\n".format(temp_buff)) # else: # temp_buff += val # if self.DEBUG: # sys.stdout.write("DEBUG: else case, buff is equal to val, so they are {}\n".format(temp_buff)) try: while True: c = self.ser_conn.read() # attempt to read a character from Serial c = c.decode('utf-8', errors= 'ignore') #was anything read? if len(c) == 0: break # check if character is a delimiter if c == '\r': c = '' # ignore CR elif c == '\n': serBuffer += "\n" # add the newline to the buffer if self.DEBUG: sys.stdout.write("Serial buffer is now {}\n".format(serBuffer)) self.queue.put(serBuffer) serBuffer = '' # empty the buffer else: #print("Try to print: {}".format(c)) serBuffer += c # add to the buffer except (OSError, serial.SerialException): self.running = False sys.stdout.write("Serial Exception in listener\n") # A class that reads the queue and launch the processInput method of the AispClient. class ConsumerThread(Thread): queue = None asip = None running = False DEBUG = False # overriding constructor def __init__(self, queue, asip, running, debug): Thread.__init__(self) self.queue = queue self.asip = asip self.running = running self.DEBUG = debug if self.DEBUG: sys.stdout.write("DEBUG: consumer thread created \n") # if needed, kill will stops the loop inside run method def kill(self): self.running = False # overriding run method, thread activity def run(self): # global _queue # global asip while self.running: temp = self.queue.get() self.asip.process_input(temp) self.queue.task_done() # if temp == "\n": # print("WARNING") # print ("Consumed", temp) # ************ END PRIVATE CLASSES *************
def __init__(self, broker_ip, target_board, my_name="Client", keepalive=180): # setting mqtt connection and asip protocol try: sys.stdout.write("Setting mqtt: attempting to connect to broker {}\n".format(broker_ip)) self.__Broker = broker_ip self.__keepalive = keepalive self.__SUBTOPIC = "asip/"+target_board+"/in" self.__PUBTOPIC = "asip/"+target_board+"/out" self.mqtt_client = mqtt.Client(my_name) self.mqtt_client.on_connect = self.on_connect self.connect() self.asip = AsipClient(self.SimpleMQTTWriter(self.mqtt_client, self.__PUBTOPIC, self.DEBUG)) except Exception as e: sys.stdout.write("Exception caught in init mqtt and asip protocols: {}\n".format(e)) try: # try to close connection self.disconnect() except Exception as e: sys.stdout.write("Caught generic exception while disconnecting MQTT: {}\n".format(e)) finally: sys.exit(1) # Listener creation try: self.__threads.append(self.ListenerThread( self.asip, self.mqtt_client, self.__SUBTOPIC, self.DEBUG)) sys.stdout.write("Creating Threads: starting\n") self.__threads[0].start() while not self.__threads[0].is_alive(): # checking that listener is alive pass sys.stdout.write("Creating Threads: all threads created and alive\n") self.mqtt_client.loop_start() # starting mqtt loop except Exception as e: sys.stdout.write("Caught exception in threads launch: {}\n".format(e)) self.thread_killer() sys.exit(1) else: # Running try: # TODO: version checking still missing # flag will be set to true when valid version message is received # while self.asip.isVersionOk() == False: # self.asip.request_info() # time.sleep(1.0) # Checking mapping while not self.asip.check_mapping(): self.asip.request_port_mapping() time.sleep(0.5) self.asip.set_auto_report_interval(0) sys.stdout.write("Creating Threads: Mapping received, auto-report interval set to 0. Running now!\n") # KeyboardInterrupt handling in order to close every thread correctly except KeyboardInterrupt: # KeyboardInterrupt handling in order to close every thread correctly sys.stdout.write("KeyboardInterrupt while checking mapping. Attempting to close listener thread.\n") self.thread_killer() sys.exit() except Exception as e: # killing threads and exiting in case of generic exception sys.stdout.write("Caught generic exception while checking mapping: {}\n".format(e)) self.thread_killer() sys.exit(1)
class MQTTBoard: # ************ BEGIN CONSTANTS DEFINITION **************** DEBUG = True # Activates debug messages __TCP_port = 1883 # MQTT registered port # ************ END CONSTANTS DEFINITION **************** # ************ BEGIN PRIVATE FIELDS DEFINITION **************** # asip: The client for the asip protocol # mqtt_client: self board uses mqtt __threads = [] # List of threads # ************ END PRIVATE FIELDS DEFINITION **************** # self constructor takes the broker address and the target board # client name and keepalive are optional parameters def __init__(self, broker_ip, target_board, my_name="Client", keepalive=180): # setting mqtt connection and asip protocol try: sys.stdout.write("Setting mqtt: attempting to connect to broker {}\n".format(broker_ip)) self.__Broker = broker_ip self.__keepalive = keepalive self.__SUBTOPIC = "asip/"+target_board+"/in" self.__PUBTOPIC = "asip/"+target_board+"/out" self.mqtt_client = mqtt.Client(my_name) self.mqtt_client.on_connect = self.on_connect self.connect() self.asip = AsipClient(self.SimpleMQTTWriter(self.mqtt_client, self.__PUBTOPIC, self.DEBUG)) except Exception as e: sys.stdout.write("Exception caught in init mqtt and asip protocols: {}\n".format(e)) try: # try to close connection self.disconnect() except Exception as e: sys.stdout.write("Caught generic exception while disconnecting MQTT: {}\n".format(e)) finally: sys.exit(1) # Listener creation try: self.__threads.append(self.ListenerThread( self.asip, self.mqtt_client, self.__SUBTOPIC, self.DEBUG)) sys.stdout.write("Creating Threads: starting\n") self.__threads[0].start() while not self.__threads[0].is_alive(): # checking that listener is alive pass sys.stdout.write("Creating Threads: all threads created and alive\n") self.mqtt_client.loop_start() # starting mqtt loop except Exception as e: sys.stdout.write("Caught exception in threads launch: {}\n".format(e)) self.thread_killer() sys.exit(1) else: # Running try: # TODO: version checking still missing # flag will be set to true when valid version message is received # while self.asip.isVersionOk() == False: # self.asip.request_info() # time.sleep(1.0) # Checking mapping while not self.asip.check_mapping(): self.asip.request_port_mapping() time.sleep(0.5) self.asip.set_auto_report_interval(0) sys.stdout.write("Creating Threads: Mapping received, auto-report interval set to 0. Running now!\n") # KeyboardInterrupt handling in order to close every thread correctly except KeyboardInterrupt: # KeyboardInterrupt handling in order to close every thread correctly sys.stdout.write("KeyboardInterrupt while checking mapping. Attempting to close listener thread.\n") self.thread_killer() sys.exit() except Exception as e: # killing threads and exiting in case of generic exception sys.stdout.write("Caught generic exception while checking mapping: {}\n".format(e)) self.thread_killer() sys.exit(1) # ************ BEGIN PUBLIC METHODS ************* # stops and waits for the join for threads in the given pool # TODO: improve in case of timeout of the join def thread_killer(self): for i in self.__threads: try: i.stopper() sys.stdout.write("Killing Threads: event for {} successfully set\n".format(i)) except Exception as e: sys.stdout.write("Caught exception while stropping thread {}.\nException is: {}\n".format(i, e)) time.sleep(0.5) sys.stdout.write("Killing Threads: waiting for join\n") for i in self.__threads: i.join() sys.stdout.write("Killing Threads: thread {} successfully closed\n".format(i)) self.__threads = [] try: self.disconnect() except Exception as e: sys.stdout.write("Caught generic exception while disconnecting MQTT: {}\n".format(e)) sys.stdout.write("All threads terminated.\n") return True def get_asip_client(self): return self.asip # ************ END PUBLIC METHODS ************* # ************ BEGIN PRIVATE METHODS ************* # The callback for when the client receives a CONNACK response from the server. def on_connect(self, client, userdata, flags, rc): if self.DEBUG: sys.stdout.write("Connected with result code: {}\n".format(rc)) def connect(self): self.mqtt_client.connect(self.__Broker, self.__TCP_port, self.__keepalive) sys.stdout.write("Connected to MQTT broker: {} port: {} keepalive: {} .\n" .format(self.__Broker, self.__TCP_port, self.__keepalive)) def disconnect(self): self.mqtt_client.disconnect() sys.stdout.write("Disconnected from MQTT broker.\n") # ************ END PRIVATE METHODS ************* # ************ BEGIN PRIVATE CLASSES ************* # SimpleMQTTBoard writes messages to the MQTT stream. # inner class SimpleMQTTWriter implements abstract class AsipWriter: class SimpleMQTTWriter(AsipWriter): def __init__(self, mqtt_client, pubtopic, debug=False): self.mqtt_client = mqtt_client self.pubtopic = pubtopic self.DEBUG = debug # val is a string # TODO: improve try catch def write(self, val): # TODO: insert a way to check weather the connection is still open or not try: self.mqtt_client.publish(self.pubtopic, val) if self.DEBUG: sys.stdout.write("DEBUG: sent {}\n".format(val)) except Exception as e: sys.stdout.write("Caught exception in MQTT publish: {}\n".format(e)) # ListenerThread read the mqtt stream and call process_input class ListenerThread(Thread): # overriding constructor def __init__(self, asip, mqtt_client, subtopic, debug=False): Thread.__init__(self) self.asip = asip self.mqtt_client = mqtt_client self.mqtt_client.on_message = self.on_message # callback self.DEBUG = debug self.listener_buffer = "" self.mqtt_client.subscribe(topic=subtopic) sys.stdout.write("Listener Thread: subscribed to topic: {} .\n".format(subtopic)) self._stopper = threading.Event() sys.stdout.write("Listener Thread: thread process created.\n") # if needed, kill will stops the loop inside run method def stopper(self): sys.stdout.write("Listener Thread: now stopping.\n") self._stopper.set() # The callback for when a PUBLISH message is received from the server. def on_message(self, client, userdata, msg): try: if not msg.payload: pass else: data = msg.payload.decode('utf-8') if data != '\r' and data != '\n' and data != ' ': # ignore empty lines self.listener_buffer += data if self.DEBUG: sys.stdout.write("DEBUG: Received {}\n".format(data)) except Exception as e: sys.stdout.write("Exception in listener on_message method: {}\nListener will now stop\n".format(e)) self.stopper() # overriding run method, thread activity def run(self): time.sleep(2) # TODO: maybe reduce this sleep? sys.stdout.write("Listener Thread: now running.\n") while not self._stopper.is_set(): time.sleep(0.001) # TODO: thread concurrency try: # If there is at least one newline, we need to process the message # (the buffer may contain previous characters). while "\n" in self.listener_buffer: temp = self.listener_buffer[0:self.listener_buffer.index("\n")] self.asip.process_input(temp) self.listener_buffer = self.listener_buffer[self.listener_buffer.index("\n")+1:] except Exception as e: sys.stdout.write("Exception in listener run method: {}\nListener will now stop\n".format(e)) self.stopper() sys.stdout.write("Listener Thread: stopped\n") # ************ END PRIVATE CLASSES *************
def __init__(self, IPaddress): try: self.IPaddress = IPaddress sys.stdout.write( "Attempting to connect to {} and port {}\n".format( self.IPaddress, self._TCPport)) self.sock_conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock_conn.connect((self.IPaddress, self._TCPport)) self.asip = AsipClient( self.SimpleTCPWriter(self, sock_conn=self.sock_conn)) except Exception as e: sys.stdout.write( "Exception caught in init tcp socket and asip protocols {}\n". format(e)) else: worker = [] try: # NOTICE: two request_port_mapping() are required. If this method is not called two times, # the client won't be able to set the pin mapping worker.append( self.ListenerThread(self.queue, self.sock_conn, True, self.DEBUG)) worker.append( self.ConsumerThread(self.queue, self.asip, True, self.DEBUG)) for i in worker: if self.DEBUG: print("Starting {}".format(i)) i.start() all_alive = False while not all_alive: # cheching that every thread is alive # TODO: improve syntax in following line if worker[0].is_alive() and worker[1].is_alive(): all_alive = True # TODO: check following code # while self.asip.isVersionOk() == False: # flag will be set to true when valid version message is received # self.request_info() # time.sleep(1.0) active_workers = threading.active_count() sys.stdout.write("*** All threads created and alive ***\n") except Exception as e: sys.stdout.write( "Caught exception in thread launch: {}\n".format(e)) self.close_tcp_conn() self.thread_killer(worker) sys.exit(1) else: try: while not self.asip.check_mapping(): self.request_port_mapping() time.sleep(0.25) self.set_auto_report_interval(0) # checking that a thread is not killed by an exception while len(threading.enumerate()) == active_workers: pass # KeyboardInterrupt handling in order to close every thread correctly except KeyboardInterrupt: sys.stdout.write( "KeyboardInterrupt: attempting to close threads.\n") finally: # killing thread in both cases: keyboardinterrupt or exception in one of the thread self.close_tcp_conn() self.thread_killer(worker) sys.stdout.write("All terminated.\n") sys.exit()