parameter_generator_catkin.py
Go to the documentation of this file.
00001 # Copyright (c) 2016, Claudio Bandera
00002 # All rights reserved.
00003 #
00004 # Redistribution and use in source and binary forms, with or without
00005 # modification, are permitted provided that the following conditions are met:
00006 #     * Redistributions of source code must retain the above copyright
00007 #       notice, this list of conditions and the following disclaimer.
00008 #     * Redistributions in binary form must reproduce the above copyright
00009 #       notice, this list of conditions and the following disclaimer in the
00010 #       documentation and/or other materials provided with the distribution.
00011 #     * Neither the name of the organization nor the
00012 #       names of its contributors may be used to endorse or promote products
00013 #       derived from this software without specific prior written permission.
00014 #
00015 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00016 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00017 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00018 # DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
00019 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00020 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00021 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00022 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00023 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00024 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00025 #
00026 # Author: Claudio Bandera
00027 #
00028 
00029 from __future__ import print_function
00030 from string import Template
00031 import sys
00032 import os
00033 import re
00034 
00035 
00036 def eprint(*args, **kwargs):
00037     print("************************************************", file=sys.stderr, **kwargs)
00038     print("Error when setting up parameter '{}':".format(args[0]), file=sys.stderr, **kwargs)
00039     print(*args[1:], file=sys.stderr, **kwargs)
00040     print("************************************************", file=sys.stderr, **kwargs)
00041     sys.exit(1)
00042 
00043 
00044 class ParameterGenerator(object):
00045     """Automatic config file and header generator"""
00046 
00047     def __init__(self, parent=None, group=""):
00048         """Constructor for ParamGenerator"""
00049         self.enums = []
00050         self.parameters = []
00051         self.childs = []
00052         self.parent = parent
00053         if group:
00054             self.group = group
00055         else:
00056             self.group = "gen"
00057         self.group_variable = filter(str.isalnum, self.group)
00058 
00059         if len(sys.argv) != 5:
00060             eprint(
00061                 "ParameterGenerator: Unexpected amount of args, did you try to call this directly? You shouldn't do this!")
00062 
00063         self.dynconfpath = sys.argv[1]
00064         self.share_dir = sys.argv[2]
00065         self.cpp_gen_dir = sys.argv[3]
00066         self.py_gen_dir = sys.argv[4]
00067 
00068         self.pkgname = None
00069         self.nodename = None
00070         self.classname = None
00071 
00072     def add_group(self, name):
00073         """
00074         Add a new group in the dynamic reconfigure selection
00075         :param name: name of the new group
00076         :return: a new group object that you can add parameters to
00077         """
00078         if not name:
00079             eprint("You have added a group with an empty group name. This is not supported!")
00080         child = ParameterGenerator(self, name)
00081         self.childs.append(child)
00082         return child
00083 
00084     def add_enum(self, name, description, entry_strings, default=None):
00085         """
00086         Add an enum to dynamic reconfigure
00087         :param name: Name of enum parameter
00088         :param description: Informative documentation string
00089         :param entry_strings: Enum entries, must be strings! (will be numbered with increasing value)
00090         :param default: Default value
00091         :return:
00092         """
00093 
00094         entry_strings = [str(e) for e in entry_strings]  # Make sure we only get strings
00095         if default is None:
00096             default = 0
00097         else:
00098             default = entry_strings.index(default)
00099         self.add(name=name, paramtype="int", description=description, edit_method=name, default=default,
00100                  configurable=True)
00101         for e in entry_strings:
00102             self.add(name=name + "_" + e, paramtype="int", description="Constant for enum {}".format(name),
00103                      default=entry_strings.index(e), constant=True)
00104         self.enums.append({'name': name, 'description': description, 'values': entry_strings})
00105 
00106     def add(self, name, paramtype, description, level=0, edit_method='""', default=None, min=None, max=None,
00107             configurable=False, global_scope=False, constant=False):
00108         """
00109         Add parameters to your parameter struct. Call this method from your .params file!
00110 
00111         - If no default value is given, you need to specify one in your launch file
00112         - Global parameters, vectors, maps and constant params can not be configurable
00113 
00114         :param self:
00115         :param name: The Name of you new parameter
00116         :param paramtype: The C++ type of this parameter. Can be any of ['std::string', 'int', 'bool', 'float',
00117         'double'] or std::vector<...> or std::map<std::string, ...>
00118         :param description: Choose an informative documentation string for this parameter.
00119         :param level: (optional) Passed to dynamic_reconfigure
00120         :param edit_method: (optional) Passed to dynamic_reconfigure
00121         :param default: (optional) default value
00122         :param min: (optional)
00123         :param max: (optional)
00124         :param configurable: (optional) Should this parameter be dynamic configurable
00125         :param global_scope: (optional) If true, parameter is searched in global ('/') namespace instead of private (
00126         '~') ns
00127         :param constant: (optional) If this is true, the parameter will not be fetched from param server,
00128         but the default value is kept.
00129         :return: None
00130         """
00131         configurable = self._make_bool(configurable)
00132         global_scope = self._make_bool(global_scope)
00133         constant = self._make_bool(constant)
00134         newparam = {
00135             'name': name,
00136             'type': paramtype,
00137             'default': default,
00138             'level': level,
00139             'edit_method': edit_method,
00140             'description': description,
00141             'min': min,
00142             'max': max,
00143             'is_vector': False,
00144             'is_map': False,
00145             'configurable': configurable,
00146             'constant': constant,
00147             'global_scope': global_scope,
00148         }
00149         self._perform_checks(newparam)
00150         self.parameters.append(newparam)
00151 
00152     def _perform_checks(self, param):
00153         """
00154         Will test some logical constraints as well as correct types.
00155         Throws Exception in case of error.
00156         :param self:
00157         :param param: Dictionary of one param
00158         :return:
00159         """
00160 
00161         in_type = param['type'].strip()
00162         if param['max'] is not None or param['min'] is not None:
00163             if in_type in ["std::string", "bool"]:
00164                 eprint(param['name'], "Max and min can not be specified for variable of type %s" % in_type)
00165 
00166         if in_type.startswith('std::vector'):
00167             param['is_vector'] = True
00168         if in_type.startswith('std::map'):
00169             param['is_map'] = True
00170 
00171         if (param['is_vector']):
00172             if (param['max'] is not None or param['min'] is not None):
00173                 ptype = in_type[12:-1].strip()
00174                 if ptype == "std::string":
00175                     eprint(param['name'], "Max and min can not be specified for variable of type %s" % in_type)
00176 
00177         if (param['is_map']):
00178             if (param['max'] is not None or param['min'] is not None):
00179                 ptype = in_type[9:-1].split(',')
00180                 if len(ptype) != 2:
00181                     eprint(param['name'],
00182                            "Wrong syntax used for setting up std::map<... , ...>: You provided '%s' with "
00183                            "parameter %s" % in_type)
00184                 ptype = ptype[1].strip()
00185                 if ptype == "std::string":
00186                     eprint(param['name'], "Max and min can not be specified for variable of type %s" % in_type)
00187 
00188         pattern = r'^[a-zA-Z][a-zA-Z0-9_]*$'
00189         if not re.match(pattern, param['name']):
00190             eprint(param['name'], "The name of field does not follow the ROS naming conventions, "
00191                                   "see http://wiki.ros.org/ROS/Patterns/Conventions")
00192         if param['configurable'] and (
00193                             param['global_scope'] or param['is_vector'] or param['is_map'] or param['constant']):
00194             eprint(param['name'],
00195                    "Global Parameters, vectors, maps and constant params can not be declared configurable! ")
00196         if param['global_scope'] and param['default'] is not None:
00197             eprint(param['name'], "Default values for global parameters should not be specified in node! ")
00198         if param['constant'] and param['default'] is None:
00199             eprint(param['name'], "Constant parameters need a default value!")
00200         if param['name'] in [p['name'] for p in self.parameters]:
00201             eprint(param['name'], "Parameter with the same name exists already")
00202         if param['edit_method'] == '':
00203             param['edit_method'] = '""'
00204         elif param['edit_method'] != '""':
00205             param['configurable'] = True
00206 
00207         # Check type
00208         if param['is_vector']:
00209             ptype = in_type[12:-1].strip()
00210             self._test_primitive_type(param['name'], ptype)
00211             param['type'] = 'std::vector<{}>'.format(ptype)
00212         elif param['is_map']:
00213             ptype = in_type[9:-1].split(',')
00214             if len(ptype) != 2:
00215                 eprint(param['name'], "Wrong syntax used for setting up std::map<... , ...>: You provided '%s' with "
00216                                       "parameter %s" % in_type)
00217             ptype[0] = ptype[0].strip()
00218             ptype[1] = ptype[1].strip()
00219             if ptype[0] != "std::string":
00220                 eprint(param['name'], "Can not setup map with %s as key type. Only std::map<std::string, "
00221                                       "...> are allowed" % ptype[0])
00222             self._test_primitive_type(param['name'], ptype[0])
00223             self._test_primitive_type(param['name'], ptype[1])
00224             param['type'] = 'std::map<{},{}>'.format(ptype[0], ptype[1])
00225         else:
00226             # Pytype and defaults can only be applied to primitives
00227             self._test_primitive_type(param['name'], in_type)
00228             param['pytype'] = self._pytype(in_type)
00229 
00230     @staticmethod
00231     def _pytype(drtype):
00232         """Convert C++ type to python type"""
00233         return {'std::string': "str", 'int': "int", 'double': "double", 'bool': "bool"}[drtype]
00234 
00235     @staticmethod
00236     def _test_primitive_type(name, drtype):
00237         """
00238         Test whether parameter has one of the accepted C++ types
00239         :param name: Parametername
00240         :param drtype: Typestring
00241         :return:
00242         """
00243         primitive_types = ['std::string', 'int', 'bool', 'float', 'double']
00244         if drtype not in primitive_types:
00245             raise TypeError("'%s' has type %s, but allowed are: %s" % (name, drtype, primitive_types))
00246 
00247     @staticmethod
00248     def _get_cvalue(param, field):
00249         """
00250         Helper function to convert strings and booleans to correct C++ syntax
00251         :param param:
00252         :return: C++ compatible representation
00253         """
00254         value = param[field]
00255         if param['type'] == 'std::string':
00256             value = '"{}"'.format(param[field])
00257         elif param['type'] == 'bool':
00258             value = str(param[field]).lower()
00259         return str(value)
00260 
00261     @staticmethod
00262     def _get_pyvalue(param, field):
00263         """
00264         Helper function to convert strings and booleans to correct C++ syntax
00265         :param param:
00266         :return: C++ compatible representation
00267         """
00268         value = param[field]
00269         if param['type'] == 'std::string':
00270             value = '"{}"'.format(param[field])
00271         elif param['type'] == 'bool':
00272             value = str(param[field]).capitalize()
00273         return str(value)
00274 
00275     @staticmethod
00276     def _get_cvaluelist(param, field):
00277         """
00278         Helper function to convert python list of strings and booleans to correct C++ syntax
00279         :param param:
00280         :return: C++ compatible representation
00281         """
00282         values = param[field]
00283         assert (isinstance(values, list))
00284         form = ""
00285         for value in values:
00286             if param['type'] == 'std::vector<std::string>':
00287                 value = '"{}"'.format(value)
00288             elif param['type'] == 'std::vector<bool>':
00289                 value = str(value).lower()
00290             else:
00291                 value = str(value)
00292             form += value + ','
00293         # remove last ','
00294         return form[:-1]
00295 
00296     @staticmethod
00297     def _get_cvaluedict(param, field):
00298         """
00299         Helper function to convert python dict of strings and booleans to correct C++ syntax
00300         :param param:
00301         :return: C++ compatible representation
00302         """
00303         values = param[field]
00304         assert (isinstance(values, dict))
00305         form = ""
00306         for key, value in values.items():
00307             if param['type'] == 'std::map<std::string,std::string>':
00308                 pair = '{{"{}","{}"}}'.format(key, value)
00309             elif param['type'] == 'std::map<std::string,bool>':
00310                 pair = '{{"{}",{}}}'.format(key, str(value).lower())
00311             else:
00312                 pair = '{{"{}",{}}}'.format(key, str(value))
00313             form += pair + ','
00314         # remove last ','
00315         return form[:-1]
00316 
00317     def generate(self, pkgname, nodename, classname):
00318         """
00319         Main working Function, call this at the end of your .params file!
00320         :param self:
00321         :param pkgname: Name of the catkin package
00322         :param nodename: Name of the Node that will hold these params
00323         :param classname: This should match your file name, so that cmake will detect changes in config file.
00324         :return: Exit Code
00325         """
00326         self.pkgname = pkgname
00327         self.nodename = nodename
00328         self.classname = classname
00329 
00330         if self.parent:
00331             eprint("You should not call generate on a group! Call it on the main parameter generator instead!")
00332 
00333         return self._generateImpl()
00334 
00335     def _generateImpl(self):
00336         """
00337         Implementation level function. Can be overwritten by derived classes.
00338         :return:
00339         """
00340         self._generatecfg()
00341         self._generatehpp()
00342         self._generatepy()
00343 
00344         return 0
00345 
00346     def _generatecfg(self):
00347         """
00348         Generate .cfg file for dynamic reconfigure
00349         :param self:
00350         :return:
00351         """
00352         templatefile = os.path.join(self.dynconfpath, "templates", "ConfigType.h.template")
00353         with open(templatefile, 'r') as f:
00354             template = f.read()
00355 
00356         param_entries = self._generate_param_entries()
00357 
00358         param_entries = "\n".join(param_entries)
00359         template = Template(template).substitute(pkgname=self.pkgname, nodename=self.nodename,
00360                                                  classname=self.classname, params=param_entries)
00361 
00362         cfg_file = os.path.join(self.share_dir, "cfg", self.classname + ".cfg")
00363         try:
00364             if not os.path.exists(os.path.dirname(cfg_file)):
00365                 os.makedirs(os.path.dirname(cfg_file))
00366         except OSError:
00367             # Stupid error, sometimes the directory exists anyway
00368             pass
00369         with open(cfg_file, 'w') as f:
00370             f.write(template)
00371         os.chmod(cfg_file, 509)  # entspricht 775 (octal)
00372 
00373     def _generatehpp(self):
00374         """
00375         Generate C++ Header file, holding the parameter struct.
00376         :param self:
00377         :return:
00378         """
00379 
00380         # Read in template file
00381         templatefile = os.path.join(self.dynconfpath, "templates", "Parameters.h.template")
00382         with open(templatefile, 'r') as f:
00383             template = f.read()
00384 
00385         param_entries = []
00386         string_representation = []
00387         from_server = []
00388         to_server = []
00389         non_default_params = []
00390         from_config = []
00391         to_config = []
00392         test_limits = []
00393 
00394         params = self._get_parameters()
00395 
00396         # Create dynamic parts of the header file for every parameter
00397         for param in params:
00398             name = param['name']
00399 
00400             # Adjust key for parameter server
00401             if param["global_scope"]:
00402                 namespace = 'globalNamespace'
00403             else:
00404                 namespace = 'privateNamespace'
00405             full_name = '{} + "{}"'.format(namespace, param["name"])
00406 
00407             # Test for default value
00408             if param["default"] is None:
00409                 default = ""
00410                 non_default_params.append(Template('      << "\t" << $namespace << "$name" << " ($type) '
00411                                                    '\\n"\n').substitute(
00412                     namespace=namespace, name=name, type=param["type"]))
00413             else:
00414                 if param['is_vector']:
00415                     default = ', {}'.format(str(param['type']) + "{" + self._get_cvaluelist(param, "default") + "}")
00416                 elif param['is_map']:
00417                     default = ', {}'.format(str(param['type']) + "{" + self._get_cvaluedict(param, "default") + "}")
00418                 else:
00419                     default = ', {}'.format(str(param['type']) + "{" + self._get_cvalue(param, "default") + "}")
00420 
00421             # Test for constant value
00422             if param['constant']:
00423                 param_entries.append(Template('  static constexpr auto ${name} = $default; /*!< ${description} '
00424                                               '*/').substitute(type=param['type'], name=name,
00425                                                                description=param['description'],
00426                                                                default=self._get_cvalue(param, "default")))
00427                 from_server.append(Template('    rosparam_handler::testConstParam($paramname);').substitute(paramname=full_name))
00428             else:
00429                 param_entries.append(Template('  ${type} ${name}; /*!< ${description} */').substitute(
00430                     type=param['type'], name=name, description=param['description']))
00431                 from_server.append(Template('    success &= rosparam_handler::getParam($paramname, $name$default);').substitute(
00432                     paramname=full_name, name=name, default=default, description=param['description']))
00433                 to_server.append(
00434                     Template('  rosparam_handler::setParam(${paramname},${name});').substitute(paramname=full_name, name=name))
00435 
00436             # Test for configurable params
00437             if param['configurable']:
00438                 from_config.append(Template('    $name = config.$name;').substitute(name=name))
00439                 to_config.append(Template('   config.$name = $name;').substitute(name=name))
00440 
00441             # Test limits
00442             if param['is_vector']:
00443                 ttype = param['type'][12:-1].strip()
00444             elif param['is_map']:
00445                 ttype = param['type'][9:-1].strip()
00446             else:
00447                 ttype = param['type']
00448             if param['min'] is not None:
00449                 test_limits.append(Template('    rosparam_handler::testMin<$type>($paramname, $name, $min);').substitute(
00450                     paramname=full_name, name=name, min=param['min'], type=ttype))
00451             if param['max'] is not None:
00452                 test_limits.append(Template('    rosparam_handler::testMax<$type>($paramname, $name, $max);').substitute(
00453                     paramname=full_name, name=name, max=param['max'], type=ttype))
00454 
00455             # Add debug output
00456             if param['is_vector'] or param['is_map']:
00457                 string_representation.append(Template('      << "\t" << p.$namespace << "$name:" << rosparam_handler::to_string(p.$name) << '
00458                                                       '"\\n"\n').substitute(namespace=namespace, name=name))
00459             else:
00460                 string_representation.append(Template('      << "\t" << p.$namespace << "$name:" << p.$name << '
00461                                                       '"\\n"\n').substitute(namespace=namespace, name=name))
00462 
00463         param_entries = "\n".join(param_entries)
00464         string_representation = "".join(string_representation)
00465         non_default_params = "".join(non_default_params)
00466         from_server = "\n".join(from_server)
00467         to_server = "\n".join(to_server)
00468         from_config = "\n".join(from_config)
00469         to_config = "\n".join(to_config)
00470         test_limits = "\n".join(test_limits)
00471 
00472         content = Template(template).substitute(pkgname=self.pkgname, ClassName=self.classname,
00473                                                 parameters=param_entries, fromConfig=from_config,
00474                                                 fromParamServer=from_server,
00475                                                 string_representation=string_representation,
00476                                                 non_default_params=non_default_params, nodename=self.nodename,
00477                                                 test_limits=test_limits, toParamServer=to_server,
00478                                                 toConfig=to_config)
00479 
00480         header_file = os.path.join(self.cpp_gen_dir, self.classname + "Parameters.h")
00481         try:
00482             if not os.path.exists(os.path.dirname(header_file)):
00483                 os.makedirs(os.path.dirname(header_file))
00484         except OSError:
00485             # Stupid error, sometimes the directory exists anyway
00486             pass
00487         with open(header_file, 'w') as f:
00488             f.write(content)
00489 
00490     def _generatepy(self):
00491         """
00492         Generate Python parameter file
00493         :param self:
00494         :return:
00495         """
00496         params = self._get_parameters()
00497         paramDescription = str(params)
00498 
00499         # Read in template file
00500         templatefile = os.path.join(self.dynconfpath, "templates", "Parameters.py.template")
00501         with open(templatefile, 'r') as f:
00502             template = f.read()
00503 
00504         content = Template(template).substitute(pkgname=self.pkgname, ClassName=self.classname,
00505                                                 paramDescription=paramDescription)
00506 
00507         py_file = os.path.join(self.py_gen_dir, "param", self.classname + "Parameters.py")
00508         try:
00509             if not os.path.exists(os.path.dirname(py_file)):
00510                 os.makedirs(os.path.dirname(py_file))
00511         except OSError:
00512             # Stupid error, sometimes the directory exists anyway
00513             pass
00514         with open(py_file, 'w') as f:
00515             f.write(content)
00516         init_file = os.path.join(self.py_gen_dir, "param", "__init__.py")
00517         with open(init_file, 'wa') as f:
00518             f.write("")
00519 
00520     def _generateyml(self):
00521         """
00522         Generate .yaml file for roslaunch
00523         :param self:
00524         :return:
00525         """
00526         params = self._get_parameters()
00527 
00528         content = "### This file was generated using the rosparam_handler generate_yaml script.\n"
00529 
00530         for entry in params:
00531             if not entry["constant"]:
00532                 content += "\n"
00533                 content += "# Name:\t" + str(entry["name"]) + "\n"
00534                 content += "# Desc:\t" + str(entry["description"]) + "\n"
00535                 content += "# Type:\t" + str(entry["type"]) + "\n"
00536                 if entry['min'] or entry['max']:
00537                     content += "# [min,max]:\t[" + str(entry["min"]) + "/" + str(entry["max"]) + "]" + "\n"
00538                 if entry["global_scope"]:
00539                     content += "# Lives in global namespace!\n"
00540                 if entry["default"] is not None:
00541                     content += str(entry["name"]) + ": " + str(entry["default"]) + "\n"
00542                 else:
00543                     content += str(entry["name"]) + ": \n"
00544 
00545         yaml_file = os.path.join(os.getcwd(), self.classname + "Parameters.yaml")
00546 
00547         with open(yaml_file, 'w') as f:
00548             f.write(content)
00549 
00550     def _get_parameters(self):
00551         """
00552         Returns parameter of this and all childs
00553         :return: list of all parameters
00554         """
00555         params = self.parameters
00556         for child in self.childs:
00557             params.extend(child._get_parameters())
00558         return params
00559 
00560     def _generate_param_entries(self):
00561         """
00562         Generates the entries for the cfg-file
00563         :return: list of param entries as string
00564         """
00565         param_entries = []
00566         dynamic_params = [p for p in self.parameters if p["configurable"]]
00567 
00568         if self.parent:
00569             param_entries.append(Template("$group_variable = $parent.add_group('$group')").substitute(
00570                 group_variable=self.group_variable,
00571                 group=self.group,
00572                 parent=self.parent.group_variable))
00573 
00574         for enum in self.enums:
00575             param_entries.append(Template("$name = gen.enum([").substitute(
00576                 name=enum['name'],
00577                 parent=self.group))
00578             i = 0
00579             for value in enum['values']:
00580                 param_entries.append(
00581                     Template("    gen.const(name='$name', type='$type', value=$value, descr='$descr'),")
00582                         .substitute(name=value, type="int", value=i, descr=""))
00583                 i += 1
00584             param_entries.append(Template("    ], '$description')").substitute(description=enum["description"]))
00585 
00586         for param in dynamic_params:
00587             content_line = Template("$group_variable.add(name = '$name', paramtype = '$paramtype', level = $level, "
00588                                     "description = '$description', edit_method=$edit_method").substitute(
00589                 group_variable=self.group_variable,
00590                 name=param["name"],
00591                 paramtype=param['pytype'],
00592                 level=param['level'],
00593                 edit_method=param['edit_method'],
00594                 description=param['description'])
00595             if param['default'] is not None:
00596                 content_line += Template(", default=$default").substitute(default=self._get_pyvalue(param, "default"))
00597             if param['min'] is not None:
00598                 content_line += Template(", min=$min").substitute(min=param['min'])
00599             if param['max'] is not None:
00600                 content_line += Template(", max=$max").substitute(max=param['max'])
00601             content_line += ")"
00602             param_entries.append(content_line)
00603 
00604         for child in self.childs:
00605             param_entries.extend(child._generate_param_entries())
00606         return param_entries
00607 
00608     @staticmethod
00609     def _make_bool(param):
00610         if isinstance(param, bool):
00611             return param
00612         else:
00613             # Pray and hope that it is a string
00614             return bool(param)
00615 
00616 
00617 # Create derived class for yaml generation
00618 class YamlGenerator(ParameterGenerator):
00619     def _generateImpl(self):
00620         self._generateyml()
00621         return 0


rosparam_handler
Author(s): Claudio Bandera
autogenerated on Sat Jun 8 2019 20:21:53