mirror of
https://github.com/ArduPilot/ardupilot
synced 2025-02-27 10:13:57 -04:00
Docs: Add XML emit to param_parse.py. See below for more details...
I've refactored the param_parse tool to use various 'emitters'. An emitter can take parameter info and output it in a particular format. Currently the only supported emitters are the wiki and XML formats. The goal of these changes is to create a standard machine readable description of parameters - mainly for use by ground control stations, but it will also enable spiffy scripting environments where code can refer symbolically to vehicle parameters (reflectionish). Open issue: Is there any sort of Ardupilot build id which can be included in the generated XML? That would ensure that we select the correct paramdefs for the load on the target (possibly by asking the target for a SHA or somesuch). If that issue is resolved, then the filename for the XML file should probably be something like: arduplane-ca5742ac.pdef.xml. It is worth noting that I've proposed a suffix of ".pdef.xml" for these file types. This facilitates automated file handling on Android devices. On Android you can register 'handlers' for particular file extensions and if the user tries to open that extension in email or a web browser your app will be given a chance to do something about it. The 'outer' xml extension will allow naive editors to know that at least this is an xml file. I will include a sample of the XML format with the pull-request for this CL.
This commit is contained in:
parent
fcfed2e81d
commit
9885cc7ed1
19
Tools/autotest/param_metadata/emit.py
Normal file
19
Tools/autotest/param_metadata/emit.py
Normal file
@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import re
|
||||
from param import *
|
||||
|
||||
# The standard interface emitters must implement
|
||||
class Emit:
|
||||
prog_values_field = re.compile(r"\s*(-?\w+:\w+)+,*")
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
def start_libraries(self):
|
||||
pass
|
||||
|
||||
def emit(self, g, f):
|
||||
pass
|
||||
|
||||
|
@ -3,7 +3,8 @@
|
||||
import os, glob, re
|
||||
|
||||
from param import *
|
||||
|
||||
from wikiemit import WikiEmit
|
||||
from xmlemit import XmlEmit
|
||||
|
||||
# Regular expressions for parsing the parameter metadata
|
||||
|
||||
@ -15,50 +16,6 @@ prog_groups = re.compile(r"@Group:\s*(\w+).*((?:\n\s*//\s*@(\w+): (.*))+)\s*G",
|
||||
|
||||
prog_group_param = re.compile(r"@Param:\s*(\w+).*((?:\n\s*//\s*@(\w+): (.*))+)\s*AP_", re.MULTILINE)
|
||||
|
||||
prog_values_field = re.compile(r"\s*(-?\w+:\w+)+,*")
|
||||
|
||||
def camelcase_escape(word):
|
||||
if re.match(r"([A-Z][a-z]+[A-Z][a-z]*)", word.strip()):
|
||||
return "!"+word
|
||||
else:
|
||||
return word
|
||||
|
||||
def wikichars_escape(text):
|
||||
for c in "*,{,},[,],_,=,#,^,~,!,@,$,|,<,>,&,|,\,/".split(','):
|
||||
text = re.sub("\\"+c, '`'+c+'`', text)
|
||||
return text
|
||||
|
||||
def wiki_parameters(g, f):
|
||||
|
||||
t = "\n\n== %s Parameters ==\n" % (camelcase_escape(g.name))
|
||||
|
||||
for param in g.params:
|
||||
if hasattr(param, 'DisplayName'):
|
||||
t += "\n\n=== %s (%s) ===" % (camelcase_escape(param.DisplayName),camelcase_escape(param.name))
|
||||
else:
|
||||
t += "\n\n=== %s ===" % camelcase_escape(param.name)
|
||||
|
||||
if hasattr(param, 'Description'):
|
||||
t += "\n\n_%s_\n" % wikichars_escape(param.Description)
|
||||
else:
|
||||
t += "\n\n_TODO: description_\n"
|
||||
|
||||
for field in param.__dict__.keys():
|
||||
if field not in ['name', 'DisplayName', 'Description', 'User'] and field in known_param_fields:
|
||||
if field == 'Values' and prog_values_field.match(param.__dict__[field]):
|
||||
t+= " * Values \n"
|
||||
values = (param.__dict__[field]).split(',')
|
||||
t+="|| *Value* || *Meaning* ||\n"
|
||||
for value in values:
|
||||
v = value.split(':')
|
||||
t+="|| "+v[0]+" || "+camelcase_escape(v[1])+" ||\n"
|
||||
else:
|
||||
t += " * %s: %s\n" % (camelcase_escape(field), wikichars_escape(param.__dict__[field]))
|
||||
|
||||
#print t
|
||||
f.write(t)
|
||||
|
||||
|
||||
apm_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), '../../../')
|
||||
vehicle_paths = glob.glob(apm_path + "*/Parameters.pde")
|
||||
vehicle_paths.sort(reverse=True)
|
||||
@ -154,25 +111,20 @@ for library in libraries:
|
||||
|
||||
print "Processed %u documented parameters" % len(library.params)
|
||||
|
||||
wiki_fname = 'Parameters.wiki'
|
||||
f = open(wiki_fname, mode='w')
|
||||
preamble = '''#summary Dynamically generated list of documented parameters
|
||||
= Table of Contents =
|
||||
<wiki:toc max_depth="4" />
|
||||
def do_emit(emit):
|
||||
for vehicle in vehicles:
|
||||
emit.emit(vehicle, f)
|
||||
|
||||
emit.start_libraries()
|
||||
|
||||
for library in libraries:
|
||||
if library.params:
|
||||
emit.emit(library, f)
|
||||
|
||||
emit.close()
|
||||
|
||||
= Vehicles =
|
||||
'''
|
||||
f.write(preamble)
|
||||
do_emit(XmlEmit())
|
||||
do_emit(WikiEmit())
|
||||
|
||||
for vehicle in vehicles:
|
||||
wiki_parameters(vehicle, f)
|
||||
|
||||
t = "\n\n=Libraries=\n\n"
|
||||
f.write(t)
|
||||
|
||||
for library in libraries:
|
||||
if library.params:
|
||||
wiki_parameters(library, f)
|
||||
|
||||
f.close
|
||||
|
||||
|
72
Tools/autotest/param_metadata/wikiemit.py
Normal file
72
Tools/autotest/param_metadata/wikiemit.py
Normal file
@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import re
|
||||
from param import *
|
||||
from emit import Emit
|
||||
|
||||
# Emit docs in a form acceptable to the APM wiki site
|
||||
class WikiEmit(Emit):
|
||||
|
||||
def __init__(self):
|
||||
wiki_fname = 'Parameters.wiki'
|
||||
self.f = open(wiki_fname, mode='w')
|
||||
preamble = '''#summary Dynamically generated list of documented parameters
|
||||
= Table of Contents =
|
||||
<wiki:toc max_depth="4" />
|
||||
|
||||
= Vehicles =
|
||||
'''
|
||||
self.f.write(preamble)
|
||||
|
||||
def close(self):
|
||||
self.f.close
|
||||
|
||||
def camelcase_escape(self, word):
|
||||
if re.match(r"([A-Z][a-z]+[A-Z][a-z]*)", word.strip()):
|
||||
return "!"+word
|
||||
else:
|
||||
return word
|
||||
|
||||
def wikichars_escape(self, text):
|
||||
for c in "*,{,},[,],_,=,#,^,~,!,@,$,|,<,>,&,|,\,/".split(','):
|
||||
text = re.sub("\\"+c, '`'+c+'`', text)
|
||||
return text
|
||||
|
||||
def emit_comment(self, s):
|
||||
self.f.write("\n\n=" + s + "=\n\n")
|
||||
|
||||
def start_libraries(self):
|
||||
self.emit_comment("Libraries")
|
||||
|
||||
def emit(self, g, f):
|
||||
|
||||
t = "\n\n== %s Parameters ==\n" % (self.camelcase_escape(g.name))
|
||||
|
||||
for param in g.params:
|
||||
if hasattr(param, 'DisplayName'):
|
||||
t += "\n\n=== %s (%s) ===" % (self.camelcase_escape(param.DisplayName),self.camelcase_escape(param.name))
|
||||
else:
|
||||
t += "\n\n=== %s ===" % self.camelcase_escape(param.name)
|
||||
|
||||
if hasattr(param, 'Description'):
|
||||
t += "\n\n_%s_\n" % self.wikichars_escape(param.Description)
|
||||
else:
|
||||
t += "\n\n_TODO: description_\n"
|
||||
|
||||
for field in param.__dict__.keys():
|
||||
if field not in ['name', 'DisplayName', 'Description', 'User'] and field in known_param_fields:
|
||||
if field == 'Values' and Emit.prog_values_field.match(param.__dict__[field]):
|
||||
t+= " * Values \n"
|
||||
values = (param.__dict__[field]).split(',')
|
||||
t+="|| *Value* || *Meaning* ||\n"
|
||||
for value in values:
|
||||
v = value.split(':')
|
||||
t+="|| "+v[0]+" || "+self.camelcase_escape(v[1])+" ||\n"
|
||||
else:
|
||||
t += " * %s: %s\n" % (self.camelcase_escape(field), self.wikichars_escape(param.__dict__[field]))
|
||||
|
||||
#print t
|
||||
self.f.write(t)
|
||||
|
||||
|
||||
|
70
Tools/autotest/param_metadata/xmlemit.py
Normal file
70
Tools/autotest/param_metadata/xmlemit.py
Normal file
@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from xml.sax.saxutils import escape, quoteattr
|
||||
|
||||
from param import *
|
||||
from emit import Emit
|
||||
|
||||
# Emit APM documentation in an machine readable XML format
|
||||
class XmlEmit(Emit):
|
||||
|
||||
def __init__(self):
|
||||
wiki_fname = 'arduplane.pdef.xml'
|
||||
self.f = open(wiki_fname, mode='w')
|
||||
preamble = '''<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Dynamically generated list of documented parameters (generated by param_parse.py) -->
|
||||
<paramfile>
|
||||
<vehicles>
|
||||
'''
|
||||
self.f.write(preamble)
|
||||
|
||||
def close(self):
|
||||
self.f.write('</libraries>')
|
||||
self.f.write('''</paramfile>\n''')
|
||||
self.f.close
|
||||
|
||||
def emit_comment(self, s):
|
||||
self.f.write("<!-- " + s + " -->")
|
||||
|
||||
def start_libraries(self):
|
||||
self.f.write('</vehicles>')
|
||||
self.f.write('<libraries>')
|
||||
|
||||
def emit(self, g, f):
|
||||
t = '''<parameters name=%s>\n''' % quoteattr(g.name) # i.e. ArduPlane
|
||||
|
||||
for param in g.params:
|
||||
# Begin our parameter node
|
||||
if hasattr(param, 'DisplayName'):
|
||||
t += '<param humanName=%s name=%s ' % (quoteattr(param.DisplayName),quoteattr(param.name)) # i.e. ArduPlane (ArduPlane:FOOPARM)
|
||||
else:
|
||||
t += '<param name=%s ' % quoteattr(param.name)
|
||||
|
||||
if hasattr(param, 'Description'):
|
||||
t += 'documentation=%s' % quoteattr(param.Description) # i.w. parameter docs
|
||||
|
||||
t += ">\n"
|
||||
|
||||
# Add values as chidren of this node
|
||||
for field in param.__dict__.keys():
|
||||
if field not in ['name', 'DisplayName', 'Description', 'User'] and field in known_param_fields:
|
||||
if field == 'Values' and Emit.prog_values_field.match(param.__dict__[field]):
|
||||
t+= "<values>\n"
|
||||
|
||||
values = (param.__dict__[field]).split(',')
|
||||
for value in values:
|
||||
v = value.split(':')
|
||||
t+='''<value code=%s>%s</value>\n''' % (quoteattr(v[0]), escape(v[1])) # i.e. numeric value, string label
|
||||
|
||||
t += "</values>\n"
|
||||
else:
|
||||
t += '''<field name=%s>%s</field>\n''' % (quoteattr(field), escape(param.__dict__[field])) # i.e. Range: 0 10
|
||||
|
||||
t += '''</param>\n'''
|
||||
t += '''</parameters>\n'''
|
||||
|
||||
#print t
|
||||
self.f.write(t)
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user