use crate::{Frame, FrameBuilder, IdBuilder, PDU_NOT_AVAILABLE, PGN};
pub const DATA_MAX_LENGTH: usize = 1785;
pub const DATA_FRAME_SIZE: usize = 7;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnectionManagement {
RequestToSend = 0x10,
ClearToSend = 0x11,
EndOfMessageAcknowledgment = 0x13,
BroadcastAnnounceMessage = 0x20,
Abort = 0xff,
}
impl TryFrom<u8> for ConnectionManagement {
type Error = u8;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x10 => Ok(ConnectionManagement::RequestToSend),
0x11 => Ok(ConnectionManagement::ClearToSend),
0x13 => Ok(ConnectionManagement::EndOfMessageAcknowledgment),
0x20 => Ok(ConnectionManagement::BroadcastAnnounceMessage),
0xff => Ok(ConnectionManagement::Abort),
_ => Err(value),
}
}
}
pub enum BroadcastTransportState {
ConnectionManagement,
DataTransfer(u8),
}
pub struct BroadcastTransport {
sa: u8,
pgn: PGN,
data: [u8; DATA_MAX_LENGTH],
data_length: usize,
tail: usize,
state: BroadcastTransportState,
}
impl BroadcastTransport {
#[must_use]
pub fn new(sa: u8, pgn: PGN) -> Self {
Self {
sa,
pgn,
data: [PDU_NOT_AVAILABLE; DATA_MAX_LENGTH],
data_length: 0,
tail: 0,
state: BroadcastTransportState::ConnectionManagement,
}
}
#[must_use]
pub fn with_data(mut self, data: &[u8]) -> Self {
let len = data.len().min(DATA_MAX_LENGTH);
self.data[..len].copy_from_slice(&data[..len]);
self.data_length = len;
self.tail = len;
self
}
#[must_use]
pub fn data(&self) -> &[u8] {
&self.data[..self.tail]
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.tail
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.tail == 0
}
#[must_use]
pub fn packet_count(&self) -> usize {
let quotient = self.data_length / DATA_FRAME_SIZE;
let remainder = self.data_length % DATA_FRAME_SIZE;
if remainder > 0 {
quotient + 1
} else {
quotient
}
}
pub fn next_frame(&mut self) -> Frame {
match self.state {
BroadcastTransportState::ConnectionManagement => {
#[allow(clippy::cast_possible_truncation)]
let data_length = (self.data_length as u16).to_le_bytes();
#[allow(clippy::cast_possible_truncation)]
let packets = self.packet_count() as u8;
let byte_array = self.pgn.to_le_bytes();
let frame = FrameBuilder::new(
IdBuilder::from_pgn(PGN::TransportProtocolConnectionManagement)
.priority(7)
.sa(self.sa)
.da(0xff)
.build(),
)
.copy_from_slice(&[
ConnectionManagement::BroadcastAnnounceMessage as u8,
data_length[0],
data_length[1],
packets,
PDU_NOT_AVAILABLE,
byte_array[0],
byte_array[1],
byte_array[2],
])
.build();
self.state = BroadcastTransportState::DataTransfer(0);
frame
}
BroadcastTransportState::DataTransfer(packet) => {
let start = packet as usize * DATA_FRAME_SIZE;
if start >= self.data_length {
return FrameBuilder::new(
IdBuilder::from_pgn(PGN::TransportProtocolDataTransfer)
.priority(7)
.sa(self.sa)
.da(0xff)
.build(),
)
.set_len(8)
.build();
}
let sequence = packet + 1;
let end = (start + DATA_FRAME_SIZE).min(self.data_length);
let mut frame_builder = FrameBuilder::new(
IdBuilder::from_pgn(PGN::TransportProtocolDataTransfer)
.priority(7)
.sa(self.sa)
.da(0xff)
.build(),
);
let payload = frame_builder.as_mut();
payload[0] = sequence;
let data_chunk = &self.data[start..end];
payload[1..=data_chunk.len()].copy_from_slice(data_chunk);
let frame = frame_builder.set_len(8).build();
self.state = BroadcastTransportState::DataTransfer(sequence);
frame
}
}
}
pub fn from_frame(&mut self, frame: &Frame) {
let pgn = frame.id().pgn();
if pgn == PGN::TransportProtocolConnectionManagement {
let data = frame.as_ref();
if data.len() < 8 {
return; }
let data_length = u16::from_le_bytes([data[1], data[2]]) as usize;
if data[0] == ConnectionManagement::BroadcastAnnounceMessage as u8 {
self.pgn = PGN::from_le_bytes([data[5], data[6], data[7]]);
self.data_length = data_length.min(DATA_MAX_LENGTH);
self.state = BroadcastTransportState::DataTransfer(0);
}
} else if pgn == PGN::TransportProtocolDataTransfer {
let data = frame.as_ref();
if data.len() < 2 {
return; }
let sequence = data[0];
if sequence == 0 {
return; }
let data_chunk = &data[1..];
let start = (sequence as usize - 1) * DATA_FRAME_SIZE;
let end = start + data_chunk.len();
if end > DATA_MAX_LENGTH {
return; }
self.tail = self.data_length.min(end);
self.data[start..end].copy_from_slice(data_chunk);
}
}
}
impl AsRef<[u8]> for BroadcastTransport {
fn as_ref(&self) -> &[u8] {
&self.data[..self.tail]
}
}
#[cfg(test)]
mod tests {
use crate::Id;
use super::*;
#[test]
fn test_broadcast_transport() {
let data = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09];
let mut transport = BroadcastTransport::new(0x01, PGN::AddressClaimed).with_data(&data);
let frame = transport.next_frame();
assert_eq!(frame.id().as_raw(), 0x1CECFF01);
assert_eq!(frame.len(), 8);
assert_eq!(
frame.as_ref(),
&[0x20, 0x09, 0x00, 0x02, 0xFF, 0x00, 0xEE, 0x00]
);
let frame = transport.next_frame();
assert_eq!(frame.id().as_raw(), 0x1CEBFF01);
assert_eq!(frame.len(), 8);
assert_eq!(
frame.as_ref(),
&[0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]
);
let frame = transport.next_frame();
assert_eq!(frame.id().as_raw(), 0x1CEBFF01);
assert_eq!(frame.len(), 8);
assert_eq!(
frame.as_ref(),
&[0x02, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]
);
}
#[test]
fn test_broadcast_transport2() {
let frame1 = [0x20, 0x09, 0x00, 0x02, 0xFF, 0x00, 0xEE, 0x00];
let frame2 = [0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07];
let frame3 = [0x02, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
let mut transport = BroadcastTransport::new(0x01, PGN::AddressClaimed);
transport.from_frame(
&FrameBuilder::new(Id::new(0x1CECFF01))
.copy_from_slice(&frame1)
.build(),
);
assert_eq!(transport.len(), 0);
assert_eq!(transport.packet_count(), 2);
transport.from_frame(
&FrameBuilder::new(Id::new(0x1CEBFF01))
.copy_from_slice(&frame2)
.build(),
);
transport.from_frame(
&FrameBuilder::new(Id::new(0x1CEBFF01))
.copy_from_slice(&frame3)
.build(),
);
assert_eq!(transport.len(), 9);
assert_eq!(
transport.data(),
&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09]
);
}
#[test]
fn test_oversized_data() {
let large_data = [0xAB; DATA_MAX_LENGTH + 100];
let transport = BroadcastTransport::new(0x01, PGN::AddressClaimed)
.with_data(&large_data);
assert_eq!(transport.len(), DATA_MAX_LENGTH);
assert_eq!(transport.data().len(), DATA_MAX_LENGTH);
assert!(transport.data().iter().all(|&b| b == 0xAB));
}
#[test]
fn test_invalid_sequence_zero() {
let mut transport = BroadcastTransport::new(0x01, PGN::AddressClaimed);
let cm_frame = [0x20, 0x09, 0x00, 0x02, 0xFF, 0x00, 0xEE, 0x00];
transport.from_frame(
&FrameBuilder::new(Id::new(0x1CECFF01))
.copy_from_slice(&cm_frame)
.build(),
);
assert_eq!(transport.data_length, 9);
let invalid_frame = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07];
transport.from_frame(
&FrameBuilder::new(Id::new(0x1CEBFF01))
.copy_from_slice(&invalid_frame)
.build(),
);
assert_eq!(transport.tail, 0);
}
#[test]
fn test_max_packets() {
let max_data = [0xCC; DATA_MAX_LENGTH];
let transport = BroadcastTransport::new(0x01, PGN::ProprietaryA)
.with_data(&max_data);
let expected_packets = (DATA_MAX_LENGTH + DATA_FRAME_SIZE - 1) / DATA_FRAME_SIZE;
assert_eq!(transport.packet_count(), expected_packets);
assert_eq!(transport.len(), DATA_MAX_LENGTH);
assert_eq!(transport.data(), &max_data[..]);
}
#[test]
fn test_empty_transport() {
let transport = BroadcastTransport::new(0x01, PGN::Request);
assert_eq!(transport.len(), 0);
assert!(transport.is_empty());
assert_eq!(transport.packet_count(), 0);
}
}