Skip to content
This repository was archived by the owner on May 31, 2025. It is now read-only.

Various fixes #98

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions cfg/Test.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ gen.add("int_", int_t, 1, "Int parameter",0, -10, 10)
gen.add("double_", double_t, 2, "double parameter", 0, -2, 10)
gen.add("double_no_minmax", double_t, 2, "double parameter without boundaries", 1)
gen.add("double_no_max", double_t, 2, "double parameter without max value", 2, 0)
gen.add("str_", str_t, 4, "String parameter","foo")
gen.add("str_", str_t, 4, "String parameter","foo, default=\"this is it\"")
gen.add("mstr_", str_t, 4, "Multibyte String parameter","bar")
gen.add("bool_", bool_t, 8, "Boolean parameter",False)
gen.add("level", int_t, 16, "Contains the level of the previous change",0)
Expand All @@ -67,9 +67,9 @@ group2.add("group2_string", str_t, 0, "A third level group parameter", "test str
group2.add("some_other", str_t, 0, "Something", "AAAAAAGGHH")
group2.add("variable", bool_t,0, "A boolean", True)
group3 = group2.add_group("Group3")
group3.add("deep_var_int", int_t, 0, "Were very far down now", 0, 0, 3, edit_method = enum)
group3.add("deep_var_bool", bool_t, 0, "Were even farther down now!!", True)
group3.add("deep_var_double", double_t, 0, "Were super far down now!!", -1.0)
group3.add("deep_var_int", int_t, 0, "We're very far down now", 0, 0, 3, edit_method = enum)
group3.add("deep_var_bool", bool_t, 0, "We're even farther down now!!", True)
group3.add("deep_var_double", double_t, 0, "We're \"super\" far down now!!", -1.0)

group12 = gen.add_group("Upper Group 2")
group12.add("some_param", int_t, 0, "Some param", 20)
Expand Down
243 changes: 125 additions & 118 deletions src/dynamic_reconfigure/parameter_generator_catkin.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import string
import sys
import re
import textwrap

# LINEDEBUG="#line"
LINEDEBUG = "//#line"
Expand All @@ -63,20 +64,27 @@
id = 0


def check_description(description):
quotes = ['"', "'"]
for quote in quotes:
if description.find(quote) != -1:
raise Exception(r"""quotes not allowed in description string `%s`""" % description)


def check_name(name):
pattern = r'^[a-zA-Z][a-zA-Z0-9_]*$'
if not re.match(pattern, name):
raise Exception("The name of field \'%s\' does not follow the ROS naming conventions, see http://wiki.ros.org/ROS/Patterns/Conventions" % name)

def cquote(s):
""" quote a string for inclusion in C source """
escape = {
'\\': r'\\',
'"': r'\"',
'\r': r'\r',
'\n': r'\n',
'\0': r'\0'
}
return '"{}"'.format(re.sub(
'|'.join(re.escape(c) for c in escape),
lambda m: escape[m.group(0)],
s
))

class ParameterGenerator:
class ParameterGenerator(object):
minval = {
'int': -0x80000000, # 'INT_MIN',
'double': '-std::numeric_limits<double>::infinity()',
Expand All @@ -98,7 +106,7 @@ class ParameterGenerator:
'bool': False,
}

class Group:
class Group(object):
instances = {}

def __init__(self, gen, name, type, state, id, parent):
Expand Down Expand Up @@ -246,21 +254,19 @@ def __init__(self):
id = 1
self.constants = []
if len(sys.argv) < 5:
msg = """
ahhhh! Unexpected command line syntax!
raise SystemExit(textwrap.dedent("""\
ahhhh! Unexpected command line syntax!

Are you trying to call a dynamic_reconfigure configuration generation script
directly? When you are using dynamic_reconfigure with python, you don't ever
need to invoke the configuration generator script yourself; it loads
automatically. If you are using dynamic_reconfigure from C++, you need to
add a call to generate_dynamic_reconfigure_options() in your CMakeLists.txt
Are you trying to call a dynamic_reconfigure configuration generation script
directly? When you are using dynamic_reconfigure with python, you don't ever
need to invoke the configuration generator script yourself; it loads
automatically. If you are using dynamic_reconfigure from C++, you need to
add a call to generate_dynamic_reconfigure_options() in your CMakeLists.txt

For an example, see http://wiki.ros.org/dynamic_reconfigure/Tutorials
For an example, see http://wiki.ros.org/dynamic_reconfigure/Tutorials

Have a nice day
"""
print(msg)
sys.exit(1)
Have a nice day
"""))
self.dynconfpath = sys.argv[1] # FIXME this is awful
self.binary_dir = sys.argv[2]
self.cpp_gen_dir = sys.argv[3]
Expand All @@ -275,7 +281,6 @@ def const(self, name, type, value, descr):
'srcfile': inspect.getsourcefile(inspect.currentframe().f_back.f_code),
'description': descr
}
check_description(descr)
self.fill_type(newconst)
self.check_type(newconst, 'value')
self.constants.append(newconst)
Expand All @@ -284,7 +289,6 @@ def const(self, name, type, value, descr):
def enum(self, constants, description):
if len(set(const['type'] for const in constants)) != 1:
raise Exception("Inconsistent types in enum!")
check_description(description)
return repr({'enum': constants, 'enum_description': description})

