#[cfg(feature = "python")]
use pyo3::{PyErr, exceptions::PyValueError};
use std::time::Duration;
use super::{
ResultComm,
packets::{Packet, PacketConstruct, PacketParse},
tags::status::StatusCode,
};
pub mod i2c;
pub mod uart;
pub mod usb;
#[derive(thiserror::Error, Debug)]
pub enum CommunicationError {
#[error("error raised by UART library")]
SerialPortError(#[from] serialport::Error),
#[error("error occurred while reading or writing to device")]
IOError(#[from] std::io::Error),
#[error("error while reading or writing a file")]
FileError(#[source] std::io::Error),
#[error("board sent NACK")]
NACKSent,
#[error("received incorrect CRC")]
InvalidCrc,
#[error("invalid response header")]
InvalidHeader,
#[error("data in the packet is invalid")]
InvalidData,
#[error("received another packet type than was expected")]
InvalidPacketReceived,
#[error("error occured while parsing: {0}")]
ParseError(String),
#[error("unexpected status code: {1} ({1:#X}) {0}")]
UnexpectedStatus(StatusCode, u32),
#[error("communication was aborted")]
Aborted,
#[error("this functionality is not supported on the current platform")]
UnsupportedPlatform,
#[error("timeout occured while waiting for response")]
Timeout,
}
impl From<StatusCode> for CommunicationError {
fn from(value: StatusCode) -> Self {
CommunicationError::UnexpectedStatus(value, value.into())
}
}
#[cfg(feature = "python")]
impl From<CommunicationError> for PyErr {
fn from(value: CommunicationError) -> Self {
PyValueError::new_err(value.to_string())
}
}
#[cfg_attr(any(feature = "python", feature = "c_api"), enum_dispatch::enum_dispatch)]
pub trait Protocol {
fn get_timeout(&self) -> Duration;
fn get_polling_interval(&self) -> Duration;
fn get_identifier(&self) -> &str;
fn read(&mut self, bytes: usize) -> ResultComm<Vec<u8>>;
fn write_packet_raw(&mut self, data: &[u8]) -> ResultComm<()>;
fn read_packet_raw(&mut self, packet_code: u8) -> ResultComm<Vec<u8>>;
fn write_packet_concrete<T>(&mut self, packet: T) -> ResultComm<()>
where
T: PacketConstruct + Packet,
{
self.write_packet_raw(&packet.construct())
}
fn read_packet_concrete<T>(&mut self) -> ResultComm<T>
where
T: PacketParse + Packet,
{
let data_slice = self.read_packet_raw(T::get_code())?;
T::parse(&data_slice)
}
}
pub trait ProtocolOpen: Protocol {
fn open(identifier: &str) -> ResultComm<Self>
where
Self: Sized;
#[expect(
unused_variables,
reason = "rust-analyzer would show the underscores for inlay hints"
)]
fn open_with_options(
identifier: &str,
baudrate: u32,
timeout: Duration,
polling_interval: Duration,
) -> ResultComm<Self>
where
Self: Sized,
{
Self::open(identifier)
}
}
#[cfg(any(feature = "c_api", feature = "python"))]
pub mod protocol_impl;
const ACK: u8 = 0xA1;
const NACK: u8 = 0xA2;
const ACK_ABORT: u8 = 0xA3;