use anyhow::Result;
pub trait CanBusSocket: Send + Sync {
fn is_open(&self) -> bool;
fn write_raw(&self, can_id: u32, data: &[u8]) -> anyhow::Result<()>;
fn read_raw(&self) -> anyhow::Result<Option<(u32, Vec<u8>)>>;
fn is_data_available(&self, timeout_us: u64) -> anyhow::Result<bool>;
fn set_recv_timeout(&mut self, timeout_us: u64) -> anyhow::Result<()>;
}
#[derive(Clone, Debug)]
pub struct CanFrame {
id: u32,
data: Vec<u8>,
pub(crate) is_extended: bool,
is_remote: bool,
is_error: bool,
}
impl CanFrame {
pub fn new(id: u32, data: &[u8]) -> Result<Self> {
if data.len() > 8 {
anyhow::bail!("CAN frame data cannot exceed 8 bytes");
}
Ok(Self {
id,
data: data.to_vec(),
is_extended: id > 0x7FF,
is_remote: false,
is_error: false,
})
}
pub fn new_extended(id: u32, data: &[u8]) -> Result<Self> {
if data.len() > 8 {
anyhow::bail!("CAN frame data cannot exceed 8 bytes");
}
Ok(Self {
id,
data: data.to_vec(),
is_extended: true,
is_remote: false,
is_error: false,
})
}
pub fn new_remote(id: u32, dlc: u8) -> Result<Self> {
if dlc > 8 {
anyhow::bail!("CAN RTR DLC cannot exceed 8");
}
Ok(Self {
id,
data: vec![0; dlc as usize],
is_extended: id > 0x7FF,
is_remote: true,
is_error: false,
})
}
pub fn id(&self) -> u32 {
self.id
}
pub fn data(&self) -> &[u8] {
&self.data
}
pub fn is_extended(&self) -> bool {
self.is_extended
}
pub fn is_remote(&self) -> bool {
self.is_remote
}
pub fn is_error(&self) -> bool {
self.is_error
}
pub fn dlc(&self) -> u8 {
self.data.len() as u8
}
}
#[derive(Clone, Debug)]
pub struct CanFdFrame {
id: u32,
data: Vec<u8>,
pub(crate) is_extended: bool,
flags: CanFdFlags,
}
#[derive(Clone, Copy, Debug, Default)]
pub struct CanFdFlags {
pub brs: bool,
pub esi: bool,
}
impl CanFdFrame {
pub fn new(id: u32, data: &[u8]) -> Result<Self> {
if data.len() > 64 {
anyhow::bail!("CAN FD frame data cannot exceed 64 bytes");
}
Ok(Self {
id,
data: data.to_vec(),
is_extended: id > 0x7FF,
flags: CanFdFlags::default(),
})
}
pub fn new_with_flags(id: u32, data: &[u8], flags: CanFdFlags) -> Result<Self> {
if data.len() > 64 {
anyhow::bail!("CAN FD frame data cannot exceed 64 bytes");
}
Ok(Self {
id,
data: data.to_vec(),
is_extended: id > 0x7FF,
flags,
})
}
pub fn id(&self) -> u32 {
self.id
}
pub fn data(&self) -> &[u8] {
&self.data
}
pub fn is_extended(&self) -> bool {
self.is_extended
}
pub fn flags(&self) -> CanFdFlags {
self.flags
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
#[derive(Clone, Debug)]
pub enum AnyCanFrame {
Can(CanFrame),
CanFd(CanFdFrame),
}
impl AnyCanFrame {
pub fn id(&self) -> u32 {
match self {
AnyCanFrame::Can(f) => f.id(),
AnyCanFrame::CanFd(f) => f.id(),
}
}
pub fn data(&self) -> &[u8] {
match self {
AnyCanFrame::Can(f) => f.data(),
AnyCanFrame::CanFd(f) => f.data(),
}
}
pub fn is_fd(&self) -> bool {
matches!(self, AnyCanFrame::CanFd(_))
}
}
#[derive(Clone, Debug)]
pub struct CanInterfaceInfo {
pub name: String,
}
pub mod wire {
use super::{AnyCanFrame, CanFdFlags, CanFdFrame, CanFrame};
use anyhow::Result;
pub const FRAME_SIZE: usize = 72;
const CAN_EFF_FLAG: u32 = 0x80000000; const CAN_RTR_FLAG: u32 = 0x40000000; const CAN_EFF_MASK: u32 = 0x1FFFFFFF; const CAN_SFF_MASK: u32 = 0x000007FF;
const CANFD_BRS: u8 = 0x01; const CANFD_ESI: u8 = 0x02;
pub fn encode(frame: &AnyCanFrame) -> Vec<u8> {
let mut buf = vec![0u8; FRAME_SIZE];
let (raw_id, flags, data) = match frame {
AnyCanFrame::Can(f) => {
let mut id = f.id();
if f.is_extended() {
id |= CAN_EFF_FLAG;
}
if f.is_remote() {
id |= CAN_RTR_FLAG;
}
(id, 0u8, f.data())
}
AnyCanFrame::CanFd(f) => {
let mut id = f.id();
if f.is_extended() {
id |= CAN_EFF_FLAG;
}
let mut flags = 0u8;
if f.flags().brs {
flags |= CANFD_BRS;
}
if f.flags().esi {
flags |= CANFD_ESI;
}
(id, flags, f.data())
}
};
buf[0..4].copy_from_slice(&raw_id.to_le_bytes());
buf[4] = data.len() as u8;
buf[5] = flags;
buf[8..8 + data.len()].copy_from_slice(data);
buf
}
pub fn decode(data: &[u8]) -> Result<(AnyCanFrame, usize)> {
if data.len() < FRAME_SIZE {
anyhow::bail!(
"Need {} bytes for canfd_frame, got {}",
FRAME_SIZE,
data.len()
);
}
let raw_id = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
let len = (data[4] as usize).min(64);
let flags = data[5];
let is_extended = (raw_id & CAN_EFF_FLAG) != 0;
let is_rtr = (raw_id & CAN_RTR_FLAG) != 0;
let can_id = if is_extended {
raw_id & CAN_EFF_MASK
} else {
raw_id & CAN_SFF_MASK
};
let frame_data = &data[8..8 + len];
let frame = if flags != 0 || len > 8 {
let fd_flags = CanFdFlags {
brs: (flags & CANFD_BRS) != 0,
esi: (flags & CANFD_ESI) != 0,
};
let mut f = CanFdFrame::new_with_flags(can_id, frame_data, fd_flags)?;
f.is_extended = is_extended;
AnyCanFrame::CanFd(f)
} else {
let mut f = if is_rtr {
CanFrame::new_remote(can_id, len as u8)?
} else {
CanFrame::new(can_id, frame_data)?
};
f.is_extended = is_extended;
AnyCanFrame::Can(f)
};
Ok((frame, FRAME_SIZE))
}
pub fn encoded_size(_header: &[u8]) -> Result<usize> {
Ok(FRAME_SIZE)
}
}