Example #1
0
File: server.py Project: HMTL/HMTL
class HMTLServer():
    address = ('localhost', 6000)

    # Default logging color
    LOGGING_COLOR = TimedLogger.RED

    def __init__(self, serial_device, address, device_scan=False, logger=True):
        self.ser = serial_device
        self.address = address

        self.logger = TimedLogger(self.ser.serial.start_time,
                                  textcolor=self.LOGGING_COLOR)
        if not logger:
            self.logger.disable()

        self.terminate = False

         # TODO: Is this needed at all? If so should the SerialBuffer handle synchronization?
        self.serial_cv = threading.Condition()

        self.conn = None
        self.listener = None

        if device_scan:
            self.scanner = DeviceScanner(self, True)
            self.scanner.start()
        else:
            self.scanner = None

    def get_connection(self):
        try:
            self.logger.log("Waiting for connection")
            self.listener = Listener(self.address, authkey='secret password')
            self.conn = self.listener.accept()
            self.logger.log("Connection accepted from %s:%d" %
                            self.listener.last_accepted)
        except KeyboardInterrupt:
            print("Exiting")
            self.terminate = True

    def handle_msg(self, item):
        if item.data == SERVER_EXIT:
            self.logger.log("* Received exit signal *")
            self.conn.send(SERVER_ACK)
            self.close()
        elif item.data == SERVER_DATA_REQ:
            self.logger.log("* Recieved data request")
            item = self.get_data_msg()
            if item:
                self.conn.send(item.data)
            else:
                self.conn.send(None)
        else:
            # Forward the message to the device
            self.serial_cv.acquire()
            self.send_data(item.data)
            self.serial_cv.release()

            # Reply with acknowledgement
            self.conn.send(SERVER_ACK)

    def send_data(self, data):
            self.serial_cv.acquire()
            self.ser.send_and_confirm(data, False)
            self.serial_cv.release()


    # Wait for and handle incoming connections
    def listen(self):
        self.logger.log("Server started")
        self.get_connection()

        while not self.terminate:
            try:
                data = self.conn.recv()
                item = InputItem.from_data(data)

                self.logger.log("Received: %s" % item)
                self.handle_msg(item)
                self.logger.log("Acked: %s" % item)

            except (EOFError, IOError):
                # Attempt to reconnect
                self.logger.log("Lost connection")
                self.listener.close()
                self.get_connection()
            except Exception as e:
                # Close the connection on uncaught exception
                self.logger.log("Exception during listen")
                self.close()
                raise e

    def close(self):
        self.listener.close()
        if self.conn:
            self.conn.close()
        self.terminate = True

    def get_data_msg(self, timeout=0.25):
        """Listen on the serial device for a properly formatted data message"""

        self.serial_cv.acquire()
        self.logger.log("Starting data request")
        
        time_limit = time.time() + timeout
        item = None
        while True:
            # Try to get a message with low timeout
            item = self.ser.get_message(timeout=0.1)
            if item and item.is_hmtl:
                self.logger.log("Received response: %s:\n%s" %
                                (item, HMTLprotocol.decode_data(item.data)))
                break

            # Check for timeout
            if time.time() > time_limit:
                self.logger.log("Data request time limit exceeded")
                item = None
                break

        self.serial_cv.release()

        return item
Example #2
0
File: client.py Project: HMTL/HMTL
class HMTLClient():
    address = HMTLprotocol.BROADCAST
    verbose = False

    def __init__(self, address='localhost', port=6000, hmtladdress=None,
                 verbose=False, logger=True, authenticate=True):
        self.logger = TimedLogger()
        if not logger:
            self.logger.disable()

        self.address = hmtladdress
        self.verbose = verbose

        address = (address, port)
        try:
            if authenticate:
                authkey = b'secret password'
            else:
                authkey = None
            self.conn = Client(address, authkey=authkey)
        except Exception as e:
            raise Exception("Failed to connect to '%s'" % (str(address)))
        random.seed()
        print("HMTLClient initialized")

    def close(self):
        self.conn.close()

    def send(self, msg):
        if (self.verbose):
            self.logger.log(" - Sending %s" % (hexlify(msg)))

        self.conn.send(msg)

    def get_ack(self):
        msg = self.conn.recv()
        if (self.verbose):
            self.logger.log(" - Received: '%s' '%s'" % (msg, hexlify(msg)))
        if (msg == server.SERVER_ACK):
            return True
        else:
            return False

    def send_and_ack(self, msg, expect_response=False):
        self.send(msg)

        # Wait for message acknowledgement
        if self.verbose:
            self.logger.log(" - Waiting on ack")

        # Wait for an ack from the server
        has_ack = False
        while True:
            if self.get_ack():
                has_ack = True
                break

        if has_ack and expect_response:
            # If a response is expected then request the response data from the
            # server, repeating until the message indicates that there is no
            # more data.

            messages = []
            headers = []

            more_data = True
            while more_data:
                more_data = False

                # Request response data
                msg = self.get_response_data()
                messages.append(msg)

                if self.verbose:
                    if msg:
                            self.logger.log(" - Received data response: '%s':\n%s" %
                                  (hexlify(msg), HMTLprotocol.decode_data(msg)))
                    else:
                        self.logger.log(" - Failed to receive data response")

                try:
                    # Attempt to decode the message
                    decoded_msg = HMTLprotocol.msg_to_headers(msg)
                    if decoded_msg:
                        headers.append(decoded_msg)

                        # Check if the message indicate that there are
                        # additional messages expected.
                        hdr = decoded_msg[0]
                        if isinstance(hdr, HMTLprotocol.MsgHdr):
                            if hdr.more_data():
                                # Request another message
                                more_data = True
                    else:
                        headers.append(None)
                except Exception as e:
                    print("Exception decoding messages: %s" % (str(e)))
                    headers.append(None)

            return messages, headers

        return [None, None]

    def get_response_data(self):
        '''Request and attempt to retrieve response data'''
        self.conn.send(server.SERVER_DATA_REQ)
        #TODO: This should pickle some object with parameters like timeout
        msg = self.conn.recv()
        return msg
        

    def send_exit(self):
        self.send_and_ack(server.SERVER_EXIT)