CanKit.Pro.RawCan 1.3.0

dotnet add package CanKit.Pro.RawCan --version 1.3.0
                    
NuGet\Install-Package CanKit.Pro.RawCan -Version 1.3.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CanKit.Pro.RawCan" Version="1.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CanKit.Pro.RawCan" Version="1.3.0" />
                    
Directory.Packages.props
<PackageReference Include="CanKit.Pro.RawCan" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add CanKit.Pro.RawCan --version 1.3.0
                    
#r "nuget: CanKit.Pro.RawCan, 1.3.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package CanKit.Pro.RawCan@1.3.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=CanKit.Pro.RawCan&version=1.3.0
                    
Install as a Cake Addin
#tool nuget:?package=CanKit.Pro.RawCan&version=1.3.0
                    
Install as a Cake Tool

CanKit.Pro.RawCan

Raw-CAN service layer for CanKit: multi-protocol demultiplexing / subscriptions (arc42 §5.3, ADR-5; SRS FR-RAW-010..015) and a TX-confirm abstraction (arc42 §6.3, ADR-7; SRS FR-RAW-030..034).

Status: 1.0.0 – 1.2.3 are withdrawn from nuget.org — they were published as stable before the API had been reviewed. 1.3.0 will be the first release whose API is stable; until it is tagged there is no listed version to install, so the dotnet add package line below resolves nothing and the withdrawn releases come back only on an exact version pin. The public surface can still change until then. See Versioning.

What is validated, and what is not

Validated: Demultiplexing and subscriptions, filter overlap, TX confirmation and concurrency, by the test suite in tests/CanKit.Pro.Tests, over CanKit.Adapter.Virtual and in-repository bus doubles.

Not validated: The behaviour of real adapters. TX echo as SocketCAN, Kvaser and Vector deliver it is modelled by a test double (ControllableBus.EchoCapable), not observed on those adapters; the "accepted by the driver" approximation for adapters without echo has not been checked against a real driver. Nothing in this package has run against real CAN hardware, a conformance tester or a third-party implementation: the test project references CanKit.Adapter.Virtual and no hardware adapter.

One ICanBusService wraps one ICanBus and turns its single FrameObserved RX stream into N independent, filtered, read-only ISubscriptions — so several protocol instances (ISO-TP, J1939, CANopen, …) can each see their own view of the same bus without competing over ReceiveAsync and without one slow consumer blocking the others.

using CanKit.Core;
using CanKit.Pro.RawCan;

using var bus = CanBus.Open("virtual://demo/0", cfg => cfg.SetProtocolMode(CanProtocolMode.Can20).Baud(500_000));
using var service = new CanBusService(bus);

// Fast path: one 11-bit ID range per protocol instance (no per-frame delegate).
using var isoTp = service.Subscribe(CanIdFilter.Range(0x700, 0x7FF));

// Generic predicate when a range/mask is not enough.
using var custom = service.Subscribe(e => e.Frame.IsExtendedFrame && e.Frame.Len == 8);

await foreach (var e in isoTp.Frames.WithCancellation(token))
{
    // e.Frame            read-only CanFrameView, no ownership/disposal concerns, and it owns
    //                    its payload -- valid after the adapter has released the RX lease
    // e.IsEcho           the bus's own echo flag (see below)
    // e.ReceiveTimestamp what the adapter recorded; zero on adapters that do not timestamp
}

Each subscription owns its own bounded, drop-oldest buffer (FR-RAW-011). Disposing a subscription deterministically deregisters it and completes its Frames stream; disposing the service unwinds all subscriptions and detaches from the bus (FR-RAW-012). Call subscription.Reconfigure(CanIdFilter) or Reconfigure(predicate) to change filter criteria at runtime without recreating the subscription (FR-RAW-014); only frames observed after the call follow the new criterion. This layer is built purely on the public ICanBus.FrameObserved surface, so it works identically for every adapter.

Echoes

A bus opened with WorkMode == ChannelWorkMode.Echo reports the host's own transmissions back through the same RX stream, flagged as echoes. Subscriptions do not deliver them unless asked:

