Skip to content

iColgateZz/dbc-codegen2

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

381 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

dbc-codegen2

dbc-codegen2 is a DBC code generator for CAN bus messages.

It reads one or more .dbc files, parses them with can-dbc, builds an intermediate representation, validates and normalizes that representation, and then generates strongly typed source code for working with CAN frames.

The generator currently supports:

  • Rust output (.rs)
  • C++ output (.hpp)
  • no-std compatible output
  • plain CAN messages
  • simple multiplexed messages with one multiplexor signal per message
  • enum generation from DBC value descriptions
  • generating tests for generated Rust code
  • Rust code injection at selected locations

Unsupported DBC features currently include:

  • extended multiplexing symbols
  • messages with more than one multiplexor signal
  • signals that are both multiplexors and multiplexed signals

Installation

Install the command-line tool with Cargo:

cargo install dbc-codegen2

After installation, the executable is available as:

dbc-codegen2

You can also use the crate as a Rust library by adding it to your Cargo.toml:

[dependencies]
dbc-codegen2 = "*"

Command-line usage

General form:

dbc-codegen2 <COMMAND> [OPTIONS]

Get top-level help:

dbc-codegen2 --help

Get help for a specific command:

dbc-codegen2 gen --help

Commands

parse

Parse a DBC file and write the parsed can-dbc message data as debug output.

dbc-codegen2 parse input.dbc -o parsed.txt

ir

Parse a DBC file, build the internal IR, and write the IR as debug output.

dbc-codegen2 ir input.dbc -o ir.txt

gen

Generate Rust or C++ code from one or more DBC files.

dbc-codegen2 gen input.dbc -o generated --lang rust

When multiple input files are passed, the parser output is merged before IR construction and validation:

dbc-codegen2 gen a.dbc b.dbc c.dbc -o vehicle_can.rs

Arguments and options:

Argument / option Description Default
inputs... One or more input DBC file paths required
-o, --output Output path. The extension is replaced with the target language extension. data/generated.rs
-l, --lang Target language. Supported values: rust, cpp. rust
--no-enum-other Do not generate the fallback _Other(...) variant for signal value enums. Unknown enum values then become errors. false
--no-enum-dedup Disable signal value enum deduplication. By default, equal enums are shared. false
--zero-zero-range-allows-all Treat DBC physical ranges written as [0|0] as unconstrained. false
--test Generate message tests. Rust output emits #[cfg(test)] tests; C++ output emits a generated_tests::run_all() harness. false

Examples:

Generate Rust:

dbc-codegen2 gen vehicle.dbc -o src/generated_can

Generate Rust with generated tests:

dbc-codegen2 gen vehicle.dbc -o src/generated_can --test

Generate C++:

dbc-codegen2 gen vehicle.dbc -o include/generated_can --lang cpp

Generate C++ with generated tests:

dbc-codegen2 gen vehicle.dbc -o include/generated_can --lang cpp --test

Generate Rust while rejecting unknown enum values:

dbc-codegen2 gen vehicle.dbc -o src/generated_can --no-enum-other

Generate from multiple DBC files:

dbc-codegen2 gen network_a.dbc network_b.dbc -o generated/network

Library usage

Library entry point is CodegenPipeline::run, using CodegenConfig.

use dbc_codegen2::{CodegenPipeline, CodegenConfig, Language};
use std::collections::HashMap;

fn main() -> anyhow::Result<()> {
    let config = CodegenConfig {
        inputs: vec!["vehicle.dbc".to_string()],
        output: "src/generated_can".to_string(),
        lang: Language::Rust,
        no_enum_other: false,
        no_enum_dedup: false,
        zero_zero_range_allows_all: false,
        rust_code_injections: HashMap::new(),
        generate_tests: true,
    };

    CodegenPipeline::run(config)
}

Rust code injection

Rust code injection lets library users insert extra Rust tokens before selected generated items. This is useful for adding derives or conditional derives to generated types.

Available injection points are:

Injection point Inserted before
RustCodeInjectionPoint::MessageStruct every generated message struct
RustCodeInjectionPoint::MessageEnum the generated top-level message enum
RustCodeInjectionPoint::SignalValueEnum every generated signal value enum
RustCodeInjectionPoint::MuxEnum every generated mux enum
RustCodeInjectionPoint::MuxVariantStruct every generated mux variant struct
RustCodeInjectionPoint::ErrorEnum the generated CanError enum

Example:

use dbc_codegen2::{CodegenPipeline, CodegenConfig, Language, RustCodeInjectionPoint};
use std::collections::HashMap;

