2020-10-07 17:14:20 -03:00
|
|
|
from lxml import etree
|
2013-02-16 18:01:55 -04:00
|
|
|
|
|
|
|
from emit import Emit
|
2017-05-02 07:36:14 -03:00
|
|
|
from param import known_param_fields, known_units
|
2024-01-16 00:11:45 -04:00
|
|
|
import re
|
2016-07-31 07:22:06 -03:00
|
|
|
|
2021-04-07 02:07:29 -03:00
|
|
|
# Emit ArduPilot documentation in an machine readable XML format
|
2013-02-16 18:01:55 -04:00
|
|
|
class XmlEmit(Emit):
|
2021-04-07 00:12:41 -03:00
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
Emit.__init__(self, *args, **kwargs)
|
2020-09-15 07:27:21 -03:00
|
|
|
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)
|
2020-10-07 17:14:20 -03:00
|
|
|
self.paramfile = etree.Element('paramfile')
|
|
|
|
self.vehicles = etree.SubElement(self.paramfile, 'vehicles')
|
|
|
|
self.libraries = etree.SubElement(self.paramfile, 'libraries')
|
|
|
|
self.current_element = self.vehicles
|
2016-07-31 07:22:06 -03:00
|
|
|
|
|
|
|
def close(self):
|
2021-04-01 22:53:42 -03:00
|
|
|
# etree.indent(self.paramfile) # not available on thor, Ubuntu 16.04
|
2020-10-07 17:14:20 -03:00
|
|
|
pretty_xml = etree.tostring(self.paramfile, pretty_print=True, encoding='unicode')
|
|
|
|
self.f.write(pretty_xml)
|
2016-07-31 07:22:06 -03:00
|
|
|
self.f.close()
|
|
|
|
|
2013-02-16 18:01:55 -04:00
|
|
|
def emit_comment(self, s):
|
|
|
|
self.f.write("<!-- " + s + " -->")
|
|
|
|
|
|
|
|
def start_libraries(self):
|
2020-10-07 17:14:20 -03:00
|
|
|
self.current_element = self.libraries
|
2016-07-31 07:22:06 -03:00
|
|
|
|
2024-09-24 06:38:50 -03:00
|
|
|
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
|
|
|
|
|
2024-01-16 00:11:45 -04:00
|
|
|
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)
|
|
|
|
|
2018-12-06 05:07:51 -04:00
|
|
|
def emit(self, g):
|
2021-05-12 01:45:27 -03:00
|
|
|
xml_parameters = etree.SubElement(self.current_element, 'parameters', name=g.reference) # i.e. ArduPlane
|
2016-07-31 07:22:06 -03:00
|
|
|
|
2013-02-16 18:01:55 -04:00
|
|
|
for param in g.params:
|
|
|
|
# Begin our parameter node
|
2016-07-31 07:22:06 -03:00
|
|
|
if hasattr(param, 'DisplayName'):
|
2020-10-07 17:14:20 -03:00
|
|
|
xml_param = etree.SubElement(xml_parameters, 'param', humanName=param.DisplayName, name=param.name) # i.e. ArduPlane (ArduPlane:FOOPARM)
|
2013-02-16 18:01:55 -04:00
|
|
|
else:
|
2020-10-07 17:14:20 -03:00
|
|
|
xml_param = etree.SubElement(xml_parameters, 'param', name=param.name)
|
2016-07-31 07:22:06 -03:00
|
|
|
|
|
|
|
if hasattr(param, 'Description'):
|
2020-10-07 17:14:20 -03:00
|
|
|
xml_param.set('documentation', param.Description) # i.e. parameter docs
|
2013-04-15 21:51:01 -03:00
|
|
|
if hasattr(param, 'User'):
|
2020-10-07 17:14:20 -03:00
|
|
|
xml_param.set('user', param.User) # i.e. Standard or Advanced
|
2016-07-31 07:22:06 -03:00
|
|
|
|
2020-02-01 20:56:29 -04:00
|
|
|
if hasattr(param, 'Calibration'):
|
2020-10-07 17:14:20 -03:00
|
|
|
xml_param.set('calibration', param.Calibration)
|
2016-07-31 07:22:06 -03:00
|
|
|
|
2013-02-16 18:01:55 -04:00
|
|
|
# Add values as chidren of this node
|
|
|
|
for field in param.__dict__.keys():
|
2024-09-04 08:02:52 -03:00
|
|
|
if not self.should_emit_field(param, field):
|
|
|
|
continue
|
2024-01-16 00:11:45 -04:00
|
|
|
if field not in ['name', 'DisplayName', 'Description', 'User', 'SortValues'] and field in known_param_fields:
|
2024-09-24 06:38:50 -03:00
|
|
|
# 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',
|
|
|
|
)
|
|
|
|
|
2013-02-16 18:01:55 -04:00
|
|
|
if field == 'Values' and Emit.prog_values_field.match(param.__dict__[field]):
|
2024-09-24 06:38:50 -03:00
|
|
|
self.add_xml_subtree_for_param_field(
|
|
|
|
param,
|
|
|
|
xml_param,
|
|
|
|
field='Values',
|
|
|
|
new_element_name='values',
|
|
|
|
item_name='value',
|
|
|
|
)
|
2016-07-31 07:22:06 -03:00
|
|
|
|
2017-05-02 07:36:14 -03:00
|
|
|
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
|
2020-10-07 17:14:20 -03:00
|
|
|
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
|
2013-02-16 18:01:55 -04:00
|
|
|
else:
|
2020-10-07 17:14:20 -03:00
|
|
|
xml_field = etree.SubElement(xml_param, 'field', name=field)
|
|
|
|
xml_field.text = param.__dict__[field]
|
2013-02-16 18:01:55 -04:00
|
|
|
|
2020-10-07 17:14:20 -03:00
|
|
|
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.
|