Skip to content

IIsoTpChannel Interface

A single ISO 15765-2 (ISO-TP) channel bound to one IsoTpEndpoint (TX/RX CAN-ID pair, plus addressing mode). Threading model per SRS FR-TP-016 / FR-RAW-020..023: all protocol state is owned by a single IProtocolActor mailbox, so callers may invoke SendAsync(ReadOnlyMemory<byte>, CancellationToken) concurrently from arbitrary threads and receive frames from arbitrary threads; internal state is never mutated from more than one place at a time.

public interface IIsoTpChannel : System.IDisposable

Implements IDisposable

Remarks

One channel = one PDU at a time on the wire: an outgoing multi-frame PDU (First-Frame + Consecutive-Frames) is completed (or aborted with an IsoTpException) before the next SendAsync(ReadOnlyMemory<byte>, CancellationToken) call is transmitted. This mirrors ISO 15765-2 §6.4's "one N-USData at a time" model; overlapping sends from different callers are serialized by the channel.

Received PDUs are delivered both as an event (DatagramReceived) and as an System.Collections.Generic.IAsyncEnumerable<> from ReceiveAllAsync(CancellationToken); a single ReceiveAsync(CancellationToken) call awaits the next one. Both surfaces share the same bounded buffer.

IDisposable.Dispose is thread-safe and idempotent (FR-RAW-021).

Properties

IIsoTpChannel.Endpoint Property

The endpoint this channel is bound to (TX/RX CAN-ID pair + addressing mode).

CanKit.Pro.IsoTp.IsoTpEndpoint Endpoint { get; }

Property Value

IsoTpEndpoint

IIsoTpChannel.Options Property

The channel options used at construction (immutable).

CanKit.Pro.IsoTp.IsoTpChannelOptions Options { get; }

Property Value

IsoTpChannelOptions

Methods

IIsoTpChannel.DiscardPendingPdus() Method

Drains every buffered inbox item — both completed PDUs and pending reassembly-abort faults enqueued by AbortRx — and returns how many were dropped. Also silently aborts any in-flight multi-frame reassembly on the actor so leftover consecutive frames cannot finish and enqueue a stale PDU after a higher-layer timeout/cancel Higher layers (e.g. UDS) call this after a cancelled/timed-out wait so a late peer PDU or a leftover abort fault cannot be consumed as the answer to a later request. A receive on another thread meanwhile reads the inbox before the discard or after it, never in between, so it neither takes an item the discard drops nor misses one it keeps.

int DiscardPendingPdus();

Returns

Int32

IIsoTpChannel.DiscardPendingPdus(long) Method

As DiscardPendingPdus(), dropping what arrived before arrivedBefore (a GetTimestamp reading) rather than before now, and keeping what arrived since. For a caller that inspects the inbox before discarding — routing an NRC 0x78 to its window — the stamp taken before the inspection makes the two one step: everything the discard drops was inspected, and a frame arriving between the inspection and the discard is kept for the caller's next read instead of vanishing.

int DiscardPendingPdus(long arrivedBefore);

Parameters

arrivedBefore Int64

Returns

Int32

IIsoTpChannel.GetReceptionsInProgress() Method

The multi-frame receptions in progress — First Frames read off the bus whose PDUs have not been delivered — oldest first, each as its First Frame: arrival, announced length and leading data bytes. For a caller whose own deadline ends with the first frame of a response and not with its last (ISO 14229-2 P2, #28): a response that began in time is then waited for beyond that deadline, bounded by the transport's N_Cr instead — after checking from the leading bytes that it is the response being waited for. More than one is possible when the channel's actor is behind the bus; a record is withdrawn only once its outcome — the PDU, or the error of an aborted reassembly — is in the inbox, or the frame was refused. A snapshot: a reception can begin, complete or be refused the moment after, so a caller waiting on one re-checks rather than waiting unboundedly.

System.Collections.Generic.IReadOnlyList<CanKit.Pro.IsoTp.IsoTpReceptionInProgress> GetReceptionsInProgress();

Returns

IReadOnlyList<IsoTpReceptionInProgress>

IIsoTpChannel.ReceiveAllAsync(CancellationToken) Method

Enumerates every fully reassembled inbound PDU as it becomes available. The enumeration ends when the channel is disposed. A reassembly abort (N_Cr / CF sequence mismatch / SF·FF supersede) faults the enumerator with the same exception ReceiveAsync(CancellationToken) would throw. Cancel by passing a token via WithCancellation or by disposing the channel.

System.Collections.Generic.IAsyncEnumerable<byte[]> ReceiveAllAsync(System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));

