CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
Ardupilot

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

GitHub Repository: Ardupilot/ardupilot
Path: blob/master/Tools/autotest/param_metadata/xmlemit.py
Views: 1799
1
from lxml import etree
2
3
from emit import Emit
4
from param import known_param_fields, known_units
5
import re
6
7
# Emit ArduPilot documentation in an machine readable XML format
8
class XmlEmit(Emit):
9
def __init__(self, *args, **kwargs):
10
Emit.__init__(self, *args, **kwargs)
11
self.wiki_fname = 'apm.pdef.xml'
12
self.f = open(self.wiki_fname, mode='w')
13
self.preamble = '''<?xml version="1.0" encoding="utf-8"?>
14
<!-- Dynamically generated list of documented parameters (generated by param_parse.py) -->
15
'''
16
self.f.write(self.preamble)
17
self.paramfile = etree.Element('paramfile')
18
self.vehicles = etree.SubElement(self.paramfile, 'vehicles')
19
self.libraries = etree.SubElement(self.paramfile, 'libraries')
20
self.current_element = self.vehicles
21
22
def close(self):
23
# etree.indent(self.paramfile) # not available on thor, Ubuntu 16.04
24
pretty_xml = etree.tostring(self.paramfile, pretty_print=True, encoding='unicode')
25
self.f.write(pretty_xml)
26
self.f.close()
27
28
def emit_comment(self, s):
29
self.f.write("<!-- " + s + " -->")
30
31
def start_libraries(self):
32
self.current_element = self.libraries
33
34
def add_xml_subtree_for_param_field(self, param, xml_param, field, new_element_name, item_name):
35
'''assumes that "field" is a comma-separated list of items,
36
creates a subtree with those elements within'''
37
xml_values = etree.SubElement(xml_param, new_element_name)
38
values = (param.__dict__[field]).split(',')
39
nv_unsorted = {}
40
for value in values:
41
v = value.split(':')
42
if len(v) != 2:
43
raise ValueError("Bad value (%s)" % v)
44
# i.e. numeric value, string label
45
if v[0] in nv_unsorted:
46
raise ValueError("%s already exists" % v[0])
47
nv_unsorted[v[0]] = v[1]
48
49
all_keys = nv_unsorted.keys()
50
if hasattr(param, 'SortValues'):
51
sort = getattr(param, 'SortValues').lower()
52
zero_at_top = False
53
if sort == 'alphabeticalzeroattop':
54
zero_at_top = True
55
else:
56
raise ValueError("Unknown sort (%s)" % sort)
57
58
all_keys = self.sorted_Values_keys(nv_unsorted, zero_at_top=zero_at_top)
59
60
for key in all_keys:
61
value = nv_unsorted[key].strip()
62
code = key.rstrip().strip()
63
xml_value = etree.SubElement(xml_values, item_name, code=code)
64
xml_value.text = value
65
66
def sorted_Values_keys(self, nv_pairs, zero_at_top=False):
67
'''sorts name/value pairs derived from items in @Values. Sorts by
68
value, with special attention paid to common "Do nothing" values'''
69
keys = nv_pairs.keys()
70
def sort_key(value):
71
description = nv_pairs[value]
72
if zero_at_top:
73
# make sure -1 and 0 appear at the top of the list
74
if value == "-1":
75
return "0000000"
76
if value == "0":
77
return "0000001"
78
return description
79
return sorted(keys, key=sort_key)
80
81
def emit(self, g):
82
xml_parameters = etree.SubElement(self.current_element, 'parameters', name=g.reference) # i.e. ArduPlane
83
84
for param in g.params:
85
# Begin our parameter node
86
if hasattr(param, 'DisplayName'):
87
xml_param = etree.SubElement(xml_parameters, 'param', humanName=param.DisplayName, name=param.name) # i.e. ArduPlane (ArduPlane:FOOPARM)
88
else:
89
xml_param = etree.SubElement(xml_parameters, 'param', name=param.name)
90
91
if hasattr(param, 'Description'):
92
xml_param.set('documentation', param.Description) # i.e. parameter docs
93
if hasattr(param, 'User'):
94
xml_param.set('user', param.User) # i.e. Standard or Advanced
95
96
if hasattr(param, 'Calibration'):
97
xml_param.set('calibration', param.Calibration)
98
99
# Add values as chidren of this node
100
for field in param.__dict__.keys():
101
if not self.should_emit_field(param, field):
102
continue
103
if field not in ['name', 'DisplayName', 'Description', 'User', 'SortValues'] and field in known_param_fields:
104
# we emit Bitmask as both a sub-element (so that
105
# consumers don't need to parse the Bimask list),
106
# but also as a text so we don't break existing
107
# implementations. Contrast with "values", which
108
# is only emitted as a sub-element.
109
if field == 'Bitmask':
110
self.add_xml_subtree_for_param_field(
111
param,
112
xml_param,
113
field='Bitmask',
114
new_element_name='bitmask',
115
item_name='bit',
116
)
117
118
if field == 'Values' and Emit.prog_values_field.match(param.__dict__[field]):
119
self.add_xml_subtree_for_param_field(
120
param,
121
xml_param,
122
field='Values',
123
new_element_name='values',
124
item_name='value',
125
)
126
127
elif field == 'Units':
128
abreviated_units = param.__dict__[field]
129
if abreviated_units != '':
130
units = known_units[abreviated_units] # use the known_units dictionary to convert the abreviated unit into a full textual one
131
xml_field1 = etree.SubElement(xml_param, 'field', name=field) # i.e. A/s
132
xml_field1.text = abreviated_units
133
xml_field2 = etree.SubElement(xml_param, 'field', name='UnitText') # i.e. ampere per second
134
xml_field2.text = units
135
else:
136
xml_field = etree.SubElement(xml_param, 'field', name=field)
137
xml_field.text = param.__dict__[field]
138
139
if xml_param.text is None and not len(xml_param):
140
xml_param.text = '\n' # add </param> on next line in case of empty element.
141
142