Path: blob/master/Tools/autotest/param_metadata/xmlemit.py
9743 views
# flake8: noqa12from lxml import etree34from emit import Emit5from param import known_param_fields, known_units6import re78# Emit ArduPilot documentation in an machine readable XML format9class XmlEmit(Emit):10def __init__(self, *args, **kwargs):11Emit.__init__(self, *args, **kwargs)12self.wiki_fname = 'apm.pdef.xml'13self.f = open(self.wiki_fname, mode='w')14self.preamble = '''<?xml version="1.0" encoding="utf-8"?>15<!-- Dynamically generated list of documented parameters (generated by param_parse.py) -->16'''17self.f.write(self.preamble)18self.paramfile = etree.Element('paramfile')19self.vehicles = etree.SubElement(self.paramfile, 'vehicles')20self.libraries = etree.SubElement(self.paramfile, 'libraries')21self.current_element = self.vehicles2223def close(self):24# etree.indent(self.paramfile) # not available on thor, Ubuntu 16.0425pretty_xml = etree.tostring(self.paramfile, pretty_print=True, encoding='unicode')26self.f.write(pretty_xml)27self.f.close()2829def emit_comment(self, s):30self.f.write("<!-- " + s + " -->")3132def start_libraries(self):33self.current_element = self.libraries3435def add_xml_subtree_for_param_field(self, param, xml_param, field, new_element_name, item_name):36'''assumes that "field" is a comma-separated list of items,37creates a subtree with those elements within'''38xml_values = etree.SubElement(xml_param, new_element_name)39values = (param.__dict__[field]).split(',')40nv_unsorted = {}41for value in values:42v = value.split(':')43if len(v) != 2:44raise ValueError("Bad value (%s)" % v)45# i.e. numeric value, string label46if v[0] in nv_unsorted:47raise ValueError("%s already exists" % v[0])48nv_unsorted[v[0]] = v[1]4950all_keys = nv_unsorted.keys()51if hasattr(param, 'SortValues'):52sort = getattr(param, 'SortValues').lower()53zero_at_top = False54if sort == 'alphabeticalzeroattop':55zero_at_top = True56else:57raise ValueError("Unknown sort (%s)" % sort)5859all_keys = self.sorted_Values_keys(nv_unsorted, zero_at_top=zero_at_top)6061for key in all_keys:62value = nv_unsorted[key].strip()63code = key.rstrip().strip()64xml_value = etree.SubElement(xml_values, item_name, code=code)65xml_value.text = value6667def sorted_Values_keys(self, nv_pairs, zero_at_top=False):68'''sorts name/value pairs derived from items in @Values. Sorts by69value, with special attention paid to common "Do nothing" values'''70keys = nv_pairs.keys()71def sort_key(value):72description = nv_pairs[value]73if zero_at_top:74# make sure -1 and 0 appear at the top of the list75if value == "-1":76return "0000000"77if value == "0":78return "0000001"79return description80return sorted(keys, key=sort_key)8182def emit(self, g):83xml_parameters = etree.SubElement(self.current_element, 'parameters', name=g.reference) # i.e. ArduPlane8485for param in g.params:86if not self.should_emit_param(param):87continue88# Begin our parameter node89if hasattr(param, 'DisplayName'):90xml_param = etree.SubElement(xml_parameters, 'param', humanName=param.DisplayName, name=param.name) # i.e. ArduPlane (ArduPlane:FOOPARM)91else:92xml_param = etree.SubElement(xml_parameters, 'param', name=param.name)9394if hasattr(param, 'Description'):95xml_param.set('documentation', param.Description) # i.e. parameter docs96if hasattr(param, 'User'):97xml_param.set('user', param.User) # i.e. Standard or Advanced9899if hasattr(param, 'Calibration'):100xml_param.set('calibration', param.Calibration)101102# Add values as children of this node103for field in param.__dict__.keys():104if not self.should_emit_field(param, field):105continue106if field not in ['name', 'DisplayName', 'Description', 'User', 'SortValues'] and field in known_param_fields:107# we emit Bitmask as both a sub-element (so that108# consumers don't need to parse the Bitmask list),109# but also as a text so we don't break existing110# implementations. Contrast with "values", which111# is only emitted as a sub-element.112if field == 'Bitmask':113self.add_xml_subtree_for_param_field(114param,115xml_param,116field='Bitmask',117new_element_name='bitmask',118item_name='bit',119)120121if field == 'Values' and Emit.prog_values_field.match(param.__dict__[field]):122self.add_xml_subtree_for_param_field(123param,124xml_param,125field='Values',126new_element_name='values',127item_name='value',128)129130elif field == 'Units':131abreviated_units = param.__dict__[field]132if abreviated_units != '':133units = known_units[abreviated_units] # use the known_units dictionary to convert the abbreviated unit into a full textual one134xml_field1 = etree.SubElement(xml_param, 'field', name=field) # i.e. A/s135xml_field1.text = abreviated_units136xml_field2 = etree.SubElement(xml_param, 'field', name='UnitText') # i.e. ampere per second137xml_field2.text = units138else:139xml_field = etree.SubElement(xml_param, 'field', name=field)140xml_field.text = param.__dict__[field]141142if xml_param.text is None and not len(xml_param):143xml_param.text = '\n' # add </param> on next line in case of empty element.144145146