using var quiet = service.Subscribe();                     // peer traffic only (the default)
using var trace = service.Subscribe(includeEcho: true);    // everything, e.Frame + e.IsEcho

Off by default because frames you sent are not frames you 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 only on the hardware that happens to echo. Where a subscription did not opt in, an echo is dropped before the filter runs, so it never reaches a caller-supplied predicate either.

Two limits make this a convenience rather than a guarantee, and both matter:

The gate only drops what the adapter flags. An adapter that echoes without setting IsEcho delivers its echo to every subscription no matter what includeEcho says — CanKit.Adapter.Virtual in ChannelWorkMode.Echo is such an adapter today.

IsEcho is host-scoped, not instance-scoped. It means something on this host sent this, not I sent this. Several protocol instances may share one ICanBusService (every factory here documents that), and a sibling's transmission carries the same flag as your own. So a protocol layer that shares a service asks for echoes instead, and tells its own traffic apart by something it owns. includeEcho: false is for a single consumer that owns its bus, such as a monitor or a one-node application.

How far each layer takes that differs, and this package does not promise it uniformly: J1939-TP rejects its own source address, the J1939 node rejects its own NAME on an Address Claim and its own source address on an application PGN, and CANopen rejects its own node id on an EMCY or a heartbeat.

None of them is a blanket self-filter, and the exceptions are the interesting part: a J1939 frame addressed to the node itself is delivered, so a request against your own address is answered; CANopen delivers NMT, SYNC, both SDO directions and RPDOs from its own producer, and still feeds a heartbeat or node-guarding consumer explicitly registered for the local node id. The rule each layer follows is that an explicitly configured or explicitly addressed frame outranks a guess about who sent it.

SendConfirmedAsync is independent of this: it matches echoes on the bus event itself, so withholding them from subscribers does not affect TX confirmation.

TX-Confirm

SendConfirmedAsync gives a uniform "was this frame actually sent" answer regardless of whether the bus has hardware TX echo enabled:

// Bus opened with CanFeature.Echo + WorkMode = ChannelWorkMode.Echo -> real echo matching.
// Otherwise -> confirmed as soon as the driver accepts the frame (TxConfirmation.IsApproximated).
var result = await service.SendConfirmedAsync(CanFrame.Classic(0x123, new byte[] { 1, 2, 3 }));

if (result.Confirmed)
{
    // result.IsApproximated tells you whether this was a real echo or driver-acceptance only.
}
else
{
    // result.FailureReason: Timeout, BusOff, or Rejected -- never an indefinite hang.
}

Concurrent, byte-identical sends are matched to their own confirmation in FIFO order, never cross-matched (FR-RAW-031). The per-call timeout is configurable (FR-RAW-034); disposing the service cancels any outstanding SendConfirmedAsync calls rather than leaving them to time out.

