Greenfield Steps 1-6 baseline for the autopilot rewrite from legacy Qt/C++ to a Rust workspace. - Remove legacy Qt/C++ tree (ai_controller, drone_controller, misc/camera, python_scaffold, root Dockerfile, autopilot.pro, legacy main.py / requirements.txt). - Add _docs/00_problem (problem, restrictions, acceptance criteria, security approach, input data + fixtures). - Add _docs/01_solution/solution_draft01. - Add _docs/02_document (architecture, system-flows, data_model, glossary, decision-rationale, deployment, 13 component descriptions, tests/ specs, FINAL_report, module-layout). - Add _docs/02_tasks/todo with 47 task specs (AZ-640..AZ-686, one bootstrap + 46 component tasks) and _dependencies_table.md. - Add .cursor/rules/artifact-srp.mdc (single-responsibility rule for canonical _docs artifacts). - Track autodev state in _docs/_autodev_state.md (Step 6 completed, ready for Step 7 Implement). Jira: bootstrap AZ-626; component epics AZ-627..AZ-639; tasks AZ-640..AZ-686. Total complexity 173 points across 12 epics. Co-authored-by: Cursor <cursoragent@cursor.com>
7.1 KiB
Component — scan_controller
Layer: Decision + Memory Status: forward-looking design (Rust)
1. Purpose
The system's brain. A deterministic typed state machine — ZoomedOut, ZoomedIn { roi, hold_started_at }, and TargetFollow { target_id, started_at }. Owns the POI queue, timeouts, the ≤5 POIs/min operator-review cap, the confidence-scaled operator-decision window, gimbal command issuance, and the new/moved/existing/removed dispatch into mapobjects_store.
The full behaviour-tree spec — including tick scenarios and the 15 fixed-wing rules — lives in system-flows.md §F4.
2. Inputs
| Input | Source | Cadence | Notes |
|---|---|---|---|
DetectionBatch |
detection_client |
per frame | Tier 1 primitives. |
MovementCandidate |
movement_detector |
per frame at both zoom-out and zoom-in (suppressed only during TargetFollow) |
Each candidate carries source_zoom_band. |
Tier2Evidence |
semantic_analyzer |
per zoom-in hold | Path / endpoint / concealment scoring. |
VlmAssessment |
vlm_client (optional) |
per zoom-in endpoint hold | status: disabled if VLM is off. |
| Operator commands | operator_bridge |
event | confirm / decline / target-follow start / target-follow release. Authenticated, signed, replay-protected upstream of this component. |
| UAV telemetry | mavlink_layer (via mission_executor) |
10 Hz target | Position used for proximity-weighted POI priority and middle-waypoint computation. |
| Mission state | mission_executor |
event | Current waypoint, mission progress; used for sweep-vs-route alignment. |
| MapObjects sync state | mapobjects_store |
event at startup + post-flight | synced / cached_fallback / degraded — surfaces a health flag and (for degraded) suppresses MapObject diff classifications until corrected. |
3. Outputs
| Output | Consumer | Shape |
|---|---|---|
GimbalCommand (yaw / pitch / zoom) |
gimbal_controller |
per state-machine tick or per zoom-in plan step |
POI to operator |
operator_bridge (then telemetry_stream) |
enqueue / dequeue events |
| Middle-waypoint hint | mission_executor |
event on operator-confirmed target |
| MapObjects update | mapobjects_store |
new / moved / existing / removed dispatch |
| Health metric | health aggregator | state, pois_in_queue, pois_per_min, tick_latency_p99, last_state_change_ts, mapobjects_sync_state. |
4. Key Responsibilities
- Run the
ZoomedOut/ZoomedIn/TargetFollowstate machine. Transitions are explicit, typed, and fully enumerated; no ad-hoc booleans. - Maintain the POI queue ordered by
confidence × proximity_to_current_camera × age_factor. Hard-cap output to ≤5 POIs/min surfaced to the operator. - Apply the confidence-scaled operator decision window (40 % → 30 s, 100 % → 120 s, linear; below 40 % the POI is not surfaced). Timeout = forget; decline =
IgnoredItementry viamapobjects_store. - Suppress new POIs whose
(MGRS, class_group)matches an existingIgnoredItem. - For each new detection or movement candidate: compute the H3 cell, ask
mapobjects_storeto classify as new / moved / existing, and only surface non-existing entries. - Zoom-in candidate handling. When a
MovementCandidatearrives withsource_zoom_band = zoomed_in, evaluate against the current ROI: if inside, bump current-ROI confidence; if outside the ROI but inside the broader zoomed FOV, enqueue as a candidate-POI; only interrupt the current zoom-in hold if the candidate's priority exceeds the current hold's priority. - On operator confirmation: hand a middle-waypoint hint to
mission_executor, transition toTargetFollow, and commandgimbal_controllerto keep the target in the centre 25 % of frame. - On operator decline / timeout / target loss: append (decline only) an
IgnoredItemand return toZoomedOut. - On
mapobjects_storereportingsync_state = degraded, surface health → red and do not classify new detections (avoid corrupting the central observation log on next push); continue to surface POIs to the operator on Tier-1 + movement evidence alone.
5. Internal State
The state machine lives entirely in this component. State variables:
- Current state:
ZoomedOut | ZoomedIn { roi, hold_started_at } | TargetFollow { target_id, started_at }. - POI queue: ordered, with per-entry priority and queue position.
- Per-class operator-decision-window thresholds.
- Last-N tick timestamps for tick-latency observability.
- Frame-rate floor monitor: when sustained FPS < 10, suppress
ZoomedOut → ZoomedIntransitions and surface health → yellow.
State is in-process only; restart starts in ZoomedOut with an empty queue.
6. Failure Modes
| Failure | Detection | Behaviour |
|---|---|---|
detection_client health red |
health input | Continue zoom-out sweep; emit no new POIs from Tier 1; movement candidates still flow. |
movement_detector health red |
health input | Continue; lose movement-candidate enqueueing. |
semantic_analyzer health red |
health input | Skip Tier 2; surface POIs with Tier-1-only evidence; flag in operator overlay. |
vlm_client returns status: disabled | timeout | ipc_error | schema_invalid |
per-call status | Surface POI without VLM evidence (fail-closed). |
gimbal_controller not ready |
health input | Stay in current state; alert; do not silently drop scan steps. |
operator_bridge disconnected |
health input | Continue zoom-out (operator UI is unreachable, but the system must not crash); pause POI surfacing; resume on reconnect. F10 lost-link ladder owns the larger response. |
mapobjects_store sync degraded |
sync_state input | Suppress diff classifications; surface POIs on Tier-1 + movement only; health → red. |
| Sustained FPS < 10 | self-instrumented | Suppress zoom-in transitions; health → yellow. |
| Tick-latency above budget | self-instrumented | Health → yellow; investigate (likely upstream consumer slowness). |
7. Dependencies
In-process (input): detection_client, movement_detector, semantic_analyzer, vlm_client, operator_bridge, mission_executor, mapobjects_store.
In-process (output): gimbal_controller, operator_bridge, mission_executor, mapobjects_store.
External: none directly. All external integrations are mediated by other components.
8. Non-Functional Targets
| Concern | Target |
|---|---|
| Tick latency | ≤10 ms p99 |
| POI enqueue → operator surface | ≤1 s in normal load |
| POI rate to operator | ≤5 POIs/min (hard cap) |
| Zoom-out → zoom-in transition | ≤2 s including physical zoom |
| Zoom-in hold duration | configurable; default 5 s/POI |
| Target-follow centre-window | target inside centre 25 % of frame while visible |
| Frame-rate floor | ≥10 fps sustained; below this, suppress zoom-in transitions |
9. References
architecture.md §3,§5 Architectural Principles,§6 NFR,§7.6 Scan controller and POI queue,§7.12 New vs Existing / Moved / Removed Object Detection,§7.13 MapObjects Sync.system-flows.md §F4 Scan controller behaviour tree(full BT spec, tick scenarios, 15 fixed-wing rules).data_model.md §POI,§IgnoredItem,§MapObject.