Skip to content

Commit 064deae

Browse files
committed
feat(motor test): Make the motor test application more pythonic
It now raises exceptions that the frontend should catch and display to the user. The motor diagrams are all png now, I've given up on svg. They're to hard to render correctly
1 parent 550e80d commit 064deae

7 files changed

Lines changed: 995 additions & 688 deletions

ARCHITECTURE_motor_test.md

Lines changed: 231 additions & 79 deletions
Large diffs are not rendered by default.

ardupilot_methodic_configurator/backend_filesystem_program_settings.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,10 @@ def _get_settings_defaults(cls) -> dict[str, Union[int, bool, str, float, dict]]
6868
"annotate_docs_into_param_files": False,
6969
"gui_complexity": "simple", # simple or normal
7070
# Motor test settings
71-
"motor_test_duration": 2.5, # Default test duration in seconds
72-
"motor_test_throttle_pct": 10, # Default throttle percentage (10%)
71+
"motor_test": {
72+
"duration": 2.5, # Default test duration in seconds
73+
"throttle_pct": 10, # Default throttle percentage (10%)
74+
},
7375
}
7476

7577
@staticmethod
@@ -341,14 +343,14 @@ def set_setting(setting: str, value: Union[bool, str, float]) -> None:
341343
@staticmethod
342344
def motor_diagram_filepath(frame_class: int, frame_type: int) -> tuple[str, str]:
343345
"""
344-
Get the filepath for the motor diagram SVG file.
346+
Get the filepath for the motor diagram PNG file.
345347
346348
Args:
347349
frame_class: ArduPilot frame class (1=QUAD, 2=HEXA, etc.)
348350
frame_type: ArduPilot frame type (0=PLUS, 1=X, etc.)
349351
350352
Returns:
351-
str: Absolute path to the motor diagram SVG file
353+
str: Absolute path to the motor diagram PNG file
352354
str: Error message if multiple or no files found, empty string if no error
353355
354356
"""
@@ -361,12 +363,12 @@ def motor_diagram_filepath(frame_class: int, frame_type: int) -> tuple[str, str]
361363
# Running as script
362364
application_path = os_path.dirname(os_path.dirname(os_path.abspath(__file__)))
363365

364-
images_dir = os_path.join(application_path, "ardupilot_methodic_configurator", "images")
366+
images_dir = os_path.join(application_path, "ardupilot_methodic_configurator", "images", "motor_diagrams_png")
365367

366-
# Generate SVG filename based on frame configuration
367-
filename = f"m_{frame_class:02d}_{frame_type:02d}_*.svg"
368+
# Generate PNG filename based on frame configuration
369+
filename = f"m_{frame_class:02d}_{frame_type:02d}_*.png"
368370

369-
# Search for matching SVG file (since exact naming varies)
371+
# Search for matching PNG file (since exact naming varies)
370372
matching_files = glob.glob(os_path.join(images_dir, filename))
371373

372374
err_msg = (

ardupilot_methodic_configurator/backend_flightcontroller.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,43 @@ def set_param(self, param_name: str, param_value: float) -> None:
714714
return
715715
self.master.param_set_send(param_name, param_value)
716716

717+
def fetch_param(self, param_name: str, timeout: int = 5) -> Optional[float]:
718+
"""
719+
Fetch a parameter from the flight controller using MAVLink PARAM_REQUEST_READ message.
720+
721+
Args:
722+
param_name (str): The name of the parameter to fetch.
723+
timeout (int): Timeout in seconds to wait for the response. Default is 5.
724+
725+
Returns:
726+
float: The value of the parameter, or None if not found or timeout occurred.
727+
728+
"""
729+
if self.master is None: # FIXME for testing only pylint: disable=fixme
730+
return None
731+
732+
# Send PARAM_REQUEST_READ message
733+
self.master.mav.param_request_read_send(
734+
self.master.target_system,
735+
self.master.target_component,
736+
param_name.encode("utf-8"),
737+
-1, # param_index: -1 means use param_id instead
738+
)
739+
740+
# Wait for PARAM_VALUE response
741+
start_time = time_time()
742+
while time_time() - start_time < timeout:
743+
msg = self.master.recv_match(type="PARAM_VALUE", blocking=False)
744+
if msg is not None:
745+
# Check if this is the parameter we requested
746+
received_param_name = msg.param_id.rstrip("\x00")
747+
if received_param_name == param_name:
748+
logging_debug(_("Received parameter: %s = %s"), param_name, msg.param_value)
749+
return float(msg.param_value)
750+
time_sleep(0.01) # Small sleep to prevent busy waiting
751+
752+
raise TimeoutError(_("Timeout waiting for parameter %s") % param_name)
753+
717754
def reset_all_parameters_to_default(self) -> tuple[bool, str]:
718755
"""
719756
Reset all parameters to their factory default values.

0 commit comments

Comments
 (0)