2个版本

0.2.1 2024年7月10日
0.2.0 2024年7月10日

#356硬件支持

每月41次下载

MIT 许可证

110KB
1K SLoC

Hadusos - 串行协议的 half-duplex session。

客户端应提供一个串行设备实例和一个计时器实例,它们分别实现了 SerialTimer 特性。然后,可以构建一个会话层实例。

Session 支持半双工数据传输,即客户端一次作为发送者或接收者操作。作为发送者时,客户端应调用 send 来发送数据。作为接收者时,客户端应调用 listen 来获取传入数据长度以分配缓冲区,然后调用 receive 来接收数据。如果接收者在 listen 之后不想继续接收数据,也可以调用 reject

以下是一个示例。

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() {
    // Create a pair of connected serial devices.
    let (serial0, serial1) = MockSerial::new_pair();

    // Create two timers.
    let (timer0, timer1) = (MockTimer, MockTimer);

    const TIMEOUT: u32 = 1000;

    // Start a thread what will echo back whatever it receives.
    let thread_echo = thread::spawn(|| {
        // Create a session layer instance using the serial device and timer.
        let mut sess = Session::<_, _, 50, 3>::new(serial0, timer0);

        // Listen for incoming data.
        let data_len = sess.listen(TIMEOUT).unwrap();

        // Allocate a buffer and receive incoming data.
        let mut buffer = vec![0u8; data_len as usize].into_boxed_slice();
        sess.receive(&mut buffer, TIMEOUT).unwrap();

        // Echo the data back.
        sess.send(&buffer, TIMEOUT).unwrap();
    });

    // Start a thread that will send "hello world!" over the serial and verify
    // the echoed data.
    let thread_check = thread::spawn(|| {
        // Create a session layer instance using the serial device and timer.
        let mut sess = Session::<_, _, 50, 3>::new(serial1, timer1);

        // Send "hello world!".
        sess.send("hello world!".as_bytes(), TIMEOUT).unwrap();

        // Listen for echoed data.
        let data_len = sess.listen(TIMEOUT).unwrap();

        // Allocate a buffer and receive echoed data.
        let mut buffer = vec![0u8; data_len as usize].into_boxed_slice();
        sess.receive(&mut buffer, TIMEOUT).unwrap();

        // Verify echoed data.
        assert!(buffer.as_ref() == "hello world!".as_bytes());
    });

    thread_echo.join().unwrap();
    thread_check.join().unwrap();
}

/// Simulated timer.
struct MockTimer;

impl Timer for MockTimer {
    /// Get the timestamp from the std library.
    fn get_timestamp_ms(&mut self) -> u32 {
        SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_millis() as u32
    }
}

/// Simulated serial device. It is simulated with `crossbeam`'s MPMC queues.
struct MockSerial {
    send: Arc<Sender<u8>>,
    recv: Arc<Receiver<u8>>,
}

impl MockSerial {
    /// Get a pair of connected serial devices.
    fn new_pair() -> (Self, Self) {
        // Create channels to send in both directions.
        let (send0, recv0) = channel::unbounded();
        let (send1, recv1) = channel::unbounded();

        // Wrap the endpoints in `Arc`s.
        let send0 = Arc::new(send0);
        let send1 = Arc::new(send1);
        let recv0 = Arc::new(recv0);
        let recv1 = Arc::new(recv1);

        // Deliberately keep the reference counts to be always positive, so
        // that the channels will always be kept alive, otherwise we will get
        // channel disconnection errors.
        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>;

    /// Read a byte from the channel with a timeout.
    fn read_byte_with_timeout(
        &mut self,
        timeout_ms: u32,
    ) -> Result<u8, SerialError<Self::ReadError, Self::WriteError>> {
        // Read byte from the channel. MUST map the timeout error to the
        // specific `SerialError` variant because the protocol stack treats
        // timeout error in a special way as it is sometimes recoverable.
        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),
            })
    }

    /// Write a byte to the channel.
    fn write_byte(
        &mut self,
        byte: u8,
    ) -> Result<(), SerialError<Self::ReadError, Self::WriteError>> {
        self.send.send(byte).map_err(|e| SerialError::WriteError(e))
    }
}

依赖关系

~120KB