stepper_motion/config/
limits.rs1use serde::Deserialize;
4
5use super::units::Degrees;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Deserialize)]
9#[serde(rename_all = "snake_case")]
10pub enum LimitPolicy {
11 #[default]
13 Reject,
14 Clamp,
16}
17
18#[derive(Debug, Clone, Deserialize)]
20pub struct SoftLimits {
21 #[serde(rename = "min_degrees")]
23 pub min: Degrees,
24
25 #[serde(rename = "max_degrees")]
27 pub max: Degrees,
28
29 #[serde(default)]
31 pub policy: LimitPolicy,
32}
33
34impl SoftLimits {
35 pub fn new(min: Degrees, max: Degrees, policy: LimitPolicy) -> Self {
37 Self { min, max, policy }
38 }
39
40 pub fn is_valid(&self) -> bool {
42 self.min.0 < self.max.0
43 }
44
45 pub fn contains(&self, position: Degrees) -> bool {
47 position.0 >= self.min.0 && position.0 <= self.max.0
48 }
49
50 pub fn apply(&self, target: Degrees) -> Option<Degrees> {
54 if self.contains(target) {
55 Some(target)
56 } else {
57 match self.policy {
58 LimitPolicy::Reject => None,
59 LimitPolicy::Clamp => {
60 if target.0 < self.min.0 {
61 Some(self.min)
62 } else {
63 Some(self.max)
64 }
65 }
66 }
67 }
68 }
69}
70
71#[derive(Debug, Clone)]
73pub struct StepLimits {
74 pub min_steps: i64,
76 pub max_steps: i64,
78 pub policy: LimitPolicy,
80}
81
82impl StepLimits {
83 pub fn from_soft_limits(soft: &SoftLimits, steps_per_degree: f32) -> Self {
85 Self {
86 min_steps: (soft.min.0 * steps_per_degree) as i64,
87 max_steps: (soft.max.0 * steps_per_degree) as i64,
88 policy: soft.policy,
89 }
90 }
91
92 pub fn contains(&self, steps: i64) -> bool {
94 steps >= self.min_steps && steps <= self.max_steps
95 }
96
97 pub fn apply(&self, target: i64) -> Option<i64> {
101 if self.contains(target) {
102 Some(target)
103 } else {
104 match self.policy {
105 LimitPolicy::Reject => None,
106 LimitPolicy::Clamp => {
107 if target < self.min_steps {
108 Some(self.min_steps)
109 } else {
110 Some(self.max_steps)
111 }
112 }
113 }
114 }
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[test]
123 fn test_soft_limits_reject() {
124 let limits = SoftLimits::new(Degrees(-180.0), Degrees(180.0), LimitPolicy::Reject);
125
126 assert!(limits.apply(Degrees(0.0)).is_some());
127 assert!(limits.apply(Degrees(180.0)).is_some());
128 assert!(limits.apply(Degrees(-180.0)).is_some());
129 assert!(limits.apply(Degrees(181.0)).is_none());
130 assert!(limits.apply(Degrees(-181.0)).is_none());
131 }
132
133 #[test]
134 fn test_soft_limits_clamp() {
135 let limits = SoftLimits::new(Degrees(-180.0), Degrees(180.0), LimitPolicy::Clamp);
136
137 assert_eq!(limits.apply(Degrees(0.0)).unwrap().0, 0.0);
138 assert_eq!(limits.apply(Degrees(360.0)).unwrap().0, 180.0);
139 assert_eq!(limits.apply(Degrees(-360.0)).unwrap().0, -180.0);
140 }
141}