17 releases (8 stable)

2.2.1 Nov 5, 2020
2.2.0 Oct 23, 2020
2.1.2 Sep 23, 2020
2.1.0 Jul 22, 2020
1.0.1 Apr 10, 2019

#145 in Database implementations

MIT license

295KB
519 lines

dbcc Build Status

=============

dbcc can translate data base CAN files into Rust code. The generated code allows interacting with CAN signals in a type safe manner by e.g. matching against signal value enum types. Furthermore it provides a convenient way to use SocketCAN BCM Sockets, via tokio streams, to filter for a specified message by can identifier.

Features

  • Generate message, signal decoder code
  • Generate message id constants
  • Generate enums for matching against signal values
  • Generate tokio streams for CAN messages
  • Generate message, signal encoders

Option 1 - Run CLI

Install

cargo install dbcc

Generate code using the CLI.

dbcc --input dbcc --with-tokio -i examples/j1939.dbc > examples/gen/j1939.rs

For warnings during the generation run with:

RUST_LOG=info dbcc --with-tokio -i examples/j1939.dbc > examples/gen/j1939.rs

Option 2 - build.rs

Generate code at build time. Add the following to your build.rs. Adapt the dbc input path and target path according to your needs.

use dbcc::{DbccOpt, can_code_gen};
use can_dbc;

use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

fn main() -> std::io::Result<()> {
    let dbcs = &[
        ("./dbcs/j1939.dbc", "./src/lib.rs"),
    ];
    generate_code_for_dbc(dbcs)?;
    Ok(())
}

fn generate_code_for_dbc<P: AsRef<Path>>(input_output: &[(P, P)]) -> std::io::Result<()> {

    for (input_path, output_path) in input_output {
        let mut f = File::open(input_path).expect("Failed to open input file");
        let mut buffer = Vec::new();
        f.read_to_end(&mut buffer).expect("Failed to read file");

        let opt = DbccOpt {
            with_tokio: true,
        };

        let dbc_content = can_dbc::DBC::from_slice(&buffer).expect("Failed to read DBC file");
        let code = can_code_gen(&opt, &dbc_content).expect("Failed to generate rust code");

        let mut f = File::create(output_path)?;
        f.write_all(&code.to_string().into_bytes())?;
    }

    Ok(())
}

Include

  • Move the generated rust file to your project's src/ folder.
  • Add the following dependency to your project's Cargo.toml
[dependencies]
byteorder = "1.2"

Use

/// If you are using Rust 2018 no `external crate byteorder;` is necessary
/// Generated module
mod j1939;

fn main() {
    // J1939 - Operators External Light Controls Message Id
    let can_message_id = 2365443326u32;
    // can frame data field (0-8 bytes)
    let can_frame_data: Vec<u8> = vec![0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];

    // CAN Message ID constant from generated code
    if can_message_id == j1939::MESSAGE_ID_OEL {
        // J1939 - Operators External Light Controls Message
        let oel = j1939::Oel::new(can_frame_data);

        // Signal indicate the selected position of the operator's hazard light switch.
        match oel.hazardlightswitch() {
            j1939::HazardLightSwitch2365443326::HazardLampsToBeFlashing => println!("Hazard Lamps To Be Flashing"),
            j1939::HazardLightSwitch2365443326::HazardLampsToBeOff => println!("Hazard Lamps To Be Off"),
            j1939::HazardLightSwitch2365443326::NotAvailable => println!("Not available"),
            j1939::HazardLightSwitch2365443326::Error => println!("Error"),
            j1939::HazardLightSwitch2365443326::XValue(_) => unreachable!(),
        }
    }
}

Including SocketCAN Streams

  • Make sure you pass the --with-tokio flag when invoking dbcc.
  • Move the generated rust file to your project's src/ folder.
  • Add the following dependencies to your project's Cargo.toml
[dependencies]
byteorder = "1.3"
futures = "0.3"
tokio = "0.3"
tokio-socketcan-bcm = "1.0"
mod j1939;

use futures::future::Future;
use futures::stream::Stream;
use std::io;
use std::time::Duration;
use tokio;

fn main() -> io::Result<()> {
    let ival = Duration::from_secs(0);

    let f = j1939::Oel::stream("vcan0", &ival, &ival)?
        .for_each(|oel| {
            // Signal indicates the selected position of the operator's hazard light switch.
            match oel.hazardlightswitch() {
                j1939::HazardLightSwitch2365443326::HazardLampsToBeFlashing => {
                    println!("Hazard Lamps To Be Flashing")
                }
                j1939::HazardLightSwitch2365443326::HazardLampsToBeOff => {
                    println!("Hazard Lamps To Be Off")
                }
                j1939::HazardLightSwitch2365443326::NotAvailable => println!("Not available"),
                j1939::HazardLightSwitch2365443326::Error => println!("Error"),
                j1939::HazardLightSwitch2365443326::XValue(_) => unreachable!(),
            }
            Ok(())
        });

    tokio::run(f.map_err(|_| ()));

    Ok(())
}

Naming

Recommendation: Value descriptions aka VAL_ ... should contain only alphanumeric characters or underscores and should start with an alphabetic character. E.g. VAL_ 100 "111 Wunderschön Inc" 255 should be VAL_ 100 " Wunderschoen Inc 111" 255

  • Enums: Generated names are prefixed with an X if the name does not start with an alphabetic character.
  • Enums: Characters that are not alphanumeric or _ are replaced with an X
  • Enums: An XValue(f64) variant is added to each enum since value descriptions often do not cover all possibilities.

Dependencies

~9–18MB
~239K SLoC