Recovery - #1941
Conversation
There was a problem hiding this comment.
Pull request overview
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
This PR improves resilience when dealing with MAVFTP parameter transfer failures and ambiguous/invalid frame/vehicle identifiers, preventing hard exits and enabling UI-level recovery paths.
Changes:
- Replace
sys.exit()behavior during MAVFTP param decode/read failures with error returns and fallback handling. - Resolve ambiguous VTOL vehicle type classification using firmware banner parsing.
- Allow motor-test UI/model to remain usable when
FRAME_CLASS/FRAME_TYPEcombos are invalid, including metadata-based recovery and new layouts.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_data_model_flightcontroller_info.py | Updates classification expectations for ambiguous VTOL MAV type. |
| tests/test_backend_mavftp.py | Adds coverage ensuring MAVFTP decode failures return an error instead of exiting. |
| tests/test_backend_flightcontroller_params.py | Adds coverage for fallback behavior when MAVFTP transfer raises. |
| tests/test_backend_flightcontroller_connection.py | Adds tests for early vehicle-type logging and banner-based resolution. |
| tests/plugins/test_data_model_motor_test.py | Adds/updates tests for invalid frame-type correction and motor layout population. |
| ardupilot_methodic_configurator/plugins/frontend_tkinter_motor_test.py | Hardens combobox selection and forces diagram refresh on frame-type changes/activation. |
| ardupilot_methodic_configurator/plugins/data_model_motor_test.py | Refactors motor layout loading; introduces invalid-frame-type recovery and metadata fallback. |
| ardupilot_methodic_configurator/plugins/AP_Motors_test.json | Adds a BiCopter (class 10) layout for motor testing. |
| ardupilot_methodic_configurator/log_analysis/data_model_quality_fft.py | Minor simplification of return path. |
| ardupilot_methodic_configurator/data_model_flightcontroller_info.py | Marks VTOL_DUOROTOR as ambiguous vehicle type. |
| ardupilot_methodic_configurator/backend_mavftp.py | Returns MAVFTP error codes instead of terminating on param read/decode failures. |
| ardupilot_methodic_configurator/backend_flightcontroller_params.py | Converts MAVFTP download exceptions into logged fallback results. |
| ardupilot_methodic_configurator/backend_flightcontroller_connection.py | Extracts firmware type from banner more robustly; resolves ambiguous heartbeat vehicle types. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| class_name = next( | ||
| (str(name).strip().upper() for code, name in class_values.items() if int(code) == frame_class), | ||
| None, | ||
| ) |
| try: | ||
| mavftp = create_mavftp(self.master) | ||
|
|
||
| def get_params_progress_callback(completion: float) -> None: | ||
| if progress_callback is not None and completion is not None: | ||
| progress_callback(int(completion * 100), 100) |
| except Exception as error: # pylint: disable=broad-exception-caught | ||
| logging_warning( | ||
| _("MAVFTP parameter download failed; falling back to MAVLink: %(error)s"), | ||
| {"error": str(error)}, | ||
| ) | ||
| return {}, ParDict() |
| data = fh.read() | ||
| except OSError as exp: | ||
| logging.error("FTP: Failed to read file param.pck: %s", exp) | ||
| sys.exit(1) | ||
| return MAVFTPReturn("GetParams", ERR_Fail) | ||
| pdata = MAVFTP.ftp_param_decode(data) | ||
| if pdata is None: | ||
| sys.exit(1) | ||
| logging.error("FTP: Failed to decode parameter file param.pck") | ||
| return MAVFTPReturn("GetParams", ERR_Fail) |
| frame_type_pairs = self.model.get_frame_type_pairs() | ||
| current_selection = self.model.get_current_frame_selection_key() if frame_type_pairs else None | ||
| current_selection_key = self.model.get_current_frame_selection_key() if frame_type_pairs else None | ||
| current_selection = current_selection_key if current_selection_key in {key for key, _ in frame_type_pairs} else None |
☂️ Code Coverage
Overall Coverage
New FilesNo new covered files... Modified Files
|
Test Results 4 files ± 0 4 suites ±0 45m 58s ⏱️ -31s For more details on these failures, see this check. Results for commit 35a8852. ± Comparison against base commit 0f76d6d. This pull request removes 1 and adds 7 tests. Note that renamed tests count towards both.♻️ This comment has been updated with latest results. |
c274b9a to
8d74c95
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.
Suppressed comments (3)
ardupilot_methodic_configurator/data_model_flightcontroller_info.py:252
- Encoding “ambiguity” as a human-readable string (and then keying logic off
" or "elsewhere) is brittle and mixes display text with decision logic. A more robust approach is to store ambiguity as structured state (e.g.,vehicle_type_candidates: set[str]or a dedicated enum/flag) and derive a user-facing string separately; then banner resolution can update the structured field without relying on substring checks.
mavutil.mavlink.MAV_TYPE_VTOL_DUOROTOR: "ArduPlane or ArduCopter",
ardupilot_methodic_configurator/plugins/frontend_tkinter_motor_test.py:937
- Displaying a modal UI dialog inside a model factory couples model instantiation to the UI layer and makes the factory harder to reuse/test (and can be problematic if invoked before Tk is fully initialized, depending on call site). Prefer returning the model + error status to the view layer (or raising a recoverable exception) and letting the UI decide how/when to notify the user.
model = MotorTestDataModel(context.flight_controller, context.local_filesystem)
if model.invalid_frame_type_error:
showerror(_("Invalid Frame Type"), model.invalid_frame_type_error)
return model
tests/test_backend_flightcontroller_connection.py:1325
- This assertion is likely brittle because it depends on the exact MAVLink enum description formatting (punctuation/capitalization) from
pymavlinkand could change across versions/localization. Consider asserting on more stable substrings (e.g.,"Vehicle type:"and"running ArduPlane firmware") and relying primarily onconnection.info.vehicle_type == "ArduPlane"for correctness.
assert "Vehicle type: Fixed wing aircraft." in caplog.text
| break | ||
| if layout.get("Class") == self._frame_class and layout.get("motors"): | ||
| has_frame_class_configuration = True | ||
| if layout.get("Class") == self._frame_class and layout.get("Type") == self._frame_type: |
Coverage Report for CI Build 33101030817Warning Build has drifted: This PR's base is out of sync with its target branch, so coverage data may include unrelated changes. Coverage decreased (-0.01%) to 89.378%Details
Uncovered ChangesNo uncovered changes found. Coverage Regressions39 previously-covered lines in 3 files lost coverage.
Coverage Stats💛 - Coveralls |
… state Keep the motor-test plugin usable when the flight controller reports a valid frame class with an unsupported FRAME_TYPE. Show an informative error dialog while still rendering the plugin and allowing the user to select a valid frame type. Refresh motor controls and diagrams after frame changes and flight-controller resets. Avoid invalid combobox selections and suppress diagram lookups while the frame type is invalid. Add regression coverage for invalid frame recovery, plugin rendering, diagram refresh.
Description
Recover from invalid FRAME_CLASS, FRAME_TYPE combinations
Checklist
git commit --signoff)Testing
Describe how you tested these changes: