Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Ardupilot
GitHub Repository: Ardupilot/ardupilot
Path: blob/master/Tools/autotest/param_metadata/rstemit.py
9670 views
1
#!/usr/bin/env python3
2
3
# flake8: noqa
4
5
import re
6
from param import known_param_fields, known_units
7
from emit import Emit
8
try:
9
from cgi import escape as cescape
10
except Exception:
11
from html import escape as cescape
12
13
14
# Emit docs in a RST format
15
class RSTEmit(Emit):
16
def blurb(self):
17
return """This is a complete list of the parameters which can be set (e.g. via the MAVLink protocol) to control vehicle behaviour. They are stored in persistent storage on the vehicle.
18
19
This list is automatically generated from the latest ardupilot source code, and so may contain parameters which are not yet in the stable released versions of the code.
20
""" # noqa
21
22
def toolname(self):
23
return "Tools/autotest/param_metadata/param_parse.py"
24
25
def output_fname(self):
26
return 'Parameters.rst'
27
28
def __init__(self, *args, **kwargs):
29
Emit.__init__(self, *args, **kwargs)
30
self.f = open(self.output_fname(), mode='w')
31
self.spacer = re.compile("^", re.MULTILINE)
32
self.rstescape = re.compile("([^a-zA-Z0-9\n ])")
33
self.emitted_sitl_heading = False
34
parameterlisttype = "Complete Parameter List"
35
parameterlisttype += "\n" + "=" * len(parameterlisttype)
36
self.preamble = """.. Dynamically generated list of documented parameters
37
.. This page was generated using {toolname}
38
39
.. DO NOT EDIT
40
41
42
.. _parameters:
43
44
{parameterlisttype}
45
46
{blurb}
47
48
""".format(blurb=self.escape(self.blurb()),
49
parameterlisttype=parameterlisttype,
50
toolname=self.escape(self.toolname()))
51
self.t = ''
52
53
def escape(self, s):
54
ret = re.sub(self.rstescape, r"\\\g<1>", s)
55
return ret
56
57
def close(self):
58
self.f.write(self.preamble)
59
self.f.write(self.t)
60
self.f.close()
61
62
def start_libraries(self):
63
pass
64
65
def tablify_row(self, rowheading, row, widths, height):
66
joiner = "|"
67
68
row_lines = [x.split("\n") for x in row]
69
for row_line in row_lines:
70
row_line.extend([""] * (height - len(row_line)))
71
if rowheading is not None:
72
rowheading_lines = rowheading.split("\n")
73
rowheading_lines.extend([""] * (height - len(rowheading_lines)))
74
75
out_lines = []
76
for i in range(0, height):
77
out_line = ""
78
if rowheading is not None:
79
rowheading_line = rowheading_lines[i]
80
out_line += joiner + " " + rowheading_line + " " * (widths[0] - len(rowheading_line) - 1)
81
joiner = "#"
82
j = 0
83
for item in row_lines:
84
widthnum = j
85
if rowheading is not None:
86
widthnum += 1
87
line = item[i]
88
out_line += joiner + " " + line + " " * (widths[widthnum] - len(line) - 1)
89
joiner = "|"
90
j += 1
91
out_line += "|"
92
out_lines.append(out_line)
93
return "\n".join(out_lines)
94
95
def tablify_longest_row_length(self, rows, rowheadings, headings):
96
check_width_rows = rows[:]
97
if headings is not None:
98
check_width_rows.append(headings)
99
longest_row_length = 0
100
for row in check_width_rows:
101
if len(row) > longest_row_length:
102
longest_row_length = len(row)
103
if rowheadings is not None:
104
longest_row_length += 1
105
return longest_row_length
106
107
def longest_line_in_string(self, string):
108
longest = 0
109
for line in string.split("\n"):
110
if len(line) > longest:
111
longest = len(line)
112
return longest
113
114
def tablify_calc_row_widths_heights(self, rows, rowheadings, headings):
115
rows_to_check = []
116
if headings is not None:
117
rows_to_check.append(headings)
118
rows_to_check.extend(rows[:])
119
120
heights = [0] * len(rows_to_check)
121
122
longest_row_length = self.tablify_longest_row_length(rows, rowheadings, headings)
123
widths = [0] * longest_row_length
124
125
all_rowheadings = []
126
if rowheadings is not None:
127
if headings is not None:
128
all_rowheadings.append("")
129
all_rowheadings.extend(rowheadings)
130
131
for rownum in range(0, len(rows_to_check)):
132
row = rows_to_check[rownum]
133
values_to_check = []
134
if rowheadings is not None:
135
values_to_check.append(all_rowheadings[rownum])
136
values_to_check.extend(row[:])
137
colnum = 0
138
for value in values_to_check:
139
height = len(value.split("\n"))
140
if height > heights[rownum]:
141
heights[rownum] = height
142
longest_line = self.longest_line_in_string(value)
143
width = longest_line + 2 # +2 for leading/trailing ws
144
if width > widths[colnum]:
145
widths[colnum] = width
146
colnum += 1
147
return (widths, heights)
148
149
def tablify(self, rows, headings=None, rowheadings=None):
150
151
(widths, heights) = self.tablify_calc_row_widths_heights(rows, rowheadings, headings)
152
153
# create dividing lines
154
bar = ""
155
heading_bar = ""
156
for width in widths:
157
bar += "+"
158
heading_bar += "+"
159
bar += "-" * width
160
heading_bar += "=" * width
161
bar += "+"
162
heading_bar += "+"
163
164
# create table
165
ret = bar + "\n"
166
if headings is not None:
167
rowheading = None
168
if rowheadings is not None:
169
rowheading = ""
170
ret += self.tablify_row(rowheading, headings, widths, heights[0]) + "\n"
171
ret += heading_bar + "\n"
172
for i in range(0, len(rows)):
173
rowheading = None
174
height = i
175
if rowheadings is not None:
176
rowheading = rowheadings[i]
177
if headings is not None:
178
height += 1
179
ret += self.tablify_row(rowheading, rows[i], widths, heights[height]) + "\n"
180
ret += bar + "\n"
181
182
return ret
183
184
def render_prog_values_field(self, render_info, param, field):
185
values = (param.__dict__[field]).split(',')
186
rows = []
187
for value in values:
188
v = [x.strip() for x in value.split(':')]
189
rows.append(v)
190
return self.tablify(rows, headings=render_info["headings"])
191
192
def render_table_headings(self, ret, row, headings, field_table_info, field, param):
193
row.append(self.render_prog_values_field(field_table_info[field], param, field))
194
return ''
195
196
def emit(self, g):
197
# make only a single group for SIM_ parameters
198
do_emit_heading = True
199
if g.reference.startswith("SIM_"):
200
if self.emitted_sitl_heading:
201
do_emit_heading = False
202
self.emitted_sitl_heading = True
203
tag = "Simulation Parameters"
204
reference = "parameters_sim"
205
else:
206
tag = '%s Parameters' % self.escape(g.reference)
207
reference = "parameters_" + g.reference
208
209
field_table_info = {
210
"Values": {
211
"headings": ['Value', 'Meaning'],
212
},
213
"Bitmask": {
214
"headings": ['Bit', 'Meaning'],
215
},
216
}
217
218
ret = ""
219
if do_emit_heading:
220
ret = """
221
222
.. _{reference}:
223
224
{tag}
225
{underline}
226
""".format(tag=tag, underline="-" * len(tag),
227
reference=reference)
228
229
for param in g.params:
230
if not self.should_emit_param(param):
231
continue
232
if not hasattr(param, 'DisplayName') or not hasattr(param, 'Description'):
233
continue
234
d = param.__dict__
235
236
# Get param path if defined (i.e. is duplicate parameter)
237
param_path = getattr(param, 'path', '')
238
239
name = param.name.split(':')[-1]
240
241
tag_param_path = ' (%s)' % param_path if param_path else ''
242
tag = '%s%s: %s' % (self.escape(name), self.escape(tag_param_path), self.escape(param.DisplayName),)
243
244
tag = tag.strip()
245
reference = param.name
246
# remove e.g. "ArduPlane:" from start of parameter name:
247
reference = reference.split(":")[-1]
248
if param_path:
249
reference += '__' + param_path
250
251
ret += """
252
253
.. _{reference}:
254
255
{tag}
256
{tag_underline}
257
""".format(tag=tag, tag_underline='~' * len(tag), reference=reference)
258
259
if d.get('User', None) == 'Advanced':
260
ret += '\n| *Note: This parameter is for advanced users*'
261
if d.get('RebootRequired', None) == 'True':
262
ret += '\n| *Note: Reboot required after change*'
263
elif 'RebootRequired' in d and d.get('RebootRequired') != 'True':
264
raise Exception("Bad RebootRequired metadata tag value for {} in {}".format(d.get('name'),d.get('real_path')))
265
ret += "\n\n%s\n" % self.escape(param.Description)
266
267
headings = []
268
row = []
269
for field in sorted(param.__dict__.keys()):
270
if not self.should_emit_field(param, field):
271
continue
272
if (field not in ['name', 'DisplayName', 'Description', 'User', 'SortValues', 'RebootRequired'] and
273
field in known_param_fields):
274
headings.append(field)
275
if field in field_table_info and Emit.prog_values_field.match(param.__dict__[field]):
276
ret += self.render_table_headings(ret, row, headings, field_table_info, field, param)
277
elif field == "Range":
278
(param_min, param_max) = (param.__dict__[field]).split(' ')
279
row.append("%s to %s" % (param_min, param_max,))
280
elif field == 'Units':
281
abbreviated_units = param.__dict__[field]
282
if abbreviated_units != '':
283
# use the known_units dictionary to
284
# convert the abbreviated unit into a full
285
# textual one:
286
units = known_units[abbreviated_units]
287
row.append(cescape(units))
288
else:
289
row.append(cescape(param.__dict__[field]))
290
if len(row):
291
ret += "\n\n" + self.tablify([row], headings=headings) + "\n\n"
292
self.t += ret + "\n"
293
294
295
def table_test():
296
e = RSTEmit()
297
print("Test 1")
298
print(e.tablify([["A", "B"], ["C", "D"]]))
299
300
print("Test 2")
301
print(e.tablify([["A", "B"], ["CD\nE", "FG"]]))
302
303
print("Test 3")
304
print(e.tablify([["A", "B"], ["CD\nEF", "GH"]], rowheadings=["r1", "row2"]))
305
306
print("Test 4")
307
print(e.tablify([["A", "B"], ["CD\nEF", "GH"]], headings=["c1", "col2"]))
308
309
print("Test 5")
310
print(e.tablify([["A", "B"], ["CD\nEF", "GH"]], headings=["c1", "col2"], rowheadings=["r1", "row2"]))
311
312
313
if __name__ == '__main__':
314
table_test()
315
316