# dbcc [![Build Status](https://travis-ci.org/marcelbuesing/dbcc.svg?branch=dev)](https://travis-ci.org/marcelbuesing/dbcc) ============= 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](https://crates.io/crates/tokio-socketcan-bcm), via [tokio](https://crates.io/crates/tokio) streams, to filter for a specified message by can identifier. ## Features - [x] Generate message, signal decoder code - [x] Generate message id constants - [x] Generate enums for matching against signal values - [x] 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](https://doc.rust-lang.org/cargo/reference/build-scripts.html). Adapt the dbc input path and target path according to your needs. ```Rust 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>(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` ```YAML [dependencies] byteorder = "1.2" ``` ## Use ```Rust /// 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 = 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` ```YAML [dependencies] byteorder = "1.3" futures = "0.3" tokio = "0.3" tokio-socketcan-bcm = "1.0" ``` ```Rust 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.