class Example(object): def __init__(self): # Create a D(ynamic)DynamicReconfigure self.ddr = DDynamicReconfigure("class_example") # Add variables (name, description, default value, min, max, edit_method) self.ddr.add_variable("decimal", "float/double variable", 0.0, -1.0, 1.0) self.ddr.add_variable("integer", "integer variable", 0, -1, 1) self.ddr.add_variable("bool", "bool variable", True) self.ddr.add_variable("string", "string variable", "string dynamic variable") enum_method = self.ddr.enum([ self.ddr.const("Small", "int", 0, "A small constant"), self.ddr.const("Medium", "int", 1, "A medium constant"), self.ddr.const("Large", "int", 2, "A large constant"), self.ddr.const("ExtraLarge", "int", 3, "An extra large constant")], "An enum example") self.ddr.add_variable("enumerate", "enumerate variable", 1, 0, 3, edit_method=enum_method) self.add_variables_to_self() self.ddr.start(self.dyn_rec_callback) def add_variables_to_self(self): var_names = self.ddr.get_variable_names() for var_name in var_names: self.__setattr__(var_name, None) def dyn_rec_callback(self, config, level): rospy.loginfo("Received reconf call: " + str(config)) # Update all variables var_names = self.ddr.get_variable_names() for var_name in var_names: self.__dict__[var_name] = config[var_name] return config
import rospy from ddynamic_reconfigure_python.ddynamic_reconfigure import DDynamicReconfigure def dyn_rec_callback(config, level): rospy.loginfo("Received reconf call: " + str(config)) return config if __name__ == '__main__': rospy.init_node('test_ddynrec') # Create a D(ynamic)DynamicReconfigure ddynrec = DDynamicReconfigure("example_dyn_rec") # Add variables (name, description, default value, min, max, edit_method) ddynrec.add_variable("decimal", "float/double variable", 0.0, -1.0, 1.0) ddynrec.add_variable("integer", "integer variable", 0, -1, 1) ddynrec.add_variable("bool", "bool variable", True) ddynrec.add_variable("string", "string variable", "string dynamic variable") enum_method = ddynrec.enum([ ddynrec.const("Small", "int", 0, "A small constant"), ddynrec.const("Medium", "int", 1, "A medium constant"), ddynrec.const("Large", "int", 2, "A large constant"), ddynrec.const("ExtraLarge", "int", 3, "An extra large constant")], "An enum example") ddynrec.add_variable("enumerate", "enumerate variable", 1, 0, 3, edit_method=enum_method) # Start the server ddynrec.start(dyn_rec_callback) rospy.spin()
class RestClient(object): def __init__(self, rest_name, ignored_parameters=[]): self.rest_name = rest_name self.ignored_parameters = ignored_parameters self.rest_services = [] self.ddr = None rospy.init_node(rest_name + '_client', log_level=rospy.INFO) self.host = rospy.get_param('~host', '') if not self.host: rospy.logerr('host is not set') sys.exit(1) self._setup_ddr() def _get_rest_parameters(self): try: url = 'http://{}/api/v1/nodes/{}/parameters'.format( self.host, self.rest_name) res = requests_retry_session().get(url) if res.status_code != 200: rospy.logerr("Getting parameters failed with status code: %d", res.status_code) return [] return res.json() except Exception as e: rospy.logerr(str(e)) return [] def _set_rest_parameters(self, parameters): try: url = 'http://{}/api/v1/nodes/{}/parameters'.format( self.host, self.rest_name) res = requests_retry_session().put(url, json=parameters) j = res.json() rospy.logdebug("set parameters response: %s", json.dumps(j, indent=2)) if 'return_code' in j and j['return_code']['value'] != 0: rospy.logwarn("Setting parameter failed: %s", j['return_code']['message']) return [] if res.status_code != 200: rospy.logerr("Setting parameters failed with status code: %d", res.status_code) return [] return j except Exception as e: rospy.logerr(str(e)) return [] def _setup_ddr(self): self.ddr = DDynamicReconfigure(rospy.get_name()) rest_params = [ p for p in self._get_rest_parameters() if p['name'] not in self.ignored_parameters ] def enum_method_from_param(p): if p['type'] != 'string': return "" enum_matches = re.findall(r'.*\[(?P<enum>.+)\].*', p['description']) if not enum_matches: return "" enum_names = [str(e.strip()) for e in enum_matches[0].split(',')] enum_list = [self.ddr.const(n, 'str', n, n) for n in enum_names] return self.ddr.enum(enum_list, p['name'] + '_enum') for p in rest_params: level = 0 edit_method = enum_method_from_param(p) if p['type'] == 'int32': self.ddr.add(p['name'], 'int', level, p['description'], p['default'], p['min'], p['max']) elif p['type'] == 'float64': self.ddr.add(p['name'], 'double', level, p['description'], p['default'], p['min'], p['max']) elif p['type'] == 'string': self.ddr.add(p['name'], 'str', level, p['description'], str(p['default']), edit_method=edit_method) elif p['type'] == 'bool': self.ddr.add(p['name'], 'bool', level, p['description'], p['default']) else: rospy.logwarn("Unsupported parameter type: %s", p['type']) self.ddr.start(self._dyn_rec_callback) def _dyn_rec_callback(self, config, level): rospy.logdebug("Received reconf call: " + str(config)) new_rest_params = [{ 'name': n, 'value': config[n] } for n in self.ddr.get_variable_names() if n in config] if new_rest_params: returned_params = self._set_rest_parameters(new_rest_params) for p in returned_params: if p['name'] not in config: rospy.logerr("param %s not in config", p['name']) continue config[p['name']] = p['value'] return config def call_rest_service(self, name, srv_type=None, request=None): try: args = {} if request is not None: # convert ROS request to JSON (with custom API mappings) args = convert_ros_message_to_dictionary(request) rospy.logdebug('calling {} with args: {}'.format(name, args)) url = 'http://{}/api/v1/nodes/{}/services/{}'.format( self.host, self.rest_name, name) res = requests_retry_session().put(url, json={'args': args}) j = res.json() rospy.logdebug("{} rest response: {}".format( name, json.dumps(j, indent=2))) rc = j['response'].get('return_code') if rc is not None and rc['value'] < 0: rospy.logwarn("service {} returned an error: [{}] {}".format( name, rc['value'], rc['message'])) # convert to ROS response if srv_type is not None: response = convert_dictionary_to_ros_message( srv_type._response_class(), j['response'], strict_mode=False) else: response = j['response'] except Exception as e: rospy.logerr(str(e)) if srv_type is not None: response = srv_type._response_class() if hasattr(response, 'return_code'): response.return_code.value = -1000 response.return_code.message = str(e) return response def add_rest_service(self, srv_type, srv_name, callback): """create a service and inject the REST-API service name""" srv = rospy.Service(rospy.get_name() + "/" + srv_name, srv_type, partial(callback, srv_name, srv_type)) self.rest_services.append(srv)