use crate::constants::{
CANFD_DLC_TO_LEN, CANFD_MAX_DLEN, CAN_EFF_FLAG, CAN_EFF_MASK, CAN_ERR_FLAG, CAN_MAX_DLEN,
CAN_RTR_FLAG, GS_CAN_FLAG_BRS, GS_CAN_FLAG_FD, GS_USB_ECHO_ID, GS_USB_FRAME_SIZE,
GS_USB_FRAME_SIZE_FD, GS_USB_FRAME_SIZE_FD_HW_TIMESTAMP, GS_USB_FRAME_SIZE_HW_TIMESTAMP,
GS_USB_RX_ECHO_ID,
};
pub fn dlc_to_len(dlc: u8, fd: bool) -> usize {
if fd {
if (dlc as usize) < CANFD_DLC_TO_LEN.len() {
CANFD_DLC_TO_LEN[dlc as usize]
} else {
CANFD_MAX_DLEN
}
} else {
(dlc as usize).min(CAN_MAX_DLEN)
}
}
pub fn len_to_dlc(length: usize, fd: bool) -> u8 {
if fd {
for (dlc, &dlen) in CANFD_DLC_TO_LEN.iter().enumerate() {
if dlen >= length {
return dlc as u8;
}
}
15 } else {
length.min(CAN_MAX_DLEN) as u8
}
}
#[derive(Clone)]
pub struct GsUsbFrame {
pub echo_id: u32,
pub can_id: u32,
pub can_dlc: u8,
pub channel: u8,
pub flags: u8,
pub reserved: u8,
pub data: [u8; CANFD_MAX_DLEN],
pub timestamp_us: u32,
}
impl Default for GsUsbFrame {
fn default() -> Self {
Self::new()
}
}
impl GsUsbFrame {
pub fn new() -> Self {
Self {
echo_id: GS_USB_ECHO_ID,
can_id: 0,
can_dlc: 0,
channel: 0,
flags: 0,
reserved: 0,
data: [0u8; CANFD_MAX_DLEN],
timestamp_us: 0,
}
}
pub fn with_data(can_id: u32, data: &[u8]) -> Self {
let mut frame = Self::new();
frame.can_id = can_id;
frame.set_data(data, false);
frame
}
pub fn with_fd_data(can_id: u32, data: &[u8], brs: bool) -> Self {
let mut frame = Self::new();
frame.can_id = can_id;
frame.flags |= GS_CAN_FLAG_FD;
if brs {
frame.flags |= GS_CAN_FLAG_BRS;
}
frame.set_data(data, true);
frame
}
fn set_data(&mut self, data: &[u8], fd: bool) {
let max_len = if fd { CANFD_MAX_DLEN } else { CAN_MAX_DLEN };
let data_len = data.len().min(max_len);
self.data = [0u8; CANFD_MAX_DLEN];
self.data[..data_len].copy_from_slice(&data[..data_len]);
self.can_dlc = len_to_dlc(data.len(), fd);
}
pub fn arbitration_id(&self) -> u32 {
self.can_id & CAN_EFF_MASK
}
pub fn is_extended_id(&self) -> bool {
(self.can_id & CAN_EFF_FLAG) != 0
}
pub fn is_remote_frame(&self) -> bool {
(self.can_id & CAN_RTR_FLAG) != 0
}
pub fn is_error_frame(&self) -> bool {
(self.can_id & CAN_ERR_FLAG) != 0
}
pub fn is_fd(&self) -> bool {
(self.flags & GS_CAN_FLAG_FD) != 0
}
pub fn is_brs(&self) -> bool {
(self.flags & GS_CAN_FLAG_BRS) != 0
}
pub fn is_echo_frame(&self) -> bool {
self.echo_id != GS_USB_RX_ECHO_ID
}
pub fn is_rx_frame(&self) -> bool {
self.echo_id == GS_USB_RX_ECHO_ID
}
pub fn timestamp(&self) -> f64 {
self.timestamp_us as f64 / 1_000_000.0
}
pub fn data_length(&self) -> usize {
dlc_to_len(self.can_dlc, self.is_fd())
}
pub fn data(&self) -> &[u8] {
&self.data[..self.data_length()]
}
pub fn frame_size(hw_timestamp: bool, fd_mode: bool) -> usize {
match (fd_mode, hw_timestamp) {
(true, true) => GS_USB_FRAME_SIZE_FD_HW_TIMESTAMP,
(true, false) => GS_USB_FRAME_SIZE_FD,
(false, true) => GS_USB_FRAME_SIZE_HW_TIMESTAMP,
(false, false) => GS_USB_FRAME_SIZE,
}
}
pub fn pack(&self, hw_timestamp: bool, fd_mode: bool) -> Vec<u8> {
let data_len = if fd_mode { 64 } else { 8 };
let size = Self::frame_size(hw_timestamp, fd_mode);
let mut buf = Vec::with_capacity(size);
buf.extend_from_slice(&self.echo_id.to_le_bytes());
buf.extend_from_slice(&self.can_id.to_le_bytes());
buf.push(self.can_dlc);
buf.push(self.channel);
buf.push(self.flags);
buf.push(self.reserved);
buf.extend_from_slice(&self.data[..data_len]);
if hw_timestamp {
buf.extend_from_slice(&self.timestamp_us.to_le_bytes());
}
buf
}
pub fn unpack_from(&mut self, data: &[u8], hw_timestamp: bool, fd_mode: bool) {
self.echo_id = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
self.can_id = u32::from_le_bytes([data[4], data[5], data[6], data[7]]);
self.can_dlc = data[8];
self.channel = data[9];
self.flags = data[10];
self.reserved = data[11];
let data_len = if fd_mode { 64 } else { 8 };
self.data = [0u8; CANFD_MAX_DLEN];
let copy_len = data_len.min(data.len() - 12);
self.data[..copy_len].copy_from_slice(&data[12..12 + copy_len]);
if hw_timestamp && data.len() >= 12 + data_len + 4 {
let ts_offset = 12 + data_len;
self.timestamp_us = u32::from_le_bytes([
data[ts_offset],
data[ts_offset + 1],
data[ts_offset + 2],
data[ts_offset + 3],
]);
} else {
self.timestamp_us = 0;
}
}
pub fn from_bytes(data: &[u8], hw_timestamp: bool, fd_mode: bool) -> Self {
let mut frame = Self::new();
frame.unpack_from(data, hw_timestamp, fd_mode);
frame
}
}
impl std::fmt::Display for GsUsbFrame {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let fd_indicator = if self.is_fd() { " FD" } else { "" };
let brs_indicator = if self.is_brs() { " BRS" } else { "" };
let data_str = if self.is_remote_frame() {
"remote request".to_string()
} else {
self.data()
.iter()
.map(|b| format!("{:02X}", b))
.collect::<Vec<_>>()
.join(" ")
};
write!(
f,
"{:>8X}{}{} [{}] {}",
self.arbitration_id(),
fd_indicator,
brs_indicator,
self.data_length(),
data_str
)
}
}
impl std::fmt::Debug for GsUsbFrame {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GsUsbFrame")
.field("echo_id", &format_args!("0x{:08X}", self.echo_id))
.field("can_id", &format_args!("0x{:08X}", self.can_id))
.field("can_dlc", &self.can_dlc)
.field("channel", &self.channel)
.field("flags", &format_args!("0x{:02X}", self.flags))
.field("data_length", &self.data_length())
.field("is_fd", &self.is_fd())
.field("is_echo", &self.is_echo_frame())
.field("timestamp_us", &self.timestamp_us)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dlc_to_len_classic() {
assert_eq!(dlc_to_len(0, false), 0);
assert_eq!(dlc_to_len(8, false), 8);
assert_eq!(dlc_to_len(15, false), 8); }
#[test]
fn test_dlc_to_len_fd() {
assert_eq!(dlc_to_len(0, true), 0);
assert_eq!(dlc_to_len(8, true), 8);
assert_eq!(dlc_to_len(9, true), 12);
assert_eq!(dlc_to_len(15, true), 64);
}
#[test]
fn test_len_to_dlc_classic() {
assert_eq!(len_to_dlc(0, false), 0);
assert_eq!(len_to_dlc(8, false), 8);
assert_eq!(len_to_dlc(64, false), 8); }
#[test]
fn test_len_to_dlc_fd() {
assert_eq!(len_to_dlc(0, true), 0);
assert_eq!(len_to_dlc(8, true), 8);
assert_eq!(len_to_dlc(12, true), 9);
assert_eq!(len_to_dlc(64, true), 15);
}
#[test]
fn test_frame_creation() {
let data = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0];
let frame = GsUsbFrame::with_data(0x7FF, &data);
assert_eq!(frame.arbitration_id(), 0x7FF);
assert!(!frame.is_extended_id());
assert!(!frame.is_fd());
assert_eq!(frame.data_length(), 8);
assert_eq!(frame.data(), &data);
}
#[test]
fn test_fd_frame_creation() {
let data: Vec<u8> = (0..64).collect();
let frame = GsUsbFrame::with_fd_data(0x123 | CAN_EFF_FLAG, &data, true);
assert_eq!(frame.arbitration_id(), 0x123);
assert!(frame.is_extended_id());
assert!(frame.is_fd());
assert!(frame.is_brs());
assert_eq!(frame.data_length(), 64);
}
#[test]
fn test_pack_unpack_classic() {
let data = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0];
let frame = GsUsbFrame::with_data(0x7FF, &data);
let packed = frame.pack(false, false);
assert_eq!(packed.len(), GS_USB_FRAME_SIZE);
let unpacked = GsUsbFrame::from_bytes(&packed, false, false);
assert_eq!(unpacked.can_id, frame.can_id);
assert_eq!(unpacked.data(), frame.data());
}
#[test]
fn test_pack_unpack_fd() {
let data: Vec<u8> = (0..64).collect();
let frame = GsUsbFrame::with_fd_data(0x123, &data, true);
let packed = frame.pack(true, true);
assert_eq!(packed.len(), GS_USB_FRAME_SIZE_FD_HW_TIMESTAMP);
let unpacked = GsUsbFrame::from_bytes(&packed, true, true);
assert_eq!(unpacked.can_id, frame.can_id);
assert_eq!(unpacked.flags, frame.flags);
assert_eq!(unpacked.data(), frame.data());
}
}