pub mod responses;
pub mod command;
pub mod config;
pub mod acquisition;
pub mod calibration;
use serialport::SerialPort;
use std::{error::Error, hash::Hasher, string::FromUtf8Error, time::Duration};
#[macro_use]
extern crate derive_more;
use command::Command;
use responses::{Get, ModInfoResp};
#[derive(Debug, Display)]
pub enum ReadError {
PipeError(std::io::Error),
ParseError(String),
#[display(
fmt = "ChecksumMismatch {{ expected: {}, actual: {} }}",
expected,
actual
)]
ChecksumMismatch { expected: u16, actual: u16 }, #[display(fmt = "SizeMismatch {{ expected: {}, actual: {} }}", expected, actual)]
SizeMismatch { expected: u16, actual: u16 },
}
impl Error for ReadError {}
impl From<std::io::Error> for ReadError {
fn from(value: std::io::Error) -> Self {
Self::PipeError(value)
}
}
impl From<FromUtf8Error> for ReadError {
fn from(e: FromUtf8Error) -> Self {
Self::ParseError(format!("UTF8 String couldn't be parsed: {}", e))
}
}
#[derive(Debug, Display)]
pub enum WriteError {
PipeError(std::io::Error),
}
impl Error for WriteError {}
impl From<std::io::Error> for WriteError {
fn from(value: std::io::Error) -> Self {
Self::PipeError(value)
}
}
#[derive(Debug, Display)]
pub enum RWError {
ReadError(ReadError),
WriteError(WriteError),
DeviceError(String),
}
impl Error for RWError {}
impl From<WriteError> for RWError {
fn from(value: WriteError) -> Self {
Self::WriteError(value)
}
}
impl From<ReadError> for RWError {
fn from(value: ReadError) -> Self {
Self::ReadError(value)
}
}
pub struct Device {
serialport: Box<dyn SerialPort>,
read_checksum: crc16::State<crc16::XMODEM>,
read_bytes: u16,
}
impl Device {
pub fn new(serialport: impl Into<Box<dyn SerialPort>>) -> Self {
Self {
serialport: serialport.into(),
read_checksum: crc16::State::<crc16::XMODEM>::new(),
read_bytes: 0,
}
}
pub fn connect(port: Option<String>) -> Result<Self, Box<dyn Error>> {
let ports = serialport::available_ports()?;
let port = if let Some(provided_port) = port {
provided_port
} else {
match ports.into_iter().fold(None, |chosen, port| {
if port.port_name.contains("usb") {
Some(port)
} else {
chosen
}
}) {
Some(port) => port.port_name,
None => {
return Err(Box::new(serialport::Error::new(
serialport::ErrorKind::NoDevice,
"Could not auto-detect serial port",
)))
}
}
};
println!("Using port {}", port);
Ok(Device::new(
serialport::new(port, 38400)
.data_bits(serialport::DataBits::Eight)
.stop_bits(serialport::StopBits::One)
.parity(serialport::Parity::None)
.timeout(Duration::new(1, 0))
.open()?,
))
}
pub fn write_frame(
&mut self,
command: Command,
payload: Option<&[u8]>,
) -> Result<(), WriteError> {
let payload_length = if let Some(payload) = payload {
payload.len() as u16
} else {
0
};
let size = (payload_length + 5u16).to_be_bytes();
let command = command.discriminant().to_be_bytes();
let mut crc = crc16::State::<crc16::XMODEM>::new();
self.serialport.write(&size)?;
crc.update(&size);
self.serialport.write(&command)?;
crc.update(&command);
if let Some(payload_bytes) = payload {
self.serialport.write(payload_bytes)?;
crc.update(payload_bytes);
}
let crc = &(crc.finish() as u16).to_be_bytes();
self.serialport.write(crc)?;
Ok(())
}
fn end_frame(&mut self, expected_frame_len: u16) -> Result<(), ReadError> {
let expected_sum = self.read_checksum.finish() as u16;
let checksum: u16 = Get::<u16>::get(self)?;
self.read_checksum = crc16::State::<crc16::XMODEM>::new();
if expected_sum == checksum && self.read_bytes == expected_frame_len {
self.read_bytes = 0;
Ok(())
} else if self.read_bytes != expected_frame_len {
let read_bytes = self.read_bytes;
self.read_bytes = 0;
Err(ReadError::SizeMismatch {
expected: expected_frame_len,
actual: read_bytes,
})
} else {
self.read_bytes = 0;
Err(ReadError::ChecksumMismatch {
expected: expected_sum,
actual: checksum,
})
}
}
pub fn get_mod_info(&mut self) -> Result<ModInfoResp, RWError> {
self.write_frame(Command::GetModInfo, None)?;
let expected_size = Get::<u16>::get(self)?;
if Get::<u8>::get(self)? == Command::GetModInfoResp.discriminant() {
let device_type = Get::<u32>::get_string(self)?;
let revision = Get::<u32>::get_string(self)?;
self.end_frame(expected_size)?;
Ok(ModInfoResp {
device_type,
revision,
})
} else {
let _ = self.end_frame(expected_size);
Err(RWError::ReadError(ReadError::ParseError(
"Unexpected response type".to_string(),
)))
}
}
pub fn serial_number(&mut self) -> Result<u32, RWError> {
self.write_frame(Command::SerialNumber, None)?;
let expected_size = Get::<u16>::get(self)?;
if Get::<u8>::get(self)? == Command::SerialNumberResp.discriminant() {
let serial_number = Get::<u32>::get(self)?;
self.end_frame(expected_size)?;
Ok(serial_number)
} else {
let _ = self.end_frame(expected_size);
Err(RWError::ReadError(ReadError::ParseError(
"Unexpected response type".to_string(),
)))
}
}
pub fn save(&mut self) -> Result<(), RWError> {
self.write_frame(Command::Save, None)?;
let expected_size = Get::<u16>::get(self)?;
if Get::<u8>::get(self)? == Command::SaveDone.discriminant() {
let error_code = Get::<u16>::get(self)?;
self.end_frame(expected_size)?;
if error_code != 0 {
return Err(RWError::DeviceError(
"Recieved error code from device, settings not saved succesfully".to_string(),
));
}
Ok(())
} else {
let _ = self.end_frame(expected_size);
Err(RWError::ReadError(ReadError::ParseError(
"Unexpected response type".to_string(),
)))
}
}
pub fn power_up(&mut self) -> Result<(), RWError> {
self.write_frame(Command::SerialNumber, None)?;
let expected_size = Get::<u16>::get(self)?;
let resp_command = Get::<u8>::get(self)?;
if resp_command == Command::PowerUpDone.discriminant() {
self.end_frame(expected_size)?;
Ok(())
} else if resp_command == Command::SerialNumberResp.discriminant() {
Get::<u32>::get(self)?;
self.end_frame(expected_size)?;
Ok(())
} else {
let _ = self.end_frame(expected_size);
Err(RWError::ReadError(ReadError::ParseError(
"Unexpected response type".to_string(),
)))
}
}
fn power_down_impl(&mut self) -> Result<(), RWError> {
self.write_frame(Command::PowerDown, None)?;
let expected_size = Get::<u16>::get(self)?;
if Get::<u8>::get(self)? == Command::PowerDownDone.discriminant() {
self.end_frame(expected_size)?;
Ok(())
} else {
let _ = self.end_frame(expected_size);
Err(RWError::ReadError(ReadError::ParseError(
"Unexpected response type".to_string(),
)))
}
}
#[cfg(feature = "reserved")]
pub fn power_down_raw(&mut self) -> Result<(), RWError> {
self.power_down_impl()
}
pub fn power_down(mut self) -> Result<(), RWError> {
let ret = match self.power_down_impl() {
Ok(_) => Ok(()),
Err(RWError::ReadError(_)) => Ok(()),
Err(e) => Err(e),
};
ret
}
}
#[cfg(test)]
mod tests {
use crate::acquisition::*;
use crate::*;
#[test]
fn continuous_mode() {
let tp3 = Device::connect(None).expect("connects to device");
let mut tp3 = tp3
.continuous_mode_easy(0.25, vec![DataID::AccelX])
.expect("got into cont mode");
{
let mut iter = tp3.iter();
for _ in 0..16 {
assert!(match iter.next() { Some(Ok(Data { accel_x: Some(_accel_measurement), ..})) => true, _ => false }, "Calling next on interator in continuous mode should yield the data we asked for");
}
}
let mut tp3 = tp3.stop_continuous_mode_easy().unwrap();
{
let mut iter = tp3.iter();
assert!(
match iter.next() {
None => true,
_ => false,
},
"Stop continious mode should leave continuous mode"
)
}
}
}