use std::sync::{Arc, Mutex, PoisonError};
use std::time::Duration;
use crate::error::Result;
use crate::protocol::{
Frame, ReplyKind, frame_brake, frame_current, frame_feedback, frame_position, frame_velocity,
frames, parse_feedback,
};
use crate::transport::{SerialTransport, Transport};
use crate::types::{Feedback, Mode};
use super::Bus;
use super::pacing::{Port, mode_all, peek_min_gap, stop_all, with_gap};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
#[non_exhaustive]
pub enum PositionMirror {
#[default]
PassThrough,
Reflect,
}
pub struct M0601<T: Transport = SerialTransport> {
pub(super) port: Arc<Mutex<Port<T>>>,
pub(super) id: u8,
pub(super) timeout: Duration,
pub(super) mirrored: bool,
pub(super) position_mirror: PositionMirror,
pub(super) strict_crc: bool,
pub(super) default_accel: u8,
}
impl<T: Transport> Clone for M0601<T> {
fn clone(&self) -> Self {
Self {
port: Arc::clone(&self.port),
id: self.id,
timeout: self.timeout,
mirrored: self.mirrored,
position_mirror: self.position_mirror,
strict_crc: self.strict_crc,
default_accel: self.default_accel,
}
}
}
impl<T: Transport> std::fmt::Debug for M0601<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("M0601")
.field("id", &self.id)
.field("timeout", &self.timeout)
.field("mirrored", &self.mirrored)
.field("position_mirror", &self.position_mirror)
.field("strict_crc", &self.strict_crc)
.field("default_accel", &self.default_accel)
.field("min_gap", &peek_min_gap(&self.port))
.finish_non_exhaustive()
}
}
impl M0601<SerialTransport> {
pub fn open(port: &str, id: u8, timeout: Duration) -> Result<Self> {
Bus::open(port, timeout)?.motor(id)
}
}
impl<T: Transport> M0601<T> {
pub fn with_transport(transport: T, id: u8, timeout: Duration) -> Result<Self> {
Bus::with_transport(transport, timeout).motor(id)
}
#[must_use]
pub fn mirrored(mut self, mirrored: bool) -> Self {
self.mirrored = mirrored;
self
}
pub fn is_mirrored(&self) -> bool {
self.mirrored
}
#[must_use]
pub fn position_mirror(mut self, mode: PositionMirror) -> Self {
self.position_mirror = mode;
self
}
pub fn position_mirror_mode(&self) -> PositionMirror {
self.position_mirror
}
#[must_use]
pub fn with_strict_crc(mut self, strict: bool) -> Self {
self.strict_crc = strict;
self
}
pub fn set_strict_crc(&mut self, strict: bool) {
self.strict_crc = strict;
}
pub fn strict_crc(&self) -> bool {
self.strict_crc
}
#[must_use]
pub fn with_default_accel(mut self, accel: u8) -> Self {
self.default_accel = accel;
self
}
pub fn set_default_accel(&mut self, accel: u8) {
self.default_accel = accel;
}
pub fn default_accel(&self) -> u8 {
self.default_accel
}
pub fn id(&self) -> u8 {
self.id
}
pub fn timeout(&self) -> Duration {
self.timeout
}
pub fn into_transport(self) -> Option<T> {
Arc::into_inner(self.port).map(|m| {
m.into_inner()
.unwrap_or_else(PoisonError::into_inner)
.transport
})
}
pub fn send_raw(&mut self, frame: &[u8], wait: Duration) -> Result<Vec<u8>> {
with_gap(&self.port, |t| t.send_recv(frame, wait))
}
fn parse_reply(&self, tx: &[u8], rx: &[u8]) -> Option<Feedback> {
let kind = ReplyKind::from_tx(tx)?;
let fb = frames(tx, rx)?.find_map(|frame| {
parse_feedback(frame, kind)
.filter(|fb| fb.id == self.id && (!self.strict_crc || fb.crc_ok))
})?;
Some(self.adjust(fb))
}
fn adjust(&self, mut fb: Feedback) -> Feedback {
if self.mirrored {
fb.speed_rpm = fb.speed_rpm.saturating_neg();
fb.current_a = 0.0 - fb.current_a;
}
if self.position_mirror == PositionMirror::Reflect {
fb.position_deg = (360.0 - fb.position_deg).rem_euclid(360.0);
}
fb
}
pub fn transact(&mut self, frame: &Frame, wait: Duration) -> Result<Option<Feedback>> {
let rx = with_gap(&self.port, |t| t.send_recv(frame, wait))?;
Ok(self.parse_reply(frame, &rx))
}
pub fn query(&mut self) -> Result<Option<Feedback>> {
self.query_with(self.timeout)
}
pub fn query_with(&mut self, wait: Duration) -> Result<Option<Feedback>> {
let frame = frame_feedback(self.id);
self.transact(&frame, wait)
}
pub fn set_mode(&mut self, mode: Mode) -> Result<()> {
mode_all(&self.port, &[self.id], mode)
}
pub fn drive_velocity(&mut self, rpm: i16) -> Result<()> {
self.drive_velocity_accel(rpm, self.default_accel)
}
pub fn drive_velocity_accel(&mut self, rpm: i16, accel: u8) -> Result<()> {
let rpm = if self.mirrored {
rpm.saturating_neg()
} else {
rpm
};
let frame = frame_velocity(self.id, rpm, accel);
with_gap(&self.port, |t| t.send(&frame))
}
pub fn drive_current(&mut self, value: i16) -> Result<()> {
let value = if self.mirrored {
value.saturating_neg()
} else {
value
};
let frame = frame_current(self.id, value);
with_gap(&self.port, |t| t.send(&frame))
}
pub fn drive_position(&mut self, raw: u16) -> Result<()> {
let frame = frame_position(self.id, raw);
with_gap(&self.port, |t| t.send(&frame))
}
pub fn brake(&mut self) -> Result<()> {
let frame = frame_brake(self.id);
with_gap(&self.port, |t| t.send(&frame))
}
pub fn safe_stop(&mut self) {
stop_all(&self.port, &[self.id]);
}
}