Path: blob/master/Tools/autotest/param_metadata/rstemit.py
9670 views
#!/usr/bin/env python312# flake8: noqa34import re5from param import known_param_fields, known_units6from emit import Emit7try:8from cgi import escape as cescape9except Exception:10from html import escape as cescape111213# Emit docs in a RST format14class RSTEmit(Emit):15def blurb(self):16return """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.1718This 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.19""" # noqa2021def toolname(self):22return "Tools/autotest/param_metadata/param_parse.py"2324def output_fname(self):25return 'Parameters.rst'2627def __init__(self, *args, **kwargs):28Emit.__init__(self, *args, **kwargs)29self.f = open(self.output_fname(), mode='w')30self.spacer = re.compile("^", re.MULTILINE)31self.rstescape = re.compile("([^a-zA-Z0-9\n ])")32self.emitted_sitl_heading = False33parameterlisttype = "Complete Parameter List"34parameterlisttype += "\n" + "=" * len(parameterlisttype)35self.preamble = """.. Dynamically generated list of documented parameters36.. This page was generated using {toolname}3738.. DO NOT EDIT394041.. _parameters:4243{parameterlisttype}4445{blurb}4647""".format(blurb=self.escape(self.blurb()),48parameterlisttype=parameterlisttype,49toolname=self.escape(self.toolname()))50self.t = ''5152def escape(self, s):53ret = re.sub(self.rstescape, r"\\\g<1>", s)54return ret5556def close(self):57self.f.write(self.preamble)58self.f.write(self.t)59self.f.close()6061def start_libraries(self):62pass6364def tablify_row(self, rowheading, row, widths, height):65joiner = "|"6667row_lines = [x.split("\n") for x in row]68for row_line in row_lines:69row_line.extend([""] * (height - len(row_line)))70if rowheading is not None:71rowheading_lines = rowheading.split("\n")72rowheading_lines.extend([""] * (height - len(rowheading_lines)))7374out_lines = []75for i in range(0, height):76out_line = ""77if rowheading is not None:78rowheading_line = rowheading_lines[i]79out_line += joiner + " " + rowheading_line + " " * (widths[0] - len(rowheading_line) - 1)80joiner = "#"81j = 082for item in row_lines:83widthnum = j84if rowheading is not None:85widthnum += 186line = item[i]87out_line += joiner + " " + line + " " * (widths[widthnum] - len(line) - 1)88joiner = "|"89j += 190out_line += "|"91out_lines.append(out_line)92return "\n".join(out_lines)9394def tablify_longest_row_length(self, rows, rowheadings, headings):95check_width_rows = rows[:]96if headings is not None:97check_width_rows.append(headings)98longest_row_length = 099for row in check_width_rows:100if len(row) > longest_row_length:101longest_row_length = len(row)102if rowheadings is not None:103longest_row_length += 1104return longest_row_length105106def longest_line_in_string(self, string):107longest = 0108for line in string.split("\n"):109if len(line) > longest:110longest = len(line)111return longest112113def tablify_calc_row_widths_heights(self, rows, rowheadings, headings):114rows_to_check = []115if headings is not None:116rows_to_check.append(headings)117rows_to_check.extend(rows[:])118119heights = [0] * len(rows_to_check)120121longest_row_length = self.tablify_longest_row_length(rows, rowheadings, headings)122widths = [0] * longest_row_length123124all_rowheadings = []125if rowheadings is not None:126if headings is not None:127all_rowheadings.append("")128all_rowheadings.extend(rowheadings)129130for rownum in range(0, len(rows_to_check)):131row = rows_to_check[rownum]132values_to_check = []133if rowheadings is not None:134values_to_check.append(all_rowheadings[rownum])135values_to_check.extend(row[:])136colnum = 0137for value in values_to_check:138height = len(value.split("\n"))139if height > heights[rownum]:140heights[rownum] = height141longest_line = self.longest_line_in_string(value)142width = longest_line + 2 # +2 for leading/trailing ws143if width > widths[colnum]:144widths[colnum] = width145colnum += 1146return (widths, heights)147148def tablify(self, rows, headings=None, rowheadings=None):149150(widths, heights) = self.tablify_calc_row_widths_heights(rows, rowheadings, headings)151152# create dividing lines153bar = ""154heading_bar = ""155for width in widths:156bar += "+"157heading_bar += "+"158bar += "-" * width159heading_bar += "=" * width160bar += "+"161heading_bar += "+"162163# create table164ret = bar + "\n"165if headings is not None:166rowheading = None167if rowheadings is not None:168rowheading = ""169ret += self.tablify_row(rowheading, headings, widths, heights[0]) + "\n"170ret += heading_bar + "\n"171for i in range(0, len(rows)):172rowheading = None173height = i174if rowheadings is not None:175rowheading = rowheadings[i]176if headings is not None:177height += 1178ret += self.tablify_row(rowheading, rows[i], widths, heights[height]) + "\n"179ret += bar + "\n"180181return ret182183def render_prog_values_field(self, render_info, param, field):184values = (param.__dict__[field]).split(',')185rows = []186for value in values:187v = [x.strip() for x in value.split(':')]188rows.append(v)189return self.tablify(rows, headings=render_info["headings"])190191def render_table_headings(self, ret, row, headings, field_table_info, field, param):192row.append(self.render_prog_values_field(field_table_info[field], param, field))193return ''194195def emit(self, g):196# make only a single group for SIM_ parameters197do_emit_heading = True198if g.reference.startswith("SIM_"):199if self.emitted_sitl_heading:200do_emit_heading = False201self.emitted_sitl_heading = True202tag = "Simulation Parameters"203reference = "parameters_sim"204else:205tag = '%s Parameters' % self.escape(g.reference)206reference = "parameters_" + g.reference207208field_table_info = {209"Values": {210"headings": ['Value', 'Meaning'],211},212"Bitmask": {213"headings": ['Bit', 'Meaning'],214},215}216217ret = ""218if do_emit_heading:219ret = """220221.. _{reference}:222223{tag}224{underline}225""".format(tag=tag, underline="-" * len(tag),226reference=reference)227228for param in g.params:229if not self.should_emit_param(param):230continue231if not hasattr(param, 'DisplayName') or not hasattr(param, 'Description'):232continue233d = param.__dict__234235# Get param path if defined (i.e. is duplicate parameter)236param_path = getattr(param, 'path', '')237238name = param.name.split(':')[-1]239240tag_param_path = ' (%s)' % param_path if param_path else ''241tag = '%s%s: %s' % (self.escape(name), self.escape(tag_param_path), self.escape(param.DisplayName),)242243tag = tag.strip()244reference = param.name245# remove e.g. "ArduPlane:" from start of parameter name:246reference = reference.split(":")[-1]247if param_path:248reference += '__' + param_path249250ret += """251252.. _{reference}:253254{tag}255{tag_underline}256""".format(tag=tag, tag_underline='~' * len(tag), reference=reference)257258if d.get('User', None) == 'Advanced':259ret += '\n| *Note: This parameter is for advanced users*'260if d.get('RebootRequired', None) == 'True':261ret += '\n| *Note: Reboot required after change*'262elif 'RebootRequired' in d and d.get('RebootRequired') != 'True':263raise Exception("Bad RebootRequired metadata tag value for {} in {}".format(d.get('name'),d.get('real_path')))264ret += "\n\n%s\n" % self.escape(param.Description)265266headings = []267row = []268for field in sorted(param.__dict__.keys()):269if not self.should_emit_field(param, field):270continue271if (field not in ['name', 'DisplayName', 'Description', 'User', 'SortValues', 'RebootRequired'] and272field in known_param_fields):273headings.append(field)274if field in field_table_info and Emit.prog_values_field.match(param.__dict__[field]):275ret += self.render_table_headings(ret, row, headings, field_table_info, field, param)276elif field == "Range":277(param_min, param_max) = (param.__dict__[field]).split(' ')278row.append("%s to %s" % (param_min, param_max,))279elif field == 'Units':280abbreviated_units = param.__dict__[field]281if abbreviated_units != '':282# use the known_units dictionary to283# convert the abbreviated unit into a full284# textual one:285units = known_units[abbreviated_units]286row.append(cescape(units))287else:288row.append(cescape(param.__dict__[field]))289if len(row):290ret += "\n\n" + self.tablify([row], headings=headings) + "\n\n"291self.t += ret + "\n"292293294def table_test():295e = RSTEmit()296print("Test 1")297print(e.tablify([["A", "B"], ["C", "D"]]))298299print("Test 2")300print(e.tablify([["A", "B"], ["CD\nE", "FG"]]))301302print("Test 3")303print(e.tablify([["A", "B"], ["CD\nEF", "GH"]], rowheadings=["r1", "row2"]))304305print("Test 4")306print(e.tablify([["A", "B"], ["CD\nEF", "GH"]], headings=["c1", "col2"]))307308print("Test 5")309print(e.tablify([["A", "B"], ["CD\nEF", "GH"]], headings=["c1", "col2"], rowheadings=["r1", "row2"]))310311312if __name__ == '__main__':313table_test()314315316