use serde::Deserialize;
use super::units::Degrees;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum LimitPolicy {
#[default]
Reject,
Clamp,
}
#[derive(Debug, Clone, Deserialize)]
pub struct SoftLimits {
#[serde(rename = "min_degrees")]
pub min: Degrees,
#[serde(rename = "max_degrees")]
pub max: Degrees,
#[serde(default)]
pub policy: LimitPolicy,
}
impl SoftLimits {
pub fn new(min: Degrees, max: Degrees, policy: LimitPolicy) -> Self {
Self { min, max, policy }
}
pub fn is_valid(&self) -> bool {
self.min.0 < self.max.0
}
pub fn contains(&self, position: Degrees) -> bool {
position.0 >= self.min.0 && position.0 <= self.max.0
}
pub fn apply(&self, target: Degrees) -> Option<Degrees> {
if self.contains(target) {
Some(target)
} else {
match self.policy {
LimitPolicy::Reject => None,
LimitPolicy::Clamp => {
if target.0 < self.min.0 {
Some(self.min)
} else {
Some(self.max)
}
}
}
}
}
}
#[derive(Debug, Clone)]
pub struct StepLimits {
pub min_steps: i64,
pub max_steps: i64,
pub policy: LimitPolicy,
}
impl StepLimits {
pub fn from_soft_limits(soft: &SoftLimits, steps_per_degree: f32) -> Self {
Self {
min_steps: (soft.min.0 * steps_per_degree) as i64,
max_steps: (soft.max.0 * steps_per_degree) as i64,
policy: soft.policy,
}
}
pub fn contains(&self, steps: i64) -> bool {
steps >= self.min_steps && steps <= self.max_steps
}
pub fn apply(&self, target: i64) -> Option<i64> {
if self.contains(target) {
Some(target)
} else {
match self.policy {
LimitPolicy::Reject => None,
LimitPolicy::Clamp => {
if target < self.min_steps {
Some(self.min_steps)
} else {
Some(self.max_steps)
}
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_soft_limits_reject() {
let limits = SoftLimits::new(Degrees(-180.0), Degrees(180.0), LimitPolicy::Reject);
assert!(limits.apply(Degrees(0.0)).is_some());
assert!(limits.apply(Degrees(180.0)).is_some());
assert!(limits.apply(Degrees(-180.0)).is_some());
assert!(limits.apply(Degrees(181.0)).is_none());
assert!(limits.apply(Degrees(-181.0)).is_none());
}
#[test]
fn test_soft_limits_clamp() {
let limits = SoftLimits::new(Degrees(-180.0), Degrees(180.0), LimitPolicy::Clamp);
assert_eq!(limits.apply(Degrees(0.0)).unwrap().0, 0.0);
assert_eq!(limits.apply(Degrees(360.0)).unwrap().0, 180.0);
assert_eq!(limits.apply(Degrees(-360.0)).unwrap().0, -180.0);
}
}