mirror of
https://github.com/paparazzi/paparazzi.git
synced 2026-05-09 22:49:53 +08:00
[SHA] Store the SHA of a successful airframe test (#1921)
This commit is contained in:
committed by
GitHub
parent
dd9b81df78
commit
fcf972fd44
@@ -12,8 +12,6 @@ import commands
|
||||
|
||||
import lxml.etree as ET
|
||||
|
||||
test = 1
|
||||
|
||||
# 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__)), '../../../')))
|
||||
@@ -95,6 +93,11 @@ def get_list_of_subsystems(firmware):
|
||||
#subsys_dir = path.join( firmwares_dir, "subsystems/shared/" )
|
||||
return get_list_of_files(subsys_dir, ".makefile")
|
||||
|
||||
def get_list_of_flight_plan_files():
|
||||
mylist = glob.glob(path.join(flight_plan_dir, "*.xml"))
|
||||
mylist.sort()
|
||||
return mylist
|
||||
|
||||
def get_list_of_servo_drivers():
|
||||
# \todo where do we know this?
|
||||
return ["Ppm", "Asctec", "Scilab"]
|
||||
|
||||
Executable
+239
@@ -0,0 +1,239 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import webbrowser
|
||||
import os
|
||||
import datetime
|
||||
from fnmatch import fnmatch
|
||||
import subprocess
|
||||
PIPE = subprocess.PIPE
|
||||
|
||||
import paparazzi
|
||||
|
||||
import xml.etree.ElementTree
|
||||
|
||||
class Airframe:
|
||||
name = ""
|
||||
id = ""
|
||||
xml = ""
|
||||
flight_plan = ""
|
||||
release = ""
|
||||
def __init__(self):
|
||||
name = ""
|
||||
ac_id = ""
|
||||
xml = ""
|
||||
flight_plan = ""
|
||||
release = ""
|
||||
|
||||
class AirframeFile:
|
||||
name = ""
|
||||
description = ""
|
||||
firmware = []
|
||||
boards = []
|
||||
xml = ""
|
||||
includes = []
|
||||
def __init__(self):
|
||||
name = ""
|
||||
description = ""
|
||||
firmware = []
|
||||
boards = []
|
||||
xml = ""
|
||||
includes = []
|
||||
|
||||
class PaparazziOverview(object):
|
||||
|
||||
def RepresentsInt(self, s):
|
||||
try:
|
||||
v=int(s)
|
||||
return v
|
||||
except ValueError:
|
||||
return -1
|
||||
|
||||
def maximize_text_size(self, 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)
|
||||
stdoutput, stderroutput = process.communicate()
|
||||
return self.RepresentsInt(stdoutput)
|
||||
|
||||
def git_ahead(self, sha):
|
||||
process = subprocess.Popen(['git', 'rev-list', "HEAD.."+sha, '--count'], stdout=PIPE, stderr=PIPE)
|
||||
stdoutput, stderroutput = process.communicate()
|
||||
return self.RepresentsInt(stdoutput)
|
||||
|
||||
def get_last_commit_sha(self, file):
|
||||
process = subprocess.Popen(['git', 'log', '-n', 1, '--pretty=format:%H', '--', sha+"..HEAD"], stdout=PIPE, stderr=PIPE)
|
||||
stdoutput, stderroutput = process.communicate()
|
||||
return stdoutput
|
||||
|
||||
|
||||
def find_xml_files(self, 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 name in files:
|
||||
if fnmatch(name, confn):
|
||||
continue
|
||||
if fnmatch(name, controlpanel):
|
||||
continue
|
||||
if fnmatch(name, pattern):
|
||||
filepath = os.path.join(path, name)
|
||||
entry = os.path.relpath(filepath, paparazzi.conf_dir)
|
||||
airframe_files.append(entry)
|
||||
airframe_files.sort()
|
||||
return airframe_files
|
||||
|
||||
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):
|
||||
if conf is None:
|
||||
return []
|
||||
list_of_airframes = []
|
||||
|
||||
afile = os.path.join(paparazzi.conf_dir, conf)
|
||||
if os.path.exists(afile):
|
||||
e = xml.etree.ElementTree.parse(afile).getroot()
|
||||
for atype in e.findall('aircraft'):
|
||||
release = ""
|
||||
if (not atype.get('release') is None) & (not 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
|
||||
list_of_airframes.append(af)
|
||||
return list_of_airframes
|
||||
|
||||
def airframe_details(self, xmlname):
|
||||
airframe = AirframeFile()
|
||||
airframe.xml = xmlname
|
||||
airframe.firmware = []
|
||||
airframe.includes = []
|
||||
airframe.board = []
|
||||
if xml is None:
|
||||
return airframe
|
||||
afile = os.path.join(paparazzi.conf_dir, xmlname)
|
||||
if os.path.exists(afile):
|
||||
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):
|
||||
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') )
|
||||
for atype in e.findall('include'):
|
||||
if (not atype.get('href') is None) & (not atype.get('href') == ""):
|
||||
airframe.includes.append( atype.get('href') )
|
||||
for atype in e.findall('description'):
|
||||
airframe.description = atype.text
|
||||
|
||||
return airframe
|
||||
|
||||
def flightplan_includes(self, xmlname):
|
||||
includes = []
|
||||
print(xmlname)
|
||||
if xml is None:
|
||||
return includes
|
||||
afile = os.path.join(paparazzi.conf_dir, xmlname)
|
||||
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') )
|
||||
return includes
|
||||
|
||||
|
||||
# Constructor Functions
|
||||
def __init__(self, verbose):
|
||||
self.exclude_backups = 1
|
||||
self.verbose = verbose
|
||||
|
||||
def run(self):
|
||||
# find all airframe XML's
|
||||
afs = self.find_airframe_files()
|
||||
fps = self.find_flightplan_files()
|
||||
#brds = self.find_boards()
|
||||
# write all confs
|
||||
with open('var/paparazzi.html','w') as f:
|
||||
f.write('<!DOCTYPE html>\n<html lang="en">\n<head>\n<title>Paparazzi</title>\n<meta charset="utf-8"/>\n<meta http-equiv="Cache-Control" content="no-cache" />\n')
|
||||
f.write('<style>\n.conf {\n\tfloat: left;\n\tmargin: 10px;\n\tpadding: 5px;\n}\n\n.uav {\n\tfloat: left;\n\tmargin: 10px;\n\tpadding: 5px;\n\twidth: 250px;\n\tborder: 1px solid black;\n\tbackground-color:#fef9e7;\n}\n</style>\n\n</head>\n')
|
||||
f.write('<body>\n')
|
||||
conf_files = paparazzi.get_list_of_conf_files()
|
||||
for conf in conf_files:
|
||||
airframes = self.list_airframes_in_conf(conf)
|
||||
f.write('<div class="conf"><h2>' + conf + '</h2>')
|
||||
for ac in airframes:
|
||||
f.write('<div class="uav" title="'+ ac.name + ': ' + ac.xml +'"><h4>' + ac.name + ' (' + ac.ac_id + ')</h4 >')
|
||||
sha = ac.release
|
||||
xml = ac.xml
|
||||
name = ac.name
|
||||
# remove airframe xml from list
|
||||
if xml in afs:
|
||||
afs.remove(xml)
|
||||
if ac.flight_plan in fps:
|
||||
fps.remove(ac.flight_plan)
|
||||
if (not sha is None) and (not sha == ""):
|
||||
f.write('<p>Last flown with <a href="https://github.com/paparazzi/paparazzi/commit/' + sha + '">' + sha[:6] + '...</a></p>')
|
||||
behind = self.git_behind(sha)
|
||||
color = 'orange'
|
||||
if behind < 200:
|
||||
color = 'green'
|
||||
if behind > 2000:
|
||||
color = 'red'
|
||||
if behind > 0:
|
||||
f.write( '<p><font color="' + color + '">Is <b>' + str(behind) + '</b> commits behind</font></p>')
|
||||
else:
|
||||
f.write( '<p><font color="red">Is <b>not available</b> on this machine</font></p>')
|
||||
outside = self.git_ahead(sha)
|
||||
if outside > 0:
|
||||
f.write( '<p><font color="red">Using <b>' + str(outside) + '</b> commits not in master</font></p>')
|
||||
af = self.airframe_details(xml)
|
||||
f.write('<p>' + ", ".join(af.firmware) + ' on ' + ", ".join(af.board) + ' <a href="file:////' + os.path.realpath('./conf/'+ac.xml) + '">[E]</a></p>')
|
||||
f.write('<p><font color="gray"><i>' + self.maximize_text_size(af.description) + '</i></font></p>')
|
||||
if self.verbose:
|
||||
f.write('<p><a href="https://github.com/paparazzi/paparazzi/blob/master/conf/' + ac.xml + '"/>' + ac.xml + '</a></p>')
|
||||
if self.verbose:
|
||||
f.write('<p><a href="https://github.com/paparazzi/paparazzi/blob/master/conf/' + ac.flight_plan + '"/>' + ac.flight_plan + '</a></p>')
|
||||
#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 self.verbose:
|
||||
f.write('<p>Includes: ' + ", ".join(af.includes) + '</p>')
|
||||
f.write('</div>\n\n')
|
||||
f.write('</div>\n')
|
||||
f.write('<hr><div class="conf"><h1>Airframe xml that are not tested by any conf:</h1>')
|
||||
for af in afs:
|
||||
f.write('<li>' + af)
|
||||
f.write('</div><div class="conf"><h1>Flight_plan xml that are not tested by any conf:</h1>')
|
||||
for af in fps:
|
||||
f.write('<li>' + af)
|
||||
|
||||
f.write('</div></body>\n</html>\n')
|
||||
webbrowser.open('file://' + os.path.realpath('./var/paparazzi.html'))
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
brief = 0
|
||||
if len(sys.argv) > 1:
|
||||
brief = 1
|
||||
|
||||
obj = PaparazziOverview(brief)
|
||||
obj.run()
|
||||
|
||||
@@ -1037,6 +1037,104 @@
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
|
||||
<child>
|
||||
<widget class="GtkFrame" id="frame919">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment958">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="left_padding">12</property>
|
||||
<child>
|
||||
<widget class="GtkHBox" id="hbox960">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label_release">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_________________</property>
|
||||
<property name="ellipsize">start</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkButton" id="button_compare_release">
|
||||
<property name="label" translatable="yes">Compare</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkButton" id="button_gitk">
|
||||
<property name="label" translatable="yes">Gitk</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkButton" id="button_store_release">
|
||||
<property name="label">Tag</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="use_stock">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label998">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes"><b>Release</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="type">label_item</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
|
||||
@@ -45,7 +45,8 @@ let aircraft_sample = fun name ac_id ->
|
||||
"flight_plan", "flight_plans/basic.xml";
|
||||
"settings", "settings/fixedwing_basic.xml";
|
||||
"settings_modules", "";
|
||||
"gui_color", "blue" ],
|
||||
"gui_color", "blue";
|
||||
"release", "" ],
|
||||
[])
|
||||
|
||||
|
||||
@@ -104,6 +105,32 @@ let gcs_or_edit = fun file ->
|
||||
| 2 -> ignore (Sys.command (sprintf "%s -edit '%s'&" gcs file))
|
||||
| _ -> failwith "Internal error: gcs_or_edit"
|
||||
|
||||
let gitk_version = fun sha ->
|
||||
(Sys.command (sprintf "gitk '%s'&" sha));
|
||||
()
|
||||
|
||||
|
||||
let execute_cmd_and_return_text = fun cmd ->
|
||||
let tmp_file = Filename.temp_file "" ".txt" in
|
||||
let _ = Sys.command @@ cmd ^ " > " ^ tmp_file in
|
||||
let chan = open_in tmp_file in
|
||||
let s = input_line chan in
|
||||
close_in chan;
|
||||
s
|
||||
|
||||
let tag_this_version = fun _ ->
|
||||
(execute_cmd_and_return_text "git rev-parse HEAD")
|
||||
|
||||
let get_commits_after_version = fun sha ->
|
||||
(execute_cmd_and_return_text (sprintf "git rev-list %s..HEAD --count" sha))
|
||||
|
||||
let get_commits_outside_version = fun sha ->
|
||||
(execute_cmd_and_return_text (sprintf "git rev-list HEAD..%s --count" sha))
|
||||
|
||||
let show_gitk_of_version = fun sha ->
|
||||
GToolbox.message_box ~title:"Compare" ("There have been " ^ (get_commits_after_version sha ) ^ " commits since the last reported test.\n The last reported test used " ^ (get_commits_outside_version sha ) ^ " commits that are not in this branch.");
|
||||
(execute_cmd_and_return_text (sprintf "gitk %s..HEAD & gitk HEAD..%s &" sha sha))
|
||||
|
||||
type ac_data =
|
||||
Label of GMisc.label
|
||||
| Tree of Gtk_tools.tree
|
||||
@@ -154,7 +181,8 @@ let save_callback = fun ?user_save gui ac_combo tree tree_modules () ->
|
||||
"flight_plan", gui#label_flight_plan#text;
|
||||
"settings", Gtk_tools.tree_values ~only_checked:false tree;
|
||||
"settings_modules", Gtk_tools.tree_values ~only_checked:false tree_modules;
|
||||
"gui_color", color ],
|
||||
"gui_color", color;
|
||||
"release", gui#label_release#text ],
|
||||
[]) in
|
||||
begin try Hashtbl.remove Utils.aircrafts ac_name with _ -> () end;
|
||||
Hashtbl.add Utils.aircrafts ac_name aircraft
|
||||
@@ -295,7 +323,8 @@ let ac_combo_handler = fun gui (ac_combo:Gtk_tools.combo) target_combo flash_com
|
||||
"settings", "settings", Tree tree_set, Some gui#button_browse_settings, Some gui#button_edit_settings, edit, Some gui#button_remove_settings;
|
||||
"settings_modules", "settings", Tree tree_set_mod, None, None, (fun _ -> ()), None;
|
||||
"radio", "radios", Label gui#label_radio, Some gui#button_browse_radio, Some gui#button_edit_radio, edit, None;
|
||||
"telemetry", "telemetry", Label gui#label_telemetry, Some gui#button_browse_telemetry, Some gui#button_edit_telemetry, edit, None]
|
||||
"telemetry", "telemetry", Label gui#label_telemetry, Some gui#button_browse_telemetry, Some gui#button_edit_telemetry, edit, None;
|
||||
"release", "release", Label gui#label_release, None, None, edit, None]
|
||||
in
|
||||
|
||||
(* Update_params callback *)
|
||||
@@ -532,6 +561,36 @@ let ac_combo_handler = fun gui (ac_combo:Gtk_tools.combo) target_combo flash_com
|
||||
)
|
||||
ac_files;
|
||||
|
||||
(* Tag Current Commit-Aircraft *)
|
||||
let callback = fun _ ->
|
||||
match GToolbox.question_box ~title:"Mark Test-flight Successfull" ~default:2 ~buttons:["Yes"; "Cancel"] "Are you sure you tested this airframe in all its modes (e.g. GPS) and confirm all works well." with
|
||||
| 1 ->
|
||||
begin
|
||||
gui#label_release#set_text (tag_this_version () );
|
||||
save_callback gui ac_combo tree_set tree_set_mod ();
|
||||
let ac_name = Gtk_tools.combo_value ac_combo in
|
||||
update_params ac_name
|
||||
|
||||
end
|
||||
| _ -> ()
|
||||
in
|
||||
ignore (gui#button_store_release#connect#clicked ~callback);
|
||||
|
||||
(* Compare *)
|
||||
let callback = fun _ ->
|
||||
show_gitk_of_version gui#label_release#text;
|
||||
()
|
||||
in
|
||||
ignore (gui#button_compare_release#connect#clicked ~callback);
|
||||
|
||||
(* Browse Version *)
|
||||
let callback = fun _ ->
|
||||
gitk_version gui#label_release#text
|
||||
in
|
||||
ignore (gui#button_gitk#connect#clicked ~callback);
|
||||
|
||||
|
||||
|
||||
(* Save button *)
|
||||
ignore(gui#menu_item_save_ac#connect#activate ~callback:(save_callback ~user_save:true gui ac_combo tree_set tree_set_mod))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user