# Wrap add and add_group for the default group
Expand Down Expand Up @@ -344,85 +348,82 @@ def generate(self, pkgname, nodename, name):

def generatewikidoc(self):
self.mkdirabs(os.path.join(self.binary_dir, "docs"))
f = open(os.path.join(self.binary_dir, "docs", self.msgname + ".wikidoc"), 'w')
print(
"""# Autogenerated param section. Do not hand edit.
param {
group.0 {
name=Dynamically Reconfigurable Parameters
desc=See the [[dynamic_reconfigure]] package for details on dynamically reconfigurable parameters.""", file=f)
i = -1
for param in self.group.get_parameters():
i = i + 1
range = ""
try:
enum = eval(param['edit_method'])['enum']
range = ", ".join(Template("$name ($value): $description").substitute(const) for const in enum)
range = "Possible values are: " + range
except:
if param['type'] == int_t or param['type'] == double_t:
range = Template("Range: $min to $max").substitute(param)
print(Template(
"""$i.name= ~$name
$i.default= $default
$i.type= $type
$i.desc=$description $range"""
).substitute(param, range=range, i=i), file=f)
print("}\n}\n# End of autogenerated section. You may edit below.", file=f)
f.close()
with open(os.path.join(self.binary_dir, "docs", self.msgname + ".wikidoc"), 'w') as f:
print(textwrap.dedent("""\
# Autogenerated param section. Do not hand edit.
param {
group.0 {
name=Dynamically Reconfigurable Parameters
desc=See the [[dynamic_reconfigure]] package for details on dynamically reconfigurable parameters."""
), file=f)
i = -1
for param in self.group.get_parameters():
i = i + 1
range = ""
try:
enum = eval(param['edit_method'])['enum']
range = ", ".join(Template("$name ($value): $description").substitute(const) for const in enum)
range = "Possible values are: " + range
except:
if param['type'] == int_t or param['type'] == double_t:
range = Template("Range: $min to $max").substitute(param)
print(Template(textwrap.dedent("""\
$i.name= ~$name
$i.default= $default
$i.type= $type
$i.desc=$description $range"""
)).substitute(param, range=range, i=i), file=f)
print("}\n}\n# End of autogenerated section. You may edit below.", file=f)

def generateusage(self):
self.mkdirabs("docs")
f = open(os.path.join(self.binary_dir, "docs", self.msgname + "-usage.dox"), 'w')
# print("/**", file=f)
print("\\subsubsection usage Usage", file=f)
print('\\verbatim', file=f)
print(Template('<node name="$nodename" pkg="$pkgname" type="$nodename">').substitute(
pkgname=self.pkgname, nodename=self.nodename), file=f)
for param in self.group.get_parameters():
print(Template(' <param name="$name" type="$type" value="$default" />').substitute(param), file=f)
print('</node>', file=f)
print('\\endverbatim', file=f)
print('', file=f)
# print("*/", file=f)
f.close()
with open(os.path.join(self.binary_dir, "docs", self.msgname + "-usage.dox"), 'w') as f:
# print("/**", file=f)
print("\\subsubsection usage Usage", file=f)
print('\\verbatim', file=f)
print(Template('<node name="$nodename" pkg="$pkgname" type="$nodename">').substitute(
pkgname=self.pkgname, nodename=self.nodename), file=f)
for param in self.group.get_parameters():
print(Template(' <param name="$name" type="$type" value="$default" />').substitute(param), file=f)
print('</node>', file=f)
print('\\endverbatim', file=f)
print('', file=f)
# print("*/", file=f)

