Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

zelos-can

Rust-first CAN codec for the Zelos ecosystem. DBC-driven signal decode + trace emission at wire speed, with optional python-can–compatible bus implementations for live SocketCAN and in-memory testing.

Install

pip install zelos-can
# python-can compat layer (optional — only needed if you want to use
# can.Bus(interface="zelos-socketcan", ...)):
pip install 'zelos-can[python-can]'

Prebuilt wheels ship for Linux, macOS, and Windows. Headless decode (CanDecoder, VirtualBus, CanCodec) works on all three; the live zelos-socketcan bus is Linux-only (see the feature matrix).

Three ways to use it

1. Drop-in python-can bus (live capture on Linux)

import can
bus = can.Bus(interface="zelos-socketcan", channel="can0")
# Kernel-level filters, SO_RCVBUF tuning, SO_TIMESTAMPNS,
# auto-reconnect on link-down — all handled in Rust.

Compatible with can.Notifier, bus.send_periodic, bus.state, and the bus.socket escape hatch. Drop-in for interface="socketcan".

2. Bring your own frame source (this is the main pattern)

CanDecoder is a thread-safe handle that takes raw frames and emits decoded signals into the Zelos trace pipeline. Where frames come from is your problem — anything that can produce arbitration_id + data + timestamp works.

The DBC is optional. With no database_file / database_bytes the decoder skips signal decode and just traces the raw frames (log_raw_frames defaults on in that case) — handy for capturing a bus you don't have a DBC for yet:

from zelos_can import CanDecoder
raw = CanDecoder(source_name="can")          # no DBC → raw frames only
raw.decode_frame(arbitration_id=0x123, data=b"\x01\x02")
from zelos_can import CanDecoder
decoder = CanDecoder(database_file="vehicle.dbc", source_name="can")

# From python-can (PEAK/Vector/Kvaser/socketcan/whatever):
for msg in my_bus:
    decoder.decode_message(msg)

# From a network gateway (cannelloni/SLCAN/your protocol):
decoder.decode_frame(
    arbitration_id=0x123,
    data=b"\x01\x02",
    timestamp_ns=time.time_ns(),
    is_extended=False,
    is_fd=False,
)

# From a .asc/.blf/.trc/.mf4 file via python-can readers:
for msg in can.BLFReader("capture.blf"):
    decoder.decode_message(msg)

decode_frame and decode_message release the GIL for the duration of the Rust decode + trace emit — the caller thread stays free.

Complete examples in examples/:

3. Native Rust capture pipeline (fastest)

For the tightest hot path (no Python callbacks per frame), use the native CanCodec with either a VirtualBus or a SocketCAN channel:

from zelos_can import CanCodec, VirtualBus
bus = VirtualBus(channel="sim")
codec = CanCodec(database_file="vehicle.dbc", source_name="sim", bus=bus)
# codec reads frames in Rust; Python never sees individual frames.

Same applies to real hardware via channel="can0" (Linux SocketCAN):

codec = CanCodec(
    database_file="vehicle.dbc",
    source_name="can",
    channel="can0",
    log_raw_frames=True,
    timestamp_mode="hardware",
)

Trace pipeline

Every decode path funnels through zelos-sdk's TraceNamespace. If zelos_sdk.init() has been called the decoded signals stream live to the Zelos agent over gRPC; wrap your code in a zelos_sdk.TraceWriter(...) context to also record to a .trz file. Both work simultaneously.

Feature matrix

feature zelos-socketcan zelos-virtual CanDecoder
python-can can.Bus compat n/a
kernel filter (CAN_RAW_FILTER) n/a n/a
bus.state / bus.socket n/a n/a
error frames (is_error_frame) ✓ (on by default) n/a n/a
CAN FD bitrate_switch / error_state_indicator n/a n/a
local_loopback / receive_own_messages n/a n/a
native periodic transmit kernel BCM (send_periodic) + stop_all_periodic_tasks Rust timer n/a
DBC decode + trace in Rust via Notifier + CanDecoder via CanCodec or CanDecoder always
Linux-only yes no no

Transmitted frames are traced like any other when the bus is opened with receive_own_messages=True — TX frames echo back through the RX path (with is_rx=False) and flow into the decode/trace pipeline, so a recording captures exactly what hit the wire (including kernel BCM periodics).

Decoder semantics — divergences from cantools

zelos-can decodes per the DBC spec. For well-formed inputs we match cantools exactly.

SIG_VALTYPE_ interpretation

Both decoders treat the : 1 / : 2 marker as advisory and pick the IEEE 754 binary format from the signal length:

length format
16 binary16 (half precision)
32 binary32 (single precision)
64 binary64 (double precision)
anything else no canonical IEEE format → decode-time error

