Ejemplo n.º 1
0
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()
Ejemplo n.º 2
0
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()
Ejemplo n.º 4
0
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()