Parameters

cancellationToken CancellationToken

Returns

IAsyncEnumerable<Byte[]>

IIsoTpChannel.ReceiveAsync(CancellationToken) Method

Awaits the next fully reassembled inbound PDU. Cancels via cancellationToken. Faults with IsoTpTimeoutException (NCr) or IsoTpException when an in-progress multi-frame reassembly is aborted (N_Cr, CF sequence mismatch, or a superseding SF/FF — including when the new FF is refused with FC(OVFLW); FR-TP-010), so a caller waiting for that PDU does not hang indefinitely. A PDU queued before either end below is still delivered; only an empty inbox meets the end. Faults with InvalidOperationException once the channel is disposed, and with ObjectDisposedException -- or the failure itself -- once the bus service under the channel was disposed or its subscription failed: nothing will arrive again, and every waiting and later receive gets that same exception. The failure is also raised once through BackgroundExceptionOccurred.

System.Threading.Tasks.Task<byte[]> ReceiveAsync(System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));

Parameters

cancellationToken CancellationToken

Returns

Task<Byte[]>

IIsoTpChannel.ReceiveWithArrivalAsync(CancellationToken) Method

As ReceiveAsync(CancellationToken), with the same ends and faults, but also reports the monotonic instant the PDU was queued.

System.Threading.Tasks.Task<CanKit.Pro.IsoTp.IsoTpReceivedPdu> ReceiveWithArrivalAsync(System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));

Parameters

cancellationToken CancellationToken

Returns

Task<IsoTpReceivedPdu>

Remarks

For callers that enforce a response deadline. ReceiveAsync(CancellationToken) can only tell them when they observed the PDU, and a caller descheduled past its own deadline cannot tell a punctual response from a late one on that basis — it would either accept a response it had already given up on, or reject a timely one for arriving while it was not looking. The arrival timestamp removes the ambiguity, so the deadline holds regardless of scheduling.

IIsoTpChannel.SendAsync(ReadOnlyMemory<byte>, CancellationToken) Method

Sends pdu as one ISO-TP N-USData PDU. The returned task completes when the last frame of the PDU is TX-confirmed by the driver (SF) or when the peer has FC-cleared the entire multi-frame PDU and the last CF is confirmed (FF+CFs). It faults with an IsoTpException on protocol timeouts (FR-TP-010), an Overflow FC (FR-TP-012), exceeding WFTmax (FR-TP-011), or a driver rejection; with ObjectDisposedException when the channel is disposed before or during the send, including while the call is queued behind another send; with OperationCanceledException when cancellationToken is cancelled; and with whatever the bus layer threw from the driver call, unwrapped. An empty or over-long pdu is refused before anything is queued, with ArgumentException / ArgumentOutOfRangeException.

System.Threading.Tasks.Task SendAsync(System.ReadOnlyMemory<byte> pdu, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));

Parameters

pdu ReadOnlyMemory<Byte>

User data, 1..4095 bytes for classic CAN, or up to MaxFdFirstFrameLength for CAN-FD. Must not be empty.

cancellationToken CancellationToken

Cancels the returned task (standard .NET convention); a canceled send aborts the in-flight PDU. The channel keeps the one-PDU-at-a-time send gate until any already-submitted bus TX from that PDU completes, so the next call cannot interleave frames on the wire with the aborted transfer.

Returns

Task

IIsoTpChannel.SendWithTransmitStampAsync(ReadOnlyMemory<byte>, CancellationToken) Method

As SendAsync(ReadOnlyMemory<byte>, CancellationToken), but returns the two monotonic (GetTimestamp) instants bracketing the driver call for the PDU's last frame: just before it was handed over, and no later than the driver accepted it (see IsoTpTransmitStamps). Zero for an instant not reported.

System.Threading.Tasks.Task<CanKit.Pro.IsoTp.IsoTpTransmitStamps> SendWithTransmitStampAsync(System.ReadOnlyMemory<byte> pdu, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));

Parameters

pdu ReadOnlyMemory<Byte>

cancellationToken CancellationToken

Returns

Task<IsoTpTransmitStamps>

Remarks

The send-side counterpart of ReceiveWithArrivalAsync(CancellationToken), and needed for the same reason. A caller whose response deadline starts when its request went out cannot read that instant off its own clock: awaiting this method returns behind the bus TX confirmation, an actor hop and the caller's own scheduling, all of which happen after the peer already has the request. Timing the deadline from the returned stamp makes its start as independent of scheduling as the arrival stamp makes its end -- pinning only one of the two leaves the deadline movable from the other side.

