Skip to content

IUdsClient Interface

A single Unified Diagnostic Services (ISO 14229-1) client bound to one IIsoTpChannel. One client = one tester ↔ ECU relationship; overlapping requests from multiple callers are serialized by the client so at most one UDS request is outstanding at any time (mirrors ISO 14229-1 §7.3's "one active request per tester" model).

public interface IUdsClient : System.IDisposable

Implements IDisposable

Remarks

The surface covers eleven services: the seven of SRS FR-UDS-001..007 (0x10, 0x11, 0x22, 0x27, 0x2E, 0x31, 0x3E) and the four of the upload/download sequence, FR-UDS-012 (0x34, 0x35, 0x36, 0x37), with a multi-DID 0x22 (FR-UDS-011) and the one-shot DownloadAsync(byte, byte, ReadOnlyMemory<byte>, ReadOnlyMemory<byte>, ReadOnlyMemory<byte>, CancellationToken) and UploadAsync(byte, byte, ReadOnlyMemory<byte>, ReadOnlyMemory<byte>, CancellationToken) on top -- plus the raw escape hatch SendRawAsync(ReadOnlyMemory<byte>, CancellationToken). Every method: - Serializes on the client's internal request lock — callers may invoke from arbitrary threads. - Applies P2/P2* timing (SRS FR-UDS-008/009): the initial response is awaited for at most P2ClientMax; every NRC 0x78 (requestCorrectlyReceived-ResponsePending) restarts the P2* (P2StarClientMax) timer. - Surfaces negative responses as UdsNegativeResponseException with the request SID and the raw NRC byte (SRS FR-UDS-010).

The client honours the caller's CancellationToken for both the serialization gate and the response wait; a cancelled call abandons the response but does not corrupt the client (the next call will still see a clean state).

IDisposable.Dispose is thread-safe and idempotent; disposing an active client cancels any pending request and stops the TesterPresent keep-alive (if any). It then waits for the keep-alive loop to end and for the request in flight to release the request lock, each wait bounded by five seconds -- up to ten in all where both outlast the cancellation -- and holds the calling thread meanwhile. DisposeAsync(this IUdsClient) does the same without holding a thread.

Properties

IUdsClient.Channel Property

The channel this client is bound to (never null).

CanKit.Pro.IsoTp.IIsoTpChannel Channel { get; }

Property Value

IIsoTpChannel

IUdsClient.CurrentSession Property

The most recently negotiated session, updated on a successful DiagnosticSessionControlAsync(byte, CancellationToken) and reset to Default after a successful EcuResetAsync(UdsEcuResetType, CancellationToken). Starts at Default because ISO 14229-1 §9.2 guarantees an ECU starts up in the default session.

byte CurrentSession { get; }

Property Value

Byte

Remarks

Only those two methods move it. A DiagnosticSessionControl or ECUReset sent through SendRawAsync(ReadOnlyMemory<byte>, CancellationToken) -- answered, or suppressed (10 83, 11 81), which the ECU acts on without a word -- changes the ECU's session and leaves this property, and the P2/P2* budgets the session's record would have set, as they were (#354).

IUdsClient.Options Property

The immutable options used at construction.

CanKit.Pro.Uds.UdsClientOptions Options { get; }

Property Value

UdsClientOptions

Methods

IUdsClient.DiagnosticSessionControlAsync(byte, CancellationToken) Method

Sends DiagnosticSessionControl (0x10) with a raw sub-function byte. Convenience for vendor-specific session numbers that are not covered by UdsSessionType. The returned session parameter record sets the client's P2/P2* budgets as described on DiagnosticSessionControlAsync(UdsSessionType, CancellationToken).

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

Parameters

sessionType Byte

cancellationToken CancellationToken

Returns

Task<Byte[]>

Exceptions

ArgumentOutOfRangeException
sessionType is 0x00 (ISOSAEReserved) or has bit 7 set: that bit is suppressPosRspMsgIndication, not part of the session type, and this method waits for the response (#57).

IUdsClient.DiagnosticSessionControlAsync(UdsSessionType, CancellationToken) Method

Sends DiagnosticSessionControl (0x10, SRS FR-UDS-001) with a named UdsSessionType. Returns the raw parameter bytes echoed back by the ECU (session parameter record, typically 4 bytes carrying P2/P2* server timing).

System.Threading.Tasks.Task<byte[]> DiagnosticSessionControlAsync(CanKit.Pro.Uds.UdsSessionType session, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));

Parameters

session UdsSessionType

cancellationToken CancellationToken

Returns

Task<Byte[]>

Remarks

The record's P2Server_max (bytes 0..1, 1 ms units) and P2*Server_max (bytes 2..3, 10 ms units) set the client's budgets for every later request: P2 becomes the longer of P2ClientMax and P2Server_max + SessionTimingMargin, P2* the longer of P2StarClientMax and P2*Server_max + SessionTimingMargin. A configured budget is never shortened. Each successful response starts again from the configured budgets, so a record shorter than 4 bytes leaves them as configured; a successful EcuResetAsync(UdsEcuResetType, CancellationToken) returns to them as well (#340). Options keeps reporting the configured values.

IUdsClient.DownloadAsync(byte, byte, ReadOnlyMemory<byte>, ReadOnlyMemory<byte>, ReadOnlyMemory<byte>, CancellationToken) Method

Convenience one-shot download that runs the full 0x34 → N × 0x36 → 0x37 sequence for data. The client negotiates maxNumberOfBlockLength with RequestDownload, then loops TransferDataAsync(byte, ReadOnlyMemory<byte>, CancellationToken) with an automatically-managed block sequence counter (starts at 0x01, increments per block, wraps 0xFF → 0x00), and finally calls RequestTransferExitAsync(ReadOnlyMemory<byte>, CancellationToken). When dataFormatIdentifier is 0x00 (no compression / no encryption), memorySize is the big-endian byte count sent in RequestDownload and must equal the length of data; a mismatch is rejected before RequestDownload. When compression or encryption is selected, memorySize is the decoded target memory range and data is the encoded payload this client forwards unchanged, so their lengths are not compared.

System.Threading.Tasks.Task DownloadAsync(byte dataFormatIdentifier, byte addressAndLengthFormatIdentifier, System.ReadOnlyMemory<byte> memoryAddress, System.ReadOnlyMemory<byte> memorySize, System.ReadOnlyMemory<byte> data, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));

Parameters

dataFormatIdentifier Byte

addressAndLengthFormatIdentifier Byte

memoryAddress ReadOnlyMemory<Byte>

memorySize ReadOnlyMemory<Byte>

data ReadOnlyMemory<Byte>

cancellationToken CancellationToken

Returns

Task

Exceptions

ArgumentException
dataFormatIdentifier is 0x00 and memorySize does not decode to the length of data.

ArgumentOutOfRangeException
dataFormatIdentifier is 0x00 and memorySize has nonzero high-order bytes beyond what a UInt64 can represent.

UdsProtocolException
The ECU reported a maxNumberOfBlockLength of 0 or 1 so no payload byte would fit in a TransferData request, or a chunk validation failed.

Remarks

No block is retried. Beyond the repeat after NRC 0x21 (busyRepeatRequest) that every request gets (MaxBusyRepeatRequests), the first failure of a TransferData -- a negative response such as NRC 0x73 (wrongBlockSequenceCounter), a timeout, a transport error -- ends the call with that exception, and no RequestTransferExit follows, so the server's download is left where the failed block left it. This is a scope limit of this convenience method, not a reading of ISO 14229-1: whether and how the standard has a client repeat a block has not been checked against the text (#354). A caller that wants to repeat blocks drives RequestDownloadAsync(byte, byte, ReadOnlyMemory<byte>, ReadOnlyMemory<byte>, CancellationToken), TransferDataAsync(byte, ReadOnlyMemory<byte>, CancellationToken) and RequestTransferExitAsync(ReadOnlyMemory<byte>, CancellationToken) itself.

IUdsClient.EcuResetAsync(UdsEcuResetType, CancellationToken) Method

Sends ECUReset (0x11, SRS FR-UDS-005) and returns the raw powerDownTime parameter (0..1 bytes) echoed back by the ECU. On success, CurrentSession is reset to Default to match the ECU returning to the default session, and the P2/P2* budgets to the configured P2ClientMax / P2StarClientMax. Callers should typically wait for the ECU to reboot before issuing further requests.

System.Threading.Tasks.Task<byte[]> EcuResetAsync(CanKit.Pro.Uds.UdsEcuResetType resetType, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));

Parameters

resetType UdsEcuResetType

cancellationToken CancellationToken

Returns

Task<Byte[]>

IUdsClient.ReadDataByIdentifierAsync(IReadOnlyList<ushort>, IReadOnlyDictionary<ushort,int>, CancellationToken) Method

Sends ReadDataByIdentifier (0x22) with more than one DID in a single request (SRS FR-UDS-011, ISO 14229-1 §9.3.4). Returns a dictionary keyed by DID with the raw data-record bytes of each identifier the ECU answered. An ECU that supports only some of the requested DIDs answers with those (ISO 14229-1 §10.2), so a requested DID that has no key in the result was not answered; if none is supported the ECU's negative response (requestOutOfRange) is raised instead.

System.Threading.Tasks.Task<System.Collections.Generic.IReadOnlyDictionary<ushort,byte[]>> ReadDataByIdentifierAsync(System.Collections.Generic.IReadOnlyList<ushort> dataIdentifiers, System.Collections.Generic.IReadOnlyDictionary<ushort,int> dataRecordLengths, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));

