CanKit.Pro.Reliability 1.3.0

dotnet add package CanKit.Pro.Reliability --version 1.3.0
                    
NuGet\Install-Package CanKit.Pro.Reliability -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.Reliability" 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.Reliability" Version="1.3.0" />
                    
Directory.Packages.props
<PackageReference Include="CanKit.Pro.Reliability" />
                    
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.Reliability --version 1.3.0
                    
#r "nuget: CanKit.Pro.Reliability, 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.Reliability@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.Reliability&version=1.3.0
                    
Install as a Cake Addin
#tool nuget:?package=CanKit.Pro.Reliability&version=1.3.0
                    
Install as a Cake Tool

CanKit.Pro.Reliability

Error/timeout infrastructure for CanKit (arc42 §5.3 / ADR-11; SRS FR-RAW-050/051): a reusable deadline primitive whose expiry is guaranteed to actually be checked and fired, and a bus-state monitor that pushes ICanBus.BusState transitions to a protocol instance — both composed on top of CanKit.Pro.Actor's single-mailbox loop, so there are no free-running timers, no busy loops, and no second background-exception channel.

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: Deadline expiry and the bus-state monitor, by the test suite in tests/CanKit.Pro.Tests, largely on a virtual clock, and with bus states set by a software-controlled bus double (ControllableBus, built on a virtual-adapter session) rather than reported by an adapter.

Not validated: Bus-off and error-passive transitions as a real controller produces them. 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.

This package depends only on CanKit.Abstractions (for ICanBus/BusState) and CanKit.Pro.Actor (for IProtocolActor). Every protocol instance already runs on a ProtocolActor (FR-RAW-020), so a deadline is not an independent standalone timer — it is scheduled through the actor's own event-driven timer queue, which is exactly why its expiry can never sit as inert, never-checked data (the deep-code-review finding "Deadlines werden gepflegt, aber nie geprüft", Review §1.1 Punkt 10).

using CanKit.Core;
using CanKit.Pro.Actor;
using CanKit.Pro.Reliability;

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

// (1) React to bus degradation so a controlled TX can abort/pause and resume (FR-RAW-051).
using var monitor = new BusStateMonitor(bus, actor);
monitor.StateChanged += (_, e) =>
{
    if (e.Current.IsTransmitBlocked())   // BusOff
        AbortActiveTransmission();
    else if (!e.Current.IsDegraded() && e.Previous.IsDegraded())
        ResumeTransmission();            // recovered back to ErrActive
};

// (2) Arm a timeout for a time-bounded transition (FR-RAW-050), e.g. an ISO-TP N_Cr window.
var scheduler = new DeadlineScheduler(actor);
var deadline = scheduler.Arm(TimeSpan.FromMilliseconds(150), () => channel.OnTimeout());

// ... later, when the awaited event arrives in time:
if (deadline.Complete())
{
    // We finished before the deadline fired; onTimeout will not run.
}
// Or refresh it on each consecutive frame instead of letting it expire:
deadline.Rearm(TimeSpan.FromMilliseconds(150));

Deadlines (FR-RAW-050)

  • Guaranteed to be checked, not just stored: onExpired is scheduled via the actor's own Schedule, so it is dispatched and run on the loop rather than sitting as data nobody re-reads.
  • Single, race-free resolution: a deadline is Pending until exactly one of expiry, Complete(), or Dispose() (which is how a deadline is cancelled — there is no separate Cancel()) wins an Interlocked state transition; the others become idempotent no-ops. Complete() returns whether it won — a caller's answer to "did I finish before the deadline fired?".
  • Rearm best-effort semantics: re-arming a still-Pending deadline disposes the old actor-timer handle (best-effort) and arms a new one, using a generation counter so a stale pre-Rearm timer that the actor already dispatched no-ops instead of double-firing. Mirroring the actor's own documented Schedule caveat, a Rearm racing an already-in-flight fire is best-effort, not linearizable.
  • Exceptions: an exception thrown from onExpired propagates out of the actor's Schedule callback and surfaces through the actor's existing BackgroundExceptionOccurred (FR-RAW-023) — there is deliberately no second exception channel.
  • Actor lifetime: disposing the owning actor implicitly stops still-pending deadlines from firing — the actor's FinalDrain discards not-yet-due Schedule callbacks rather than firing them, so a deadline that was Pending when the actor is disposed simply never resolves (neither expires nor errors) and reads exactly like a healthy pending one — none of IsExpired, IsCompleted, IsCancelled will ever become true. Signalling that would need a fourth flag on the public IDeadline, which is a break for implementers, so the rule is instead: resolve outstanding deadlines (Complete()/Dispose()) before disposing the actor they run on. Rearm is the one operation that notices, because it has to talk to the actor: it lets the resulting ObjectDisposedException propagate rather than swallowing it, and forces the deadline to Cancelled so it is not left as an unobservable zombie.

