class Tests(unittest.TestCase): test_dict_send = { 'action': ACTIONS_FLAGS['presence'], 'time': 111111.111111, 'user': { 'account_name': 'test_test' } } test_dict_recv_ok = {'response': 200} test_dict_recv_err = {'response': 400, 'error': 'Bad Request'} def setUp(self): self.connector = Connector() self.connector.disconnect() def test_send_message(self): test_socket = TestSocket(self.test_dict_send) self.connector.socket = test_socket self.connector.send_message(self.test_dict_send) self.assertEqual(self.connector.socket.encoded_message, self.connector.socket.receved_message) with self.assertRaises(Exception): self.connector.send(test_socket) def test_get_message(self): test_sock_ok = TestSocket(self.test_dict_recv_ok) test_sock_err = TestSocket(self.test_dict_recv_err) # тест корректной расшифровки корректного словаря self.connector.socket = test_sock_ok self.assertEqual(self.connector.get_message(), self.test_dict_recv_ok) # тест корректной расшифровки ошибочного словаря self.connector.socket = test_sock_err self.assertEqual(self.connector.get_message(), self.test_dict_recv_err)
def mode(self, mode = 'hour', color = [0, 0, 0]): """ Update Dotti to the given mode Args: mode (str): either 'hour' or 'color' color (arr[int, int, int]): R,G,B color to use """ conn = Connector(self.mac) conn.connect() if not conn.isconnected: conn.connect() if not conn.isconnected: return try: if mode == 'color': conn.writeCharacteristic('0x2a', '0601'+self.__twoDigitHex(int(color[0]))+self.__twoDigitHex(int(color[1]))+self.__twoDigitHex(int(color[2]))+'00') elif mode == 'hour': conn.writeCharacteristic('0x2a', '040502') except Exception as error: logger.error(str(error)) conn.disconnect() return
class Target: ## @var config # A configurator::Configurator object ## @var connection # A connector::Connector instance used to send commands to XML-RPC connection ## @var is_slave # Shows if this is a slave target ## @var parameters # The target parameters ## @var datastream_queue # The queue where DataCollector thread puts data and observers get it ## @var datastream_poller # The DataPoller thread ## @var datastream_collector # The DataCollector thread ## @var signal_structure # The structure of the signal obtained with getSignalStructure ## @var is_connected # Checks if the Target is connected or not. Actually the status is simply set bu connect() and disconnect() ## Target initializer. def __init__(self, slave=False, protocol=None, address=None, port=None, target_name=None): self.config = Configurator() self.is_connected = False self.is_slave = slave self.parameters = None self.datastream_queue = None self.datastream_poller = None self.datastream_collector = None self.signal_structure = None # Sets basic configurations if protocol: self.config.setAttr("protocol", protocol) if address: self.config.setAttr("address", address) if port: self.config.setAttr("port", port) if target_name: self.config.setAttr("target_name", target_name) # Initializes connection self.connection = Connector(self.config) ## Prints a summary of the target and its status # TODO: Decidere che informazioni restituire def getInfo(self): print "Target name: %s" % self.config.getAttr("target_name") print "Connection status: %s" % ("Connected" if self.is_connected else "Disconnected") print "Connection request: %s://%s:%s" % ( self.config.getAttr("protocol"), self.config.getAttr("address"), self.config.getAttr("port"), ) ## Setups the queue and the threads needed to read the data stream # @param sample_time The sample time of the signal # @param decimation The decimation to use # @start_threads The flag given by startData() to let the data threads start (False if this method is called by addObserver()) # @return The boolean result of the operation def setup_data_threads(self, sample_time=None, decimation=None, start_threads=False): if self.is_slave: if not self.datastream_queue: self.datastream_queue = [] # TODO: creare un DataPoller per ogni segnale (non per tutta la traccia) e registrare gli observer sugli specifici DataPoller. if not self.datastream_poller: self.datastream_poller = DataPoller(self.datastream_queue, decimation) # TODO: Il DataCollector deve leggere il numero di segnale e usare la coda specifica per inserirci i dati if not self.datastream_collector: # TODO: Fermare i thread alla chiusura del target self.datastream_collector = DataCollector( self.config.getAttr("address"), self.config.getAttr("slave_data_port"), self.datastream_queue ) if start_threads: print ("Starting data threads...") if sample_time: self.datastream_poller.updateSampleTime(sample_time) self.datastream_poller.start() self.datastream_collector.start() print ("Data threads started...") else: print ("Can't start data threads without a sample time") return False return True else: print ("Can't setup the data stream threads on a not-slave server") return False ## Stops the data threads # @return The boolean result of the operation def stop_data_threads(self): if self.is_slave: if self.datastream_collector: print ("Shutting down DataCollector thread") self.datastream_collector.stop() self.datastream_collector.join(15.0) if self.datastream_collector.is_alive(): print ("Can't stop the DataCollector thread, timeout occurs") else: print ("DataCollector thread was shut down") if self.datastream_poller: print ("Shutting down DataPoller thread") self.datastream_poller.stop() self.datastream_poller.join(15.0) if self.datastream_poller.is_alive(): print ("Can't stop the DataPoller thread, timeout occurs") else: print ("DataPoller thread was shut down") # Clears vars self.datastream_poller = None self.datastream_collector = None self.datastream_queue = None return True else: print ("Can't stop the data stream threads on a not-slave server") return False ## Add a new observer "update" method to the list of observers. # @param update_method Is the method of the observer which will be called when new data arrives. The method name is arbitrary, but it must accept a string parameter # @param signal_number The optional channel to register the observer to # @param decimation The decimation to use # @return The ID of the observer, to be used to remove the observer def addObserver(self, update_method, decimation=None, signal_number=None): self.setup_data_threads(None, decimation) return self.datastream_poller.addObserver(update_method, signal_number) ## Removes the observer # @param observer_id The ID of the observer to remove # @return False if the observer isn't in the observers list, True if it was correctly removed def removeObserver(self, observer_id): return self.datastream_poller.removeObserver(observer_id) ## Connect to the target server. Checks if master or server and call the right method on connector::Connector # @param password The (optional) password to catch a running master connection # @return The boolean result of the operation def connect(self, password=None): if self.is_slave: if ( self.connection.connectSlave() ): # TODO: Cambiare il metodo di connessione non passando tutto il Configurator ma usando i dati passati nel costruttore di Connector self.is_connected = True self.signal_structure = self.getSignalStructure() return True else: return False else: if password: result = self.connection.Connection_Request( password ) # Passing self.config to let the Connector set some connection data (e.g. the session_id) else: result = self.connection.Connection_Request() self.is_connected = result return result ## Checks if master and if affirmative, returns the slave # @param target_name The target to connect to. If None gets the name from the default configuration # @return The slave Target or False if not a master target def getSlave(self, target_name=None): if self.is_slave: print "Can't ask for a slave to a slave. Try asking to a master." return False else: # If target is not specified, caller are requesting a "standard" connection to the primary slave target if not target_name: target_name = self.config.getAttr("target_name") slave = Target( True, self.config.getAttr("protocol"), self.config.getAttr("address"), self.config.getAttr("slave_port"), target_name, ) # Sets the session ID slave.config.setAttr("session_id", self.config.session_id) return slave ## Disconnects from the XML-RPC server # @return The boolean result of the operation def disconnect(self): if self.is_slave: result = self.connection.disconnect() self.is_connected = result return result else: print ("Can't call disconnect on master server") return False ## Stops target server. This method, called on a master server, calls connector::Connector::stopServer and called on slave, calls connector::Connection::stopSlave # @return The boolean result of the operation def stop(self): if self.is_slave: # TODO: Stop the data thread if active result = self.connection.stopSlaveServer() self.is_connected = result # Stops data threads self.stop_data_threads() return result else: result = self.connection.stopMasterServer() self.is_connected = result return result ## Closes the session # @return The boolean result of the operation def close(self): result = self.connection.closeSession() # Stops data threads self.stop_data_threads() self.is_connected = result return result ## Stops the target # This method closes the data threads (if they exist) then closes the session # @return The boolean result of the operation def halt(self): self.stop_data_threads() result = self.connection.stopTarget() self.is_connected = result return result ## Starts the connected target # @return The boolean result of the operation def start(self): return self.connection.start() ## Starts data transfer for the specified signal # @param signal_number The number of the signal to start # @param sample_time The sample time of the signal # @param decimation The decimation to use # @return The boolean result of the operation def startData(self, signal_number, decimation=None): sample_time = self.getSampleTimeFromSignalNumber(signal_number) self.setup_data_threads(sample_time, decimation, True) return self.connection.startData(signal_number, decimation) ## Returns sample time given the signal number # @param signal_number The number of the signal to analyze # @return The sample time of the given signal number def getSampleTimeFromSignalNumber(self, signal_number): try: sample_time = None for signal in self.signal_structure: if signal["number"] == signal_number: sample_time = signal["sample_time"] break return sample_time except: return None ## Stops (all) data transfers # @param signal_number The number of signal to catch # @param all_data If True calls StopAllData(). Calls StopData() if False. # @return The boolean result of the operation def stopData(self, signal_number=None, all_data=False): # TODO: Devo chiudere anche i threads dei poller? if all_data: return self.connection.stopAllData() elif signal_number: return self.connection.stopData(signal_number) else: print ("Can't stop data without signal number or all_data flag") return False ## Get the structure of the signals # @return The structure of the signals or False if the target has no signals def getSignalStructure(self): return self.connection.getSignalStructure() ## Sends new parameters to the server # @param params The array of parameters to set # @return The boolean result of the operation def setParameters(self, params): return self.connection.setParameters(params) ## Get the parameters from the target server # @return An array of params. Every param is an hash def getParameters(self): self.parameters = self.connection.getParameters() return self.parameters ## Interface to set a single param # @param identifier The identifier string to get the param to update # @param value The value to set to the param def setParam(self, identifier, value): if not self.parameters: self.parameters = self.connection.getParameters() ids = identifier.split("/") param_name = ids.pop() block_name = "/".join(ids) new_params = [] for param in self.parameters: if param["block_name"] == block_name and param["param_name"] == param_name: param["values"] = value new_params.append(param) self.parameters = new_params return self.setParameters(self.parameters)
class Client: def __init__(self): self.connector = Connector() def connect(self, host='127.0.0.1', port=11111): self.connector.connect(host, port) data = self.connector.read_next_packet() packet = CanalProtocol_pb2.Packet() packet.MergeFromString(data) if packet.type != CanalProtocol_pb2.PacketType.HANDSHAKE: raise Exception('connect error') print('connected to %s:%s' % (host, port)) def disconnect(self): self.connector.disconnect() def check_valid(self, username=b'', password=b''): client_auth = CanalProtocol_pb2.ClientAuth() client_auth.username = username client_auth.password = password packet = CanalProtocol_pb2.Packet() packet.type = CanalProtocol_pb2.PacketType.CLIENTAUTHENTICATION packet.body = client_auth.SerializeToString() self.connector.write_with_header(packet.SerializeToString()) data = self.connector.read_next_packet() packet = CanalProtocol_pb2.Packet() packet.MergeFromString(data) if packet.type != CanalProtocol_pb2.PacketType.ACK: raise Exception('Auth error') ack = CanalProtocol_pb2.Ack() ack.MergeFromString(packet.body) if ack.error_code > 0: raise Exception('something goes wrong when doing authentication. error code:%s, error message:%s' % ( ack.error_code, ack.error_message)) print('Auth succed') def subscribe(self, client_id=b'1001', destination=b'example', filter=b'.*\\..*'): self.client_id = client_id self.destination = destination self.rollback(0) sub = CanalProtocol_pb2.Sub() sub.destination = destination sub.client_id = client_id sub.filter = filter packet = CanalProtocol_pb2.Packet() packet.type = CanalProtocol_pb2.PacketType.SUBSCRIPTION packet.body = sub.SerializeToString() self.connector.write_with_header(packet.SerializeToString()) data = self.connector.read_next_packet() packet = CanalProtocol_pb2.Packet() packet.MergeFromString(data) if packet.type != CanalProtocol_pb2.PacketType.ACK: raise Exception('Subscribe error.') ack = CanalProtocol_pb2.Ack() ack.MergeFromString(packet.body) if ack.error_code > 0: raise Exception( 'Failed to subscribe. error code:%s, error message:%s' % (ack.error_code, ack.error_message)) print('Subscribe succed') def unsubscribe(self): pass def get(self, size=100): message = self.get_without_ack(size) self.ack(message['id']) return message def get_without_ack(self, batch_size=10, timeout=-1, unit=-1): get = CanalProtocol_pb2.Get() get.client_id = self.client_id get.destination = self.destination get.auto_ack = False get.fetch_size = batch_size get.timeout = timeout get.unit = unit packet = CanalProtocol_pb2.Packet() packet.type = CanalProtocol_pb2.PacketType.GET packet.body = get.SerializeToString() self.connector.write_with_header(packet.SerializeToString()) data = self.connector.read_next_packet() packet = CanalProtocol_pb2.Packet() packet.MergeFromString(data) message = dict(id=0, entries=[]) if packet.type == CanalProtocol_pb2.PacketType.MESSAGES: messages = CanalProtocol_pb2.Messages() messages.MergeFromString(packet.body) if messages.batch_id > 0: message['id'] = messages.batch_id for item in messages.messages: entry = EntryProtocol_pb2.Entry() entry.MergeFromString(item) message['entries'].append(entry) elif packet.type == CanalProtocol_pb2.PacketType.ACK: ack = CanalProtocol_pb2.PacketType.Ack() ack.MergeFromString(packet.body) if ack.error_code > 0: raise Exception('get data error. error code:%s, error message:%s' % (ack.error_code, ack.error_message)) else: raise Exception('unexpected packet type:%s' % (packet.type)) return message def ack(self, message_id): if message_id: clientack = CanalProtocol_pb2.ClientAck() clientack.destination = self.destination clientack.client_id = self.client_id clientack.batch_id = message_id packet = CanalProtocol_pb2.Packet() packet.type = CanalProtocol_pb2.PacketType.CLIENTACK packet.body = clientack.SerializeToString() self.connector.write_with_header(packet.SerializeToString()) def rollback(self, batch_id): cb = CanalProtocol_pb2.ClientRollback() cb.batch_id = batch_id cb.client_id = self.client_id cb.destination = self.destination packet = CanalProtocol_pb2.Packet() packet.type = CanalProtocol_pb2.PacketType.CLIENTROLLBACK packet.body = cb.SerializeToString() self.connector.write_with_header(packet.SerializeToString())
class Target: ## @var config # A configurator::Configurator object ## @var connection # A connector::Connector instance used to send commands to XML-RPC connection ## @var is_slave # Shows if this is a slave target ## @var parameters # The target parameters ## @var datastream_queue # The queue where DataCollector thread puts data and observers get it ## @var datastream_poller # The DataPoller thread ## @var datastream_collector # The DataCollector thread ## @var signal_structure # The structure of the signal obtained with getSignalStructure ## @var is_connected # Checks if the Target is connected or not. Actually the status is simply set bu connect() and disconnect() ## Target initializer. def __init__(self, slave = False, protocol = None, address = None, port = None, target_name = None): self.config = Configurator() self.is_connected = False self.is_slave = slave self.parameters = None self.datastream_queue = None self.datastream_poller = None self.datastream_collector = None self.signal_structure = None # Sets basic configurations if protocol: self.config.setAttr('protocol', protocol) if address: self.config.setAttr('address', address) if port: self.config.setAttr('port', port) if target_name: self.config.setAttr('target_name', target_name) # Initializes connection self.connection = Connector(self.config) ## Prints a summary of the target and its status # TODO: Decidere che informazioni restituire def getInfo(self): print "Target name: %s" % self.config.getAttr('target_name') print "Connection status: %s" % ("Connected" if self.is_connected else "Disconnected") print "Connection request: %s://%s:%s" % (self.config.getAttr('protocol'), self.config.getAttr('address'), self.config.getAttr('port')) ## Setups the queue and the threads needed to read the data stream # @param sample_time The sample time of the signal # @param decimation The decimation to use # @start_threads The flag given by startData() to let the data threads start (False if this method is called by addObserver()) # @return The boolean result of the operation def setup_data_threads(self, sample_time = None, decimation = None, start_threads = False): if self.is_slave: if not self.datastream_queue: self.datastream_queue = [] # TODO: creare un DataPoller per ogni segnale (non per tutta la traccia) e registrare gli observer sugli specifici DataPoller. if not self.datastream_poller: self.datastream_poller = DataPoller(self.datastream_queue, decimation) # TODO: Il DataCollector deve leggere il numero di segnale e usare la coda specifica per inserirci i dati if not self.datastream_collector: # TODO: Fermare i thread alla chiusura del target self.datastream_collector = DataCollector(self.config.getAttr('address'), self.config.getAttr('slave_data_port'), self.datastream_queue) if start_threads: print("Starting data threads...") if sample_time: self.datastream_poller.updateSampleTime(sample_time) self.datastream_poller.start() self.datastream_collector.start() print("Data threads started...") else: print("Can't start data threads without a sample time") return False return True else: print("Can't setup the data stream threads on a not-slave server") return False ## Stops the data threads # @return The boolean result of the operation def stop_data_threads(self): if self.is_slave: if self.datastream_collector: print("Shutting down DataCollector thread") self.datastream_collector.stop() self.datastream_collector.join(15.0) if self.datastream_collector.is_alive(): print("Can't stop the DataCollector thread, timeout occurs") else: print("DataCollector thread was shut down") if self.datastream_poller: print("Shutting down DataPoller thread") self.datastream_poller.stop() self.datastream_poller.join(15.0) if self.datastream_poller.is_alive(): print("Can't stop the DataPoller thread, timeout occurs") else: print("DataPoller thread was shut down") # Clears vars self.datastream_poller = None self.datastream_collector = None self.datastream_queue = None return True else: print("Can't stop the data stream threads on a not-slave server") return False ## Add a new observer "update" method to the list of observers. # @param update_method Is the method of the observer which will be called when new data arrives. The method name is arbitrary, but it must accept a string parameter # @param signal_number The optional channel to register the observer to # @param decimation The decimation to use # @return The ID of the observer, to be used to remove the observer def addObserver(self, update_method, decimation = None, signal_number = None): self.setup_data_threads(None, decimation) return self.datastream_poller.addObserver(update_method, signal_number) ## Removes the observer # @param observer_id The ID of the observer to remove # @return False if the observer isn't in the observers list, True if it was correctly removed def removeObserver(self, observer_id): return self.datastream_poller.removeObserver(observer_id) ## Connect to the target server. Checks if master or server and call the right method on connector::Connector # @param password The (optional) password to catch a running master connection # @return The boolean result of the operation def connect(self, password = None): if self.is_slave: if self.connection.connectSlave(): #TODO: Cambiare il metodo di connessione non passando tutto il Configurator ma usando i dati passati nel costruttore di Connector self.is_connected = True self.signal_structure = self.getSignalStructure() return True else: return False else: if password: result = self.connection.Connection_Request(password) # Passing self.config to let the Connector set some connection data (e.g. the session_id) else: result = self.connection.Connection_Request() self.is_connected = result return result ## Checks if master and if affirmative, returns the slave # @param target_name The target to connect to. If None gets the name from the default configuration # @return The slave Target or False if not a master target def getSlave(self, target_name = None): if self.is_slave: print "Can't ask for a slave to a slave. Try asking to a master." return False else: # If target is not specified, caller are requesting a "standard" connection to the primary slave target if not target_name: target_name = self.config.getAttr('target_name') slave = Target(True, self.config.getAttr('protocol'), self.config.getAttr('address'), self.config.getAttr('slave_port'), target_name) # Sets the session ID slave.config.setAttr('session_id', self.config.session_id) return slave ## Disconnects from the XML-RPC server # @return The boolean result of the operation def disconnect(self): if self.is_slave: result = self.connection.disconnect() self.is_connected = result return result else: print("Can't call disconnect on master server") return False ## Stops target server. This method, called on a master server, calls connector::Connector::stopServer and called on slave, calls connector::Connection::stopSlave # @return The boolean result of the operation def stop(self): if self.is_slave: # TODO: Stop the data thread if active result = self.connection.stopSlaveServer() self.is_connected = result # Stops data threads self.stop_data_threads() return result else: result = self.connection.stopMasterServer() self.is_connected = result return result ## Closes the session # @return The boolean result of the operation def close(self): result = self.connection.closeSession() # Stops data threads self.stop_data_threads() self.is_connected = result return result ## Stops the target # This method closes the data threads (if they exist) then closes the session # @return The boolean result of the operation def halt(self): self.stop_data_threads() result = self.connection.stopTarget() self.is_connected = result return result ## Starts the connected target # @return The boolean result of the operation def start(self): return self.connection.start() ## Starts data transfer for the specified signal # @param signal_number The number of the signal to start # @param sample_time The sample time of the signal # @param decimation The decimation to use # @return The boolean result of the operation def startData(self, signal_number, decimation = None): sample_time = self.getSampleTimeFromSignalNumber(signal_number) self.setup_data_threads(sample_time, decimation, True) return self.connection.startData(signal_number, decimation) ## Returns sample time given the signal number # @param signal_number The number of the signal to analyze # @return The sample time of the given signal number def getSampleTimeFromSignalNumber(self, signal_number): try: sample_time = None for signal in self.signal_structure: if signal['number'] == signal_number: sample_time = signal['sample_time'] break return sample_time except: return None ## Stops (all) data transfers # @param signal_number The number of signal to catch # @param all_data If True calls StopAllData(). Calls StopData() if False. # @return The boolean result of the operation def stopData(self, signal_number = None, all_data = False): # TODO: Devo chiudere anche i threads dei poller? if all_data: return self.connection.stopAllData() elif signal_number: return self.connection.stopData(signal_number) else: print("Can't stop data without signal number or all_data flag") return False ## Get the structure of the signals # @return The structure of the signals or False if the target has no signals def getSignalStructure(self): return self.connection.getSignalStructure() ## Sends new parameters to the server # @param params The array of parameters to set # @return The boolean result of the operation def setParameters(self, params): return self.connection.setParameters(params) ## Get the parameters from the target server # @return An array of params. Every param is an hash def getParameters(self): self.parameters = self.connection.getParameters() return self.parameters ## Interface to set a single param # @param identifier The identifier string to get the param to update # @param value The value to set to the param def setParam(self, identifier, value): if not self.parameters: self.parameters = self.connection.getParameters() ids = identifier.split("/") param_name = ids.pop() block_name = "/".join(ids) new_params = [] for param in self.parameters: if param['block_name'] == block_name and param['param_name'] == param_name: param['values'] = value new_params.append(param) self.parameters = new_params return self.setParameters(self.parameters)