[AZ-652] mission_executor safety + resume + middle-waypoint (batch 9)

Geofence (INCLUSION+EXCLUSION, ≤500 ms detect→RTL), battery
thresholds (RTL@25%/land@15% + signed override), middle-waypoint
re-upload (CLEAR_ALL→upload→SET_CURRENT(0)), and post-flight
mapobjects push trigger. Adds production MAVLink command issuers
for both geofence and battery failsafe families.

Implements 6 ACs with 12 integration tests + module unit tests;
full workspace test suite green. See batch_09_cycle1_report.md
for AC coverage and known limitations.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-19 19:48:46 +03:00
parent 8a4bd00526
commit 358b2fbb53
10 changed files with 2392 additions and 47 deletions
@@ -0,0 +1,92 @@
# Geofence + Battery Enforcement + Middle-Waypoint Re-Upload + Post-Flight Trigger
**Task**: AZ-652_mission_executor_safety_and_resume
**Name**: Geofence + battery thresholds + middle-waypoint re-upload + post-flight push trigger
**Description**: Continuous safety enforcement (INCLUSION + EXCLUSION geofences honoured equally; battery thresholds with operator override). Mission re-upload on middle-waypoint hint. Mission revert on target-follow ending. Trigger post-flight MapObjects push on `POST_FLIGHT_SYNC` entry.
**Complexity**: 5 points
**Dependencies**: AZ-640_initial_structure, AZ-648_mission_executor_state_machine, AZ-649_mission_executor_telemetry_forwarding, AZ-643_mavlink_ack_demux_and_signing, AZ-647_mission_client_mapobjects_push
**Component**: mission_executor
**Tracker**: AZ-652
**Epic**: AZ-636
## Problem
The state machine alone is not enough — three continuous concerns must run on every tick:
1. **Geofence enforcement**: both INCLUSION and EXCLUSION violations trigger RTL. The earlier C++ behaviour silently ignored EXCLUSION; the new design rejects that.
2. **Battery / fuel thresholds**: RTL at `battery ≤ rtl_threshold` (default 25 %); land-now at `battery ≤ hard_floor` (default 15 %); operator override only via signed command.
3. **Middle-waypoint re-upload + target-follow revert**: on operator confirm, recompute and re-upload (`MISSION_CLEAR_ALL` → re-upload → `MISSION_SET_CURRENT(0)`); on target-follow ending, recompute and re-upload the original mission from current position.
Plus the post-flight trigger: on `POST_FLIGHT_SYNC` entry, hand off to `mission_client::push_mapobjects_diff`.
## Outcome
- `GeofenceMonitor::tick(uav_telemetry, mission_geofences)` triggers RTL on INCLUSION exit or EXCLUSION entry within ≤500 ms; alert is observable.
- `BatteryMonitor::tick(sys_status, ext_sys_state)` triggers RTL at `≤rtl_threshold`, land-now at `≤hard_floor`; signed operator-override is honoured and audit-logged.
- `MissionRePlanner::on_middle_waypoint(hint)` computes the patched mission and issues the re-upload sequence; result is observable.
- `MissionRePlanner::on_target_follow_release(reason)` recomputes the original mission from the current position and re-uploads.
- On entry to `POST_FLIGHT_SYNC`, the executor calls `mission_client::push_mapobjects_diff(mission_id, diff)`; result is logged; the machine still reaches `DONE` even on push failure (push surface manual-replay warning).
## Scope
### Included
- Continuous geofence check using `geo` crate or equivalent (point-in-polygon).
- Continuous battery check using `SYS_STATUS` + `EXTENDED_SYS_STATE`.
- Re-upload sequence helpers.
- Post-flight push trigger.
### Excluded
- Middle-waypoint computation algorithm (`scan_controller` provides the hint with `target_mgrs` + `target_class`; the executor only handles re-upload mechanics).
- Operator signature validation (`operator_bridge`).
- The actual push (`mission_client` task 08).
- The audit log persistence layer (lives in `shared::audit`).
## Acceptance Criteria
**AC-1: INCLUSION geofence exit triggers RTL**
Given a multirotor flying inside an INCLUSION polygon
When the UAV position crosses outside the polygon
Then RTL is triggered within ≤500 ms; the alert is observable; the state machine transitions to `LAND`.
**AC-2: EXCLUSION geofence entry triggers RTL**
Given a multirotor flying outside an EXCLUSION polygon
When the UAV position crosses into the polygon
Then RTL is triggered within ≤500 ms (parity with INCLUSION); the alert is observable.
**AC-3: Battery thresholds**
Given a multirotor flying with battery at 30 %
When `SYS_STATUS` reports battery at 24 %
Then RTL is triggered; transition to `LAND`.
When (in a separate scenario) `SYS_STATUS` drops below 15 %
Then `MAV_CMD_NAV_LAND` is issued (land-now); health → red.
**AC-4: Signed operator override of battery RTL**
Given the battery monitor would otherwise RTL at 24 %
When a signed `BatteryOverride { until_ts }` is received from `operator_bridge`
Then RTL is suppressed until `until_ts`; the override is recorded with operator id + rationale in the audit log.
**AC-5: Middle-waypoint re-upload sequence**
Given a confirmed POI yields a middle-waypoint hint
When `on_middle_waypoint` is invoked
Then the sequence `MISSION_CLEAR_ALL` → upload all waypoints → `MISSION_SET_CURRENT(0)` is issued in order, completing in ≤2 s end-to-end.
**AC-6: Post-flight push trigger**
Given the executor enters `POST_FLIGHT_SYNC`
When the entry guard runs
Then `mission_client::push_mapobjects_diff(mission_id, diff)` is called exactly once; the executor reaches `DONE` regardless of push success.
## Non-Functional Requirements
**Performance**
- Geofence response time: ≤500 ms from violation detection to RTL command.
- Middle-waypoint re-upload: ≤2 s end-to-end.
**Reliability**
- Both geofence variants enforced; symmetric behaviour.
- No infinite retry on re-upload — bounded by the executor's transition-retry budget.
## Runtime Completeness
- **Named capability**: geofence enforcement (both variants) + battery thresholds + re-upload sequence + post-flight push trigger.
- **Production code that must exist**: real point-in-polygon; real `SYS_STATUS` decode; real `MAV_CMD_*` issuance.
- **Unacceptable substitutes**: ignoring EXCLUSION (the pre-existing C++ bug) is unacceptable; ignoring battery overrides without signed proof is unacceptable.