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