Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,47 @@ Config::builder()
.check_ranges(FeatureConfig::Never);
```

### Attribute-driven constants

Many DBCs contain additional metadata in `BA_` attributes, e.g. parameters describing an AUTOSAR E2E protection scheme. `attribute_structs` lets you expose these as typed constants in the generated message types.

You declare a struct that your own crate owns and map each field to a DBC attribute, derived signal, or a literal:

```rust,no_run
use dbc_codegen::{AttributeField, AttributeScope, AttributeStruct, Config, FieldSource};

// `data_protection::E2EDataIdInfo { data_id, start_byte, width_bit }` is a type defined in your crate.
let e2e = AttributeStruct {
type_path: "data_protection::E2EDataIdInfo",
const_name: "E2E",
scope: AttributeScope::Signal, // One const per matching signal
require: "E2EDataId", // Only signals carrying this attribute
fields: &[
AttributeField { name: "data_id", source: FieldSource::Attr("E2EDataId") },
AttributeField { name: "start_byte", source: FieldSource::StartByte },
AttributeField { name: "width_bit", source: FieldSource::Attr("E2EDataLength") },
],
};

let dbc_file = "";
Config::builder()
.dbc_name("example.dbc")
.dbc_content(dbc_file)
.attribute_structs(&[e2e])
.build()
.generate()
.unwrap();
```

For every signal that carries an `E2EDataId` attribute, this generates:

```rust,ignore
impl SomeMessage {
pub const SOME_SIGNAL_E2E: data_protection::E2EDataIdInfo =
data_protection::E2EDataIdInfo { data_id: 373, start_byte: 0, width_bit: 48 };
}
```

### `no_std`

The generated code is `no_std` compatible, unless you enable `impl_error`.
Expand Down
82 changes: 82 additions & 0 deletions examples/attribute_structs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//! Demonstrates [`Config::attribute_structs`].
//! Emits constants for AUTOSAR E2E / SecOC-style metdata from DBC 'BA_' attributes.

use dbc_codegen::{AttributeField, AttributeScope, AttributeStruct, Config, FieldSource};

const DBC: &str = r#"VERSION ""

NS_ :

BS_:

BU_: ECU1

BO_ 256 Protected: 8 ECU1
SG_ Speed_CRC : 16|8@1+ (1,0) [0|255] "" ECU1

BA_DEF_ BO_ "SCP_FreshnessValueId" INT 0 65535;
BA_DEF_ SG_ "E2EDataId" INT 0 65535;
BA_DEF_ SG_ "E2EDataLength" INT 0 65535;
BA_DEF_ SG_ "E2EProfile" STRING ;
BA_DEF_DEF_ "SCP_FreshnessValueId" 0;
BA_DEF_DEF_ "E2EDataId" 0;
BA_DEF_DEF_ "E2EDataLength" 0;
BA_DEF_DEF_ "E2EProfile" "none";
BA_ "SCP_FreshnessValueId" BO_ 256 1002;
BA_ "E2EDataId" SG_ 256 Speed_CRC 373;
BA_ "E2EDataLength" SG_ 256 Speed_CRC 48;
BA_ "E2EProfile" SG_ 256 Speed_CRC "P01";
"#;

fn field(name: &'static str, source: FieldSource<'static>) -> AttributeField<'static> {
AttributeField { name, source }
}

fn main() {
let e2e = AttributeStruct {
type_path: "data_protection::E2EDataIdInfo",
const_name: "E2E",
scope: AttributeScope::Signal,
require: "E2EDataId",
fields: &[
field("data_id", FieldSource::Attr("E2EDataId")),
field("start_byte", FieldSource::StartByte),
field("width_bit", FieldSource::Attr("E2EDataLength")),
field("profile", FieldSource::Attr("E2EProfile")),
],
};
let secoc = AttributeStruct {
type_path: "data_protection::SecOcInfo",
const_name: "SEC_OC",
scope: AttributeScope::Message,
require: "SCP_FreshnessValueId",
fields: &[field(
"freshness_id",
FieldSource::Attr("SCP_FreshnessValueId"),
)],
};

let code = Config::builder()
.dbc_name("example")
.dbc_content(DBC)
.attribute_structs(&[e2e, secoc])
.build()
.generate()
.expect("generate");

// Print just the associated consts to keep the output readable.
for line in code.lines() {
let t = line.trim_start();
if t.starts_with("pub const")
|| t.starts_with("data_id")
|| t.starts_with("start_byte")
|| t.starts_with("width_bit")
|| t.starts_with("profile")
|| t.starts_with("freshness_id")
|| t.contains("E2EDataIdInfo {")
|| t.contains("SecOcInfo {")
{
println!("{line}");
}
}
}
Loading