Skip to main content

rrtk/
command.rs

1// SPDX-License-Identifier: BSD-3-Clause
2// Copyright 2024-2026 UxuginPython
3use super::*;
4//You can add pretty much whatever bounds here as long as they're implemented for Command and
5//AngularCommand. Most of the strange or redundant seeming ones here have to do with MotionProfile.
6///This trait allows one to write code generically over [`LinearCommand`] and [`AngularCommand`]. Each of
7///those has a corresponding method to each method of this trait without the `generic_` prefix.
8///This is necessary because many of the implementations should be const fn and can't be in a
9///trait. Calling the direct methods (without `generic_`) is preferred where possible.
10pub trait GenericCommand:
11    Copy
12    + Debug
13    + PartialEq
14    + From<Self::Position>
15    + From<Self::Velocity>
16    + From<Self::Acceleration>
17    + From<Self::CorrespondingState>
18    + Add<Output = Self>
19    + Sub<Output = Self>
20    + Mul<Dimensionless<f32>>
21    + Div<Dimensionless<f32>>
22    + Neg<Output = Self>
23    + AddAssign
24    + SubAssign
25    + MulAssign<Dimensionless<f32>>
26    + DivAssign<Dimensionless<f32>>
27    + Into<PositionDerivative>
28    + Into<f32>
29{
30    ///The type that position is stored as. Almost certainly a [`Quantity`] of some type.
31    type Position: Copy
32        + Debug
33        + Default
34        + fmt::Display
35        + PartialOrd
36        + Neg<Output = Self::Position>
37        + Add<Output = Self::Position>
38        + Sub<Output = Self::Position>
39        + Mul<Dimensionless<f32>, Output = Self::Position>
40        + Div<Dimensionless<f32>, Output = Self::Position>
41        + Div<Time, Output = Self::Velocity>
42        + Div<Self::Velocity, Output = Second<f32>>
43        + Div<SecondSquared<f32>, Output = Self::Acceleration>
44        + stulta::AbsoluteValue;
45    ///The type that velocity is stored as. Almost certainly a [`Quantity`] of some type.
46    type Velocity: Copy
47        + Debug
48        + Default
49        + fmt::Display
50        + PartialOrd
51        + Neg<Output = Self::Velocity>
52        + Add<Output = Self::Velocity>
53        + Sub<Output = Self::Velocity>
54        + Mul<Dimensionless<f32>, Output = Self::Velocity>
55        + Div<Dimensionless<f32>, Output = Self::Velocity>
56        + Mul<Time, Output = Self::Position>
57        + Div<Time, Output = Self::Acceleration>
58        + Div<Self::Acceleration, Output = Second<f32>>
59        + Mul<Second<f32>, Output = Self::Position>
60        + stulta::AbsoluteValue;
61    ///The type that acceleration is stored as. Almost certainly a [`Quantity`] of some type.
62    type Acceleration: Copy
63        + Debug
64        + Default
65        + fmt::Display
66        + PartialOrd
67        + Neg<Output = Self::Acceleration>
68        + Add<Output = Self::Acceleration>
69        + Sub<Output = Self::Acceleration>
70        + Mul<Dimensionless<f32>, Output = Self::Acceleration>
71        + Div<Dimensionless<f32>, Output = Self::Acceleration>
72        + Mul<Time, Output = Self::Velocity>
73        + Mul<Second<f32>, Output = Self::Velocity>
74        + Mul<SecondSquared<f32>, Output = Self::Position>
75        + stulta::AbsoluteValue;
76    ///The corresponding state type with the same types for position, velocity, and acceleration.
77    type CorrespondingState: GenericState<
78            Position = Self::Position,
79            Velocity = Self::Velocity,
80            Acceleration = Self::Acceleration,
81        >;
82    ///Constructor from a position derivative and value.
83    fn generic_new(position_derivative: PositionDerivative, value: f32) -> Self;
84    ///If the command requires a known constant position, get it; otherwise, return `None`. This
85    ///will only return `Some` with the `Position` variant.
86    fn generic_get_position(&self) -> Option<Self::Position>;
87    ///If the command requires a known constant velocity, get it; otherwise, return `None`. This
88    ///will return `Some` with either the `Position` or `Velocity` variant. More specifically, if
89    ///the command is the `Position` variant, this will always return `Some` with a value of zero.
90    ///This returns `None` with the `Acceleration` variant because either velocity is not constant
91    ///(most cases) or the constant velocity is not known (with a fixed acceleration of zero).
92    fn generic_get_velocity(&self) -> Option<Self::Velocity>;
93    ///Get the (constant) acceleration required by the command. Returns zero with the `Position` or
94    ///`Velocity` variant, and, of course, returns the specified acceleration with the
95    ///`Acceleration` variant.
96    fn generic_get_acceleration(&self) -> Self::Acceleration;
97}
98macro_rules! build_command_enum {
99    ($name: ident, $pos: ty, $vel: ty, $acc: ty, $corresponding_state: ty) => {
100        ///A command for a motor to perform: go to a position, run at a velocity, or accelerate at a rate.
101        #[derive(Clone, Copy, Debug, PartialEq)]
102        pub enum $name {
103            ///Where you want to be.
104            Position($pos),
105            ///How fast you want to be going.
106            Velocity($vel),
107            ///How fast you want how fast you're going to change.
108            Acceleration($acc),
109        }
110        impl $name {
111            ///Constructor from a position derivative and value.
112            pub const fn new(position_derivative: PositionDerivative, value: f32) -> Self {
113                match position_derivative {
114                    PositionDerivative::Position => Self::Position(<$pos>::new(value)),
115                    PositionDerivative::Velocity => Self::Velocity(<$vel>::new(value)),
116                    PositionDerivative::Acceleration => Self::Acceleration(<$acc>::new(value)),
117                }
118            }
119            ///Get the commanded constant position if there is one. If the position derivative is
120            ///velocity or acceleration, this will return `None` as there is not a constant position.
121            pub const fn get_position(&self) -> Option<$pos> {
122                if let Self::Position(pos) = self {
123                    Some(*pos)
124                } else {
125                    None
126                }
127            }
128            ///Get the commanded constant velocity if there is one. If the position derivative is
129            ///acceleration, this will return `None` as there is not a constant
130            ///velocity. If the position derivative is position, this will return 0 as
131            ///velocity should be zero with a constant position.
132            pub const fn get_velocity(&self) -> Option<$vel> {
133                match self {
134                    Self::Position(_) => Some(<$vel>::new(0.0)),
135                    Self::Velocity(vel) => Some(*vel),
136                    Self::Acceleration(_) => None,
137                }
138            }
139            ///Get the commanded constant acceleration. If the position derivative is not
140            ///acceleration, this will return 0 as acceleration should be zero with a constant velocity or
141            ///position.
142            pub const fn get_acceleration(&self) -> $acc {
143                if let Self::Acceleration(acc) = self {
144                    *acc
145                } else {
146                    <$acc>::new(0.0)
147                }
148            }
149        }
150        impl GenericCommand for $name {
151            type Position = $pos;
152            type Velocity = $vel;
153            type Acceleration = $acc;
154            type CorrespondingState = $corresponding_state;
155            #[inline]
156            fn generic_new(position_derivative: PositionDerivative, value: f32) -> Self {
157                Self::new(position_derivative, value)
158            }
159            #[inline]
160            fn generic_get_position(&self) -> Option<$pos> {
161                self.get_position()
162            }
163            #[inline]
164            fn generic_get_velocity(&self) -> Option<$vel> {
165                self.get_velocity()
166            }
167            #[inline]
168            fn generic_get_acceleration(&self) -> $acc {
169                self.get_acceleration()
170            }
171        }
172        impl From<$pos> for $name {
173            fn from(was: $pos) -> Self {
174                Self::Position(was)
175            }
176        }
177        impl From<$vel> for $name {
178            fn from(was: $vel) -> Self {
179                Self::Velocity(was)
180            }
181        }
182        impl From<$acc> for $name {
183            fn from(was: $acc) -> Self {
184                Self::Acceleration(was)
185            }
186        }
187        impl From<$corresponding_state> for $name {
188            fn from(state: $corresponding_state) -> Self {
189                if state.acceleration == <$acc>::new(0.0) {
190                    if state.velocity == <$vel>::new(0.0) {
191                        Self::Position(state.position)
192                    } else {
193                        Self::Velocity(state.velocity)
194                    }
195                } else {
196                    Self::Acceleration(state.acceleration)
197                }
198            }
199        }
200        impl From<$name> for f32 {
201            fn from(was: $name) -> f32 {
202                match was {
203                    $name::Position(pos) => pos.into_inner(),
204                    $name::Velocity(vel) => vel.into_inner(),
205                    $name::Acceleration(acc) => acc.into_inner(),
206                }
207            }
208        }
209        impl Add for $name {
210            type Output = Self;
211            fn add(self, rhs: Self) -> Self {
212                let self_pos_der = PositionDerivative::from(self);
213                assert_eq!(self_pos_der, PositionDerivative::from(rhs));
214                Self::new(self_pos_der, f32::from(self) + f32::from(rhs))
215            }
216        }
217        impl Sub for $name {
218            type Output = Self;
219            fn sub(self, rhs: Self) -> Self {
220                let self_pos_der = PositionDerivative::from(self);
221                assert_eq!(self_pos_der, PositionDerivative::from(rhs));
222                Self::new(self_pos_der, f32::from(self) - f32::from(rhs))
223            }
224        }
225        impl Mul<Dimensionless<f32>> for $name {
226            type Output = Self;
227            fn mul(self, rhs: Dimensionless<f32>) -> Self {
228                match self {
229                    Self::Position(pos) => Self::Position(pos * rhs),
230                    Self::Velocity(vel) => Self::Velocity(vel * rhs),
231                    Self::Acceleration(acc) => Self::Acceleration(acc * rhs),
232                }
233            }
234        }
235        impl Div<Dimensionless<f32>> for $name {
236            type Output = Self;
237            fn div(self, rhs: Dimensionless<f32>) -> Self {
238                match self {
239                    Self::Position(pos) => Self::Position(pos / rhs),
240                    Self::Velocity(vel) => Self::Velocity(vel / rhs),
241                    Self::Acceleration(vel) => Self::Acceleration(vel / rhs),
242                }
243            }
244        }
245        impl Neg for $name {
246            type Output = Self;
247            fn neg(self) -> Self {
248                match self {
249                    Self::Position(pos) => Self::Position(-pos),
250                    Self::Velocity(vel) => Self::Velocity(-vel),
251                    Self::Acceleration(acc) => Self::Acceleration(-acc),
252                }
253            }
254        }
255        impl AddAssign for $name {
256            fn add_assign(&mut self, rhs: Self) {
257                *self = *self + rhs;
258            }
259        }
260        impl SubAssign for $name {
261            fn sub_assign(&mut self, rhs: Self) {
262                *self = *self - rhs;
263            }
264        }
265        //You might be able to optimize this a bit more with an unsafe dereference of the field since it's
266        //always an f32 and the variant never changes.
267        impl MulAssign<Dimensionless<f32>> for $name {
268            fn mul_assign(&mut self, rhs: Dimensionless<f32>) {
269                match self {
270                    Self::Position(pos) => *pos *= rhs,
271                    Self::Velocity(vel) => *vel *= rhs,
272                    Self::Acceleration(acc) => *acc *= rhs,
273                }
274            }
275        }
276        impl DivAssign<Dimensionless<f32>> for $name {
277            fn div_assign(&mut self, rhs: Dimensionless<f32>) {
278                match self {
279                    Self::Position(pos) => *pos /= rhs,
280                    Self::Velocity(vel) => *vel /= rhs,
281                    Self::Acceleration(acc) => *acc /= rhs,
282                }
283            }
284        }
285        impl From<$name> for PositionDerivative {
286            fn from(was: $name) -> Self {
287                match was {
288                    $name::Position(_) => Self::Position,
289                    $name::Velocity(_) => Self::Velocity,
290                    $name::Acceleration(_) => Self::Acceleration,
291                }
292            }
293        }
294    };
295}
296build_command_enum!(
297    LinearCommand,
298    Millimeter<f32>,
299    MillimeterPerSecond<f32>,
300    MillimeterPerSecondSquared<f32>,
301    LinearState
302);
303build_command_enum!(
304    AngularCommand,
305    Dimensionless<f32>,
306    InverseSecond<f32>,
307    InverseSecondSquared<f32>,
308    AngularState
309);
310impl Mul<Millimeter<f32>> for AngularCommand {
311    type Output = LinearCommand;
312    fn mul(self, rhs: Millimeter<f32>) -> LinearCommand {
313        match self {
314            Self::Position(pos) => LinearCommand::Position(pos * rhs),
315            Self::Velocity(vel) => LinearCommand::Velocity(vel * rhs),
316            Self::Acceleration(acc) => LinearCommand::Acceleration(acc * rhs),
317        }
318    }
319}
320impl Div<Millimeter<f32>> for LinearCommand {
321    type Output = AngularCommand;
322    fn div(self, rhs: Millimeter<f32>) -> AngularCommand {
323        match self {
324            Self::Position(pos) => AngularCommand::Position(pos / rhs),
325            Self::Velocity(vel) => AngularCommand::Velocity(vel / rhs),
326            Self::Acceleration(acc) => AngularCommand::Acceleration(acc / rhs),
327        }
328    }
329}