use crate::error::CanIdBuildError;
use crate::protocol::transport::can_frame::CanFrame;
use crate::protocol::transport::can_id::CanId;
use crate::protocol::transport::fast_packet::MAX_FAST_PACKET_PAYLOAD;
#[cfg(target_has_atomic = "8")]
use core::sync::atomic::{AtomicU8, Ordering};
#[cfg(target_has_atomic = "8")]
static GLOBAL_SEQUENCE_ID: AtomicU8 = AtomicU8::new(0);
#[cfg(not(target_has_atomic = "8"))]
static mut GLOBAL_SEQUENCE_ID: u8 = 0;
fn next_sequence_id() -> u8 {
#[cfg(target_has_atomic = "8")]
{
GLOBAL_SEQUENCE_ID
.try_update(Ordering::AcqRel, Ordering::Relaxed, |prev| {
Some((prev + 1) & 0x07)
})
.unwrap()
& 0x07
}
#[cfg(not(target_has_atomic = "8"))]
unsafe {
let current = GLOBAL_SEQUENCE_ID & 0x07;
GLOBAL_SEQUENCE_ID = (current + 1) & 0x07;
current
}
}
#[derive(Debug)]
pub struct FastPacketBuilder<'a> {
pgn: u32,
source_address: u8,
destination: Option<u8>,
priority: u8,
payload: &'a [u8],
sequence_id: u8,
}
pub struct FrameIterator<'a> {
builder: FastPacketBuilder<'a>,
frame_index: u8,
bytes_sent: usize,
}
impl<'a> Iterator for FrameIterator<'a> {
type Item = Result<CanFrame, CanIdBuildError>;
fn next(&mut self) -> Option<Self::Item> {
if self.bytes_sent >= self.builder.payload.len() {
return None;
}
let mut id_builder = CanId::builder(self.builder.pgn, self.builder.source_address)
.with_priority(self.builder.priority);
if let Some(destination) = self.builder.destination {
id_builder = id_builder.to_destination(destination);
}
let id = match id_builder.build() {
Ok(id) => id,
Err(e) => return Some(Err(e)),
};
let total_len = self.builder.payload.len();
if total_len > MAX_FAST_PACKET_PAYLOAD {
self.bytes_sent = total_len;
return Some(Err(CanIdBuildError::InvalidData));
}
if total_len <= 8 {
let mut data = [0xFF; 8];
data[..total_len].copy_from_slice(self.builder.payload);
self.bytes_sent = total_len;
return Some(Ok(CanFrame {
id,
data,
len: total_len,
}));
}
let header = ((self.builder.sequence_id & 0x07) << 5) | (self.frame_index & 0x1F);
let frame = if self.bytes_sent == 0 {
let mut data = [0xFF; 8];
data[0] = header;
data[1] = self.builder.payload.len() as u8;
let bytes_to_copy = 6.min(self.builder.payload.len());
data[2..2 + bytes_to_copy].copy_from_slice(&self.builder.payload[0..bytes_to_copy]);
self.bytes_sent += bytes_to_copy;
CanFrame {
id,
data,
len: 2 + bytes_to_copy,
}
} else {
let mut data = [0xFF; 8];
data[0] = header;
let remaining_bytes = self.builder.payload.len() - self.bytes_sent;
let bytes_to_copy = 7.min(remaining_bytes);
let payload_slice =
&self.builder.payload[self.bytes_sent..self.bytes_sent + bytes_to_copy];
data[1..1 + bytes_to_copy].copy_from_slice(payload_slice);
self.bytes_sent += bytes_to_copy;
CanFrame {
id,
data,
len: 1 + bytes_to_copy,
}
};
self.frame_index = self.frame_index.wrapping_add(1);
Some(Ok(frame))
}
}
impl<'a> FastPacketBuilder<'a> {
pub fn new(pgn: u32, source_address: u8, destination: Option<u8>, payload: &'a [u8]) -> Self {
Self {
pgn,
source_address,
priority: 6,
destination,
payload,
sequence_id: next_sequence_id(),
}
}
pub fn with_sequence_id(mut self, sequence_id: u8) -> Self {
self.sequence_id = sequence_id & 0x07;
self
}
pub fn with_priority(mut self, priority: u8) -> Self {
self.priority = priority & 0x07;
self
}
pub fn build(self) -> FrameIterator<'a> {
FrameIterator {
builder: self,
frame_index: 0,
bytes_sent: 0,
}
}
}
#[cfg(test)]
#[path = "tests.rs"]
mod tests;