mirror of
https://github.com/ArduPilot/ardupilot
synced 2025-01-03 06:28:27 -04:00
01bf3fe6a1
make it easy for consumers to get at this data. leaves the old form, so we don't break older consumers
138 lines
6.2 KiB
Python
138 lines
6.2 KiB
Python
from lxml import etree
|
|
|
|
from emit import Emit
|
|
from param import known_param_fields, known_units
|
|
import re
|
|
|
|
# Emit ArduPilot documentation in an machine readable XML format
|
|
class XmlEmit(Emit):
|
|
def __init__(self, *args, **kwargs):
|
|
Emit.__init__(self, *args, **kwargs)
|
|
self.wiki_fname = 'apm.pdef.xml'
|
|
self.f = open(self.wiki_fname, mode='w')
|
|
self.preamble = '''<?xml version="1.0" encoding="utf-8"?>
|
|
<!-- Dynamically generated list of documented parameters (generated by param_parse.py) -->
|
|
'''
|
|
self.f.write(self.preamble)
|
|
self.paramfile = etree.Element('paramfile')
|
|
self.vehicles = etree.SubElement(self.paramfile, 'vehicles')
|
|
self.libraries = etree.SubElement(self.paramfile, 'libraries')
|
|
self.current_element = self.vehicles
|
|
|
|
def close(self):
|
|
# etree.indent(self.paramfile) # not available on thor, Ubuntu 16.04
|
|
pretty_xml = etree.tostring(self.paramfile, pretty_print=True, encoding='unicode')
|
|
self.f.write(pretty_xml)
|
|
self.f.close()
|
|
|
|
def emit_comment(self, s):
|
|
self.f.write("<!-- " + s + " -->")
|
|
|
|
def start_libraries(self):
|
|
self.current_element = self.libraries
|
|
|
|
def add_xml_subtree_for_param_field(self, param, xml_param, field, new_element_name, item_name):
|
|
'''assumes that "field" is a comma-separated list of items,
|
|
creates a subtree with those elements within'''
|
|
xml_values = etree.SubElement(xml_param, new_element_name)
|
|
values = (param.__dict__[field]).split(',')
|
|
nv_unsorted = {}
|
|
for value in values:
|
|
v = value.split(':')
|
|
if len(v) != 2:
|
|
raise ValueError("Bad value (%s)" % v)
|
|
# i.e. numeric value, string label
|
|
if v[0] in nv_unsorted:
|
|
raise ValueError("%s already exists" % v[0])
|
|
nv_unsorted[v[0]] = v[1]
|
|
|
|
all_keys = nv_unsorted.keys()
|
|
if hasattr(param, 'SortValues'):
|
|
sort = getattr(param, 'SortValues').lower()
|
|
zero_at_top = False
|
|
if sort == 'alphabeticalzeroattop':
|
|
zero_at_top = True
|
|
else:
|
|
raise ValueError("Unknown sort (%s)" % sort)
|
|
|
|
all_keys = self.sorted_Values_keys(nv_unsorted, zero_at_top=zero_at_top)
|
|
|
|
for key in all_keys:
|
|
value = nv_unsorted[key].strip()
|
|
code = key.rstrip().strip()
|
|
xml_value = etree.SubElement(xml_values, item_name, code=code)
|
|
xml_value.text = value
|
|
|
|
def sorted_Values_keys(self, nv_pairs, zero_at_top=False):
|
|
'''sorts name/value pairs derived from items in @Values. Sorts by
|
|
value, with special attention paid to common "Do nothing" values'''
|
|
keys = nv_pairs.keys()
|
|
def sort_key(value):
|
|
description = nv_pairs[value]
|
|
if zero_at_top and value == "0":
|
|
# make sure this item goes at the top of the list:
|
|
return "AAAAAAA"
|
|
return description
|
|
return sorted(keys, key=sort_key)
|
|
|
|
def emit(self, g):
|
|
xml_parameters = etree.SubElement(self.current_element, 'parameters', name=g.reference) # i.e. ArduPlane
|
|
|
|
for param in g.params:
|
|
# Begin our parameter node
|
|
if hasattr(param, 'DisplayName'):
|
|
xml_param = etree.SubElement(xml_parameters, 'param', humanName=param.DisplayName, name=param.name) # i.e. ArduPlane (ArduPlane:FOOPARM)
|
|
else:
|
|
xml_param = etree.SubElement(xml_parameters, 'param', name=param.name)
|
|
|
|
if hasattr(param, 'Description'):
|
|
xml_param.set('documentation', param.Description) # i.e. parameter docs
|
|
if hasattr(param, 'User'):
|
|
xml_param.set('user', param.User) # i.e. Standard or Advanced
|
|
|
|
if hasattr(param, 'Calibration'):
|
|
xml_param.set('calibration', param.Calibration)
|
|
|
|
# Add values as chidren of this node
|
|
for field in param.__dict__.keys():
|
|
if not self.should_emit_field(param, field):
|
|
continue
|
|
if field not in ['name', 'DisplayName', 'Description', 'User', 'SortValues'] and field in known_param_fields:
|
|
# we emit Bitmask as both a sub-element (so that
|
|
# consumers don't need to parse the Bimask list),
|
|
# but also as a text so we don't break existing
|
|
# implementations. Contrast with "values", which
|
|
# is only emitted as a sub-element.
|
|
if field == 'Bitmask':
|
|
self.add_xml_subtree_for_param_field(
|
|
param,
|
|
xml_param,
|
|
field='Bitmask',
|
|
new_element_name='bitmask',
|
|
item_name='bit',
|
|
)
|
|
|
|
if field == 'Values' and Emit.prog_values_field.match(param.__dict__[field]):
|
|
self.add_xml_subtree_for_param_field(
|
|
param,
|
|
xml_param,
|
|
field='Values',
|
|
new_element_name='values',
|
|
item_name='value',
|
|
)
|
|
|
|
elif field == 'Units':
|
|
abreviated_units = param.__dict__[field]
|
|
if abreviated_units != '':
|
|
units = known_units[abreviated_units] # use the known_units dictionary to convert the abreviated unit into a full textual one
|
|
xml_field1 = etree.SubElement(xml_param, 'field', name=field) # i.e. A/s
|
|
xml_field1.text = abreviated_units
|
|
xml_field2 = etree.SubElement(xml_param, 'field', name='UnitText') # i.e. ampere per second
|
|
xml_field2.text = units
|
|
else:
|
|
xml_field = etree.SubElement(xml_param, 'field', name=field)
|
|
xml_field.text = param.__dict__[field]
|
|
|
|
if xml_param.text is None and not len(xml_param):
|
|
xml_param.text = '\n' # add </param> on next line in case of empty element.
|