Skip to main content

Rust-first CAN codec for the Zelos ecosystem. SocketCAN tracing with DBC signal decoding.

Project description

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]'

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.

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
native periodic transmit python-can path Rust timer n/a
DBC decode + trace in Rust via Notifier + CanDecoder via CanCodec or CanDecoder always
Linux-only yes no no

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 in api/py/zelos-can.

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.

Project details


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.4-cp310-abi3-manylinux_2_28_x86_64.whl (1.2 MB view details)

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

zelos_can-0.0.4-cp310-abi3-manylinux_2_28_aarch64.whl (862.7 kB view details)

Uploaded CPython 3.10+manylinux: glibc 2.28+ ARM64

File details

Details for the file zelos_can-0.0.4-cp310-abi3-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for zelos_can-0.0.4-cp310-abi3-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 c60a9fafa3f1258b30bca7e09c16aa1efae54ea13d178682083d150f2e88a24b
MD5 2566cbac429a84eb25a6b30125da9932
BLAKE2b-256 6cf2cf4065606eedfa6cec56c0b880769b892916a1d4ac5f9ccac6eb4460287c

See more details on using hashes here.

File details

Details for the file zelos_can-0.0.4-cp310-abi3-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for zelos_can-0.0.4-cp310-abi3-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 be9ed8592c404ca7f4628726aa17c330c80abeebaa8ed7e7c59d1e97e1442efc
MD5 dd1d2f1636c96642554f863f26c14a69
BLAKE2b-256 6ab1bbac238d8a15c2a60f864bab928c4caa2eebc2c0bf95616bacfeeee2a0ac

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 Pingdom Monitoring Sentry Error logging StatusPage Status page