You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
193 lines
6.3 KiB
193 lines
6.3 KiB
#!/usr/bin/env python |
|
|
|
from __future__ import print_function |
|
|
|
import argparse |
|
import copy |
|
import os |
|
import re |
|
import sys |
|
|
|
import emit_html |
|
import emit_rst |
|
import emit_xml |
|
|
|
topdir = os.path.join(os.path.dirname(os.path.realpath(__file__)), '../../../') |
|
topdir = os.path.realpath(topdir) |
|
|
|
re_loggermessage = re.compile(r"@LoggerMessage\s*:\s*([\w,]+)", re.MULTILINE) |
|
re_commentline = re.compile(r"\s*//") |
|
re_description = re.compile(r"\s*//\s*@Description\s*:\s*(.*)") |
|
re_url = re.compile(r"\s*//\s*@URL\s*:\s*(.*)") |
|
re_field = re.compile(r"\s*//\s*@Field\s*:\s*(\w+):\s*(.*)") |
|
re_fieldbits = re.compile(r"\s*//\s*@FieldBits\s*:\s*(\w+):\s*(.*)") |
|
re_fieldbits = re.compile(r"\s*//\s*@FieldBits\s*:\s*(\w+):\s*(.*)") |
|
re_vehicles = re.compile(r"\s*//\s*@Vehicles\s*:\s*(.*)") |
|
|
|
# TODO: validate URLS actually return 200 |
|
# TODO: augment with other information from log definitions; type and units... |
|
|
|
|
|
class LoggerDocco(object): |
|
|
|
vehicle_map = { |
|
"Rover": "Rover", |
|
"Sub": "ArduSub", |
|
"Copter": "ArduCopter", |
|
"Plane": "ArduPlane", |
|
"Tracker": "AntennaTracker", |
|
"Blimp": "Blimp", |
|
} |
|
|
|
def __init__(self, vehicle): |
|
self.vehicle = vehicle |
|
self.doccos = [] |
|
self.emitters = [ |
|
emit_html.HTMLEmitter(), |
|
emit_rst.RSTEmitter(), |
|
emit_xml.XMLEmitter(), |
|
] |
|
|
|
class Docco(object): |
|
|
|
def __init__(self, name): |
|
self.name = name |
|
self.url = None |
|
self.description = None |
|
self.fields = {} |
|
self.fields_order = [] |
|
self.vehicles = None |
|
|
|
def set_description(self, desc): |
|
self.description = desc |
|
|
|
def set_url(self, url): |
|
self.url = url |
|
|
|
def ensure_field(self, field): |
|
if field not in self.fields: |
|
self.fields[field] = {} |
|
self.fields_order.append(field) |
|
|
|
def set_field_description(self, field, description): |
|
if field in self.fields: |
|
raise ValueError("Already have field %s in %s" % |
|
(field, self.name)) |
|
self.ensure_field(field) |
|
self.fields[field]["description"] = description |
|
|
|
def set_field_bits(self, field, bits): |
|
self.ensure_field(field) |
|
self.fields[field]["bits"] = bits |
|
|
|
def set_vehicles(self, vehicles): |
|
self.vehicles = vehicles |
|
|
|
def search_for_files(self, dirs_to_search): |
|
_next = [] |
|
for _dir in dirs_to_search: |
|
_dir = os.path.join(topdir, _dir) |
|
for entry in os.listdir(_dir): |
|
filepath = os.path.join(_dir, entry) |
|
if os.path.isdir(filepath): |
|
_next.append(filepath) |
|
continue |
|
(name, extension) = os.path.splitext(filepath) |
|
if extension not in [".cpp", ".h"]: |
|
continue |
|
self.files.append(filepath) |
|
if len(_next): |
|
self.search_for_files(_next) |
|
|
|
def parse_file(self, filepath): |
|
with open(filepath) as f: |
|
# print("Opened (%s)" % filepath) |
|
lines = f.readlines() |
|
f.close() |
|
state_outside = "outside" |
|
state_inside = "inside" |
|
state = state_outside |
|
docco = None |
|
for line in lines: |
|
if state == state_outside: |
|
m = re_loggermessage.search(line) |
|
if m is None: |
|
continue |
|
name = m.group(1) |
|
if "," in name: |
|
name = name.split(",") |
|
state = state_inside |
|
docco = LoggerDocco.Docco(name) |
|
elif state == state_inside: |
|
if not re_commentline.match(line): |
|
state = state_outside |
|
if docco.vehicles is None or self.vehicle in docco.vehicles: |
|
self.finalise_docco(docco) |
|
continue |
|
m = re_description.match(line) |
|
if m is not None: |
|
docco.set_description(m.group(1)) |
|
continue |
|
m = re_url.match(line) |
|
if m is not None: |
|
docco.set_url(m.group(1)) |
|
continue |
|
m = re_field.match(line) |
|
if m is not None: |
|
docco.set_field_description(m.group(1), m.group(2)) |
|
continue |
|
m = re_fieldbits.match(line) |
|
if m is not None: |
|
docco.set_field_bits(m.group(1), m.group(2)) |
|
continue |
|
m = re_vehicles.match(line) |
|
if m is not None: |
|
docco.set_vehicles([x.strip() for x in m.group(1).split(',')]) |
|
continue |
|
print("Unknown field (%s)" % str(line)) |
|
sys.exit(1) |
|
|
|
def parse_files(self): |
|
for _file in self.files: |
|
self.parse_file(_file) |
|
|
|
def emit_output(self): |
|
# expand things like PIDR,PIDQ,PIDA into multiple doccos |
|
new_doccos = [] |
|
for docco in self.doccos: |
|
if type(docco.name) == list: |
|
for name in docco.name: |
|
tmpdocco = copy.copy(docco) |
|
tmpdocco.name = name |
|
new_doccos.append(tmpdocco) |
|
else: |
|
new_doccos.append(docco) |
|
new_doccos = sorted(new_doccos, key=lambda x : x.name) |
|
|
|
for emitter in self.emitters: |
|
emitter.emit(new_doccos) |
|
|
|
def run(self): |
|
self.files = [] |
|
self.search_for_files([self.vehicle_map[self.vehicle], "libraries"]) |
|
self.parse_files() |
|
self.emit_output() |
|
|
|
def finalise_docco(self, docco): |
|
self.doccos.append(docco) |
|
|
|
|
|
if __name__ == '__main__': |
|
parser = argparse.ArgumentParser(description="Parse parameters.") |
|
parser.add_argument("-v", "--verbose", dest='verbose', action='store_true', default=False, help="show debugging output") |
|
parser.add_argument("--vehicle", required=True, help="Vehicle type to generate for") |
|
|
|
args = parser.parse_args() |
|
|
|
s = LoggerDocco(args.vehicle) |
|
|
|
if args.vehicle not in s.vehicle_map: |
|
print("Invalid vehicle (choose from: %s)" % str(s.vehicle_map.keys())) |
|
sys.exit(1) |
|
|
|
s.run()
|
|
|