Architecture¶
CANExplorer has three layers:
| Layer | Package | Depends on Qt |
|---|---|---|
| Core: sources, sessions, storage, decoding, protocols, diagnostics | can_explorer/core |
no |
| Scripting: simulation nodes and their runtime | can_explorer/scripting |
no |
| GUI adapters: models and controllers exposed to QML | can_explorer/gui |
yes |
| User interface | can_explorer/gui/qt (QML) |
yes |
The core is plain asyncio and is tested without Qt. Qt and asyncio share one event loop through
qasync (can_explorer/__main__.py), so coroutines, timers and Qt
signals all run on the GUI thread.
Receive pipeline¶
flowchart LR
bus[python-can Bus] -->|notifier thread| bridge[ThreadBridge]
file[Log file reader thread] --> bridge
bridge -->|batches| session[BusSession]
session --> trace[TraceBuffer]
session --> stats[IdStatistics / BusStatistics]
session --> signals[SignalTracker]
session --> recorder[Recorder]
session --> listeners[Message listeners: ISO-TP / LIN TP clients, simulation, panels]
trace -->|every 33 ms| models[Trace and overview models] --> qml[QML views]
- Sources (
core/sources.py):LiveSourcereads a python-can bus in python-can's notifier thread,FileSourcereads a log file in a worker thread. Both hand frames to the event loop through aThreadBridge, which wakes the loop once per batch instead of once per frame. - BusSession (
core/session.py) consumes the batches. For each frame it runs the protocol decoder (stateful, hence in arrival order), appends it to the boundedTraceBuffer, updates the per-identifier and bus statistics and the tracked signals, records it and calls the message listeners. After each batch it yields to the event loop so the GUI stays responsive under sustained traffic. It also transmits (single, periodic, hardware timed) and records. - Decoding (
core/decoding.py): the protocol decoder describes every frame on arrival; signal databases are stateless, so frames are only decoded with them when displayed, and the result is cached in the frame. - GUI adapters (
gui/connection_manager.py,gui/models.py): aCanConnectionper connection drains the new frames from the session on a timer (33 ms by default) and appends them to the Qt models in one batch; the overview only refreshes the rows that changed.
tools/benchmark.py measures the pipeline: about 300 000 frames per second with J1939 and DBC decoding on a desktop
computer.
Modules¶
| Module | Purpose |
|---|---|
core/frames.py |
Frame, hex parsing and formatting |
core/filters.py |
Display filter syntax |
core/store.py |
Trace ring buffer and per-identifier statistics |
core/busstats.py |
Frame rates and bus load |
core/signals.py |
Time series of tracked signals |
core/database_io.py, core/dbf.py |
Loading, saving and converting signal databases; BUSMASTER DBF |
core/database_editor.py, core/ldf_editor.py, core/ldf_writer.py |
Editing CAN databases and LDF files; writing LDF files |
core/decoding.py, core/ldf.py |
Signal decoding with cantools databases and LDF files |
core/protocols/ |
Passive ISO-TP, J1939 and CANopen decoders |
core/lin.py, core/lin_tp.py, core/lin_schedule.py |
LIN frames and decoder, LIN transport client, LIN master |
core/isotp_client.py, core/uds.py |
Active ISO-TP client and UDS client and decoder |
core/flashing.py |
Intel HEX, S-record and binary images; UDS download sequence |
core/j1939_tools.py |
J1939 NAME, DM1/DM2, transport reassembly, network tracking and sending with BAM or RTS/CTS |
core/canopen_tools.py |
EDS/DCF object dictionaries, SDO client, NMT, PDO mappings, network tracking, simulated node |
core/compare.py |
Comparing two recordings |
core/bushealth.py |
Error frame decoding, error states and the bus health history |
core/updates.py, gui/updates.py |
Finding, downloading, verifying and installing new releases from GitHub |
core/remote.py |
RemoteBus, the python-can interface for buses shared by can-server |
core/diagnostic_description.py |
CANdelaStudio CDD reader |
core/trace_store.py |
Trace frames in memory and in temporary files on disk, by absolute index |
core/conditions.py, core/recording.py |
Conditions on frames; split and triggered recordings |
core/simulation.py |
Simulated UDS ECU and LIN slaves (tests, demos) |
scripting/ |
Simulation node API and runtime |
gui/connection_manager.py |
Connections, preferences and channel detection for QML |
gui/models.py |
Trace (a paged view of the trace store) and overview table models |
gui/trace_tools.py, gui/workspace.py |
Trace search and bookmarks; workspace files |
gui/diagnostics.py, gui/simulation.py |
Diagnostics and simulation controllers, panel bridge |
gui/flashing.py, gui/j1939.py, gui/canopen.py |
Flashing, J1939 and CANopen controllers of a connection |
gui/database_editor.py, gui/compare.py, gui/multibus.py |
Database editor, recording comparison and multi-bus trace tools |
gui/bushealth.py |
Bus health page of a connection |
gui/translations.py |
Translations from Qt Linguist files |
gui/plot_view.py, gui/signal_plot.py |
Time range, cursors and charts of the signal plots; plot item (QQuickPaintedItem) |
gui/app_state.py |
Persistent settings (QSettings) |
gui/qt/CanExplorer/ |
QML views, dialogs and dashboard components; Theme.qml holds the colours of the six themes, GlassPanel.qml the frosted glass of dialogs, menus and popups |
gui/qt/CanExplorerStyle/ |
Qt Quick Controls style drawing buttons, inputs, dialogs, menus etc. with the theme colours (Fusion for the rest) |
sys/config.py, sys/environment.py |
Application settings; clean-up of snap environments |
can_server/ |
can-server: shares local CAN interfaces over WebSocket |