//! `scan_controller` — central typed state machine. //! //! States per architecture.md §5: `ZoomedOut | ZoomedIn { roi, hold_started_at } //! | TargetFollow { target_id, started_at }`. Full behaviour-tree spec lives in //! `system-flows.md §F4`. //! //! Real implementation lands in: //! - AZ-682 `scan_controller_state_machine` //! - AZ-683 `scan_controller_poi_queue_and_window` //! - AZ-684 `scan_controller_evidence_ladder` //! - AZ-685 `scan_controller_mapobjects_dispatch` //! - AZ-686 `scan_controller_gimbal_issuance` use serde::{Deserialize, Serialize}; use uuid::Uuid; use shared::error::{AutopilotError, Result}; use shared::health::ComponentHealth; use shared::models::operator::OperatorCommand; const NAME: &str = "scan_controller"; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(tag = "state", rename_all = "snake_case")] pub enum ScanState { ZoomedOut, ZoomedIn { roi: Uuid, hold_started_at_ns: u64 }, TargetFollow { target_id: Uuid, started_at_ns: u64 }, } pub struct ScanController; impl ScanController { pub fn new() -> Self { Self } pub fn handle(&self) -> ScanControllerHandle { ScanControllerHandle } } impl Default for ScanController { fn default() -> Self { Self::new() } } #[derive(Clone, Copy)] pub struct ScanControllerHandle; impl ScanControllerHandle { pub async fn tick(&self) -> Result<()> { Err(AutopilotError::NotImplemented( "scan_controller::tick (AZ-682)", )) } pub async fn submit_operator_cmd(&self, _command: OperatorCommand) -> Result<()> { Err(AutopilotError::NotImplemented( "scan_controller::submit_operator_cmd (AZ-682)", )) } pub fn state(&self) -> ScanState { ScanState::ZoomedOut } pub fn health(&self) -> ComponentHealth { ComponentHealth::disabled(NAME) } } #[cfg(test)] mod tests { use super::*; #[test] fn it_compiles() { let h = ScanController::new().handle(); assert!(matches!(h.state(), ScanState::ZoomedOut)); assert_eq!(h.health().level, shared::health::HealthLevel::Disabled); } }