vexide_motorgroup/lib.rs
1//! # vexide-motorgroup
2//!
3//! Missing `MotorGroup` from VEXCode or PROS? This is a simple implementation of a
4//! `MotorGroup` for vexide which allows you to group motors together and control
5//! them as one.
6//!
7//! ## Installation
8//!
9//! Add the following to your `Cargo.toml`:
10//!
11//! ```toml
12//! [dependencies]
13//! # ... other dependencies
14//! vexide-motorgroup = "2.1.0"
15//! ```
16//!
17//! Or if you prefer the command line:
18//!
19//! ```sh
20//! cargo add vexide-motorgroup
21//! ```
22//!
23//! ## Usage
24//!
25//! Normally, you would have to set each motor's target and other values
26//! individually even if the motors were physically connected in a drivetrain or
27//! similar, but with `MotorGroup`, you can control them as if they were one motor.
28//!
29//! Just create a `MotorGroup` with a `Vec` of `Motor`s and use the `MotorGroup`
30//! methods just like you would with a `Motor`. It's that simple!
31//!
32//! ```rust
33//! #![no_std]
34//! #![no_main]
35//!
36//! extern crate alloc;
37//!
38//! use core::time::Duration;
39//!
40//! use alloc::vec;
41//! use vexide_motorgroup::*;
42//!
43//! use vexide::prelude::*;
44//!
45//! #[vexide::main]
46//! async fn main(peripherals: Peripherals) {
47//! // Here's where the magic happens
48//! let mut motor_group = MotorGroup::new(vec![
49//! Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
50//! Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
51//! ]);
52//!
53//! // Set the motor group's target to a voltage as if it were a motor
54//! motor_group.set_voltage(5.0).unwrap();
55//! sleep(Duration::from_secs(1)).await;
56//!
57//! // Set the motor group's target to a position
58//! motor_group
59//! .set_position_target(Position::from_degrees(90.0), 200)
60//! .unwrap();
61//! sleep(Duration::from_secs(1)).await;
62//!
63//! // Set the motor group's target to a velocity
64//! motor_group.set_velocity(100).unwrap();
65//! sleep(Duration::from_secs(1)).await;
66//!
67//! // Brake the motor group
68//! motor_group.brake(BrakeMode::Hold).unwrap();
69//! }
70//! ```
71//!
72//! ## Error handling
73//!
74//! ### Read errors
75//!
76//! For functions returning values and reading data (i.e., those taking a
77//! read-only reference to self), upon encountering an error accessing any
78//! motor, the result will be a MotorGroupError that contains all the errors
79//! encountered during the operation. Using [`MotorGroupError::result`] will
80//! return the average of all the results that were successfully read.
81//!
82//! ### Write errors
83//!
84//! vexide-motorgroup provides two different strategies for handling write
85//! errors. Both of them will return an `Err` when any motor returns an error.
86//!
87//! 1. [`WriteErrorStrategy::Ignore`] (default): This strategy will ignore
88//! errors and continue writing to the other motors.
89//! 2. [`WriteErrorStrategy::Stop`]: This strategy will stop writing to the
90//! other motors and return the error immediately.
91
92#![no_std]
93
94extern crate alloc;
95
96mod macros;
97mod shared_motors;
98
99pub use shared_motors::SharedMotors;
100
101use alloc::vec::Vec;
102use vexide::{
103 devices::smart::{motor::MotorError, Motor},
104 prelude::{BrakeMode, Direction, Gearset, MotorControl, Position},
105};
106
107/// An error that occurs when controlling a motor group.
108///
109/// This error is returned when an individual motor in the group encounters an
110/// error. The error contains a list of all the errors that occurred.
111///
112/// A MotorGroupError is guaranteed to have at least one error in it.
113///
114/// MotorGroupError also implements `Into<MotorError>`, which will return the
115/// first error that occurred. This means that you can use the `?` operator
116/// with a `MotorGroupError` to return a `MotorError` to a result.
117#[derive(Debug)]
118#[non_exhaustive]
119pub struct MotorGroupError<T = ()> {
120 pub errors: Vec<MotorError>,
121 pub result: Option<T>,
122}
123
124impl MotorGroupError<()> {
125 /// Creates a new motor group error from a `Vec` of motor errors.
126 ///
127 /// # Panics
128 ///
129 /// Panics if the errors vector is empty.
130 pub(crate) fn new(errors: Vec<MotorError>) -> Self {
131 assert!(
132 !errors.is_empty(),
133 "Cannot create a MotorGroupError with no errors"
134 );
135 Self {
136 errors,
137 result: None,
138 }
139 }
140}
141
142impl<T> MotorGroupError<T> {
143 pub(crate) fn with_result(errors: Vec<MotorError>, result: T) -> Self {
144 assert!(
145 !errors.is_empty(),
146 "Cannot create a MotorGroupError with no errors"
147 );
148 Self {
149 errors,
150 result: Some(result),
151 }
152 }
153
154 pub(crate) fn with_empty_result(errors: Vec<MotorError>) -> Self {
155 assert!(
156 !errors.is_empty(),
157 "Cannot create a MotorGroupError with no errors"
158 );
159 Self {
160 errors,
161 result: None,
162 }
163 }
164
165 /// Returns the result of the motor group error.
166 ///
167 /// For getters that return a result, this is the value that would be returned
168 /// if there were no errors. It is usually an average of the available data.
169 /// If all motors in the group return an error, this will be None.
170 pub fn result(&self) -> &Option<T> {
171 &self.result
172 }
173
174 /// The first error that occurred in the motor group.
175 pub fn first(&self) -> &MotorError {
176 &self.errors[0]
177 }
178
179 /// Whether the motor group has a busy error.
180 ///
181 /// A busy error occurs when communication with a motor is not possible
182 /// when reading flags.
183 pub fn has_busy_error(&self) -> bool {
184 self.errors
185 .iter()
186 .any(|error| matches!(error, MotorError::Busy))
187 }
188
189 /// Whether the motor group has a port error.
190 ///
191 /// A port error occurs when a motor is not currently connected to a Smart
192 /// Port.
193 pub fn has_port_error(&self) -> bool {
194 self.errors
195 .iter()
196 .any(|error| matches!(error, MotorError::Port { source: _ }))
197 }
198}
199
200impl From<MotorGroupError> for MotorError {
201 fn from(error: MotorGroupError) -> Self {
202 error.errors.into_iter().next().unwrap()
203 }
204}
205
206impl core::fmt::Display for MotorGroupError {
207 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
208 write!(f, "error(s) in MotorGroup: {:?}", self.errors)
209 }
210}
211
212impl core::error::Error for MotorGroupError {}
213
214/// The mode for handling errors when writing to a motor group.
215///
216/// This is used to determine how to handle errors when writing to a motor group.
217/// "Writing" means doing things like setting a target, setting a voltage,
218/// setting a gearset, etc.
219#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
220pub enum WriteErrorStrategy {
221 /// Ignore errors and continue writing.
222 ///
223 /// This means that if a motor is, for example, unplugged, then writes to
224 /// other plugged in motors will still be attempted. You should use this
225 /// mode for most places where redundancy is practiced. Note that methods will
226 /// still return an `Err` variant when an error occurs even if some writes
227 /// succeed.
228 ///
229 /// This is the default mode.
230 #[default]
231 Ignore,
232 /// Stop writing on the first error and return early.
233 ///
234 /// This means that if a motor encounters an error, no further writes will
235 /// be attempted, and the error will be returned immediately. This is useful
236 /// for debugging or when you want to ensure that all motors are in a valid
237 /// state at all times (e.g. a subsystem should either 100% work or not work
238 /// at all.)
239 Stop,
240}
241
242/// A group of motors that can be controlled together.
243///
244/// This is a simple wrapper around a vector of motors, with methods to easily
245/// control all motors in the group at once as if they were a single motor.
246///
247/// A motor group is guaranteed to have at least one motor in it.
248#[derive(Debug)]
249pub struct MotorGroup<M: AsRef<[Motor]> + AsMut<[Motor]> = Vec<Motor>> {
250 pub(crate) motors: M,
251 write_error_strategy: WriteErrorStrategy,
252}
253
254type GetterResult<T> = Result<T, MotorGroupError<T>>;
255
256impl<M: AsRef<[Motor]> + AsMut<[Motor]>> MotorGroup<M> {
257 /// Creates a new motor group from a vector of motors.
258 ///
259 /// You can set the write handling mode afterwards by calling
260 /// [`write_error_handling_mode`].
261 ///
262 /// # Examples
263 ///
264 /// ```
265 /// use vexide::prelude::*;
266 /// use vexide_motorgroup::*;
267 ///
268 /// #[vexide::main]
269 /// async fn main(peripherals: Peripherals) {
270 /// let motor_group = MotorGroup::new(vec![
271 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
272 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
273 /// ]);
274 /// _ = motor_group.set_voltage(5.0);
275 /// }
276 /// ```
277 ///
278 /// # Panics
279 ///
280 /// Panics if there are no motors in the vector.
281 pub fn new(motors: M) -> Self {
282 assert!(
283 !motors.as_ref().is_empty(),
284 "Cannot create a motor group with no motors"
285 );
286 Self {
287 motors,
288 write_error_strategy: WriteErrorStrategy::default(),
289 }
290 }
291
292 /// Sets the write error handling strategy for the motor group.
293 ///
294 /// This determines how to handle errors when writing to the motor group
295 /// using methods like [`set_target`], [`set_voltage`], etc.
296 ///
297 /// # Examples
298 ///
299 /// ```
300 /// use vexide::prelude::*;
301 /// use vexide_motorgroup::*;
302 ///
303 pub fn write_error_strategy(&mut self, mode: WriteErrorStrategy) -> &mut Self {
304 self.write_error_strategy = mode;
305 self
306 }
307
308 /// Sets the target that the motor group should attempt to reach.
309 ///
310 /// This could be a voltage, velocity, position, or even brake mode.
311 ///
312 /// # Errors
313 ///
314 /// - A [`MotorError::Port`] error is returned if a motor device is not currently connected to the Smart Port.
315 ///
316 /// # Examples
317 ///
318 /// ```
319 /// use vexide::prelude::*;
320 /// use vexide_motorgroup::*;
321 ///
322 /// #[vexide::main]
323 /// async fn main(peripherals: Peripherals) {
324 /// let mut motor_group = MotorGroup::new(vec![
325 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
326 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
327 /// ]);
328 /// let _ = motor_group.set_target(MotorControl::Voltage(5.0));
329 /// sleep(Duration::from_secs(1)).await;
330 /// let _ = motor_group.set_target(MotorControl::Brake(BrakeMode::Hold));
331 /// }
332 /// ```
333 ///
334 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.set_target).
335 pub fn set_target(&mut self, target: MotorControl) -> Result<(), MotorGroupError> {
336 let mut errors = Vec::new();
337 for motor in self.motors.as_mut() {
338 if let Err(error) = motor.set_target(target) {
339 errors.push(error);
340 if self.write_error_strategy == WriteErrorStrategy::Stop {
341 break;
342 }
343 }
344 }
345 if errors.is_empty() {
346 Ok(())
347 } else {
348 Err(MotorGroupError::new(errors))
349 }
350 }
351
352 /// Sets the motor group's target to a given [`BrakeMode`].
353 ///
354 /// # Errors
355 ///
356 /// - A [`MotorError::Port`] error is returned if a motor device is not currently connected to the Smart Port.
357 ///
358 /// # Examples
359 ///
360 /// ```
361 /// use vexide::prelude::*;
362 /// use vexide_motorgroup::*;
363 ///
364 /// #[vexide::main]
365 /// async fn main(peripherals: Peripherals) {
366 /// let mut motor_group = MotorGroup::new(vec![
367 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
368 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
369 /// ]);
370 /// let _ = motor_group.brake(BrakeMode::Hold);
371 /// }
372 /// ```
373 ///
374 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.brake).
375 pub fn brake(&mut self, mode: BrakeMode) -> Result<(), MotorGroupError> {
376 let mut errors = Vec::new();
377 for motor in self.motors.as_mut() {
378 if let Err(error) = motor.brake(mode) {
379 errors.push(error);
380 if self.write_error_strategy == WriteErrorStrategy::Stop {
381 break;
382 }
383 }
384 }
385 if errors.is_empty() {
386 Ok(())
387 } else {
388 Err(MotorGroupError::new(errors))
389 }
390 }
391
392 /// Spins the motor group at a target velocity.
393 ///
394 /// This velocity corresponds to different actual speeds in RPM depending on the gearset used for the motor.
395 /// Velocity is held with an internal PID controller to ensure consistent speed, as opposed to setting the
396 /// motor's voltage.
397 ///
398 /// # Errors
399 ///
400 /// - A [`MotorError::Port`] error is returned if a motor device is not currently connected to the Smart Port.
401 ///
402 /// # Examples
403 ///
404 /// Spin a motor group at 100 RPM:
405 ///
406 /// ```
407 /// use vexide::prelude::*;
408 /// use vexide_motorgroup::*;
409 ///
410 /// #[vexide::main]
411 /// async fn main(peripherals: Peripherals) {
412 /// let mut motor_group = MotorGroup::new(vec![
413 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
414 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
415 /// ]);
416 /// let _ = motor_group.set_velocity(100);
417 /// sleep(Duration::from_secs(1)).await;
418 /// }
419 /// ```
420 ///
421 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.set_velocity).
422 pub fn set_velocity(&mut self, rpm: i32) -> Result<(), MotorGroupError> {
423 let mut errors = Vec::new();
424 for motor in self.motors.as_mut() {
425 if let Err(error) = motor.set_velocity(rpm) {
426 errors.push(error);
427 if self.write_error_strategy == WriteErrorStrategy::Stop {
428 break;
429 }
430 }
431 }
432 if errors.is_empty() {
433 Ok(())
434 } else {
435 Err(MotorGroupError::new(errors))
436 }
437 }
438
439 /// Sets the motor group's output voltage.
440 ///
441 /// This voltage value spans from -12 (fully spinning reverse) to +12 (fully spinning forwards) volts, and
442 /// controls the raw output of the motor.
443 ///
444 /// # Errors
445 ///
446 /// - A [`MotorError::Port`] error is returned if a motor device is not currently connected to the Smart Port.
447 ///
448 /// # Examples
449 ///
450 /// Give the motor group full power:
451 ///
452 /// ```
453 /// use vexide::prelude::*;
454 /// use vexide_motorgroup::*;
455 ///
456 /// #[vexide::main]
457 /// async fn main(peripherals: Peripherals) {
458 /// let mut motor_group = MotorGroup::new(vec![
459 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
460 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
461 /// ]);
462 /// let _ = motor_group.set_voltage(motor_group.max_voltage());
463 /// }
464 /// ```
465 ///
466 /// Drive the motor group based on a controller joystick:
467 ///
468 /// ```
469 /// use vexide::prelude::*;
470 /// use vexide_motorgroup::*;
471 ///
472 /// #[vexide::main]
473 /// async fn main(peripherals: Peripherals) {
474 /// let mut motor_group = MotorGroup::new(vec![
475 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
476 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
477 /// ]);
478 /// let controller = peripherals.primary_controller;
479 /// loop {
480 /// let controller_state = controller.state().unwrap_or_default();
481 /// let voltage = controller_state.left_stick.x() * motor_group.max_voltage();
482 /// motor_group.set_voltage(voltage).unwrap();
483 /// }
484 /// }
485 /// ```
486 ///
487 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.set_voltage).
488 pub fn set_voltage(&mut self, volts: f64) -> Result<(), MotorGroupError> {
489 let mut errors = Vec::new();
490 for motor in self.motors.as_mut() {
491 if let Err(error) = motor.set_voltage(volts) {
492 errors.push(error);
493 if self.write_error_strategy == WriteErrorStrategy::Stop {
494 break;
495 }
496 }
497 }
498 if errors.is_empty() {
499 Ok(())
500 } else {
501 Err(MotorGroupError::new(errors))
502 }
503 }
504
505 /// Sets an absolute position target for the motor group to attempt to reach.
506 ///
507 /// # Errors
508 ///
509 /// - A [`MotorError::Port`] error is returned if a motor device is not currently connected to the Smart Port.
510 ///
511 /// # Examples
512 ///
513 /// ```
514 /// use vexide::prelude::*;
515 /// use vexide_motorgroup::*;
516 ///
517 /// #[vexide::main]
518 ///
519 /// async fn main(peripherals: Peripherals) {
520 /// let mut motor_group = MotorGroup::new(vec![
521 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
522 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
523 /// ]);
524 /// let _ = motor_group.set_position_target(Position::from_degrees(90.0), 200);
525 /// }
526 /// ```
527 ///
528 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.set_position_target).
529 pub fn set_position_target(
530 &mut self,
531 position: Position,
532 velocity: i32,
533 ) -> Result<(), MotorGroupError> {
534 let mut errors = Vec::new();
535 for motor in self.motors.as_mut() {
536 if let Err(error) = motor.set_position_target(position, velocity) {
537 errors.push(error);
538 if self.write_error_strategy == WriteErrorStrategy::Stop {
539 break;
540 }
541 }
542 }
543 if errors.is_empty() {
544 Ok(())
545 } else {
546 Err(MotorGroupError::new(errors))
547 }
548 }
549
550 /// Changes the output velocity for a profiled movement (motor_move_absolute or motor_move_relative).
551 ///
552 /// This will have no effect if the motor group is not following a profiled movement.
553 ///
554 /// # Errors
555 ///
556 /// - A [`MotorError::Port`] error is returned if a motor device is not currently connected to the Smart Port.
557 ///
558 /// # Examples
559 ///
560 /// ```
561 /// use vexide::prelude::*;
562 /// use vexide_motorgroup::*;
563 ///
564 /// #[vexide::main]
565 /// async fn main(peripherals: Peripherals) {
566 /// let mut motor_group = MotorGroup::new(vec![
567 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
568 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
569 /// ]);
570 /// // Set the motor group's target to a Position so that changing the velocity isn't a noop.
571 /// let _ = motor_group.set_target(MotorControl::Position(Position::from_degrees(90.0), 200));
572 /// let _ = motor_group.set_profiled_velocity(100).unwrap();
573 /// }
574 /// ```
575 ///
576 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.set_profiled_velocity).
577 pub fn set_profiled_velocity(&mut self, velocity: i32) -> Result<(), MotorGroupError> {
578 let mut errors = Vec::new();
579 for motor in self.motors.as_mut() {
580 if let Err(error) = motor.set_profiled_velocity(velocity) {
581 errors.push(error);
582 if self.write_error_strategy == WriteErrorStrategy::Stop {
583 break;
584 }
585 }
586 }
587 if errors.is_empty() {
588 Ok(())
589 } else {
590 Err(MotorGroupError::new(errors))
591 }
592 }
593
594 /// Sets the gearset of an 11W motor group.
595 ///
596 /// # Errors
597 ///
598 /// - A [`MotorError::Port`] error is returned if a motor device is not currently connected to the Smart Port.
599 /// - A [`MotorError::SetGearsetExp`] is returned if the motor is a 5.5W EXP Smart Motor, which has no swappable gearset.
600 ///
601 /// # Examples
602 ///
603 /// ```
604 /// use vexide::prelude::*;
605 /// use vexide_motorgroup::*;
606 ///
607 /// #[vexide::main]
608 /// async fn main(peripherals: Peripherals) {
609 /// // This must be a V5 motor group
610 /// let mut motor_group = MotorGroup::new(vec![
611 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
612 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
613 /// ]);
614 ///
615 /// // Set the motor group to use the red gearset
616 /// motor_group.set_gearset(Gearset::Red).unwrap();
617 /// }
618 /// ```
619 ///
620 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.set_gearset).
621 pub fn set_gearset(&mut self, gearset: Gearset) -> Result<(), MotorGroupError> {
622 let mut errors = Vec::new();
623 for motor in self.motors.as_mut() {
624 if let Err(error) = motor.set_gearset(gearset) {
625 errors.push(error);
626 if self.write_error_strategy == WriteErrorStrategy::Stop {
627 break;
628 }
629 }
630 }
631 if errors.is_empty() {
632 Ok(())
633 } else {
634 Err(MotorGroupError::new(errors))
635 }
636 }
637
638 /// Returns `true` if the motor group has a 5.5W (EXP) Smart Motor.
639 ///
640 /// # Examples
641 ///
642 /// ```
643 /// use vexide::prelude::*;
644 /// use vexide_motorgroup::*;
645 ///
646 /// #[vexide::main]
647 /// async fn main(peripherals: Peripherals) {
648 /// let motor_group = MotorGroup::new(vec![
649 /// Motor::new_exp(peripherals.port_1, Direction::Forward),
650 /// Motor::new_exp(peripherals.port_2, Direction::Forward),
651 /// ]);
652 /// if motor_group.has_exp() {
653 /// println!("Motor group has a 5.5W EXP Smart Motor");
654 /// }
655 /// }
656 /// ```
657 ///
658 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.is_exp).
659 pub fn has_exp(&self) -> bool {
660 self.motors.as_ref().iter().any(|motor| motor.is_exp())
661 }
662
663 /// Returns `true` if the motor group has an 11W (V5) Smart Motor.
664 ///
665 /// # Examples
666 ///
667 /// ```
668 /// use vexide::prelude::*;
669 /// use vexide_motorgroup::*;
670 ///
671 /// #[vexide::main]
672 /// async fn main(peripherals: Peripherals) {
673 /// let motor_group = MotorGroup::new(vec![
674 /// Motor::new(peripherals.port_1, Gearset::Red, Direction::Forward),
675 /// Motor::new(peripherals.port_2, Gearset::Red, Direction::Forward),
676 /// ]);
677 /// if motor_group.has_v5() {
678 /// println!("Motor group has an 11W V5 Smart Motor");
679 /// }
680 /// }
681 /// ```
682 ///
683 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.is_v5).
684 pub fn has_v5(&self) -> bool {
685 self.motors.as_ref().iter().any(|motor| motor.is_v5())
686 }
687
688 /// Returns the maximum voltage for the motor group based off of its [motor type](Motor::motor_type).
689 ///
690 /// # Examples
691 ///
692 /// Run a motor group at max speed, agnostic of its type:
693 /// ```
694 /// use vexide::prelude::*;
695 /// use vexide_motorgroup::*;
696 ///
697 /// fn run_motor_group_at_max_speed(motor_group: &mut MotorGroup) {
698 /// motor_group.set_voltage(motor_group.max_voltage()).unwrap();
699 /// }
700 /// ```
701 ///
702 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.max_voltage).
703 pub fn max_voltage(&self) -> f64 {
704 self.motors
705 .as_ref()
706 .iter()
707 .map(|motor| motor.max_voltage())
708 .reduce(f64::max)
709 .unwrap()
710 }
711
712 /// Returns the average estimated angular velocity of motors in a motor group in rotations per minute (RPM).
713 ///
714 /// # Accuracy
715 ///
716 /// In some cases, this reported value may be noisy or innaccurate, especially for systems where accurate
717 /// velocity control at high speeds is required (such as flywheels). If the accuracy of this value proves
718 /// inadequate, you may opt to perform your own velocity calculations by differentiating [`Motor::position`]
719 /// over the reported internal timestamp of the motor using [`Motor::timestamp`].
720 ///
721 /// > For more information about Smart motor velocity estimation, see [this article](https://sylvie.fyi/sylib/docs/db/d8e/md_module_writeups__velocity__estimation.html).
722 ///
723 /// # Note
724 ///
725 /// To get the current **target** velocity instead of the estimated velocity, use [`Motor::target`].
726 ///
727 /// # Errors
728 ///
729 /// - A [`MotorGroupError`] error is returned if any motor in the group encounters an error.
730 ///
731 /// # Examples
732 ///
733 /// Get the current velocity of a motor group:
734 /// ```
735 /// use vexide::prelude::*;
736 /// use vexide_motorgroup::*;
737 ///
738 /// #[vexide::main]
739 /// async fn main(peripherals: Peripherals) {
740 /// let motor_group = MotorGroup::new(vec![
741 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
742 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
743 /// ]);
744 ///
745 /// println!("{:?}", motor_group.velocity().unwrap());
746 /// }
747 /// ```
748 ///
749 /// Calculate acceleration of a motor group:
750 /// ```
751 /// use vexide::prelude::*;
752 /// use vexide_motorgroup::*;
753 ///
754 /// #[vexide::main]
755 /// async fn main(peripherals: Peripherals) {
756 /// let motor_group = MotorGroup::new(vec![
757 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
758 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
759 /// ]);
760 ///
761 /// let mut last_velocity = motor_group.velocity().unwrap();
762 /// let mut start_time = Instant::now();
763 /// loop {
764 /// let velocity = motor_group.velocity().unwrap();
765 /// // Make sure we don't divide by zero
766 /// let elapsed = start_time.elapsed().as_secs_f64() + 0.001;
767 ///
768 /// // Calculate acceleration
769 /// let acceleration = (velocity - last_velocity) / elapsed;
770 /// println!("Velocity: {:.2} RPM, Acceleration: {:.2} RPM/s", velocity, acceleration);
771 ///
772 /// last_velocity = velocity;
773 /// start_time = Instant::now();
774 /// }
775 /// }
776 /// ```
777 ///
778 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.velocity).
779 pub fn velocity(&self) -> GetterResult<f64> {
780 let mut errors = Vec::new();
781 let mut sum = 0.0;
782 let mut count = 0;
783 for motor in self.motors.as_ref() {
784 match motor.velocity() {
785 Ok(velocity) => {
786 sum += velocity;
787 count += 1;
788 }
789 Err(error) => errors.push(error),
790 }
791 }
792 if errors.is_empty() {
793 Ok(sum / count as f64)
794 } else if count > 0 {
795 Err(MotorGroupError::with_result(errors, sum / count as f64))
796 } else {
797 Err(MotorGroupError::with_empty_result(errors))
798 }
799 }
800
801 /// Returns the average power drawn by a motor in this the motor group in Watts.
802 ///
803 /// # Errors
804 ///
805 /// - A [`MotorGroupError`] error is returned if any motor in the group encounters an error.
806 ///
807 /// # Examples
808 ///
809 /// Print the power drawn by a motor group:
810 /// ```
811 /// use vexide::prelude::*;
812 /// use vexide_motorgroup::*;
813 ///
814 /// #[vexide::main]
815 /// async fn main(peripherals: Peripherals) {
816 /// let motor_group = MotorGroup::new(vec![
817 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
818 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
819 /// ]);
820 ///
821 /// println!("{:?}", motor_group.power().unwrap());
822 /// }
823 /// ```
824 ///
825 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.power).
826 pub fn power(&self) -> GetterResult<f64> {
827 let mut errors = Vec::new();
828 let mut sum = 0.0;
829 let mut count = 0;
830 for motor in self.motors.as_ref() {
831 match motor.power() {
832 Ok(power) => {
833 sum += power;
834 count += 1;
835 }
836 Err(error) => errors.push(error),
837 }
838 }
839 if errors.is_empty() {
840 Ok(sum / count as f64)
841 } else if count > 0 {
842 Err(MotorGroupError::with_result(errors, sum / count as f64))
843 } else {
844 Err(MotorGroupError::with_empty_result(errors))
845 }
846 }
847
848 /// Returns the average torque of motors in the motor group in Newton-meters.
849 ///
850 /// # Errors
851 ///
852 /// - A [`MotorGroupError`] error is returned if any motor in the group encounters an error.
853 ///
854 /// # Examples
855 ///
856 /// Print the torque of a motor group:
857 /// ```
858 /// use vexide::prelude::*;
859 /// use vexide_motorgroup::*;
860 ///
861 /// #[vexide::main]
862 /// async fn main(peripherals: Peripherals) {
863 /// let motor_group = MotorGroup::new(vec![
864 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
865 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
866 /// ]);
867 ///
868 /// println!("{:?}", motor_group.torque().unwrap());
869 /// }
870 /// ```
871 ///
872 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.torque).
873 pub fn torque(&self) -> GetterResult<f64> {
874 let mut errors = Vec::new();
875 let mut sum = 0.0;
876 let mut count = 0;
877 for motor in self.motors.as_ref() {
878 match motor.torque() {
879 Ok(torque) => {
880 sum += torque;
881 count += 1;
882 }
883 Err(error) => errors.push(error),
884 }
885 }
886 if errors.is_empty() {
887 Ok(sum / count as f64)
888 } else if count > 0 {
889 Err(MotorGroupError::with_result(errors, sum / count as f64))
890 } else {
891 Err(MotorGroupError::with_empty_result(errors))
892 }
893 }
894
895 /// Returns the motor group's output voltage.
896 ///
897 /// # Errors
898 ///
899 /// - A [`MotorGroupError`] error is returned if any motor in the group encounters an error.
900 ///
901 /// # Examples
902 ///
903 /// Print the voltage of a motor group:
904 /// ```
905 /// use vexide::prelude::*;
906 /// use vexide_motorgroup::*;
907 ///
908 /// #[vexide::main]
909 /// async fn main(peripherals: Peripherals) {
910 /// let motor_group = MotorGroup::new(vec![
911 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
912 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
913 /// ]);
914 ///
915 /// println!("{:?}", motor_group.voltage().unwrap());
916 /// }
917 /// ```
918 ///
919 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.voltage).
920 pub fn voltage(&self) -> GetterResult<f64> {
921 let mut errors = Vec::new();
922 let mut sum = 0.0;
923 let mut count = 0;
924 for motor in self.motors.as_ref() {
925 match motor.voltage() {
926 Ok(voltage) => {
927 sum += voltage;
928 count += 1;
929 }
930 Err(error) => errors.push(error),
931 }
932 }
933 if errors.is_empty() {
934 Ok(sum / count as f64)
935 } else if count > 0 {
936 Err(MotorGroupError::with_result(errors, sum / count as f64))
937 } else {
938 Err(MotorGroupError::with_empty_result(errors))
939 }
940 }
941
942 /// Returns the motor group's average position in ticks.
943 ///
944 /// # Errors
945 ///
946 /// - A [`MotorGroupError`] error is returned if any motor in the group encounters an error.
947 ///
948 /// # Examples
949 ///
950 /// Print the position of a motor group:
951 /// ```
952 /// use vexide::prelude::*;
953 /// use vexide_motorgroup::*;
954 ///
955 /// #[vexide::main]
956 /// async fn main(peripherals: Peripherals) {
957 /// let motor_group = MotorGroup::new(vec![
958 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
959 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
960 /// ]);
961 ///
962 /// println!("{:?}", motor_group.position().unwrap());
963 /// }
964 /// ```
965 ///
966 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.position).
967 pub fn position(&self) -> GetterResult<Position> {
968 let mut errors = Vec::new();
969 let mut sum = Position::from_ticks(0, 36000);
970 let mut count = 0;
971 for motor in self.motors.as_ref() {
972 match motor.position() {
973 Ok(position) => {
974 sum += position;
975 count += 1;
976 }
977 Err(error) => errors.push(error),
978 }
979 }
980 if errors.is_empty() {
981 Ok(sum / count as i64)
982 } else if count > 0 {
983 Err(MotorGroupError::with_result(errors, sum / count as i64))
984 } else {
985 Err(MotorGroupError::with_empty_result(errors))
986 }
987 }
988
989 /// Returns the motor group's average current in Amperes.
990 ///
991 /// # Errors
992 ///
993 /// - A [`MotorGroupError`] error is returned if any motor in the group encounters an error.
994 ///
995 /// # Examples
996 ///
997 /// Print the current of a motor group:
998 /// ```
999 /// use vexide::prelude::*;
1000 /// use vexide_motorgroup::*;
1001 ///
1002 /// #[vexide::main]
1003 /// async fn main(peripherals: Peripherals) {
1004 /// let motor_group = MotorGroup::new(vec![
1005 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
1006 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
1007 /// ]);
1008 ///
1009 /// println!("{:?}", motor_group.current().unwrap());
1010 /// }
1011 /// ```
1012 ///
1013 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.current).
1014 pub fn current(&self) -> GetterResult<f64> {
1015 let mut errors = Vec::new();
1016 let mut sum = 0.0;
1017 let mut count = 0;
1018 for motor in self.motors.as_ref() {
1019 match motor.current() {
1020 Ok(current) => {
1021 sum += current;
1022 count += 1;
1023 }
1024 Err(error) => errors.push(error),
1025 }
1026 }
1027 if errors.is_empty() {
1028 Ok(sum / count as f64)
1029 } else if count > 0 {
1030 Err(MotorGroupError::with_result(errors, sum / count as f64))
1031 } else {
1032 Err(MotorGroupError::with_empty_result(errors))
1033 }
1034 }
1035
1036 /// Returns the motor group's average efficiency as a percentage.
1037 ///
1038 /// # Errors
1039 ///
1040 /// - A [`MotorGroupError`] error is returned if any motor in the group encounters an error.
1041 ///
1042 /// # Examples
1043 ///
1044 /// Print the efficiency of a motor group:
1045 /// ```
1046 /// use vexide::prelude::*;
1047 /// use vexide_motorgroup::*;
1048 ///
1049 /// #[vexide::main]
1050 /// async fn main(peripherals: Peripherals) {
1051 /// let motor_group = MotorGroup::new(vec![
1052 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
1053 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
1054 /// ]);
1055 ///
1056 /// println!("{:?}", motor_group.efficiency().unwrap());
1057 /// }
1058 /// ```
1059 ///
1060 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.efficiency).
1061 pub fn efficiency(&self) -> GetterResult<f64> {
1062 let mut errors = Vec::new();
1063 let mut sum = 0.0;
1064 let mut count = 0;
1065 for motor in self.motors.as_ref() {
1066 match motor.efficiency() {
1067 Ok(efficiency) => {
1068 sum += efficiency;
1069 count += 1;
1070 }
1071 Err(error) => errors.push(error),
1072 }
1073 }
1074 if errors.is_empty() {
1075 Ok(sum / count as f64)
1076 } else if count > 0 {
1077 Err(MotorGroupError::with_result(errors, sum / count as f64))
1078 } else {
1079 Err(MotorGroupError::with_empty_result(errors))
1080 }
1081 }
1082
1083 /// Resets every motor in the motor group's position to zero.
1084 ///
1085 /// # Errors
1086 ///
1087 /// - A [`MotorError::Port`] error is returned if a motor device is not currently connected to the Smart Port.
1088 ///
1089 /// # Examples
1090 ///
1091 /// Reset the position of a motor group:
1092 /// ```
1093 /// use vexide::prelude::*;
1094 /// use vexide_motorgroup::*;
1095 ///
1096 /// #[vexide::main]
1097 /// async fn main(peripherals: Peripherals) {
1098 /// let mut motor_group = MotorGroup::new(vec![
1099 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
1100 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
1101 /// ]);
1102 ///
1103 /// motor_group.reset_position().unwrap();
1104 /// }
1105 /// ```
1106 ///
1107 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.reset_position).
1108 pub fn reset_position(&mut self) -> Result<(), MotorGroupError> {
1109 let mut errors = Vec::new();
1110 for motor in self.motors.as_mut() {
1111 if let Err(error) = motor.reset_position() {
1112 errors.push(error);
1113 if self.write_error_strategy == WriteErrorStrategy::Stop {
1114 break;
1115 }
1116 }
1117 }
1118 if errors.is_empty() {
1119 Ok(())
1120 } else {
1121 Err(MotorGroupError::new(errors))
1122 }
1123 }
1124
1125 /// Sets the motor group's position to a given value.
1126 ///
1127 /// # Errors
1128 ///
1129 /// - A [`MotorError::Port`] error is returned if a motor device is not currently connected to the Smart Port.
1130 ///
1131 /// # Examples
1132 ///
1133 /// Set the position of a motor group:
1134 /// ```
1135 /// use vexide::prelude::*;
1136 /// use vexide_motorgroup::*;
1137 ///
1138 /// #[vexide::main]
1139 /// async fn main(peripherals: Peripherals) {
1140 /// let mut motor_group = MotorGroup::new(vec![
1141 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
1142 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
1143 /// ]);
1144 ///
1145 /// motor_group.set_position(Position::from_degrees(90.0)).unwrap();
1146 /// }
1147 /// ```
1148 ///
1149 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.set_position).
1150 pub fn set_position(&mut self, position: Position) -> Result<(), MotorGroupError> {
1151 let mut errors = Vec::new();
1152 for motor in self.motors.as_mut() {
1153 if let Err(error) = motor.set_position(position) {
1154 errors.push(error);
1155 if self.write_error_strategy == WriteErrorStrategy::Stop {
1156 break;
1157 }
1158 }
1159 }
1160 if errors.is_empty() {
1161 Ok(())
1162 } else {
1163 Err(MotorGroupError::new(errors))
1164 }
1165 }
1166
1167 /// Sets the motor group's current limit in Amperes.
1168 ///
1169 /// # Errors
1170 ///
1171 /// - A [`MotorError::Port`] error is returned if a motor device is not currently connected to the Smart Port.
1172 ///
1173 /// # Examples
1174 ///
1175 /// Set the current limit of a motor group:
1176 /// ```
1177 /// use vexide::prelude::*;
1178 /// use vexide_motorgroup::*;
1179 ///
1180 /// #[vexide::main]
1181 /// async fn main(peripherals: Peripherals) {
1182 /// let mut motor_group = MotorGroup::new(vec![
1183 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
1184 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
1185 /// ]);
1186 ///
1187 /// motor_group.set_current_limit(2.5).unwrap();
1188 /// }
1189 /// ```
1190 ///
1191 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.set_current_limit).
1192 pub fn set_current_limit(&mut self, limit: f64) -> Result<(), MotorGroupError> {
1193 let mut errors = Vec::new();
1194 for motor in self.motors.as_mut() {
1195 if let Err(error) = motor.set_current_limit(limit) {
1196 errors.push(error);
1197 if self.write_error_strategy == WriteErrorStrategy::Stop {
1198 break;
1199 }
1200 }
1201 }
1202 if errors.is_empty() {
1203 Ok(())
1204 } else {
1205 Err(MotorGroupError::new(errors))
1206 }
1207 }
1208
1209 /// Sets the motor group's voltage limit in Volts.
1210 ///
1211 /// # Errors
1212 ///
1213 /// - A [`MotorError::Port`] error is returned if a motor device is not currently connected to the Smart Port.
1214 ///
1215 /// # Examples
1216 ///
1217 /// Set the voltage limit of a motor group:
1218 /// ```
1219 /// use vexide::prelude::*;
1220 /// use vexide_motorgroup::*;
1221 ///
1222 /// #[vexide::main]
1223 /// async fn main(peripherals: Peripherals) {
1224 /// let mut motor_group = MotorGroup::new(vec![
1225 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
1226 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
1227 /// ]);
1228 ///
1229 /// motor_group.set_voltage_limit(10.0).unwrap();
1230 /// }
1231 /// ```
1232 ///
1233 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.set_voltage_limit).
1234 pub fn set_voltage_limit(&mut self, limit: f64) -> Result<(), MotorGroupError> {
1235 let mut errors = Vec::new();
1236 for motor in self.motors.as_mut() {
1237 if let Err(error) = motor.set_voltage_limit(limit) {
1238 errors.push(error);
1239 if self.write_error_strategy == WriteErrorStrategy::Stop {
1240 break;
1241 }
1242 }
1243 }
1244 if errors.is_empty() {
1245 Ok(())
1246 } else {
1247 Err(MotorGroupError::new(errors))
1248 }
1249 }
1250
1251 /// Returns the motor group's temperature in degrees Celsius.
1252 ///
1253 /// # Errors
1254 ///
1255 /// - A [`MotorGroupError`] error is returned if any motor in the group encounters an error.
1256 ///
1257 /// # Examples
1258 ///
1259 /// Print the temperature of a motor group:
1260 /// ```
1261 /// use vexide::prelude::*;
1262 /// use vexide_motorgroup::*;
1263 ///
1264 /// #[vexide::main]
1265 /// async fn main(peripherals: Peripherals) {
1266 /// let motor_group = MotorGroup::new(vec![
1267 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
1268 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
1269 /// ]);
1270 ///
1271 /// println!("{:?}", motor_group.temperature().unwrap());
1272 /// }
1273 /// ```
1274 ///
1275 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.temperature).
1276 pub fn temperature(&self) -> GetterResult<f64> {
1277 let mut errors = Vec::new();
1278 let mut sum = 0.0;
1279 let mut count = 0;
1280 for motor in self.motors.as_ref() {
1281 match motor.temperature() {
1282 Ok(temperature) => {
1283 sum += temperature;
1284 count += 1;
1285 }
1286 Err(error) => errors.push(error),
1287 }
1288 }
1289 if errors.is_empty() {
1290 Ok(sum / count as f64)
1291 } else if count > 0 {
1292 Err(MotorGroupError::with_result(errors, sum / count as f64))
1293 } else {
1294 Err(MotorGroupError::with_empty_result(errors))
1295 }
1296 }
1297
1298 /// Returns `true` if any motor in the motor group is over temperature.
1299 ///
1300 /// # Errors
1301 ///
1302 /// - A [`MotorGroupError`] error is returned if any motor encounters an error.
1303 ///
1304 /// Note that this method will still return `Ok` if a motor encounters an
1305 /// error but a motor in a group is still over temperature.
1306 ///
1307 /// # Examples
1308 ///
1309 /// Check if a motor group is over temperature:
1310 /// ```
1311 /// use vexide::prelude::*;
1312 /// use vexide_motorgroup::*;
1313 ///
1314 /// #[vexide::main]
1315 /// async fn main(peripherals: Peripherals) {
1316 /// let motor_group = MotorGroup::new(vec![
1317 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
1318 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
1319 /// ]);
1320 ///
1321 /// println!("{:?}", motor_group.is_over_temperature().unwrap());
1322 /// }
1323 /// ```
1324 ///
1325 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.is_over_temperature).
1326 pub fn is_over_temperature(&self) -> Result<bool, MotorGroupError> {
1327 let mut errors = Vec::new();
1328 for motor in self.motors.as_ref() {
1329 match motor.is_over_temperature() {
1330 Ok(true) => return Ok(true),
1331 Err(error) => errors.push(error),
1332 _ => {}
1333 }
1334 }
1335 if errors.is_empty() {
1336 Ok(false)
1337 } else {
1338 Err(MotorGroupError::new(errors))
1339 }
1340 }
1341
1342 /// Returns `true` if any motor in the motor group is over current.
1343 ///
1344 /// # Errors
1345 ///
1346 /// - A [`MotorGroupError`] error is returned if any motor encounters an error.
1347 ///
1348 /// Note that this method will still return `Ok` if a motor encounters an
1349 /// error but a motor in a group is still over current.
1350 ///
1351 /// # Examples
1352 ///
1353 /// Check if a motor group is over current:
1354 /// ```
1355 /// use vexide::prelude::*;
1356 /// use vexide_motorgroup::*;
1357 ///
1358 /// #[vexide::main]
1359 /// async fn main(peripherals: Peripherals) {
1360 /// let motor_group = MotorGroup::new(vec![
1361 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
1362 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
1363 /// ]);
1364 ///
1365 /// println!("{:?}", motor_group.is_over_current().unwrap());
1366 /// }
1367 /// ```
1368 ///
1369 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.is_over_current).
1370 pub fn is_over_current(&self) -> Result<bool, MotorGroupError> {
1371 let mut errors = Vec::new();
1372 for motor in self.motors.as_ref() {
1373 match motor.is_over_current() {
1374 Ok(true) => return Ok(true),
1375 Err(error) => errors.push(error),
1376 _ => {}
1377 }
1378 }
1379 if errors.is_empty() {
1380 Ok(false)
1381 } else {
1382 Err(MotorGroupError::new(errors))
1383 }
1384 }
1385
1386 /// Returns `true` if any motor in the motor group has a driver fault.
1387 ///
1388 /// # Errors
1389 ///
1390 /// - A [`MotorGroupError`] error is returned if any motor encounters an error.
1391 ///
1392 /// Note that this method will still return `Ok` if a motor encounters an
1393 /// error but a motor in a group has a driver fault.
1394 ///
1395 /// # Examples
1396 ///
1397 /// Check if a motor group has a driver fault:
1398 /// ```
1399 /// use vexide::prelude::*;
1400 /// use vexide_motorgroup::*;
1401 ///
1402 /// #[vexide::main]
1403 /// async fn main(peripherals: Peripherals) {
1404 /// let motor_group = MotorGroup::new(vec![
1405 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
1406 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
1407 /// ]);
1408 ///
1409 /// println!("{:?}", motor_group.is_driver_fault().unwrap());
1410 /// }
1411 /// ```
1412 ///
1413 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.is_driver_fault).
1414 pub fn is_driver_fault(&self) -> Result<bool, MotorGroupError> {
1415 let mut errors = Vec::new();
1416 for motor in self.motors.as_ref() {
1417 match motor.is_driver_fault() {
1418 Ok(true) => return Ok(true),
1419 Err(error) => errors.push(error),
1420 _ => {}
1421 }
1422 }
1423 if errors.is_empty() {
1424 Ok(false)
1425 } else {
1426 Err(MotorGroupError::new(errors))
1427 }
1428 }
1429
1430 /// Returns `true` if the any motor in the motor group is over current.
1431 ///
1432 /// # Errors
1433 ///
1434 /// - A [`MotorGroupError`] error is returned if any motor encounters an error.
1435 ///
1436 /// Note that this method will still return `Ok` if a motor encounters an
1437 /// error but a motor in a group is still over current.
1438 ///
1439 /// # Examples
1440 ///
1441 /// Check if a motor group is over current:
1442 /// ```
1443 /// use vexide::prelude::*;
1444 /// use vexide_motorgroup::*;
1445 ///
1446 /// #[vexide::main]
1447 /// async fn main(peripherals: Peripherals) {
1448 /// let motor_group = MotorGroup::new(vec![
1449 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
1450 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
1451 /// ]);
1452 ///
1453 /// println!("{:?}", motor_group.is_driver_over_current().unwrap());
1454 /// }
1455 /// ```
1456 ///
1457 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.is_driver_over_current).
1458 pub fn is_driver_over_current(&self) -> Result<bool, MotorGroupError> {
1459 let mut errors = Vec::new();
1460 for motor in self.motors.as_ref() {
1461 match motor.is_driver_over_current() {
1462 Ok(true) => return Ok(true),
1463 Err(error) => errors.push(error),
1464 _ => {}
1465 }
1466 }
1467 if errors.is_empty() {
1468 Ok(false)
1469 } else {
1470 Err(MotorGroupError::new(errors))
1471 }
1472 }
1473
1474 /// Sets the motor group's direction.
1475 ///
1476 /// # Errors
1477 ///
1478 /// - A [`MotorError::Port`] error is returned if a motor device is not currently connected to the Smart Port.
1479 ///
1480 /// # Examples
1481 ///
1482 /// Set the direction of a motor group:
1483 /// ```
1484 /// use vexide::prelude::*;
1485 /// use vexide_motorgroup::*;
1486 ///
1487 /// #[vexide::main]
1488 /// async fn main(peripherals: Peripherals) {
1489 /// let mut motor_group = MotorGroup::new(vec![
1490 /// Motor::new(peripherals.port_1, Gearset::Green, Direction::Forward),
1491 /// Motor::new(peripherals.port_2, Gearset::Green, Direction::Forward),
1492 /// ]);
1493 ///
1494 /// motor_group.set_direction(Direction::Reverse).unwrap();
1495 /// }
1496 /// ```
1497 ///
1498 /// See the original method [here](https://docs.rs/vexide/latest/vexide/devices/smart/struct.Motor.html#method.set_direction).
1499 pub fn set_direction(&mut self, direction: Direction) -> Result<(), MotorGroupError> {
1500 let mut errors = Vec::new();
1501 for motor in self.motors.as_mut() {
1502 if let Err(error) = motor.set_direction(direction) {
1503 errors.push(error);
1504 if self.write_error_strategy == WriteErrorStrategy::Stop {
1505 break;
1506 }
1507 }
1508 }
1509 if errors.is_empty() {
1510 Ok(())
1511 } else {
1512 Err(MotorGroupError::new(errors))
1513 }
1514 }
1515}