Skip to content

Commit

Permalink
IMPROVEMENTS: display flight controller information on the GUI and do…
Browse files Browse the repository at this point in the history
…wnload parameters early on the process

This allows other processes to depend on parameter values
  • Loading branch information
amilcarlucas committed May 31, 2024
1 parent e2796c6 commit 61020f2
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 12 deletions.
13 changes: 6 additions & 7 deletions MethodicConfigurator/ardupilot_methodic_configurator.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

from frontend_tkinter_connection_selection import ConnectionSelectionWindow

from frontend_tkinter_flightcontroller_info import FlightControllerInfoWindow

from frontend_tkinter_directory_selection import VehicleDirectorySelectionWindow

from frontend_tkinter_component_editor import ComponentEditorWindow
Expand Down Expand Up @@ -87,6 +89,9 @@ def main():
vehicle_type = "ArduCopter"
logging_warning("Could not detect vehicle type. Defaulting to ArduCopter.")

if flight_controller.master is not None:
FlightControllerInfoWindow(flight_controller)

local_filesystem = LocalFilesystem(args.vehicle_dir, vehicle_type, args.allow_editing_template_files)

# Get the list of intermediate parameter files files that will be processed sequentially
Expand All @@ -103,13 +108,7 @@ def main():
component_editor_window.set_vehicle_type_and_version(vehicle_type, flight_controller.version)
if vehicle_dir_window and \
vehicle_dir_window.created_new_vehicle_from_template and \
flight_controller.master is not None:
param_download_progress_window = ProgressWindow(component_editor_window.root, "Downloading FC parameters",
"Downloaded {} of {} parameters")
# Download all parameters from the flight controller
flight_controller.fc_parameters = flight_controller.download_params(
param_download_progress_window.update_progress_bar)
param_download_progress_window.destroy() # for the case that '--device test' and there is no real FC connected
flight_controller.fc_parameters:
# copy vehicle parameters to component editor values
component_editor_window.set_values_from_fc_parameters(flight_controller.fc_parameters, local_filesystem.doc_dict)
if not args.skip_component_editor:
Expand Down
27 changes: 23 additions & 4 deletions MethodicConfigurator/backend_flightcontroller.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,11 @@
import serial.tools.list_ports_common

from serial.serialutil import SerialException

from annotate_params import Par

from backend_flightcontroller_info import BackendFlightcontrollerInfo

# adding all this allows pyinstaller to build a working windows executable
# note that using --hidden-import does not work for these modules
try:
Expand Down Expand Up @@ -113,6 +116,7 @@ def __init__(self, reboot_time: int):
self.__capabilities = None
self.version = None
self.vehicle_type = None
self.info = BackendFlightcontrollerInfo()

