Skip to main content

regain_core/
camera_options.rs

1//! Live camera controls shared by all frontends, serviced at worker checkpoints.
2use crate::RecoveryOptions;
3use anyhow::Result;
4use serde::{Deserialize, Serialize};
5use std::sync::{Arc, Mutex};
6
7#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
8#[serde(rename_all = "camelCase", deny_unknown_fields)]
9pub struct CameraRuntimeOptions {
10    pub transfer_log_mode: String,
11    pub cooler_max_power_change_per_5_seconds: f64,
12}
13impl CameraRuntimeOptions {
14    pub fn from_recovery(options: &RecoveryOptions) -> Self {
15        Self {
16            transfer_log_mode: options.transfer_log_mode.clone(),
17            cooler_max_power_change_per_5_seconds: options.cooler_max_power_change_per_5_seconds,
18        }
19    }
20    pub fn validate(&self) -> Result<()> {
21        let mut recovery = RecoveryOptions::default();
22        self.apply(&mut recovery);
23        recovery.validate()
24    }
25    pub fn apply(&self, recovery: &mut RecoveryOptions) {
26        recovery
27            .transfer_log_mode
28            .clone_from(&self.transfer_log_mode);
29        recovery.cooler_max_power_change_per_5_seconds = self.cooler_max_power_change_per_5_seconds;
30    }
31}
32#[derive(Clone)]
33pub struct CameraRuntimeHandle(Arc<Mutex<CameraRuntimeOptions>>);
34impl CameraRuntimeHandle {
35    pub(crate) fn new(options: &RecoveryOptions) -> Self {
36        Self(Arc::new(Mutex::new(CameraRuntimeOptions::from_recovery(
37            options,
38        ))))
39    }
40    pub fn get(&self) -> CameraRuntimeOptions {
41        self.0.lock().unwrap().clone()
42    }
43    pub fn update(&self, options: CameraRuntimeOptions) -> Result<()> {
44        options.validate()?;
45        *self.0.lock().unwrap() = options;
46        Ok(())
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53    #[test]
54    fn live_controls_are_shared_with_one_owner_and_invalid_changes_are_inert() {
55        let recovery = RecoveryOptions::default();
56        let first = CameraRuntimeHandle::new(&recovery);
57        let owner = first.clone();
58        let second = CameraRuntimeHandle::new(&recovery);
59        let mut options = first.get();
60        options.transfer_log_mode = "off".into();
61        options.cooler_max_power_change_per_5_seconds = 5.0;
62        first.update(options.clone()).unwrap();
63        assert_eq!(owner.get(), options);
64        assert_eq!(second.get(), CameraRuntimeOptions::from_recovery(&recovery));
65        for invalid in [0.0, f64::NAN, 101.0] {
66            let mut bad = options.clone();
67            bad.cooler_max_power_change_per_5_seconds = invalid;
68            assert!(first.update(bad).is_err());
69            assert_eq!(owner.get(), options);
70        }
71        let mut bad = options.clone();
72        bad.transfer_log_mode = "anything".into();
73        assert!(first.update(bad).is_err());
74        assert_eq!(owner.get(), options);
75    }
76}