IJ1939Node Interface¶
One application-layer SAE J1939 node bound to a single physical bus and a single 64-bit J1939Name identity. The node owns the SAE J1939-81 address-claim state (SRS FR-J1939-003/004), routes outbound PGNs through direct 29-bit frames (≤ 8 bytes) or the shared J1939-TP channel (> 8 bytes, SRS FR-J1939-006), and surfaces every inbound application PGN (including Request-PGN, SRS FR-J1939-005) via MessageReceived.
Implements IDisposable, IAsyncDisposable
Remarks¶
The node composes on the CanKit.Pro L2 services (ICanBusService for RX demux and TX confirmation, IProtocolActor for single-writer state, DeadlineScheduler for the SAE J1939-81 §4.4.3.3 250 ms arbitration window and periodic-send timing) and never touches vendor-specific SDKs directly.
IDisposable.Dispose is thread-safe and idempotent. It unwinds the underlying subscriptions and transport channel and faults an in-flight ClaimAddressAsync(byte, CancellationToken) with ObjectDisposedException -- or, for a claim that has already lost and still owes its Cannot Claim, with J1939CannotClaimException. It does not cancel an in-flight SendAsync(J1939Message, CancellationToken): a single-frame send ends as its confirmation does (a node that owns its bus service disposes that service last, which cancels a confirmation still awaited there), and a multi-frame send fails with ObjectDisposedException once the transport channel is torn down. ClaimState and Address keep the last values they had.
Called from a MessageReceived or AddressClaimChanged handler, which run on the node's actor, neither IDisposable.Dispose nor IAsyncDisposable.DisposeAsync waits for the teardown: it runs on the actor once that handler's turn has ended, a failure of it is reported through BackgroundExceptionOccurred, and a IAsyncDisposable.DisposeAsync called from anywhere else completes when it has.
Properties¶
IJ1939Node.Address Property¶
The node's claimed source address, or null when it has none
(NotClaimed / CannotClaim).
Property Value¶
IJ1939Node.ClaimState Property¶
Current SAE J1939-81 claim state (SRS FR-J1939-003/004).
Property Value¶
IJ1939Node.Name Property¶
The node's immutable 64-bit SAE J1939-81 NAME (from Name).
Property Value¶
IJ1939Node.Options Property¶
Node options captured at construction.
Property Value¶
Methods¶
IJ1939Node.ClaimAddressAsync(byte, CancellationToken) Method¶
Runs the SAE J1939-81 address-claim procedure for preferredAddress (SRS FR-J1939-003): 1. Transmit Address Claim (PGN 0xEE00, SA = preferred) carrying the node's NAME. 2. Listen for contending claims within ClaimAnnounceTimeout (default 250 ms). 3. On a losing contest (peer's NAME numerically lower, or equal — SAE J1939-81 §4.4.3.3 gives the address only to the lower NAME), transition to CannotClaim, broadcast Cannot Claim (SA = 0xFE) per SAE J1939-81 §4.4.3.4 and throw J1939CannotClaimException (SRS FR-J1939-004). An equal NAME does not scan for another address.
The Cannot Claim, and an arbitrary-address node's next claim after losing, go out after the pseudo-random 0..153 ms backoff of SAE J1939-81 §4.4.4.3 -- the low byte of the NAME's bytes summed, times 0.6 ms -- so two nodes colliding on an address do not answer in lockstep (#58). The task faults after that Cannot Claim has gone out, in the order step 3 gives, so disposing the node on the exception cannot suppress the frame; a further claim started before it drops the Cannot Claim and faults this one at once.
System.Threading.Tasks.Task ClaimAddressAsync(byte preferredAddress, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));
Parameters¶
preferredAddress Byte
cancellationToken CancellationToken
Returns¶
Exceptions¶
J1939CannotClaimException
The preferred address was lost to a
higher-priority NAME and no fallback was available, or to a peer with this node's
own NAME (SAE J1939-81 §4.4.3).
InvalidOperationException
A claim is still in arbitration: it is
not silently replaced -- await it, or cancel it, before claiming again (#58). The move
an arbitrary-address node makes on its own after losing a claimed address is not such a
claim: nothing awaits it, so this call replaces it -- in its backoff or in its
announcement window -- and the node claims preferredAddress instead of
finishing the move (#355).
IJ1939Node.RequestPgnAsync(uint, byte, CancellationToken) Method¶
Sends a Request-PGN (SAE J1939-21 PGN 0xEA00, SRS FR-J1939-005). The 3-byte payload carries requestedPgn little-endian; the request itself is a direct single-frame PGN. A destination of 0xFF makes the request global (every node responds with its usual PGN); any other value targets a specific ECU.
System.Threading.Tasks.Task RequestPgnAsync(uint requestedPgn, byte destinationAddress=byte.MaxValue, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));
Parameters¶
requestedPgn UInt32
destinationAddress Byte
cancellationToken CancellationToken
Returns¶
IJ1939Node.SendAsync(J1939Message, CancellationToken) Method¶
Sends message on the bus (SRS FR-J1939-001/006). Payloads ≤ 8 bytes go as one direct 29-bit CAN frame; larger payloads are broken into a TP.BAM (global destination) or TP.CM (specific destination) session on the shared J1939-TP channel. The task completes when the last frame of the message has been TX-confirmed (single frame) or when the TP session finishes (multi-frame).
System.Threading.Tasks.Task SendAsync(CanKit.Pro.J1939.J1939Message message, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));
Parameters¶
message J1939Message
cancellationToken CancellationToken
Returns¶
Exceptions¶
J1939NoAddressException
The node has not yet claimed an address.
IJ1939Node.StartPeriodicSend(J1939Message, TimeSpan) Method¶
Starts sending message periodically at the given period
(SRS FR-J1939-007). Every emission — single-frame and multi-frame alike — is driven by
the node's actor / SendAsync loop on top of the L2
DeadlineScheduler. Each iteration re-checks the
SAE J1939-81 claim state through SendAsync(J1939Message, CancellationToken)'s pre-flight gate, so a
concurrent address loss stops wire traffic automatically and a subsequent successful
claim (possibly on a different SA) resumes it — the emitted 29-bit ID is composed from
the currently-claimed SA on every tick.
Send failures are surfaced via BackgroundExceptionOccurred. Disposing the
returned handle stops the schedule. Multiple concurrent schedules for the same PGN are
allowed (callers may want to send the same PGN to two destinations).
The caller supplies period; mapping application PGNs to their
SAE J1939-71 standard rate is the caller's responsibility.
Payload snapshot:message's payload is snapshotted
into an owned buffer when this method returns and every emission transmits that
snapshot. In-place mutation of the caller's original buffer after
StartPeriodicSend is NOT observed on the wire — this matches
J1939Message's "payload is copied by the sender" contract
. To change the transmitted data, dispose the returned handle and
start a fresh schedule with a new J1939Message.
System.IDisposable StartPeriodicSend(CanKit.Pro.J1939.J1939Message message, System.TimeSpan period);
Parameters¶
message J1939Message
period TimeSpan
Returns¶
Exceptions¶
J1939NoAddressException
The node has not yet claimed an address
(matches SendAsync(J1939Message, CancellationToken)'s pre-flight gate).
Events¶
IJ1939Node.AddressClaimChanged Event¶
Raised whenever the node's ClaimState transitions (a claim starts, succeeds, is lost, or Cannot Claim is broadcast).
Event Type¶
EventHandler<J1939ClaimEventArgs>
IJ1939Node.BackgroundExceptionOccurred Event¶
Raised when a background failure (subscription faulted, actor exception, event-handler throw, TP channel background error) must be surfaced to the application.
Raised on a thread-pool thread, never on the node's actor loop, so a handler may wait on the node -- a send, a claim, IDisposable.Dispose, which then waits for the teardown as any other caller's does -- without deadlocking 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 node. 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 Type¶
IJ1939Node.MessageReceived Event¶
Raised on the actor's loop thread for every inbound application PGN — both direct single-frame PGNs and reassembled J1939-TP payloads (SRS FR-J1939-001/006). Request PGN (0xEA00) messages are also surfaced here so applications can respond in kind (SRS FR-J1939-005). Handlers must not throw; a throwing handler is caught and reported via BackgroundExceptionOccurred.