Parameters

dataIdentifiers IReadOnlyList<UInt16>

DIDs to request, in the order they are placed on the wire.

dataRecordLengths IReadOnlyDictionary<UInt16,Int32>

Expected dataRecord length (bytes) for every requested DID. ISO 14229-1 §9.3.4.4 does not encode lengths in the positive response — the client must know them from the ECU's DID definition (ODX/CDD/etc.). Zero-length records are allowed.

cancellationToken CancellationToken

Cancels the wait for the ECU response.

Returns

Task<IReadOnlyDictionary<UInt16,Byte[]>>

Exceptions

ArgumentException
A requested DID has no length entry, or a length is negative.

UdsProtocolException
The ECU response is malformed, truncated, carries no data record at all, repeats a DID, or contains a DID that was not requested behind its first. A positive response whose <em>first</em> DID was not requested is not this request's answer but a stray -- the receive loop discards it and the wait goes on (#340). A DID that is merely not answered is not an error: it is absent from the result.

IUdsClient.ReadDataByIdentifierAsync(ushort, CancellationToken) Method

Sends ReadDataByIdentifier (0x22, SRS FR-UDS-002) for a single Data Identifier. Returns the raw data-record bytes that followed the DID in the positive response.

System.Threading.Tasks.Task<byte[]> ReadDataByIdentifierAsync(ushort dataIdentifier, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));

