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