diff --git a/start.py b/start.py
index fc36fa5b8c..0c6c1a87ea 100755
--- a/start.py
+++ b/start.py
@@ -66,21 +66,22 @@ class ConfChooser(object):
# CallBack Functions
- def find_conf_files(self):
+ def find_conf_files(self, combo):
conf_files = paparazzi.get_list_of_conf_files(self.exclude_backups)
- self.update_combo(self.conf_file_combo, conf_files, self.conf_xml)
+ self.update_combo(combo, conf_files, self.conf_xml)
def find_controlpanel_files(self):
controlpanel_files = paparazzi.get_list_of_controlpanel_files(self.exclude_backups)
self.update_combo(self.controlpanel_file_combo, controlpanel_files, self.controlpanel_xml)
- def count_airframes_in_conf(self):
+ @staticmethod
+ def count_airframes_in_conf(combo, conf_airframes):
airframes = 0
releases = 0
- if self.conf_file_combo.get_active_text() is None:
+ if combo.get_active_text() is None:
return
desc = ""
- afile = os.path.join(paparazzi.conf_dir, self.conf_file_combo.get_active_text())
+ afile = os.path.join(paparazzi.conf_dir, combo.get_active_text())
if os.path.exists(afile):
e = xml.etree.ElementTree.parse(afile).getroot()
for atype in e.findall('aircraft'):
@@ -94,7 +95,7 @@ class ConfChooser(object):
else:
desc += atype.get('name')
desc = "" + str(airframes) + " airframes: " + desc
- self.conf_airframes.set_markup(desc)
+ conf_airframes.set_markup(desc)
return
def about(self, widget):
@@ -120,13 +121,22 @@ class ConfChooser(object):
def set_backups(self, widget):
self.exclude_backups = not widget.get_active()
- self.find_conf_files()
+ self.find_conf_files(self.conf_file_combo)
self.find_controlpanel_files()
def more_info(self, widget):
- obj = PaparazziOverview(0)
- obj.run()
+ self.obj.run()
+ def show_untested(self, widget, data):
+ self.obj.run(show_af_detail=False, show_untested=True,
+ show_airframes=data["Airframes"].get_active(),
+ show_flightplans=data["Flightplans"].get_active(),
+ show_boards=data["Boards"].get_active(),
+ show_modules=data["Modules"].get_active())
+
+ def module_usage(self, widget, data):
+ selected_conf = data.get_active_text()
+ self.obj.airframe_module_overview(selected_conf)
def launch(self, widget):
self.accept(widget)
@@ -184,8 +194,8 @@ class ConfChooser(object):
if os.path.exists(filename):
os.remove(filename)
self.update_conf_label()
- self.count_airframes_in_conf()
- self.find_conf_files()
+ self.count_airframes_in_conf(self.conf_file_combo, self.conf_airframes)
+ self.find_conf_files(self.conf_file_combo)
self.print_status("Deleted: " + filename)
def delete_controlpanel(self, widget):
@@ -208,8 +218,8 @@ class ConfChooser(object):
os.remove(self.conf_xml)
os.symlink(selected, self.conf_xml)
self.update_conf_label()
- self.count_airframes_in_conf()
- self.find_conf_files()
+ self.count_airframes_in_conf(self.conf_file_combo, self.conf_airframes)
+ self.find_conf_files(self.conf_file_combo)
selected = self.controlpanel_file_combo.get_active_text()
if selected == "control_panel.xml":
@@ -232,8 +242,8 @@ class ConfChooser(object):
os.remove(self.conf_xml)
os.symlink(self.conf_personal_name, self.conf_xml)
self.update_conf_label()
- self.count_airframes_in_conf()
- self.find_conf_files()
+ self.count_airframes_in_conf(self.conf_file_combo, self.conf_airframes)
+ self.find_conf_files(self.conf_file_combo)
def personal_controlpanel(self, widget):
if os.path.exists(self.controlpanel_personal):
@@ -250,11 +260,94 @@ class ConfChooser(object):
def print_status(self, text):
self.statusbar.push(self.context_id, text)
- def changed_cb(self, entry):
- self.count_airframes_in_conf()
+ def changed_cb(self, widget, data):
+ self.count_airframes_in_conf(data["combo"], data["list"])
+
+ def maintenance_window(self, widget):
+ mtn_window = gtk.Window()
+ mtn_window.set_position(gtk.WIN_POS_CENTER)
+ mtn_window.set_size_request(750, 300)
+ mtn_window.set_title("Maintenance Tools")
+
+ mnt_vbox = gtk.VBox()
+
+ mnt_conf_label = gtk.Label("Conf:")
+ mnt_conf_label.set_size_request(100, 30)
+ mnt_conf_file_combo = gtk.combo_box_new_text()
+ self.find_conf_files(mnt_conf_file_combo)
+ mnt_conf_file_combo.set_size_request(500, 30)
+
+ mnt_conf_airframes = gtk.Label("")
+ self.count_airframes_in_conf(mnt_conf_file_combo, mnt_conf_airframes)
+ mnt_conf_airframes.set_size_request(650, 180)
+ mnt_conf_airframes.set_line_wrap(True)
+
+ combo_list = {"combo": mnt_conf_file_combo, "list": mnt_conf_airframes}
+ mnt_conf_file_combo.connect("changed", self.changed_cb, combo_list)
+
+ mnt_confbar = gtk.HBox()
+ mnt_confbar.pack_start(mnt_conf_label)
+ mnt_confbar.pack_start(mnt_conf_file_combo)
+ mnt_vbox.pack_start(mnt_confbar, False)
+
+ btnModule = gtk.Button("Module\nUsage")
+ btnModule.connect("clicked", self.module_usage, mnt_conf_file_combo)
+ btnModule.set_tooltip_text("More information on the modules used by these airframes")
+
+ mnt_caexbar = gtk.HBox()
+ mnt_caexbar.pack_start(mnt_conf_airframes)
+ mnt_caexbar.pack_start(btnModule)
+ mnt_vbox.pack_start(mnt_caexbar)
+
+ separator = gtk.HSeparator()
+ mnt_vbox.pack_start(separator)
+
+ cbtnAirframes = gtk.CheckButton()
+ cbtnAirframes.set_label("Airframes")
+ cbtnAirframes.set_active(True)
+
+ cbtnFlightplans = gtk.CheckButton()
+ cbtnFlightplans.set_label("Flight plans")
+ cbtnFlightplans.set_active(True)
+
+ cbtnBoards = gtk.CheckButton()
+ cbtnBoards.set_label("Boards")
+ cbtnBoards.set_active(True)
+
+ cbtnModules = gtk.CheckButton()
+ cbtnModules.set_label("Modules")
+ cbtnModules.set_active(False)
+
+ selectedOptions = {"Airframes": cbtnAirframes, "Flightplans": cbtnFlightplans,
+ "Boards": cbtnBoards, "Modules": cbtnModules}
+
+ btnUntested = gtk.Button(None, "Show Untested Files")
+ btnUntested.connect("clicked", self.show_untested, selectedOptions)
+ btnUntested.set_tooltip_text("For the selected options show the files not tested by any conf")
+
+ untestedHBox = gtk.HBox()
+ cbtnVBox = gtk.VBox()
+ cbRowUpper = gtk.HBox()
+ cbRowLower = gtk.HBox()
+
+ cbRowUpper.pack_start(cbtnAirframes)
+ cbRowUpper.pack_start(cbtnBoards)
+ cbRowLower.pack_start(cbtnFlightplans)
+ cbRowLower.pack_start(cbtnModules)
+ cbtnVBox.pack_start(cbRowUpper)
+ cbtnVBox.pack_start(cbRowLower)
+ untestedHBox.pack_start(cbtnVBox)
+ untestedHBox.pack_start(btnUntested)
+
+ mnt_vbox.pack_start(untestedHBox)
+
+ mtn_window.add(mnt_vbox)
+ mtn_window.show_all()
# Constructor Functions
def __init__(self):
+ self.obj = PaparazziOverview(0)
+
# paparazzi process
self.pp = None
@@ -311,8 +404,7 @@ class ConfChooser(object):
self.conf_label.set_size_request(100, 30)
self.conf_file_combo = gtk.combo_box_new_text()
- self.find_conf_files()
- self.conf_file_combo.connect("changed", self.changed_cb)
+ self.find_conf_files(self.conf_file_combo)
self.conf_file_combo.set_size_request(550, 30)
self.btnDeleteConf = gtk.Button(None, gtk.STOCK_DELETE)
@@ -344,17 +436,27 @@ class ConfChooser(object):
# Count Airframes
self.conf_airframes = gtk.Label("")
- self.count_airframes_in_conf()
- self.conf_airframes.set_size_request(650,180)
+ self.count_airframes_in_conf(self.conf_file_combo, self.conf_airframes)
+ self.conf_airframes.set_size_request(650, 180)
self.conf_airframes.set_line_wrap(True)
+ self.combo_list = {"combo": self.conf_file_combo, "list": self.conf_airframes}
+ self.conf_file_combo.connect("changed", self.changed_cb, self.combo_list)
+
self.btnInfo = gtk.Button(None, "More\nInfo")
self.btnInfo.connect("clicked", self.more_info)
self.btnInfo.set_tooltip_text("More information on airframe files")
+ self.btnMaintenance = gtk.Button(None, "Maintenance\n\tTools")
+ self.btnMaintenance.connect("clicked", self.maintenance_window)
+ self.btnMaintenance.set_tooltip_text("Show maintenance tools")
+
self.caexbar = gtk.HBox()
self.caexbar.pack_start(self.conf_airframes)
- self.caexbar.pack_start(self.btnInfo)
+ self.caexbar_btns = gtk.VBox()
+ self.caexbar.pack_start(self.caexbar_btns)
+ self.caexbar_btns.pack_start(self.btnInfo)
+ self.caexbar_btns.pack_start(self.btnMaintenance)
self.my_vbox.pack_start(self.caexbar, False)
diff --git a/sw/lib/python/paparazzi.py b/sw/lib/python/paparazzi.py
index 4720c0075d..2b79ad7d0c 100755
--- a/sw/lib/python/paparazzi.py
+++ b/sw/lib/python/paparazzi.py
@@ -14,7 +14,8 @@ import lxml.etree as ET
# if PAPARAZZI_HOME not set, then assume the tree containing this
# file is a reasonable substitute
-PAPARAZZI_HOME = getenv("PAPARAZZI_HOME", path.normpath(path.join(path.dirname(path.abspath(__file__)), '../../../')))
+PAPARAZZI_SRC = getenv("PAPARAZZI_HOME", path.normpath(path.join(path.dirname(path.abspath(__file__)), '../../../')))
+PAPARAZZI_HOME = getenv("PAPARAZZI_HOME", PAPARAZZI_SRC)
# Directories
conf_dir = path.join(PAPARAZZI_HOME, "conf/")
diff --git a/sw/lib/python/paparazzi_health.py b/sw/lib/python/paparazzi_health.py
index 8a5b94070c..2fe049fe83 100755
--- a/sw/lib/python/paparazzi_health.py
+++ b/sw/lib/python/paparazzi_health.py
@@ -6,19 +6,25 @@ import webbrowser
import os
import datetime
from fnmatch import fnmatch
+import re
+import numpy as np
+from collections import Counter
+import paparazzi
+import xml.etree.ElementTree
+from lxml import etree
import subprocess
+
PIPE = subprocess.PIPE
-import paparazzi
-
-import xml.etree.ElementTree
class Airframe:
name = ""
- id = ""
+ ac_id = ""
xml = ""
flight_plan = ""
release = ""
+ modules = []
+
def __init__(self):
name = ""
ac_id = ""
@@ -26,6 +32,7 @@ class Airframe:
flight_plan = ""
release = ""
+
class AirframeFile:
name = ""
description = ""
@@ -33,6 +40,10 @@ class AirframeFile:
boards = []
xml = ""
includes = []
+ last_commit = ""
+ commit_processes = []
+ modules = []
+
def __init__(self):
name = ""
description = ""
@@ -41,44 +52,128 @@ class AirframeFile:
xml = ""
includes = []
+
+class Module:
+ """ Stores data related to single module
+ Data class for storage of information related to a single module xml file.
+ """
+ module_airborne_dir = paparazzi.PAPARAZZI_SRC + "/sw/airborne/"
+ name = ""
+ last_commit = ""
+ xml = ""
+ usage = 0
+ files = []
+ missing_files = []
+
+ def __init__(self, name, file_list):
+ self.files = dict()
+ self.missing_files = []
+ self.name = name
+ self.xml = paparazzi.modules_dir + name + ".xml"
+ self.find_files(file_list)
+ self.last_commit = ""
+ self.calc_last_commit()
+
+ def find_files(self, file_dict):
+ """ Finds files mentioned in xml
+ Finds all files mentioned in the xml and looks them up in the files in the sw folder
+ """
+ parser = etree.XMLParser(recover=True)
+ e = etree.parse(self.xml, parser)
+ for atype in e.findall('.//file'):
+ file_name = atype.get('name')
+ if (file_name is not None) & (not file_name == ""):
+ if file_name.rfind('/') != -1:
+ file_name = file_name[file_name.rfind('/')+1:]
+ self.files[file_name] = ""
+
+ self.missing_files = list(self.files.viewkeys())
+ for mod_file in self.files.viewkeys():
+ if mod_file in file_dict:
+ self.files[mod_file] = file_dict[mod_file]
+ if mod_file in self.missing_files:
+ self.missing_files.remove(mod_file)
+ return
+
+ def calc_last_commit(self):
+ """ Calculates the most recent commit from any of the files related to this module."""
+ commit_processes = [self.open_commit_log_process(self.xml)]
+ commit_list = []
+ for _, value in self.files.items():
+ commit_processes.append(self.open_commit_log_process(value))
+ for process in commit_processes:
+ out = process.communicate()
+ if out[0][:-2]:
+ commit_list.append(out[0][:-2])
+ commit_list = sorted(commit_list, key=lambda x: datetime.datetime.strptime(x, '%d-%m-%Y'),
+ reverse=True)
+ self.last_commit = commit_list[0]
+ return
+
+ @staticmethod
+ def open_commit_log_process(filename):
+ """ Opens subprocess to retrieve last commit date."""
+ process = subprocess.Popen(['git', 'log', '-1', '--date=format:%d-%m-%Y', '--format=%cd ', filename],
+ stdout=PIPE, stderr=PIPE)
+ return process
+
+ def get_comments(self):
+ """ Formats the comment field which gets printed to the html file."""
+ output = "Last commit: " + self.last_commit + "
"
+ if len(self.missing_files):
+ output = output + "The following files could not be found: "
+ for missing_file in self.missing_files:
+ output = output + missing_file + " "
+
+ return output
+
+
class PaparazziOverview(object):
- def RepresentsInt(self, s):
+ @staticmethod
+ def represents_int(s):
try:
- v=int(s)
+ v = int(s)
return v
except ValueError:
return -1
- def maximize_text_size(self, txt):
+ @staticmethod
+ def maximize_text_size(txt):
if len(txt) > 500:
return txt[:500] + '...'
else:
return txt
def git_behind(self, sha):
- process = subprocess.Popen(['git', 'rev-list', sha+"..HEAD", '--count'], stdout=PIPE, stderr=PIPE)
+ process = subprocess.Popen(['git', 'rev-list', sha+"..HEAD", '--count'],
+ stdout=PIPE, stderr=PIPE)
stdoutput, stderroutput = process.communicate()
- return self.RepresentsInt(stdoutput)
+ return self.represents_int(stdoutput)
def git_ahead(self, sha):
- process = subprocess.Popen(['git', 'rev-list', "HEAD.."+sha, '--count'], stdout=PIPE, stderr=PIPE)
+ process = subprocess.Popen(['git', 'rev-list', "HEAD.."+sha, '--count'],
+ stdout=PIPE, stderr=PIPE)
stdoutput, stderroutput = process.communicate()
- return self.RepresentsInt(stdoutput)
+ return self.represents_int(stdoutput)
- def get_last_commit_sha(self, file):
- process = subprocess.Popen(['git', 'log', '-n', 1, '--pretty=format:%H', '--', sha+"..HEAD"], stdout=PIPE, stderr=PIPE)
+ @staticmethod
+ def get_last_commit_date(filename):
+ process = subprocess.Popen(['git', 'log', '-1', '--date=format:%d-%m-%Y', '--format=%cd ', filename], bufsize=-1,
+ stdout=PIPE, stderr=PIPE)
stdoutput, stderroutput = process.communicate()
+ if stdoutput == "":
+ stdoutput = "00-00-0000"
return stdoutput
-
- def find_xml_files(self, folder):
+ @staticmethod
+ def find_xml_files(folder):
airframe_files = []
pattern = "*.xml"
confn = "*conf[._-]*xml"
controlpanel = "*control_panel[._-]*xml"
- for path, subdirs, files in os.walk(os.path.join(paparazzi.conf_dir,folder)):
+ for path, subdirs, files in os.walk(os.path.join(paparazzi.conf_dir, folder)):
for name in files:
if fnmatch(name, confn):
continue
@@ -91,13 +186,31 @@ class PaparazziOverview(object):
airframe_files.sort()
return airframe_files
+ @staticmethod
+ def find_makefiles(folder):
+ makefiles = []
+ pattern = "*.makefile"
+
+ for path, subdirs, files in os.walk(os.path.join(paparazzi.conf_dir, folder)):
+ for name in files:
+ if fnmatch(name, pattern):
+ filepath = os.path.join(path, name)
+ entry = os.path.relpath(filepath, paparazzi.conf_dir)
+ makefiles.append(entry)
+ makefiles.sort()
+ return makefiles
+
def find_airframe_files(self):
return self.find_xml_files('airframes/')
def find_flightplan_files(self):
return self.find_xml_files('flight_plans/')
- def list_airframes_in_conf(self, conf):
+ def find_board_files(self):
+ return self.find_makefiles('boards/')
+
+ @staticmethod
+ def list_airframes_in_conf(conf):
if conf is None:
return []
list_of_airframes = []
@@ -108,13 +221,16 @@ class PaparazziOverview(object):
for atype in e.findall('aircraft'):
release = ""
if (not atype.get('release') is None) & (not atype.get('release') == ""):
- release = atype.get('release')
+ release = atype.get('release')
af = Airframe()
af.name = atype.get('name')
af.ac_id = atype.get('ac_id')
af.xml = atype.get('airframe')
af.flight_plan = atype.get('flight_plan')
af.release = release
+ if (not atype.get('settings_modules') is None) & (not atype.get('settings_modules') == ""):
+ modules = atype.get('settings_modules').translate(None, '[]').split()
+ af.modules = modules
list_of_airframes.append(af)
return list_of_airframes
@@ -123,7 +239,8 @@ class PaparazziOverview(object):
airframe.xml = xmlname
airframe.firmware = []
airframe.includes = []
- airframe.board = []
+ airframe.boards = []
+ airframe.modules = []
if xml is None:
return airframe
afile = os.path.join(paparazzi.conf_dir, xmlname)
@@ -131,106 +248,357 @@ class PaparazziOverview(object):
try:
e = xml.etree.ElementTree.parse(afile).getroot()
for atype in e.findall('firmware'):
- if (not atype.get('name') is None) & (not atype.get('name') == "") & (not atype.get('name') in airframe.firmware):
+ for ctype in atype.findall('module'):
+ module_name_type = self.get_module_name_type(ctype)
+ airframe.modules.append(module_name_type)
+ if (not atype.get('name') is None) & (not atype.get('name') == "") \
+ & (not atype.get('name') in airframe.firmware):
airframe.firmware.append(atype.get('name'))
for btype in atype.findall('target'):
- if (not btype.get('board') is None) & (not btype.get('board') == "") & (not btype.get('board') in airframe.board):
- airframe.board.append( btype.get('board') )
+ if (not btype.get('board') is None) & (not btype.get('board') == "") \
+ & (not btype.get('board') in airframe.boards):
+ airframe.boards.append(btype.get('board'))
+ for ctype in btype.findall('module'):
+ module_name_type = self.get_module_name_type(ctype)
+ airframe.modules.append(module_name_type)
for atype in e.findall('include'):
if (not atype.get('href') is None) & (not atype.get('href') == ""):
- airframe.includes.append( atype.get('href') )
+ airframe.includes.append(atype.get('href'))
for atype in e.findall('description'):
airframe.description = atype.text
+
except xml.etree.ElementTree.ParseError as e:
print("Could not parse {}: {}".format(afile, e))
return airframe
- def flightplan_includes(self, xmlname):
+ @staticmethod
+ def get_module_name_type(ctype):
+ module_name = re.sub(r'(\.xml)$', "", ctype.get('name'))
+ if ctype.get('type') is not None:
+ module_type = re.sub(r'(\.xml)$', "", ctype.get('type'))
+ else:
+ module_type = ""
+ module = (module_name, module_type)
+ return module
+
+ @staticmethod
+ def flightplan_includes(xmlname):
includes = []
- print(xmlname)
if xml is None:
return includes
afile = os.path.join(paparazzi.conf_dir, xmlname)
+ parser = etree.XMLParser(recover=True)
if os.path.exists(afile):
- e = xml.etree.ElementTree.parse(afile).getroot()
- for atype in e.findall('include'):
- if (not atype.get('procedure') is None) & (not atype.get('procedure') == ""):
- includes.append( atype.get('procedure') )
+ try:
+ e = etree.parse(afile, parser).getroot()
+ for atype in e.findall('include'):
+ if (not atype.get('procedure') is None) & (not atype.get('procedure') == ""):
+ if atype.get('procedure')[0:7] == "include_":
+ includes.append(atype.get('procedure')[8:])
+ else:
+ includes.append(atype.get('procedure'))
+ for btype in e.findall('includes'):
+ for atype in btype.findall('include'):
+ if (not atype.get('procedure') is None) & (not atype.get('procedure') == ""):
+ if atype.get('procedure')[0:7] == "include_":
+ includes.append(atype.get('procedure')[8:])
+ else:
+ includes.append(atype.get('procedure'))
+ except etree.ParseError as e:
+ print("Could not parse {}: {}".format(afile, e))
return includes
+ @staticmethod
+ def remove_path_and_xml(filename):
+ if filename[-4:] == ".xml":
+ return os.path.splitext(os.path.split(filename)[1])[0]
+ else:
+ return os.path.split(filename)[1]
+
+ def generate_sorted_list(self, lst):
+ commit_dates = [re.sub(r"( \n)$", "", self.get_last_commit_date(paparazzi.conf_dir + elm)) for elm in lst]
+ file_date_lst = sorted(zip(lst, commit_dates), key=lambda x: datetime.datetime.strptime(x[1], '%d-%m-%Y'),
+ reverse=True)
+ return file_date_lst
+
+ def airframe_module_overview(self, selected_conf):
+ """
+ Creates a nested dictionary of the airframe names and the modules they use to generate an html table
+ that provides an overview of module usage of the airframes of interest
+
+ :param selected_conf: str of the name of conf file
+ """
+ # Looks at airframes in selected conf (not the currently active conf)
+ airframes = self.list_airframes_in_conf(selected_conf)
+
+ # Structure of nested dictionary: {af name: {module name: module type}}
+ afs_mods = {}
+ for ac in airframes:
+ afs_mods[ac.name] = {'xml': [ac.xml]}
+ af = self.airframe_details(ac.xml)
+ for mod in af.modules:
+ if mod[0] in afs_mods[ac.name]:
+ afs_mods[ac.name][mod[0]].append(mod[1])
+ else:
+ afs_mods[ac.name][mod[0]] = [mod[1]]
+
+ # Generates Counter object of all module names used by airframes in conf
+ unique_mods_ctr = Counter()
+ for mods in afs_mods.values():
+ unique_mods_ctr.update(mods.keys())
+ del unique_mods_ctr['xml']
+
+ # Table initialization, airframe names are ordered alphabetically, module names by most used
+ ac_mod_table = np.zeros((len(afs_mods.keys()) + 1, len(unique_mods_ctr.keys()) + 1), dtype=object)
+ ac_mod_table[0, 0] = "Name \\ Modules"
+ ac_mod_table[1:, 0] = sorted(afs_mods.keys(), key=lambda s: s.lower())
+ ac_mod_table[0, 1:] = [i for i, _ in unique_mods_ctr.most_common()]
+
+ for i in range(1, len(ac_mod_table[:, 0])):
+ for j in range(1, len(ac_mod_table[0, :])):
+ ac_name = ac_mod_table[i, 0]
+ module_name = ac_mod_table[0, j]
+ if ac_mod_table[0, j] not in afs_mods[ac_name]:
+ ac_mod_table[i, j] = "\\"
+ else:
+ ac_mod_table[i, j] = ""
+ for module_type in afs_mods[ac_name][module_name]:
+ if module_type == "":
+ ac_mod_table[i, j] = "default"
+ else:
+ ac_mod_table[i, j] += module_type + "\n"
+
+ with open('var/airframe_module_overview.html', 'w') as f:
+ f.write('\n\n
| {} | \n\t'.format(column_header)) + f.write('
|---|
| {} | \n\t'.format(element)) + f.write('
| Filename | Last commit date |
|---|---|
| ' + af[1] + ' |
| Filename | Last commit date | Included in: |
|---|---|---|
| ' + af[1] + ' | ' + fps_usage[name] + ' |
| Filename | Last commit date |
|---|---|
| ' + b[1] + ' |
| Filename | Number of airframes used in | Comments |
|---|---|---|
| ' + + str(mod.usage) + ' | ' + + mod.get_comments() + ' |
Last flown with ' + sha[:6] + '...
') - behind = self.git_behind(sha) - color = 'orange' - if behind < 200: - color = 'green' - if behind > 2000: - color = 'red' - if behind > 0: - f.write( 'Is ' + str(behind) + ' commits behind
') - else: - f.write( 'Is not available on this machine
') - outside = self.git_ahead(sha) - if outside > 0: - f.write( 'Using ' + str(outside) + ' commits not in master
') - af = self.airframe_details(xml) - f.write('' + ", ".join(af.firmware) + ' on ' + ", ".join(af.board) + ' [E]
') - f.write('' + self.maximize_text_size(af.description) + '
') - if self.verbose: - f.write('') - if self.verbose: - f.write('') - #fp_inc = self.flightplan_includes(ac.flight_plan) - if len(af.includes) > 0: - for i in af.includes: - inc_name = i[5:].replace('$AC_ID',ac.ac_id) - if inc_name in afs: - afs.remove(inc_name) + if show_af_detail: + conf_files = paparazzi.get_list_of_conf_files() + for conf in conf_files: + airframes = self.list_airframes_in_conf(conf) + f.write('Last flown with ' + sha[:6] + '...
') + behind = self.git_behind(sha) + color = 'orange' + if behind < 200: + color = 'green' + if behind > 2000: + color = 'red' + if behind > 0: + f.write('Is ' + str(behind) + + ' commits behind
') + else: + f.write('Is not available on this machine
') + outside = self.git_ahead(sha) + if outside > 0: + f.write('Using ' + str(outside) + + ' commits not in master
') + af = self.airframe_details(xml) + f.write('' + ", ".join(af.firmware) + ' on ' + ", ".join(af.boards) + + ' [E]
') + f.write('' + + self.maximize_text_size(af.description) + '
') if self.verbose: - f.write('Includes: ' + ", ".join(af.includes) + '
') - f.write('