Parameters

dataIdentifier UInt16

cancellationToken CancellationToken

Returns

Task<Byte[]>

IUdsClient.RequestDownloadAsync(byte, byte, ReadOnlyMemory<byte>, ReadOnlyMemory<byte>, CancellationToken) Method

Sends RequestDownload (0x34, ISO 14229-1 §14.2, SRS FR-UDS-012). Negotiates a download session with the ECU: the tester declares the payload format (compression / encryption via dataFormatIdentifier) and the target memory range (memoryAddress, memorySize) whose widths are packed in addressAndLengthFormatIdentifier. The ECU replies with the maximum TransferData block length it will accept, wrapped in UdsDownloadResponse.

System.Threading.Tasks.Task<CanKit.Pro.Uds.UdsDownloadResponse> RequestDownloadAsync(byte dataFormatIdentifier, byte addressAndLengthFormatIdentifier, System.ReadOnlyMemory<byte> memoryAddress, System.ReadOnlyMemory<byte> memorySize, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));

Parameters

dataFormatIdentifier Byte

Byte encoding the compression method (high nibble) and encryption method (low nibble); 0x00 means "no compression / no encryption".

addressAndLengthFormatIdentifier Byte

Byte packing the width of memoryAddress (low nibble) and memorySize (high nibble) in bytes. Both widths MUST be in 1..0x0F and MUST match the respective buffer lengths.

