ev3_drivebase/drivebase/
utils.rs

1//! Utility functions for drivebase control and status checking.
2
3use super::DriveBase;
4use ev3dev_lang_rust::Ev3Error;
5
6impl DriveBase {
7    /// Stops both motors immediately.
8    ///
9    /// The motors will stop according to the configured brake mode:
10    /// - `Coast`: Motors freely coast to a stop
11    /// - `Brake`: Motors actively brake
12    /// - `Hold`: Motors hold their current position
13    ///
14    /// This method is automatically called when the `DriveBase` is dropped.
15    ///
16    /// # Errors
17    ///
18    /// Returns an error if the stop command cannot be sent to either motor.
19    ///
20    /// # Examples
21    ///
22    /// ```no_run
23    /// use ev3_drivebase::{DriveBase, Motor, Direction};
24    /// use ev3_drivebase::ev3dev_lang_rust::{Ev3Error, motors::MotorPort};
25    ///
26    /// fn main() -> Result<(), Ev3Error> {
27    ///     let left = Motor::new(MotorPort::OutA, Direction::Clockwise);
28    ///     let right = Motor::new(MotorPort::OutB, Direction::CounterClockwise);
29    ///     let mut drivebase = DriveBase::new(left, right, 43.2, 185.0)?;
30    ///
31    ///     // Start driving
32    ///     drivebase.drive(300, 1000, false)?;
33    ///
34    ///     // Stop immediately
35    ///     drivebase.stop()?;
36    ///
37    ///     Ok(())
38    /// }
39    /// ```
40    pub fn stop(&self) -> Result<&Self, Ev3Error> {
41        self.left.stop()?;
42        self.right.stop()?;
43        Ok(self)
44    }
45
46    /// Resets all motor parameters to their default values.
47    ///
48    /// This method:
49    /// - Stops both motors
50    /// - Resets position counters to zero
51    /// - Clears any pending commands
52    /// - Resets speed, acceleration, and other settings to defaults
53    ///
54    /// This is automatically called when creating a new `DriveBase`.
55    ///
56    /// # Errors
57    ///
58    /// Returns an error if the reset command fails on either motor.
59    ///
60    /// # Examples
61    ///
62    /// ```no_run
63    /// use ev3_drivebase::{DriveBase, Motor, Direction};
64    /// use ev3_drivebase::ev3dev_lang_rust::{Ev3Error, motors::MotorPort};
65    ///
66    /// fn main() -> Result<(), Ev3Error> {
67    ///     let left = Motor::new(MotorPort::OutA, Direction::Clockwise);
68    ///     let right = Motor::new(MotorPort::OutB, Direction::CounterClockwise);
69    ///     let drivebase = DriveBase::new(left, right, 43.2, 185.0)?;
70    ///
71    ///     // After some movements, reset everything
72    ///     drivebase.reset()?;
73    ///
74    ///     Ok(())
75    /// }
76    /// ```
77    pub fn reset(&self) -> Result<&Self, Ev3Error> {
78        self.left.reset()?;
79        self.right.reset()?;
80        Ok(self)
81    }
82
83    /// Checks if power is being sent to either motor.
84    ///
85    /// Returns `true` if at least one motor is currently running (receiving power),
86    /// even if the motor is ramping up or down.
87    ///
88    /// # Errors
89    ///
90    /// Returns an error if the motor state cannot be read.
91    ///
92    /// # Examples
93    ///
94    /// ```no_run
95    /// use ev3_drivebase::{DriveBase, Motor, Direction};
96    /// use ev3_drivebase::ev3dev_lang_rust::{Ev3Error, motors::MotorPort};
97    ///
98    /// fn main() -> Result<(), Ev3Error> {
99    ///     let left = Motor::new(MotorPort::OutA, Direction::Clockwise);
100    ///     let right = Motor::new(MotorPort::OutB, Direction::CounterClockwise);
101    ///     let mut drivebase = DriveBase::new(left, right, 43.2, 185.0)?;
102    ///
103    ///     drivebase.drive(200, 500, false)?;
104    ///
105    ///     if drivebase.is_running()? {
106    ///         println!("Motors are running!");
107    ///     }
108    ///
109    ///     Ok(())
110    /// }
111    /// ```
112    pub fn is_running(&self) -> Result<bool, Ev3Error> {
113        Ok(self.left.is_running()? || self.right.is_running()?)
114    }
115
116    /// Checks if either motor is currently ramping up to speed.
117    ///
118    /// Returns `true` during the acceleration phase after a motor command is issued,
119    /// before the motor reaches its target speed.
120    ///
121    /// # Errors
122    ///
123    /// Returns an error if the motor state cannot be read.
124    ///
125    /// # Examples
126    ///
127    /// ```no_run
128    /// use ev3_drivebase::{DriveBase, Motor, Direction};
129    /// use ev3_drivebase::ev3dev_lang_rust::{Ev3Error, motors::MotorPort};
130    ///
131    /// fn main() -> Result<(), Ev3Error> {
132    ///     let left = Motor::new(MotorPort::OutA, Direction::Clockwise);
133    ///     let right = Motor::new(MotorPort::OutB, Direction::CounterClockwise);
134    ///     let mut drivebase = DriveBase::new(left, right, 43.2, 185.0)?;
135    ///
136    ///     drivebase.drive(300, 1000, false)?;
137    ///
138    ///     if drivebase.is_ramping()? {
139    ///         println!("Motors are accelerating!");
140    ///     }
141    ///
142    ///     Ok(())
143    /// }
144    /// ```
145    pub fn is_ramping(&self) -> Result<bool, Ev3Error> {
146        Ok(self.left.is_ramping()? || self.right.is_ramping()?)
147    }
148
149    /// Checks if either motor is actively holding its position.
150    ///
151    /// Returns `true` when a motor has stopped and is in `BrakeMode::Hold`,
152    /// actively maintaining its position against external forces.
153    ///
154    /// # Errors
155    ///
156    /// Returns an error if the motor state cannot be read.
157    ///
158    /// # Examples
159    ///
160    /// ```no_run
161    /// use ev3_drivebase::{DriveBase, Motor, Direction, BrakeMode};
162    /// use ev3_drivebase::ev3dev_lang_rust::{Ev3Error, motors::MotorPort};
163    ///
164    /// fn main() -> Result<(), Ev3Error> {
165    ///     let left = Motor::new(MotorPort::OutA, Direction::Clockwise);
166    ///     let right = Motor::new(MotorPort::OutB, Direction::CounterClockwise);
167    ///     let mut drivebase = DriveBase::new(left, right, 43.2, 185.0)?;
168    ///     drivebase.set_brake_mode(BrakeMode::Hold)?;
169    ///
170    ///     drivebase.drive(200, 300, true)?;
171    ///
172    ///     if drivebase.is_holding()? {
173    ///         println!("Motors are holding position!");
174    ///     }
175    ///
176    ///     Ok(())
177    /// }
178    /// ```
179    pub fn is_holding(&self) -> Result<bool, Ev3Error> {
180        Ok(self.left.is_holding()? || self.right.is_holding()?)
181    }
182
183    /// Checks if either motor is overloaded.
184    ///
185    /// Returns `true` if a motor is drawing excessive current, which typically indicates:
186    /// - The robot is stuck against an obstacle
187    /// - The motor is trying to move a load that's too heavy
188    /// - Mechanical binding or damage
189    ///
190    /// # Errors
191    ///
192    /// Returns an error if the motor state cannot be read.
193    ///
194    /// # Examples
195    ///
196    /// ```no_run
197    /// use ev3_drivebase::{DriveBase, Motor, Direction};
198    /// use ev3_drivebase::ev3dev_lang_rust::{Ev3Error, motors::MotorPort};
199    ///
200    /// fn main() -> Result<(), Ev3Error> {
201    ///     let left = Motor::new(MotorPort::OutA, Direction::Clockwise);
202    ///     let right = Motor::new(MotorPort::OutB, Direction::CounterClockwise);
203    ///     let mut drivebase = DriveBase::new(left, right, 43.2, 185.0)?;
204    ///
205    ///     drivebase.drive(300, 1000, false)?;
206    ///
207    ///     if drivebase.is_overloaded()? {
208    ///         println!("Warning: Motors overloaded!");
209    ///         drivebase.stop()?;
210    ///     }
211    ///
212    ///     Ok(())
213    /// }
214    /// ```
215    pub fn is_overloaded(&self) -> Result<bool, Ev3Error> {
216        Ok(self.left.is_overloaded()? || self.right.is_overloaded()?)
217    }
218
219    /// Checks if either motor has stalled.
220    ///
221    /// Returns `true` if a motor is not moving despite receiving power, indicating:
222    /// - The robot is blocked by an obstacle
223    /// - The wheels are slipping
224    /// - Mechanical failure
225    ///
226    /// # Errors
227    ///
228    /// Returns an error if the motor state cannot be read.
229    ///
230    /// # Examples
231    ///
232    /// ```no_run
233    /// use ev3_drivebase::{DriveBase, Motor, Direction};
234    /// use ev3_drivebase::ev3dev_lang_rust::{Ev3Error, motors::MotorPort};
235    ///
236    /// fn main() -> Result<(), Ev3Error> {
237    ///     let left = Motor::new(MotorPort::OutA, Direction::Clockwise);
238    ///     let right = Motor::new(MotorPort::OutB, Direction::CounterClockwise);
239    ///     let mut drivebase = DriveBase::new(left, right, 43.2, 185.0)?;
240    ///
241    ///     drivebase.drive(300, 1000, false)?;
242    ///
243    ///     if drivebase.is_stalled()? {
244    ///         println!("Warning: Motors stalled!");
245    ///         drivebase.stop()?;
246    ///     }
247    ///
248    ///     Ok(())
249    /// }
250    /// ```
251    pub fn is_stalled(&self) -> Result<bool, Ev3Error> {
252        Ok(self.left.is_stalled()? || self.right.is_stalled()?)
253    }
254
255    /// Calculates encoder counts for a given distance in millimeters.
256    ///
257    /// This internal method converts distance in millimeters to motor encoder counts,
258    /// taking into account wheel circumference and motor direction.
259    ///
260    /// # Parameters
261    ///
262    /// - `left_distance`: Distance for the left wheel in millimeters
263    /// - `right_distance`: Distance for the right wheel in millimeters
264    ///
265    /// # Returns
266    ///
267    /// A tuple `(left_counts, right_counts)` representing the encoder counts for each motor,
268    /// with correct sign according to motor direction.
269    ///
270    /// # Errors
271    ///
272    /// Returns an error if encoder information cannot be read from the motors.
273    #[expect(clippy::cast_possible_truncation)]
274    pub fn calculate_counts(
275        &self,
276        left_distance: i32,
277        right_distance: i32,
278    ) -> Result<(i32, i32), Ev3Error> {
279        let left_distance = left_distance.abs();
280        let right_distance = right_distance.abs();
281
282        let mut left_counts = ((f64::from(left_distance) / self.circumference)
283            * f64::from(self.left.get_count_per_rot()?))
284        .round()
285        .clamp(f64::from(i32::MIN), f64::from(i32::MAX)) as i32;
286
287        let mut right_counts = ((f64::from(right_distance) / self.circumference)
288            * f64::from(self.right.get_count_per_rot()?))
289        .round()
290        .clamp(f64::from(i32::MIN), f64::from(i32::MAX)) as i32;
291
292        left_counts = left_counts.saturating_mul(self.left_meta.direction.sign());
293        right_counts = right_counts.saturating_mul(self.right_meta.direction.sign());
294
295        Ok((left_counts, right_counts))
296    }
297}
298
299impl Drop for DriveBase {
300    fn drop(&mut self) {
301        let _ = self.stop();
302        let _ = self.set_brake_mode(super::BrakeMode::Coast);
303    }
304}