use embedded_io_async::{Read, Write};
use super::driver::SplitDriverError;
use crate::split::driver::{PeripheralManager, SplitReader, SplitWriter, set_peripheral_connected};
use crate::split::{SPLIT_MESSAGE_MAX_SIZE, SplitMessage};
pub(crate) async fn run_serial_peripheral_manager<S: Read + Write>(
id: usize,
receiver: S,
matrix_config: crate::split::PeripheralMatrixConfig,
#[cfg(feature = "dfu_split")] policy: crate::split::driver::UpdatePolicy,
) {
let split_serial_driver: SerialSplitDriver<S> = SerialSplitDriver::new(receiver);
let peripheral_manager = PeripheralManager::new(
split_serial_driver,
id,
matrix_config,
#[cfg(feature = "dfu_split")]
policy,
);
info!("Running peripheral manager {}", id);
set_peripheral_connected(id, true);
peripheral_manager.run().await;
}
pub(crate) struct SerialSplitDriver<S> {
serial: S,
buffer: [u8; SPLIT_MESSAGE_MAX_SIZE],
n_bytes_part: usize,
}
impl<S> SerialSplitDriver<S> {
pub(crate) fn new(serial: S) -> Self {
Self {
serial,
buffer: [0_u8; SPLIT_MESSAGE_MAX_SIZE],
n_bytes_part: 0,
}
}
}
impl<S: Read> SplitReader for SerialSplitDriver<S> {
async fn read(&mut self) -> Result<SplitMessage, SplitDriverError> {
const SENTINEL: u8 = 0x00;
while !self.buffer[..self.n_bytes_part].contains(&SENTINEL) && self.n_bytes_part < self.buffer.len() {
let n_bytes = self
.serial
.read(&mut self.buffer[self.n_bytes_part..])
.await
.map_err(|_e| {
self.n_bytes_part = 0;
SplitDriverError::SerialError
})?;
if n_bytes == 0 {
return Err(SplitDriverError::EmptyMessage);
}
debug_assert!(self.n_bytes_part + n_bytes <= self.buffer.len());
self.n_bytes_part += n_bytes;
}
let (result, n_bytes_unused) =
match postcard::take_from_bytes_cobs::<SplitMessage>(&mut self.buffer[..self.n_bytes_part]) {
Ok((message, unused_bytes)) => (Ok(message), unused_bytes.len()),
Err(e) => {
error!("Postcard deserialize split message error: {}", e);
let n_bytes_unused = self.buffer[..self.n_bytes_part]
.iter()
.position(|&x| x == SENTINEL)
.map_or(0, |index| self.n_bytes_part - index - 1);
(Err(SplitDriverError::SerializeError), n_bytes_unused)
}
};
self.buffer
.copy_within(self.n_bytes_part - n_bytes_unused..self.n_bytes_part, 0);
self.n_bytes_part = n_bytes_unused;
result
}
}
impl<S: Write> SplitWriter for SerialSplitDriver<S> {
async fn write(&mut self, message: &SplitMessage) -> Result<usize, SplitDriverError> {
let mut buf = [0_u8; SPLIT_MESSAGE_MAX_SIZE];
let bytes = postcard::to_slice_cobs(message, &mut buf).map_err(|e| {
error!("Postcard serialize split message error: {}", e);
SplitDriverError::SerializeError
})?;
let mut remaining_bytes = bytes.len();
while remaining_bytes > 0 {
let sent_bytes = self
.serial
.write(&bytes[bytes.len() - remaining_bytes..])
.await
.map_err(|_e| SplitDriverError::SerialError)?;
remaining_bytes -= sent_bytes;
}
Ok(bytes.len())
}
}
#[cfg(test)]
mod tests {
use std::collections::VecDeque;
use std::convert::Infallible;
use embassy_futures::block_on;
use embedded_io_async::ErrorType;
use super::*;
struct FakeSerial {
chunks: VecDeque<Vec<u8>>,
read_calls: usize,
}
impl FakeSerial {
fn new<I: IntoIterator<Item = Vec<u8>>>(chunks: I) -> Self {
Self {
chunks: chunks.into_iter().collect(),
read_calls: 0,
}
}
}
impl ErrorType for FakeSerial {
type Error = Infallible;
}
impl Read for FakeSerial {
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
self.read_calls += 1;
let chunk = self
.chunks
.pop_front()
.expect("SerialSplitDriver made an unexpected underlying read() call");
assert!(
chunk.len() <= buf.len(),
"scripted chunk larger than driver's read slice"
);
buf[..chunk.len()].copy_from_slice(&chunk);
Ok(chunk.len())
}
}
fn encode(msg: &SplitMessage) -> Vec<u8> {
let mut buf = [0u8; SPLIT_MESSAGE_MAX_SIZE];
let encoded = postcard::to_slice_cobs(msg, &mut buf).unwrap();
encoded.to_vec()
}
#[test]
fn read_single_message_in_one_chunk() {
let fake = FakeSerial::new([encode(&SplitMessage::LedState(true))]);
let mut drv = SerialSplitDriver::new(fake);
let msg = block_on(drv.read()).expect("read should succeed");
assert!(matches!(msg, SplitMessage::LedState(true)));
assert_eq!(drv.serial.read_calls, 1);
}
#[test]
fn read_message_split_across_chunks() {
let bytes = encode(&SplitMessage::LedState(true));
let (a, b) = bytes.split_at(bytes.len() / 2);
let fake = FakeSerial::new([a.to_vec(), b.to_vec()]);
let mut drv = SerialSplitDriver::new(fake);
let msg = block_on(drv.read()).expect("read should succeed");
assert!(matches!(msg, SplitMessage::LedState(true)));
assert_eq!(drv.serial.read_calls, 2);
}
#[test]
fn two_bundled_messages_do_not_trigger_extra_read() {
let status = rmk_types::connection::ConnectionStatus::new();
let mut bundled = encode(&SplitMessage::LedState(true));
bundled.extend_from_slice(&encode(&SplitMessage::ConnectionStatus(status)));
let fake = FakeSerial::new([bundled]);
let mut drv = SerialSplitDriver::new(fake);
let m1 = block_on(drv.read()).expect("first read should succeed");
assert!(matches!(m1, SplitMessage::LedState(true)));
let m2 = block_on(drv.read()).expect("second read should not touch serial");
match m2 {
SplitMessage::ConnectionStatus(s) => assert_eq!(s, status),
other => panic!("expected ConnectionStatus, got {:?}", other),
}
assert_eq!(drv.serial.read_calls, 1);
}
#[test]
fn trailing_partial_message_is_carried_over() {
let status = rmk_types::connection::ConnectionStatus::new();
let full1 = encode(&SplitMessage::LedState(true));
let full2 = encode(&SplitMessage::ConnectionStatus(status));
let (prefix, suffix) = full2.split_at(full2.len() / 2);
let mut first_chunk = full1;
first_chunk.extend_from_slice(prefix);
let fake = FakeSerial::new([first_chunk, suffix.to_vec()]);
let mut drv = SerialSplitDriver::new(fake);
let m1 = block_on(drv.read()).expect("first read should succeed");
assert!(matches!(m1, SplitMessage::LedState(true)));
let m2 = block_on(drv.read()).expect("second read should succeed");
match m2 {
SplitMessage::ConnectionStatus(s) => assert_eq!(s, status),
other => panic!("expected ConnectionStatus, got {:?}", other),
}
assert_eq!(drv.serial.read_calls, 2);
}
}