use std::{
error::Error,
marker::PhantomData,
sync::mpsc::*,
time::{Duration, Instant},
};
use crate::byte_converter::FromByteSlice;
#[derive(Debug, Copy, Clone)]
pub enum BrickletError {
InvalidParameter,
FunctionNotSupported,
UnknownError,
NotConnected,
SuccessButResponseExpectedIsDisabled,
}
impl From<u8> for BrickletError {
fn from(byte: u8) -> BrickletError {
match byte {
1 => BrickletError::InvalidParameter,
2 => BrickletError::FunctionNotSupported,
_ => BrickletError::UnknownError,
}
}
}
impl std::fmt::Display for BrickletError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.description()) }
}
impl std::error::Error for BrickletError {
fn description(&self) -> &str {
match self {
BrickletError::InvalidParameter => "A parameter was invalid or had an unexpected length.",
BrickletError::FunctionNotSupported => "The brick or bricklet does not support the requested function.",
BrickletError::UnknownError => "UnknownError, Currently unused",
BrickletError::NotConnected => "The request can not be fulfulled, as there is currently no connection to a brick daemon.",
BrickletError::SuccessButResponseExpectedIsDisabled =>
"The request was sent, but response expected is disabled, so no response can be received. This is not an error.",
}
}
}
pub struct ConvertingReceiver<T: FromByteSlice> {
receiver: Receiver<Result<Vec<u8>, BrickletError>>,
sent_time: Instant,
timeout: Duration,
phantom: PhantomData<T>,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum BrickletRecvTimeoutError {
QueueDisconnected,
QueueTimeout,
InvalidParameter,
FunctionNotSupported,
UnknownError,
MalformedPacket,
NotConnected,
SuccessButResponseExpectedIsDisabled,
}
impl std::fmt::Display for BrickletRecvTimeoutError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.description()) }
}
impl std::error::Error for BrickletRecvTimeoutError {
fn description(&self) -> &str {
match self {
BrickletRecvTimeoutError::QueueDisconnected =>
"The queue was disconnected. This usually happens if the ip connection is destroyed.",
BrickletRecvTimeoutError::QueueTimeout => "The request could not be responded to before the timeout was reached.",
BrickletRecvTimeoutError::InvalidParameter => "A parameter was invalid or had an unexpected length.",
BrickletRecvTimeoutError::FunctionNotSupported => "The brick or bricklet does not support the requested function.",
BrickletRecvTimeoutError::UnknownError => "UnknownError, Currently unused",
BrickletRecvTimeoutError::MalformedPacket =>
"The received packet had an unexpected length. Maybe a function was called on a wrong brick or bricklet?",
BrickletRecvTimeoutError::NotConnected =>
"The request can not be fulfulled, as there is currently no connection to a brick daemon.",
BrickletRecvTimeoutError::SuccessButResponseExpectedIsDisabled =>
"The request was sent, but response expected is disabled, so no response can be received. This is not an error.",
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum BrickletTryRecvError {
QueueDisconnected,
QueueEmpty,
InvalidParameter,
FunctionNotSupported,
UnknownError,
MalformedPacket,
NotConnected,
SuccessButResponseExpectedIsDisabled,
}
impl std::fmt::Display for BrickletTryRecvError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.description()) }
}
impl std::error::Error for BrickletTryRecvError {
fn description(&self) -> &str {
match self {
BrickletTryRecvError::QueueDisconnected =>
"The queue was disconnected. This usually happens if the ip connection is destroyed.",
BrickletTryRecvError::QueueEmpty => "There are currently no responses available.",
BrickletTryRecvError::InvalidParameter => "A parameter was invalid or had an unexpected length.",
BrickletTryRecvError::FunctionNotSupported => "The brick or bricklet does not support the requested function.",
BrickletTryRecvError::UnknownError => "UnknownError, Currently unused",
BrickletTryRecvError::MalformedPacket =>
"The received packet had an unexpected length. Maybe a function was called on a wrong brick or bricklet?",
BrickletTryRecvError::NotConnected =>
"The request can not be fulfulled, as there is currently no connection to a brick daemon.",
BrickletTryRecvError::SuccessButResponseExpectedIsDisabled =>
"The request was sent, but response expected is disabled, so no response can be received. This is not an error.",
}
}
}
impl<T: FromByteSlice> ConvertingReceiver<T> {
pub fn new(receiver: Receiver<Result<Vec<u8>, BrickletError>>, timeout: Duration) -> ConvertingReceiver<T> {
ConvertingReceiver { receiver, sent_time: Instant::now(), timeout, phantom: PhantomData }
}
pub fn try_recv(&self) -> Result<T, BrickletTryRecvError> {
let recv_result = self.receiver.try_recv();
match recv_result {
Ok(Ok(bytes)) =>
if T::bytes_expected() == bytes.len() {
Ok(T::from_le_byte_slice(&bytes))
} else {
Err(BrickletTryRecvError::MalformedPacket)
},
Ok(Err(BrickletError::InvalidParameter)) => Err(BrickletTryRecvError::InvalidParameter),
Ok(Err(BrickletError::FunctionNotSupported)) => Err(BrickletTryRecvError::FunctionNotSupported),
Ok(Err(BrickletError::UnknownError)) => Err(BrickletTryRecvError::UnknownError),
Ok(Err(BrickletError::NotConnected)) => Err(BrickletTryRecvError::NotConnected),
Ok(Err(BrickletError::SuccessButResponseExpectedIsDisabled)) => Err(BrickletTryRecvError::SuccessButResponseExpectedIsDisabled),
Err(TryRecvError::Disconnected) => Err(BrickletTryRecvError::QueueDisconnected),
Err(TryRecvError::Empty) => Err(BrickletTryRecvError::QueueEmpty),
}
}
pub fn recv(&self) -> Result<T, BrickletRecvTimeoutError> {
let recv_result = self.receiver.recv_timeout(self.sent_time + self.timeout - Instant::now());
match recv_result {
Ok(Ok(bytes)) =>
if T::bytes_expected() == bytes.len() {
Ok(T::from_le_byte_slice(&bytes))
} else {
Err(BrickletRecvTimeoutError::MalformedPacket)
},
Ok(Err(BrickletError::InvalidParameter)) => Err(BrickletRecvTimeoutError::InvalidParameter),
Ok(Err(BrickletError::FunctionNotSupported)) => Err(BrickletRecvTimeoutError::FunctionNotSupported),
Ok(Err(BrickletError::UnknownError)) => Err(BrickletRecvTimeoutError::UnknownError),
Ok(Err(BrickletError::NotConnected)) => Err(BrickletRecvTimeoutError::NotConnected),
Ok(Err(BrickletError::SuccessButResponseExpectedIsDisabled)) =>
Err(BrickletRecvTimeoutError::SuccessButResponseExpectedIsDisabled),
Err(RecvTimeoutError::Disconnected) => Err(BrickletRecvTimeoutError::QueueDisconnected),
Err(RecvTimeoutError::Timeout) => Err(BrickletRecvTimeoutError::QueueTimeout),
}
}
pub fn iter(&self) -> Iter<T> { Iter { rx: self } }
pub fn try_iter(&self) -> TryIter<T> { TryIter { rx: self } }
}
pub struct Iter<'a, T: 'a + FromByteSlice> {
rx: &'a ConvertingReceiver<T>,
}
pub struct TryIter<'a, T: 'a + FromByteSlice> {
rx: &'a ConvertingReceiver<T>,
}
pub struct IntoIter<T: FromByteSlice> {
rx: ConvertingReceiver<T>,
}
impl<'a, T: FromByteSlice> Iterator for Iter<'a, T> {
type Item = T;
fn next(&mut self) -> Option<T> { self.rx.recv().ok() }
}
impl<'a, T: FromByteSlice> Iterator for TryIter<'a, T> {
type Item = T;
fn next(&mut self) -> Option<T> { self.rx.try_recv().ok() }
}
impl<'a, T: FromByteSlice> IntoIterator for &'a ConvertingReceiver<T> {
type Item = T;
type IntoIter = Iter<'a, T>;
fn into_iter(self) -> Iter<'a, T> { self.iter() }
}
impl<T: FromByteSlice> Iterator for IntoIter<T> {
type Item = T;
fn next(&mut self) -> Option<T> { self.rx.recv().ok() }
}
impl<T: FromByteSlice> IntoIterator for ConvertingReceiver<T> {
type Item = T;
type IntoIter = IntoIter<T>;
fn into_iter(self) -> IntoIter<T> { IntoIter { rx: self } }
}