tinkerforge/
converting_receiver.rs

1//! A wrapper for [`Receiver`](std::sync::mpsc::Receiver), which converts received byte vectors to structured data.
2use std::{
3    error::Error,
4    marker::PhantomData,
5    sync::mpsc::*,
6    time::{Duration, Instant},
7};
8
9use crate::byte_converter::FromByteSlice;
10
11/// Error type for interactions with Tinkerforge bricks or bricklets.
12#[derive(Debug, Copy, Clone)]
13pub enum BrickletError {
14    /// A parameter was invalid or had an unexpected length
15    InvalidParameter,
16    /// The brick or bricklet does not support the requested function.
17    FunctionNotSupported,
18    /// Currently unused
19    UnknownError,
20    /// The request can not be fulfulled, as there is currently no connection to a brick daemon.
21    NotConnected,
22    /// The request was sent, but response expected is disabled, so no response can be received. This is not an error.
23    SuccessButResponseExpectedIsDisabled,
24}
25
26impl From<u8> for BrickletError {
27    fn from(byte: u8) -> BrickletError {
28        match byte {
29            1 => BrickletError::InvalidParameter,
30            2 => BrickletError::FunctionNotSupported,
31            _ => BrickletError::UnknownError,
32        }
33    }
34}
35
36impl std::fmt::Display for BrickletError {
37    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.description()) }
38}
39
40impl std::error::Error for BrickletError {
41    fn description(&self) -> &str {
42        match self {
43            BrickletError::InvalidParameter => "A parameter was invalid or had an unexpected length.",
44            BrickletError::FunctionNotSupported => "The brick or bricklet does not support the requested function.",
45            BrickletError::UnknownError => "UnknownError, Currently unused",
46            BrickletError::NotConnected => "The request can not be fulfulled, as there is currently no connection to a brick daemon.",
47            BrickletError::SuccessButResponseExpectedIsDisabled =>
48                "The request was sent, but response expected is disabled, so no response can be received. This is not an error.",
49        }
50    }
51}
52
53/// A wrapper for [`Receiver`], which converts received byte vectors to structured data.
54///
55/// This receiver wraps a [`Receiver`] receiving raw bytes. Calling [`recv`] or [`try_recv`]
56/// will call equivalent methods on the wrapped [`Receiver`] and then convert the received bytes
57/// to a instance of `T`.
58///
59/// ### Note
60///
61/// Calling [`recv`] will not block indefinitely. Instead the timeout passed to the [`new`](#method.new) method is used for
62/// a call of [`recv_timeout`](std::sync::mpsc::Receiver::recv_timeout)
63///
64/// # Type parameters
65///
66/// * `T` - Type which is created from received byte vectors. Must implement [`FromByteSlice`](crate::byte_converter::FromByteSlice)
67///
68/// # Errors
69///
70/// Returned errors are equivalent to those returned from methods of a [`Receiver`]. Additionally errors
71/// raised by the brick or bricklet, such as `InvalidParameter`, `FunctionNotSupported` and `UnknownError`
72/// will be returned. If the received response can not be interpreted as the result type `T`, a `MalformedPacket`
73/// error is raised.
74/// ### Note
75/// If the device is configured to send no response for a result-less setter, the Error `SuccessButResponseExpectedIsDisabled`
76/// will be returned. This indicates, that the request was sent to the device, but no further guarantees can be made.
77///
78/// [`Receiver`]: std::sync::mpsc::Receiver
79/// [`recv`]: #method.recv
80/// [`try_recv`]: #method.try_recv
81pub struct ConvertingReceiver<T: FromByteSlice> {
82    receiver: Receiver<Result<Vec<u8>, BrickletError>>,
83    sent_time: Instant,
84    timeout: Duration,
85    phantom: PhantomData<T>,
86}
87
88/// Error type which is returned if a ConvertingReceiver::recv call fails.
89#[derive(Copy, Clone, Debug, PartialEq)]
90pub enum BrickletRecvTimeoutError {
91    /// The queue was disconnected. This usually happens if the ip connection is destroyed.
92    QueueDisconnected,
93    /// The request could not be responded to before the timeout was reached.
94    QueueTimeout,
95    /// A parameter was invalid or had an unexpected length.
96    InvalidParameter,
97    /// The brick or bricklet does not support the requested function.
98    FunctionNotSupported,
99    /// Currently unused
100    UnknownError,
101    /// The received packet had an unexpected length. Maybe a function was called on a wrong brick or bricklet?
102    MalformedPacket,
103    /// The request can not be fulfulled, as there is currently no connection to a brick daemon.
104    NotConnected,
105    /// The request was sent, but response expected is disabled, so no response can be received. This is not an error.
106    SuccessButResponseExpectedIsDisabled,
107}
108
109impl std::fmt::Display for BrickletRecvTimeoutError {
110    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.description()) }
111}
112
113impl std::error::Error for BrickletRecvTimeoutError {
114    fn description(&self) -> &str {
115        match self {
116            BrickletRecvTimeoutError::QueueDisconnected =>
117                "The queue was disconnected. This usually happens if the ip connection is destroyed.",
118            BrickletRecvTimeoutError::QueueTimeout => "The request could not be responded to before the timeout was reached.",
119            BrickletRecvTimeoutError::InvalidParameter => "A parameter was invalid or had an unexpected length.",
120            BrickletRecvTimeoutError::FunctionNotSupported => "The brick or bricklet does not support the requested function.",
121            BrickletRecvTimeoutError::UnknownError => "UnknownError, Currently unused",
122            BrickletRecvTimeoutError::MalformedPacket =>
123                "The received packet had an unexpected length. Maybe a function was called on a wrong brick or bricklet?",
124            BrickletRecvTimeoutError::NotConnected =>
125                "The request can not be fulfulled, as there is currently no connection to a brick daemon.",
126            BrickletRecvTimeoutError::SuccessButResponseExpectedIsDisabled =>
127                "The request was sent, but response expected is disabled, so no response can be received. This is not an error.",
128        }
129    }
130}
131
132/// Error type which is returned if a [`try_recv`](crate::converting_receiver::ConvertingReceiver::try_recv) call fails.
133#[derive(Copy, Clone, Debug, PartialEq)]
134pub enum BrickletTryRecvError {
135    /// The queue was disconnected. This usually happens if the ip connection is destroyed.
136    QueueDisconnected,
137    /// There are currently no responses available.
138    QueueEmpty,
139    /// A parameter was invalid or had an unexpected length.
140    InvalidParameter,
141    /// The brick or bricklet does not support the requested function.
142    FunctionNotSupported,
143    /// Currently unused
144    UnknownError,
145    /// The received packet had an unexpected length. Maybe a function was called on a wrong brick or bricklet?
146    MalformedPacket,
147    /// The request can not be fulfulled, as there is currently no connection to a brick daemon.
148    NotConnected,
149    /// The request was sent, but response expected is disabled, so no response can be received. This is not an error.
150    SuccessButResponseExpectedIsDisabled,
151}
152
153impl std::fmt::Display for BrickletTryRecvError {
154    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.description()) }
155}
156
157impl std::error::Error for BrickletTryRecvError {
158    fn description(&self) -> &str {
159        match self {
160            BrickletTryRecvError::QueueDisconnected =>
161                "The queue was disconnected. This usually happens if the ip connection is destroyed.",
162            BrickletTryRecvError::QueueEmpty => "There are currently no responses available.",
163            BrickletTryRecvError::InvalidParameter => "A parameter was invalid or had an unexpected length.",
164            BrickletTryRecvError::FunctionNotSupported => "The brick or bricklet does not support the requested function.",
165            BrickletTryRecvError::UnknownError => "UnknownError, Currently unused",
166            BrickletTryRecvError::MalformedPacket =>
167                "The received packet had an unexpected length. Maybe a function was called on a wrong brick or bricklet?",
168            BrickletTryRecvError::NotConnected =>
169                "The request can not be fulfulled, as there is currently no connection to a brick daemon.",
170            BrickletTryRecvError::SuccessButResponseExpectedIsDisabled =>
171                "The request was sent, but response expected is disabled, so no response can be received. This is not an error.",
172        }
173    }
174}
175
176impl<T: FromByteSlice> ConvertingReceiver<T> {
177    /// Creates a new converting receiver which wraps the given [`Receiver`](std::sync::mpsc::Receiver). [`recv`](#method.recv) calls will time out after the given timeout.
178    pub fn new(receiver: Receiver<Result<Vec<u8>, BrickletError>>, timeout: Duration) -> ConvertingReceiver<T> {
179        ConvertingReceiver { receiver, sent_time: Instant::now(), timeout, phantom: PhantomData }
180    }
181
182    /// Attempts to return a pending value on this receiver without blocking. This method behaves like [`try_recv`](std::sync::mpsc::Receiver::try_recv).
183    ///
184    /// # Errors
185    ///
186    /// Returns an error on the following conditions:
187    /// * There is no connection to a brick daemon.
188    /// * The brick or bricklet returns an error.
189    /// * The queue was disconnected or currently empty.
190    /// * Response expected was disabled for a result-less setter. This is not an error.
191    pub fn try_recv(&self) -> Result<T, BrickletTryRecvError> {
192        let recv_result = self.receiver.try_recv();
193        match recv_result {
194            Ok(Ok(bytes)) =>
195                if T::bytes_expected() == bytes.len() {
196                    Ok(T::from_le_byte_slice(&bytes))
197                } else {
198                    Err(BrickletTryRecvError::MalformedPacket)
199                },
200            Ok(Err(BrickletError::InvalidParameter)) => Err(BrickletTryRecvError::InvalidParameter),
201            Ok(Err(BrickletError::FunctionNotSupported)) => Err(BrickletTryRecvError::FunctionNotSupported),
202            Ok(Err(BrickletError::UnknownError)) => Err(BrickletTryRecvError::UnknownError),
203            Ok(Err(BrickletError::NotConnected)) => Err(BrickletTryRecvError::NotConnected),
204            Ok(Err(BrickletError::SuccessButResponseExpectedIsDisabled)) => Err(BrickletTryRecvError::SuccessButResponseExpectedIsDisabled),
205            Err(TryRecvError::Disconnected) => Err(BrickletTryRecvError::QueueDisconnected),
206            Err(TryRecvError::Empty) => Err(BrickletTryRecvError::QueueEmpty),
207        }
208    }
209
210    /// Attempts to wait for a value on this receiver, returning an error if the corresponding channel has hung up, or if it waits more than timeout.
211    /// This method behaves like [`recv_timeout`](std::sync::mpsc::Receiver::recv_timeout).
212    ///
213    /// # Errors
214    ///
215    /// Returns an error on one of the following conditions:
216    /// * There is no connection to a brick daemon.
217    /// * The brick or bricklet returns an error.
218    /// * The queue was disconnected.
219    /// * Response expected was disabled for a result-less setter. This is not an error.
220    /// * Blocked longer than the configured time out.
221    pub fn recv(&self) -> Result<T, BrickletRecvTimeoutError> {
222        let recv_result = self.receiver.recv_timeout(self.sent_time + self.timeout - Instant::now());
223        match recv_result {
224            Ok(Ok(bytes)) =>
225                if T::bytes_expected() == bytes.len() {
226                    Ok(T::from_le_byte_slice(&bytes))
227                } else {
228                    Err(BrickletRecvTimeoutError::MalformedPacket)
229                },
230            Ok(Err(BrickletError::InvalidParameter)) => Err(BrickletRecvTimeoutError::InvalidParameter),
231            Ok(Err(BrickletError::FunctionNotSupported)) => Err(BrickletRecvTimeoutError::FunctionNotSupported),
232            Ok(Err(BrickletError::UnknownError)) => Err(BrickletRecvTimeoutError::UnknownError),
233            Ok(Err(BrickletError::NotConnected)) => Err(BrickletRecvTimeoutError::NotConnected),
234            Ok(Err(BrickletError::SuccessButResponseExpectedIsDisabled)) =>
235                Err(BrickletRecvTimeoutError::SuccessButResponseExpectedIsDisabled),
236            Err(RecvTimeoutError::Disconnected) => Err(BrickletRecvTimeoutError::QueueDisconnected),
237            Err(RecvTimeoutError::Timeout) => Err(BrickletRecvTimeoutError::QueueTimeout),
238        }
239    }
240    /*
241        pub fn recv_timeout(&self, timeout: Duration) -> Result<T, BrickletRecvTimeoutError> {
242            let recv_result = self.receiver.recv_timeout(timeout);
243            match recv_result {
244                Ok(Ok(bytes))                 => if T::bytes_expected() == bytes.len()
245                                                    {Ok(T::from_le_byte_slice(bytes))}
246                                                 else
247                                                    {Err(BrickletRecvTimeoutError::MalformedPacket)},
248                Ok(Err(BrickletError::InvalidParameter))     => Err(BrickletRecvTimeoutError::InvalidParameter),
249                Ok(Err(BrickletError::FunctionNotSupported)) => Err(BrickletRecvTimeoutError::FunctionNotSupported),
250                Ok(Err(BrickletError::UnknownError))         => Err(BrickletRecvTimeoutError::UnknownError),
251                Ok(Err(BrickletError::NotConnected))         => Err(BrickletRecvTimeoutError::NotConnected),
252                Err(RecvTimeoutError::Disconnected)             => Err(BrickletRecvTimeoutError::QueueDisconnected),
253                Err(RecvTimeoutError::Timeout)                  => Err(BrickletRecvTimeoutError::QueueTimeout),
254            }
255        }
256    */
257    /* uncomment if https://github.com/rust-lang/rust/issues/46316 has landed
258        pub fn recv_deadline(&self, deadline: Instant) -> Result<T, BrickletRecvTimeoutError> {
259            let recv_result = self.receiver.recv_deadline(deadline);
260            match recv_result {
261                Ok(Ok(bytes))                 => Ok(T::from_le_byte_slice(bytes)),
262                Ok(Err(InvalidParameter))     => Err(BrickletRecvTimeoutError::InvalidParameter),
263                Ok(Err(FunctionNotSupported)) => Err(BrickletRecvTimeoutError::FunctionNotSupported),
264                Ok(Err(UnknownError))         => Err(BrickletRecvTimeoutError::UnknownError),
265                Err(Disconnected)             => Err(BrickletRecvTimeoutError::QueueDisconnected),
266                Err(Timeout)                  => Err(BrickletRecvTimeoutError::QueueTimeout),
267            }
268        }
269    */
270    pub fn iter(&self) -> Iter<T> { Iter { rx: self } }
271
272    pub fn try_iter(&self) -> TryIter<T> { TryIter { rx: self } }
273}
274
275pub struct Iter<'a, T: 'a + FromByteSlice> {
276    rx: &'a ConvertingReceiver<T>,
277}
278
279pub struct TryIter<'a, T: 'a + FromByteSlice> {
280    rx: &'a ConvertingReceiver<T>,
281}
282
283pub struct IntoIter<T: FromByteSlice> {
284    rx: ConvertingReceiver<T>,
285}
286
287impl<'a, T: FromByteSlice> Iterator for Iter<'a, T> {
288    type Item = T;
289
290    fn next(&mut self) -> Option<T> { self.rx.recv().ok() }
291}
292
293impl<'a, T: FromByteSlice> Iterator for TryIter<'a, T> {
294    type Item = T;
295
296    fn next(&mut self) -> Option<T> { self.rx.try_recv().ok() }
297}
298
299impl<'a, T: FromByteSlice> IntoIterator for &'a ConvertingReceiver<T> {
300    type Item = T;
301    type IntoIter = Iter<'a, T>;
302
303    fn into_iter(self) -> Iter<'a, T> { self.iter() }
304}
305
306impl<T: FromByteSlice> Iterator for IntoIter<T> {
307    type Item = T;
308    fn next(&mut self) -> Option<T> { self.rx.recv().ok() }
309}
310
311impl<T: FromByteSlice> IntoIterator for ConvertingReceiver<T> {
312    type Item = T;
313    type IntoIter = IntoIter<T>;
314
315    fn into_iter(self) -> IntoIter<T> { IntoIter { rx: self } }
316}