memoryAddress ReadOnlyMemory<Byte>

Big-endian target start address.

memorySize ReadOnlyMemory<Byte>

Big-endian target byte count.

cancellationToken CancellationToken

Cancels the wait for the ECU response.

Returns

Task<UdsDownloadResponse>

Exceptions

ArgumentOutOfRangeException
A width nibble in addressAndLengthFormatIdentifier is zero, or does not match the buffer length.

IUdsClient.RequestTransferExitAsync(ReadOnlyMemory<byte>, CancellationToken) Method

Sends RequestTransferExit (0x37, ISO 14229-1 §14.4, SRS FR-UDS-012) to close the current transfer session. transferRequestParameterRecord is an optional vendor-specific record (e.g. checksum) appended after the SID.

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

Parameters

transferRequestParameterRecord ReadOnlyMemory<Byte>

cancellationToken CancellationToken

Returns

Task

IUdsClient.RequestUploadAsync(byte, byte, ReadOnlyMemory<byte>, ReadOnlyMemory<byte>, CancellationToken) Method

Sends RequestUpload (0x35, ISO 14229-1 §14.1, SRS FR-UDS-012). Mirror of RequestDownloadAsync(byte, byte, ReadOnlyMemory<byte>, ReadOnlyMemory<byte>, CancellationToken) for the tester-reads-from-ECU direction; the returned MaxNumberOfBlockLength constrains the size of TransferData responses the ECU will emit.

System.Threading.Tasks.Task<CanKit.Pro.Uds.UdsUploadResponse> RequestUploadAsync(byte dataFormatIdentifier, byte addressAndLengthFormatIdentifier, System.ReadOnlyMemory<byte> memoryAddress, System.ReadOnlyMemory<byte> memorySize, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));

Parameters

dataFormatIdentifier Byte

addressAndLengthFormatIdentifier Byte

memoryAddress ReadOnlyMemory<Byte>

memorySize ReadOnlyMemory<Byte>

cancellationToken CancellationToken

Returns

Task<UdsUploadResponse>

IUdsClient.RoutineControlAsync(UdsRoutineControlType, ushort, ReadOnlyMemory<byte>, CancellationToken) Method

Sends RoutineControl (0x31, SRS FR-UDS-004). The routine control option record (routineControlOptionRecord) is sent verbatim after the routine identifier; the returned array contains the routine info bytes from the positive response (excluding the echoed sub-function and routine identifier).

System.Threading.Tasks.Task<byte[]> RoutineControlAsync(CanKit.Pro.Uds.UdsRoutineControlType routineType, ushort routineIdentifier, System.ReadOnlyMemory<byte> routineControlOptionRecord=default(System.ReadOnlyMemory<byte>), System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));

Parameters

routineType UdsRoutineControlType

routineIdentifier UInt16

routineControlOptionRecord ReadOnlyMemory<Byte>

cancellationToken CancellationToken

Returns

Task<Byte[]>

IUdsClient.SecurityAccessAsync(byte, Func<byte[],byte[]>, CancellationToken) Method

