use core::{
convert::identity,
ops::{Index, IndexMut},
slice::{Iter, IterMut},
};
use uom::si::{
angle::revolution,
angular_velocity::revolution_per_minute,
electric_current::milliampere,
electric_potential::{millivolt, volt},
f64::{
Angle, AngularVelocity, ElectricCurrent, Power, Ratio, ThermodynamicTemperature, Torque,
},
power::watt,
quantities::ElectricPotential,
ratio::percent,
thermodynamic_temperature::degree_celsius,
torque::newton_meter,
};
use crate::{
bindings,
error::{get_errno, Error},
rtos::DataSource,
};
pub struct Motor {
port: u8,
}
impl Motor {
pub unsafe fn new(port: u8, gearset: Gearset, reverse: bool) -> Result<Self, MotorError> {
let mut motor = Self { port };
motor.set_reversed(reverse)?;
motor.set_gearing(gearset)?;
match bindings::motor_set_encoder_units(
port,
bindings::motor_encoder_units_e_E_MOTOR_ENCODER_ROTATIONS,
) {
bindings::PROS_ERR_ => Err(MotorError::from_errno()),
_ => Ok(motor),
}
}
pub fn move_i8(&mut self, voltage: i8) -> Result<(), MotorError> {
match unsafe { bindings::motor_move(self.port, voltage as i32) } {
bindings::PROS_ERR_ => Err(MotorError::from_errno()),
_ => Ok(()),
}
}
pub fn move_absolute(
&mut self,
position: Angle,
velocity: AngularVelocity,
) -> Result<(), MotorError> {
match unsafe {
bindings::motor_move_absolute(
self.port,
position.get::<revolution>(),
velocity.get::<revolution_per_minute>() as i32,
)
} {
bindings::PROS_ERR_ => Err(MotorError::from_errno()),
_ => Ok(()),
}
}
pub fn move_relative(
&mut self,
position: Angle,
velocity: AngularVelocity,
) -> Result<(), MotorError> {
match unsafe {
bindings::motor_move_relative(
self.port,
position.get::<revolution>(),
velocity.get::<revolution_per_minute>() as i32,
)
} {
bindings::PROS_ERR_ => Err(MotorError::from_errno()),
_ => Ok(()),
}
}
pub fn move_velocity(&mut self, velocity: AngularVelocity) -> Result<(), MotorError> {
match unsafe {
bindings::motor_move_velocity(self.port, velocity.get::<revolution_per_minute>() as i32)
} {
bindings::PROS_ERR_ => Err(MotorError::from_errno()),
_ => Ok(()),
}
}
pub fn move_voltage(&mut self, voltage: ElectricPotential<f64>) -> Result<(), MotorError> {
match unsafe { bindings::motor_move_voltage(self.port, voltage.get::<millivolt>() as i32) }
{
bindings::PROS_ERR_ => Err(MotorError::from_errno()),
_ => Ok(()),
}
}
pub fn modify_profiled_velocity(
&mut self,
velocity: AngularVelocity,
) -> Result<(), MotorError> {
match unsafe {
bindings::motor_modify_profiled_velocity(
self.port,
velocity.get::<revolution_per_minute>() as i32,
)
} {
bindings::PROS_ERR_ => Err(MotorError::from_errno()),
_ => Ok(()),
}
}
pub fn get_target_position(&self) -> Result<Angle, MotorError> {
match unsafe { bindings::motor_get_target_position(self.port) } {
x if x == bindings::PROS_ERR_F_ => Err(MotorError::from_errno()),
x => Ok(Angle::new::<revolution>(x)),
}
}
pub fn get_target_velocity(&self) -> Result<AngularVelocity, MotorError> {
match unsafe { bindings::motor_get_target_velocity(self.port) } {
bindings::PROS_ERR_ => Err(MotorError::from_errno()),
x => Ok(AngularVelocity::new::<revolution_per_minute>(x as f64)),
}
}
pub fn get_actual_velocity(&self) -> Result<AngularVelocity, MotorError> {
match unsafe { bindings::motor_get_actual_velocity(self.port) } {
x if x == bindings::PROS_ERR_F_ => Err(MotorError::from_errno()),
x => Ok(AngularVelocity::new::<revolution_per_minute>(x)),
}
}
pub fn get_current_draw(&self) -> Result<ElectricCurrent, MotorError> {
match unsafe { bindings::motor_get_current_draw(self.port) } {
bindings::PROS_ERR_ => Err(MotorError::from_errno()),
x => Ok(ElectricCurrent::new::<milliampere>(x as f64)),
}
}
pub fn get_direction(&self) -> Result<Direction, MotorError> {
match unsafe { bindings::motor_get_direction(self.port) } {
bindings::PROS_ERR_ => Err(MotorError::from_errno()),
1 => Ok(Direction::Positive),
-1 => Ok(Direction::Negative),
x => panic!(
"bindings::motor_get_direction returned unexpected value: {}",
x
),
}
}
pub fn get_efficiency(&self) -> Result<Ratio, MotorError> {
match unsafe { bindings::motor_get_efficiency(self.port) } {
x if x == bindings::PROS_ERR_F_ => Err(MotorError::from_errno()),
x => Ok(Ratio::new::<percent>(x)),
}
}
pub fn get_position(&self) -> Result<Angle, MotorError> {
match unsafe { bindings::motor_get_position(self.port) } {
x if x == bindings::PROS_ERR_F_ => Err(MotorError::from_errno()),
x => Ok(Angle::new::<revolution>(x)),
}
}
pub fn get_power(&self) -> Result<Power, MotorError> {
match unsafe { bindings::motor_get_power(self.port) } {
x if x == bindings::PROS_ERR_F_ => Err(MotorError::from_errno()),
x => Ok(Power::new::<watt>(x)),
}
}
pub fn get_temperature(&self) -> Result<ThermodynamicTemperature, MotorError> {
match unsafe { bindings::motor_get_temperature(self.port) } {
x if x == bindings::PROS_ERR_F_ => Err(MotorError::from_errno()),
x => Ok(ThermodynamicTemperature::new::<degree_celsius>(x)),
}
}
pub fn get_torque(&self) -> Result<Torque, MotorError> {
match unsafe { bindings::motor_get_torque(self.port) } {
x if x == bindings::PROS_ERR_F_ => Err(MotorError::from_errno()),
x => Ok(Torque::new::<newton_meter>(x)),
}
}
pub fn get_voltage(&self) -> Result<ElectricPotential<f64>, MotorError> {
match unsafe { bindings::motor_get_voltage(self.port) } {
x if x == bindings::PROS_ERR_ => Err(MotorError::from_errno()),
x => Ok(ElectricPotential::new::<millivolt>(x as f64)),
}
}
pub fn is_over_current(&self) -> Result<bool, MotorError> {
match unsafe { bindings::motor_is_over_current(self.port) } {
bindings::PROS_ERR_ => Err(MotorError::from_errno()),
0 => Ok(false),
_ => Ok(true),
}
}
pub fn is_over_temp(&self) -> Result<bool, MotorError> {
match unsafe { bindings::motor_is_over_temp(self.port) } {
bindings::PROS_ERR_ => Err(MotorError::from_errno()),
0 => Ok(false),
_ => Ok(true),
}
}
pub fn get_brake_mode(&self) -> Result<BrakeMode, MotorError> {
match unsafe { bindings::motor_get_brake_mode(self.port) } {
bindings::motor_brake_mode_e_E_MOTOR_BRAKE_BRAKE => Ok(BrakeMode::Brake),
bindings::motor_brake_mode_e_E_MOTOR_BRAKE_COAST => Ok(BrakeMode::Coast),
bindings::motor_brake_mode_e_E_MOTOR_BRAKE_HOLD => Ok(BrakeMode::Hold),
bindings::motor_brake_mode_e_E_MOTOR_BRAKE_INVALID => Err(MotorError::from_errno()),
x => panic!(
"bindings::motor_get_brake_mode returned unexpected value: {}.",
x
),
}
}
pub fn get_current_limit(&self) -> Result<ElectricCurrent, MotorError> {
match unsafe { bindings::motor_get_current_limit(self.port) } {
bindings::PROS_ERR_ => Err(MotorError::from_errno()),
x => Ok(ElectricCurrent::new::<milliampere>(x as f64)),
}
}
pub fn get_gearing(&self) -> Result<Gearset, MotorError> {
match unsafe { bindings::motor_get_gearing(self.port) } {
bindings::motor_gearset_e_E_MOTOR_GEARSET_36 => Ok(Gearset::SixToOne),
bindings::motor_gearset_e_E_MOTOR_GEARSET_18 => Ok(Gearset::EighteenToOne),
bindings::motor_gearset_e_E_MOTOR_GEARSET_06 => Ok(Gearset::ThirtySixToOne),
bindings::motor_gearset_e_E_MOTOR_GEARSET_INVALID => Err(MotorError::from_errno()),
x => panic!(
"bindings::motor_get_gearing returned unexpected value: {}.",
x
),
}
}
pub fn get_voltage_limit(&self) -> Result<ElectricPotential<i32>, MotorError> {
match unsafe { bindings::motor_get_voltage_limit(self.port) } {
bindings::PROS_ERR_ => Err(MotorError::from_errno()),
x => Ok(ElectricPotential::new::<volt>(x)),
}
}
pub fn is_reversed(&self) -> Result<bool, MotorError> {
match unsafe { bindings::motor_is_reversed(self.port) } {
bindings::PROS_ERR_ => Err(MotorError::from_errno()),
0 => Ok(false),
_ => Ok(true),
}
}
pub fn set_brake_mode(&mut self, mode: BrakeMode) -> Result<(), MotorError> {
match unsafe { bindings::motor_set_brake_mode(self.port, mode.into()) } {
bindings::PROS_ERR_ => Err(MotorError::from_errno()),
_ => Ok(()),
}
}
pub fn set_current_limit(&mut self, limit: ElectricCurrent) -> Result<(), MotorError> {
match unsafe {
bindings::motor_set_current_limit(self.port, limit.get::<milliampere>() as i32)
} {
bindings::PROS_ERR_ => Err(MotorError::from_errno()),
_ => Ok(()),
}
}
pub fn set_gearing(&mut self, gearset: Gearset) -> Result<(), MotorError> {
match unsafe { bindings::motor_set_gearing(self.port, gearset.into()) } {
bindings::PROS_ERR_ => Err(MotorError::from_errno()),
_ => Ok(()),
}
}
pub fn set_reversed(&mut self, reverse: bool) -> Result<(), MotorError> {
match unsafe { bindings::motor_set_reversed(self.port, reverse) } {
bindings::PROS_ERR_ => Err(MotorError::from_errno()),
_ => Ok(()),
}
}
pub fn set_voltage_limit(&mut self, limit: ElectricPotential<i32>) -> Result<(), MotorError> {
match unsafe { bindings::motor_set_voltage_limit(self.port, limit.get::<volt>()) } {
bindings::PROS_ERR_ => Err(MotorError::from_errno()),
_ => Ok(()),
}
}
pub fn set_zero_position(&mut self, position: Angle) -> Result<(), MotorError> {
match unsafe { bindings::motor_set_zero_position(self.port, position.get::<revolution>()) }
{
bindings::PROS_ERR_ => Err(MotorError::from_errno()),
_ => Ok(()),
}
}
pub fn tare_position(&mut self) -> Result<(), MotorError> {
match unsafe { bindings::motor_tare_position(self.port) } {
bindings::PROS_ERR_ => Err(MotorError::from_errno()),
_ => Ok(()),
}
}
}
impl DataSource for Motor {
type Data = MotorData;
type Error = MotorError;
fn read(&self) -> Result<Self::Data, Self::Error> {
Ok(MotorData {
target_position: self.get_target_position()?,
target_velocity: self.get_target_velocity()?,
actual_velocity: self.get_actual_velocity()?,
current_draw: self.get_current_draw()?,
direction: self.get_direction()?,
efficiency: self.get_efficiency()?,
position: self.get_position()?,
power: self.get_power()?,
temperature: self.get_temperature()?,
torque: self.get_torque()?,
voltage: self.get_voltage()?,
over_current: self.is_over_current()?,
over_temp: self.is_over_temp()?,
brake_mode: self.get_brake_mode()?,
current_limit: self.get_current_limit()?,
voltage_limit: self.get_voltage_limit()?,
})
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct MotorData {
pub target_position: Angle,
pub target_velocity: AngularVelocity,
pub actual_velocity: AngularVelocity,
pub current_draw: ElectricCurrent,
pub direction: Direction,
pub efficiency: Ratio,
pub position: Angle,
pub power: Power,
pub temperature: ThermodynamicTemperature,
pub torque: Torque,
pub voltage: ElectricPotential<f64>,
pub over_current: bool,
pub over_temp: bool,
pub brake_mode: BrakeMode,
pub current_limit: ElectricCurrent,
pub voltage_limit: ElectricPotential<i32>,
}
pub struct MotorGroup<const N: usize> {
motors: [Motor; N],
}
impl<const N: usize> MotorGroup<N> {
pub fn new(motors: [Motor; N]) -> Self {
Self { motors }
}
pub fn move_i8(&mut self, voltage: i8) -> Result<(), MotorError> {
for motor in self.motors.iter_mut() {
motor.move_i8(voltage)?;
}
Ok(())
}
pub fn move_absolute(
&mut self,
position: Angle,
velocity: AngularVelocity,
) -> Result<(), MotorError> {
for motor in self.motors.iter_mut() {
motor.move_absolute(position, velocity)?;
}
Ok(())
}
pub fn move_relative(
&mut self,
position: Angle,
velocity: AngularVelocity,
) -> Result<(), MotorError> {
for motor in self.motors.iter_mut() {
motor.move_relative(position, velocity)?;
}
Ok(())
}
pub fn move_velocity(&mut self, velocity: AngularVelocity) -> Result<(), MotorError> {
for motor in self.motors.iter_mut() {
motor.move_velocity(velocity)?;
}
Ok(())
}
pub fn move_voltage(&mut self, voltage: ElectricPotential<f64>) -> Result<(), MotorError> {
for motor in self.motors.iter_mut() {
motor.move_voltage(voltage)?;
}
Ok(())
}
pub fn modify_profiled_velocity(
&mut self,
velocity: AngularVelocity,
) -> Result<(), MotorError> {
for motor in self.motors.iter_mut() {
motor.modify_profiled_velocity(velocity)?;
}
Ok(())
}
pub fn get_actual_velocity(&self) -> Result<[AngularVelocity; N], MotorError> {
self.motors.each_ref().try_map(Motor::get_actual_velocity)
}
pub fn get_average_actual_velocity(&self) -> Result<AngularVelocity, MotorError> {
let mut value = self.get_actual_velocity()?.into_iter().sum();
value *= (N as f64).recip();
Ok(value)
}
pub fn get_current_draw(&self) -> Result<[ElectricCurrent; N], MotorError> {
self.motors.each_ref().try_map(Motor::get_current_draw)
}
pub fn get_total_current_draw(&self) -> Result<ElectricCurrent, MotorError> {
Ok(self.get_current_draw()?.into_iter().sum())
}
pub fn get_efficiency(&self) -> Result<[Ratio; N], MotorError> {
self.motors.each_ref().try_map(Motor::get_efficiency)
}
pub fn get_average_efficiency(&self) -> Result<Ratio, MotorError> {
let mut value = self.get_efficiency()?.into_iter().sum();
value *= (N as f64).recip();
Ok(value)
}
pub fn get_position(&self) -> Result<[Angle; N], MotorError> {
self.motors.each_ref().try_map(Motor::get_position)
}
pub fn get_average_position(&self) -> Result<Angle, MotorError> {
let mut value = self.get_position()?.into_iter().sum();
value *= (N as f64).recip();
Ok(value)
}
pub fn get_power(&self) -> Result<[Power; N], MotorError> {
self.motors.each_ref().try_map(Motor::get_power)
}
pub fn get_total_power(&self) -> Result<Power, MotorError> {
Ok(self.get_power()?.into_iter().sum())
}
pub fn get_temperature(&self) -> Result<[ThermodynamicTemperature; N], MotorError> {
self.motors.each_ref().try_map(Motor::get_temperature)
}
pub fn get_torque(&self) -> Result<[Torque; N], MotorError> {
self.motors.each_ref().try_map(Motor::get_torque)
}
pub fn get_total_torque(&self) -> Result<Torque, MotorError> {
Ok(self.get_torque()?.into_iter().sum())
}
pub fn get_voltage(&self) -> Result<[ElectricPotential<f64>; N], MotorError> {
self.motors.each_ref().try_map(Motor::get_voltage)
}
pub fn get_average_voltage(&self) -> Result<ElectricPotential<f64>, MotorError> {
let mut value = self.get_voltage()?.into_iter().sum();
value *= (N as f64).recip();
Ok(value)
}
pub fn is_over_current(&self) -> Result<[bool; N], MotorError> {
self.motors.each_ref().try_map(Motor::is_over_current)
}
pub fn is_any_over_current(&self) -> Result<bool, MotorError> {
Ok(self.is_over_current()?.into_iter().any(identity))
}
pub fn is_over_temp(&self) -> Result<[bool; N], MotorError> {
self.motors.each_ref().try_map(Motor::is_over_temp)
}
pub fn is_any_over_temp(&self) -> Result<bool, MotorError> {
Ok(self.is_over_temp()?.into_iter().any(identity))
}
pub fn set_brake_mode(&mut self, mode: BrakeMode) -> Result<(), MotorError> {
for motor in self.motors.iter_mut() {
motor.set_brake_mode(mode)?;
}
Ok(())
}
pub fn set_current_limit(&mut self, limit: ElectricCurrent) -> Result<(), MotorError> {
for motor in self.motors.iter_mut() {
motor.set_current_limit(limit)?;
}
Ok(())
}
pub fn set_voltage_limit(&mut self, limit: ElectricPotential<i32>) -> Result<(), MotorError> {
for motor in self.motors.iter_mut() {
motor.set_voltage_limit(limit)?;
}
Ok(())
}
pub fn tare_position(&mut self) -> Result<(), MotorError> {
for motor in self.motors.iter_mut() {
motor.tare_position()?;
}
Ok(())
}
pub fn iter(&self) -> Iter<'_, Motor> {
self.motors.iter()
}
pub fn iter_mut(&mut self) -> IterMut<'_, Motor> {
self.motors.iter_mut()
}
}
impl<Idx, const N: usize> Index<Idx> for MotorGroup<N>
where
[Motor; N]: Index<Idx>,
{
type Output = <[Motor; N] as Index<Idx>>::Output;
fn index(&self, index: Idx) -> &Self::Output {
&self.motors[index]
}
}
impl<Idx, const N: usize> IndexMut<Idx> for MotorGroup<N>
where
[Motor; N]: IndexMut<Idx>,
{
fn index_mut(&mut self, index: Idx) -> &mut Self::Output {
&mut self.motors[index]
}
}
impl<const N: usize> DataSource for MotorGroup<N> {
type Data = [MotorData; N];
type Error = MotorError;
fn read(&self) -> Result<Self::Data, Self::Error> {
self.motors.each_ref().try_map(DataSource::read)
}
}
#[derive(Debug)]
pub enum MotorError {
PortOutOfRange,
PortNotMotor,
Unknown(i32),
}
impl MotorError {
fn from_errno() -> Self {
match get_errno() {
libc::ENXIO => Self::PortOutOfRange,
libc::ENODEV => Self::PortNotMotor,
x => Self::Unknown(x),
}
}
}
impl From<MotorError> for Error {
fn from(err: MotorError) -> Self {
match err {
MotorError::PortOutOfRange => Error::Custom("port out of range".into()),
MotorError::PortNotMotor => Error::Custom("port not a motor".into()),
MotorError::Unknown(n) => Error::System(n),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BrakeMode {
Coast,
Brake,
Hold,
}
impl From<BrakeMode> for bindings::motor_brake_mode_e {
fn from(mode: BrakeMode) -> Self {
match mode {
BrakeMode::Coast => bindings::motor_brake_mode_e_E_MOTOR_BRAKE_COAST,
BrakeMode::Brake => bindings::motor_brake_mode_e_E_MOTOR_BRAKE_BRAKE,
BrakeMode::Hold => bindings::motor_brake_mode_e_E_MOTOR_BRAKE_HOLD,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Gearset {
SixToOne,
EighteenToOne,
ThirtySixToOne,
}
impl From<Gearset> for bindings::motor_gearset_e {
fn from(gearset: Gearset) -> Self {
match gearset {
Gearset::SixToOne => bindings::motor_gearset_e_E_MOTOR_GEARSET_06,
Gearset::EighteenToOne => bindings::motor_gearset_e_E_MOTOR_GEARSET_18,
Gearset::ThirtySixToOne => bindings::motor_gearset_e_E_MOTOR_GEARSET_36,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Direction {
Positive,
Negative,
}