|
8 | 8 | SPDX-License-Identifier: GPL-3.0-or-later
|
9 | 9 | """
|
10 | 10 |
|
| 11 | +import logging |
11 | 12 | import tkinter as tk
|
12 |
| - |
13 |
| -# from logging import debug as logging_debug |
14 |
| -from logging import info as logging_info |
15 |
| -from tkinter import ttk |
| 13 | +from tkinter import messagebox, ttk |
| 14 | +from typing import Callable, Optional, Union |
16 | 15 |
|
17 | 16 | from ardupilot_methodic_configurator import _, __version__
|
18 | 17 | from ardupilot_methodic_configurator.annotate_params import Par
|
|
21 | 20 | from ardupilot_methodic_configurator.frontend_tkinter_progress_window import ProgressWindow
|
22 | 21 |
|
23 | 22 |
|
| 23 | +class FlightControllerInfoPresenter: |
| 24 | + """ |
| 25 | + Business logic for flight controller information presentation. |
| 26 | +
|
| 27 | + Separated from UI for better testability. |
| 28 | + """ |
| 29 | + |
| 30 | + def __init__(self, flight_controller: FlightController) -> None: |
| 31 | + self.flight_controller = flight_controller |
| 32 | + self.param_default_values: dict[str, Par] = {} |
| 33 | + |
| 34 | + def get_info_data(self) -> dict[str, Union[str, dict[str, str]]]: |
| 35 | + """Get formatted flight controller information for display.""" |
| 36 | + return self.flight_controller.info.get_info() |
| 37 | + |
| 38 | + def log_flight_controller_info(self) -> None: |
| 39 | + """Log flight controller information.""" |
| 40 | + self.flight_controller.info.log_flight_controller_info() |
| 41 | + |
| 42 | + def download_parameters(self, progress_callback: Optional[Callable[[int, int], None]] = None) -> dict[str, Par]: |
| 43 | + """ |
| 44 | + Download flight controller parameters. |
| 45 | +
|
| 46 | + Args: |
| 47 | + progress_callback: Optional callback function for progress updates |
| 48 | +
|
| 49 | + Returns: |
| 50 | + Dictionary of parameter default values |
| 51 | +
|
| 52 | + """ |
| 53 | + fc_parameters, param_default_values = self.flight_controller.download_params(progress_callback) |
| 54 | + self.flight_controller.fc_parameters = fc_parameters |
| 55 | + self.param_default_values = param_default_values |
| 56 | + return param_default_values |
| 57 | + |
| 58 | + def get_param_default_values(self) -> dict[str, Par]: |
| 59 | + """Get parameter default values.""" |
| 60 | + return self.param_default_values |
| 61 | + |
| 62 | + |
24 | 63 | class FlightControllerInfoWindow(BaseWindow):
|
25 | 64 | """Display flight controller hardware, firmware and parameter information."""
|
26 | 65 |
|
27 | 66 | def __init__(self, flight_controller: FlightController) -> None:
|
28 | 67 | super().__init__()
|
29 | 68 | self.root.title(_("ArduPilot methodic configurator ") + __version__ + _(" - Flight Controller Info"))
|
30 | 69 | self.root.geometry("500x350") # Adjust the window size as needed
|
31 |
| - self.flight_controller = flight_controller |
32 |
| - self.param_default_values: dict[str, Par] = {} |
33 | 70 |
|
| 71 | + self.presenter = FlightControllerInfoPresenter(flight_controller) |
| 72 | + |
| 73 | + self._create_info_display() |
| 74 | + self.presenter.log_flight_controller_info() |
| 75 | + |
| 76 | + # Schedule parameter download after UI is ready |
| 77 | + self.root.after(50, self._download_flight_controller_parameters) |
| 78 | + self.root.mainloop() |
| 79 | + |
| 80 | + def _create_info_display(self) -> None: |
| 81 | + """Create the flight controller information display.""" |
34 | 82 | # Create a frame to hold all the labels and text fields
|
35 | 83 | self.info_frame = ttk.Frame(self.main_frame)
|
36 | 84 | self.info_frame.pack(fill=tk.BOTH, padx=20, pady=20)
|
37 | 85 |
|
38 | 86 | # Dynamically create labels and text fields for each attribute
|
39 |
| - for row_nr, (description, attr_value) in enumerate(flight_controller.info.get_info().items()): |
40 |
| - label = ttk.Label(self.info_frame, text=f"{description}:") |
41 |
| - label.grid(row=row_nr, column=0, sticky="w") |
42 |
| - |
43 |
| - text_field = ttk.Entry(self.info_frame) |
44 |
| - text_field.grid(row=row_nr, column=1, sticky="ew", columnspan=1) |
45 |
| - |
46 |
| - # Check if the attribute exists and has a non-empty value before inserting |
47 |
| - if attr_value: |
48 |
| - if isinstance(attr_value, dict): |
49 |
| - text_field.insert(tk.END, (", ").join(attr_value.keys())) |
50 |
| - else: |
51 |
| - text_field.insert(tk.END, attr_value) |
52 |
| - else: |
53 |
| - text_field.insert(tk.END, _("N/A")) # Insert "Not Available" if the attribute is missing or empty |
54 |
| - text_field.configure(state="readonly") |
| 87 | + info_data = self.presenter.get_info_data() |
| 88 | + for row_nr, (description, attr_value) in enumerate(info_data.items()): |
| 89 | + self._create_info_row(row_nr, description, attr_value) |
55 | 90 |
|
56 | 91 | self.info_frame.columnconfigure(1, weight=1)
|
57 | 92 |
|
58 |
| - logging_info(_("Firmware Version: %s"), flight_controller.info.flight_sw_version_and_type) |
59 |
| - logging_info(_("Firmware first 8 hex bytes of the FC git hash: %s"), flight_controller.info.flight_custom_version) |
60 |
| - logging_info(_("Firmware first 8 hex bytes of the ChibiOS git hash: %s"), flight_controller.info.os_custom_version) |
61 |
| - logging_info( |
62 |
| - _("Flight Controller firmware type: %s (%s)"), |
63 |
| - flight_controller.info.firmware_type, |
64 |
| - flight_controller.info.apj_board_id, |
65 |
| - ) |
66 |
| - logging_info(_("Flight Controller HW / board version: %s"), flight_controller.info.board_version) |
67 |
| - logging_info(_("Flight Controller USB vendor ID: %s"), flight_controller.info.vendor) |
68 |
| - logging_info(_("Flight Controller USB product ID: %s"), flight_controller.info.product) |
| 93 | + def _create_info_row(self, row_nr: int, description: str, attr_value: Union[str, dict[str, str]]) -> None: |
| 94 | + """Create a single row of information display.""" |
| 95 | + label = ttk.Label(self.info_frame, text=f"{description}:") |
| 96 | + label.grid(row=row_nr, column=0, sticky="w") |
69 | 97 |
|
70 |
| - self.root.after(50, self.download_flight_controller_parameters()) # type: ignore[func-returns-value] |
71 |
| - self.root.mainloop() |
| 98 | + text_field = ttk.Entry(self.info_frame) |
| 99 | + text_field.grid(row=row_nr, column=1, sticky="ew", columnspan=1) |
| 100 | + |
| 101 | + # Format the value for display using the backend logic |
| 102 | + display_value = self.presenter.flight_controller.info.format_display_value(attr_value) |
| 103 | + text_field.insert(tk.END, display_value) |
| 104 | + text_field.configure(state="readonly") |
72 | 105 |
|
73 |
| - def download_flight_controller_parameters(self) -> None: |
| 106 | + def _download_flight_controller_parameters(self) -> None: |
| 107 | + """Download flight controller parameters with progress feedback.""" |
74 | 108 | param_download_progress_window = ProgressWindow(
|
75 | 109 | self.root, _("Downloading FC parameters"), _("Downloaded {} of {} parameters")
|
76 | 110 | )
|
77 |
| - self.flight_controller.fc_parameters, self.param_default_values = self.flight_controller.download_params( |
78 |
| - param_download_progress_window.update_progress_bar |
79 |
| - ) |
80 |
| - param_download_progress_window.destroy() # for the case that '--device test' and there is no real FC connected |
81 |
| - self.root.destroy() |
| 111 | + |
| 112 | + try: |
| 113 | + self.presenter.download_parameters(param_download_progress_window.update_progress_bar) |
| 114 | + except Exception as e: # pylint: disable=broad-exception-caught |
| 115 | + # Log the error |
| 116 | + logging.error("Failed to download parameters: %s", e) |
| 117 | + # Show an error message to the user |
| 118 | + messagebox.showerror(_("Error"), f"{_('Failed to download parameters')}: {e}") |
| 119 | + finally: |
| 120 | + param_download_progress_window.destroy() # for the case that '--device test' and there is no real FC connected |
| 121 | + self.root.destroy() |
82 | 122 |
|
83 | 123 | def get_param_default_values(self) -> dict[str, Par]:
|
84 |
| - return self.param_default_values |
| 124 | + """Get parameter default values from the presenter.""" |
| 125 | + return self.presenter.get_param_default_values() |
0 commit comments