Skip to main content

mecha10_controllers/mock/
camera.rs

1//! Mock camera controller for testing
2
3use crate::camera::*;
4use crate::{Controller, ControllerCapabilities, ControllerError, ControllerHealth, ControllerState};
5use async_trait::async_trait;
6use mecha10_core::sensor::{CameraInfo, DepthImage, RgbImage};
7use std::time::{SystemTime, UNIX_EPOCH};
8
9/// Mock camera controller configuration
10#[derive(Debug, Clone)]
11pub struct MockCameraConfig {
12    pub width: u32,
13    pub height: u32,
14    pub fps: u32,
15    pub has_depth: bool,
16    pub has_infrared: bool,
17}
18
19impl Default for MockCameraConfig {
20    fn default() -> Self {
21        Self {
22            width: 640,
23            height: 480,
24            fps: 30,
25            has_depth: true,
26            has_infrared: false,
27        }
28    }
29}
30
31/// Mock camera controller for testing
32///
33/// Generates synthetic frames with configurable patterns.
34pub struct MockCameraController {
35    config: MockCameraConfig,
36    state: ControllerState,
37    frame_count: u64,
38    intrinsics: CameraIntrinsics,
39    parameters: CameraParameters,
40}
41
42#[async_trait]
43impl Controller for MockCameraController {
44    type Config = MockCameraConfig;
45    type Error = ControllerError;
46
47    async fn init(config: Self::Config) -> Result<Self, Self::Error> {
48        let intrinsics = CameraIntrinsics {
49            width: config.width,
50            height: config.height,
51            fx: 500.0,
52            fy: 500.0,
53            cx: config.width as f64 / 2.0,
54            cy: config.height as f64 / 2.0,
55            distortion: vec![0.0; 5],
56            distortion_model: "plumb_bob".to_string(),
57        };
58
59        Ok(Self {
60            config,
61            state: ControllerState::Initialized,
62            frame_count: 0,
63            intrinsics,
64            parameters: CameraParameters::default(),
65        })
66    }
67
68    async fn start(&mut self) -> Result<(), Self::Error> {
69        self.state = ControllerState::Running;
70        Ok(())
71    }
72
73    async fn stop(&mut self) -> Result<(), Self::Error> {
74        self.state = ControllerState::Stopped;
75        Ok(())
76    }
77
78    async fn health_check(&self) -> ControllerHealth {
79        match self.state {
80            ControllerState::Running => ControllerHealth::Healthy,
81            ControllerState::Error => ControllerHealth::Unhealthy {
82                reason: "Mock error".to_string(),
83            },
84            _ => ControllerHealth::Unknown,
85        }
86    }
87
88    fn capabilities(&self) -> ControllerCapabilities {
89        ControllerCapabilities::new("camera", "mock")
90            .with_vendor("Mecha10")
91            .with_model("Mock Camera")
92            .with_feature("depth", self.config.has_depth)
93            .with_feature("infrared", self.config.has_infrared)
94            .with_feature("rgb", true)
95    }
96}
97
98#[async_trait]
99impl CameraController for MockCameraController {
100    type Frame = CameraFrame;
101
102    async fn capture_frame(&mut self) -> Result<Self::Frame, Self::Error> {
103        if self.state != ControllerState::Running {
104            return Err(ControllerError::InvalidState("Camera not running".to_string()));
105        }
106
107        self.frame_count += 1;
108
109        let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_micros() as u64;
110
111        // Generate synthetic RGB image (gradient pattern)
112        let rgb = self.generate_rgb_image(timestamp);
113
114        if self.config.has_depth {
115            // Generate synthetic depth image
116            let depth = self.generate_depth_image(timestamp);
117
118            Ok(CameraFrame::Rgbd {
119                color: rgb,
120                depth,
121                aligned: true,
122            })
123        } else {
124            Ok(CameraFrame::Rgb(rgb))
125        }
126    }
127
128    fn intrinsics(&self) -> Option<CameraIntrinsics> {
129        Some(self.intrinsics.clone())
130    }
131
132    async fn set_parameters(&mut self, params: CameraParameters) -> Result<(), Self::Error> {
133        self.parameters = params;
134        Ok(())
135    }
136
137    async fn get_parameters(&self) -> Result<CameraParameters, Self::Error> {
138        Ok(self.parameters.clone())
139    }
140
141    fn info(&self) -> CameraInfo {
142        CameraInfo {
143            timestamp: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_micros() as u64,
144            width: self.config.width,
145            height: self.config.height,
146            fps: self.config.fps as f32,
147        }
148    }
149}
150
151impl MockCameraController {
152    /// Generate a synthetic RGB image with a gradient pattern
153    fn generate_rgb_image(&self, timestamp: u64) -> RgbImage {
154        let size = (self.config.width * self.config.height * 3) as usize;
155        let mut data = vec![0u8; size];
156
157        // Create a time-varying gradient pattern
158        let phase = (timestamp / 1_000_000) % 256;
159
160        for y in 0..self.config.height {
161            for x in 0..self.config.width {
162                let idx = ((y * self.config.width + x) * 3) as usize;
163                data[idx] = ((x as u64 + phase) % 256) as u8; // R
164                data[idx + 1] = ((y as u64 + phase) % 256) as u8; // G
165                data[idx + 2] = ((x as u64 + y as u64 + phase) % 256) as u8; // B
166            }
167        }
168
169        RgbImage {
170            timestamp,
171            frame_id: self.frame_count,
172            width: self.config.width,
173            height: self.config.height,
174            encoding: "rgb8".to_string(),
175            data,
176        }
177    }
178
179    /// Generate a synthetic depth image with a radial pattern
180    fn generate_depth_image(&self, timestamp: u64) -> DepthImage {
181        let size = (self.config.width * self.config.height) as usize;
182        let mut data = vec![0.0f32; size];
183
184        let cx = self.config.width as f32 / 2.0;
185        let cy = self.config.height as f32 / 2.0;
186
187        for y in 0..self.config.height {
188            for x in 0..self.config.width {
189                let idx = (y * self.config.width + x) as usize;
190
191                // Radial distance from center
192                let dx = x as f32 - cx;
193                let dy = y as f32 - cy;
194                let dist = (dx * dx + dy * dy).sqrt();
195
196                // Convert to depth (closer at center, farther at edges)
197                let max_dist = (cx * cx + cy * cy).sqrt();
198                data[idx] = 1.0 + (dist / max_dist) * 3.0; // 1-4 meters
199            }
200        }
201
202        DepthImage {
203            timestamp,
204            frame_id: self.frame_count,
205            width: self.config.width,
206            height: self.config.height,
207            data,
208        }
209    }
210
211    /// Get the current frame count
212    pub fn frame_count(&self) -> u64 {
213        self.frame_count
214    }
215
216    /// Reset the frame count
217    pub fn reset_frame_count(&mut self) {
218        self.frame_count = 0;
219    }
220}