fn main() -> anyhow::Result<()> {
    let mut config = CodegenConfig {
        inputs: vec!["vehicle.dbc".to_string()],
        output: "src/generated_can".to_string(),
        lang: Language::Rust,
        no_enum_other: false,
        no_enum_dedup: false,
        zero_zero_range_allows_all: false,
        rust_code_injections: HashMap::new(),
        generate_tests: false,
    };

    config.add_rust_code_injection(
        RustCodeInjectionPoint::SignalValueEnum,
        "#[derive(serde::Serialize, serde::Deserialize)]",
    );

    config.add_rust_code_injection(
        RustCodeInjectionPoint::MessageStruct,
        "#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]",
    );

    CodegenPipeline::run(config)
}

Validation and transformation pipeline

Before code generation, dbc-codegen2 computes bit positions, signal usage information, and inferred signal types. It then validates the IR.

Current validation includes checks for:

  • [0|0] physical ranges, unless --zero-zero-range-allows-all is used
  • duplicate message IDs
  • invalid signal layouts
  • overlapping message bits and unused bits
  • unsupported multiplexing forms
  • invalid enum variants
  • physical ranges that cannot be represented by the raw signal encoding
  • unsafe signal scaling arithmetic

After validation, the IR is normalized for code generation by sanitizing enum variant names, deduplicating signal value enums, attaching enum types to signals, and sanitizing message, enum, and signal names.

Name mangling rules

DBC names are not always valid Rust or C++ identifiers. dbc-codegen2 keeps the raw DBC name in the IR, then applies deterministic mangling before code generation.

The rules below describe the current implementation.

Identifier validity

An identifier is considered valid when:

  • it is not empty
  • the first character is _ or an ASCII letter
  • all following characters are _ or ASCII alphanumeric characters
  • it is not a Rust keyword
  • it is not the internally reserved name _other

If a rendered identifier is invalid, the sanitizer prefixes it with x. This repeats if needed by later transformations.

Examples:

Raw name Reason Sanitized base
1st_signal starts with a digit x1st_signal
type Rust keyword xtype
_Other conflicts with generated enum fallback name x_Other

After validity fixes, generated Rust names are converted with heck casing helpers, usually to UpperCamelCase for types and snake_case for functions/fields.

Message names

Every message gets a _MSG postfix before casing. If multiple messages have the same message name case-insensitively, later duplicates get _MSG1, _MSG2, and so on.

Generated message structs use UpperCamelCase.

The top-level Rust message enum variant uses the message name with only the numeric part of the postfix kept. This keeps the enum variant less redundant while still disambiguating duplicates.

Examples:

DBC message name Duplicate index Struct name Rust message enum variant
EngineStatus 0 EngineStatusMsg EngineStatus
EngineStatus 1 EngineStatusMsg1 EngineStatus1
123Status 0 X123StatusMsg X123Status

Signal names

Signal names are made unique inside each message. The first signal keeps its base name. Later signals with the same raw name case-insensitively receive numeric postfixes: 1, 2, and so on.

Generated getter names use snake_case; setter names use set_<snake_case>.

Examples:

DBC signal names in one message Generated getter names Generated setter names
VehicleSpeed vehicle_speed() set_vehicle_speed(...)
Speed, speed, SPEED speed(), speed1(), speed2() set_speed(...), set_speed1(...), set_speed2(...)
type xtype() set_xtype(...)
1stValue x1st_value() set_x1st_value(...)

Signal value enum names

Signal value enum names receive an _ENUM postfix. Duplicate enum names, compared case-insensitively after adding _ENUM, receive _ENUM1, _ENUM2, and so on.

Generated enum type names use UpperCamelCase.

Examples:

DBC enum/signal enum name Duplicate index Generated enum type
Gear 0 GearEnum
Gear 1 GearEnum1
type 0 XtypeEnum

Signal value enum variant names

Enum variant names are sanitized before enum-name sanitization:

  1. the raw signal name is removed from the value-description text
  2. the result is converted to UpperCamelCase
  3. if the result is empty, the variant becomes V<raw_value>
  4. if the result is not a valid identifier, it is prefixed with V
  5. if multiple variants still have the same name, each duplicate gets its raw numeric value appended

Examples for a signal named Gear:

Raw value description Raw value Generated variant
Gear Off 0 Off
Gear Drive 1 Drive
Gear 2 V2
1st 3 V1st
State and another State 4, 5 State4, State5

When --no-enum-other is not used, generated Rust enums include an _Other(<repr>) fallback variant. Because _other is reserved internally, a DBC variant that would become _Other is prefixed during validity checks.

Multiplexed message names

For a multiplexed message named <MessageName>, generated Rust names follow this shape:

Generated item Name pattern Example for Sensor
wrapper message struct <MessageName>Msg SensorMsg
mux enum <MessageStructName>Mux SensorMsgMux
mux enum variant V<mux_value> V0, V1
mux variant struct <MessageStructName>Mux<mux_value> SensorMsgMux0
wrapper mux getter mux() mux()
wrapper mux setter set_mux<mux_value>(...) set_mux0(...)

Generated C++ multiplexed names follow the same type-name shape, except C++ mux setters use set_mux_<mux_value>(...).

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors