CanKit.Pro.Reliability
1.2.3
dotnet add package CanKit.Pro.Reliability --version 1.2.3
NuGet\Install-Package CanKit.Pro.Reliability -Version 1.2.3
<PackageReference Include="CanKit.Pro.Reliability" Version="1.2.3" />
<PackageVersion Include="CanKit.Pro.Reliability" Version="1.2.3" />
<PackageReference Include="CanKit.Pro.Reliability" />
paket add CanKit.Pro.Reliability --version 1.2.3
#r "nuget: CanKit.Pro.Reliability, 1.2.3"
#:package CanKit.Pro.Reliability@1.2.3
#addin nuget:?package=CanKit.Pro.Reliability&version=1.2.3
#tool nuget:?package=CanKit.Pro.Reliability&version=1.2.3
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.
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:
onExpiredis scheduled via the actor's ownSchedule, so it is dispatched and run on the loop rather than sitting as data nobody re-reads. - Single, race-free resolution: a deadline is
Pendinguntil exactly one of expiry,Complete(), orDispose()(which is how a deadline is cancelled — there is no separateCancel()) wins anInterlockedstate transition; the others become idempotent no-ops.Complete()returns whether it won — a caller's answer to "did I finish before the deadline fired?". Rearmbest-effort semantics: re-arming a still-Pendingdeadline disposes the old actor-timer handle (best-effort) and arms a new one, using a generation counter so a stale pre-Rearmtimer that the actor already dispatched no-ops instead of double-firing. Mirroring the actor's own documentedSchedulecaveat, aRearmracing an already-in-flight fire is best-effort, not linearizable.- Exceptions: an exception thrown from
onExpiredpropagates out of the actor'sSchedulecallback and surfaces through the actor's existingBackgroundExceptionOccurred(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
FinalDraindiscards not-yet-dueSchedulecallbacks rather than firing them, so a deadline that wasPendingwhen the actor is disposed simply never resolves (neither expires nor errors). Callers needing a guaranteed resolution must track actor lifetime themselves.Rearmafter the actor is disposed lets the resultingObjectDisposedExceptionpropagate rather than swallowing it.
Bus-state monitoring (FR-RAW-051)
- Self-rearming poll, not a free-running timer:
ICanBus.BusStatehas no change event, and an adapter'sErrorFrameReceived/FaultOccurredmay not fire on every transition, so the reliable mechanism is a poll (default 50 ms) driven through the actor'sSchedule, staying inside the event-driven-actor model instead of a busy loop. - Low-latency hints:
ErrorFrameReceivedandFaultOccurredare additionally subscribed as hints thatPostan 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. - Edge-triggered:
StateChangedfires 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
BusStatesynchronously 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 forBusOff) andIsDegraded()(true forErrWarning/ErrPassive/BusOff).
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 | Versions 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. |
-
.NETStandard 2.0
- CanKit.Abstractions (>= 0.5.6)
- CanKit.Pro.Actor (>= 1.2.3)
-
net10.0
- CanKit.Abstractions (>= 0.5.6)
- CanKit.Pro.Actor (>= 1.2.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|