pub const MAX_FD_DATA_LEN: usize = 64;
#[inline]
pub const fn dlc_to_len(dlc: u8) -> usize {
match dlc {
0..=8 => dlc as usize,
9 => 12,
10 => 16,
11 => 20,
12 => 24,
13 => 32,
14 => 48,
15 => 64,
_ => 64, }
}
#[inline]
pub const fn len_to_dlc(len: usize) -> u8 {
match len {
0 => 0,
1 => 1,
2 => 2,
3 => 3,
4 => 4,
5 => 5,
6 => 6,
7 => 7,
8 => 8,
9..=12 => 9,
13..=16 => 10,
17..=20 => 11,
21..=24 => 12,
25..=32 => 13,
33..=48 => 14,
_ => 15, }
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct FdFlags {
brs: bool,
esi: bool,
}
impl FdFlags {
#[inline]
pub const fn new(brs: bool, esi: bool) -> Self {
Self { brs, esi }
}
#[inline]
pub const fn from_byte(byte: u8) -> Self {
Self {
brs: byte & 0x01 != 0,
esi: byte & 0x02 != 0,
}
}
#[inline]
pub const fn to_byte(self) -> u8 {
(self.brs as u8) | ((self.esi as u8) << 1)
}
#[inline]
pub const fn brs(&self) -> bool {
self.brs
}
#[inline]
pub const fn esi(&self) -> bool {
self.esi
}
}
#[cfg(feature = "can")]
pub trait FdFrame: Sized {
fn new_fd(id: impl Into<embedded_can::Id>, data: &[u8], flags: FdFlags) -> Option<Self>;
fn is_fd(&self) -> bool;
fn fd_flags(&self) -> FdFlags;
fn id(&self) -> embedded_can::Id;
fn dlc(&self) -> usize;
fn data_len(&self) -> usize {
if self.is_fd() {
dlc_to_len(self.dlc() as u8)
} else {
self.dlc().min(8)
}
}
fn data(&self) -> &[u8];
fn is_extended(&self) -> bool {
matches!(self.id(), embedded_can::Id::Extended(_))
}
fn is_standard(&self) -> bool {
!self.is_extended()
}
}
#[cfg(feature = "can")]
#[derive(Debug, Clone)]
pub struct SimpleFdFrame {
id: embedded_can::Id,
data: [u8; MAX_FD_DATA_LEN],
len: usize,
flags: FdFlags,
is_fd: bool,
}
#[cfg(feature = "can")]
impl SimpleFdFrame {
pub fn new_classic(id: impl Into<embedded_can::Id>, data: &[u8]) -> Option<Self> {
if data.len() > 8 {
return None;
}
let mut frame_data = [0u8; MAX_FD_DATA_LEN];
frame_data[..data.len()].copy_from_slice(data);
Some(Self {
id: id.into(),
data: frame_data,
len: data.len(),
flags: FdFlags::default(),
is_fd: false,
})
}
pub fn new_fd_frame(
id: impl Into<embedded_can::Id>,
data: &[u8],
flags: FdFlags,
) -> Option<Self> {
if data.len() > MAX_FD_DATA_LEN {
return None;
}
let mut frame_data = [0u8; MAX_FD_DATA_LEN];
frame_data[..data.len()].copy_from_slice(data);
Some(Self {
id: id.into(),
data: frame_data,
len: data.len(),
flags,
is_fd: true,
})
}
}
#[cfg(feature = "can")]
impl FdFrame for SimpleFdFrame {
fn new_fd(id: impl Into<embedded_can::Id>, data: &[u8], flags: FdFlags) -> Option<Self> {
Self::new_fd_frame(id, data, flags)
}
fn is_fd(&self) -> bool {
self.is_fd
}
fn fd_flags(&self) -> FdFlags {
self.flags
}
fn id(&self) -> embedded_can::Id {
self.id
}
fn dlc(&self) -> usize {
len_to_dlc(self.len) as usize
}
fn data(&self) -> &[u8] {
&self.data[..self.len]
}
}
#[cfg(feature = "can")]
impl embedded_can::Frame for SimpleFdFrame {
fn new(id: impl Into<embedded_can::Id>, data: &[u8]) -> Option<Self> {
Self::new_classic(id, data)
}
fn new_remote(id: impl Into<embedded_can::Id>, dlc: usize) -> Option<Self> {
if dlc > 8 {
return None;
}
Some(Self {
id: id.into(),
data: [0u8; MAX_FD_DATA_LEN],
len: dlc,
flags: FdFlags::default(),
is_fd: false,
})
}
fn is_extended(&self) -> bool {
matches!(self.id, embedded_can::Id::Extended(_))
}
fn is_remote_frame(&self) -> bool {
false }
fn id(&self) -> embedded_can::Id {
self.id
}
fn dlc(&self) -> usize {
self.len.min(8)
}
fn data(&self) -> &[u8] {
&self.data[..self.len.min(8)]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dlc_to_len() {
assert_eq!(dlc_to_len(0), 0);
assert_eq!(dlc_to_len(8), 8);
assert_eq!(dlc_to_len(9), 12);
assert_eq!(dlc_to_len(10), 16);
assert_eq!(dlc_to_len(11), 20);
assert_eq!(dlc_to_len(12), 24);
assert_eq!(dlc_to_len(13), 32);
assert_eq!(dlc_to_len(14), 48);
assert_eq!(dlc_to_len(15), 64);
}
#[test]
fn test_len_to_dlc() {
assert_eq!(len_to_dlc(0), 0);
assert_eq!(len_to_dlc(8), 8);
assert_eq!(len_to_dlc(12), 9);
assert_eq!(len_to_dlc(16), 10);
assert_eq!(len_to_dlc(20), 11);
assert_eq!(len_to_dlc(24), 12);
assert_eq!(len_to_dlc(32), 13);
assert_eq!(len_to_dlc(48), 14);
assert_eq!(len_to_dlc(64), 15);
assert_eq!(len_to_dlc(10), 9); assert_eq!(len_to_dlc(50), 15); }
#[test]
fn test_fd_flags() {
let flags = FdFlags::new(true, false);
assert!(flags.brs());
assert!(!flags.esi());
assert_eq!(flags.to_byte(), 0x01);
let flags = FdFlags::new(false, true);
assert!(!flags.brs());
assert!(flags.esi());
assert_eq!(flags.to_byte(), 0x02);
let flags = FdFlags::new(true, true);
assert_eq!(flags.to_byte(), 0x03);
let flags = FdFlags::from_byte(0x03);
assert!(flags.brs());
assert!(flags.esi());
}
#[cfg(feature = "can")]
#[test]
fn test_simple_fd_frame_classic() {
use embedded_can::StandardId;
let id = StandardId::new(0x100).unwrap();
let frame = SimpleFdFrame::new_classic(id, &[1, 2, 3, 4, 5, 6, 7, 8]).unwrap();
assert!(!frame.is_fd());
assert_eq!(frame.data(), &[1, 2, 3, 4, 5, 6, 7, 8]);
assert!(!frame.fd_flags().brs());
assert!(!frame.fd_flags().esi());
}
#[cfg(feature = "can")]
#[test]
fn test_simple_fd_frame_fd() {
use embedded_can::StandardId;
let id = StandardId::new(0x100).unwrap();
let data = [0xAAu8; 32];
let flags = FdFlags::new(true, false);
let frame = SimpleFdFrame::new_fd_frame(id, &data, flags).unwrap();
assert!(frame.is_fd());
assert_eq!(frame.data().len(), 32);
assert!(frame.fd_flags().brs());
assert!(!frame.fd_flags().esi());
}
#[cfg(feature = "can")]
#[test]
fn test_simple_fd_frame_max_size() {
use embedded_can::StandardId;
let id = StandardId::new(0x100).unwrap();
let data = [0xBBu8; 64];
let frame = SimpleFdFrame::new_fd_frame(id, &data, FdFlags::default()).unwrap();
assert!(frame.is_fd());
assert_eq!(frame.data().len(), 64);
}
#[cfg(feature = "can")]
#[test]
fn test_simple_fd_frame_too_large() {
use embedded_can::StandardId;
let id = StandardId::new(0x100).unwrap();
let data = [0u8; 65]; assert!(SimpleFdFrame::new_fd_frame(id, &data, FdFlags::default()).is_none());
}
}