use serde::{Deserialize, Serialize};
use crate::model::component::v0::CapabilityRef;
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
#[cfg_attr(test, derive(schemars::JsonSchema))]
#[serde(deny_unknown_fields)]
pub struct MotionLimits {
pub max_linear_speed_mps: f64,
pub max_angular_speed_radps: f64,
}
impl MotionLimits {
pub fn validate(self) -> anyhow::Result<Self> {
anyhow::ensure!(
self.max_linear_speed_mps.is_finite() && self.max_linear_speed_mps > 0.0,
"robot.motion_limits.max_linear_speed_mps must be finite and > 0"
);
anyhow::ensure!(
self.max_linear_speed_mps <= f64::from(f32::MAX),
"robot.motion_limits.max_linear_speed_mps must fit in f32"
);
anyhow::ensure!(
self.max_angular_speed_radps.is_finite() && self.max_angular_speed_radps > 0.0,
"robot.motion_limits.max_angular_speed_radps must be finite and > 0"
);
anyhow::ensure!(
self.max_angular_speed_radps <= f64::from(f32::MAX),
"robot.motion_limits.max_angular_speed_radps must fit in f32"
);
Ok(self)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[cfg_attr(test, derive(schemars::JsonSchema))]
#[serde(deny_unknown_fields)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum KinematicConfig {
Differential {
#[cfg_attr(test, schemars(with = "Vec<String>"))]
left_actuators: Vec<CapabilityRef>,
#[cfg_attr(test, schemars(with = "Vec<String>"))]
right_actuators: Vec<CapabilityRef>,
#[cfg_attr(test, schemars(with = "Vec<String>"))]
left_encoders: Vec<CapabilityRef>,
#[cfg_attr(test, schemars(with = "Vec<String>"))]
right_encoders: Vec<CapabilityRef>,
wheel_radius_m: f64,
wheel_base_m: f64,
},
Mecanum {
#[cfg_attr(test, schemars(with = "String"))]
front_left_actuator: CapabilityRef,
#[cfg_attr(test, schemars(with = "String"))]
front_right_actuator: CapabilityRef,
#[cfg_attr(test, schemars(with = "String"))]
rear_left_actuator: CapabilityRef,
#[cfg_attr(test, schemars(with = "String"))]
rear_right_actuator: CapabilityRef,
wheel_radius_m: f64,
wheel_base_m: f64,
track_m: f64,
},
Ackermann {
#[cfg_attr(test, schemars(with = "String"))]
steering_actuator: CapabilityRef,
#[cfg_attr(test, schemars(with = "String"))]
drive_actuator: CapabilityRef,
#[cfg_attr(test, schemars(with = "Option<String>"))]
steering_encoder: Option<CapabilityRef>,
#[cfg_attr(test, schemars(with = "Option<String>"))]
drive_encoder: Option<CapabilityRef>,
wheel_base_m: f64,
track_m: f64,
max_steering_angle_rad: f64,
},
Omnidirectional {
#[cfg_attr(test, schemars(with = "Vec<String>"))]
actuators: Vec<CapabilityRef>,
#[cfg_attr(test, schemars(with = "Vec<String>"))]
encoders: Vec<CapabilityRef>,
},
}
impl KinematicConfig {
#[must_use]
pub const fn variant_label(&self) -> &'static str {
match self {
Self::Differential { .. } => "differential",
Self::Mecanum { .. } => "mecanum",
Self::Ackermann { .. } => "ackermann",
Self::Omnidirectional { .. } => "omnidirectional",
}
}
}