use ace_can::{
IsoTpAddressingMode, IsoTpError, ReassembleResult, Reassembler, ReassemblerConfig,
SegmentResult, Segmenter, SegmenterConfig,
};
use ace_sim::{clock::Instant, io::NodeAddress};
pub const ISOTP_MAX_UDS: usize = 4096;
pub const ISOTP_MAX_FRAME: usize = 64;
pub const ISOTP_MAX_OUT: usize = 64;
#[derive(Debug)]
pub enum IsoTpNodeError {
Reassembler(IsoTpError),
Segmenter(IsoTpError),
OutboxFull,
}
pub struct IsoTpNode<const N: usize = ISOTP_MAX_UDS> {
request_can_id: u32,
response_can_id: u32,
address: NodeAddress,
reassembler: Reassembler<N>,
req_segmenter: Segmenter<N>,
resp_segmenter: Segmenter<N>,
pub can_outbox: heapless::Vec<(NodeAddress, heapless::Vec<u8, ISOTP_MAX_FRAME>), ISOTP_MAX_OUT>,
uds_outbox: heapless::Vec<(NodeAddress, heapless::Vec<u8, N>), 4>,
}
impl<const N: usize> IsoTpNode<N> {
pub fn new(
request_can_id: u32,
response_can_id: u32,
addressing_mode: IsoTpAddressingMode,
) -> Self {
let address = NodeAddress(response_can_id);
let rsm_config = ReassemblerConfig::new(addressing_mode.clone());
let seg_config = SegmenterConfig::classic(addressing_mode);
Self {
request_can_id,
response_can_id,
address,
reassembler: Reassembler::new(rsm_config),
req_segmenter: Segmenter::new(seg_config.clone()),
resp_segmenter: Segmenter::new(seg_config),
can_outbox: heapless::Vec::new(),
uds_outbox: heapless::Vec::new(),
}
}
pub fn address(&self) -> &NodeAddress {
&self.address
}
pub fn handle_from_gateway(
&mut self,
uds_data: &[u8],
_now: Instant,
) -> Result<(), IsoTpNodeError> {
self.req_segmenter
.start(uds_data)
.map_err(IsoTpNodeError::Segmenter)?;
let ecu_rx_addr = NodeAddress(self.response_can_id);
Self::drain_segmenter(&mut self.req_segmenter, ecu_rx_addr, &mut self.can_outbox)
}
pub fn handle_uds_response(
&mut self,
uds_data: &[u8],
_now: Instant,
) -> Result<(), IsoTpNodeError> {
self.resp_segmenter
.start(uds_data)
.map_err(IsoTpNodeError::Segmenter)?;
let gw_rx_addr = NodeAddress(self.request_can_id);
Self::drain_segmenter(&mut self.resp_segmenter, gw_rx_addr, &mut self.can_outbox)
}
pub fn handle_from_ecu(
&mut self,
can_frame: &[u8],
_now: Instant,
) -> Result<(), IsoTpNodeError> {
match self
.reassembler
.feed(can_frame)
.map_err(IsoTpNodeError::Reassembler)?
{
ReassembleResult::Complete { len } => {
if let Some(uds_bytes) = self.reassembler.message(len) {
let gateway_addr = NodeAddress(self.request_can_id);
let mut frame = heapless::Vec::new();
let _ = frame.extend_from_slice(&uds_bytes[..len.min(N)]);
self.uds_outbox
.push((gateway_addr, frame))
.map_err(|_| IsoTpNodeError::OutboxFull)?;
}
}
ReassembleResult::FlowControl { frame, len: fc_len } => {
let ecu_addr = NodeAddress(self.response_can_id);
let mut fc_frame = heapless::Vec::new();
let _ = fc_frame.extend_from_slice(&frame[..fc_len]);
self.can_outbox
.push((ecu_addr, fc_frame))
.map_err(|_| IsoTpNodeError::OutboxFull)?;
}
ReassembleResult::InProgress => {}
ReassembleResult::SessionAborted {
flow_control,
fc_len,
} => {
let ecu_addr = NodeAddress(self.response_can_id);
let mut fc_frame = heapless::Vec::new();
let _ = fc_frame.extend_from_slice(&flow_control[..fc_len]);
let _ = self.can_outbox.push((ecu_addr, fc_frame));
self.reassembler.reset();
}
}
Ok(())
}
pub fn drain_can_outbox(
&mut self,
out: &mut heapless::Vec<(NodeAddress, heapless::Vec<u8, ISOTP_MAX_FRAME>), ISOTP_MAX_OUT>,
) -> usize {
let n = self.can_outbox.len();
for item in self.can_outbox.drain(..) {
let _ = out.push(item);
}
n
}
pub fn drain_uds_outbox(
&mut self,
out: &mut heapless::Vec<(NodeAddress, heapless::Vec<u8, N>), 4>,
) -> usize {
let n = self.uds_outbox.len();
for item in self.uds_outbox.drain(..) {
let _ = out.push(item);
}
n
}
fn drain_segmenter(
segmenter: &mut Segmenter<N>,
dst: NodeAddress,
can_outbox: &mut heapless::Vec<
(NodeAddress, heapless::Vec<u8, ISOTP_MAX_FRAME>),
ISOTP_MAX_OUT,
>,
) -> Result<(), IsoTpNodeError> {
let mut out_buf = [0u8; ISOTP_MAX_FRAME];
loop {
match segmenter
.next_frame(&mut out_buf)
.map_err(IsoTpNodeError::Segmenter)?
{
SegmentResult::Complete => break,
SegmentResult::WaitForFlowControl => break,
SegmentResult::Frame { len } => {
let mut frame: heapless::Vec<u8, ISOTP_MAX_FRAME> = heapless::Vec::new();
let _ = frame.extend_from_slice(&out_buf[..len]);
can_outbox
.push((dst.clone(), frame))
.map_err(|_| IsoTpNodeError::OutboxFull)?;
}
}
}
Ok(())
}
}