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}