Rename param and paramgroups to airframe and airframegroups

The srcparser.py is specific to each use case (e.g. Airframes, Parameters, px4events, etc as in Tool/* folders).
Therefore it is confusing to have the px_process_airframes.py script handle concept of airframes under the generic name 'params'.

This improves readability and sets the baseground for implementing more specific vehicle type supports, as mentioned in https://github.com/PX4/PX4-user_guide/pull/1858#discussion_r876554728
This commit is contained in:
Junwoo Hwang 2022-05-20 13:10:42 +02:00 committed by Beat Küng
parent ee11b57e75
commit 855eb42c59
5 changed files with 136 additions and 130 deletions

View File

@ -62,7 +62,7 @@ div.frame_variant td, div.frame_variant th {
result += '## %s\n\n' % group.GetClass() result += '## %s\n\n' % group.GetClass()
type_set.add(group.GetClass()) type_set.add(group.GetClass())
result += '### %s\n\n' % group.GetName() result += '### %s\n\n' % group.GetType()
# Display an image of the frame # Display an image of the frame
image_name = group.GetImageName() image_name = group.GetImageName()
@ -73,11 +73,11 @@ div.frame_variant td, div.frame_variant th {
# check if all outputs are equal for the group: if so, show them # check if all outputs are equal for the group: if so, show them
# only once # only once
all_outputs = {} all_outputs = {}
num_configs = len(group.GetParams()) num_configs = len(group.GetAirframes())
for param in group.GetParams(): for airframe in group.GetAirframes():
if not self.IsExcluded(param, board): if not self.IsExcluded(airframe, board):
for output_name in param.GetOutputCodes(): for output_name in airframe.GetOutputCodes():
value = param.GetOutputValue(output_name) value = airframe.GetOutputValue(output_name)
key_value_pair = (output_name, value) key_value_pair = (output_name, value)
if key_value_pair not in all_outputs: if key_value_pair not in all_outputs:
all_outputs[key_value_pair] = 0 all_outputs[key_value_pair] = 0
@ -104,18 +104,17 @@ div.frame_variant td, div.frame_variant th {
result += ' </thead>\n' result += ' </thead>\n'
result += '<tbody>\n' result += '<tbody>\n'
for param in group.GetParams(): for airframe in group.GetAirframes():
if not self.IsExcluded(param, board): if not self.IsExcluded(airframe, board):
#print("generating: {0} {1}".format(param.GetName(), excluded)) name = airframe.GetName()
name = param.GetName() airframe_id = airframe.GetId()
airframe_id = param.GetId()
airframe_id_entry = '<p><code>SYS_AUTOSTART</code> = %s</p>' % (airframe_id) airframe_id_entry = '<p><code>SYS_AUTOSTART</code> = %s</p>' % (airframe_id)
maintainer = param.GetMaintainer() maintainer = airframe.GetMaintainer()
maintainer_entry = '' maintainer_entry = ''
if maintainer != '': if maintainer != '':
maintainer_entry = 'Maintainer: %s' % (html.escape(maintainer)) maintainer_entry = 'Maintainer: %s' % (html.escape(maintainer))
url = param.GetFieldValue('url') url = airframe.GetFieldValue('url')
name_anchor='%s_%s_%s' % (group.GetClass(),group.GetName(),name) name_anchor='%s_%s_%s' % (group.GetClass(),group.GetType(),name)
name_anchor=name_anchor.replace(' ','_').lower() name_anchor=name_anchor.replace(' ','_').lower()
name_anchor=name_anchor.replace('"','_').lower() name_anchor=name_anchor.replace('"','_').lower()
name_anchor='id="%s"' % name_anchor name_anchor='id="%s"' % name_anchor
@ -124,8 +123,8 @@ div.frame_variant td, div.frame_variant th {
name_entry = '<a href="%s">%s</a>' % (url, name) name_entry = '<a href="%s">%s</a>' % (url, name)
outputs = '<ul>' outputs = '<ul>'
has_outputs = False has_outputs = False
for output_name in param.GetOutputCodes(): for output_name in airframe.GetOutputCodes():
value = param.GetOutputValue(output_name) value = airframe.GetOutputValue(output_name)
valstrs = value.split(";") valstrs = value.split(";")
key_value_pair = (output_name, value) key_value_pair = (output_name, value)
if all_outputs[key_value_pair] < num_configs: if all_outputs[key_value_pair] < num_configs:
@ -152,9 +151,9 @@ div.frame_variant td, div.frame_variant th {
self.output = result self.output = result
def IsExcluded(self, param, board): def IsExcluded(self, airframe, board):
for code in param.GetArchCodes(): for code in airframe.GetArchCodes():
if "CONFIG_ARCH_BOARD_{0}".format(code) == board and param.GetArchValue(code) == "exclude": if "CONFIG_ARCH_BOARD_{0}".format(code) == board and airframe.GetArchValue(code) == "exclude":
return True return True
return False return False

View File

@ -3,6 +3,9 @@ import codecs
import os import os
class RCOutput(): class RCOutput():
"""
Generates RC scripts for the airframes
"""
def __init__(self, groups, board, post_start=False): def __init__(self, groups, board, post_start=False):
result = ( "#\n" result = ( "#\n"
@ -34,33 +37,33 @@ class RCOutput():
result += "set AIRFRAME none\n" result += "set AIRFRAME none\n"
result += "\n" result += "\n"
for group in groups: for group in groups:
result += "# GROUP: %s\n\n" % group.GetName() result += "# GROUP: %s\n\n" % group.GetType()
for param in group.GetParams(): for airframe in group.GetAirframes():
excluded = False excluded = False
for code in param.GetArchCodes(): for code in airframe.GetArchCodes():
if "{0}".format(code) == board and param.GetArchValue(code) == "exclude": if "{0}".format(code) == board and airframe.GetArchValue(code) == "exclude":
excluded = True excluded = True
if excluded: if excluded:
continue continue
if post_start: if post_start:
# Path to post-start sript # Path to post-start sript
path = param.GetPostPath() path = airframe.GetPostPath()
else: else:
# Path to start script # Path to start script
path = param.GetPath() path = airframe.GetPath()
if not path: if not path:
continue continue
path = os.path.split(path)[1] path = os.path.split(path)[1]
id_val = param.GetId() id_val = airframe.GetId()
name = param.GetFieldValue("short_desc") name = airframe.GetFieldValue("short_desc")
long_desc = param.GetFieldValue("long_desc") long_desc = airframe.GetFieldValue("long_desc")
result += "#\n" result += "#\n"
result += "# %s\n" % param.GetName() result += "# %s\n" % airframe.GetName()
result += "if param compare SYS_AUTOSTART %s\n" % id_val result += "if param compare SYS_AUTOSTART %s\n" % id_val
result += "then\n" result += "then\n"
result += "\tset AIRFRAME %s\n" % path result += "\tset AIRFRAME %s\n" % path

View File

@ -2,31 +2,38 @@ import sys
import re import re
import os import os
class ParameterGroup(object): class AirframeGroup(object):
""" """
Single parameter group Airframe group
type: specific vehicle type (e.g. VTOL Tiltrotor, VTOL Quadrotor, etc.)
class: vehicle class (e.g. Multicopter, Fixed Wing, etc.)
""" """
def __init__(self, name, af_class): def __init__(self, type, af_class):
self.name = name self.type = type
self.af_class = af_class self.af_class = af_class
self.params = [] self.airframes = []
def AddParameter(self, param): def AddAirframe(self, airframe):
""" """
Add parameter to the group Add airframe to the airframe group
""" """
self.params.append(param) self.airframes.append(airframe)
def GetName(self): def GetType(self):
""" """
Get parameter group name Get airframe group's vehicle type
e.g. VTOL Tiltrotor, VTOL Quadrotor, etc.
""" """
return self.name return self.type
def GetClass(self): def GetClass(self):
""" """
Get parameter group vehicle type. Get airframe group's vehicle class
e.g. Multicopter, Fixed Wing, etc.
""" """
return self.af_class return self.af_class
@ -34,86 +41,84 @@ class ParameterGroup(object):
""" """
Get parameter group image base name (w/o extension) Get parameter group image base name (w/o extension)
""" """
if (self.name == "Standard Plane"): if (self.type == "Standard Plane"):
return "Plane" return "Plane"
elif (self.name == "Flying Wing"): elif (self.type == "Flying Wing"):
return "FlyingWing" return "FlyingWing"
elif (self.name == "Quadrotor x"): elif (self.type == "Quadrotor x"):
return "QuadRotorX" return "QuadRotorX"
elif (self.name == "Quadrotor +"): elif (self.type == "Quadrotor +"):
return "QuadRotorPlus" return "QuadRotorPlus"
elif (self.name == "Hexarotor x"): elif (self.type == "Hexarotor x"):
return "HexaRotorX" return "HexaRotorX"
elif (self.name == "Hexarotor +"): elif (self.type == "Hexarotor +"):
return "HexaRotorPlus" return "HexaRotorPlus"
elif (self.name == "Octorotor +"): elif (self.type == "Octorotor +"):
return "OctoRotorPlus" return "OctoRotorPlus"
elif (self.name == "Octorotor x"): elif (self.type == "Octorotor x"):
return "OctoRotorX" return "OctoRotorX"
elif (self.name == "Octorotor Coaxial"): elif (self.type == "Octorotor Coaxial"):
return "OctoRotorXCoaxial" return "OctoRotorXCoaxial"
elif (self.name == "Octo Coax Wide"): elif (self.type == "Octo Coax Wide"):
return "OctoRotorXCoaxial" return "OctoRotorXCoaxial"
elif (self.name == "Quadrotor Wide"): elif (self.type == "Quadrotor Wide"):
return "QuadRotorWide" return "QuadRotorWide"
elif (self.name == "Quadrotor H"): elif (self.type == "Quadrotor H"):
return "QuadRotorH" return "QuadRotorH"
elif (self.name == "Dodecarotor cox"): elif (self.type == "Dodecarotor cox"):
return "DodecaRotorXCoaxial" return "DodecaRotorXCoaxial"
elif (self.name == "Simulation"): elif (self.type == "Simulation"):
return "AirframeSimulation" return "AirframeSimulation"
elif (self.name == "Plane A-Tail"): elif (self.type == "Plane A-Tail"):
return "PlaneATail" return "PlaneATail"
elif (self.name == "Plane V-Tail"): elif (self.type == "Plane V-Tail"):
return "PlaneVTail" return "PlaneVTail"
elif (self.name == "VTOL Duo Tailsitter"): elif (self.type == "VTOL Duo Tailsitter"):
return "VTOLDuoRotorTailSitter" return "VTOLDuoRotorTailSitter"
elif (self.name == "Standard VTOL"): elif (self.type == "Standard VTOL"):
return "VTOLPlane" return "VTOLPlane"
elif (self.name == "VTOL Quad Tailsitter"): elif (self.type == "VTOL Quad Tailsitter"):
return "VTOLQuadRotorTailSitter" return "VTOLQuadRotorTailSitter"
elif (self.name == "VTOL Tiltrotor"): elif (self.type == "VTOL Tiltrotor"):
return "VTOLTiltRotor" return "VTOLTiltRotor"
elif (self.name == "VTOL Octoplane"): elif (self.type == "VTOL Octoplane"):
return "VTOLPlaneOcto" return "VTOLPlaneOcto"
elif (self.name == "Coaxial Helicopter"): elif (self.type == "Coaxial Helicopter"):
return "HelicopterCoaxial" return "HelicopterCoaxial"
elif (self.name == "Helicopter"): elif (self.type == "Helicopter"):
return "Helicopter" return "Helicopter"
elif (self.name == "Hexarotor Coaxial"): elif (self.type == "Hexarotor Coaxial"):
return "Y6B" return "Y6B"
elif (self.name == "Y6A"): elif (self.type == "Y6A"):
return "Y6A" return "Y6A"
elif (self.name == "Tricopter Y-"): elif (self.type == "Tricopter Y-"):
return "YMinus" return "YMinus"
elif (self.name == "Tricopter Y+"): elif (self.type == "Tricopter Y+"):
return "YPlus" return "YPlus"
elif (self.name == "Autogyro"): elif (self.type == "Autogyro"):
return "Autogyro" return "Autogyro"
elif (self.name == "Airship"): elif (self.type == "Airship"):
return "Airship" return "Airship"
elif (self.name == "Rover"): elif (self.type == "Rover"):
return "Rover" return "Rover"
elif (self.name == "Boat"): elif (self.type == "Boat"):
return "Boat" return "Boat"
elif (self.name == "Balloon"): elif (self.type == "Balloon"):
return "Balloon" return "Balloon"
elif (self.name == "Vectored 6 DOF UUV"): elif (self.type == "Vectored 6 DOF UUV"):
return "Vectored6DofUUV" return "Vectored6DofUUV"
return "AirframeUnknown" return "AirframeUnknown"
def GetParams(self): def GetAirframes(self):
""" """
Returns the parsed list of parameters. Every parameter is a Parameter Returns the parsed list of airframes objects. Note that returned
object. Note that returned object is not a copy. Modifications affect object is not a copy. Modifications affect state of the parser.
state of the parser.
""" """
return sorted(self.airframes, key=lambda x: x.GetId())
return sorted(self.params, key=lambda x: x.GetId()) class Airframe(object):
class Parameter(object):
""" """
Single parameter Single Airframe definition
""" """
# Define sorting order of the fields # Define sorting order of the fields
@ -288,7 +293,7 @@ class SourceParser(object):
} }
def __init__(self): def __init__(self):
self.param_groups = {} self.airframe_groups = {}
def GetSupportedExtensions(self): def GetSupportedExtensions(self):
""" """
@ -347,10 +352,10 @@ class SourceParser(object):
tag, desc = m.group(1, 2) tag, desc = m.group(1, 2)
if (tag == "output"): if (tag == "output"):
key, text = desc.split(' ', 1) key, text = desc.split(' ', 1)
outputs[key] = text; outputs[key] = text
elif (tag == "board"): elif (tag == "board"):
key, text = desc.split(' ', 1) key, text = desc.split(' ', 1)
archs[key] = text; archs[key] = text
else: else:
tags[tag] = desc tags[tag] = desc
current_tag = tag current_tag = tag
@ -427,7 +432,7 @@ class SourceParser(object):
post_path = None post_path = None
# We already know this is an airframe config, so add it # We already know this is an airframe config, so add it
param = Parameter(path, post_path, airframe_name, airframe_type, airframe_class, airframe_id, maintainer) airframe = Airframe(path, post_path, airframe_name, airframe_type, airframe_class, airframe_id, maintainer)
# Done with file, store # Done with file, store
for tag in tags: for tag in tags:
@ -440,24 +445,24 @@ class SourceParser(object):
if tag == "name": if tag == "name":
airframe_name = tags[tag] airframe_name = tags[tag]
else: else:
param.SetField(tag, tags[tag]) airframe.SetField(tag, tags[tag])
# Store outputs # Store outputs
for output in outputs: for output in outputs:
param.SetOutput(output, outputs[output]) airframe.SetOutput(output, outputs[output])
# Store outputs # Store outputs
for arch in archs: for arch in archs:
param.SetArch(arch, archs[arch]) airframe.SetArch(arch, archs[arch])
# Store the parameter # Store the parameter
# Create a class-specific airframe group. This is needed to catch cases where an airframe type might cross classes (e.g. simulation) # Create a class-specific airframe group. This is needed to catch cases where an airframe type might cross classes (e.g. simulation)
class_group_identifier=airframe_type+airframe_class class_group_identifier=airframe_type + airframe_class
if class_group_identifier not in self.param_groups: if class_group_identifier not in self.airframe_groups:
#self.param_groups[airframe_type] = ParameterGroup(airframe_type) #HW TEST REMOVE #self.airframe_groups[airframe_type] = ParameterGroup(airframe_type) #HW TEST REMOVE
self.param_groups[class_group_identifier] = ParameterGroup(airframe_type, airframe_class) self.airframe_groups[class_group_identifier] = AirframeGroup(airframe_type, airframe_class)
self.param_groups[class_group_identifier].AddParameter(param) self.airframe_groups[class_group_identifier].AddAirframe(airframe)
return True return True
@ -473,8 +478,8 @@ class SourceParser(object):
Validates the airframe meta data. Validates the airframe meta data.
""" """
seenParamNames = [] seenParamNames = []
for group in self.GetParamGroups(): for group in self.GetAirframeGroups():
for param in group.GetParams(): for param in group.GetAirframes():
name = param.GetName() name = param.GetName()
board = param.GetFieldValue("board") board = param.GetFieldValue("board")
# Check for duplicates # Check for duplicates
@ -487,27 +492,27 @@ class SourceParser(object):
return True return True
def GetParamGroups(self): def GetAirframeGroups(self):
""" """
Returns the parsed list of parameters. Every parameter is a Parameter Returns the parsed list of Airframe groups. Every Airframe is an Airframe
object. Note that returned object is not a copy. Modifications affect object. Note that returned object is not a copy. Modifications affect
state of the parser. state of the parser.
""" """
groups = self.param_groups.values() groups = self.airframe_groups.values()
groups = sorted(groups, key=lambda x: x.GetName()) groups = sorted(groups, key=lambda x: x.GetType())
groups = sorted(groups, key=lambda x: x.GetClass()) groups = sorted(groups, key=lambda x: x.GetClass())
groups = sorted(groups, key=lambda x: self.priority.get(x.GetName(), 0), reverse=True) groups = sorted(groups, key=lambda x: self.priority.get(x.GetType(), 0), reverse=True)
#Rename duplicate groups to include the class (creating unique headings in page TOC) #Rename duplicate groups to include the class (creating unique headings in page TOC)
duplicate_test=set() duplicate_test=set()
duplicate_set=set() duplicate_set=set()
for group in groups: for group in groups:
if group.GetName() in duplicate_test: if group.GetType() in duplicate_test:
duplicate_set.add(group.GetName()) duplicate_set.add(group.GetType())
else: else:
duplicate_test.add(group.GetName() ) duplicate_test.add(group.GetType() )
for group in groups: for group in groups:
if group.GetName() in duplicate_set: if group.GetType() in duplicate_set:
group.name=group.GetName()+' (%s)' % group.GetClass() group.name=group.GetType()+' (%s)' % group.GetClass()
return groups return groups

View File

@ -28,28 +28,28 @@ class XMLOutput():
xml_version.text = "1" xml_version.text = "1"
for group in groups: for group in groups:
xml_group = ET.SubElement(xml_parameters, "airframe_group") xml_group = ET.SubElement(xml_parameters, "airframe_group")
xml_group.attrib["name"] = group.GetName() xml_group.attrib["name"] = group.GetType()
xml_group.attrib["image"] = group.GetImageName() xml_group.attrib["image"] = group.GetImageName()
for param in group.GetParams(): for airframe in group.GetAirframes():
# check if there is an exclude tag for this airframe # check if there is an exclude tag for this airframe
excluded = False excluded = False
for code in param.GetArchCodes(): for code in airframe.GetArchCodes():
if "CONFIG_ARCH_BOARD_{0}".format(code) == board and param.GetArchValue(code) == "exclude": if "CONFIG_ARCH_BOARD_{0}".format(code) == board and airframe.GetArchValue(code) == "exclude":
excluded = True excluded = True
if not excluded: if not excluded:
#print("generating: {0} {1}".format(param.GetName(), excluded)) #print("generating: {0} {1}".format(airframe.GetName(), excluded))
xml_param = ET.SubElement(xml_group, "airframe") xml_param = ET.SubElement(xml_group, "airframe")
xml_param.attrib["name"] = param.GetName() xml_param.attrib["name"] = airframe.GetName()
xml_param.attrib["id"] = param.GetId() xml_param.attrib["id"] = airframe.GetId()
xml_param.attrib["maintainer"] = param.GetMaintainer() xml_param.attrib["maintainer"] = airframe.GetMaintainer()
for code in param.GetFieldCodes(): for code in airframe.GetFieldCodes():
value = param.GetFieldValue(code) value = airframe.GetFieldValue(code)
xml_field = ET.SubElement(xml_param, code) xml_field = ET.SubElement(xml_param, code)
xml_field.text = value xml_field.text = value
for code in param.GetOutputCodes(): for code in airframe.GetOutputCodes():
value = param.GetOutputValue(code) value = airframe.GetOutputValue(code)
valstrs = value.split(";") valstrs = value.split(";")
xml_field = ET.SubElement(xml_param, "output") xml_field = ET.SubElement(xml_param, "output")
xml_field.attrib["name"] = code xml_field.attrib["name"] = code

View File

@ -35,12 +35,11 @@
# #
# PX4 airframe config processor (main executable file) # PX4 airframe config processor (main executable file)
# #
# This tool scans the PX4 ROMFS code for declarations of airframes # This tool scans the PX4 ROMFS directory for declarations of airframes
#
# Currently supported formats are:
# * XML for the parametric UI generator
# * Markdown for the PX4 dev guide (https://github.com/PX4/Devguide)
# #
# Currently supported output formats are:
# * XML for the parametric UI generator (Used in QGC)
# * Markdown for the PX4 User guide (https://github.com/PX4/PX4-user_guide)
# #
from __future__ import print_function from __future__ import print_function
@ -104,31 +103,31 @@ def main():
# We can't validate yet # We can't validate yet
# if not parser.Validate(): # if not parser.Validate():
# sys.exit(1) # sys.exit(1)
param_groups = parser.GetParamGroups() airframe_groups = parser.GetAirframeGroups()
# Output to XML file # Output to XML file
if args.xml: if args.xml:
if args.verbose: print("Creating XML file " + args.xml) if args.verbose: print("Creating XML file " + args.xml)
out = xmlout.XMLOutput(param_groups, args.board) out = xmlout.XMLOutput(airframe_groups, args.board)
out.Save(args.xml) out.Save(args.xml)
# Output to markdown file # Output to markdown file
if args.markdown: if args.markdown:
if args.verbose: print("Creating markdown file " + args.markdown) if args.verbose: print("Creating markdown file " + args.markdown)
out = markdownout.MarkdownTablesOutput(param_groups, args.board, args.image_path) out = markdownout.MarkdownTablesOutput(airframe_groups, args.board, args.image_path)
out.Save(args.markdown) out.Save(args.markdown)
# Output to start scripts # Output to start scripts
if args.start_script: if args.start_script:
# Airframe start script # Airframe start script
if args.verbose: print("Creating start script " + args.start_script) if args.verbose: print("Creating start script " + args.start_script)
out = rcout.RCOutput(param_groups, args.board) out = rcout.RCOutput(airframe_groups, args.board)
out.Save(args.start_script) out.Save(args.start_script)
# Airframe post-start script # Airframe post-start script
post_start_script = args.start_script + '.post' post_start_script = args.start_script + '.post'
if args.verbose: print("Creating post-start script " + post_start_script) if args.verbose: print("Creating post-start script " + post_start_script)
out_post = rcout.RCOutput(param_groups, args.board, post_start=True) out_post = rcout.RCOutput(airframe_groups, args.board, post_start=True)
out_post.Save(post_start_script) out_post.Save(post_start_script)
if (args.verbose): print("All done!") if (args.verbose): print("All done!")