Hadusos - A half-duplex session over serial protocol.
The client should provide a serial device instance and a timer instance, which respectively implements the Serial and Timer trait. Then, a session layer instance can be constructed.
The Session supports half-duplex data transmission, i.e., a client operates as either the sender or the receiver at a time. When operating as the sender, the client should call send to send data. When operating as the receiver, the client should call listen to learn the incoming data length to allocate a buffer, and subsequently call receive to receive the data. The receiver may also call reject if it does not want to proceed to receiving the data after listen.
Below shows an example.
use crossbeam::channel::{self, Receiver, RecvError, SendError, Sender};
use hadusos::{Serial, SerialError, Session, Timer};
use std::{
sync::Arc,
thread,
time::{Duration, SystemTime, UNIX_EPOCH},
};
fn main() {
let (serial0, serial1) = MockSerial::new_pair();
let (timer0, timer1) = (MockTimer, MockTimer);
const TIMEOUT: u32 = 1000;
let thread_echo = thread::spawn(|| {
let mut sess = Session::<_, _, 50, 3>::new(serial0, timer0);
let data_len = sess.listen(TIMEOUT).unwrap();
let mut buffer = vec![0u8; data_len as usize].into_boxed_slice();
sess.receive(&mut buffer, TIMEOUT).unwrap();
sess.send(&buffer, TIMEOUT).unwrap();
});
let thread_check = thread::spawn(|| {
let mut sess = Session::<_, _, 50, 3>::new(serial1, timer1);
sess.send("hello world!".as_bytes(), TIMEOUT).unwrap();
let data_len = sess.listen(TIMEOUT).unwrap();
let mut buffer = vec![0u8; data_len as usize].into_boxed_slice();
sess.receive(&mut buffer, TIMEOUT).unwrap();
assert!(buffer.as_ref() == "hello world!".as_bytes());
});
thread_echo.join().unwrap();
thread_check.join().unwrap();
}
struct MockTimer;
impl Timer for MockTimer {
fn get_timestamp_ms(&mut self) -> u32 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis() as u32
}
}
struct MockSerial {
send: Arc<Sender<u8>>,
recv: Arc<Receiver<u8>>,
}
impl MockSerial {
fn new_pair() -> (Self, Self) {
let (send0, recv0) = channel::unbounded();
let (send1, recv1) = channel::unbounded();
let send0 = Arc::new(send0);
let send1 = Arc::new(send1);
let recv0 = Arc::new(recv0);
let recv1 = Arc::new(recv1);
std::mem::forget(Arc::clone(&send0));
std::mem::forget(Arc::clone(&send1));
std::mem::forget(Arc::clone(&recv0));
std::mem::forget(Arc::clone(&recv1));
(
Self {
send: send0,
recv: recv1,
},
Self {
send: send1,
recv: recv0,
},
)
}
}
impl Serial for MockSerial {
type ReadError = RecvError;
type WriteError = SendError<u8>;
fn read_byte_with_timeout(
&mut self,
timeout_ms: u32,
) -> Result<u8, SerialError<Self::ReadError, Self::WriteError>> {
self.recv
.recv_timeout(Duration::from_millis(timeout_ms as u64))
.map_err(|e| match e {
channel::RecvTimeoutError::Timeout => SerialError::Timeout,
channel::RecvTimeoutError::Disconnected => SerialError::ReadError(RecvError),
})
}
fn write_byte(
&mut self,
byte: u8,
) -> Result<(), SerialError<Self::ReadError, Self::WriteError>> {
self.send.send(byte).map_err(|e| SerialError::WriteError(e))
}
}