use std::collections::{BTreeMap, BTreeSet};
use std::path::PathBuf;
use crate::model::CapabilityRole;
use crate::model::component::capability::CapabilityKind;
use crate::model::identity::{CapabilityId, LinkId};
use crate::model::robot::{KinematicConfig, MotionLimits};
use crate::authoring::source::robot::driver::DriverConfig;
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct Robot {
pub id: String,
pub structure: PathBuf,
pub kinematic: KinematicConfig,
pub motion_limits: MotionLimits,
pub instances: BTreeMap<String, ComponentInstance>,
pub services: BTreeMap<String, Option<serde_json::Value>>,
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct ComponentInstance {
pub component_type: String,
pub mount_link: String,
pub driver: Option<DriverConfig>,
pub roles: BTreeMap<String, BTreeSet<CapabilityRole>>,
pub parameters: BTreeMap<String, CapabilityParameters>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct CapabilityParameters {
pub kind: CapabilityKind,
pub direction_sign: i8,
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct Component {
pub capabilities: BTreeMap<CapabilityId, crate::model::component::capability::Capability>,
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct Simulation {
pub capabilities: BTreeMap<CapabilityId, crate::model::simulation::Capability>,
pub links: BTreeMap<LinkId, Option<String>>,
}
impl Robot {
pub(crate) fn used_component_types(&self) -> BTreeSet<&str> {
self.instances
.values()
.map(|instance| instance.component_type.as_str())
.collect()
}
}
#[cfg(test)]
mod tests {
use crate::authoring::source::robot::Manifest;
fn document(components: &str, services: &str, roles: &str) -> String {
format!(
r#"
schema: phoxal/robot/v0
robot:
id: order-bot
motion_limits:
max_linear_speed_mps: 0.6
max_angular_speed_radps: 2.0
kinematic:
kind: omnidirectional
actuators: [alpha.motor]
encoders: []
components:
{components}
services:
{services}
"#
)
.replace("<ROLES>", roles)
}
const ALPHA: &str = " alpha:\n component: drive\n mount_link: alpha_mount\n \
roles:\n range: <ROLES>\n";
const BETA: &str = " beta:\n component: sensor\n mount_link: beta_mount\n";
#[test]
fn normalization_is_deterministic_and_independent_of_authored_order() -> anyhow::Result<()> {
let first = Manifest::parse(&document(
&[ALPHA, BETA].concat(),
" localize: {}\n map: {}\n",
"[mapping, safety]",
))?
.normalize()?;
let again = Manifest::parse(&document(
&[ALPHA, BETA].concat(),
" localize: {}\n map: {}\n",
"[mapping, safety]",
))?
.normalize()?;
assert_eq!(first, again, "the same document normalizes to one value");
let reordered = Manifest::parse(&document(
&[BETA, ALPHA].concat(),
" map: {}\n localize: {}\n",
"[safety, mapping]",
))?
.normalize()?;
assert_eq!(
reordered, first,
"authored order is not a fact that survives normalization"
);
assert_eq!(
first.instances.keys().collect::<Vec<_>>(),
["alpha", "beta"]
);
assert_eq!(
first.services.keys().collect::<Vec<_>>(),
["localize", "map"]
);
Ok(())
}
}