use crate::error::CanError;
use crate::id::CanId;
const MAX_FD_DATA: usize = 64;
pub fn len_to_dlc(len: usize) -> u8 {
match len {
0..=8 => len as u8,
9..=12 => 9,
13..=16 => 10,
17..=20 => 11,
21..=24 => 12,
25..=32 => 13,
33..=48 => 14,
_ => 15,
}
}
pub fn dlc_to_len(dlc: u8) -> usize {
match dlc & 0x0F {
small @ 0..=8 => small as usize,
9 => 12,
10 => 16,
11 => 20,
12 => 24,
13 => 32,
14 => 48,
_ => 64,
}
}
fn is_fd_length(len: usize) -> bool {
matches!(len, 0..=8 | 12 | 16 | 20 | 24 | 32 | 48 | 64)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Frame {
id: CanId,
data: [u8; MAX_FD_DATA],
len: usize,
fd: bool,
remote: bool,
}
impl Frame {
pub fn new(id: CanId, data: &[u8]) -> Result<Frame, CanError> {
if data.len() > 8 {
return Err(CanError::DataTooLong);
}
Ok(Self::store(id, data, false, false))
}
pub fn fd(id: CanId, data: &[u8]) -> Result<Frame, CanError> {
if data.len() > MAX_FD_DATA {
return Err(CanError::DataTooLong);
}
if !is_fd_length(data.len()) {
return Err(CanError::InvalidFdLength);
}
Ok(Self::store(id, data, true, false))
}
pub fn remote(id: CanId, len: usize) -> Frame {
Frame {
id,
data: [0; MAX_FD_DATA],
len: len.min(8),
fd: false,
remote: true,
}
}
fn store(id: CanId, data: &[u8], fd: bool, remote: bool) -> Frame {
let mut buf = [0; MAX_FD_DATA];
buf[..data.len()].copy_from_slice(data);
Frame {
id,
data: buf,
len: data.len(),
fd,
remote,
}
}
pub fn id(&self) -> CanId {
self.id
}
pub fn data(&self) -> &[u8] {
if self.remote {
&[]
} else {
&self.data[..self.len]
}
}
pub fn signals(&self) -> Option<crate::Signals> {
let bytes: [u8; 8] = self.data().try_into().ok()?;
Some(crate::Signals::from_bytes(bytes))
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn dlc(&self) -> u8 {
len_to_dlc(self.len)
}
pub fn is_fd(&self) -> bool {
self.fd
}
pub fn is_remote(&self) -> bool {
self.remote
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_classic_frame_holds_its_data() {
let frame = Frame::new(CanId::standard(0x100), &[1, 2, 3, 4]).unwrap();
assert_eq!(frame.data(), &[1, 2, 3, 4]);
assert_eq!(frame.len(), 4);
assert_eq!(frame.dlc(), 4);
assert!(!frame.is_fd());
assert!(!frame.is_remote());
}
#[test]
fn a_classic_frame_rejects_more_than_eight_bytes() {
assert_eq!(
Frame::new(CanId::standard(0x100), &[0; 9]),
Err(CanError::DataTooLong)
);
}
#[test]
fn an_fd_frame_carries_up_to_sixty_four_bytes() {
let frame = Frame::fd(CanId::extended(0x1234), &[0xAB; 64]).unwrap();
assert_eq!(frame.len(), 64);
assert_eq!(frame.dlc(), 15);
assert!(frame.is_fd());
}
#[test]
fn an_fd_frame_rejects_a_length_it_cannot_carry() {
assert_eq!(
Frame::fd(CanId::standard(0x1), &[0; 9]),
Err(CanError::InvalidFdLength)
);
}
#[test]
fn an_fd_frame_rejects_more_than_sixty_four_bytes() {
assert_eq!(
Frame::fd(CanId::standard(0x1), &[0; 65]),
Err(CanError::DataTooLong)
);
}
#[test]
fn a_remote_frame_requests_a_length_and_carries_no_data() {
let frame = Frame::remote(CanId::standard(0x200), 8);
assert!(frame.is_remote());
assert_eq!(frame.data(), &[]);
assert_eq!(frame.len(), 8);
assert_eq!(frame.dlc(), 8);
}
#[test]
fn the_dlc_encoding_round_trips_at_each_step() {
for &len in &[0usize, 1, 8, 12, 16, 20, 24, 32, 48, 64] {
assert_eq!(dlc_to_len(len_to_dlc(len)), len);
}
}
#[test]
fn a_length_between_steps_rounds_up() {
assert_eq!(len_to_dlc(9), 9); assert_eq!(dlc_to_len(9), 12);
assert_eq!(len_to_dlc(33), 14); assert_eq!(dlc_to_len(14), 48);
}
#[test]
fn a_zero_length_fd_frame_is_valid() {
let frame = Frame::fd(CanId::standard(0x1), &[]).unwrap();
assert!(frame.is_fd());
assert!(frame.is_empty());
assert_eq!(frame.len(), 0);
assert_eq!(frame.dlc(), 0);
assert_eq!(frame.data(), &[]);
}
#[test]
fn a_classic_frame_can_be_empty() {
let frame = Frame::new(CanId::standard(0x1), &[]).unwrap();
assert!(frame.is_empty());
assert_eq!(frame.dlc(), 0);
}
}