Skip to main content

braid_sim/
harness.rs

1// Copyright (C) The Strand-Braid Authors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Generate the on-disk artifacts a full `braid-run` needs to run a scenario:
5//! the synthetic calibration (flydra XML) and a Braid configuration TOML wiring
6//! up the `sim` camera backend.
7
8use std::path::{Path, PathBuf};
9
10use braid_config_data::{BraidConfig, MainbrainConfig};
11use braid_types::{BraidCameraConfig, FakeSyncConfig, StartCameraBackend, TriggerType};
12
13use crate::Scenario;
14use crate::calibration::{build_tracking_calibration, to_flydra_xml_string};
15
16/// Paths to the artifacts generated for a run.
17#[derive(Debug, Clone)]
18pub struct GeneratedRun {
19    /// The Braid configuration TOML (pass to `braid run`).
20    pub config_path: PathBuf,
21    /// The synthetic calibration (flydra XML), referenced by the config.
22    pub calibration_path: PathBuf,
23    /// The directory into which Braid writes `.braidz` files.
24    pub braidz_output_dir: PathBuf,
25}
26
27/// Build the in-memory Braid configuration for a scenario: one `sim`-backed
28/// camera per scenario camera, FakeSync at the scenario frame rate, and the
29/// given calibration / output directory / control-API address.
30pub fn build_braid_config(
31    scenario: &Scenario,
32    calibration_path: &Path,
33    braidz_output_dir: &Path,
34    http_api_server_addr: &str,
35) -> BraidConfig {
36    let mainbrain = MainbrainConfig {
37        cal_fname: Some(calibration_path.to_path_buf()),
38        output_base_dirname: braidz_output_dir.to_path_buf(),
39        http_api_server_addr: http_api_server_addr.to_string(),
40        ..Default::default()
41    };
42
43    let trigger = TriggerType::FakeSync(FakeSyncConfig {
44        framerate: scenario.fps,
45    });
46
47    let cameras = (0..scenario.cameras.count)
48        .map(|k| {
49            let mut cam = BraidCameraConfig::default_absdiff_config(Scenario::camera_name(k));
50            cam.start_backend = StartCameraBackend::Sim;
51            cam
52        })
53        .collect();
54
55    BraidConfig {
56        mainbrain,
57        trigger,
58        cameras,
59    }
60}
61
62/// Serialize a [`BraidConfig`] to TOML.
63///
64/// Uses the same two-step `toml::Value` dance as `braid default-config` to avoid
65/// a `ValueAfterTable` serialization error.
66pub fn braid_config_to_toml(config: &BraidConfig) -> eyre::Result<String> {
67    let value = toml::Value::try_from(config)?;
68    Ok(toml::to_string(&value)?)
69}
70
71/// Write the calibration XML and the Braid config TOML for `scenario` into
72/// `out_dir`, returning the resulting paths.
73pub fn generate_run(
74    scenario: &Scenario,
75    out_dir: &Path,
76    http_api_server_addr: &str,
77) -> eyre::Result<GeneratedRun> {
78    std::fs::create_dir_all(out_dir)?;
79
80    // Braid reconstructs with the *tracking* calibration (the perfect generation
81    // calibration with any scenario perturbation applied); the sim cameras still
82    // project ground truth with the perfect one, so a perturbation shows up as
83    // realistic reprojection error.
84    let calibration_path = out_dir.join("calibration.xml");
85    let system = build_tracking_calibration(scenario)?;
86    std::fs::write(&calibration_path, to_flydra_xml_string(&system)?)?;
87
88    let braidz_output_dir = out_dir.join("braid-data");
89
90    let config = build_braid_config(
91        scenario,
92        &calibration_path,
93        &braidz_output_dir,
94        http_api_server_addr,
95    );
96    let config_path = out_dir.join("braid-config.toml");
97    std::fs::write(&config_path, braid_config_to_toml(&config)?)?;
98
99    Ok(GeneratedRun {
100        config_path,
101        calibration_path,
102        braidz_output_dir,
103    })
104}