use heapless::String;
use crate::config::{TrajectoryConfig, WaypointTrajectory};
use crate::config::units::{Degrees, DegreesPerSecSquared};
use crate::error::{Error, Result, TrajectoryError};
#[derive(Debug, Clone)]
pub struct TrajectoryBuilder {
motor: Option<String<32>>,
target_degrees: Option<Degrees>,
velocity_percent: u8,
acceleration_percent: u8,
acceleration: Option<DegreesPerSecSquared>,
deceleration: Option<DegreesPerSecSquared>,
dwell_ms: Option<u32>,
}
impl Default for TrajectoryBuilder {
fn default() -> Self {
Self::new()
}
}
impl TrajectoryBuilder {
pub fn new() -> Self {
Self {
motor: None,
target_degrees: None,
velocity_percent: 100,
acceleration_percent: 100,
acceleration: None,
deceleration: None,
dwell_ms: None,
}
}
pub fn motor(mut self, name: &str) -> Self {
self.motor = String::try_from(name).ok();
self
}
pub fn target(mut self, position: Degrees) -> Self {
self.target_degrees = Some(position);
self
}
pub fn velocity_percent(mut self, percent: u8) -> Self {
self.velocity_percent = percent.clamp(1, 200);
self
}
pub fn acceleration_percent(mut self, percent: u8) -> Self {
self.acceleration_percent = percent.clamp(1, 200);
self
}
pub fn acceleration(mut self, accel: DegreesPerSecSquared) -> Self {
self.acceleration = Some(accel);
self
}
pub fn deceleration(mut self, decel: DegreesPerSecSquared) -> Self {
self.deceleration = Some(decel);
self
}
pub fn asymmetric(mut self, accel: DegreesPerSecSquared, decel: DegreesPerSecSquared) -> Self {
self.acceleration = Some(accel);
self.deceleration = Some(decel);
self
}
pub fn dwell(mut self, dwell_ms: u32) -> Self {
self.dwell_ms = Some(dwell_ms);
self
}
pub fn build(self) -> Result<TrajectoryConfig> {
let motor = self.motor.ok_or_else(|| {
Error::Trajectory(TrajectoryError::InvalidName(
String::try_from("motor not specified").unwrap(),
))
})?;
let target_degrees = self.target_degrees.ok_or_else(|| {
Error::Trajectory(TrajectoryError::InvalidName(
String::try_from("target not specified").unwrap(),
))
})?;
Ok(TrajectoryConfig {
motor,
target_degrees,
velocity_percent: self.velocity_percent,
acceleration_percent: self.acceleration_percent,
acceleration: self.acceleration,
deceleration: self.deceleration,
dwell_ms: self.dwell_ms,
})
}
}
pub const MAX_WAYPOINTS: usize = 32;
#[derive(Debug, Clone)]
pub struct WaypointTrajectoryBuilder {
motor: Option<String<32>>,
waypoints: heapless::Vec<Degrees, MAX_WAYPOINTS>,
velocity_percent: u8,
dwell_ms: u32,
}
impl Default for WaypointTrajectoryBuilder {
fn default() -> Self {
Self::new()
}
}
impl WaypointTrajectoryBuilder {
pub fn new() -> Self {
Self {
motor: None,
waypoints: heapless::Vec::new(),
velocity_percent: 100,
dwell_ms: 0,
}
}
pub fn motor(mut self, name: &str) -> Self {
self.motor = String::try_from(name).ok();
self
}
pub fn waypoint(mut self, position: Degrees) -> Self {
let _ = self.waypoints.push(position);
self
}
pub fn waypoints(mut self, positions: &[Degrees]) -> Self {
for pos in positions {
let _ = self.waypoints.push(*pos);
}
self
}
pub fn velocity_percent(mut self, percent: u8) -> Self {
self.velocity_percent = percent.clamp(1, 200);
self
}
pub fn dwell(mut self, dwell_ms: u32) -> Self {
self.dwell_ms = dwell_ms;
self
}
pub fn build(self) -> Result<WaypointTrajectory> {
let motor = self.motor.ok_or_else(|| {
Error::Trajectory(TrajectoryError::InvalidName(
String::try_from("motor not specified").unwrap(),
))
})?;
if self.waypoints.is_empty() {
return Err(Error::Trajectory(TrajectoryError::Empty));
}
Ok(WaypointTrajectory {
motor,
waypoints: self.waypoints,
velocity_percent: self.velocity_percent,
dwell_ms: self.dwell_ms,
})
}
}