Skip to content

ICanBusService Interface

One demultiplexing service instance per ICanBus: it turns the single RX stream exposed by ICanBus.FrameObserved into N independent, filtered read-only ISubscriptions, so multiple protocol instances (ISO-TP, J1939, CANopen, …) can each see their own view of the bus without competing over ICanBus.ReceiveAsync(Int32, Int32, CancellationToken) (arc42 §5.3, ADR-5; FR-RAW-010..013).

public interface ICanBusService : System.IDisposable

Derived
CanBusService

Implements IDisposable

Remarks

The service is built purely on top of the public ICanBus.FrameObserved surface (a read-only CanFrameView per frame, with no disposal/ownership concerns), so it works identically for every adapter with no per-adapter changes. Disposing the service unwinds all outstanding subscriptions and detaches its handler from the underlying bus (FR-RAW-012). Dispose is idempotent.

Echoes. When the bus is configured for echo (WorkMode == ChannelWorkMode.Echo) it reports the host's own transmissions back through the same RX stream, flagged as such. A subscription receives them only if it asked for them, and the flag rides along on every delivered CanFrameEvent either way. The default is off because the frames a protocol layer sends are not frames it received: a J1939 node that treats its own Address Claim as a competitor's, or a CANopen node that acts on its own PDO, is broken in a way that only shows up on hardware that happens to echo. Before this flag existed each of those layers each carried their own answer to "is this mine?" — comparing NAMEs, comparing source addresses, guarding on actor affinity. Those checks are still there and still needed; what changed is that the flag and the timestamp are now available to a caller who wants them.

Two limits, both of which make the gate a convenience rather than a guarantee. First, it can only drop what the adapter flags: an adapter that echoes without setting IsEchoCanKit.Adapter.Virtual in ChannelWorkMode.Echo does — delivers its echo to every subscription regardless of includeEcho. Second, and more important, the flag is host-scoped: it says something on this host transmitted the frame, not which of the possibly several protocol instances sharing this service did. A sibling instance's traffic is flagged identically to one's own.

So includeEcho: false suits a single consumer that owns its bus. A protocol layer that may share a service — every one in this repository does, by documented design — asks for echoes instead, and must then tell its own traffic apart by something it actually owns. That is not a workaround for a missing feature: the demux genuinely cannot attribute a transmission to a local instance.

How far each layer takes that is a per-layer decision, not a guarantee this interface makes. The J1939 transport rejects its own source address, and J1939 rejects its own NAME on an Address Claim. CANopen deliberately does not filter its own non-SYNC traffic by node-id — the state that predates the echo gate — so a CANopen node on a flagging adapter still observes its own PDOs and heartbeats.

Properties

ICanBusService.Bus Property

The underlying bus this service demultiplexes.

CanKit.Abstractions.API.Can.ICanBus Bus { get; }

Property Value

ICanBus

ICanBusService.SubscriptionCount Property

Number of currently registered (not yet disposed) subscriptions. Primarily for diagnostics/tests: after disposing every subscription it returns to zero, proving no registry entries leak (FR-RAW-012).

int SubscriptionCount { get; }

Property Value

Int32

Methods

ICanBusService.FindOverlappingFilterSubscriptions() Method

Diagnostic: finds every pair of currently registered, still-undisposed CanIdFilter-based subscriptions whose ID spaces overlap, and the range of CAN IDs each pair shares (FR-RAW-041, "Should") -- helps catch misconfiguration when multiple protocol instances were meant to have disjoint ID ranges but don't. Subscriptions registered via the generic Subscribe(Func<CanFrameEvent,bool>, Nullable<int>, bool) predicate overload are opaque and are not analyzable, so they are skipped.

System.Collections.Generic.IReadOnlyList<CanKit.Pro.RawCan.FilterOverlap> FindOverlappingFilterSubscriptions();

Returns

IReadOnlyList<FilterOverlap>
One FilterOverlap per overlapping pair, each naming the two subscriptions and the ID range on which they collide. Empty when no two filters share ID space.

ICanBusService.SendConfirmed(CanFrame, Nullable<TimeSpan>, CancellationToken) Method

