def _add_method(self, mw_data, component_instance): """ Include a new serialisation method in the middleware. Parameters are the array with parameters for the middleware and the instance of the component """ # Get the variables from the data passed # Second parameter is the name of the function to add to the component function_name = mw_data[1] # Third parameter is the file where the function can be found source_file = mw_data[2] module_name = re.sub('/', '.', source_file) # Import the module containing the class func = load_module_attribute(module_name, function_name) if not func: logger.error("Check the 'component_config.py' file for typos.") return # Insert the function and get a reference to it setattr(self, func.__name__, types.MethodType(func, self)) bound_function = getattr(self, function_name) try: # Call the init method of the new serialisation # Sends the name of the function as a means to identify # what kind of port it should use. module.init_extra_module(self, component_instance, bound_function, mw_data) except AttributeError as detail: logger.error("%s in module '%s'" % (detail, source_file)) # Store the name of the function, to cleanup later # If function with the same name already included, pass. Otherwise middleware will fail to cleanup if function_name in self._extra_methods: logger.info("Extra method already known") else: self._extra_methods.append(function_name) return bound_function
def _add_method(self, mw_data, component_instance): """ Include a new serialisation method in the middleware. Parameters are the array with parameters for the middleware and the instance of the component """ # Get the variables from the data passed # Second parameter is the name of the function to add to the component function_name = mw_data[1] # Third parameter is the file where the function can be found source_file = mw_data[2] module_name = re.sub("/", ".", source_file) # Import the module containing the class func = load_module_attribute(module_name, function_name) if not func: logger.error("Check the 'component_config.py' file for typos.") return # Insert the function and get a reference to it setattr(self, func.__name__, types.MethodType(func, self)) bound_function = getattr(self, function_name) try: # Call the init method of the new serialisation # Sends the name of the function as a means to identify # what kind of port it should use. module.init_extra_module(self, component_instance, bound_function, mw_data) except AttributeError as detail: logger.error("%s in module '%s'" % (detail, source_file)) # Store the name of the function, to cleanup later # If function with the same name already included, pass. Otherwise middleware will fail to cleanup if function_name in self._extra_methods: logger.info("Extra method already known") else: self._extra_methods.append(function_name) return bound_function
def add_stream(self, datastream, method=None, path=None, classpath=None, direction=None, **kwargs): """ Add a data stream interface to the component Do the binding between a component and the method to export/import its data. This must be used in general by sensors and actuators. A single component can make several calls to this function to add bindings with more than one middleware. :param datastream: enum in ['ros', 'socket', 'yarp', 'text', 'pocolibs', 'moos'] :param classpath: if set, force to use the configuration of the given component, instead of our own (default=None). You can pass other argument to this method, they will be added as a map to the configuration. .. code-block:: python component.add_stream('ros', topic='/myrobots/data') component.add_stream('moos', moos_host='127.0.0.1', moos_port=9000, moos_name='iMorse') """ self._err_if_not_exportable() if not classpath: classpath = self.property_value("classpath") if not classpath: logger.error("%s: no classpath defined for this " "component! Check component definition " % self.name) return level = self.property_value("abstraction_level") or "default" if not direction: direction = self._compute_direction(classpath) if not direction: return config = [] # Configure the datastream for this component if not method: if not classpath in MORSE_DATASTREAM_DICT: klass = get_class(classpath) from morse.core.actuator import Actuator from morse.core.sensor import Sensor # Check if we can use default interface... if klass and \ issubclass(klass, Actuator) and \ datastream in INTERFACE_DEFAULT_IN: logger.warning("%s: no interfaces available for this " "component! Trying to use default one " "for %s." % (classpath, datastream)) config = [INTERFACE_DEFAULT_IN[datastream]] elif klass and \ issubclass(klass, Sensor) and \ datastream in INTERFACE_DEFAULT_OUT: logger.warning("%s: no interfaces available for this " "component! Trying to use default one " "for %s." % (classpath, datastream)) config = [INTERFACE_DEFAULT_OUT[datastream]] else: logger.error( "%s: no interfaces available for this component!" " Check builder/data.py." % classpath) return else: interfaces = MORSE_DATASTREAM_DICT[classpath] if not level in interfaces: if level == "default": # we need to look for the default level module_name, class_name = classpath.rsplit('.', 1) klass = load_module_attribute(module_name, class_name) if not hasattr(klass, "_levels"): logger.error( "Component <%s> does not declare any " "default interface. You must call " "`add_stream` with an explicit method " "and Python module." % str(classpath)) return # iterate over levels to find the one with the default flag for key, value in klass._levels.items(): if value[2]: level = key # set the right default level self.properties(abstraction_level=level) logger.info("Using default level <%s> for " "component <%s>" % (level, classpath)) break if level == "default": logger.error( "Component <%s> does not declare any" "default interface, and none of its " "abstraction levels is marked as the " "default one. You must call `add_stream`" " with an explicit method and Python " "module." % str(classpath)) return if not level in interfaces: logger.error( "%s: no interfaces defined for this " "component for abstraction level <%s>!" "Check builder/data.py." % (classpath, level)) return else: logger.error( "%s: no interfaces defined for this component" "for abstraction level <%s>! Check " "builder/data.py." % (classpath, level)) interfaces = interfaces[level] if not datastream in interfaces: logger.error( "%s: no %s interface defined for this component " "for abstraction level <%s>! " "Check builder/data.py." % (classpath, datastream, level)) return config = interfaces[datastream] if isinstance(config, list): config = config[0] if config == INTERFACE_DEFAULT_OUT: config = INTERFACE_DEFAULT_OUT[datastream] if config == INTERFACE_DEFAULT_IN: config = INTERFACE_DEFAULT_IN[datastream] if isinstance(config, str): config = [config] elif not path: config = [method] else: config = [method, path] if datastream in MORSE_DATASTREAM_MODULE: datastream_classpath = MORSE_DATASTREAM_MODULE[datastream] else: datastream_classpath = datastream config.insert(0, datastream_classpath) config.append(direction) config.append( kwargs) # append additional configuration (eg. topic name) Configuration.link_datastream(self, config)
def add_stream(self, datastream, method=None, path=None, classpath=None, direction=None, **kwargs): """ Add a data stream interface to the component Do the binding between a component and the method to export/import its data. This must be used in general by sensors and actuators. A single component can make several calls to this function to add bindings with more than one middleware. :param datastream: enum in ['ros', 'socket', 'yarp', 'text', 'pocolibs'] :param classpath: if set, force to use the configuration of the given component, instead of our own (default=None). You can pass other argument to this method, they will be added as a map to the configuration. .. code-block:: python component.add_stream('ros', topic='/myrobots/data') """ if not classpath: classpath = self.property_value("classpath") if not classpath: logger.error("%s: no classpath defined for this " "component! Check component definition " % self.name) return level = self.property_value("abstraction_level") or "default" config = [] # Configure the datastream for this component if not method: if not classpath in MORSE_DATASTREAM_DICT: # Check if we can use default interface... from morse.core.actuator import Actuator from morse.core.sensor import Sensor klass = get_class(classpath) if klass and issubclass(klass, Actuator) and datastream in INTERFACE_DEFAULT_IN: logger.warning( "%s: no interfaces available for this " "component! Trying to use default one " "for %s." % (classpath, datastream) ) config = [INTERFACE_DEFAULT_IN[datastream]] elif klass and issubclass(klass, Sensor) and datastream in INTERFACE_DEFAULT_OUT: logger.warning( "%s: no interfaces available for this " "component! Trying to use default one " "for %s." % (classpath, datastream) ) config = [INTERFACE_DEFAULT_OUT[datastream]] else: logger.error( "%s: no interfaces available for this component!" " Check builder/data.py." % classpath ) return else: interfaces = MORSE_DATASTREAM_DICT[classpath] if not level in interfaces: if level == "default": # we need to look for the default level module_name, class_name = classpath.rsplit(".", 1) klass = load_module_attribute(module_name, class_name) if not hasattr(klass, "_levels"): logger.error( "Component <%s> does not declare any " "default interface. You must call " "`add_stream` with an explicit method " "and Python module." % str(classpath) ) return # iterate over levels to find the one with the default flag for key, value in klass._levels.items(): if value[2] == True: level = key # set the right default level self.properties(abstraction_level=level) logger.info("Using default level <%s> for " "component <%s>" % (level, classpath)) break if level == "default": logger.error( "Component <%s> does not declare any" "default interface, and none of its " "abstraction levels is marked as the " "default one. You must call `add_stream`" " with an explicit method and Python " "module." % str(classpath) ) return if not level in interfaces: logger.error( "%s: no interfaces defined for this " "component for abstraction level <%s>!" "Check builder/data.py." % (classpath, level) ) return else: logger.error( "%s: no interfaces defined for this component" "for abstraction level <%s>! Check " "builder/data.py." % (classpath, level) ) interfaces = interfaces[level] if not datastream in interfaces: logger.error( "%s: no %s interface defined for this component " "for abstraction level <%s>! " "Check builder/data.py." % (classpath, datastream, level) ) return config = interfaces[datastream] if isinstance(config, list): config = config[0] if config == INTERFACE_DEFAULT_OUT: config = INTERFACE_DEFAULT_OUT[datastream] if config == INTERFACE_DEFAULT_IN: config = INTERFACE_DEFAULT_IN[datastream] if isinstance(config, str): config = [config] elif not path: config = [method] else: config = [method, path] if datastream in MORSE_DATASTREAM_MODULE: datastream_classpath = MORSE_DATASTREAM_MODULE[datastream] else: datastream_classpath = datastream config.insert(0, datastream_classpath) config.append(kwargs) # append additional configuration (eg. topic name) Configuration.link_datastream(self, config)