Runs the ISO 14229-1 §9.4 SecurityAccess (0x27, SRS FR-UDS-006) two-step exchange: 1. Send 0x27 requestSeedLevel, receive the seed bytes. 2. Invoke computeKey synchronously with the seed and the returned key, then send 0x27 sendKeyLevel with the caller-computed key bytes. requestSeedLevel must be an odd byte in 0x01..0x7D and sendKeyLevel = requestSeedLevel + 1 is derived automatically, so at most 0x7E. A seed of all zeroes -- of any length, including none -- means "already unlocked" (ISO 14229-1 §9.4.5.3) and short-circuits the exchange without invoking computeKey or sending a key.

System.Threading.Tasks.Task SecurityAccessAsync(byte requestSeedLevel, System.Func<byte[],byte[]> computeKey, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));

Parameters

requestSeedLevel Byte

computeKey Func<Byte[],Byte[]>

cancellationToken CancellationToken

Returns

Task

Exceptions

ArgumentOutOfRangeException
requestSeedLevel is not an odd byte in the range 0x01..0x7D.

ArgumentNullException
computeKey is null.

Remarks

The client's request lock is held from the seed request to the key's answer, and so across computeKey: no other request -- the StartTesterPresentKeepAlive(Nullable<TimeSpan>) TesterPresent included -- goes out in between, so nothing can come between seed and key on the ECU's side. The cost is that a computeKey that takes longer than the server's S3 timer (an HSM-backed one, say) also holds back the keep-alive, and the session may time out before the key is sent. Whether ISO 14229-1 lets a TesterPresent between seed and key leave the seed valid -- in which case the keep-alive alone could be let through during computeKey -- has not been checked against the text; until it is, the lock stays (#354).

IUdsClient.SendRawAsync(ReadOnlyMemory<byte>, CancellationToken) Method

Sends the raw request bytes verbatim (starting with the SID) and returns the ECU's raw positive-response bytes (again including the response SID). The same P2/P2* timing and NRC handling apply as for the strongly-typed methods. A request whose sub-function byte has suppressPosRspMsgIndication set (bit 7, on a service that carries a sub-function) is sent without waiting: an empty array comes back once the frame is confirmed, and a negative response the server may still send is not waited for (#57).

System.Threading.Tasks.Task<byte[]> SendRawAsync(System.ReadOnlyMemory<byte> request, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));

Parameters

request ReadOnlyMemory<Byte>

cancellationToken CancellationToken

Returns

Task<Byte[]>

Remarks

The request is not interpreted: a session change or reset sent here does not move CurrentSession or the P2/P2* budgets, whether it is answered or suppressed (#354). Use DiagnosticSessionControlAsync(byte, CancellationToken) and EcuResetAsync(UdsEcuResetType, CancellationToken) where the client should follow the session.

IUdsClient.StartTesterPresentKeepAlive(Nullable<TimeSpan>) Method

Starts a background TesterPresent (0x3E) keep-alive that fires every period (defaults to TesterPresentPeriod) until the returned IDisposable is disposed or the client itself is disposed. The keep-alive uses TesterPresentAsync(bool, CancellationToken) under the hood so it serializes with normal requests through the same request lock; a slow request never causes a stale keep-alive frame to interleave (SRS FR-UDS-007).

System.IDisposable StartTesterPresentKeepAlive(System.Nullable<System.TimeSpan> period=null);

Parameters

period Nullable<TimeSpan>

Override for the send interval; must be positive.

Returns

IDisposable

Exceptions

InvalidOperationException
A keep-alive is already running; call IDisposable.Dispose on the previous handle first.

Remarks

A TesterPresent that fails -- a timeout, a negative response, a transport error -- is dropped and the next one goes out a period later; nothing reports it, and the channel's BackgroundExceptionOccurred does not carry it either. A session the ECU has left (after a reset, or with S3 run out) therefore shows only in the answer to the next request, typically NRC 0x7E or 0x7F. The keep-alive ends without notice when the client or its channel is disposed.

IUdsClient.TesterPresentAsync(bool, CancellationToken) Method

Sends a single TesterPresent (0x3E, SRS FR-UDS-007). When suppressPositiveResponse is true the ISO 14229-1 §7.5 suppressPositiveResponse bit is set (sub-function 0x80) and the method returns as soon as the request is on the wire — the ECU is expected to stay silent. Otherwise the method awaits the positive response.

System.Threading.Tasks.Task TesterPresentAsync(bool suppressPositiveResponse=true, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));

