can_tools 1.4.0

Mix of useful tools for CAN bus
Documentation

can_tools

Rust utilities for parsing and modeling automotive CAN databases and logs.


Highlights

  • DBC parsing → streaming reader that decodes Windows-1252, transliterates a few common characters, and materialises a SlotMap-backed DatabaseDBC (nodes, messages, signals, attributes, relations).
  • ASC parsing → single-pass Vector ASCII reader that discovers optional date headers, formats absolute timestamps, keeps every frame in can_frames, and tracks one latest frame per (id, channel) in last_id_chn_frame.

Feature Flags

The crate enables both parsers by default. Use Cargo features to opt out:

Feature Enabled by default? Description
dbc DBC database parser and models (brings encoding_rs, slotmap).
asc Vector ASCII trace parser and models (depends on dbc, adds chrono).

This README documents the library API (no application/UI specifics).


Installation

Add to Cargo.toml:

[dependencies]
can_tools = "1.4.0"

Use only the DBC parser (disable default features):

[dependencies]
can_tools = { version = "1.4.0", default-features = false, features = ["dbc"] }

Minimal usage with DBC only:

use can_tools::dbc;
use can_tools::dbc::types::database::DatabaseDBC;

fn main() -> Result<(), String> {
    // Parse a .dbc file into an in-memory database
    let mut db: DatabaseDBC = dbc::parse::from_file("network.dbc")?;

    // Optional: sort presentation (ASCII case-insensitive)
    db.sort_db_nodes_by_name();
    db.sort_db_messages_by_name();
    db.sort_db_signals_by_name();

    // Iterate and use
    for m in db.iter_messages() {
        println!("{} ({})", m.name, m.msgtype);
    }

    Ok(())
}

Usage Overview

Parse a DBC

use can_tools::dbc;
use can_tools::dbc::types::database::DatabaseDBC;

let db: DatabaseDBC = dbc::parse::from_file("network.dbc")?;

Iterate in presentation order:

for n in db.iter_nodes() { println!("{}", n.name); }
for m in db.iter_messages() { println!("{} ({} bytes)", m.name, m.byte_length); }
for s in db.iter_signals() { println!("{}", s.name); }

Sort by name (ASCII case-insensitive):

let mut db = db;
db.sort_db_nodes_by_name();
db.sort_db_messages_by_name();
db.sort_db_signals_by_name();

Lookups:

let by_id = db.get_message_by_id(0x123);
let by_hex = db.get_message_by_id_hex("0x1A2B");
let by_name = db.get_message_by_name("EngineData");
let node = db.get_node_by_name("Gateway");
let sig = db.get_signal_by_name("VehicleSpeed");

Reset:

let mut db = db;
db.clear();

Notes on attributes:

  • Attribute definitions come from BA_DEF_* and defaults from BA_DEF_DEF_.
  • Assignments BA_ set values on DB/Node/Message/Signal.
  • ENUM assignments in BA_ use numeric indices into the declared enum list.

Parse an ASC trace

use std::collections::HashMap;
use can_tools::asc;
use can_tools::asc::types::canlog::{CanLog, resolve_message_signals};
use can_tools::dbc::types::database::DatabaseDBC;

// Optional: provide per-channel databases for enrichment
let mut dbs: HashMap<u8, DatabaseDBC> = HashMap::new();
dbs.insert(1, dbc::parse::from_file("network.dbc")?);

let log: CanLog = asc::parse::from_file("trace.asc", &dbs)?;

// `CanLog` contains:
// - `can_frames`: every frame in file order;
// - `messages`: one entry per frame with enriched metadata;
// - `signals`: aggregated decoded signals updated as frames arrive;
// - `last_id_chn_frame`: one index per `(id, channel)` pointing to the freshest frame;
// - `absolute_time`: optional trace start timestamp derived from the `date` header.

// Iterate all frames in file order and access their messages
for frame in &log.can_frames {
    let msg = &log.messages[frame.message];
    println!(
        "{} ch{} {} {} [{}] {}",
        frame.absolute_time, // absolute time or synthetic fallback
        frame.channel,
        msg.id,              // raw id token as in the log
        msg.name,            // may be empty if no DB was provided
        msg.protocol,        // "CAN" or "CAN FD"
        msg.data,            // hex payload as space-separated bytes
    );

    // Resolve decoded signals for each frame's message
    for sig in resolve_message_signals(&log, frame.message) {
        println!(
            "ch{} {}: {} => {} {}",
            frame.channel,
            frame.absolute_time,
            sig.name,
            sig.value,
            sig.unit,
        );
    }
}

License

MIT