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        #[serde(skip_deserializing)]
49        online: bool,
50        id: String,
51        name: String,
52        settings: Option<CameraSettings>,
53        //#[serde(skip_deserializing)]
54        possible_settings: Option<Vec<CameraSettings>>,
55        subsystems: CameraSubsystems,
56        calib: Option<serde_json::Value>,
57        auto_exposure: bool,
58        manual_exposure: Option<u32>,
59        orientation: VideoOrientation,
60        cam_offsets: CameraOffsets,
61    }
62    CameraSettings {
63        width: u32,
64        height: u32,
65        frame_rate: Option<CfgFraction>,
66        format: Option<String>,
67    }
68    CfgFraction {
69        num: u32,
70        den: u32,
71    }
72    CameraOffsets {
73        translation: CameraOffsetDimensions,
74        rotation: CameraOffsetDimensions,
75    }
76    CameraOffsetDimensions {
77        x: f64,
78        y: f64,
79        z: f64,
80    }
81    CameraSubsystems {
82        mjpeg: Option<MjpegSubsys>,
83        capriltags: Option<CAprilTagsSubsys>,
84        ml: Option<MlSubsys>,
85        custom: Vec<String>,
86    }
87    MjpegSubsys {
88        width: u32,
89        height: u32,
90    }
91    CAprilTagsSubsys {
92        max_frame_rate: u8,
93    }
94    MlSubsys {
95    }
96    CustomSubsystem {
97        code: String,
98    }
99}
100
101impl Config {
102    /// Load the configuration from the specified path
103    pub fn load(path: impl AsRef<Path>) -> Result<Self, Error> {
104        let mut f = File::open(path).map_err(|_| Error::FailedToReadConfig)?;
105        let mut buf = String::new();
106        f.read_to_string(&mut buf).unwrap();
107        toml::from_str(&buf).map_err(|_| Error::InvalidConfig)
108    }
109
110    /// Save the configuration to the specified path
111    pub async fn save(&self, path: impl AsRef<Path>) -> Result<(), Error> {
112        let mut f = File::create(path).unwrap();
113        let toml_cfgg = toml::to_string_pretty(&self).unwrap();
114        f.write_all(toml_cfgg.as_bytes()).unwrap();
115        f.flush().unwrap();
116
117        Ok(())
118    }
119}
120impl Default for Config {
121    fn default() -> Self {
122        Self {
123            team_number: u16::MAX,
124            ntables_ip: None,
125            rerun: None,
126            cameras: None,
127            device_name: None,
128
129            field_layout: None,
130            field_layouts: None,
131
132            custom_subsystems: HashMap::new(),
133        }
134    }
135}
136impl Default for Camera {
137    fn default() -> Self {
138        Self {
139            online: false,
140            id: String::new(),
141            name: String::new(),
142            settings: None,
143            auto_exposure: true,
144            manual_exposure: None,
145            possible_settings: None,
146            subsystems: CameraSubsystems {
147                mjpeg: Some(MjpegSubsys {
148                    width: 1280,
149                    height: 720,
150                }),
151                capriltags: Some(CAprilTagsSubsys { max_frame_rate: 40 }),
152                ml: None,
153                custom: Vec::new(),
154            },
155            calib: None,
156            orientation: VideoOrientation::None,
157            cam_offsets: CameraOffsets {
158                translation: CameraOffsetDimensions {
159                    x: 0.0,
160                    y: 0.0,
161                    z: 0.0,
162                },
163                rotation: CameraOffsetDimensions {
164                    x: 0.0,
165                    y: 0.0,
166                    z: 0.0,
167                },
168            },
169        }
170    }
171}
172
173impl Default for CameraSettings {
174    fn default() -> Self {
175        Self {
176            width: 0,
177            height: 0,
178            frame_rate: None,
179            format: None,
180        }
181    }
182}
183
184#[derive(Deserialize, Serialize, Clone)]
185#[cfg_attr(feature = "web", derive(utopia::ToSchema))]
186#[serde(rename_all = "snake_case")]
187pub enum CameraKind {
188    PiCam,
189    Usb,
190}
191
192#[derive(Deserialize, Serialize, Debug, Clone)]
193#[cfg_attr(feature = "web", derive(utopia::ToSchema))]
194#[serde(rename_all = "kebab-case")]
195pub enum VideoOrientation {
196    None = 0,
197    Clockwise = 1,
198    #[serde(rename = "rotate-180")]
199    Rotate180 = 2,
200    Counterclockwise = 3,
201}