A confirmation carries two host-monotonic readings that bracket the driver call, taken inside the service's send lock: HostHandoffTimestamp immediately before it and HostTransmitTimestamp immediately after. A response deadline starts at the second; whether a received frame can be a response to this transmission at all is decided against the first — the call itself can include a peer's answer on an in-process bus, or a completion callback on an asynchronous adapter, so a fast reply can be stamped before the second reading (#146, #147).

Migrating from 1.2.x

Subscriptions used to yield a bare CanFrameView. They now yield a CanFrameEvent carrying the frame plus the two facts the bus already knew and the demux was discarding:

// before
using var sub = service.Subscribe(view => view.ID == 0x123);
await foreach (var frame in sub.Frames) Use(frame.Data);

// after
using var sub = service.Subscribe(e => e.Frame.ID == 0x123);
await foreach (var e in sub.Frames) Use(e.Frame.Data);

TryRead gains an out CanFrameEvent, and predicates and callbacks take CanFrameEvent. Reach the frame through .Frame.

ISubscription.WaitToReadAsync is the pull-style counterpart to Frames beside TryRead: a consumer that must also drain the buffer from another thread — a deadline check that cannot wait for the consumer's own scheduling — waits with it and drains under its own lock with TryRead, so the two cannot reorder frames the way a caller overtaking an enumerator would. CanKit.Pro.IsoTp reads its subscription this way.

On a bus not configured for echo, nothing else changes. On an echo bus, a subscription now withholds host echoes unless it passes includeEcho: true — read the Echoes section above before choosing, in particular the part about the flag being host-scoped rather than instance-scoped.

Install

dotnet add package CanKit.Pro.RawCan

# plus a CanKit adapter for the hardware you actually talk to, e.g.
dotnet add package CanKit.Adapter.Virtual   # loopback, no hardware
# dotnet add package CanKit.Adapter.PCAN    # Kvaser, Vector, SocketCAN, ZLG, ... likewise

Dependencies: CanKit.Abstractions.

Part of CanKit.Pro — higher CAN protocol layers built on top of CanKit, which is consumed as a NuGet package rather than forked.

License

MIT — see LICENSE. CanKit itself is a separate project licensed under Apache-2.0; see THIRD-PARTY-NOTICES.md.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (5)

Showing the top 5 NuGet packages that depend on CanKit.Pro.RawCan:

Package Downloads
CanKit.Pro.J1939Tp

SAE J1939-21 Transport Protocol (TP.BAM broadcast + TP.CM connection-mode RTS/CTS/EndOfMsgAck) for CanKit.Pro: an actor-driven multi-session channel that composes on top of the CanKit.Pro L2 services (RawCan demux, TX-confirm, Actor, Reliability deadlines) and the CanKit.Pro.Addressing J1939 PGN helpers -- no vendor-SDK dependency.

CanKit.Pro.IsoTp

Specification-compliant ISO 15765-2 (ISO-TP) implementation for CanKit.Pro: deterministic Single-/First-/Consecutive-/Flow-Control-frame codec, bounds-checked PCI parser and STmin helpers, plus an actor-driven runtime (IIsoTpChannel) that composes on top of the CanKit.Pro L2 services (RawCan demux, TX-confirm, Actor, Reliability deadlines) — no vendor-SDK dependency.

CanKit.Pro.Uds

Unified Diagnostic Services (ISO 14229-1) client for CanKit.Pro. Provides an async IUdsClient over IIsoTpChannel with the implemented service set (0x10 DiagnosticSessionControl, 0x11 ECUReset, 0x22 ReadDataByIdentifier, 0x27 SecurityAccess, 0x2E WriteDataByIdentifier, 0x31 RoutineControl, 0x34 RequestDownload, 0x35 RequestUpload, 0x36 TransferData, 0x37 RequestTransferExit, 0x3E TesterPresent), P2/P2* timing, NRC 0x78 responsePending handling and structured negative-response reporting.

CanKit.Pro.J1939

SAE J1939 application-layer node for CanKit.Pro: PGN send/receive with 29-bit Priority/PF/PS/SA encode/decode, SPN scale/offset extraction, PGN 0xEE00 Address Claiming with NAME arbitration (including Cannot-Claim), PGN 0xEA00 Request-PGN, and automatic multi-frame routing through CanKit.Pro.J1939Tp for payloads > 8 bytes. Composes on the CanKit.Pro L2 services (RawCan demux, TX-confirm, Actor, Reliability deadlines) with no vendor-SDK dependency.

CanKit.Pro.CANopen

CANopen (CiA 301) node implementation for CanKit.Pro. Provides an in-process ICanOpenNode with a local Object Dictionary (FR-CO-001), SDO expedited/segmented/block transfers (FR-CO-002/003/004), static TPDO/RPDO mapping with event/timer/SYNC triggers (FR-CO-005/006), an NMT master with heartbeat producer/consumer (FR-CO-007/008), node-guarding consumer/producer (FR-CO-009), SYNC producer/consumer (FR-CO-010) and EMCY encode/decode (FR-CO-011), all composed on the L2 ICanBusService demux (FR-CO-012); the object dictionary carries the communication profile and drives the node (FR-CO-013..024) and can be loaded from a CiA 306 EDS/DCF device description, with every degradation corrected and reported (FR-CO-025..028). An NMT flying master (CiA 302-2 version 4.1.0) elects the active master and then boots the slaves assigned in 1F81h.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.3.0 538 9/30/2026