Skip to content

CanFrameEvent Struct

One frame as a subscription saw it: the frame itself, whether it is this host's own transmit echo, and the timestamp the adapter recorded for it (FR-RAW-015).

public readonly struct CanFrameEvent : System.IEquatable<CanKit.Pro.RawCan.CanFrameEvent>

Implements IEquatable<CanFrameEvent>

Remarks

This is what Frames yields. It exists because the bus already knows all three facts — ICanBus.FrameObserved carries them on CanReceiveDataView — and the demux used to forward only the frame, discarding the other two before any subscriber could see them.

That loss was not cosmetic: the echo flag and the timestamp were simply unavailable above the demux, however much a caller wanted them. What it is not is a replacement for the self-traffic checks the protocol layers carry. IsEcho is host-scoped and only as reliable as the adapter that sets it, so J1939 still compares NAMEs, the J1939 transport still compares source addresses, and CANopen's actor-provenance guard answers a different question entirely (which thread applied an OD write, not which node sent a frame). All three are retained deliberately — see the remarks on IsEcho for why the flag cannot stand in for them.

A CanKit.Pro type rather than the upstream CanReceiveDataView: the payload of a buffered frame has to be copied (the adapter may return the RX lease to its pool while a subscriber has not read it yet), and rebuilding an upstream event payload around a copied frame would tie this package's public surface to a type it does not own. Note that the copy is made for buffered events only — see Frame for the two lifetimes.

Constructors

CanFrameEvent(CanFrameView, bool, TimeSpan) Constructor

Creates an event. The caller is responsible for frame owning its payload; see the remarks on CanFrameEvent.

public CanFrameEvent(CanKit.Abstractions.API.Can.Definitions.CanFrameView frame, bool isEcho, System.TimeSpan receiveTimestamp);

Parameters

frame CanFrameView

isEcho Boolean

receiveTimestamp TimeSpan

Properties

CanFrameEvent.Frame Property

The frame.

public CanKit.Abstractions.API.Can.Definitions.CanFrameView Frame { get; }

Property Value

CanFrameView

Remarks

<b>Two lifetimes, depending on where you received this event.</b> An event read from Frames or TryRead(CanFrameEvent) owns its payload buffer and stays valid indefinitely — the demux copies the payload before buffering it, precisely so the adapter may release the RX lease meanwhile.

An event handed to a subscription predicate does not. The predicate runs on the dispatch thread before the copy is made, so its Frame aliases the adapter's RX lease and must not be retained past the call — a rejected frame is never copied at all, which is what keeps filtering allocation-free. Inspect it, return, and keep nothing.

CanFrameEvent.IsEcho Property

True when the bus reported this frame as the local host's own transmit echo.

public bool IsEcho { get; }

Property Value

Boolean

Remarks

<b>Host, not instance.</b> The bit means "something on this host transmitted this", and nothing finer. Where several protocol instances share one ICanBusService — which every protocol factory in this repository documents as supported — a sibling instance's transmission is flagged exactly like this one's own. Deciding "did *I* send this?" needs an instance-level identity the demux does not have: a source address, a NAME, a node-id.

CanFrameEvent.ReceiveTimestamp Property

The receive timestamp the adapter recorded, as reported by the bus. Zero on adapters that do not timestamp; not comparable across buses.

public System.TimeSpan ReceiveTimestamp { get; }

Property Value

TimeSpan

Methods

CanFrameEvent.Equals(CanFrameEvent) Method

Value equality over the frame's kind, ID, flags and <em>payload bytes</em>, plus IsEcho and ReceiveTimestamp.

public bool Equals(CanKit.Pro.RawCan.CanFrameEvent other);

Parameters

other CanFrameEvent

Returns

Boolean

Remarks

Deliberately not delegating to CanFrameView's own equality. That type is a record struct holding a System.ReadOnlyMemory<>, whose generated comparison tests the memory segment — the backing object, offset and length — rather than the bytes. Since the demux allocates a fresh array per delivered frame, two events carrying an identical frame to two subscriptions would otherwise never compare equal, which is the opposite of what a caller writing a == b means.

The cost is that comparison is O(payload), up to 64 bytes for CAN FD. That is the right trade for a type callers compare in assertions and deduplication, but it makes this a poor dictionary key on a hot path.

Operators

CanFrameEvent.operator ==(CanFrameEvent, CanFrameEvent) Operator

Equality operator.

public static bool operator ==(CanKit.Pro.RawCan.CanFrameEvent left, CanKit.Pro.RawCan.CanFrameEvent right);

Parameters

left CanFrameEvent

right CanFrameEvent

Returns

Boolean

CanFrameEvent.operator !=(CanFrameEvent, CanFrameEvent) Operator

Inequality operator.

public static bool operator !=(CanKit.Pro.RawCan.CanFrameEvent left, CanKit.Pro.RawCan.CanFrameEvent right);

Parameters

left CanFrameEvent

right CanFrameEvent

Returns

Boolean