#!/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 import emit_md import enum_parse from enum_parse import EnumDocco topdir = os.path.join(os.path.dirname(os.path.realpath(__file__)), '../../../') topdir = os.path.realpath(topdir) # Regular expressions for finding message information in code comments 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_fieldbitmaskenum = re.compile(r"\s*//\s*@FieldBitmaskEnum\s*:\s*(\w+):\s*(.*)") re_fieldvalueenum = re.compile(r"\s*//\s*@FieldValueEnum\s*:\s*(\w+):\s*(.*)") re_vehicles = re.compile(r"\s*//\s*@Vehicles\s*:\s*(.*)") # Regular expressions for finding message definitions in structure format re_start_messagedef = re.compile(r"^\s*{?\s*LOG_[A-Z0-9_]+_[MSGTA]+[A-Z0-9_]*\s*,") re_deffield = r'[\s\\]*"?([\w\-#?%]+)"?\s*' re_full_messagedef = re.compile(r'\s*LOG_\w+\s*,\s*\w+\([^)]+\)[\s\\]*,' + f'{re_deffield},{re_deffield},' + r'[\s\\]*"?([\w,]+)"?[\s\\]*,' + f'{re_deffield},{re_deffield}' , re.MULTILINE) re_fmt_define = re.compile(r'#define\s+(\w+_FMT)\s+"([\w\-#?%]+)"') re_units_define = re.compile(r'#define\s+(\w+_UNITS)\s+"([\w\-#?%]+)"') re_mults_define = re.compile(r'#define\s+(\w+_MULTS)\s+"([\w\-#?%]+)"') # Regular expressions for finding message definitions in Write calls re_start_writecall = re.compile(r"\s*[AP:]*logger[\(\)]*.Write[StreamingCrcl]*\(") re_writefield = r'\s*"([\w\-#?%,]+)"\s*' re_full_writecall = re.compile(r'\s*[AP:]*logger[\(\)]*.Write[StreamingCrcl]*\(' + f'{re_writefield},{re_writefield},{re_writefield},({re_writefield},{re_writefield})?' , re.MULTILINE) # Regular expression for extracting unit and multipliers from structure re_units_mults_struct = re.compile(r"^\s*{\s*'([\w\-#?%!/])',"+r'\s*"?([\w\-#?%./]*)"?\s*}') # TODO: validate URLS actually return 200 # Lookup tables are populated by reading LogStructure.h log_fmt_lookup = {} log_units_lookup = {} log_mult_lookup = {} # Lookup table to convert multiplier to prefix mult_prefix_lookup = { 0: "", 1: "", 1e-1: "d", # deci- 1e-2: "c", # centi- 1e-3: "m", # milli- 1e-6: "μ", # micro- 1e-9: "n" # nano- } 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(), emit_md.MDEmitter(), ] self.msg_fmts_list = {} self.msg_units_list = {} self.msg_mults_list = {} class Docco(object): def __init__(self, name): self.name = name self.url = None if isinstance(name,list): self.description = [None] * len(name) else: self.description = None self.fields = {} self.fields_order = [] self.vehicles = None self.bits_enums = [] def add_name(self, name): # If self.name/description aren't lists, convert them if isinstance(self.name,str): self.name = [self.name] self.description = [self.description] # Replace any existing empty descriptions with empty strings for i in range(0,len(self.description)): if self.description[i] is None: self.description[i] = "" # Extend the name and description lists if isinstance(name,list): self.name.extend(name) self.description.extend([None] * len(name)) else: self.name.append(name) self.description.append(None) def set_description(self, desc): if isinstance(self.description,list): for i in range(0,len(self.description)): if self.description[i] is None: self.description[i] = desc else: 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): bits = bits.split(",") count = 0 entries = [] for bit in bits: entries.append(EnumDocco.EnumEntry(bit, 1<