def generatedoc(self):
self.mkdirabs("docs")
dir_path = os.path.join(self.binary_dir, "docs")
self.mkdirabs(dir_path)
f = open(os.path.join(dir_path, self.msgname + ".dox"), 'w')
# print("/**", file=f)
print("\\subsubsection parameters ROS parameters", file=f)
print("", file=f)
print("Reads and maintains the following parameters on the ROS server", file=f)
print("", file=f)
for param in self.group.get_parameters():
print(Template("- \\b \"~$name\" : \\b [$type] $description min: $min, default: $default, max: $max").substitute(param), file=f)
print("", file=f)
# print("*/", file=f)
f.close()
with open(os.path.join(dir_path, self.msgname + ".dox"), 'w') as f:
# print("/**", file=f)
print("\\subsubsection parameters ROS parameters", file=f)
print("", file=f)
print("Reads and maintains the following parameters on the ROS server", file=f)
print("", file=f)
for param in self.group.get_parameters():
print(Template("- \\b \"~$name\" : \\b [$type] $description min: $min, default: $default, max: $max").substitute(param), file=f)
print("", file=f)
# print("*/", file=f)

def generateusage(self):
self.mkdirabs("docs")
f = open(os.path.join(self.binary_dir, "docs", self.msgname + "-usage.dox"), 'w')
# print("/**", file=f)
print("\\subsubsection usage Usage", file=f)
print('\\verbatim', file=f)
print(Template('<node name="$nodename" pkg="$pkgname" type="$nodename">').substitute(
pkgname=self.pkgname, nodename=self.nodename), file=f)
for param in self.group.get_parameters():
print(Template(' <param name="$name" type="$type" value="$default" />').substitute(param), file=f)
print('</node>', file=f)
print('\\endverbatim', file=f)
print("", file=f)
# print("*/", file=f)
f.close()
with open(os.path.join(self.binary_dir, "docs", self.msgname + "-usage.dox"), 'w') as f:
# print("/**", file=f)
print("\\subsubsection usage Usage", file=f)
print('\\verbatim', file=f)
print(Template('<node name="$nodename" pkg="$pkgname" type="$nodename">').substitute(
pkgname=self.pkgname, nodename=self.nodename), file=f)
for param in self.group.get_parameters():
print(Template(' <param name="$name" type="$type" value="$default" />').substitute(param), file=f)
print('</node>', file=f)
print('\\endverbatim', file=f)
print("", file=f)
# print("*/", file=f)

def crepr(self, param, val):
type = param["type"]
if type == 'str':
return '"' + val + '"'
return cquote(val)
if type in ['int', 'double']:
return str(val)
if type == 'bool':
Expand All @@ -441,12 +442,15 @@ def crepr(self, param, val):
# if 'float' in types:
# return str(val)

def appendline(self, list, text, param, value=None):
def appendline(self, list, text, param, value=None, **kwarg):
if value is None:
val = ""
else:
val = self.crepr(param, param[value])
list.append(Template('${doline} $srcline "$srcfile"\n ' + text).safe_substitute(param, v=val, doline=LINEDEBUG, configname=self.name))
list.append(
Template('${doline} $srcline "$srcfile"\n ' + text)
.safe_substitute(param, v=val, doline=LINEDEBUG, configname=self.name, **kwarg
))

