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> { /* private fields */ }Expand description
Stateful UDS tester client state machine.
Sends raw UDS request frames and emits ClientEvents as responses arrive. The client tracks
only what is necessary for the protocol exchange - P2 and P2* timeouts on pending requests.
Session state, security state, and retry logic are entirely the caller’s responsibility.
N - maximum number of concurrent pending requests. Defaults to 1. UDS is strictly sequential
in most implementations - use UdsClient<1> unless you have a specific need for pipelining.
Implementations§
Source§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>
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
pub fn address(&self) -> &NodeAddress
Sourcepub fn handle(
&mut self,
_src: &NodeAddress,
data: &[u8],
now: Instant,
) -> Result<(), ClientError>
pub fn handle( &mut self, _src: &NodeAddress, data: &[u8], now: Instant, ) -> Result<(), ClientError>
Delivers an inbound frame to the client.
Classifies the frame before attempting any pending request match:
- Empty frame - ignored
0x7F- negative response - matched by requested SID at byte 1- First byte is a subscribed periodic DID -
PeriodicDataevent - First byte has bit 6 set - positive response - matched by request SID
- Anything else -
Unsolicitedevent
Periodic classification must precede positive response classification because a periodic DID low byte could theoretically have bit 6 set. Explicit subscription registry takes priority.
Sourcepub fn tick(&mut self, now: Instant) -> Result<(), ClientError>
pub fn tick(&mut self, now: Instant) -> Result<(), ClientError>
Advances internal timers - expires timed-out pending requests.
Sourcepub fn drain_outbox(
&mut self,
out: &mut Vec<(NodeAddress, Vec<u8, SIM_MAX_FRAME>), SIM_MAX_OUTBOX>,
) -> usize
pub fn drain_outbox( &mut self, out: &mut Vec<(NodeAddress, Vec<u8, SIM_MAX_FRAME>), SIM_MAX_OUTBOX>, ) -> usize
Drains pending outbound frames into out.
Sourcepub fn request(&mut self, data: &[u8], now: Instant) -> Result<(), ClientError>
pub fn request(&mut self, data: &[u8], now: Instant) -> Result<(), ClientError>
Enqueues a raw UDS request for transmission.
data must begin with the SID byte followed by any parameters. The client tracks this as a
pending request and starts the P2 timer.
Returns ClientError::QueueFull if N concurrent requests are already pending.
Sourcepub fn subscribe_periodic(&mut self, did_low_byte: u8)
pub fn subscribe_periodic(&mut self, did_low_byte: u8)
Registers the low byte of a periodic DID identifier as subscribed.
Inbound frames whose first byte matches a subscribed DID low byte are classified as
PeriodicData events rather than Unsolicited.
The low byte corresponds to the periodic_data_identifier field in
ReadDataByPeriodicIdentifierResponseData. For a DID of 0xF201 the low byte is 0x01.
Silently does nothing if already subscribed or the registry is full.
Sourcepub fn unsubscribe_periodic(&mut self, did_low_byte: u8)
pub fn unsubscribe_periodic(&mut self, did_low_byte: u8)
Removes a periodic DID subscription.
After calling this, frames with this DID low byte will be classified as Unsolicited
rather than PeriodicData.
Sourcepub fn is_periodic_subscribed(&self, did_low_byte: u8) -> bool
pub fn is_periodic_subscribed(&self, did_low_byte: u8) -> bool
Returns true if the given DID low byte is currently subscribed.
Sourcepub fn drain_events(
&mut self,
) -> impl Iterator<Item = ClientEvent<MAX_DATA>> + '_
pub fn drain_events( &mut self, ) -> impl Iterator<Item = ClientEvent<MAX_DATA>> + '_
Drains all accumulated events, returning an iterator.
Events are consumed - calling drain_events twice returns events on the first call and
nothing on the second.
Sourcepub fn has_events(&self) -> bool
pub fn has_events(&self) -> bool
Returns true if any events are pending.
Sourcepub fn pending_count(&self) -> usize
pub fn pending_count(&self) -> usize
Returns the number of currently pending requests.
Trait Implementations§
Source§impl<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> Debug for UdsClient<PENDING, SIM_MAX_FRAME, SIM_MAX_OUTBOX, MAX_TARGET_EVENTS, PERIODIC_DIDS, MAX_DATA>
impl<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> Debug for UdsClient<PENDING, SIM_MAX_FRAME, SIM_MAX_OUTBOX, MAX_TARGET_EVENTS, PERIODIC_DIDS, MAX_DATA>
Source§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> SimNode<SIM_MAX_FRAME, SIM_MAX_OUTBOX> for UdsClient<PENDING, SIM_MAX_FRAME, SIM_MAX_OUTBOX, MAX_EVENTS, PERIODIC_DIDS, MAX_DATA>
Maximum UDS frame payload bytes = matches ace-server for bus compatibility.
Maximum outbox depth.
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> SimNode<SIM_MAX_FRAME, SIM_MAX_OUTBOX> for UdsClient<PENDING, SIM_MAX_FRAME, SIM_MAX_OUTBOX, MAX_EVENTS, PERIODIC_DIDS, MAX_DATA>
Maximum UDS frame payload bytes = matches ace-server for bus compatibility. Maximum outbox depth.