4 from copy
import deepcopy
6 import xml.etree.ElementTree
as et
16 LIBARCOMMANDS_GIT_OWNER =
"Parrot-Developers" 17 LIBARCOMMANDS_GIT_HASH =
"ab28dab91845cd36c4d7002b55f70805deaff3c8" 82 "string":
"std::string",
86 blacklist_settings_keys = set([
"wifiSecurity"])
88 min_max_regex = re.compile(
'\[([0-9\.\-]+)\:([0-9\.\-]+)\]')
89 rend = pystache.Renderer()
92 return rend.render_path(
"templates/url.mustache",
93 {
"repo_owner": LIBARCOMMANDS_GIT_OWNER,
"hash": LIBARCOMMANDS_GIT_HASH,
"filename": filename})
96 f = urllib2.urlopen(url)
102 return (
not name.find(
"State") == -1)
and (name.find(
"Settings") == -1)
105 return (
not name.find(
"Settings") == -1)
and (name.find(
"State") == -1)
108 return re.sub(
"\s\s+",
" ", text.strip().replace(
'\n',
'').replace(
'\r',
'')).replace(
'"',
'').replace(
"'",
"")
111 return text.lower().title()
114 m = min_max_regex.search(arg_comment)
116 logging.info(
" ... [min:max]")
117 return [float(m.group(1)), float(m.group(2))]
118 elif (arg_comment.lower().find(
"m/s2") != -1):
119 logging.info(
" ... acc (m/s2)")
121 elif (arg_comment.lower().find(
"m/s") != -1):
122 logging.info(
" ... speed (m/s)")
124 elif (arg_comment.lower().find(
"in meters") != -1)
or (arg_comment.lower().find(
"in m") != -1):
125 logging.info(
" ... meters")
127 elif (arg_comment.lower().find(
"in degree/s") != -1):
128 logging.info(
" ... rotations speed degrees/s")
130 elif (arg_comment.lower().find(
"in degree") != -1):
131 logging.info(
" ... degrees")
132 return [-180.0, 180.0]
133 elif (arg_comment.lower().find(
"1") != -1)
and (arg_comment.lower().find(
"0") != -1):
134 logging.info(
" ... bool")
136 elif (arg_comment.lower().find(
"latitude") != -1):
137 logging.info(
" ... latitude")
139 elif (arg_comment.lower().find(
"longitude") != -1):
140 logging.info(
" ... longitude")
141 return [-180.0, 180.0]
142 elif (arg_comment.lower().find(
"[rad/s]") != -1):
143 logging.info(
" ... angular speed (rad/s)")
145 elif (arg_comment.lower().find(
"channel") != -1):
146 logging.info(
" ... unknown int")
148 elif (arg_comment.lower().find(
"second") != -1):
149 logging.info(
" ... time (s)")
155 return datetime.datetime.now().strftime(
"%Y-%m-%d")
159 project = xml_filename.split(
".")[0]
161 logging.info(
"XML Filename: %s" % (xml_filename, ))
162 logging.info(
"Fetching source XML file for project %s " % (project, ))
163 logging.info(
"URL: %s" % (xml_url, ))
165 xml_root = et.fromstring(xml)
168 logging.info(
"Iterating all State <class> tags ...")
170 generator = os.path.basename(__file__)
171 generator_git_hash = subprocess.check_output([
'git',
'rev-parse',
'--short',
'HEAD']).strip()
177 "generator": generator,
178 "generator_git_hash": generator_git_hash,
180 "frame_id":
"base_link",
185 for cl
in xml_root.iter(
"class"):
191 for cmd
in cl.iter(
"cmd"):
193 msg_name =
cap_word(project) + cl.attrib[
"name"] + cmd.attrib[
"name"]
195 comment_el = cmd.find(
"comment")
196 msg_file_comment =
"" 197 if not comment_el
is None:
198 msg_file_comment = comment_el.attrib[
"desc"]
202 "msg_filename": msg_name,
204 "generator": generator,
205 "generator_git_hash": generator_git_hash,
206 "msg_file_comment":
strip_text(msg_file_comment),
211 cpp_class_dict_key = rend.render_path(
"templates/dictionary_key.mustache",
212 {
"project": project.upper(),
"class": cl.attrib[
"name"].upper(),
"cmd": cmd.attrib[
"name"].upper()})
214 cpp_class_name = msg_name
215 cpp_class_instance_name = project.lower() +
"_" + cl.attrib[
"name"].lower() +
"_" + cmd.attrib[
"name"].lower() +
"_ptr";
216 cpp_class_param_name =
"states/enable_" + cl.attrib[
"name"].lower() +
"_" + cmd.attrib[
"name"].lower()
217 topic_name =
"states/" + project +
"/" + cl.attrib[
"name"] +
"/" + cmd.attrib[
"name"]
220 for arg
in cmd.iter(
"arg"):
222 f_name = arg.attrib[
"name"]
223 f_type = ROS_TYPE_MAP[arg.attrib.get(
"type",
"bool")]
226 if (f_type ==
"enum"):
229 for enum
in arg.iter(
"enum"):
231 "constant_name": f_name +
"_" + enum.attrib[
"name"],
232 "constant_value": counter,
237 d[
"msg_field"].append({
238 "msg_field_type": f_type,
239 "msg_field_name": f_name,
240 "msg_field_comment": f_comment,
241 "msg_field_enum": deepcopy(f_enum_list)
246 "cpp_class_arg_key": cpp_class_dict_key +
"_" + arg.attrib[
"name"].upper(),
247 "cpp_class_arg_name": f_name,
248 "cpp_class_arg_sdk_type": BEBOP_TYPE_MAP[arg.attrib.get(
"type",
"bool")]
251 d_msg[msg_name] = deepcopy(d)
254 d_cpp[
"cpp_class"].append({
255 "cpp_class_name": cpp_class_name,
256 "cpp_class_comment":
strip_text(msg_file_comment),
257 "cpp_class_instance_name": cpp_class_instance_name,
258 "cpp_class_param_name": cpp_class_param_name,
259 "topic_name": topic_name,
261 "cpp_class_msg_type": msg_name,
262 "key": cpp_class_dict_key,
263 "cpp_class_arg": deepcopy(arg_list)
266 logging.info(
"... Done iterating, writing results to file")
268 for k, d
in d_msg.items():
269 msg_filename =
"%s.msg" % k
270 logging.info(
"Writing %s" % (msg_filename, ))
271 with open(msg_filename,
"w")
as msg_file:
272 msg_file.write(rend.render_path(
"templates/msg.mustache", d))
274 header_file_name =
"%s_state_callbacks.h" % (project.lower(), )
275 logging.info(
"Writing %s" % (header_file_name, ))
276 with open(header_file_name,
"w")
as header_file:
277 header_file.write(rend.render_path(
"templates/state_callbacks.h.mustache", d_cpp))
279 include_file_name =
"%s_state_callback_includes.h" % (project.lower(), )
280 logging.info(
"Writing %s" % (include_file_name, ))
281 with open(include_file_name,
"w")
as include_file:
282 include_file.write(rend.render_path(
"templates/state_callback_includes.h.mustache", d_cpp))
284 with open(
"callbacks_common.h",
"w")
as header_file:
285 header_file.write(rend.render_path(
"templates/callbacks_common.h.mustache", d_cpp))
287 rst_file_name =
"%s_states_param_topic.rst" % (project.lower(), )
288 logging.info(
"Writing %s" % (rst_file_name, ))
289 with open(rst_file_name,
"w")
as rst_file:
290 rst_file.write(rend.render_path(
"templates/states_param_topic.rst.mustache", d_cpp))
294 project = xml_filename.split(
".")[0]
296 logging.info(
"Fetching source XML file for project %s " % (project, ))
297 logging.info(
"URL: %s" % (xml_url, ))
299 xml_root = et.fromstring(xml)
301 generator = os.path.basename(__file__)
302 generator_git_hash = subprocess.check_output([
'git',
'rev-parse',
'--short',
'HEAD']).strip()
307 "cfg_filename":
"Bebop%s.cfg" % (project.title(), ),
309 "project": project.title(),
311 "generator": generator,
312 "generator_git_hash": generator_git_hash,
317 for cl
in xml_root.iter(
"class"):
323 if not xml_root.findall(
".//class[@name='%s']" % (cl.attrib[
"name"] +
"State", )):
324 logging.warning(
"No State Class for %s " % (cl.attrib[
"name"], ))
330 "cfg_class_name": cl.attrib[
"name"].lower(),
334 for cmd
in cl.iter(
"cmd"):
336 if not xml_root.findall(
".//cmd[@name='%s']" % (cmd.attrib[
"name"] +
"Changed", )):
337 logging.warning(
"No Changed CMD for %s " % (cmd.attrib[
"name"], ))
341 if strip_text(cmd.attrib[
"name"])
in blacklist_settings_keys:
342 logging.warning(
"Key %s is blacklisted!" % (cmd.attrib[
"name"], ))
345 comment_el = cmd.find(
"comment")
347 if not comment_el
is None:
348 cmd_comment = comment_el.attrib[
"desc"]
362 cpp_class_dict_key = rend.render_path(
"templates/dictionary_key.mustache",
363 {
"project": project.upper(),
"class": cl.attrib[
"name"].upper() +
"STATE",
"cmd": cmd.attrib[
"name"].upper() +
"CHANGED"} )
365 cpp_class_name = cl.attrib[
"name"] + cmd.attrib[
"name"]
367 cpp_class_instance_name = project.lower() +
"_" + cl.attrib[
"name"].lower() +
"_" + cmd.attrib[
"name"].lower() +
"_ptr";
368 cpp_class_params = list()
372 for arg
in cmd.iter(
"arg"):
374 arg_name = cl.attrib[
"name"] + cmd.attrib[
"name"] +
cap_word(arg.attrib[
"name"])
375 arg_type = DYN_TYPE_MAP[arg.attrib.get(
"type",
"bool")]
378 arg_enum_list = list()
384 need_enum_cast =
False 385 if (arg_type ==
"enum"):
386 need_enum_cast =
True 388 for enum
in arg.iter(
"enum"):
389 arg_enum_list.append({
390 "constant_name": arg_name +
"_" + enum.attrib[
"name"],
391 "constant_value": counter,
395 elif not arg_type ==
"str_t":
397 logging.info(
"Guessing type of \"%s\"" % (arg_name))
398 logging.info(
" from: %s" % (arg_comment))
400 if (len(minmax_list) == 2):
401 [arg_min, arg_max] = minmax_list
402 logging.info(
" min: %s max: %s" % (arg_min, arg_max))
404 logging.warning(
" Can not guess [min:max] values for this arg, skipping it")
409 if arg_type ==
"int_t" and arg_min == 0
and arg_max == 1:
410 arg_enum_list.append({
411 "constant_name": arg_name +
"_OFF",
413 "constant_comment":
"Disabled" 415 arg_enum_list.append({
416 "constant_name": arg_name +
"_ON",
418 "constant_comment":
"Enabled" 423 if len(minmax_list)
or need_enum_cast
or arg_type ==
"str_t":
425 if arg_type ==
"str_t":
430 cfg_cmd_d[
"cfg_arg"].append({
431 "cfg_arg_type": arg_type,
432 "cfg_arg_name": arg_name,
433 "cfg_arg_comment": arg_comment,
434 "cfg_arg_default": arg_default,
435 "cfg_arg_min": arg_min,
436 "cfg_arg_max": arg_max,
438 "cfg_arg_enum": {
'items' : deepcopy(arg_enum_list)}
if len(arg_enum_list)
else [],
439 "enum_max": counter - 1
444 enum_cast =
"static_cast<eARCOMMANDS_%s_%s_%s_%s>" % (project.upper(), cl.attrib[
"name"].upper(), cmd.attrib[
"name"].upper(), arg.attrib[
"name"].upper())
448 cpp_class_params.append({
449 "cpp_class_arg_key": cpp_class_dict_key +
"_" + arg.attrib[
"name"].upper(),
450 "cpp_class_param_name": arg_name,
451 "cpp_class_comment": cpp_class_comment,
452 "cpp_class_param_enum_cast": enum_cast,
453 "cpp_class_param_type": C_TYPE_MAP[arg.attrib.get(
"type",
"bool")],
454 "cpp_class_param_sdk_type": BEBOP_TYPE_MAP[arg.attrib.get(
"type",
"bool")]
458 if len(cfg_cmd_d[
"cfg_arg"]):
459 cfg_class_d[
"cfg_cmd"].append(deepcopy(cfg_cmd_d))
460 d_cfg[
"cpp_class"].append({
461 "cpp_class_dict_key": cpp_class_dict_key,
462 "cpp_class_name": cpp_class_name,
463 "cpp_class_instance_name": cpp_class_instance_name,
464 "cpp_class_params": deepcopy(cpp_class_params)
467 d_cfg[
"cfg_class"].append(deepcopy(cfg_class_d))
470 logging.info(
"... Done iterating, writing results to file")
473 cfg_file_name = d_cfg[
"cfg_filename"]
474 logging.info(
"Writing %s" % (cfg_file_name, ))
475 with open(cfg_file_name,
"w")
as cfg_file:
476 cfg_file.write(rend.render_path(
"templates/cfg.mustache", d_cfg))
478 header_file_name =
"%s_setting_callbacks.h" % (project.lower(), )
479 logging.info(
"Writing %s" % (header_file_name, ))
480 with open(header_file_name,
"w")
as header_file:
481 header_file.write(rend.render_path(
"templates/setting_callbacks.h.mustache", d_cfg))
483 include_file_name =
"%s_setting_callback_includes.h" % (project.lower(), )
484 logging.info(
"Writing %s" % (include_file_name, ))
485 with open(include_file_name,
"w")
as include_file:
486 include_file.write(rend.render_path(
"templates/setting_callback_includes.h.mustache", d_cfg))
488 rst_file_name =
"%s_settings_param.rst" % (project.lower(), )
489 logging.info(
"Writing %s" % (rst_file_name, ))
490 with open(rst_file_name,
"w")
as rst_file:
491 rst_file.write(rend.render_path(
"templates/settings_param.rst.mustache", d_cfg))
495 logging.basicConfig(level=
"INFO")
502 generator = os.path.basename(__file__)
503 generator_git_hash = subprocess.check_output([
'git',
'rev-parse',
'--short',
'HEAD']).strip()
504 with open(
"last_build_info",
"w")
as last_build_file:
505 last_build_file.write(rend.render_path(
506 "templates/last_build_info.mustache",
508 "source_hash": LIBARCOMMANDS_GIT_HASH,
509 "date": datetime.datetime.now(),
510 "generator": generator,
511 "generator_git_hash": generator_git_hash
514 if __name__ ==
"__main__":
def guess_min_max(arg_comment)
def generate_states(xml_filename)
def is_settings_tag(name)
def get_xml_url(filename)
def generate_settings(xml_filename)