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:
Kevin Hester 2013-02-16 14:01:55 -08:00 committed by Andrew Tridgell
parent fcfed2e81d
commit 9885cc7ed1
4 changed files with 176 additions and 63 deletions

View 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

View File

@ -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)
= Vehicles =
'''
f.write(preamble)
emit.start_libraries()
for vehicle in vehicles:
wiki_parameters(vehicle, f)
t = "\n\n=Libraries=\n\n"
f.write(t)
for library in libraries:
for library in libraries:
if library.params:
wiki_parameters(library, f)
emit.emit(library, f)
emit.close()
do_emit(XmlEmit())
do_emit(WikiEmit())
f.close

View 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)

View 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)