use ace_can::{
IsoTpAddressingMode, ReassembleResult, Reassembler, ReassemblerConfig, SegmentResult,
Segmenter, SegmenterConfig,
};
use ace_client::{SIM_MAX_FRAME, SIM_MAX_OUTBOX};
use ace_server::{
handler::ServerHandler,
security_provider::SecurityProvider,
server::{ServerError, UdsServer},
NrcError,
};
use ace_sim::{clock::Instant, io::NodeAddress};
use heapless::Vec;
pub const ECU_CAN_FRAME: usize = 8;
pub const ECU_CAN_OUT: usize = 128;
pub const ECU_UDS_FRAME: usize = 4096;
#[derive(Debug)]
pub enum EcuNodeError<SE: NrcError> {
IsoTp(ace_can::IsoTpError),
CanOutboxFull,
Server(ServerError<SE>),
}
pub struct EcuNode<H, S>
where
H: ServerHandler,
S: SecurityProvider,
{
pub logical_address: u16,
pub request_can_id: u32,
pub response_can_id: u32,
req_reassembler: Reassembler<ECU_UDS_FRAME>,
resp_segmenter: Segmenter<ECU_UDS_FRAME>,
pub server: UdsServer<H, S>,
can_outbox: Vec<(NodeAddress, Vec<u8, ECU_CAN_FRAME>), ECU_CAN_OUT>,
}
impl<H, S> EcuNode<H, S>
where
H: ServerHandler,
S: SecurityProvider,
{
pub fn new(
logical_address: u16,
request_can_id: u32,
response_can_id: u32,
mode: IsoTpAddressingMode,
server: UdsServer<H, S>,
) -> Self {
Self {
logical_address,
request_can_id,
response_can_id,
req_reassembler: Reassembler::new(ReassemblerConfig::new(mode.clone())),
resp_segmenter: Segmenter::new(SegmenterConfig::classic(mode)),
server,
can_outbox: Vec::new(),
}
}
pub fn handle_can_frame(
&mut self,
data: &[u8],
now: Instant,
) -> Result<(), EcuNodeError<H::Error>> {
if let Some(&first) = data.first() {
if first & 0xF0 == 0x30 {
if let Err(e) = self.resp_segmenter.handle_flow_control(data) {
let _ = e;
} else {
self.drain_resp_segmenter()?;
}
return Ok(());
}
}
match self
.req_reassembler
.feed(data)
.map_err(EcuNodeError::IsoTp)?
{
ReassembleResult::Complete { len } => {
if let Some(uds_bytes) = self.req_reassembler.message(len) {
let mut buf: Vec<u8, ECU_UDS_FRAME> = Vec::new();
let _ = buf.extend_from_slice(&uds_bytes[..len.min(ECU_UDS_FRAME)]);
self.req_reassembler.reset();
self.server
.handle(&NodeAddress(self.request_can_id), &buf, now)
.map_err(EcuNodeError::Server)?;
} else {
self.req_reassembler.reset();
}
}
ReassembleResult::FlowControl { frame, len: fc_len } => {
let mut fc: Vec<u8, ECU_CAN_FRAME> = Vec::new();
let _ = fc.extend_from_slice(&frame[..fc_len]);
self.can_outbox
.push((NodeAddress(self.request_can_id), fc))
.map_err(|_| EcuNodeError::CanOutboxFull)?;
}
ReassembleResult::InProgress => {}
ReassembleResult::SessionAborted {
flow_control,
fc_len,
} => {
let mut fc: Vec<u8, ECU_CAN_FRAME> = Vec::new();
let _ = fc.extend_from_slice(&flow_control[..fc_len]);
let _ = self.can_outbox.push((NodeAddress(self.request_can_id), fc));
self.req_reassembler.reset();
}
}
Ok(())
}
pub fn tick(&mut self, now: Instant) -> Result<(), EcuNodeError<H::Error>> {
self.server.tick(now).map_err(EcuNodeError::Server)?;
let mut srv_out: Vec<(NodeAddress, Vec<u8, SIM_MAX_FRAME>), SIM_MAX_OUTBOX> = Vec::new();
self.server.drain_outbox(&mut srv_out);
for (_, uds_data) in &srv_out {
self.resp_segmenter
.start(uds_data)
.map_err(EcuNodeError::IsoTp)?;
self.drain_resp_segmenter()?;
}
Ok(())
}
pub fn drain_can_outbox(
&mut self,
out: &mut Vec<(NodeAddress, Vec<u8, ECU_CAN_FRAME>), ECU_CAN_OUT>,
) -> usize {
let n = self.can_outbox.len();
for item in self.can_outbox.drain(..) {
let _ = out.push(item);
}
n
}
fn drain_resp_segmenter(&mut self) -> Result<(), EcuNodeError<H::Error>> {
let mut out_buf = [0u8; ECU_CAN_FRAME];
loop {
match self
.resp_segmenter
.next_frame(&mut out_buf)
.map_err(EcuNodeError::IsoTp)?
{
SegmentResult::Complete => break,
SegmentResult::WaitForFlowControl => break,
SegmentResult::Frame { len } => {
let mut frame: Vec<u8, ECU_CAN_FRAME> = Vec::new();
let _ = frame.extend_from_slice(&out_buf[..len]);
self.can_outbox
.push((NodeAddress(self.response_can_id), frame))
.map_err(|_| EcuNodeError::CanOutboxFull)?;
}
}
}
Ok(())
}
}