def discover_connections(self):
comports = FlightController.__list_serial_ports()
Expand Down Expand Up @@ -251,13 +255,18 @@ def __create_connection_with_retry(self, progress_callback, retries: int = 3, #
return "No MAVLink heartbeat received, connection failed."
self.__target_system = m.get_srcSystem()
self.__target_component = m.get_srcComponent()
self.info.set_system_and_component_ids(self.__target_system, self.__target_component)
logging_debug("Connection established with systemID %d, componentID %d.", self.__target_system,
self.__target_component)
logging_info(f"Autopilot type {self.__decode_mav_autopilot(m.autopilot)}")
autopilot_type = self.__decode_mav_autopilot(m.autopilot)
logging_info(f"Autopilot type {autopilot_type}")
self.info.set_autopilot_type(autopilot_type)
if m.autopilot != mavutil.mavlink.MAV_AUTOPILOT_ARDUPILOTMEGA:
logging_error("Unsupported autopilot type %s", self.__decode_mav_autopilot(m.autopilot))
return f"Unsupported autopilot type {self.__decode_mav_autopilot(m.autopilot)}"
logging_error("Unsupported autopilot type %s", autopilot_type)
return f"Unsupported autopilot type {autopilot_type}"
self.vehicle_type = self.__classify_vehicle_type(m.type)
self.info.set_vehicle_type(self.vehicle_type)
self.info.set_mav_type(self.__decode_mav_type(m.type))
logging_info(f"Vehicle type {self.__decode_mav_type(m.type)} running {self.vehicle_type} firmware")

self.__cmd_version()
Expand All @@ -266,34 +275,44 @@ def __create_connection_with_retry(self, progress_callback, retries: int = 3, #
logging_error("No AUTOPILOT_VERSION MAVLink message received, connection failed.")
return "No AUTOPILOT_VERSION MAVLink message received, connection failed."
self.__capabilities = m.capabilities
_cap_list = self.__decode_flight_capabilities(self.__capabilities)
cap_list = self.__decode_flight_capabilities(self.__capabilities)
self.info.set_capabilities((", ").join([cap.removeprefix("MAV_PROTOCOL_CAPABILITY_") for cap in cap_list]))
# logging_info("Flight Controller Capabilities: %s", (", ").join(
# [capability.removeprefix("MAV_PROTOCOL_CAPABILITY_")
# for capability in _cap_list]))
v_major, v_minor, v_patch, v_fw_type = self.__decode_flight_sw_version(m.flight_sw_version)
self.version = f"{v_major}.{v_minor}.{v_patch}"
self.info.set_firmware_version(self.version + " " + v_fw_type)
logging_info("Flight Controller Version: %s %s", self.version, v_fw_type)
# logging_info(f"Flight Controller Middleware version number: {m.middleware_sw_version}")
# logging_info(f"Flight Controller Operating system version number: {m.os_sw_version}")
self.info.set_hardware_version(m.board_version)
logging_info(f"Flight Controller HW / board version: {m.board_version}")
# Convert each value in the array to hex and join them together
flight_custom_version_hex = ''.join(chr(c) for c in m.flight_custom_version)
# middleware_custom_version_hex = ''.join(chr(c) for c in m.middleware_custom_version)
os_custom_version_hex = ''.join(chr(c) for c in m.os_custom_version)
self.info.set_git_hash(flight_custom_version_hex)
logging_info(f"Flight Controller first 8 hex bytes of the FC git hash: {flight_custom_version_hex}")
# logging_info(f"Flight Controller first 8 hex bytes of the MW git hash: {middleware_custom_version_hex}")
self.info.set_os_git_hash(os_custom_version_hex)
logging_info(f"Flight Controller first 8 hex bytes of the ChibiOS git hash: {os_custom_version_hex}")
if m.vendor_id == 0x1209 and m.product_id == 0x5740:
return "" # these are just generic ArduPilot values, there is no value in printing them
pid_vid_dict = self.__list_ardupilot_supported_usb_pid_vid()
if m.vendor_id in pid_vid_dict:
self.info.set_vendor(m.vendor_id, pid_vid_dict[m.vendor_id]['vendor'])
logging_info(f"Flight Controller board vendor: {pid_vid_dict[m.vendor_id]['vendor']}")
if m.product_id in pid_vid_dict[m.vendor_id]['PID']:
self.info.set_product(m.product_id, pid_vid_dict[m.vendor_id]['PID'][m.product_id])
logging_info(f"Flight Controller board product: {pid_vid_dict[m.vendor_id]['PID'][m.product_id]}")
else:
self.info.set_product(m.product_id, "")
logging_info(f"Flight Controller board product ID: 0x{hex(m.product_id)}")
else:
self.info.set_vendor(m.vendor_id, "")
logging_info(f"Flight Controller board vendor ID: 0x{hex(m.vendor_id)}")
self.info.set_product(m.product_id, "")
logging_info(f"Flight Controller product ID: 0x{hex(m.product_id)}")
# logging_info(f"Flight Controller UID if provided by hardware: {m.uid}")
except (ConnectionError, SerialException, PermissionError, ConnectionRefusedError) as e:
Expand Down
63 changes: 63 additions & 0 deletions MethodicConfigurator/backend_flightcontroller_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env python3

'''
This file is part of Ardupilot methodic configurator. https://github.com/ArduPilot/MethodicConfigurator
(C) 2024 Amilcar do Carmo Lucas, IAV GmbH
SPDX-License-Identifier: GPL-3
'''


class BackendFlightcontrollerInfo:
def __init__(self):
self.system_id = None
self.component_id = None
self.autopilot_type = None
self.vehicle_type = None
self.mav_type = None
self.firmware_version = None
self.hardware_version = None
self.git_hash = None
self.os_git_hash = None
self.vendor_id = None
self.vendor = None
self.product_id = None
self.product = None
self.capabilities = None

def set_system_and_component_ids(self, system_id, component_id):
self.system_id = system_id
self.component_id = component_id

def set_autopilot_type(self, autopilot_type):
self.autopilot_type = autopilot_type

def set_vehicle_type(self, vehicle_type):
self.vehicle_type = vehicle_type

def set_mav_type(self, mav_type):
self.mav_type = mav_type

def set_firmware_version(self, version):
self.firmware_version = version

def set_hardware_version(self, hardware_version):
self.hardware_version = hardware_version

def set_git_hash(self, git_hash):
self.git_hash = git_hash

def set_os_git_hash(self, os_git_hash):
self.os_git_hash = os_git_hash

def set_vendor(self, vendor_id, vendor):
self.vendor_id = vendor_id
self.vendor = vendor

def set_product(self, product_id, product):
self.product_id = product_id
self.product = product

def set_capabilities(self, capabilities):
self.capabilities = capabilities
77 changes: 77 additions & 0 deletions MethodicConfigurator/frontend_tkinter_flightcontroller_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env python3

'''
This file is part of Ardupilot methodic configurator. https://github.com/ArduPilot/MethodicConfigurator
(C) 2024 Amilcar do Carmo Lucas, IAV GmbH
SPDX-License-Identifier: GPL-3
'''

import tkinter as tk

from backend_flightcontroller import FlightController
#from backend_flightcontroller_info import BackendFlightcontrollerInfo

#from frontend_tkinter_base import show_tooltip
from frontend_tkinter_base import ProgressWindow
from frontend_tkinter_base import BaseWindow

from version import VERSION


class FlightControllerInfoWindow(BaseWindow):
def __init__(self, flight_controller: FlightController):
super().__init__()
self.root.title("ArduPilot methodic configurator " + VERSION + " - Flight Controller Info")
self.root.geometry("500x350") # Adjust the window size as needed
self.flight_controller = flight_controller

# Create a frame to hold all the labels and text fields
self.info_frame = tk.Frame(self.root)
self.info_frame.pack(padx=20, pady=20)

# Dictionary mapping attribute names to their descriptions
attribute_descriptions = {
"Autopilot Type": "autopilot_type",
"Vehicle Type": "vehicle_type",
"Firmware Version": "firmware_version",
"Hardware Version": "hardware_version",
"Git Hash": "git_hash",
"OS Git Hash": "os_git_hash",
"Vendor ID": "vendor_id",
"Vendor Name": "vendor",
"Product ID": "product_id",
"Product Name": "product",
"System ID": "system_id",
"Component ID": "component_id",
"MAV Type": "mav_type",
"Capabilities": "capabilities"
}

# Dynamically create labels and text fields for each attribute
for row_nr, (description, attr_name) in enumerate(attribute_descriptions.items()):
label = tk.Label(self.info_frame, text=f"{description}:")
label.grid(row=row_nr, column=0, sticky="w")

text_field = tk.Entry(self.info_frame, width=60)
text_field.grid(row=row_nr, column=1, sticky="w")

# Check if the attribute exists and has a non-empty value before inserting
attr_value = getattr(flight_controller.info, attr_name, "")
if attr_value:
text_field.insert(tk.END, attr_value)
else:
text_field.insert(tk.END, "N/A") # Insert "Not Available" if the attribute is missing or empty
text_field.configure(state="readonly")

self.root.after(50, self.download_flight_controller_parameters()) # 50 milliseconds
self.root.mainloop()

def download_flight_controller_parameters(self):
param_download_progress_window = ProgressWindow(self.root, "Downloading FC parameters",
"Downloaded {} of {} parameters")
self.flight_controller.fc_parameters = self.flight_controller.download_params(
param_download_progress_window.update_progress_bar)
param_download_progress_window.destroy() # for the case that '--device test' and there is no real FC connected
self.root.destroy()
2 changes: 1 addition & 1 deletion MethodicConfigurator/frontend_tkinter_parameter_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ def __init__(self, current_file: str, flight_controller: FlightController,

self.__create_parameter_area_widgets()

self.root.after(50, self.download_flight_controller_parameters(redownload=False)) # 50 milliseconds
#self.root.after(50, self.on_param_file_combobox_change(None, True)) # trigger a table update
self.root.after(50, self.__please_read_the_docs())
self.root.mainloop()

Expand Down

0 comments on commit 61020f2

Please sign in to comment.