def appendgroup(self, list, group):
subgroups = []
Expand All @@ -470,16 +474,15 @@ def generatecpp(self):
templatelines = []
templatefilesafe = templatefile.replace('\\', '\\\\') # line directive does backslash expansion.
curline = 1
f = open(templatefile)
for line in f:
curline = curline + 1
templatelines.append(Template(line).safe_substitute(linenum=curline, filename=templatefilesafe))
f.close()
with open(templatefile) as f:
for line in f:
curline = curline + 1
templatelines.append(Template(line).safe_substitute(linenum=curline, filename=templatefilesafe))

template = ''.join(templatelines)

# Write the configuration manipulator.
self.mkdirabs(self.cpp_gen_dir)
f = open(os.path.join(self.cpp_gen_dir, self.name + "Config.h"), 'w')
paramdescr = []
groups = []
members = []
Expand All @@ -501,11 +504,17 @@ def write_params(group):
paramdescr,
group.to_dict()['name'] +
".abstract_parameters.push_back(${configname}Config::AbstractParamDescriptionConstPtr(new ${configname}Config::ParamDescription<${ctype}>(\"${name}\", \"${type}\", ${level}, "
"\"${description}\", \"${edit_method}\", &${configname}Config::${name})));", param)
"${description}, ${edit_method}, &${configname}Config::${name})));",
param,
description=cquote(param['description']),
edit_method=cquote(param['edit_method']))
self.appendline(
paramdescr,
"__param_descriptions__.push_back(${configname}Config::AbstractParamDescriptionConstPtr(new ${configname}Config::ParamDescription<${ctype}>(\"${name}\", \"${type}\", ${level}, "
"\"${description}\", \"${edit_method}\", &${configname}Config::${name})));", param)
"${description}, ${edit_method}, &${configname}Config::${name})));",
param,
description=cquote(param['description']),
edit_method=cquote(param['edit_method']))

for g in group.groups:
write_params(g)
Expand All @@ -524,11 +533,11 @@ def write_params(group):
members = '\n'.join(members)
constants = '\n'.join(constants)
groups = '\n'.join(groups)
f.write(Template(template).substitute(
uname=self.name.upper(),
configname=self.name, pkgname=self.pkgname, paramdescr=paramdescr,
members=members, groups=groups, doline=LINEDEBUG, constants=constants))
f.close()
with open(os.path.join(self.cpp_gen_dir, self.name + "Config.h"), 'w') as f:
f.write(Template(template).substitute(
uname=self.name.upper(),
configname=self.name, pkgname=self.pkgname, paramdescr=paramdescr,
members=members, groups=groups, doline=LINEDEBUG, constants=constants))
print("Wrote header file in " + os.path.join(self.cpp_gen_dir, self.name + "Config.h"))

# def deleteoneobsolete(self, file):
Expand Down Expand Up @@ -602,22 +611,20 @@ def replace_infinity(self, config_dict):
def generatepy(self):
# Read the configuration manipulator template and insert line numbers and file name into template.
templatefile = os.path.join(self.dynconfpath, "templates", "ConfigType.py.template")
f = open(templatefile)
template = f.read()
f.close()
with open(templatefile) as f:
template = f.read()

# Write the configuration manipulator.
self.mkdirabs(os.path.join(self.py_gen_dir, "cfg"))
f = open(os.path.join(self.py_gen_dir, "cfg", self.name + "Config.py"), 'w')
pycfgdata = self.replace_infinity(self.group.to_dict())
f.write(Template(template).substitute(
name=self.name,
pkgname=self.pkgname, pycfgdata=pycfgdata))
for const in self.constants:
f.write(Template("${configname}_${name} = $v\n").substitute(
const, v=repr(const['value']),
configname=self.name))
f.close()

f = open(os.path.join(self.py_gen_dir, "cfg", "__init__.py"), 'a')
f.close()
with open(os.path.join(self.py_gen_dir, "cfg", self.name + "Config.py"), 'w') as f:
pycfgdata = self.replace_infinity(self.group.to_dict())
f.write(Template(template).substitute(
name=self.name,
pkgname=self.pkgname, pycfgdata=pycfgdata))
for const in self.constants:
f.write(Template("${configname}_${name} = $v\n").substitute(
const, v=repr(const['value']),
configname=self.name))

with open(os.path.join(self.py_gen_dir, "cfg", "__init__.py"), 'a'):
pass