Parameters

suppressPositiveResponse Boolean

cancellationToken CancellationToken

Returns

Task

IUdsClient.TransferDataAsync(byte, ReadOnlyMemory<byte>, CancellationToken) Method

Sends TransferData (0x36, ISO 14229-1 §14.3, SRS FR-UDS-012) with the caller-supplied block sequence counter and payload chunk. Returns the ECU's transferResponseParameterRecord (the bytes that follow the echoed block sequence counter in the positive response — may be empty). The client validates that the ECU echoes back the exact blockSequenceCounter.

System.Threading.Tasks.Task<byte[]> TransferDataAsync(byte blockSequenceCounter, System.ReadOnlyMemory<byte> data, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));

Parameters

blockSequenceCounter Byte

Per ISO 14229-1 §14.3.2 the counter starts at 0x01 for the first TransferData following RequestDownload/Upload, is incremented on each successful transfer, and wraps from 0xFF back to 0x00.

data ReadOnlyMemory<Byte>

Payload chunk. Total request size (SID + BSC + payload = 2 + data.Length) MUST NOT exceed the ECU's MaxNumberOfBlockLength. May be empty when the ECU is expected to synthesise data (e.g. the upload direction).

cancellationToken CancellationToken

Cancels the wait for the ECU response.

Returns

Task<Byte[]>

IUdsClient.UploadAsync(byte, byte, ReadOnlyMemory<byte>, ReadOnlyMemory<byte>, CancellationToken) Method

Convenience one-shot upload that runs the full 0x35 → N × 0x36 → 0x37 sequence and returns the bytes read from the ECU. The client negotiates with RequestUpload, then loops TransferDataAsync(byte, ReadOnlyMemory<byte>, CancellationToken) with an automatically-managed block sequence counter (starts at 0x01, increments per block, wraps 0xFF → 0x00), concatenating the ECU's transferResponseParameterRecords until memorySize bytes were read, and finally calls RequestTransferExitAsync(ReadOnlyMemory<byte>, CancellationToken).

System.Threading.Tasks.Task<byte[]> UploadAsync(byte dataFormatIdentifier, byte addressAndLengthFormatIdentifier, System.ReadOnlyMemory<byte> memoryAddress, System.ReadOnlyMemory<byte> memorySize, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));

Parameters

dataFormatIdentifier Byte

addressAndLengthFormatIdentifier Byte

memoryAddress ReadOnlyMemory<Byte>

memorySize ReadOnlyMemory<Byte>

cancellationToken CancellationToken

Returns

Task<Byte[]>

Exceptions

ArgumentOutOfRangeException
memorySize has nonzero high-order bytes beyond what a UInt64 can represent; nothing is sent.

UdsProtocolException
The ECU delivered an empty TransferData payload before the declared size was reached, or memorySize exceeds what can reasonably be buffered.

IUdsClient.WriteDataByIdentifierAsync(ushort, ReadOnlyMemory<byte>, CancellationToken) Method

Sends WriteDataByIdentifier (0x2E, SRS FR-UDS-003). Completes successfully when the ECU echoes the DID; the DID echo is validated against the request.

System.Threading.Tasks.Task WriteDataByIdentifierAsync(ushort dataIdentifier, System.ReadOnlyMemory<byte> data, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));

Parameters

dataIdentifier UInt16

data ReadOnlyMemory<Byte>

cancellationToken CancellationToken

Returns

Task