1use crate::error::{GlintError, Result};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
12pub struct Band {
13 pub name: String,
15 pub default_threshold: f64,
17 pub wavelength: Option<f64>,
19 pub description: Option<String>,
21}
22
23impl Band {
24 pub fn new(name: impl Into<String>, default_threshold: f64) -> Self {
26 Self {
27 name: name.into(),
28 default_threshold,
29 wavelength: None,
30 description: None,
31 }
32 }
33
34 pub fn with_wavelength(
36 name: impl Into<String>,
37 default_threshold: f64,
38 wavelength: f64,
39 ) -> Self {
40 Self {
41 name: name.into(),
42 default_threshold,
43 wavelength: Some(wavelength),
44 description: None,
45 }
46 }
47
48 pub fn with_description(mut self, description: impl Into<String>) -> Self {
50 self.description = Some(description.into());
51 self
52 }
53
54 pub fn validate(&self) -> Result<()> {
56 if self.default_threshold < 0.0 || self.default_threshold > 1.0 {
57 return Err(GlintError::InvalidThreshold {
58 value: self.default_threshold,
59 });
60 }
61
62 if let Some(wavelength) = self.wavelength {
63 if wavelength <= 0.0 || wavelength > 3000.0 {
64 return Err(GlintError::validation(format!(
65 "Invalid wavelength: {} nm",
66 wavelength
67 )));
68 }
69 }
70
71 Ok(())
72 }
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct Sensor {
78 pub id: String,
80 pub name: String,
82 pub bands: Vec<Band>,
84 pub bit_depth: u8,
86 pub loader_type: String,
88 pub description: Option<String>,
90 pub loader_config: HashMap<String, String>,
92}
93
94impl Sensor {
95 pub fn new(
97 id: impl Into<String>,
98 name: impl Into<String>,
99 bands: Vec<Band>,
100 bit_depth: u8,
101 loader_type: impl Into<String>,
102 ) -> Self {
103 Self {
104 id: id.into(),
105 name: name.into(),
106 bands,
107 bit_depth,
108 loader_type: loader_type.into(),
109 description: None,
110 loader_config: HashMap::new(),
111 }
112 }
113
114 pub fn with_description(mut self, description: impl Into<String>) -> Self {
116 self.description = Some(description.into());
117 self
118 }
119
120 pub fn with_loader_config(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
122 self.loader_config.insert(key.into(), value.into());
123 self
124 }
125
126 pub fn default_thresholds(&self) -> Vec<f64> {
128 self.bands.iter().map(|b| b.default_threshold).collect()
129 }
130
131 pub fn band_count(&self) -> usize {
133 self.bands.len()
134 }
135
136 pub fn band_names(&self) -> Vec<&str> {
138 self.bands.iter().map(|b| b.name.as_str()).collect()
139 }
140
141 pub fn validate(&self) -> Result<()> {
143 if !matches!(self.bit_depth, 8 | 16 | 32) {
145 return Err(GlintError::InvalidBitDepth {
146 bit_depth: self.bit_depth,
147 });
148 }
149
150 if self.bands.is_empty() {
152 return Err(GlintError::validation("Sensor must have at least one band"));
153 }
154
155 for band in &self.bands {
156 band.validate()?;
157 }
158
159 let mut names = std::collections::HashSet::new();
161 for band in &self.bands {
162 if !names.insert(&band.name) {
163 return Err(GlintError::validation(format!(
164 "Duplicate band name: {}",
165 band.name
166 )));
167 }
168 }
169
170 Ok(())
171 }
172}
173
174#[derive(Debug, Clone)]
176pub struct SensorRegistry {
177 sensors: HashMap<String, Sensor>,
178}
179
180impl SensorRegistry {
181 pub fn new() -> Self {
183 Self {
184 sensors: HashMap::new(),
185 }
186 }
187
188 pub fn with_defaults() -> Self {
190 let mut registry = Self::new();
191
192 if let Ok(sensors) = Self::load_builtin_sensors() {
194 for sensor in sensors {
195 registry.register_sensor(sensor).unwrap();
196 }
197 }
198
199 registry
200 }
201
202 pub fn from_user_config() -> Self {
204 let mut registry = Self::new();
205
206 if let Ok(sensors) = Self::load_user_sensors() {
208 for sensor in sensors {
209 if registry.register_sensor(sensor).is_err() {
210 continue;
212 }
213 }
214 }
215
216 if registry.is_empty() {
218 if let Ok(sensors) = Self::load_builtin_sensors() {
219 for sensor in sensors {
220 registry.register_sensor(sensor).unwrap();
221 }
222 }
223 }
224
225 registry
226 }
227
228 fn load_user_sensors() -> Result<Vec<Sensor>> {
230 use crate::config::ConfigManager;
231
232 let config_manager = ConfigManager::new();
233 let config_dir = config_manager.get_config_dir()?;
234 let config_path = config_dir.join("sensors.toml");
235
236 if !config_path.exists() {
238 std::fs::create_dir_all(&config_dir)
239 .map_err(|_| GlintError::config("Failed to create config directory"))?;
240
241 config_manager.create_sample_config(&config_path)?;
243 }
244
245 config_manager.load_from_file(&config_path)
246 }
247
248 fn load_builtin_sensors() -> Result<Vec<Sensor>> {
250 const BUILTIN_CONFIG: &str = include_str!("../../sensor_configs/default_sensors.toml");
252
253 let config_file: crate::config::sensor_config::SensorConfigFile =
254 toml::from_str(BUILTIN_CONFIG).map_err(GlintError::Deserialization)?;
255
256 if config_file.version != "1.0" {
258 return Err(GlintError::config(format!(
259 "Unsupported configuration file version: {}",
260 config_file.version
261 )));
262 }
263
264 let mut sensors = Vec::new();
266 for sensor_config in config_file.sensors {
267 let sensor = Sensor::from(sensor_config);
268 sensor.validate()?;
269 sensors.push(sensor);
270 }
271
272 Ok(sensors)
273 }
274
275 pub fn register_sensor(&mut self, sensor: Sensor) -> Result<()> {
277 sensor.validate()?;
278
279 if self.sensors.contains_key(&sensor.id) {
280 return Err(GlintError::validation(format!(
281 "Sensor with ID '{}' already registered",
282 sensor.id
283 )));
284 }
285
286 self.sensors.insert(sensor.id.clone(), sensor);
287 Ok(())
288 }
289
290 pub fn get_sensor(&self, id: &str) -> Option<&Sensor> {
292 self.sensors.get(id)
293 }
294
295 pub fn sensor_ids(&self) -> Vec<&str> {
297 self.sensors.keys().map(|s| s.as_str()).collect()
298 }
299
300 pub fn sensors(&self) -> Vec<&Sensor> {
302 self.sensors.values().collect()
303 }
304
305 pub fn remove_sensor(&mut self, id: &str) -> Option<Sensor> {
307 self.sensors.remove(id)
308 }
309
310 pub fn has_sensor(&self, id: &str) -> bool {
312 self.sensors.contains_key(id)
313 }
314
315 pub fn len(&self) -> usize {
317 self.sensors.len()
318 }
319
320 pub fn is_empty(&self) -> bool {
322 self.sensors.is_empty()
323 }
324
325 pub fn load_from_file(&mut self, path: &std::path::Path) -> Result<()> {
327 use crate::config::ConfigManager;
329 let config_manager = ConfigManager::new();
330 let sensors = config_manager.load_from_file(path)?;
331
332 for sensor in sensors {
333 self.register_sensor(sensor)?;
334 }
335
336 Ok(())
337 }
338
339 pub fn save_to_file(&self, path: &std::path::Path) -> Result<()> {
341 use crate::config::ConfigManager;
343 let config_manager = ConfigManager::new();
344 let sensors: Vec<Sensor> = self.sensors.values().cloned().collect();
345 config_manager.save_to_file(&sensors, path)
346 }
347}
348
349impl Default for SensorRegistry {
350 fn default() -> Self {
351 Self::new()
352 }
353}
354
355pub mod bands {
357 use super::Band;
358
359 pub fn blue() -> Band {
361 Band::with_wavelength("Blue", 0.875, 475.0)
362 .with_description("Blue band for visible light imaging")
363 }
364
365 pub fn green() -> Band {
367 Band::with_wavelength("Green", 1.0, 560.0)
368 .with_description("Green band for visible light imaging")
369 }
370
371 pub fn red() -> Band {
373 Band::with_wavelength("Red", 1.0, 650.0)
374 .with_description("Red band for visible light imaging")
375 }
376
377 pub fn red_edge() -> Band {
379 Band::with_wavelength("Red Edge", 1.0, 730.0)
380 .with_description("Red edge band for vegetation analysis")
381 }
382
383 pub fn near_ir() -> Band {
385 Band::with_wavelength("Near-IR", 1.0, 840.0)
386 .with_description("Near-infrared band for vegetation analysis")
387 }
388}
389
390#[cfg(test)]
391mod tests {
392 use super::*;
393 use tempfile::tempdir;
394
395 #[test]
396 fn test_band_creation() {
397 let band = Band::new("Red", 0.9);
398 assert_eq!(band.name, "Red");
399 assert_eq!(band.default_threshold, 0.9);
400 assert!(band.wavelength.is_none());
401
402 let band = Band::with_wavelength("Blue", 0.8, 475.0);
403 assert_eq!(band.wavelength, Some(475.0));
404 }
405
406 #[test]
407 fn test_band_validation() {
408 let band = Band::new("Red", 0.5);
409 assert!(band.validate().is_ok());
410
411 let band = Band::new("Red", 1.5);
412 assert!(band.validate().is_err());
413
414 let band = Band::with_wavelength("Red", 0.5, -100.0);
415 assert!(band.validate().is_err());
416 }
417
418 #[test]
419 fn test_sensor_creation() {
420 let bands = vec![
421 Band::new("Red", 0.9),
422 Band::new("Green", 0.8),
423 Band::new("Blue", 0.7),
424 ];
425
426 let sensor = Sensor::new("rgb", "RGB Camera", bands, 8, "single_file");
427 assert_eq!(sensor.id, "rgb");
428 assert_eq!(sensor.band_count(), 3);
429 assert_eq!(sensor.default_thresholds(), vec![0.9, 0.8, 0.7]);
430 }
431
432 #[test]
433 fn test_sensor_validation() {
434 let bands = vec![Band::new("Red", 0.9)];
435 let sensor = Sensor::new("test", "Test", bands, 8, "single_file");
436 assert!(sensor.validate().is_ok());
437
438 let sensor = Sensor::new("test", "Test", vec![], 8, "single_file");
439 assert!(sensor.validate().is_err());
440
441 let bands = vec![Band::new("Red", 1.5)];
442 let sensor = Sensor::new("test", "Test", bands, 8, "single_file");
443 assert!(sensor.validate().is_err());
444 }
445
446 #[test]
447 fn test_sensor_registry() {
448 let mut registry = SensorRegistry::new();
449
450 let bands = vec![Band::new("Red", 0.9)];
451 let sensor = Sensor::new("test", "Test", bands, 8, "single_file");
452
453 assert!(registry.register_sensor(sensor).is_ok());
454 assert!(registry.has_sensor("test"));
455 assert_eq!(registry.len(), 1);
456
457 let retrieved = registry.get_sensor("test").unwrap();
458 assert_eq!(retrieved.id, "test");
459 }
460
461 #[test]
462 fn test_sensor_registry_file_operations() {
463 let temp_dir = tempdir().unwrap();
464 let config_path = temp_dir.path().join("sensors.toml");
465
466 let mut registry = SensorRegistry::new();
467 let bands = vec![Band::new("Red", 0.9)];
468 let sensor = Sensor::new("test", "Test", bands, 8, "single_file");
469 registry.register_sensor(sensor).unwrap();
470
471 registry.save_to_file(&config_path).unwrap();
473 assert!(config_path.exists());
474
475 let mut new_registry = SensorRegistry::new();
477 new_registry.load_from_file(&config_path).unwrap();
478 assert!(new_registry.has_sensor("test"));
479 }
480}