use smallvec::SmallVec;
use super::common::{iso11783_compose, iso11783_decompose};
use crate::engine::fastpacket;
use crate::engine::{FASTPACKET_MAX_SIZE, FramePacketType, RAWFRAME_MAX_SIZE, RawFrame};
pub const BST_D0: u8 = 0xD0;
const DLE: u8 = 0x10;
const STX: u8 = 0x02;
const ETX: u8 = 0x03;
const HEADER_LEN: usize = 13;
pub const MAX_DATA_LEN: usize = RAWFRAME_MAX_SIZE;
const MAX_COLLECT: usize = HEADER_LEN + MAX_DATA_LEN + 1;
const MAX_PGN: u32 = 0x3ffff;
const C_TO_BUS: u8 = 1 << 3;
pub fn encode_frame(frame: &RawFrame) -> Option<Vec<u8>> {
let n = frame.data.len();
if n > MAX_DATA_LEN || frame.pgn > MAX_PGN {
return None;
}
let message_type = match fastpacket::packet_type(frame.pgn) {
_ if n > FASTPACKET_MAX_SIZE => 2,
FramePacketType::Fast => 1,
_ if n > 8 => 2,
_ => 0,
};
let canid = iso11783_compose(frame.prio, frame.pgn, frame.src, frame.dst);
let mut m = Vec::with_capacity(HEADER_LEN + n + 1);
m.push(BST_D0);
m.extend_from_slice(&((HEADER_LEN + n) as u16).to_le_bytes());
m.push(frame.dst);
m.extend_from_slice(&canid.to_le_bytes());
m.push(message_type | C_TO_BUS);
m.extend_from_slice(&[0, 0, 0, 0]);
m.extend_from_slice(&frame.data);
m.push(0u8.wrapping_sub(m.iter().fold(0u8, |s, &b| s.wrapping_add(b))));
let mut out = Vec::with_capacity(m.len() + 8);
out.extend_from_slice(&[DLE, STX]);
for b in m {
out.push(b);
if b == DLE {
out.push(DLE);
}
}
out.extend_from_slice(&[DLE, ETX]);
Some(out)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
enum State {
#[default]
Idle,
IdleDle,
InFrame,
InFrameDle,
}
#[derive(Debug, Default)]
pub struct BstD0Decoder {
state: State,
buf: Vec<u8>,
wire: u64,
pub skipped: u64,
}
impl BstD0Decoder {
pub fn new() -> Self {
Self::default()
}
pub fn push_bytes(&mut self, bytes: &[u8]) -> Vec<RawFrame> {
let mut frames = Vec::new();
for &b in bytes {
if let Some(f) = self.push_byte(b) {
frames.push(f);
}
}
frames
}
fn push_byte(&mut self, b: u8) -> Option<RawFrame> {
match self.state {
State::Idle => {
self.skipped += 1;
if b == DLE {
self.state = State::IdleDle;
}
}
State::IdleDle => match b {
STX => {
self.skipped -= 1;
self.start();
}
DLE => self.skipped += 1,
_ => {
self.skipped += 1;
self.state = State::Idle;
}
},
State::InFrame => {
self.wire += 1;
if b == DLE {
self.state = State::InFrameDle;
} else {
self.collect(b);
}
}
State::InFrameDle => {
self.wire += 1;
match b {
DLE => {
self.state = State::InFrame;
self.collect(DLE);
}
ETX => {
let frame = to_frame(&self.buf);
if frame.is_none() {
self.skipped += self.wire;
}
self.state = State::Idle;
self.buf.clear();
return frame;
}
STX => {
self.skipped += self.wire - 2;
self.start();
}
_ => self.drop_frame(),
}
}
}
None
}
fn start(&mut self) {
self.state = State::InFrame;
self.buf.clear();
self.wire = 2;
}
fn collect(&mut self, b: u8) {
if self.buf.len() >= MAX_COLLECT {
self.drop_frame();
} else {
self.buf.push(b);
}
}
fn drop_frame(&mut self) {
self.skipped += self.wire;
self.state = State::Idle;
self.buf.clear();
}
}
pub(crate) fn to_frame(m: &[u8]) -> Option<RawFrame> {
if m.len() < HEADER_LEN + 1 || m[0] != BST_D0 {
return None;
}
let len = u16::from_le_bytes([m[1], m[2]]) as usize;
if len < HEADER_LEN || len + 1 != m.len() || len - HEADER_LEN > MAX_DATA_LEN {
return None;
}
if m.iter().fold(0u8, |s, &b| s.wrapping_add(b)) != 0 {
return None;
}
let canid = u32::from_le_bytes([m[4], m[5], m[6], m[7]]) & 0x1fff_ffff;
let (prio, pgn, src, dst) = iso11783_decompose(canid);
Some(RawFrame {
timestamp: None,
prio,
pgn,
src,
dst,
data: SmallVec::from_slice(&m[HEADER_LEN..len]),
})
}
#[cfg(test)]
mod tests {
use super::*;
fn message(dst: u8, canid: u32, data: &[u8]) -> Vec<u8> {
let mut m = vec![BST_D0];
m.extend_from_slice(&((HEADER_LEN + data.len()) as u16).to_le_bytes());
m.push(dst);
m.extend_from_slice(&canid.to_le_bytes());
m.push(0); m.extend_from_slice(&0x1234_5678u32.to_le_bytes());
m.extend_from_slice(data);
let sum = m.iter().fold(0u8, |s, &b| s.wrapping_add(b));
m.push(0u8.wrapping_sub(sum));
let mut out = vec![DLE, STX];
for b in m {
out.push(b);
if b == DLE {
out.push(DLE);
}
}
out.extend_from_slice(&[DLE, ETX]);
out
}
const POSITION: u32 = 0x09F8_0103;
const POSITION_DATA: [u8; 8] = [0x50, 0x9b, 0x3a, 0x1e, 0x2e, 0xd7, 0x0a, 0x03];
fn frame(canid: u32, dst: u8, data: &[u8]) -> RawFrame {
let (prio, pgn, src, _) = iso11783_decompose(canid);
RawFrame {
timestamp: None,
prio,
pgn,
src,
dst,
data: SmallVec::from_slice(data),
}
}
#[test]
fn decodes_a_message() {
let frames = BstD0Decoder::new().push_bytes(&message(255, POSITION, &POSITION_DATA));
assert_eq!(frames.len(), 1);
let f = &frames[0];
assert_eq!((f.prio, f.pgn, f.src, f.dst), (2, 129025, 3, 255));
assert_eq!(f.data.as_slice(), &POSITION_DATA);
}
#[test]
fn a_pdu1_destination_comes_from_the_can_id() {
let frames =
BstD0Decoder::new().push_bytes(&message(0x1f, 0x18EA_1F03, &[0x14, 0xf0, 0x01]));
let f = &frames[0];
assert_eq!((f.prio, f.pgn, f.src, f.dst), (6, 59904, 3, 0x1f));
}
#[test]
fn data_page_one() {
let frames = BstD0Decoder::new().push_bytes(&message(255, 0x09FD_0823, &[1, 2, 3]));
assert_eq!((frames[0].prio, frames[0].pgn), (2, 130312));
}
#[test]
fn dles_are_undoubled() {
let canid = 0x09F8_1010;
let data = [DLE; 16];
let bytes = message(255, canid, &data);
assert!(bytes.windows(2).filter(|w| *w == [DLE, DLE]).count() >= 18);
let frames = BstD0Decoder::new().push_bytes(&bytes);
assert_eq!(frames.len(), 1);
assert_eq!((frames[0].pgn, frames[0].src), (129040, 0x10));
assert_eq!(frames[0].data.as_slice(), &data);
}
#[test]
fn a_long_message() {
let data: Vec<u8> = (0u8..=200).collect();
let frames = BstD0Decoder::new().push_bytes(&message(255, 0x0DF8_0503, &data));
assert_eq!(frames[0].pgn, 129029);
assert_eq!(frames[0].data.as_slice(), data.as_slice());
}
#[test]
fn a_message_split_across_reads() {
let mut stream = message(255, POSITION, &POSITION_DATA);
stream.extend(message(255, 0x09F8_1010, &[DLE; 8]));
for cut in 0..stream.len() {
let mut d = BstD0Decoder::new();
let mut frames = d.push_bytes(&stream[..cut]);
frames.extend(d.push_bytes(&stream[cut..]));
assert_eq!(frames.len(), 2, "cut at {cut}");
assert_eq!(d.skipped, 0, "cut at {cut}");
}
}
#[test]
fn drops_bad_frames_and_resyncs() {
let good = message(255, 0x18EA_1F03, &[0x14, 0xf0, 0x01]);
let mut bad_checksum = message(255, POSITION, &POSITION_DATA);
let n = bad_checksum.len();
bad_checksum[n - 3] ^= 0x01;
let mut bad_length = message(255, POSITION, &POSITION_DATA);
bad_length[3] += 1;
let mut bad_escape = message(255, POSITION, &POSITION_DATA);
bad_escape.insert(8, DLE);
bad_escape.insert(9, 0x55);
let cut_short = message(255, POSITION, &POSITION_DATA)[..10].to_vec();
let mut stream = vec![0xaa, DLE, 0x55];
for bad in [&bad_checksum, &bad_length, &bad_escape, &cut_short] {
stream.extend(bad);
stream.extend(&good);
}
let mut d = BstD0Decoder::new();
let frames = d.push_bytes(&stream);
assert_eq!(frames.len(), 4);
assert!(frames.iter().all(|f| f.pgn == 59904));
assert!(d.skipped >= 3 + bad_checksum.len() as u64 + bad_length.len() as u64);
}
#[test]
fn other_bst_messages_are_skipped() {
let mut bem = vec![0xA0u8, 3, 0x11, 0x02, 0x00];
let sum = bem.iter().fold(0u8, |s, &b| s.wrapping_add(b));
bem.push(0u8.wrapping_sub(sum));
let mut stream = vec![DLE, STX];
stream.extend(&bem);
stream.extend([DLE, ETX]);
stream.extend(message(255, POSITION, &POSITION_DATA));
let mut d = BstD0Decoder::new();
let frames = d.push_bytes(&stream);
assert_eq!(frames.len(), 1);
assert_eq!(d.skipped, 10);
}
#[test]
fn encode_round_trips() {
for (canid, dst, data, message_type) in [
(POSITION, 255u8, POSITION_DATA.to_vec(), 0u8),
(0x18EA_2303, 0x23, vec![0x14, 0xf0, 0x01], 0),
(0x0DF8_0503, 255, (0u8..43).collect(), 1),
(0x19F0_1410, 255, vec![DLE; 4], 1),
(0x19F0_1410, 255, vec![0x55; 300], 2),
] {
let f = frame(canid, dst, &data);
let bytes = encode_frame(&f).unwrap();
let mut d = BstD0Decoder::new();
assert_eq!(d.push_bytes(&bytes), vec![f.clone()]);
assert_eq!(d.skipped, 0);
let mut m = Vec::new();
let mut i = 2;
while i < bytes.len() - 2 {
m.push(bytes[i]);
i += if bytes[i] == DLE { 2 } else { 1 };
}
assert_eq!(m[8], message_type | C_TO_BUS, "{:x}", f.pgn);
assert_eq!(u16::from_le_bytes([m[1], m[2]]) as usize, 13 + data.len());
}
}
#[test]
fn an_oversized_message_is_dropped() {
let big = message(255, 0x0DF8_0503, &vec![0x55; MAX_DATA_LEN + 1]);
let mut stream = big.clone();
stream.extend(message(255, POSITION, &POSITION_DATA));
let mut d = BstD0Decoder::new();
let frames = d.push_bytes(&stream);
assert_eq!(frames.len(), 1);
assert_eq!(frames[0].pgn, 129025);
assert_eq!(d.skipped, big.len() as u64);
let max = message(255, 0x0DF8_0503, &vec![0x55; MAX_DATA_LEN]);
assert_eq!(
BstD0Decoder::new().push_bytes(&max)[0].data.len(),
MAX_DATA_LEN
);
assert!(encode_frame(&frame(0x0DF8_0503, 255, &[0x55; MAX_DATA_LEN])).is_some());
}
#[test]
fn a_pgn_beyond_18_bits_is_refused() {
let mut f = frame(POSITION, 255, &POSITION_DATA);
f.pgn = 0x40000;
assert!(encode_frame(&f).is_none());
f.pgn = 0x3ffff;
assert!(encode_frame(&f).is_some());
}
#[test]
fn noise_is_not_kept() {
let mut d = BstD0Decoder::new();
for _ in 0..100 {
assert!(d.push_bytes(&[0x55; 1000]).is_empty());
}
assert!(d.buf.is_empty());
assert_eq!(d.skipped, 100_000);
}
}