chalkydri/
config.rs

1use crate::{error::Error, pose::field_layout::AprilTagFieldLayout};
2use std::{
3    collections::HashMap,
4    fs::File,
5    io::{Read, Write},
6    path::Path,
7};
8
9macro_rules! def_cfg {
10    ($(
11        $struct_ident:ident {
12            $(
13            $(# [ $attr:ident $( ( $tt:tt ) )* ])?
14            $ident:ident : $ty:ty ,
15            )*
16        }
17    )*) => {
18       $(
19           #[derive(Deserialize, Serialize, Debug, Clone)]
20           #[cfg_attr(feature = "web", derive(utopia::ToSchema))]
21           pub struct $struct_ident {
22               $(
23                $(#[$attr $( ($tt) )?])?
24                pub $ident: $ty,
25               )*
26           }
27       )*
28    };
29}
30
31def_cfg! {
32    Config {
33        team_number: u16,
34        ntables_ip: Option<String>,
35        rerun: Option<Rerun>,
36        cameras: Option<Vec<Camera>>,
37        device_name: Option<String>,
38
39        field_layout: Option<String>,
40        field_layouts: Option<HashMap<String, AprilTagFieldLayout>>,
41
42        custom_subsystems: HashMap<String, CustomSubsystem>,
43    }
44    Rerun {
45        server_address: Option<String>,
46    }
47    Camera {
48        id: String,
49        name: String,
50        settings: Option<CameraSettings>,
51        //#[serde(skip_deserializing)]
52        possible_settings: Option<Vec<CameraSettings>>,
53        subsystems: CameraSubsystems,
54        calib: Option<serde_json::Value>,
55        auto_exposure: bool,
56        manual_exposure: Option<u32>,
57        orientation: VideoOrientation,
58        cam_offsets: CameraOffsets,
59    }
60    CameraSettings {
61        width: u32,
62        height: u32,
63        frame_rate: Option<CfgFraction>,
64        format: Option<String>,
65    }
66    CfgFraction {
67        num: u32,
68        den: u32,
69    }
70    CameraOffsets {
71        translation: CameraOffsetDimensions,
72        rotation: CameraOffsetDimensions,
73    }
74    CameraOffsetDimensions {
75        x: f64,
76        y: f64,
77        z: f64,
78    }
79    CameraSubsystems {
80        capriltags: Option<CAprilTagsSubsys>,
81        ml: Option<MlSubsys>,
82        custom: Vec<String>,
83    }
84    CAprilTagsSubsys {
85        max_frame_rate: u8,
86    }
87    MlSubsys {
88    }
89    CustomSubsystem {
90        code: String,
91    }
92}
93
94impl Config {
95    /// Load the configuration from the specified path
96    pub fn load(path: impl AsRef<Path>) -> Result<Self, Error> {
97        let mut f = File::open(path).map_err(|_| Error::FailedToReadConfig)?;
98        let mut buf = String::new();
99        f.read_to_string(&mut buf).unwrap();
100        toml::from_str(&buf).map_err(|_| Error::InvalidConfig)
101    }
102
103    /// Save the configuration to the specified path
104    pub async fn save(&self, path: impl AsRef<Path>) -> Result<(), Error> {
105        let mut f = File::create(path).unwrap();
106        let toml_cfgg = toml::to_string_pretty(&self).unwrap();
107        f.write_all(toml_cfgg.as_bytes()).unwrap();
108        f.flush().unwrap();
109
110        Ok(())
111    }
112}
113impl Default for Config {
114    fn default() -> Self {
115        Self {
116            team_number: u16::MAX,
117            ntables_ip: None,
118            rerun: None,
119            cameras: None,
120            device_name: None,
121
122            field_layout: None,
123            field_layouts: None,
124
125            custom_subsystems: HashMap::new(),
126        }
127    }
128}
129impl Default for Camera {
130    fn default() -> Self {
131        Self {
132            id: String::new(),
133            name: String::new(),
134            settings: None,
135            auto_exposure: true,
136            manual_exposure: None,
137            possible_settings: None,
138            subsystems: CameraSubsystems {
139                capriltags: Some(CAprilTagsSubsys { max_frame_rate: 40 }),
140                ml: None,
141                custom: Vec::new(),
142            },
143            calib: None,
144            orientation: VideoOrientation::None,
145            cam_offsets: CameraOffsets {
146                translation: CameraOffsetDimensions {
147                    x: 0.0,
148                    y: 0.0,
149                    z: 0.0,
150                },
151                rotation: CameraOffsetDimensions {
152                    x: 0.0,
153                    y: 0.0,
154                    z: 0.0,
155                },
156            },
157        }
158    }
159}
160
161impl Default for CameraSettings {
162    fn default() -> Self {
163        Self {
164            width: 0,
165            height: 0,
166            frame_rate: None,
167            format: None,
168        }
169    }
170}
171
172#[derive(Deserialize, Serialize, Clone)]
173#[cfg_attr(feature = "web", derive(utopia::ToSchema))]
174#[serde(rename_all = "snake_case")]
175pub enum CameraKind {
176    PiCam,
177    Usb,
178}
179
180#[derive(Deserialize, Serialize, Debug, Clone)]
181#[cfg_attr(feature = "web", derive(utopia::ToSchema))]
182#[serde(rename_all = "kebab-case")]
183pub enum VideoOrientation {
184    None = 0,
185    Clockwise = 1,
186    #[serde(rename = "rotate-180")]
187    Rotate180 = 2,
188    Counterclockwise = 3,
189}