For lengths that aren't a real IEEE binary width (e.g. SIG_VALTYPE_:1 + length=10):

  • cantools raises at decode time (expected float size of 16, 32, or 64 bits).
  • zelos-can preserves is_float so the schema field stays typed as float, but decode_signal returns None per-frame and the decode_errors metric ticks. A parse-time warning surfaces the malformed DBC up front (cantools surfaces nothing at load time).

Storage typing for unsigned signals with negative offset

For an unsigned raw with a DBC offset that drives physical values negative (e.g. (1, -1000) on a u16):

  • cantools returns Python int (unbounded — sign is implicit).
  • zelos-can promotes the Arrow schema type to the signed sibling (Int{16,32,64}). The typed cache must store a fixed-width Arrow type; the unsigned variant would saturate f64 → uN casts of negative physicals to zero, silently zeroing valid frames.

Everything else

Integer signals, factor/offset on integer raws, big/little endianness, multiplexing, value tables, and well-formed IEEE float widths all decode identically to cantools.

Metrics

m = decoder.metrics()
# messages_received / messages_decoded / unknown_messages / decode_errors
# kernel_drops   (SocketCAN only, SO_RXQ_OVFL)
# broadcast_overflows (VirtualBus slow-consumer lag)
# reconnections  (SocketCAN auto-recover)

Benchmarks

Numbers measured on a single apple-silicon core via criterion. Ballpark only — reproduce with cargo bench.

bench throughput
decode_frame/dut_status_all_signals (9 sig) ~217 Msig/s (41 ns)
virtual_bus_fanout/subscribers=1 21 Melem/s
virtual_bus_fanout/subscribers=4 38 Melem/s
virtual_bus_fanout/subscribers=16 50 Melem/s

At 9 signals per message the decode path is ~24M full DBC-decoded messages per second in pure Rust, which is well past any realistic CAN bus rate — the GIL crossing on the Python boundary is what actually sets your ceiling, which is why the CanDecoder + Notifier (no per-frame GIL re-entry) path exists.

Broadcast fanout scales sublinearly (16-subscriber case delivers 50M total frames/s vs 21M for one subscriber), so adding listeners costs negligibly in the expected 1-10kHz CAN regime.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

zelos_can-0.0.7a0-cp310-abi3-win_amd64.whl (630.4 kB view details)

Uploaded CPython 3.10+Windows x86-64

zelos_can-0.0.7a0-cp310-abi3-manylinux_2_28_x86_64.whl (1.3 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.28+ x86-64

zelos_can-0.0.7a0-cp310-abi3-manylinux_2_28_aarch64.whl (937.0 kB view details)

Uploaded CPython 3.10+manylinux: glibc 2.28+ ARM64

zelos_can-0.0.7a0-cp310-abi3-macosx_11_0_arm64.whl (665.9 kB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

File details

Details for the file zelos_can-0.0.7a0-cp310-abi3-win_amd64.whl.

File metadata

  • Download URL: zelos_can-0.0.7a0-cp310-abi3-win_amd64.whl
  • Upload date:
  • Size: 630.4 kB
  • Tags: CPython 3.10+, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.12.7

File hashes

Hashes for zelos_can-0.0.7a0-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 e816e638dbf3572980016d5005f0d15dae7da79bffcd629c9a32e1ad93ee1cd4
MD5 cda75cad0fc68c87aab79cd0368f9594
BLAKE2b-256 a6de74c8f3ffa8c9003d4ff45247fcebfee42f2c9f7e3d313b4add6efbc09b25

See more details on using hashes here.

File details

Details for the file zelos_can-0.0.7a0-cp310-abi3-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for zelos_can-0.0.7a0-cp310-abi3-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 06da17967630f9cca29e19ce092d71ff8b4222a239552a0dd58b0ead2d012da1
MD5 6c55f2332589f8556d4e0cb364bc72d0
BLAKE2b-256 1eb7e4de393b0c06855eafc25f482e9bec2da7677869f9efae3aa31f7ead1372

See more details on using hashes here.

File details

Details for the file zelos_can-0.0.7a0-cp310-abi3-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for zelos_can-0.0.7a0-cp310-abi3-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 2fcddc6179207aa8269bc375f75ec21833dda6c4d24099828ba66f385cb41d56
MD5 79844e880639c5302cc24e32d2945ebd
BLAKE2b-256 ab97d9209e47ddfd0f95065d262f70e52bbe08aa2ef9fd164997a32f9001b55d

See more details on using hashes here.

File details

Details for the file zelos_can-0.0.7a0-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for zelos_can-0.0.7a0-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 4ebb1109b4f27157b526ebd6a0a690d96b764df3e9fc915ed43391ca7c82614f
MD5 80ae0597dfc984c75edfd916340509e1
BLAKE2b-256 3164f1eaeabf8a60bc8fd32e02f3b0e6dbc6b2ea2c21f5f35ffd0c582eb246c9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page