#![warn(missing_docs)]
use easy_ext::ext;
use itertools::Itertools;
pub use pair_macro::Triplet;
pub use serialport;
use serialport::{DataBits, FlowControl, Parity, SerialPort, StopBits};
use std::borrow::Cow;
use std::fmt::{self, Display, Formatter};
use std::marker::PhantomData;
use std::str::FromStr;
use std::time::Duration;
pub struct Ready;
pub struct SensitivityNotSetYet;
pub struct DynpickSensorBuilder<C> {
port: Box<dyn SerialPort>,
sensitivity: Sensitivity,
_calibrated: PhantomData<fn() -> C>,
}
impl DynpickSensorBuilder<SensitivityNotSetYet> {
pub fn open<'a>(
path: impl Into<Cow<'a, str>>,
) -> Result<DynpickSensorBuilder<SensitivityNotSetYet>, Error> {
let port = serialport::new(path, 921600)
.data_bits(DataBits::Eight)
.flow_control(FlowControl::None)
.parity(Parity::None)
.stop_bits(StopBits::One)
.timeout(Duration::from_millis(1))
.open()
.map_err(Error::SerialPort)?;
let builder = Self {
port,
sensitivity: Sensitivity {
digital_per_newton: Triplet::default(),
digital_per_newtonmeter: Triplet::default(),
},
_calibrated: PhantomData,
};
Ok(builder)
}
pub fn set_sensitivity_manually(self, sensitivity: Sensitivity) -> DynpickSensorBuilder<Ready> {
DynpickSensorBuilder {
port: self.port,
sensitivity,
_calibrated: PhantomData,
}
}
pub fn set_sensitivity_by_builtin_data(mut self) -> Result<DynpickSensorBuilder<Ready>, Error> {
const SENSITIVITY_RESPONSE_LENGTH: usize = 46;
self.port.write_all(&['p' as u8]).map_err(Error::IO)?;
std::thread::sleep(self.port.timeout());
let mut res = [0; SENSITIVITY_RESPONSE_LENGTH];
self.port.read_exact(&mut res).map_err(Error::IO)?;
let res = std::str::from_utf8(&res).or(Err(Error::Utf8(res.to_vec())))?;
let (fx, fy, fz, mx, my, mz) = res
.split(',')
.map(f64::from_str)
.filter_map(Result::ok)
.next_tuple()
.ok_or(Error::ParseResponse(res.to_owned()))?;
let force = Triplet::new(fx, fy, fz);
let torque = Triplet::new(mx, my, mz);
let sensitivity = Sensitivity::new(force, torque);
Ok(self.set_sensitivity_manually(sensitivity))
}
}
impl DynpickSensorBuilder<Ready> {
pub fn build(self) -> Result<DynpickSensor, Error> {
let mut sensor = DynpickSensor {
port: self.port,
last_wrench: Wrench::zeroed(),
sensitivity: self.sensitivity,
};
sensor.request_next_wrench()?;
Ok(sensor)
}
}
pub struct DynpickSensor {
port: Box<dyn SerialPort>,
last_wrench: Wrench,
sensitivity: Sensitivity,
}
impl DynpickSensor {
pub fn last_wrench(&self) -> Wrench {
self.last_wrench
}
pub fn sensitivity(&self) -> Sensitivity {
self.sensitivity
}
pub fn update(&mut self) -> Result<Wrench, Error> {
const WRENCH_RESPONSE_LENGTH: usize = 27;
let mut res = [0; WRENCH_RESPONSE_LENGTH];
self.port
.read_exact(&mut res)
.map_err(Error::IO)
.finalize(|| self.request_next_wrench())?;
let res = std::str::from_utf8(&res)
.or(Err(Error::Utf8(res.to_vec())))
.finalize(|| self.request_next_wrench())?;
let (fx, fy, fz, mx, my, mz) = (0..6)
.map(|i| 1 + i * 4)
.map(|start| &res[start..start + 4])
.map(|src| i32::from_str_radix(src, 16))
.filter_map(Result::ok)
.next_tuple()
.ok_or(Error::ParseResponse(res.to_owned()))
.finalize(|| self.request_next_wrench())?;
let digital_force = Triplet::new(fx, fy, fz);
let digital_torque = Triplet::new(mx, my, mz);
let force = digital_force
.map(|d| d - 8192)
.map(|d| d as f64)
.map_entrywise(self.sensitivity.digital_per_newton, |d, s| d / s);
let torque = digital_torque
.map(|d| d - 8192)
.map(|d| d as f64)
.map_entrywise(self.sensitivity.digital_per_newtonmeter, |d, s| d / s);
self.last_wrench = Wrench::new(force, torque);
self.request_next_wrench()?;
Ok(self.last_wrench)
}
pub fn zeroed_next(&mut self) -> Result<(), Error> {
self.port.write_all(&['O' as u8]).map_err(Error::IO)
}
pub fn receive_product_info(&mut self) -> Result<String, Error> {
self.port
.clear(serialport::ClearBuffer::All)
.map_err(Error::SerialPort)?;
self.port.write_all(&['V' as u8]).map_err(Error::IO)?;
std::thread::sleep(self.port.timeout());
let bytes = self.port.bytes_to_read().map_err(Error::SerialPort)?;
let mut res = vec![0; bytes as usize];
let info = match self.port.read(&mut res) {
Ok(_) => match std::str::from_utf8(&res) {
Ok(str) => Ok(str.to_owned()),
Err(_) => Err(Error::Utf8(res)),
},
Err(e) => Err(Error::IO(e)),
};
self.request_next_wrench()?;
info
}
pub fn inner_port(&self) -> &Box<dyn SerialPort> {
&self.port
}
fn request_next_wrench(&mut self) -> Result<(), Error> {
self.port.write_all(&['R' as u8]).map_err(Error::IO)
}
}
#[derive(Debug)]
pub enum Error {
SerialPort(serialport::Error),
IO(std::io::Error),
Utf8(Vec<u8>),
ParseResponse(String),
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Error::SerialPort(e) => write!(f, "SerialPort: {}", e),
Error::IO(e) => write!(f, "IO: {}", e),
Error::Utf8(v) => write!(
f,
"The response from the sensor is invalid for utf8. Raw response: {:X?}",
v
),
Error::ParseResponse(res) => write!(
f,
"Failed to parse the response from the sensor. The response: {}",
res
),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::SerialPort(e) => Some(e),
Error::IO(e) => Some(e),
_ => None,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Wrench {
pub force: Triplet<f64>,
pub torque: Triplet<f64>,
}
impl Wrench {
pub fn new(force: Triplet<f64>, torque: Triplet<f64>) -> Wrench {
Self { force, torque }
}
pub fn zeroed() -> Wrench {
Wrench::new(Triplet::default(), Triplet::default())
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Sensitivity {
digital_per_newton: Triplet<f64>,
digital_per_newtonmeter: Triplet<f64>,
}
impl Sensitivity {
pub fn new(
digital_per_newton: Triplet<f64>,
digital_per_newtonmeter: Triplet<f64>,
) -> Sensitivity {
Self {
digital_per_newton,
digital_per_newtonmeter,
}
}
}
#[ext]
impl<T, E> Result<T, E> {
fn finalize<U, F>(self, f: F) -> Self
where
F: FnOnce() -> Result<U, E>,
{
match self {
Ok(value) => Ok(value),
Err(e1) => match f() {
Ok(_) => Err(e1),
Err(e2) => Err(e2),
},
}
}
}