extern crate alloc;
use alloc::vec::Vec;
use vexide::{
math::Angle,
prelude::{Direction, Gearset},
smart::{
PortError,
motor::{BrakeMode, Motor, MotorControl, SetGearsetError},
},
};
#[derive(Debug)]
#[non_exhaustive]
pub struct MotorGroupError<E = PortError, T = ()> {
pub errors: Vec<E>,
pub result: Option<T>,
}
impl<E> MotorGroupError<E, ()> {
pub(crate) fn new(errors: Vec<E>) -> Self {
assert!(!errors.is_empty(), "Cannot create a MotorGroupError with no errors");
Self {
errors,
result: None,
}
}
}
impl<E, T> MotorGroupError<E, T> {
pub(crate) fn with_result(errors: Vec<E>, result: T) -> Self {
assert!(!errors.is_empty(), "Cannot create a MotorGroupError with no errors");
Self {
errors,
result: Some(result),
}
}
pub(crate) fn with_empty_result(errors: Vec<E>) -> Self {
assert!(!errors.is_empty(), "Cannot create a MotorGroupError with no errors");
Self {
errors,
result: None,
}
}
pub fn result(&self) -> &Option<T> { &self.result }
pub fn first(&self) -> &E { &self.errors[0] }
}
impl From<MotorGroupError<PortError>> for PortError {
fn from(error: MotorGroupError<PortError>) -> Self { error.errors.into_iter().next().unwrap() }
}
impl From<MotorGroupError<SetGearsetError>> for SetGearsetError {
fn from(error: MotorGroupError<SetGearsetError>) -> Self {
error.errors.into_iter().next().unwrap()
}
}
impl core::fmt::Display for MotorGroupError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "error(s) in MotorGroup: {:?}", self.errors)
}
}
impl core::error::Error for MotorGroupError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum WriteErrorStrategy {
#[default]
Ignore,
Stop,
}
#[derive(Debug)]
pub struct MotorGroup<M: AsRef<[Motor]> + AsMut<[Motor]> = Vec<Motor>> {
pub(crate) motors: M,
write_error_strategy: WriteErrorStrategy,
}
type GetterResult<T> = Result<T, MotorGroupError<PortError, T>>;
impl<M: AsRef<[Motor]> + AsMut<[Motor]>> MotorGroup<M> {
pub fn new(motors: M) -> Self {
assert!(
!motors.as_ref().is_empty(),
"Cannot create a motor group with no motors"
);
Self {
motors,
write_error_strategy: WriteErrorStrategy::default(),
}
}
pub fn write_error_strategy(&mut self, mode: WriteErrorStrategy) -> &mut Self {
self.write_error_strategy = mode;
self
}
pub fn set_target(&mut self, target: MotorControl) -> Result<(), MotorGroupError> {
let mut errors = Vec::new();
for motor in self.motors.as_mut() {
if let Err(error) = motor.set_target(target) {
errors.push(error);
if self.write_error_strategy == WriteErrorStrategy::Stop {
break;
}
}
}
if errors.is_empty() {
Ok(())
} else {
Err(MotorGroupError::new(errors))
}
}
pub fn brake(&mut self, mode: BrakeMode) -> Result<(), MotorGroupError> {
let mut errors = Vec::new();
for motor in self.motors.as_mut() {
if let Err(error) = motor.brake(mode) {
errors.push(error);
if self.write_error_strategy == WriteErrorStrategy::Stop {
break;
}
}
}
if errors.is_empty() {
Ok(())
} else {
Err(MotorGroupError::new(errors))
}
}
pub fn set_velocity(&mut self, rpm: i32) -> Result<(), MotorGroupError> {
let mut errors = Vec::new();
for motor in self.motors.as_mut() {
if let Err(error) = motor.set_velocity(rpm) {
errors.push(error);
if self.write_error_strategy == WriteErrorStrategy::Stop {
break;
}
}
}
if errors.is_empty() {
Ok(())
} else {
Err(MotorGroupError::new(errors))
}
}
pub fn set_voltage(&mut self, volts: f64) -> Result<(), MotorGroupError> {
let mut errors = Vec::new();
for motor in self.motors.as_mut() {
if let Err(error) = motor.set_voltage(volts) {
errors.push(error);
if self.write_error_strategy == WriteErrorStrategy::Stop {
break;
}
}
}
if errors.is_empty() {
Ok(())
} else {
Err(MotorGroupError::new(errors))
}
}
pub fn set_position_target(
&mut self,
position: Angle,
velocity: i32,
) -> Result<(), MotorGroupError> {
let mut errors = Vec::new();
for motor in self.motors.as_mut() {
if let Err(error) = motor.set_position_target(position, velocity) {
errors.push(error);
if self.write_error_strategy == WriteErrorStrategy::Stop {
break;
}
}
}
if errors.is_empty() {
Ok(())
} else {
Err(MotorGroupError::new(errors))
}
}
pub fn set_profiled_velocity(&mut self, velocity: i32) -> Result<(), MotorGroupError> {
let mut errors = Vec::new();
for motor in self.motors.as_mut() {
if let Err(error) = motor.set_profiled_velocity(velocity) {
errors.push(error);
if self.write_error_strategy == WriteErrorStrategy::Stop {
break;
}
}
}
if errors.is_empty() {
Ok(())
} else {
Err(MotorGroupError::new(errors))
}
}
pub fn set_gearset(
&mut self,
gearset: Gearset,
) -> Result<(), MotorGroupError<SetGearsetError>> {
let mut errors = Vec::new();
for motor in self.motors.as_mut() {
if let Err(error) = motor.set_gearset(gearset) {
errors.push(error);
if self.write_error_strategy == WriteErrorStrategy::Stop {
break;
}
}
}
if errors.is_empty() {
Ok(())
} else {
Err(MotorGroupError::new(errors))
}
}
pub fn has_exp(&self) -> bool { self.motors.as_ref().iter().any(|motor| motor.is_exp()) }
pub fn has_v5(&self) -> bool { self.motors.as_ref().iter().any(|motor| motor.is_v5()) }
pub fn max_voltage(&self) -> f64 {
self.motors
.as_ref()
.iter()
.map(|motor| motor.max_voltage())
.reduce(f64::max)
.unwrap()
}
pub fn velocity(&self) -> GetterResult<f64> {
let mut errors = Vec::new();
let mut sum = 0.0;
let mut count = 0;
for motor in self.motors.as_ref() {
match motor.velocity() {
Ok(velocity) => {
sum += velocity;
count += 1;
}
Err(error) => errors.push(error),
}
}
if errors.is_empty() {
Ok(sum / count as f64)
} else if count > 0 {
Err(MotorGroupError::with_result(errors, sum / count as f64))
} else {
Err(MotorGroupError::with_empty_result(errors))
}
}
pub fn power(&self) -> GetterResult<f64> {
let mut errors = Vec::new();
let mut sum = 0.0;
let mut count = 0;
for motor in self.motors.as_ref() {
match motor.power() {
Ok(power) => {
sum += power;
count += 1;
}
Err(error) => errors.push(error),
}
}
if errors.is_empty() {
Ok(sum / count as f64)
} else if count > 0 {
Err(MotorGroupError::with_result(errors, sum / count as f64))
} else {
Err(MotorGroupError::with_empty_result(errors))
}
}
pub fn torque(&self) -> GetterResult<f64> {
let mut errors = Vec::new();
let mut sum = 0.0;
let mut count = 0;
for motor in self.motors.as_ref() {
match motor.torque() {
Ok(torque) => {
sum += torque;
count += 1;
}
Err(error) => errors.push(error),
}
}
if errors.is_empty() {
Ok(sum / count as f64)
} else if count > 0 {
Err(MotorGroupError::with_result(errors, sum / count as f64))
} else {
Err(MotorGroupError::with_empty_result(errors))
}
}
pub fn voltage(&self) -> GetterResult<f64> {
let mut errors = Vec::new();
let mut sum = 0.0;
let mut count = 0;
for motor in self.motors.as_ref() {
match motor.voltage() {
Ok(voltage) => {
sum += voltage;
count += 1;
}
Err(error) => errors.push(error),
}
}
if errors.is_empty() {
Ok(sum / count as f64)
} else if count > 0 {
Err(MotorGroupError::with_result(errors, sum / count as f64))
} else {
Err(MotorGroupError::with_empty_result(errors))
}
}
pub fn position(&self) -> GetterResult<Angle> {
let mut errors = Vec::new();
let mut sum = Angle::ZERO;
let mut count = 0;
for motor in self.motors.as_ref() {
match motor.position() {
Ok(position) => {
sum += position;
count += 1;
}
Err(error) => errors.push(error),
}
}
if errors.is_empty() {
Ok(sum / count as f64)
} else if count > 0 {
Err(MotorGroupError::with_result(errors, sum / count as f64))
} else {
Err(MotorGroupError::with_empty_result(errors))
}
}
pub fn current(&self) -> GetterResult<f64> {
let mut errors = Vec::new();
let mut sum = 0.0;
let mut count = 0;
for motor in self.motors.as_ref() {
match motor.current() {
Ok(current) => {
sum += current;
count += 1;
}
Err(error) => errors.push(error),
}
}
if errors.is_empty() {
Ok(sum / count as f64)
} else if count > 0 {
Err(MotorGroupError::with_result(errors, sum / count as f64))
} else {
Err(MotorGroupError::with_empty_result(errors))
}
}
pub fn efficiency(&self) -> GetterResult<f64> {
let mut errors = Vec::new();
let mut sum = 0.0;
let mut count = 0;
for motor in self.motors.as_ref() {
match motor.efficiency() {
Ok(efficiency) => {
sum += efficiency;
count += 1;
}
Err(error) => errors.push(error),
}
}
if errors.is_empty() {
Ok(sum / count as f64)
} else if count > 0 {
Err(MotorGroupError::with_result(errors, sum / count as f64))
} else {
Err(MotorGroupError::with_empty_result(errors))
}
}
pub fn reset_position(&mut self) -> Result<(), MotorGroupError> {
let mut errors = Vec::new();
for motor in self.motors.as_mut() {
if let Err(error) = motor.reset_position() {
errors.push(error);
if self.write_error_strategy == WriteErrorStrategy::Stop {
break;
}
}
}
if errors.is_empty() {
Ok(())
} else {
Err(MotorGroupError::new(errors))
}
}
pub fn set_position(&mut self, position: Angle) -> Result<(), MotorGroupError> {
let mut errors = Vec::new();
for motor in self.motors.as_mut() {
if let Err(error) = motor.set_position(position) {
errors.push(error);
if self.write_error_strategy == WriteErrorStrategy::Stop {
break;
}
}
}
if errors.is_empty() {
Ok(())
} else {
Err(MotorGroupError::new(errors))
}
}
pub fn set_current_limit(&mut self, limit: f64) -> Result<(), MotorGroupError> {
let mut errors = Vec::new();
for motor in self.motors.as_mut() {
if let Err(error) = motor.set_current_limit(limit) {
errors.push(error);
if self.write_error_strategy == WriteErrorStrategy::Stop {
break;
}
}
}
if errors.is_empty() {
Ok(())
} else {
Err(MotorGroupError::new(errors))
}
}
pub fn set_voltage_limit(&mut self, limit: f64) -> Result<(), MotorGroupError> {
let mut errors = Vec::new();
for motor in self.motors.as_mut() {
if let Err(error) = motor.set_voltage_limit(limit) {
errors.push(error);
if self.write_error_strategy == WriteErrorStrategy::Stop {
break;
}
}
}
if errors.is_empty() {
Ok(())
} else {
Err(MotorGroupError::new(errors))
}
}
pub fn temperature(&self) -> GetterResult<f64> {
let mut errors = Vec::new();
let mut sum = 0.0;
let mut count = 0;
for motor in self.motors.as_ref() {
match motor.temperature() {
Ok(temperature) => {
sum += temperature;
count += 1;
}
Err(error) => errors.push(error),
}
}
if errors.is_empty() {
Ok(sum / count as f64)
} else if count > 0 {
Err(MotorGroupError::with_result(errors, sum / count as f64))
} else {
Err(MotorGroupError::with_empty_result(errors))
}
}
pub fn is_over_temperature(&self) -> Result<bool, MotorGroupError> {
let mut errors = Vec::new();
for motor in self.motors.as_ref() {
match motor.is_over_temperature() {
Ok(true) => return Ok(true),
Err(error) => errors.push(error),
_ => {}
}
}
if errors.is_empty() {
Ok(false)
} else {
Err(MotorGroupError::new(errors))
}
}
pub fn is_over_current(&self) -> Result<bool, MotorGroupError> {
let mut errors = Vec::new();
for motor in self.motors.as_ref() {
match motor.is_over_current() {
Ok(true) => return Ok(true),
Err(error) => errors.push(error),
_ => {}
}
}
if errors.is_empty() {
Ok(false)
} else {
Err(MotorGroupError::new(errors))
}
}
pub fn is_driver_fault(&self) -> Result<bool, MotorGroupError> {
let mut errors = Vec::new();
for motor in self.motors.as_ref() {
match motor.is_driver_fault() {
Ok(true) => return Ok(true),
Err(error) => errors.push(error),
_ => {}
}
}
if errors.is_empty() {
Ok(false)
} else {
Err(MotorGroupError::new(errors))
}
}
pub fn is_driver_over_current(&self) -> Result<bool, MotorGroupError> {
let mut errors = Vec::new();
for motor in self.motors.as_ref() {
match motor.is_driver_over_current() {
Ok(true) => return Ok(true),
Err(error) => errors.push(error),
_ => {}
}
}
if errors.is_empty() {
Ok(false)
} else {
Err(MotorGroupError::new(errors))
}
}
pub fn set_direction(&mut self, direction: Direction) -> Result<(), MotorGroupError> {
let mut errors = Vec::new();
for motor in self.motors.as_mut() {
if let Err(error) = motor.set_direction(direction) {
errors.push(error);
if self.write_error_strategy == WriteErrorStrategy::Stop {
break;
}
}
}
if errors.is_empty() {
Ok(())
} else {
Err(MotorGroupError::new(errors))
}
}
}
#[cfg(test)]
mod tests {
use super::{MotorGroupError, WriteErrorStrategy};
#[derive(Debug, PartialEq, Eq, Clone)]
struct FakeErr(&'static str);
#[test]
fn motor_group_error_new_panics_on_empty() {
let v: Vec<FakeErr> = vec![];
let res = std::panic::catch_unwind(|| MotorGroupError::new(v));
assert!(
res.is_err(),
"expected MotorGroupError::new to panic on empty errors vector"
);
}
#[test]
fn motor_group_error_with_result_and_first() {
let errors = vec![FakeErr("a"), FakeErr("b")];
let mg_err = MotorGroupError::with_result(errors.clone(), 42u32);
assert_eq!(mg_err.first(), &errors[0]);
assert_eq!(mg_err.result(), &Some(42u32));
assert_eq!(mg_err.errors.len(), 2);
}
#[test]
fn motor_group_error_with_empty_result() {
let errors = vec![FakeErr("x")];
let mg_err: MotorGroupError<FakeErr, ()> =
MotorGroupError::with_empty_result(errors.clone());
assert_eq!(mg_err.result(), &None);
assert_eq!(mg_err.errors.len(), 1);
assert_eq!(mg_err.first(), &errors[0]);
}
#[test]
fn write_error_strategy_default_is_ignore() {
assert_eq!(WriteErrorStrategy::default(), WriteErrorStrategy::Ignore);
}
}