use alloc::vec::Vec;
use core::marker::PhantomData;
use heapless::Deque;
use zerocopy::IntoBytes;
use canadensis_core::time::Clock;
use canadensis_core::transfer::Transfer;
use canadensis_core::transport::Transmitter;
use canadensis_core::{nb, OutOfMemoryError};
use canadensis_header::{Header, RawHeader};
use crate::driver::TransmitDriver;
use crate::SerialTransport;
use crate::{cobs, Error};
const PER_FRAME_ESCAPED_OVERHEAD: usize = canadensis_header::SIZE + 4;
const PER_FRAME_UNESCAPED_OVERHEAD: usize = 1 + 1;
const DELIMITER: u8 = 0x0;
pub struct SerialTransmitter<D, const C: usize> {
queue: TransmitQueue<C>,
_driver: PhantomData<D>,
}
impl<D, const C: usize> SerialTransmitter<D, C> {
pub fn new() -> Self {
SerialTransmitter {
queue: TransmitQueue::new(),
_driver: PhantomData,
}
}
}
impl<D, const C: usize> Default for SerialTransmitter<D, C> {
fn default() -> Self {
SerialTransmitter::new()
}
}
impl<L, D, const C: usize> Transmitter<L> for SerialTransmitter<D, C>
where
L: Clock,
D: TransmitDriver,
{
type Transport = SerialTransport;
type Driver = D;
type Error = Error<D::Error>;
fn push<A>(
&mut self,
transfer: Transfer<A, Self::Transport>,
_clock: &mut L,
_driver: &mut D,
) -> nb::Result<(), Self::Error>
where
A: AsRef<[u8]>,
{
let frame_length = transfer.payload.as_ref().len() + PER_FRAME_ESCAPED_OVERHEAD;
let escaped_length = cobs::escaped_size(frame_length);
let length_on_wire = escaped_length + PER_FRAME_UNESCAPED_OVERHEAD;
if length_on_wire > (self.queue.capacity() - self.queue.len()) {
return Err(nb::Error::Other(Error::Memory(OutOfMemoryError)));
}
let header = RawHeader::from(Header::from(transfer.header));
let payload_crc = crate::make_payload_crc(transfer.payload.as_ref());
let mut escape_buffer: Vec<u8> = Vec::new();
escape_buffer
.try_reserve_exact(escaped_length)
.map_err(|e| Error::Memory(OutOfMemoryError::from(e)))?;
escape_buffer.resize(escaped_length, 0);
let data_to_escape = header
.as_bytes()
.iter()
.copied()
.chain(transfer.payload.as_ref().iter().copied())
.chain(IntoIterator::into_iter(payload_crc.to_le_bytes()));
let escaped_length = cobs::escape_from_iter(data_to_escape, &mut escape_buffer)
.expect("Incorrect escaped length");
let length_on_wire = escaped_length + PER_FRAME_UNESCAPED_OVERHEAD;
if length_on_wire > (self.queue.capacity() - self.queue.len()) {
return Err(nb::Error::Other(Error::Memory(OutOfMemoryError)));
}
self.queue.push_back(DELIMITER).unwrap();
for &byte in &escape_buffer[..escaped_length] {
self.queue.push_back(byte).unwrap();
}
self.queue.push_back(DELIMITER).unwrap();
Ok(())
}
fn flush(&mut self, _clock: &mut L, driver: &mut D) -> nb::Result<(), Self::Error> {
while let Some(byte) = self.queue.pop_front() {
match driver.send_byte(byte) {
Ok(()) => {}
Err(e) => {
self.queue
.push_front(byte)
.expect("No space to return byte to queue");
return match e {
nb::Error::WouldBlock => Err(nb::Error::WouldBlock),
nb::Error::Other(e) => Err(nb::Error::Other(Error::Driver(e))),
};
}
}
}
Ok(())
}
fn mtu(&self) -> usize {
usize::MAX
}
}
struct TransmitQueue<const C: usize>(Deque<u8, C>);
impl<const C: usize> TransmitQueue<C> {
fn new() -> Self {
TransmitQueue(Deque::new())
}
fn push_back(&mut self, item: u8) -> Result<(), OutOfMemoryError> {
self.0.push_back(item).map_err(|_| OutOfMemoryError)
}
fn capacity(&self) -> usize {
self.0.capacity()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn pop_front(&mut self) -> Option<u8> {
self.0.pop_front()
}
pub fn push_front(&mut self, item: u8) -> Result<(), OutOfMemoryError> {
self.0.push_front(item).map_err(|_| OutOfMemoryError)
}
}