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 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 *************
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 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()