IIsoTpChannel.SettleAsync(CancellationToken) Method

Completes once every frame the bus had delivered before the call has been taken through the channel: a PDU it completed is in the inbox, a reception it began is in GetReceptionsInProgress(). For a decision taken at a deadline — is a response there, was there a 0x78 — what the inbox does not hold after this did not arrive before the call; without it, a frame stamped in time can still be on its way through the channel's actor when the deadline fires. Called on the channel's own actor — from a BackgroundExceptionOccurred handler — it returns at once and the frames on their way are handled after the current work item: they queue behind frames already in the mailbox, and handling them inline would reorder the two.

cancellationToken ends the wait, not the settling: what the demux had buffered has been handed to the actor by then and is handled as usual. A token cancelled before the call, or while the wait is still queued behind the actor's other work, cancels the returned task; once the actor has reached the wait it completes normally.

System.Threading.Tasks.Task SettleAsync(System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));

Parameters

cancellationToken CancellationToken

Ends the wait for the actor to catch up.

Returns

Task

IIsoTpChannel.TryReceiveWithArrival(IsoTpReceivedPdu) Method

Takes the next inbound PDU if one is already queued, without waiting for one to arrive. Returns false — leaving pdu at its default — when the inbox is empty; a PDU queued before the channel was disposed, or before its bus service was lost, is still returned afterwards. Throws the recorded reassembly-abort exception when the queued item is a fault, exactly as ReceiveWithArrivalAsync(CancellationToken) would; neither end of the inbox is reported here. A DiscardPendingPdus(long) running concurrently is waited for: the read is made either before the discard or after it, never while it has the inbox, so a PDU the discard retains is not reported absent, and one it drops is not returned (#383). Every receive of this channel reads the inbox the same way.

bool TryReceiveWithArrival(out CanKit.Pro.IsoTp.IsoTpReceivedPdu pdu);

Parameters

pdu IsoTpReceivedPdu

Returns

Boolean

Remarks

The companion to ReceiveWithArrivalAsync(CancellationToken) for a caller whose deadline has already passed. Awaiting with an expired token is not the same thing: an already-cancelled token wins against a queued item, so the wait would report a timeout while the answer sat unread in the inbox. Separating <em>how long to wait</em> from <em>was it in time</em> leaves the second question to the arrival stamp, which is the only reading of it that does not depend on when the caller was scheduled.

Events

IIsoTpChannel.BackgroundExceptionOccurred Event

Raised when a background failure (reassembly abort, event-handler exception, subscription failure, actor loop exception) needs to be surfaced to the application. This is the documented channel for out-of-band errors (FR-RAW-023). Failures tied to a specific SendAsync(ReadOnlyMemory<byte>, CancellationToken) call are reported via that call's returned task; reassembly aborts are reported both here and by faulting a pending ReceiveAsync(CancellationToken) (FailTx analogue on the receive side).

Raised on a thread-pool thread, never on the channel's actor loop, and only once what the report announces is in place: a reassembly abort has already faulted the inbox, so a handler that answers it with ReceiveAsync(CancellationToken), DiscardPendingPdus() or a send -- or disposes the channel -- does not deadlock the actor (#356). Reports are delivered one at a time, in the order they were made; a handler that blocks holds up the reports behind it, not the channel. Each handler is called on its own, so one that throws does not keep the report from the handlers after it; its exception is dropped. A report may reach its handlers after IDisposable.Dispose has returned.

event EventHandler<Exception>? BackgroundExceptionOccurred;

Event Type

EventHandler<Exception>

IIsoTpChannel.DatagramReceived Event

Raised (on a thread-pool thread) every time a full PDU is reassembled. The same PDU is enqueued for ReceiveAsync(CancellationToken)/ReceiveAllAsync(CancellationToken) before the event fires, so a handler that synchronously waits on those APIs — or on DiscardPendingPdus() — cannot deadlock the protocol actor. Handlers must be non-throwing; a throwing handler is caught and surfaced via BackgroundExceptionOccurred.

Each PDU gets its own thread-pool work item, so handlers for successive PDUs may run concurrently and not necessarily in the order the PDUs completed; ReceiveAsync(CancellationToken)/ReceiveAllAsync(CancellationToken) keep that order. No new PDU is raised for a frame handled after IDisposable.Dispose has begun, but a handler already queued, or starting at that moment, may still run: Dispose does not wait for handlers, and a handler may itself call it.

event EventHandler<IsoTpDatagramReceivedEventArgs>? DatagramReceived;

Event Type

EventHandler<IsoTpDatagramReceivedEventArgs>