mecha10_controllers/mock/
camera.rs1use 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#[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
31pub 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 let rgb = self.generate_rgb_image(timestamp);
113
114 if self.config.has_depth {
115 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 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 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; data[idx + 1] = ((y as u64 + phase) % 256) as u8; data[idx + 2] = ((x as u64 + y as u64 + phase) % 256) as u8; }
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 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 let dx = x as f32 - cx;
193 let dy = y as f32 - cy;
194 let dist = (dx * dx + dy * dy).sqrt();
195
196 let max_dist = (cx * cx + cy * cy).sqrt();
198 data[idx] = 1.0 + (dist / max_dist) * 3.0; }
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 pub fn frame_count(&self) -> u64 {
213 self.frame_count
214 }
215
216 pub fn reset_frame_count(&mut self) {
218 self.frame_count = 0;
219 }
220}