use alloc::vec::Vec;
use crate::bus_logging::BusFrame;
pub const MAX_FLEXRAY_PAYLOAD: usize = 254;
pub const MAX_SLOT_ID: u16 = 2047;
pub const MAX_CYCLE_COUNT: u8 = 63;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum FlexRayChannel {
#[default]
A = 0,
B = 1,
AB = 2,
}
impl FlexRayChannel {
pub fn from_u8(value: u8) -> Self {
match value {
0 => Self::A,
1 => Self::B,
2 => Self::AB,
_ => Self::A,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct FlexRayFlags(u16);
impl FlexRayFlags {
pub const TX: u16 = 0x0001;
pub const STARTUP: u16 = 0x0002;
pub const SYNC: u16 = 0x0004;
pub const NULL_FRAME: u16 = 0x0008;
pub const PAYLOAD_PREAMBLE: u16 = 0x0010;
pub const HEADER_CRC_ERROR: u16 = 0x0020;
pub const FRAME_CRC_ERROR: u16 = 0x0040;
pub const CODING_ERROR: u16 = 0x0080;
pub const TSS_VIOLATION: u16 = 0x0100;
pub const VALID: u16 = 0x0200;
pub const NM_VECTOR: u16 = 0x0400;
pub const DYNAMIC: u16 = 0x0800;
pub fn from_u16(value: u16) -> Self {
Self(value)
}
pub fn to_u16(self) -> u16 {
self.0
}
pub fn rx() -> Self {
Self(Self::VALID)
}
pub fn tx() -> Self {
Self(Self::TX | Self::VALID)
}
pub fn is_tx(self) -> bool {
self.0 & Self::TX != 0
}
pub fn is_rx(self) -> bool {
!self.is_tx()
}
pub fn is_startup(self) -> bool {
self.0 & Self::STARTUP != 0
}
pub fn is_sync(self) -> bool {
self.0 & Self::SYNC != 0
}
pub fn is_null_frame(self) -> bool {
self.0 & Self::NULL_FRAME != 0
}
pub fn has_payload_preamble(self) -> bool {
self.0 & Self::PAYLOAD_PREAMBLE != 0
}
pub fn has_header_crc_error(self) -> bool {
self.0 & Self::HEADER_CRC_ERROR != 0
}
pub fn has_frame_crc_error(self) -> bool {
self.0 & Self::FRAME_CRC_ERROR != 0
}
pub fn has_coding_error(self) -> bool {
self.0 & Self::CODING_ERROR != 0
}
pub fn is_valid(self) -> bool {
self.0 & Self::VALID != 0
}
pub fn has_nm_vector(self) -> bool {
self.0 & Self::NM_VECTOR != 0
}
pub fn is_dynamic(self) -> bool {
self.0 & Self::DYNAMIC != 0
}
pub fn has_error(self) -> bool {
self.0
& (Self::HEADER_CRC_ERROR
| Self::FRAME_CRC_ERROR
| Self::CODING_ERROR
| Self::TSS_VIOLATION)
!= 0
}
pub fn with_tx(self, tx: bool) -> Self {
if tx {
Self(self.0 | Self::TX)
} else {
Self(self.0 & !Self::TX)
}
}
pub fn with_valid(self, valid: bool) -> Self {
if valid {
Self(self.0 | Self::VALID)
} else {
Self(self.0 & !Self::VALID)
}
}
pub fn with_startup(self, startup: bool) -> Self {
if startup {
Self(self.0 | Self::STARTUP)
} else {
Self(self.0 & !Self::STARTUP)
}
}
pub fn with_sync(self, sync: bool) -> Self {
if sync {
Self(self.0 | Self::SYNC)
} else {
Self(self.0 & !Self::SYNC)
}
}
pub fn with_null_frame(self, null_frame: bool) -> Self {
if null_frame {
Self(self.0 | Self::NULL_FRAME)
} else {
Self(self.0 & !Self::NULL_FRAME)
}
}
pub fn with_dynamic(self, dynamic: bool) -> Self {
if dynamic {
Self(self.0 | Self::DYNAMIC)
} else {
Self(self.0 & !Self::DYNAMIC)
}
}
}
#[derive(Debug, Clone)]
pub struct FlexRayFrame {
pub slot_id: u16,
pub cycle: u8,
pub channel: FlexRayChannel,
pub flags: FlexRayFlags,
pub header_crc: u16,
pub payload: Vec<u8>,
}
pub const FLEXRAY_HEADER_SIZE: usize = 8;
impl FlexRayFrame {
pub fn new(slot_id: u16, cycle: u8, channel: FlexRayChannel, payload: Vec<u8>) -> Self {
Self {
slot_id: slot_id.min(MAX_SLOT_ID),
cycle: cycle & MAX_CYCLE_COUNT,
channel,
flags: FlexRayFlags::rx(),
header_crc: 0,
payload: if payload.len() > MAX_FLEXRAY_PAYLOAD {
payload[..MAX_FLEXRAY_PAYLOAD].to_vec()
} else {
payload
},
}
}
pub fn channel_a(slot_id: u16, cycle: u8, payload: Vec<u8>) -> Self {
Self::new(slot_id, cycle, FlexRayChannel::A, payload)
}
pub fn channel_b(slot_id: u16, cycle: u8, payload: Vec<u8>) -> Self {
Self::new(slot_id, cycle, FlexRayChannel::B, payload)
}
pub fn null_frame(slot_id: u16, cycle: u8, channel: FlexRayChannel) -> Self {
let mut frame = Self::new(slot_id, cycle, channel, Vec::new());
frame.flags = frame.flags.with_null_frame(true);
frame
}
pub fn startup(slot_id: u16, cycle: u8, channel: FlexRayChannel, payload: Vec<u8>) -> Self {
let mut frame = Self::new(slot_id, cycle, channel, payload);
frame.flags = frame.flags.with_startup(true).with_sync(true);
frame
}
pub fn with_tx(mut self) -> Self {
self.flags = self.flags.with_tx(true);
self
}
pub fn with_rx(mut self) -> Self {
self.flags = self.flags.with_tx(false);
self
}
pub fn with_dynamic(mut self) -> Self {
self.flags = self.flags.with_dynamic(true);
self
}
pub fn to_bytes(&self) -> Vec<u8> {
let total_size = FLEXRAY_HEADER_SIZE + self.payload.len();
let mut bytes = Vec::with_capacity(total_size);
bytes.extend_from_slice(&self.slot_id.to_le_bytes());
bytes.push(self.cycle);
bytes.push(self.channel as u8);
bytes.extend_from_slice(&self.flags.to_u16().to_le_bytes());
bytes.push((self.header_crc & 0xFF) as u8);
bytes.push(self.payload.len() as u8);
bytes.extend_from_slice(&self.payload);
bytes
}
pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
if bytes.len() < FLEXRAY_HEADER_SIZE {
return None;
}
let slot_id = u16::from_le_bytes([bytes[0], bytes[1]]);
let cycle = bytes[2];
let channel = FlexRayChannel::from_u8(bytes[3]);
let flags = FlexRayFlags::from_u16(u16::from_le_bytes([bytes[4], bytes[5]]));
let header_crc = bytes[6] as u16;
let payload_len = bytes[7] as usize;
if bytes.len() < FLEXRAY_HEADER_SIZE + payload_len {
return None;
}
let payload = bytes[FLEXRAY_HEADER_SIZE..FLEXRAY_HEADER_SIZE + payload_len].to_vec();
Some(Self {
slot_id,
cycle,
channel,
flags,
header_crc,
payload,
})
}
pub fn payload(&self) -> &[u8] {
&self.payload
}
pub fn payload_len(&self) -> usize {
self.payload.len()
}
}
impl Default for FlexRayFrame {
fn default() -> Self {
Self::new(1, 0, FlexRayChannel::A, Vec::new())
}
}
impl BusFrame for FlexRayFrame {
fn to_mdf_bytes(&self) -> Vec<u8> {
self.to_bytes()
}
fn mdf_size(&self) -> usize {
FLEXRAY_HEADER_SIZE + self.payload.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_flexray_frame_basic() {
let frame = FlexRayFrame::new(100, 5, FlexRayChannel::A, vec![0x01, 0x02, 0x03, 0x04]);
assert_eq!(frame.slot_id, 100);
assert_eq!(frame.cycle, 5);
assert_eq!(frame.channel, FlexRayChannel::A);
assert_eq!(frame.payload(), &[0x01, 0x02, 0x03, 0x04]);
}
#[test]
fn test_flexray_frame_slot_id_clamping() {
let frame = FlexRayFrame::new(3000, 0, FlexRayChannel::A, vec![]);
assert_eq!(frame.slot_id, MAX_SLOT_ID);
}
#[test]
fn test_flexray_frame_cycle_masking() {
let frame = FlexRayFrame::new(1, 100, FlexRayChannel::A, vec![]);
assert_eq!(frame.cycle, 100 & MAX_CYCLE_COUNT);
}
#[test]
fn test_flexray_flags() {
let flags = FlexRayFlags::tx();
assert!(flags.is_tx());
assert!(flags.is_valid());
let flags = FlexRayFlags::rx();
assert!(flags.is_rx());
assert!(flags.is_valid());
let flags = FlexRayFlags::from_u16(
FlexRayFlags::STARTUP | FlexRayFlags::SYNC | FlexRayFlags::VALID,
);
assert!(flags.is_startup());
assert!(flags.is_sync());
assert!(flags.is_valid());
}
#[test]
fn test_flexray_frame_roundtrip() {
let original = FlexRayFrame::new(100, 10, FlexRayChannel::B, vec![0xAA, 0xBB, 0xCC, 0xDD])
.with_tx()
.with_dynamic();
let bytes = original.to_bytes();
let parsed = FlexRayFrame::from_bytes(&bytes).unwrap();
assert_eq!(parsed.slot_id, original.slot_id);
assert_eq!(parsed.cycle, original.cycle);
assert_eq!(parsed.channel, original.channel);
assert!(parsed.flags.is_tx());
assert!(parsed.flags.is_dynamic());
assert_eq!(parsed.payload(), original.payload());
}
#[test]
fn test_flexray_channel() {
assert_eq!(FlexRayChannel::from_u8(0), FlexRayChannel::A);
assert_eq!(FlexRayChannel::from_u8(1), FlexRayChannel::B);
assert_eq!(FlexRayChannel::from_u8(2), FlexRayChannel::AB);
assert_eq!(FlexRayChannel::from_u8(99), FlexRayChannel::A);
}
#[test]
fn test_null_frame() {
let frame = FlexRayFrame::null_frame(50, 0, FlexRayChannel::A);
assert!(frame.flags.is_null_frame());
assert!(frame.payload.is_empty());
}
#[test]
fn test_startup_frame() {
let frame = FlexRayFrame::startup(1, 0, FlexRayChannel::AB, vec![0x00; 8]);
assert!(frame.flags.is_startup());
assert!(frame.flags.is_sync());
}
}