use ace_proto::{common::RawFrame, UdsFrame};
use ace_sim::{clock::Instant, io::NodeAddress};
use ace_uds::ext::UdsFrameExt;
use heapless::Vec;
use crate::{config::ClientConfig, event::ClientEvent, pending::PendingRequest, ClientError};
#[derive(Debug)]
pub struct UdsClient<
const PENDING: usize,
const SIM_MAX_FRAME: usize,
const SIM_MAX_OUTBOX: usize,
const MAX_TARGET_EVENTS: usize,
const PERIODIC_DIDS: usize,
const MAX_DATA: usize,
> {
config: ClientConfig,
address: NodeAddress,
server: NodeAddress,
pending: Vec<PendingRequest, PENDING>,
outbox: Vec<(NodeAddress, Vec<u8, SIM_MAX_FRAME>), SIM_MAX_OUTBOX>,
events: Vec<ClientEvent<MAX_DATA>, MAX_TARGET_EVENTS>,
periodic_dids: Vec<u8, PERIODIC_DIDS>,
}
impl<
const PENDING: usize,
const SIM_MAX_FRAME: usize,
const SIM_MAX_OUTBOX: usize,
const MAX_EVENTS: usize,
const PERIODIC_DIDS: usize,
const MAX_DATA: usize,
> UdsClient<PENDING, SIM_MAX_FRAME, SIM_MAX_OUTBOX, MAX_EVENTS, PERIODIC_DIDS, MAX_DATA>
{
pub fn new(config: ClientConfig, address: NodeAddress) -> Self {
let server = NodeAddress(config.target_address as u32);
Self {
config,
address,
server,
pending: Vec::new(),
outbox: Vec::new(),
events: Vec::new(),
periodic_dids: Vec::new(),
}
}
pub fn address(&self) -> &NodeAddress {
&self.address
}
pub fn handle(
&mut self,
_src: &NodeAddress,
data: &[u8],
now: Instant,
) -> Result<(), ClientError> {
let first = match data.first().copied() {
Some(b) => b,
None => return Ok(()),
};
let frame = UdsFrame::from_slice(data);
if first == 0x7F {
let requested_sid = data.get(1).copied().unwrap_or(0);
let nrc = data.get(2).copied().unwrap_or(0);
if nrc == 0x78 {
if let Some(p) = self.pending.iter_mut().find(|p| p.sid == requested_sid) {
p.extend(now, self.config.p2_extended_timeout);
let _ = self
.events
.push(ClientEvent::ResponsePending { sid: requested_sid });
}
return Ok(());
}
self.complete_pending(requested_sid);
let _ = self.events.push(ClientEvent::NegativeResponse {
sid: requested_sid,
nrc,
});
return Ok(());
}
if self.periodic_dids.contains(&first) {
let raw = frame.as_bytes();
let record = raw.get(1..).unwrap_or(&[]);
let mut buf: Vec<u8, MAX_DATA> = Vec::new();
let _ = buf.extend_from_slice(&record[..record.len().min(MAX_DATA)]);
let _ = self.events.push(ClientEvent::PeriodicData {
did: first,
data: buf,
});
return Ok(());
}
if first & 0x40 != 0 {
let request_sid = first & !0x40u8;
if self.complete_pending(request_sid) {
let payload = frame.payload();
let mut buf: Vec<u8, MAX_DATA> = Vec::new();
let _ = buf.extend_from_slice(&payload[..payload.len().min(MAX_DATA)]);
let _ = self.events.push(ClientEvent::PositiveResponse {
sid: request_sid,
data: buf,
});
return Ok(());
}
}
{
let raw = frame.as_bytes();
let mut buf: Vec<u8, MAX_DATA> = Vec::new();
let _ = buf.extend_from_slice(&raw[..raw.len().min(MAX_DATA)]);
let _ = self.events.push(ClientEvent::Unsolicited { data: buf });
}
Ok(())
}
pub fn tick(&mut self, now: Instant) -> Result<(), ClientError> {
let mut expired: Vec<u8, PENDING> = Vec::new();
for p in self.pending.iter() {
if p.is_expired(now) {
let _ = expired.push(p.sid);
}
}
for sid in expired {
self.complete_pending(sid);
let _ = self.events.push(ClientEvent::Timeout { sid });
}
Ok(())
}
pub fn drain_outbox(
&mut self,
out: &mut Vec<(NodeAddress, Vec<u8, SIM_MAX_FRAME>), SIM_MAX_OUTBOX>,
) -> usize {
let n = self.outbox.len();
for item in self.outbox.drain(..) {
let _ = out.push(item);
}
n
}
pub fn request(&mut self, data: &[u8], now: Instant) -> Result<(), ClientError> {
let sid = match data.first().copied() {
Some(b) => b,
None => return Err(ClientError::EmptyRequest),
};
if self.pending.is_full() {
return Err(ClientError::QueueFull);
}
let mut frame: Vec<u8, SIM_MAX_FRAME> = Vec::new();
let _ = frame.extend_from_slice(data);
self.outbox
.push((self.server.clone(), frame))
.map_err(|_| ClientError::OutboxFull)?;
let _ = self
.pending
.push(PendingRequest::new(sid, now, self.config.p2_timeout));
Ok(())
}
pub fn subscribe_periodic(&mut self, did_low_byte: u8) {
if !self.periodic_dids.contains(&did_low_byte) {
let _ = self.periodic_dids.push(did_low_byte);
}
}
pub fn unsubscribe_periodic(&mut self, did_low_byte: u8) {
self.periodic_dids.retain(|&d| d != did_low_byte);
}
pub fn is_periodic_subscribed(&self, did_low_byte: u8) -> bool {
self.periodic_dids.contains(&did_low_byte)
}
pub fn drain_events(&mut self) -> impl Iterator<Item = ClientEvent<MAX_DATA>> + '_ {
self.events.drain(..)
}
pub fn has_events(&self) -> bool {
!self.events.is_empty()
}
pub fn pending_count(&self) -> usize {
self.pending.len()
}
fn complete_pending(&mut self, sid: u8) -> bool {
if let Some(pos) = self.pending.iter().position(|p| p.sid == sid) {
self.pending.remove(pos);
true
} else {
false
}
}
}