# 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 j1939.dbc > j1939.rs
```

For warnings during the generation run with:

```
RUST_LOG=info dbcc j1939.dbc > 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<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`
```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<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`
```YAML
[dependencies]
byteorder = "1.3"
futures = "0.1"
tokio = "0.1"
tokio-socketcan-bcm = "0.3"
```

```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.