Bus-state monitoring (FR-RAW-051)

  • Self-rearming poll, not a free-running timer: ICanBus.BusState has no change event, and an adapter's ErrorFrameReceived/FaultOccurred may not fire on every transition, so the reliable mechanism is a poll (default 50 ms) driven through the actor's Schedule, staying inside the event-driven-actor model instead of a busy loop.
  • Low-latency hints: ErrorFrameReceived and FaultOccurred are additionally subscribed as hints that Post an immediate out-of-band recheck (so a BusOff is seen near-instantly), without touching the poll timer — the self-rearming poll remains the independent reliability floor. If an adapter refuses these subscriptions (e.g. AllowErrorInfo=false), the monitor degrades cleanly to poll-only.
  • Hints are coalesced: a bus-off or error-passive storm raises ErrorFrameReceived thousands of times per second, so at most one un-run hint recheck is ever outstanding in the mailbox — further hints arriving while it is queued are dropped instead of posted. A recheck is a sample of a level (BusState is a plain getter), not the delivery of a queued event, so N back-to-back samples of an unchanged level report exactly what one reports; what is dropped is mailbox traffic that would otherwise starve the protocol work the state change exists to abort. The gate is released before the sample is taken, so a hint racing an in-flight recheck posts a follow-up and the last hint of a storm is always succeeded by a sample taken after it. Coalescing does not make the monitor miss edges it would otherwise report: as ever, the intermediate levels of a fast ErrWarning → ErrPassive → BusOff cascade are only seen if a sample lands between them — shorten the poll interval if you need finer granularity.
  • Edge-triggered: StateChanged fires only when the newly-read state differs from the last-seen one, for both degrading and recovering transitions (BusOff → ErrActive matters too).
  • Loop-thread cost: each tick reads BusState synchronously on the actor's loop thread; a slow or blocking adapter getter therefore stalls that instance's loop for the duration — a known tradeoff of reusing the actor (which keeps handling single-writer-safe), not a bug fixed here.
  • Lifetime: the poll loop also stops on its own once the owning actor is disposed. Dispose() is still required (and idempotent) to detach the two bus event subscriptions, which are independent of the actor's lifetime.
  • Helpers: BusStateExtensions.IsTransmitBlocked() (true only for BusOff) and IsDegraded() (true for ErrWarning/ErrPassive/BusOff/Unknown). The two treat Unknown differently on purpose: it means "we could not determine the controller state", which is never a basis for reporting health, but is equally never proof that the bus is off — so it degrades, and it does not block transmission on the many adapters that simply never report a state.

Out of scope: FR-RAW-052 (reserved/invalid protocol values)

FR-RAW-052 (a Should: reserved/invalid protocol values in incoming frames — e.g. reserved ISO-TP STmin values 0x80–0xF0/0xFA–0xFF — should be interpreted per-spec, as 127 ms, rather than throwing) is intentionally not implemented in this package. It is protocol-codec-specific: the correct handling lives inside the ISO-TP frame codec, not in a generic reliability primitive, and belongs with the future ISO-TP fix (FR-TP-007, the same review finding as Review §1.1 Punkt 6). Building a generic "reserved value" abstraction here would be speculative over-engineering, so this package deliberately covers only FR-RAW-050 and FR-RAW-051.

Install

dotnet add package CanKit.Pro.Reliability

# 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, CanKit.Pro.Actor.

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.Reliability:

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.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.

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.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.3.0 537 9/30/2026