use crate::{
serial::{Serial, SerialError},
timer::Timer,
};
use static_assertions::const_assert;
pub(crate) enum LinkError<RE, WE> {
SerialReadErr(RE),
SerialWriteErr(WE),
Timeout,
Overrun,
}
impl<RE, WE> From<SerialError<RE, WE>> for LinkError<RE, WE> {
fn from(se: SerialError<RE, WE>) -> Self {
match se {
SerialError::ReadError(e) => LinkError::SerialReadErr(e),
SerialError::WriteError(e) => LinkError::SerialWriteErr(e),
SerialError::Timeout => LinkError::Timeout,
}
}
}
enum ReadByteResult {
Byte(u8),
Start,
Finish,
}
pub(crate) const MAX_FRAME_PAYLOAD_SIZE: usize = 64;
const PREAMBLE: u8 = 0xaa;
const POSTAMBLE: u8 = 0xcc;
const ESCAPE: u8 = 0x99;
const XOR_PATTERN: u8 = 0x88;
const fn need_escape(byte: u8) -> bool {
byte == PREAMBLE || byte == POSTAMBLE || byte == ESCAPE
}
#[allow(dead_code)]
const fn is_escape_sound() -> bool {
need_escape(PREAMBLE)
&& need_escape(POSTAMBLE)
&& need_escape(ESCAPE)
&& !need_escape(PREAMBLE ^ XOR_PATTERN)
&& !need_escape(POSTAMBLE ^ XOR_PATTERN)
&& !need_escape(ESCAPE ^ XOR_PATTERN)
}
const_assert!(is_escape_sound());
pub(crate) struct Link<S, T>
where
S: Serial,
T: Timer,
{
serial: S,
timer: T,
}
impl<S, T> Link<S, T>
where
S: Serial,
T: Timer,
{
pub(crate) const fn new(serial: S, timer: T) -> Self {
Self { serial, timer }
}
fn get_timeout_update_func(
&mut self,
timeout_ms: u32,
) -> impl Fn(&mut T) -> Result<u32, LinkError<S::ReadError, S::WriteError>>
where
T: Timer,
{
let create_time = self.timer.get_timestamp_ms();
move |timer| {
let cur_time = timer.get_timestamp_ms();
let elapsed_time = cur_time - create_time;
let remaining_time = timeout_ms.saturating_sub(elapsed_time);
if remaining_time == 0 {
Err(LinkError::Timeout)
} else {
Ok(remaining_time)
}
}
}
pub(crate) fn send_frame(
&mut self,
buffers: &[&[u8]],
) -> Result<(), LinkError<S::ReadError, S::WriteError>> {
let mut byte_cnt = 0usize;
self.serial.write_byte(PREAMBLE)?;
for &buffer in buffers {
for &(mut byte) in buffer {
if need_escape(byte) {
self.serial.write_byte(ESCAPE)?;
byte ^= XOR_PATTERN;
}
self.serial.write_byte(byte)?;
byte_cnt += 1;
if byte_cnt > MAX_FRAME_PAYLOAD_SIZE {
break;
}
}
if byte_cnt > MAX_FRAME_PAYLOAD_SIZE {
break;
}
}
self.serial.write_byte(POSTAMBLE)?;
if byte_cnt > MAX_FRAME_PAYLOAD_SIZE {
Err(LinkError::Overrun)
} else {
Ok(())
}
}
fn read_byte_with_timeout_remove_escape(
&mut self,
timeout_ms: u32,
) -> Result<ReadByteResult, LinkError<S::ReadError, S::WriteError>> {
let update_timeout = self.get_timeout_update_func(timeout_ms);
let mut byte = self.serial.read_byte_with_timeout(timeout_ms)?;
match byte {
PREAMBLE => return Ok(ReadByteResult::Start),
POSTAMBLE => return Ok(ReadByteResult::Finish),
ESCAPE => {}
byte => return Ok(ReadByteResult::Byte(byte)),
}
loop {
let remaining_timeout = update_timeout(&mut self.timer)?;
byte = self.serial.read_byte_with_timeout(remaining_timeout)?;
match byte {
ESCAPE => {}
POSTAMBLE => return Ok(ReadByteResult::Finish),
PREAMBLE => return Ok(ReadByteResult::Start),
byte => {
return Ok(ReadByteResult::Byte(byte ^ XOR_PATTERN));
}
}
}
}
pub(crate) fn receive_frame_with_timeout(
&mut self,
buffers: &mut [&mut [u8]],
timeout_ms: u32,
) -> Result<usize, LinkError<S::ReadError, S::WriteError>> {
let update_timeout = self.get_timeout_update_func(timeout_ms);
loop {
let remaining_timeout = update_timeout(&mut self.timer)?;
if let ReadByteResult::Start =
self.read_byte_with_timeout_remove_escape(remaining_timeout)?
{
break;
}
}
loop {
let mut byte_cnt = 0;
let mut restart = false;
for buffer in buffers.iter_mut() {
for byte_ref in buffer.iter_mut() {
let remaining_timeout = update_timeout(&mut self.timer)?;
match self.read_byte_with_timeout_remove_escape(remaining_timeout)? {
ReadByteResult::Byte(byte) => {
*byte_ref = byte;
byte_cnt += 1;
}
ReadByteResult::Start => restart = true,
ReadByteResult::Finish => return Ok(byte_cnt),
}
if byte_cnt == MAX_FRAME_PAYLOAD_SIZE || restart {
break;
}
}
if byte_cnt == MAX_FRAME_PAYLOAD_SIZE || restart {
break;
}
}
if restart {
continue;
}
while byte_cnt < MAX_FRAME_PAYLOAD_SIZE {
let remaining_timeout = update_timeout(&mut self.timer)?;
match self.read_byte_with_timeout_remove_escape(remaining_timeout)? {
ReadByteResult::Byte(_) => byte_cnt += 1,
ReadByteResult::Start => {
restart = true;
break;
}
ReadByteResult::Finish => return Ok(byte_cnt),
}
}
if restart {
continue;
}
let remaining_timeout = update_timeout(&mut self.timer)?;
match self.read_byte_with_timeout_remove_escape(remaining_timeout)? {
ReadByteResult::Finish => return Ok(byte_cnt),
ReadByteResult::Start => continue,
ReadByteResult::Byte(_) => return Err(LinkError::Overrun),
}
}
}
pub(crate) fn get_timer(&mut self) -> &mut T {
&mut self.timer
}
}