use crate::engine::reassembly::Reassembler;
use crate::engine::{BusProtocol, RawFrame};
use super::raw_can::{self, FastPacketSeq};
use super::{Codec, Event, Refused};
pub const PACKET_LEN: usize = 64;
pub(crate) const BITRATES: [u32; 13] = [
10_000, 20_000, 40_000, 50_000, 80_000, 100_000, 125_000, 200_000, 250_000, 400_000, 500_000,
800_000, 1_000_000,
];
const MESSAGES_PER_PACKET: usize = 3;
const MESSAGE_LEN: usize = 21;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct Message<'a> {
pub id: u32,
pub extended: bool,
pub data: &'a [u8],
}
pub(crate) fn messages(packet: &[u8; PACKET_LEN]) -> Result<Vec<Message<'_>>, usize> {
let count = usize::from(packet[0]);
if count > MESSAGES_PER_PACKET {
return Err(count);
}
Ok(packet[1..]
.as_chunks::<MESSAGE_LEN>()
.0
.iter()
.take(count)
.filter_map(|m| {
let id = u32::from_le_bytes([m[0], m[1], m[2], m[3]]);
let (remote, extended, len) = (m[10] != 0, m[11] != 0, usize::from(m[12]));
(!remote && len <= 8).then(|| Message {
id: id & if extended { 0x1FFF_FFFF } else { 0x7FF },
extended,
data: &m[13..13 + len],
})
})
.collect())
}
pub(crate) fn encode_packets(frames: &[Message<'_>], out: &mut Vec<u8>) {
for chunk in frames.chunks(MESSAGES_PER_PACKET) {
let start = out.len();
out.resize(start + PACKET_LEN, 0);
let packet = &mut out[start..];
packet[0] = chunk.len() as u8;
for (i, f) in chunk.iter().enumerate() {
debug_assert!(f.data.len() <= 8);
let m = &mut packet[1 + i * MESSAGE_LEN..1 + (i + 1) * MESSAGE_LEN];
m[0..4].copy_from_slice(&f.id.to_le_bytes());
m[11] = u8::from(f.extended);
m[12] = f.data.len() as u8;
m[13..13 + f.data.len()].copy_from_slice(f.data);
}
}
}
#[derive(Debug, Default)]
pub(crate) struct Packets {
partial: Vec<u8>,
}
impl Packets {
pub fn push(&mut self, mut bytes: &[u8], mut each: impl FnMut(&[u8; PACKET_LEN])) {
if !self.partial.is_empty() {
let take = (PACKET_LEN - self.partial.len()).min(bytes.len());
self.partial.extend_from_slice(&bytes[..take]);
bytes = &bytes[take..];
if self.partial.len() < PACKET_LEN {
return;
}
let packet: [u8; PACKET_LEN] = self.partial[..].try_into().expect("a whole packet");
self.partial.clear();
each(&packet);
}
let (packets, rest) = bytes.as_chunks::<PACKET_LEN>();
packets.iter().for_each(each);
self.partial.extend_from_slice(rest);
}
}
pub struct Canalyst {
packets: Packets,
protocol: BusProtocol,
reassembler: Reassembler,
seq: FastPacketSeq,
}
impl Default for Canalyst {
fn default() -> Self {
Self::new(BusProtocol::Nmea2000)
}
}
impl Canalyst {
pub fn new(protocol: BusProtocol) -> Self {
Self {
packets: Packets::default(),
protocol,
reassembler: Reassembler::new(),
seq: FastPacketSeq::default(),
}
}
}
impl Codec for Canalyst {
fn tick(&mut self, now_ms: u64, _events: &mut Vec<Event>) {
self.reassembler.tick(now_ms);
}
fn receive(&mut self, bytes: &[u8], now_ms: u64, events: &mut Vec<Event>) {
let (protocol, reassembler) = (self.protocol, &mut self.reassembler);
self.packets.push(bytes, |packet| match messages(packet) {
Ok(messages) => {
for m in messages {
raw_can::receive(
protocol,
reassembler,
m.id,
m.extended,
m.data,
Some(now_ms),
events,
);
}
}
Err(count) => events.push(Event::Error(format!(
"CANalyst packet claims {count} messages"
))),
});
}
fn send(&mut self, frame: &RawFrame) -> Result<Vec<u8>, Refused> {
let frames = raw_can::split(self.protocol, &mut self.seq, frame)?;
let messages: Vec<Message> = frames
.iter()
.map(|f| Message {
id: f.id,
extended: f.extended,
data: &f.data,
})
.collect();
let mut out = Vec::new();
encode_packets(&messages, &mut out);
Ok(out)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::engine::format_iso_ms;
const NOW: u64 = 1_780_082_164_826;
fn frame(pgn: u32, data: &[u8]) -> RawFrame {
RawFrame::new(None, 2, pgn, 0x23, 255, data.iter().copied())
}
fn receive(codec: &mut Canalyst, wire: &[u8]) -> Vec<Event> {
let mut events = Vec::new();
codec.receive(wire, NOW, &mut events);
events
}
#[test]
fn a_packet_carries_three_messages() {
let wire = Canalyst::default()
.send(&frame(129029, &(0u8..20).collect::<Vec<_>>()))
.unwrap();
assert_eq!(wire.len(), PACKET_LEN);
assert_eq!(wire[0], 3);
let first = &wire[1..1 + MESSAGE_LEN];
assert_eq!(
u32::from_le_bytes(first[0..4].try_into().unwrap()),
0x09F8_0523
);
assert_eq!(&first[8..13], &[0, 0, 0, 1, 8]);
assert_eq!(&first[13..], &[0x00, 20, 0, 1, 2, 3, 4, 5]);
}
#[test]
fn a_fast_packet_goes_out_in_frames_and_comes_back_whole() {
let sent = frame(129029, &(0u8..43).collect::<Vec<_>>());
let mut codec = Canalyst::default();
let wire = codec.send(&sent).unwrap();
assert_eq!(wire.len(), 3 * PACKET_LEN);
assert_eq!((wire[0], wire[PACKET_LEN], wire[2 * PACKET_LEN]), (3, 3, 1));
let events = receive(&mut codec, &wire);
let [Event::Frame(got)] = &events[..] else {
panic!("{events:?}");
};
assert_eq!(
(got.prio, got.pgn, got.src, got.dst, got.data.as_slice()),
(2, 129029, 0x23, 255, sent.data.as_slice())
);
assert_eq!(got.timestamp.as_deref(), Some(format_iso_ms(NOW).as_str()));
}
#[test]
fn a_single_frame_has_a_destination() {
let sent = RawFrame::new(None, 6, 59904, 3, 0x23, [0x14, 0xf0, 0x01]);
let mut codec = Canalyst::default();
let wire = codec.send(&sent).unwrap();
let events = receive(&mut codec, &wire);
let [Event::Frame(got)] = &events[..] else {
panic!("{events:?}");
};
assert_eq!(
(got.prio, got.pgn, got.src, got.dst, got.data.as_slice()),
(6, 59904, 3, 0x23, &[0x14, 0xf0, 0x01][..])
);
}
#[test]
fn a_packet_split_across_reads_is_joined() {
let mut codec = Canalyst::default();
let wire = codec
.send(&frame(127250, &[1, 2, 3, 4, 5, 6, 7, 8]))
.unwrap();
let mut events = receive(&mut codec, &wire[..10]);
assert!(events.is_empty());
events = receive(&mut codec, &wire[10..]);
assert!(matches!(&events[..], [Event::Frame(f)] if f.pgn == 127250));
}
#[test]
fn standard_and_remote_frames_are_skipped() {
let mut wire = Canalyst::default()
.send(&frame(127250, &[1, 2, 3, 4, 5, 6, 7, 8]))
.unwrap();
let mut standard = wire.clone();
standard[1 + 11] = 0;
wire[1 + 10] = 1;
wire.extend(standard);
assert!(receive(&mut Canalyst::default(), &wire).is_empty());
}
#[test]
fn a_quick_bus_carries_11_bit_frames_both_ways() {
let mut codec = Canalyst::new(BusProtocol::Quick);
let q = RawFrame::new(None, 0, 0x6c1, 0, 255, [0xc1, 0x18, 0x6a]);
let wire = codec.send(&q).unwrap();
let m = messages(wire[..PACKET_LEN].try_into().unwrap()).unwrap();
assert_eq!(m.len(), 1);
assert_eq!(
(m[0].id, m[0].extended, m[0].data),
(0x6c1, false, &[0xc1, 0x18, 0x6a][..])
);
let events = receive(&mut codec, &wire);
assert!(matches!(&events[..], [Event::Frame(f)]
if (f.prio, f.pgn, f.src, f.dst) == (0, 0x6c1, 0, 255)
&& f.data.as_slice() == [0xc1, 0x18, 0x6a]));
let n2k = Canalyst::default()
.send(&frame(127250, &[1, 2, 3, 4, 5, 6, 7, 8]))
.unwrap();
assert!(receive(&mut codec, &n2k).is_empty());
assert!(receive(&mut Canalyst::default(), &wire).is_empty());
let big = RawFrame::new(None, 0, 0x800, 0, 255, [0]);
assert_eq!(codec.send(&big), Err(Refused::TooLarge));
}
#[test]
fn a_bad_count_is_reported() {
let mut wire = vec![0u8; PACKET_LEN];
wire[0] = 4;
let events = receive(&mut Canalyst::default(), &wire);
assert!(matches!(&events[..], [Event::Error(_)]), "{events:?}");
}
#[test]
fn synthetic_and_oversized_frames_are_refused() {
let mut codec = Canalyst::default();
assert_eq!(codec.send(&frame(0x40100, &[1])), Err(Refused::Synthetic));
assert_eq!(
codec.send(&frame(129029, &[0; 224])),
Err(Refused::TooLarge)
);
assert_eq!(codec.send(&frame(59904, &[0; 9])), Err(Refused::TooLarge));
}
}