1414from logging import error as logging_error
1515from logging import info as logging_info
1616from tkinter import ttk
17+ from typing import Callable , Optional
18+
19+ from serial .serialutil import SerialException
1720
1821from ardupilot_methodic_configurator import _ , __version__
1922from ardupilot_methodic_configurator .annotate_params import Par
2528class FlightControllerInfoWindow (BaseWindow ):
2629 """Display flight controller hardware, firmware and parameter information."""
2730
28- def __init__ (self , flight_controller : FlightController ) -> None :
29- super ().__init__ ()
31+ def __init__ (self , flight_controller : FlightController , root_tk : Optional [tk .Tk ] = None ) -> None :
32+ """
33+ Initialize the FlightControllerInfoWindow.
34+
35+ Args:
36+ flight_controller: The flight controller to display information for
37+ root_tk: Optional parent Tk root window
38+
39+ """
40+ super ().__init__ (root_tk )
3041 self .root .title (_ ("ArduPilot methodic configurator " ) + __version__ + _ (" - Flight Controller Info" ))
3142 self .root .geometry ("500x350" ) # Adjust the window size as needed
3243 self .flight_controller = flight_controller
3344 self .param_default_values : dict [str , Par ] = {}
3445
46+ # Initialize UI components
47+ self ._init_ui ()
48+
49+ # Log flight controller information
50+ self ._log_flight_controller_info ()
51+
52+ # Schedule parameter download after window is shown
53+ if not root_tk : # Only schedule if this is the main window
54+ self .root .after (50 , self ._schedule_download_parameters )
55+
56+ def _init_ui (self ) -> None :
57+ """Initialize the UI components."""
3558 # Create a frame to hold all the labels and text fields
3659 self .info_frame = ttk .Frame (self .main_frame )
3760 self .info_frame .pack (fill = tk .BOTH , padx = 20 , pady = 20 )
3861
3962 # Dynamically create labels and text fields for each attribute
40- for row_nr , (description , attr_value ) in enumerate (flight_controller .info .get_info ().items ()):
63+ self ._create_info_fields ()
64+
65+ self .info_frame .columnconfigure (1 , weight = 1 )
66+
67+ def _create_info_fields (self ) -> None :
68+ """Create the information fields in the UI."""
69+ for row_nr , (description , attr_value ) in enumerate (self .flight_controller .info .get_info ().items ()):
4170 label = ttk .Label (self .info_frame , text = f"{ description } :" )
4271 label .grid (row = row_nr , column = 0 , sticky = "w" )
4372
@@ -54,41 +83,79 @@ def __init__(self, flight_controller: FlightController) -> None:
5483 text_field .insert (tk .END , _ ("N/A" )) # Insert "Not Available" if the attribute is missing or empty
5584 text_field .configure (state = "readonly" )
5685
57- self .info_frame .columnconfigure (1 , weight = 1 )
58-
59- logging_info (_ ("Firmware Version: %s" ), flight_controller .info .flight_sw_version_and_type )
60- logging_info (_ ("Firmware first 8 hex bytes of the FC git hash: %s" ), flight_controller .info .flight_custom_version )
61- logging_info (_ ("Firmware first 8 hex bytes of the ChibiOS git hash: %s" ), flight_controller .info .os_custom_version )
86+ def _log_flight_controller_info (self ) -> None :
87+ """Log information about the flight controller."""
88+ logging_info (_ ("Firmware Version: %s" ), self .flight_controller .info .flight_sw_version_and_type )
89+ logging_info (_ ("Firmware first 8 hex bytes of the FC git hash: %s" ), self .flight_controller .info .flight_custom_version )
90+ logging_info (
91+ _ ("Firmware first 8 hex bytes of the ChibiOS git hash: %s" ), self .flight_controller .info .os_custom_version
92+ )
6293 logging_info (
6394 _ ("Flight Controller firmware type: %s (%s)" ),
64- flight_controller .info .firmware_type ,
65- flight_controller .info .apj_board_id ,
95+ self . flight_controller .info .firmware_type ,
96+ self . flight_controller .info .apj_board_id ,
6697 )
67- logging_info (_ ("Flight Controller HW / board version: %s" ), flight_controller .info .board_version )
68- logging_info (_ ("Flight Controller USB vendor ID: %s" ), flight_controller .info .vendor )
69- logging_info (_ ("Flight Controller USB product ID: %s" ), flight_controller .info .product )
98+ logging_info (_ ("Flight Controller HW / board version: %s" ), self . flight_controller .info .board_version )
99+ logging_info (_ ("Flight Controller USB vendor ID: %s" ), self . flight_controller .info .vendor )
100+ logging_info (_ ("Flight Controller USB product ID: %s" ), self . flight_controller .info .product )
70101
71- self .root .after (50 , self .download_flight_controller_parameters ()) # type: ignore[func-returns-value]
102+ def _schedule_download_parameters (self ) -> None :
103+ """Schedule the download of parameters and exit when complete."""
104+ try :
105+ self .download_flight_controller_parameters ()
106+ self .root .destroy ()
107+ except (OSError , ConnectionError , SerialException , PermissionError , ValueError , RuntimeError ) as e :
108+ logging_error (_ ("Failed to download parameters: %s" ), str (e ))
109+ self .root .destroy ()
110+
111+ def run (self ) -> None :
112+ """Run the application main loop."""
72113 self .root .mainloop ()
73114
74- def download_flight_controller_parameters (self ) -> None :
75- """Download parameters from the flight controller with progress tracking and error handling."""
76- param_download_progress_window = ProgressWindow (
77- self .root , _ ("Downloading FC parameters" ), _ ("Downloaded {} of {} parameters" )
78- )
79- try :
80- self .flight_controller .fc_parameters , self .param_default_values = self .flight_controller .download_params (
81- param_download_progress_window .update_progress_bar
115+ def download_flight_controller_parameters (self , progress_callback : Optional [Callable [[int , int ], None ]] = None ) -> None :
116+ """
117+ Download parameters from the flight controller with progress tracking and error handling.
118+
119+ Args:
120+ progress_callback: Optional callback function to track progress
121+
122+ Raises:
123+ ConnectionError: If connection to the flight controller fails
124+ SerialException: If there's an error with the serial communication
125+ PermissionError: If there's a permission error when accessing the device
126+ IOError: If there's an I/O error during communication
127+ ValueError: If there's an invalid value during parameter processing
128+ RuntimeError: If any other runtime error occurs during parameter download
129+
130+ """
131+ if progress_callback :
132+ # Use provided callback directly
133+ try :
134+ self .flight_controller .fc_parameters , self .param_default_values = self .flight_controller .download_params (
135+ progress_callback
136+ )
137+ except (OSError , ConnectionError , SerialException , PermissionError , ValueError , RuntimeError ) as e :
138+ logging_error (_ ("Error downloading flight controller parameters: %s" ), str (e ))
139+ # Re-raise the exception after logging
140+ raise
141+ else :
142+ # Create progress window and use its update function
143+ param_download_progress_window = ProgressWindow (
144+ self .root , _ ("Downloading FC parameters" ), _ ("Downloaded {} of {} parameters" )
82145 )
83- except Exception as e :
84- logging_error (_ ("Error downloading flight controller parameters: %s" ), str (e ))
85- # Make sure to destroy the progress window even on error
86- param_download_progress_window .destroy ()
87- # Re-raise the exception after cleanup
88- raise
89- # Normal cleanup path when no exceptions occur
90- param_download_progress_window .destroy () # for the case that '--device test' and there is no real FC connected
91- self .root .destroy ()
146+ try :
147+ self .flight_controller .fc_parameters , self .param_default_values = self .flight_controller .download_params (
148+ param_download_progress_window .update_progress_bar
149+ )
150+ except (OSError , ConnectionError , SerialException , PermissionError , ValueError , RuntimeError ) as e :
151+ logging_error (_ ("Error downloading flight controller parameters: %s" ), str (e ))
152+ # Make sure to destroy the progress window even on error
153+ param_download_progress_window .destroy ()
154+ # Re-raise the exception after cleanup
155+ raise
156+ # Normal cleanup path when no exceptions occur
157+ param_download_progress_window .destroy () # for the case that '--device test' and there is no real FC connected
92158
93159 def get_param_default_values (self ) -> dict [str , Par ]:
160+ """Get the default parameter values."""
94161 return self .param_default_values
0 commit comments