aboutsummaryrefslogtreecommitdiff
path: root/Tools
diff options
context:
space:
mode:
Diffstat (limited to 'Tools')
-rwxr-xr-xTools/fsm_visualisation.py201
-rw-r--r--Tools/px4params/.gitignore1
-rw-r--r--Tools/px4params/dokuwikiout.py62
-rw-r--r--Tools/px4params/output.py5
-rw-r--r--Tools/px4params/output_dokuwiki_listings.py (renamed from Tools/px4params/dokuwikiout_listings.py)12
-rw-r--r--Tools/px4params/output_dokuwiki_tables.py76
-rw-r--r--Tools/px4params/output_xml.py (renamed from Tools/px4params/xmlout.py)12
-rwxr-xr-xTools/px4params/px_process_params.py26
-rw-r--r--Tools/px4params/scanner.py3
-rw-r--r--Tools/px4params/srcparser.py (renamed from Tools/px4params/parser.py)20
-rw-r--r--Tools/px4params/xmlrpc.sh2
11 files changed, 323 insertions, 97 deletions
diff --git a/Tools/fsm_visualisation.py b/Tools/fsm_visualisation.py
new file mode 100755
index 000000000..c678ef0f4
--- /dev/null
+++ b/Tools/fsm_visualisation.py
@@ -0,0 +1,201 @@
+#!/usr/bin/env python3
+
+"""fsm_visualisation.py: Create dot code and dokuwiki table from a state transition table
+
+convert dot code to png using graphviz:
+
+dot fsm.dot -Tpng -o fsm.png
+"""
+
+import argparse
+import re
+
+__author__ = "Julian Oes"
+
+def get_dot_header():
+
+ return """digraph finite_state_machine {
+ graph [ dpi = 300 ];
+ ratio = 1.5
+ node [shape = circle];"""
+
+def get_dot_footer():
+
+ return """}\n"""
+
+def main():
+
+ # parse input arguments
+ parser = argparse.ArgumentParser(description='Create dot code and dokuwiki table from a state transition table.')
+ parser.add_argument("-i", "--input-file", default=None, help="choose file to parse")
+ parser.add_argument("-d", "--dot-file", default=None, help="choose file for output dot file")
+ parser.add_argument("-t", "--table-file", default=None, help="choose file for output of table")
+ args = parser.parse_args()
+
+ # open source file
+ if args.input_file == None:
+ exit('please specify file')
+ f = open(args.input_file,'r')
+ source = f.read()
+
+ # search for state transition table and extract the table itself
+ # first look for StateTable::Tran
+ # then accept anything including newline until {
+ # but don't accept the definition (without ;)
+ # then extract anything inside the brackets until };
+ match = re.search(r'StateTable::Tran(?:.|\n!;)*\{((?:.|\n)*?)\};', source)
+
+ if not match:
+ exit('no state transition table found')
+
+ table_source = match.group(1)
+
+ # bookkeeping for error checking
+ num_errors_found = 0
+
+ states = []
+ events = []
+
+ # first get all states and events
+ for table_line in table_source.split('\n'):
+
+ match = re.search(r'/\*\s+\w+_STATE_(\w+)\s+\*/', table_line)
+ if match:
+ states.append(str(match.group(1)))
+ # go to next line
+ continue
+
+ if len(states) == 1:
+ match = re.search(r'/\*\s+EVENT_(\w+)\s+\*/', table_line)
+ if match:
+ events.append(str(match.group(1)))
+
+ print('Found %d states and %d events' % (len(states), len(events)))
+
+
+ # keep track of origin state
+ state = None
+
+ # fill dot code in here
+ dot_code = ''
+
+ # create table len(states)xlen(events)
+ transition_table = [[[] for x in range(len(states))] for y in range(len(events))]
+
+ # now fill the transition table and write the dot code
+ for table_line in table_source.split('\n'):
+
+ # get states
+ # from: /* NAV_STATE_NONE */
+ # extract only "NONE"
+ match = re.search(r'/\*\s+\w+_STATE_(\w+)\s+\*/', table_line)
+ if match:
+ state = match.group(1)
+ state_index = states.index(state)
+ # go to next line
+ continue
+
+ # can't advance without proper state
+ if state == None:
+ continue
+
+ # get event and next state
+ # from /* EVENT_READY_REQUESTED */ {ACTION(&Navigator::start_ready), NAV_STATE_READY}
+ # extract "READY_REQUESTED" and "READY" if there is ACTION
+ match_action = re.search(r'/\*\s+EVENT_(\w+)\s+\*/\s+\{ACTION\((?:.|\n)*\w+_STATE_(\w+)', table_line)
+
+ # get event and next state
+ # from /* EVENT_NONE_REQUESTED */ {NO_ACTION, NAV_STATE_NONE},
+ # extract "NONE_REQUESTED" and "NAV_STATE_NONE" if there is NO_ACTION
+ match_no_action = re.search(r'/\*\s+EVENT_(\w+)\s+\*/\s+\{NO_ACTION(?:.|\n)*\w+_STATE_(\w+)', table_line)
+
+ # ignore lines with brackets only
+ if match_action or match_no_action:
+
+ # only write arrows for actions
+ if match_action:
+ event = match_action.group(1)
+ new_state = match_action.group(2)
+ dot_code += ' ' + state + ' -> ' + new_state + '[ label = "' + event + '"];\n'
+
+ elif match_no_action:
+ event = match_no_action.group(1)
+ new_state = match_no_action.group(2)
+
+ # check for state changes without action
+ if state != new_state:
+ print('Error: no action but state change:')
+ print('State: ' + state + ' changed to: ' + new_state)
+ print(table_line)
+ num_errors_found += 1
+
+ # check for wrong events
+ if event not in events:
+ print('Error: unknown event: ' + event)
+ print(table_line)
+ num_errors_found += 1
+
+ # check for wrong new states
+ if new_state not in states:
+ print('Error: unknown new state: ' + new_state)
+ print(table_line)
+ num_errors_found += 1
+
+ # save new state in transition table
+ event_index = events.index(event)
+
+ # bold for action
+ if match_action:
+ transition_table[event_index][state_index] = '**' + new_state + '**'
+ else:
+ transition_table[event_index][state_index] = new_state
+
+
+
+ # assemble dot code
+ dot_code = get_dot_header() + dot_code + get_dot_footer()
+
+ # write or print dot file
+ if args.dot_file:
+ f = open(args.dot_file,'w')
+ f.write(dot_code)
+ print('Wrote dot file')
+ else:
+ print('##########Dot-start##########')
+ print(dot_code)
+ print('##########Dot-end############')
+
+
+ # assemble doku wiki table
+ table_code = '| ^ '
+ # start with header of all states
+ for state in states:
+ table_code += state + ' ^ '
+
+ table_code += '\n'
+
+ # add events and new states
+ for event, row in zip(events, transition_table):
+ table_code += '^ ' + event + ' | '
+ for new_state in row:
+ table_code += new_state + ' | '
+ table_code += '\n'
+
+ # write or print wiki table
+ if args.table_file:
+ f = open(args.table_file,'w')
+ f.write(table_code)
+ print('Wrote table file')
+ else:
+ print('##########Table-start########')
+ print(table_code)
+ print('##########Table-end##########')
+
+ # report obvous errors
+ if num_errors_found:
+ print('Obvious errors found: %d' % num_errors_found)
+ else:
+ print('No obvious errors found')
+
+if __name__ == '__main__':
+ main()
diff --git a/Tools/px4params/.gitignore b/Tools/px4params/.gitignore
index 5d0378b4a..d78b71a6e 100644
--- a/Tools/px4params/.gitignore
+++ b/Tools/px4params/.gitignore
@@ -1,3 +1,4 @@
parameters.wiki
parameters.xml
+parameters.wikirpc.xml
cookies.txt \ No newline at end of file
diff --git a/Tools/px4params/dokuwikiout.py b/Tools/px4params/dokuwikiout.py
deleted file mode 100644
index c5cf65ea6..000000000
--- a/Tools/px4params/dokuwikiout.py
+++ /dev/null
@@ -1,62 +0,0 @@
-import output
-from xml.sax.saxutils import escape
-
-class DokuWikiOutput(output.Output):
- def Generate(self, groups):
- pre_text = """<?xml version='1.0'?>
- <methodCall>
- <methodName>wiki.putPage</methodName>
- <params>
- <param>
- <value>
- <string>:firmware:parameters</string>
- </value>
- </param>
- <param>
- <value>
- <string>"""
- result = "====== Parameter Reference ======\nThis list is auto-generated every few minutes and contains the most recent parameter names and default values."
- for group in groups:
- result += "==== %s ====\n\n" % group.GetName()
- result += "|< 100% 20% 20% 10% 10% 10% 30%>|\n"
- result += "^ Name ^ Description ^ Min ^ Max ^ Default ^ Comment ^\n"
- for param in group.GetParams():
- code = param.GetFieldValue("code")
- name = param.GetFieldValue("short_desc")
- name = name.replace("\n", "")
- result += "| %s | %s " % (code, name)
- min_val = param.GetFieldValue("min")
- if min_val is not None:
- result += " | %s " % min_val
- else:
- result += " | "
- max_val = param.GetFieldValue("max")
- if max_val is not None:
- result += " | %s " % max_val
- else:
- result += " | "
- def_val = param.GetFieldValue("default")
- if def_val is not None:
- result += "| %s " % def_val
- else:
- result += " | "
- long_desc = param.GetFieldValue("long_desc")
- if long_desc is not None:
- long_desc = long_desc.replace("\n", "")
- result += "| %s " % long_desc
- else:
- result += " | "
- result += " |\n"
- result += "\n"
- post_text = """</string>
- </value>
- </param>
- <param>
- <value>
- <name>sum</name>
- <string>Updated parameters automagically from code.</string>
- </value>
- </param>
- </params>
- </methodCall>"""
- return pre_text + escape(result) + post_text
diff --git a/Tools/px4params/output.py b/Tools/px4params/output.py
deleted file mode 100644
index c09246871..000000000
--- a/Tools/px4params/output.py
+++ /dev/null
@@ -1,5 +0,0 @@
-class Output(object):
- def Save(self, groups, fn):
- data = self.Generate(groups)
- with open(fn, 'w') as f:
- f.write(data)
diff --git a/Tools/px4params/dokuwikiout_listings.py b/Tools/px4params/output_dokuwiki_listings.py
index 33f76b415..83c50ae15 100644
--- a/Tools/px4params/dokuwikiout_listings.py
+++ b/Tools/px4params/output_dokuwiki_listings.py
@@ -1,7 +1,7 @@
-import output
+import codecs
-class DokuWikiOutput(output.Output):
- def Generate(self, groups):
+class DokuWikiListingsOutput():
+ def __init__(self, groups):
result = ""
for group in groups:
result += "==== %s ====\n\n" % group.GetName()
@@ -24,4 +24,8 @@ class DokuWikiOutput(output.Output):
if def_val is not None:
result += "* Default value: %s\n" % def_val
result += "\n"
- return result
+ self.output = result
+
+ def Save(self, filename):
+ with codecs.open(filename, 'w', 'utf-8') as f:
+ f.write(self.output)
diff --git a/Tools/px4params/output_dokuwiki_tables.py b/Tools/px4params/output_dokuwiki_tables.py
new file mode 100644
index 000000000..aa04304df
--- /dev/null
+++ b/Tools/px4params/output_dokuwiki_tables.py
@@ -0,0 +1,76 @@
+from xml.sax.saxutils import escape
+import codecs
+
+class DokuWikiTablesOutput():
+ def __init__(self, groups):
+ result = "====== Parameter Reference ======\nThis list is auto-generated every few minutes and contains the most recent parameter names and default values.\n\n"
+ for group in groups:
+ result += "==== %s ====\n\n" % group.GetName()
+ result += "|< 100% 20% 20% 10% 10% 10% 30%>|\n"
+ result += "^ Name ^ Description ^ Min ^ Max ^ Default ^ Comment ^\n"
+ for param in group.GetParams():
+ code = param.GetFieldValue("code")
+ name = param.GetFieldValue("short_desc")
+ min_val = param.GetFieldValue("min")
+ max_val = param.GetFieldValue("max")
+ def_val = param.GetFieldValue("default")
+ long_desc = param.GetFieldValue("long_desc")
+
+ name = name.replace("\n", " ")
+ result += "| %s | %s |" % (code, name)
+
+ if min_val is not None:
+ result += " %s |" % min_val
+ else:
+ result += " |"
+
+ if max_val is not None:
+ result += " %s |" % max_val
+ else:
+ result += " |"
+
+ if def_val is not None:
+ result += " %s |" % def_val
+ else:
+ result += " |"
+
+ if long_desc is not None:
+ long_desc = long_desc.replace("\n", " ")
+ result += " %s |" % long_desc
+ else:
+ result += " |"
+
+ result += "\n"
+ result += "\n"
+ self.output = result;
+
+ def Save(self, filename):
+ with codecs.open(filename, 'w', 'utf-8') as f:
+ f.write(self.output)
+
+ def SaveRpc(self, filename):
+ with codecs.open(filename, 'w', 'utf-8') as f:
+ f.write("""<?xml version='1.0'?>
+<methodCall>
+ <methodName>wiki.putPage</methodName>
+ <params>
+ <param>
+ <value>
+ <string>:firmware:parameters</string>
+ </value>
+ </param>
+ <param>
+ <value>
+ <string>""")
+ f.write(escape(self.output))
+ f.write("""</string>
+ </value>
+ </param>
+ <param>
+ <value>
+ <name>sum</name>
+ <string>Updated parameters automagically from code.</string>
+ </value>
+ </param>
+ </params>
+</methodCall>""")
diff --git a/Tools/px4params/xmlout.py b/Tools/px4params/output_xml.py
index d56802b15..e845cd1b1 100644
--- a/Tools/px4params/xmlout.py
+++ b/Tools/px4params/output_xml.py
@@ -1,8 +1,8 @@
-import output
from xml.dom.minidom import getDOMImplementation
+import codecs
-class XMLOutput(output.Output):
- def Generate(self, groups):
+class XMLOutput():
+ def __init__(self, groups):
impl = getDOMImplementation()
xml_document = impl.createDocument(None, "parameters", None)
xml_parameters = xml_document.documentElement
@@ -19,4 +19,8 @@ class XMLOutput(output.Output):
xml_param.appendChild(xml_field)
xml_value = xml_document.createTextNode(value)
xml_field.appendChild(xml_value)
- return xml_document.toprettyxml(indent=" ", newl="\n", encoding="utf-8")
+ self.xml_document = xml_document
+
+ def Save(self, filename):
+ with codecs.open(filename, 'w', 'utf-8') as f:
+ self.xml_document.writexml(f, indent=" ", addindent=" ", newl="\n")
diff --git a/Tools/px4params/px_process_params.py b/Tools/px4params/px_process_params.py
index cdf5aba7c..7799f6348 100755
--- a/Tools/px4params/px_process_params.py
+++ b/Tools/px4params/px_process_params.py
@@ -40,22 +40,28 @@
#
import scanner
-import parser
-import xmlout
-import dokuwikiout
+import srcparser
+import output_xml
+import output_dokuwiki_tables
+import output_dokuwiki_listings
# Initialize parser
-prs = parser.Parser()
+prs = srcparser.Parser()
# Scan directories, and parse the files
sc = scanner.Scanner()
sc.ScanDir("../../src", prs)
-output = prs.GetParamGroups()
+groups = prs.GetParamGroups()
# Output into XML
-out = xmlout.XMLOutput()
-out.Save(output, "parameters.xml")
+out = output_xml.XMLOutput(groups)
+out.Save("parameters.xml")
-# Output into DokuWiki
-out = dokuwikiout.DokuWikiOutput()
-out.Save(output, "parameters.wiki")
+# Output to DokuWiki listings
+#out = output_dokuwiki_listings.DokuWikiListingsOutput(groups)
+#out.Save("parameters.wiki")
+
+# Output to DokuWiki tables
+out = output_dokuwiki_tables.DokuWikiTablesOutput(groups)
+out.Save("parameters.wiki")
+out.SaveRpc("parameters.wikirpc.xml")
diff --git a/Tools/px4params/scanner.py b/Tools/px4params/scanner.py
index b5a1af47c..8779b7bbf 100644
--- a/Tools/px4params/scanner.py
+++ b/Tools/px4params/scanner.py
@@ -1,5 +1,6 @@
import os
import re
+import codecs
class Scanner(object):
"""
@@ -29,6 +30,6 @@ class Scanner(object):
Scans provided file and passes its contents to the parser using
parser.Parse method.
"""
- with open(path, 'r') as f:
+ with codecs.open(path, 'r', 'utf-8') as f:
contents = f.read()
parser.Parse(contents)
diff --git a/Tools/px4params/parser.py b/Tools/px4params/srcparser.py
index 251be672f..1b2d30110 100644
--- a/Tools/px4params/parser.py
+++ b/Tools/px4params/srcparser.py
@@ -28,8 +28,7 @@ class ParameterGroup(object):
state of the parser.
"""
return sorted(self.params,
- cmp=lambda x, y: cmp(x.GetFieldValue("code"),
- y.GetFieldValue("code")))
+ key=lambda x: x.GetFieldValue("code"))
class Parameter(object):
"""
@@ -61,9 +60,10 @@ class Parameter(object):
"""
Return list of existing field codes in convenient order
"""
- return sorted(self.fields.keys(),
- cmp=lambda x, y: cmp(self.priority.get(y, 0),
- self.priority.get(x, 0)) or cmp(x, y))
+ keys = self.fields.keys()
+ keys = sorted(keys)
+ keys = sorted(keys, key=lambda x: self.priority.get(x, 0), reverse=True)
+ return keys
def GetFieldValue(self, code):
"""
@@ -197,7 +197,7 @@ class Parser(object):
if tag == "group":
group = tags[tag]
elif tag not in self.valid_tags:
- sys.stderr.write("Skipping invalid"
+ sys.stderr.write("Skipping invalid "
"documentation tag: '%s'\n" % tag)
else:
param.SetField(tag, tags[tag])
@@ -214,7 +214,7 @@ class Parser(object):
object. Note that returned object is not a copy. Modifications affect
state of the parser.
"""
- return sorted(self.param_groups.values(),
- cmp=lambda x, y: cmp(self.priority.get(y.GetName(), 0),
- self.priority.get(x.GetName(), 0)) or cmp(x.GetName(),
- y.GetName()))
+ groups = self.param_groups.values()
+ groups = sorted(groups, key=lambda x: x.GetName())
+ groups = sorted(groups, key=lambda x: self.priority.get(x.GetName(), 0), reverse=True)
+ return groups
diff --git a/Tools/px4params/xmlrpc.sh b/Tools/px4params/xmlrpc.sh
index 36c52ff71..efd177f46 100644
--- a/Tools/px4params/xmlrpc.sh
+++ b/Tools/px4params/xmlrpc.sh
@@ -2,4 +2,4 @@ python px_process_params.py
rm cookies.txt
curl --cookie cookies.txt --cookie-jar cookies.txt --user-agent Mozilla/4.0 --data "u=$XMLRPCUSER&p=$XMLRPCPASS" https://pixhawk.org/start?do=login
-curl -k --cookie cookies.txt -H "Content-Type: application/xml" -X POST --data-binary @parameters.wiki "https://pixhawk.org/lib/exe/xmlrpc.php"
+curl -k --cookie cookies.txt -H "Content-Type: application/xml" -X POST --data-binary @parameters.wikirpc.xml "https://pixhawk.org/lib/exe/xmlrpc.php"