[AZ-649] [AZ-674] [AZ-667] telemetry + vlm schema + mapobjects hydrate batch 6

AZ-649 mission_executor telemetry forwarding:
- shared::models::telemetry::UavTelemetry canonical model
- TelemetryForwarder with atomic ArcSwap snapshot + 3 lossy
  tokio::sync::broadcast channels (MissionExecutor, ScanController,
  MavlinkUplink) + per-consumer drop counters
- MavlinkProjection::from_mavlink for HEARTBEAT/GLOBAL_POSITION_INT/
  ATTITUDE/SYS_STATUS
- spawn_mavlink_pump bridges mavlink_layer into the forwarder at the
  binary edge

AZ-674 vlm_client schema validation + model_version tracking:
- AssessmentParser owns schema validation + model-version state
- wire::read_response_raw splits raw bytes from parsing so invalid
  payloads can be logged size-capped
- VlmStatus gains an Inconclusive variant; exhaustive-match test
  guards downstream consumers
- VlmPipelineStatus mirrors the new variant in shared::models::poi

AZ-667 mapobjects_store hydrate + pending logs + cascade:
- SyncState enum aligned with description.md (FreshBoot, Synced,
  CachedFallback, Degraded, Failed)
- Store::hydrate(MapObjectsBundle) replaces in-memory map atomically;
  freshness=Stale -> CachedFallback
- classify() + end_of_pass append MapObjectObservation events to
  pending_observations (New/Moved/Existing/RemovedCandidate)
- apply_decline + LocalAppended ignored items append to pending_ignored
- drain_pending() returns and clears both logs
- cascade_mission(id) purges by_cell + IgnoredSet + pending logs
- Health surface reports sync_state, pending_obs, pending_ign

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-19 17:40:43 +03:00
parent b5cc0c321c
commit e56d428753
26 changed files with 2122 additions and 62 deletions
@@ -234,13 +234,11 @@ async fn ac1_multirotor_happy_path_reaches_done() {
landed_disarmed: true,
})
.unwrap();
await_state(
&handle,
MissionState::PostFlightSync,
Duration::from_secs(1),
)
.await;
await_state(&handle, MissionState::Done, Duration::from_secs(1)).await;
// PostFlightSync is transient (pure-guard then driver action),
// so the FSM may transit through it inside the poll interval.
// We only assert the terminal Done state — the event stream
// below proves the path traversed PostFlightSync.
await_state(&handle, MissionState::Done, Duration::from_secs(2)).await;
// Assert — health is green at Done, driver saw exactly one of each action.
let health = handle.health().await;
@@ -259,6 +257,7 @@ async fn ac1_multirotor_happy_path_reaches_done() {
observed.push((evt.from, evt.to));
}
assert!(observed.contains(&(MissionState::Disconnected, MissionState::Connected)));
assert!(observed.contains(&(MissionState::Land, MissionState::PostFlightSync)));
assert!(observed.contains(&(MissionState::PostFlightSync, MissionState::Done)));
let _ = join.await;