1use serde::{Deserialize, Serialize};
6
7use braid_types::{BraidCameraConfig, FakeSyncConfig, TriggerType, TriggerboxConfig};
8
9#[derive(thiserror::Error, Debug)]
11pub enum Error {
12 #[error("lookup error on variable: {source}")]
13 ShellExpandLookupVarError {
14 #[from]
15 source: shellexpand::LookupError<std::env::VarError>,
16 },
17 #[error("IO error: {source}")]
18 IoError {
19 #[from]
20 source: std::io::Error,
21 },
22 #[error("TOML deserialization error: {source}")]
23 TomlDeError {
24 #[from]
25 source: toml::de::Error,
26 },
27}
28
29type Result<T> = std::result::Result<T, Error>;
30
31pub const DEFAULT_HTTP_API_SERVER_ADDR: &str = "127.0.0.1:0";
33
34fn default_http_api_server_addr() -> String {
35 DEFAULT_HTTP_API_SERVER_ADDR.to_string()
36}
37
38pub const DEFAULT_OUTPUT_BASE_DIRNAME: &str = "~/BRAID-DATA";
40
41fn default_output_base_dirname() -> std::path::PathBuf {
42 DEFAULT_OUTPUT_BASE_DIRNAME.into()
43}
44
45fn default_model_server_addr() -> std::net::SocketAddr {
46 braid_types::DEFAULT_MODEL_SERVER_ADDR.parse().unwrap()
47}
48
49fn default_true() -> bool {
50 true
51}
52
53fn split_path<P: AsRef<std::path::Path>>(path: P) -> (std::path::PathBuf, std::path::PathBuf) {
55 let path = path.as_ref();
56 assert!(path.is_file());
57 let mut components = path.components();
58 let filename = components.next_back().unwrap().as_os_str().into();
59 let dirname = components.as_path().into();
60 (dirname, filename)
61}
62
63fn fixup_relative_path(path: &mut std::path::PathBuf, dirname: &std::path::Path) -> Result<()> {
68 let pathstr = path.as_os_str().to_str().unwrap();
69 let expanded = shellexpand::full(&pathstr)?;
70 *path = std::path::PathBuf::from(expanded.to_string());
71
72 if path.is_relative() {
73 *path = dirname.join(&path);
74 }
75 Ok(())
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize)]
82#[serde(deny_unknown_fields)]
83pub struct MainbrainConfig {
84 pub cal_fname: Option<std::path::PathBuf>,
92 #[serde(default = "default_output_base_dirname")]
95 pub output_base_dirname: std::path::PathBuf,
96 #[serde(default = "braid_types::default_tracking_params_full_3d")]
98 pub tracking_params: braid_types::TrackingParams,
99 pub lowlatency_camdata_udp_addr: Option<String>,
104 #[serde(default)]
105 pub lowlatency_camdata_udp_port: u16,
106 #[serde(default = "default_http_api_server_addr")]
134 pub http_api_server_addr: String,
135 #[serde(default = "default_model_server_addr")]
137 pub model_server_addr: std::net::SocketAddr,
138 #[serde(default = "default_true")]
140 pub save_empty_data2d: bool,
141 pub secret_base64: Option<String>,
143 pub acquisition_duration_allowed_imprecision_msec: Option<f64>,
151 #[serde(default = "default_write_buffer_size_num_messages")]
154 pub write_buffer_size_num_messages: usize,
155}
156
157impl std::default::Default for MainbrainConfig {
158 fn default() -> Self {
159 Self {
160 cal_fname: None,
161 output_base_dirname: default_output_base_dirname(),
162 tracking_params: braid_types::default_tracking_params_full_3d(),
163 lowlatency_camdata_udp_addr: None,
166 lowlatency_camdata_udp_port: Default::default(),
167 http_api_server_addr: default_http_api_server_addr(),
168 model_server_addr: default_model_server_addr(),
169 save_empty_data2d: true,
170 secret_base64: None,
171 acquisition_duration_allowed_imprecision_msec:
172 braid_types::DEFAULT_ACQUISITION_DURATION_ALLOWED_IMPRECISION_MSEC,
173 write_buffer_size_num_messages: default_write_buffer_size_num_messages(),
174 }
175 }
176}
177
178pub const fn default_write_buffer_size_num_messages() -> usize {
179 10000
180}
181
182#[derive(Debug, Clone, Serialize, Deserialize)]
190#[serde(deny_unknown_fields)]
191pub struct BraidConfig {
192 #[serde(default = "MainbrainConfig::default")]
194 pub mainbrain: MainbrainConfig,
195 #[serde(default)]
197 pub trigger: TriggerType,
198 pub cameras: Vec<BraidCameraConfig>,
199}
200
201impl From<BraidConfig1> for BraidConfig {
202 fn from(orig: BraidConfig1) -> BraidConfig {
203 let trigger = match orig.trigger {
204 None => TriggerType::FakeSync(FakeSyncConfig::default()),
205 Some(x) => TriggerType::TriggerboxV1(x),
206 };
207 BraidConfig {
208 mainbrain: orig.mainbrain,
209 trigger,
210 cameras: orig.cameras,
211 }
212 }
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize)]
217#[serde(deny_unknown_fields)]
218#[doc(hidden)]
219pub struct BraidConfig1 {
220 pub mainbrain: MainbrainConfig,
221 pub trigger: Option<TriggerboxConfig>,
222 pub cameras: Vec<BraidCameraConfig>,
223}
224
225impl BraidConfig {
226 fn fixup_relative_paths(&mut self, orig_path: &std::path::Path) -> Result<()> {
229 let (dirname, _orig_path) = split_path(orig_path);
230
231 if let Some(cal_fname) = self.mainbrain.cal_fname.as_mut() {
233 fixup_relative_path(cal_fname, &dirname)?;
234 }
235
236 fixup_relative_path(&mut self.mainbrain.output_base_dirname, &dirname)?;
238
239 for camera_config in self.cameras.iter_mut() {
241 if let Some(ref mut camera_settings_filename) =
242 camera_config.camera_settings_filename.as_mut()
243 {
244 fixup_relative_path(camera_settings_filename, &dirname)?;
245 }
246 }
247
248 Ok(())
249 }
250}
251
252impl std::default::Default for BraidConfig {
253 fn default() -> Self {
254 Self {
255 mainbrain: MainbrainConfig::default(),
256 trigger: TriggerType::TriggerboxV1(TriggerboxConfig::default()),
261 cameras: vec![
262 BraidCameraConfig::default_absdiff_config("fake-camera-1".to_string()),
263 BraidCameraConfig::default_absdiff_config("fake-camera-2".to_string()),
264 BraidCameraConfig::default_absdiff_config("fake-camera-3".to_string()),
265 ],
266 }
267 }
268}
269
270pub fn parse_config_file<P: AsRef<std::path::Path>>(fname: P) -> Result<BraidConfig> {
272 use std::io::Read;
273
274 let mut file = std::fs::File::open(fname.as_ref())?;
275 let mut contents = String::new();
276 file.read_to_string(&mut contents)?;
277 let mut cfg: BraidConfig = match toml::from_str(&contents) {
278 Ok(cfg) => cfg,
279 Err(err_cfg2) => {
280 let cfg1: BraidConfig1 = match toml::from_str(&contents) {
281 Ok(cfg1) => cfg1,
282 Err(err_cfg1) => {
283 tracing::error!(
284 "parsing config file first as BraidConfig failed \
285 and then again as BraidConfig1 failed. The parse error for \
286 BraidConfig1 is: {}\n The original error when parsing \
287 BraidConfig will now be raised.",
288 err_cfg1
289 );
290 return Err(err_cfg2.into());
291 }
292 };
293 BraidConfig::from(cfg1)
294 }
295 };
296 cfg.fixup_relative_paths(fname.as_ref())?;
298 Ok(cfg)
299}