Sends frame and asynchronously confirms it was actually sent, using a uniform abstraction regardless of whether the underlying bus has hardware TX echo enabled (arc42 §6.3, ADR-7; FR-RAW-030). When the bus both declares Echo and has WorkMode == ChannelWorkMode.Echo configured, confirmation comes from an actually-matched echo frame (FR-RAW-031, including correct FIFO matching of multiple concurrent byte-identical sends — no cross-matching or crash); otherwise it is a documented approximation based on driver acceptance (IsApproximated, FR-RAW-032). Never hangs: timeout, bus-off, and outright rejection all resolve the returned task within bounded time (FR-RAW-033) — see TxConfirmation for exactly how.

System.Threading.Tasks.Task<CanKit.Pro.RawCan.TxConfirmation> SendConfirmed(CanKit.Abstractions.API.Can.Definitions.CanFrame frame, System.Nullable<System.TimeSpan> timeout=null, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));

Parameters

frame CanFrame

The frame to send. As with ICanBus.Transmit(CanFrame@), the caller remains the owner (TX-lease) and is responsible for disposing it after this call returns/completes — ICanBusService never disposes it.

timeout Nullable<TimeSpan>

Maximum time to wait for an echo before failing with Timeout (FR-RAW-034); null uses DefaultConfirmTimeout. Ignored on the approximated path (driver acceptance is synchronous/immediate). Must be positive.

cancellationToken CancellationToken

Caller-supplied cancellation; cancels the returned task per standard .NET convention, distinct from the domain-level Timeout outcome.

Returns

Task<TxConfirmation>

ICanBusService.Subscribe(CanIdFilter, Nullable<int>, bool) Method

Registers a subscription using the allocation-free ID-range/mask fast path (FR-RAW-010/013).

CanKit.Pro.RawCan.ISubscription Subscribe(CanKit.Pro.RawCan.CanIdFilter filter, System.Nullable<int> bufferCapacity=null, bool includeEcho=false);

Parameters

filter CanIdFilter

ID-range or acceptance-code/mask filter.

bufferCapacity Nullable<Int32>

Bounded buffer capacity for this subscription; null uses DefaultBufferCapacity.

includeEcho Boolean

Whether this subscription also receives the local host's own transmit echoes; false by default. See the predicate overload above.

Returns

ISubscription

ICanBusService.Subscribe(Func<CanFrameEvent,bool>, Nullable<int>, bool) Method

Registers a subscription that receives every frame for which predicate returns true; a null predicate accepts all frames (FR-RAW-010).

CanKit.Pro.RawCan.ISubscription Subscribe(System.Func<CanKit.Pro.RawCan.CanFrameEvent,bool>? predicate=null, System.Nullable<int> bufferCapacity=null, bool includeEcho=false);

Parameters

predicate Func<CanFrameEvent,Boolean>

Per-frame filter, or null to accept all frames. Runs on the bus's dispatch thread before the payload is copied, so the Frame it inspects aliases the adapter's RX lease and must not be retained beyond the call. It is never offered an echo unless includeEcho is set.

bufferCapacity Nullable<Int32>

Bounded buffer capacity for this subscription; null uses DefaultBufferCapacity. When the buffer is full the oldest buffered frame is dropped so dispatch never blocks (FR-RAW-011).

includeEcho Boolean

Whether this subscription also receives the local host's own transmit echoes. Defaults to false: a protocol layer that sees its own transmissions come back as if they were peer traffic misbehaves in ways that are tedious to diagnose, so opting in is a decision the caller makes deliberately (see the remarks below).

Returns

ISubscription

Events

ICanBusService.BackgroundExceptionOccurred Event

Raised when a caller-supplied subscription filter predicate throws during dispatch (FR-RAW-023-style fault channel). The failing frame is isolated to that subscription (delivery to the other subscriptions continues), and the exception is surfaced here instead of being silently swallowed. Invoked synchronously on the bus's dispatch thread, so handlers must return quickly and must not call back into the service.

event EventHandler<Exception>? BackgroundExceptionOccurred;

Event Type

EventHandler<Exception>