#![deny(missing_docs)]
#![deny(clippy::arithmetic_side_effects)]
#![allow(clippy::needless_doctest_main)]
#![cfg_attr(feature = "std", doc = include_str!("../README.md"))]
#![cfg_attr(
not(feature = "std"),
doc = "Documentation is only available with the `std` feature."
)]
mod feature_config;
mod keywords;
mod pad;
mod signal_type;
mod utils;
use std::collections::{BTreeMap, BTreeSet, HashMap};
use std::fs::OpenOptions;
use std::io::{BufWriter, Write};
use std::path::Path;
use anyhow::{Context, Error, Result, anyhow, ensure};
use can_dbc::ByteOrder::{BigEndian, LittleEndian};
use can_dbc::MultiplexIndicator::{
MultiplexedSignal, Multiplexor, MultiplexorAndMultiplexedSignal, Plain,
};
use can_dbc::ValueType::Signed;
use can_dbc::{
AttributeValue, AttributeValueForRelationType, Dbc, Message, MessageId, Signal, ValDescription,
ValueDescription,
};
use heck::ToSnakeCase;
use quote::ToTokens;
use typed_builder::TypedBuilder;
pub use crate::feature_config::FeatureConfig;
use crate::pad::PadAdapter;
use crate::signal_type::{IntSize, ValType};
use crate::utils::{
MessageExt as _, SignalExt as _, enum_name, enum_variant_name, is_screaming_snake_case,
is_valid_ident, is_valid_type_path, multiplex_enum_name, multiplexed_enum_variant_name,
multiplexed_enum_variant_wrapper_name, node_field_name,
};
static ALLOW_DEADCODE: &str = "#[allow(dead_code)]";
static ALLOW_LINTS: &str = r"#[allow(
clippy::absurd_extreme_comparisons,
clippy::excessive_precision,
clippy::manual_range_contains,
clippy::unnecessary_cast,
clippy::useless_conversion,
unused_comparisons,
unused_variables,
)]";
#[derive(TypedBuilder)]
#[non_exhaustive]
pub struct Config<'a> {
pub dbc_name: &'a str,
pub dbc_content: &'a str,
#[builder(default)]
pub debug_prints: bool,
#[builder(default)]
pub impl_debug: FeatureConfig<'a>,
#[builder(default)]
pub impl_defmt: FeatureConfig<'a>,
#[builder(default)]
pub impl_arbitrary: FeatureConfig<'a>,
#[builder(default)]
pub impl_serde: FeatureConfig<'a>,
#[builder(default)]
pub impl_error: FeatureConfig<'a>,
#[builder(default = FeatureConfig::Always)]
pub impl_embedded_can_frame: FeatureConfig<'a>,
#[builder(default = FeatureConfig::Always)]
pub check_ranges: FeatureConfig<'a>,
#[builder(default)]
pub allow_dead_code: bool,
#[builder(default)]
pub padding_bit_value: bool,
#[builder(default)]
pub attribute_structs: &'a [AttributeStruct<'a>],
}
#[derive(Debug, Clone)]
pub struct AttributeStruct<'a> {
pub type_path: &'a str,
pub const_name: &'a str,
pub scope: AttributeScope,
pub require: &'a str,
pub fields: &'a [AttributeField<'a>],
pub for_node: Option<&'a str>,
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub enum AttributeScope {
Message,
Signal,
NodeMessage,
NodeSignal,
}
#[derive(Debug, Clone)]
pub struct AttributeField<'a> {
pub name: &'a str,
pub source: FieldSource<'a>,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum FieldSource<'a> {
Attr(&'a str),
MessageAttr(&'a str),
StartBit,
StartByte,
BitWidth,
MessageSize,
Int(i64),
Str(&'a str),
NodeName,
}
struct AttrTarget<'a> {
msg: &'a Message,
dbc: &'a Dbc,
signal: Option<&'a Signal>,
node: Option<&'a str>,
}
impl Config<'_> {
fn codegen(&self, out: impl Write) -> Result<()> {
self.validate_attribute_structs()?;
let dbc = Dbc::try_from(self.dbc_content).map_err(|e| {
let msg = "Could not parse dbc file";
if self.debug_prints {
anyhow!("{msg}: {e:#?}")
} else {
anyhow!("{msg}")
}
})?;
if self.debug_prints {
eprintln!("{dbc:#?}");
}
let mut w = BufWriter::new(out);
writeln!(
w,
"/// The name of the DBC file this code was generated from"
)?;
writeln!(w, "#[allow(dead_code)]")?;
let dbc_name = self.dbc_name.to_token_stream();
writeln!(w, "pub const DBC_FILE_NAME: &str = {dbc_name};")?;
writeln!(
w,
"/// The version of the DBC file this code was generated from"
)?;
writeln!(w, "#[allow(dead_code)]")?;
let dbc_version = dbc.version.0.to_token_stream();
writeln!(w, "pub const DBC_FILE_VERSION: &str = {dbc_version};")?;
writeln!(w, "#[allow(unused_imports)]")?;
writeln!(w, "use core::ops::BitOr;")?;
writeln!(w, "#[allow(unused_imports)]")?;
writeln!(w, "use bitvec::prelude::*;")?;
writeln!(w, "#[allow(unused_imports)]")?;
writeln!(w, "use embedded_can::{{Id, StandardId, ExtendedId}};")?;
self.impl_arbitrary.fmt_cfg(&mut w, |w| {
writeln!(w, "use arbitrary::{{Arbitrary, Unstructured}};")
})?;
self.impl_serde.fmt_cfg(&mut w, |w| {
writeln!(w, "use serde::{{Serialize, Deserialize}};")
})?;
writeln!(w)?;
self.render_dbc(&mut w, &dbc)?;
writeln!(w)?;
self.render_error(&mut w)?;
self.render_arbitrary_helpers(&mut w)?;
writeln!(w)?;
Ok(())
}
fn render_dbc(&self, w: &mut impl Write, dbc: &Dbc) -> Result<()> {
self.render_root_enum(w, dbc)?;
for msg in get_relevant_messages(dbc) {
self.render_message(w, msg, dbc)
.with_context(|| format!("write message `{}`", msg.name))?;
writeln!(w)?;
}
Ok(())
}
fn render_root_enum(&self, w: &mut impl Write, dbc: &Dbc) -> Result<()> {
writeln!(w, "/// All messages")?;
writeln!(w, "{ALLOW_LINTS}")?;
self.write_allow_dead_code(w)?;
writeln!(w, "#[derive(Clone)]")?;
self.impl_debug.fmt_attr(w, "derive(Debug)")?;
self.impl_defmt.fmt_attr(w, "derive(defmt::Format)")?;
self.impl_serde.fmt_attr(w, "derive(Serialize)")?;
self.impl_serde.fmt_attr(w, "derive(Deserialize)")?;
writeln!(w, "pub enum Messages {{")?;
{
let mut w = PadAdapter::wrap(w);
for msg in get_relevant_messages(dbc) {
writeln!(w, "/// {}", msg.name)?;
writeln!(w, "{0}({0}),", msg.type_name())?;
}
}
writeln!(w, "}}")?;
writeln!(w)?;
writeln!(w, "{ALLOW_LINTS}")?;
self.write_allow_dead_code(w)?;
writeln!(w, "impl Messages {{")?;
{
let mut w = PadAdapter::wrap(w);
writeln!(w, "/// Read message from CAN frame")?;
writeln!(w, "#[inline(never)]")?;
writeln!(
w,
"pub fn from_can_message(id: Id, payload: &[u8]) -> Result<Self, CanError> {{",
)?;
{
let mut w = PadAdapter::wrap(&mut w);
let messages: Vec<_> = get_relevant_messages(dbc).collect();
if messages.is_empty() {
writeln!(w, "Err(CanError::UnknownMessageId(id))")?;
} else {
writeln!(w)?;
writeln!(w, "let res = match id {{")?;
{
let mut w = PadAdapter::wrap(&mut w);
for msg in messages {
writeln!(
w,
"{0}::MESSAGE_ID => Messages::{0}({0}::try_from(payload)?),",
msg.type_name(),
)?;
}
writeln!(w, r"id => return Err(CanError::UnknownMessageId(id)),")?;
}
writeln!(w, "}};")?;
writeln!(w, "Ok(res)")?;
}
}
writeln!(w, "}}")?;
}
writeln!(w, "}}")?;
writeln!(w)?;
Ok(())
}
fn render_message(&self, w: &mut impl Write, msg: &Message, dbc: &Dbc) -> Result<()> {
writeln!(w, "/// {}", msg.name)?;
writeln!(w, "///")?;
match msg.id {
MessageId::Standard(id) => writeln!(w, "/// - Standard ID: {id} (0x{id:x})"),
MessageId::Extended(id) => writeln!(w, "/// - Extended ID: {id} (0x{id:x})"),
}?;
writeln!(w, "/// - Size: {} bytes", msg.size)?;
if let Some(transmitter) = &msg.transmitter {
writeln!(w, "/// - Transmitter: {transmitter}")?;
}
if let Some(comment) = dbc.message_comment(msg.id) {
writeln!(w, "///")?;
for line in comment.trim().lines() {
writeln!(w, "/// {line}")?;
}
}
writeln!(w, "#[derive(Clone, Copy)]")?;
self.impl_serde.fmt_attr(w, "derive(Serialize)")?;
self.impl_serde.fmt_attr(w, "derive(Deserialize)")?;
writeln!(w, "pub struct {} {{", msg.type_name())?;
{
let mut w = PadAdapter::wrap(w);
self.impl_serde
.fmt_attr(&mut w, "serde(with = \"serde_bytes\")")?;
writeln!(w, "raw: [u8; {}],", msg.size)?;
}
writeln!(w, "}}")?;
writeln!(w)?;
writeln!(w, "{ALLOW_LINTS}")?;
self.write_allow_dead_code(w)?;
writeln!(w, "impl {} {{", msg.type_name())?;
{
let mut w = PadAdapter::wrap(w);
writeln!(
w,
"pub const MESSAGE_ID: embedded_can::Id = {};",
match msg.id {
MessageId::Standard(id) =>
format!("Id::Standard(StandardId::new({id:#x}).unwrap())"),
MessageId::Extended(id) =>
format!("Id::Extended(ExtendedId::new({id:#x}).unwrap())"),
}
)?;
writeln!(w)?;
writeln!(w, "pub const MESSAGE_SIZE: usize = {};", msg.size)?;
writeln!(w)?;
Self::render_cycle_time(&mut w, msg, dbc)?;
for signal in &msg.signals {
let typ = ValType::from_signal(signal);
if typ != ValType::Bool {
let sig = signal.field_name().to_uppercase();
let min = signal.min;
let max = signal.max;
writeln!(w, "pub const {sig}_MIN: {typ} = {min}_{typ};")?;
writeln!(w, "pub const {sig}_MAX: {typ} = {max}_{typ};")?;
}
}
writeln!(w)?;
self.render_attribute_structs(&mut w, msg, dbc)?;
writeln!(
w,
"/// Constructs a new `{}` message from values.",
msg.name
)?;
let args = msg
.signals
.iter()
.filter_map(|signal| {
if matches!(signal.multiplexer_indicator, Plain | Multiplexor) {
let field = signal.field_name();
let typ = signal_pub_type(dbc, msg, signal);
Some(format!("{field}: {typ}"))
} else {
None
}
})
.collect::<Vec<_>>()
.join(", ");
writeln!(w, "pub fn new({args}) -> Result<Self, CanError> {{")?;
{
let mut w = PadAdapter::wrap(&mut w);
let mutable = if msg.signals.is_empty() { "" } else { "mut " };
let padding_value = if self.padding_bit_value {
"0xFF"
} else {
"0x00"
};
let size = msg.size;
writeln!(
w,
"let {mutable}res = Self {{ raw: [{padding_value}; {size}] }};"
)?;
for signal in &msg.signals {
if matches!(signal.multiplexer_indicator, Plain | Multiplexor) {
writeln!(w, "res.set_{0}({0})?;", signal.field_name())?;
}
}
writeln!(w, "Ok(res)")?;
}
writeln!(w, "}}")?;
writeln!(w)?;
writeln!(w, "/// Returns the raw `{}` message payload.", msg.name)?;
writeln!(w, "pub fn raw(&self) -> &[u8; {}] {{", msg.size)?;
{
let mut w = PadAdapter::wrap(&mut w);
writeln!(w, "&self.raw")?;
}
writeln!(w, "}}")?;
writeln!(w)?;
for signal in &msg.signals {
match signal.multiplexer_indicator {
Plain => self
.render_signal(&mut w, signal, dbc, msg)
.with_context(|| format!("write signal impl `{}`", signal.name))?,
Multiplexor => {
self.render_multiplexor_signal(&mut w, signal, dbc, msg)?;
}
MultiplexedSignal(_) | MultiplexorAndMultiplexedSignal(_) => {}
}
}
}
writeln!(w, "}}")?;
writeln!(w)?;
let typ = msg.type_name();
writeln!(w, "impl core::convert::TryFrom<&[u8]> for {typ} {{")?;
{
let mut w = PadAdapter::wrap(w);
writeln!(w, "type Error = CanError;")?;
writeln!(w)?;
writeln!(w, "#[inline(always)]")?;
writeln!(
w,
"fn try_from(payload: &[u8]) -> Result<Self, Self::Error> {{",
)?;
{
let mut w = PadAdapter::wrap(&mut w);
writeln!(
w,
r"if payload.len() != {} {{ return Err(CanError::InvalidPayloadSize); }}",
msg.size,
)?;
writeln!(w, "let mut raw = [0u8; {}];", msg.size)?;
writeln!(w, "raw.copy_from_slice(&payload[..{}]);", msg.size)?;
writeln!(w, "Ok(Self {{ raw }})")?;
}
writeln!(w, "}}")?;
}
writeln!(w, "}}")?;
writeln!(w)?;
self.render_embedded_can_frame(w, msg)?;
self.impl_debug
.fmt_cfg(&mut *w, |w| render_debug_impl(w, msg))?;
self.impl_defmt
.fmt_cfg(&mut *w, |w| render_defmt_impl(w, msg))?;
self.impl_arbitrary
.fmt_cfg(&mut *w, |w| self.render_arbitrary(w, dbc, msg))?;
let enums_for_this_message = dbc.value_descriptions.iter().filter_map(|x| {
if let ValueDescription::Signal {
message_id,
name,
value_descriptions,
} = x
{
if *message_id != msg.id {
return None;
}
dbc.signal_by_name(*message_id, name)
.map(|v| (v, value_descriptions))
} else {
None
}
});
for (signal, variants) in enums_for_this_message {
self.write_enum(w, signal, msg, variants.as_slice())?;
}
let multiplexor_signal = msg
.signals
.iter()
.find(|s| s.multiplexer_indicator == Multiplexor);
if let Some(multiplexor_signal) = multiplexor_signal {
self.render_multiplexor_enums(w, dbc, msg, multiplexor_signal)?;
}
Ok(())
}
fn validate_attribute_structs(&self) -> Result<()> {
for spec in self.attribute_structs {
ensure!(
is_screaming_snake_case(spec.const_name),
"attribute_structs: 'const_name' {:?} must be SCREAMING_SNAKE_CASE",
spec.const_name
);
ensure!(
is_valid_type_path(spec.type_path),
"attribute_structs: 'type_path' {:?} for {:?} is not a valid Rust type path",
spec.type_path,
spec.const_name
);
ensure!(
!spec.require.is_empty(),
"attribute_structs: 'require' must not be empty for {:?}",
spec.const_name
);
ensure!(
!spec.fields.is_empty(),
"attribute_structs: {:?} declares no fields",
spec.const_name
);
let has_signal = matches!(
spec.scope,
AttributeScope::Signal | AttributeScope::NodeSignal
);
let has_node = matches!(
spec.scope,
AttributeScope::NodeSignal | AttributeScope::NodeMessage
);
if let Some(for_node) = spec.for_node {
ensure!(
!for_node.is_empty(),
"attribute_structs: 'for_node' must not be empty for {:?}",
spec.const_name
);
ensure!(
has_node,
"attribute_structs: {:?} sets 'for_node' but its scope has no associated \
node (scope must be NodeSignal or NodeMessage)",
spec.const_name
);
}
let mut seen = BTreeSet::new();
for field in spec.fields {
ensure!(
is_valid_ident(field.name),
"attribute_structs: field name {:?} in {:?} is not a valid Rust identifier",
field.name,
spec.const_name
);
ensure!(
seen.insert(field.name),
"attribute_structs: duplicate field {:?} in {:?}",
field.name,
spec.const_name
);
if !has_signal {
ensure!(
!matches!(
field.source,
FieldSource::StartBit | FieldSource::StartByte | FieldSource::BitWidth
),
"attribute_structs: field {:?} of {:?} uses a signal-only source \
(StartBit/StartByte/BitWidth) but the struct has no associated signal",
field.name,
spec.const_name
);
}
if !has_node {
ensure!(
!matches!(field.source, FieldSource::NodeName),
"attribute_structs: field {:?} of {:?} uses NodeName but the struct has \
no associated node (scope must be NodeSignal or NodeMessage)",
field.name,
spec.const_name
);
}
}
}
Ok(())
}
fn render_attribute_structs(&self, w: &mut impl Write, msg: &Message, dbc: &Dbc) -> Result<()> {
if self.attribute_structs.is_empty() {
return Ok(());
}
let mut used: BTreeSet<String> = BTreeSet::new();
used.insert("MESSAGE_ID".to_string());
for signal in &msg.signals {
if ValType::from_signal(signal) != ValType::Bool {
let sig = signal.field_name().to_uppercase();
used.insert(format!("{sig}_MIN"));
used.insert(format!("{sig}_MAX"));
}
}
for spec in self.attribute_structs {
match spec.scope {
AttributeScope::Message => {
if dbc.message_attribute(msg.id, spec.require).is_none() {
continue;
}
Self::render_attribute_struct(
w,
spec,
spec.const_name,
&AttrTarget {
msg,
dbc,
signal: None,
node: None,
},
&mut used,
)?;
}
AttributeScope::Signal => {
for signal in &msg.signals {
if dbc
.signal_attribute(msg.id, &signal.name, spec.require)
.is_none()
{
continue;
}
let name =
format!("{}_{}", signal.field_name().to_uppercase(), spec.const_name);
Self::render_attribute_struct(
w,
spec,
&name,
&AttrTarget {
msg,
dbc,
signal: Some(signal),
node: None,
},
&mut used,
)?;
}
}
AttributeScope::NodeSignal => {
for signal in &msg.signals {
for node in &signal.receivers {
if spec.for_node.is_some_and(|target| node != target) {
continue;
}
if node_relation_attribute(
dbc,
node,
msg.id,
Some(&signal.name),
spec.require,
)
.is_none()
{
continue;
}
let name = if spec.for_node.is_some() {
format!(
"{}_{}",
signal.field_name().to_uppercase(),
spec.const_name
)
} else {
let node_ident = node_field_name(node).to_uppercase();
format!(
"{}_{node_ident}_{}",
signal.field_name().to_uppercase(),
spec.const_name
)
};
Self::render_attribute_struct(
w,
spec,
&name,
&AttrTarget {
msg,
dbc,
signal: Some(signal),
node: Some(node),
},
&mut used,
)?;
}
}
}
AttributeScope::NodeMessage => {
for node in &dbc.nodes {
if spec.for_node.is_some_and(|target| node.0 != target) {
continue;
}
if node_relation_attribute(dbc, &node.0, msg.id, None, spec.require)
.is_none()
{
continue;
}
let name = if spec.for_node.is_some() {
spec.const_name.to_string()
} else {
format!(
"{}_{}",
node_field_name(&node.0).to_uppercase(),
spec.const_name
)
};
Self::render_attribute_struct(
w,
spec,
&name,
&AttrTarget {
msg,
dbc,
signal: None,
node: Some(&node.0),
},
&mut used,
)?;
}
}
}
}
Ok(())
}
fn render_attribute_struct(
w: &mut impl Write,
spec: &AttributeStruct<'_>,
const_name: &str,
target: &AttrTarget<'_>,
used: &mut BTreeSet<String>,
) -> Result<()> {
ensure!(
used.insert(const_name.to_string()),
"attribute_structs: generated const '{const_name}' on message {:?} collides with \
another const. Use a distinct 'const_name'.",
target.msg.name
);
let mut fields = Vec::with_capacity(spec.fields.len());
for field in spec.fields {
let lit = resolve_field_source(&field.source, target).ok_or_else(|| {
anyhow!(
"attribute_structs: const '{const_name}' field {:?} has no value in the DBC \
and no default (source: {:?})",
field.name,
field.source
)
})?;
fields.push((field.name, lit));
}
let ty = spec.type_path;
writeln!(w, "/// Generated from DBC attributes `{}`", spec.require)?;
writeln!(w, "pub const {const_name}: {ty} = {ty} {{")?;
{
let mut w = PadAdapter::wrap(w);
for (name, lit) in fields {
writeln!(w, "{name}: {lit},")?;
}
}
writeln!(w, "}};")?;
writeln!(w)?;
Ok(())
}
fn render_cycle_time(w: &mut impl Write, msg: &Message, dbc: &Dbc) -> Result<()> {
let Some(ms) = message_cycle_time_ms(dbc, msg.id) else {
return Ok(());
};
writeln!(w, "pub const MESSAGE_CYCLE_TIME_MS: u32 = {ms};")?;
Ok(())
}
fn render_signal(
&self,
w: &mut impl Write,
signal: &Signal,
dbc: &Dbc,
msg: &Message,
) -> Result<()> {
writeln!(w, "/// Returns the value of `{}`.", signal.name)?;
if let Some(comment) = dbc.signal_comment(msg.id, &signal.name) {
writeln!(w, "///")?;
for line in comment.trim().lines() {
writeln!(w, "/// {line}")?;
}
}
writeln!(w, "///")?;
writeln!(w, "/// - Min: {}", signal.min)?;
writeln!(w, "/// - Max: {}", signal.max)?;
if signal.unit.is_empty() {
writeln!(w, "/// - Unit: Not specified")?;
} else {
writeln!(w, "/// - Unit: {:?}", signal.unit)?;
}
if signal.receivers.is_empty() {
writeln!(w, "/// - Receivers: Vector__XXX")?;
} else {
writeln!(w, "/// - Receivers: {}", signal.receivers.join(", "))?;
}
let fn_name = signal.field_name();
let variants = dbc.value_descriptions_for_signal(msg.id, &signal.name);
if variants.is_none() {
writeln!(w, "/// - Factor: {}", signal.factor)?;
writeln!(w, "/// - Offset: {}", signal.offset)?;
}
writeln!(w, "#[inline(always)]")?;
if let Some(variants) = variants {
let type_name = enum_name(msg, signal);
let signal_ty = ValType::from_signal(signal);
let variant_infos = generate_variant_info(variants, signal_ty);
writeln!(w, "pub fn {fn_name}(&self) -> {type_name} {{")?;
{
let mut w = PadAdapter::wrap(w);
let has_negative_values = variants.iter().any(|v| v.id < 0);
let load_type = if signal.value_type == Signed && has_negative_values {
signal_ty
} else {
ValType::from_signal_uint(signal)
};
let read = read_fn_with_type(signal, msg, load_type)?;
writeln!(w, r"let signal = {read};")?;
writeln!(w)?;
writeln!(w, "match signal {{")?;
{
let mut w = PadAdapter::wrap(&mut w);
for info in &variant_infos {
let literal = info.value;
let variant = &info.base_name;
match info.dup_type {
DuplicateType::Unique => {
writeln!(w, "{literal} => {type_name}::{variant},")?;
}
DuplicateType::FirstDuplicate | DuplicateType::Duplicate => {
writeln!(w, "{literal} => {type_name}::{variant}({literal}),")?;
}
}
}
writeln!(w, "_ => {type_name}::_Other(self.{fn_name}_phys_val()),")?;
}
writeln!(w, "}}")?;
}
writeln!(w, "}}")?;
writeln!(w)?;
writeln!(w, "#[inline(always)]")?;
let typ = ValType::from_signal(signal);
writeln!(w, "fn {fn_name}_phys_val(&self) -> {typ} {{")?;
{
let mut w = PadAdapter::wrap(w);
signal_from_payload(&mut w, signal, msg).context("signal from payload")?;
}
writeln!(w, "}}")?;
writeln!(w)?;
} else {
let typ = ValType::from_signal(signal);
writeln!(w, "pub fn {fn_name}(&self) -> {typ} {{")?;
{
let mut w = PadAdapter::wrap(w);
signal_from_payload(&mut w, signal, msg).context("signal from payload")?;
}
writeln!(w, "}}")?;
writeln!(w)?;
}
render_raw_accessors(w, signal, msg)?;
self.render_set_signal(w, signal, dbc, msg)?;
Ok(())
}
fn render_set_signal(
&self,
w: &mut impl Write,
signal: &Signal,
dbc: &Dbc,
msg: &Message,
) -> Result<()> {
let visibility = if signal.multiplexer_indicator == Multiplexor {
""
} else {
"pub "
};
let field = signal.field_name();
let typ = ValType::from_signal(signal);
let param_type = signal_pub_type(dbc, msg, signal);
let is_enum_backed = param_type != typ.to_string();
writeln!(w, "/// Sets the value of `{}`.", signal.name)?;
writeln!(w, "#[inline(always)]")?;
writeln!(
w,
"{visibility}fn set_{field}(&mut self, value: {param_type}) -> Result<(), CanError> {{",
)?;
{
let mut w = PadAdapter::wrap(w);
if is_enum_backed {
writeln!(w, "let value = {typ}::from(value);")?;
}
self.render_set_signal_body(&mut w, signal, msg)?;
}
writeln!(w, "}}")?;
writeln!(w)?;
Ok(())
}
fn render_set_signal_body(
&self,
w: &mut impl Write,
signal: &Signal,
msg: &Message,
) -> Result<()> {
if signal.size != 1 {
if let FeatureConfig::Gated(gate) = self.check_ranges {
writeln!(w, r"#[cfg(feature = {gate:?})]")?;
}
if let FeatureConfig::Gated(..) | FeatureConfig::Always = self.check_ranges {
let typ = ValType::from_signal(signal);
let min = signal.min;
let max = signal.max;
writeln!(w, r"if value < {min}_{typ} || {max}_{typ} < value {{")?;
{
let mut w = PadAdapter::wrap(&mut *w);
let typ = msg.type_name();
writeln!(
w,
r"return Err(CanError::ParameterOutOfRange {{ message_id: {typ}::MESSAGE_ID }});",
)?;
}
writeln!(w, r"}}")?;
}
}
signal_to_payload(&mut *w, signal, msg).context("signal to payload")?;
Ok(())
}
fn render_multiplexor_signal(
&self,
w: &mut impl Write,
signal: &Signal,
dbc: &Dbc,
msg: &Message,
) -> Result<()> {
let signal_typ = ValType::from_signal(signal);
render_raw_accessors(w, signal, msg)?;
let field = signal.field_name();
let typ = multiplex_enum_name(msg, signal)?;
writeln!(
w,
"/// Selects the active multiplexed sub-message for `{}`.",
signal.name
)?;
writeln!(
w,
"pub fn {field}_multiplexed(&mut self) -> Result<{typ}, CanError> {{"
)?;
let multiplexer_indexes: BTreeSet<u64> = msg
.signals
.iter()
.filter_map(|s| {
if let MultiplexedSignal(index) = &s.multiplexer_indicator {
Some(index)
} else {
None
}
})
.copied()
.collect();
{
let mut w = PadAdapter::wrap(w);
writeln!(w, "match self.{}_raw_val() {{", signal.field_name())?;
{
let mut w = PadAdapter::wrap(&mut w);
for idx in &multiplexer_indexes {
writeln!(
w,
"{idx} => Ok({enum_name}::{multiplexed_wrapper_name}({multiplexed_name}{{ raw: self.raw }})),",
enum_name = multiplex_enum_name(msg, signal)?,
multiplexed_wrapper_name = multiplexed_enum_variant_wrapper_name(*idx),
multiplexed_name = multiplexed_enum_variant_name(msg, signal, *idx)?,
)?;
}
let to_u16 = match signal_typ {
ValType::UnsignedInt(IntSize::Size8) => "multiplexor.into()",
ValType::UnsignedInt(IntSize::Size16) => "multiplexor",
_ => "u16::try_from(multiplexor).unwrap_or(u16::MAX)",
};
writeln!(
w,
"multiplexor => Err(CanError::InvalidMultiplexor {{ message_id: {}::MESSAGE_ID, multiplexor: {to_u16} }}),",
msg.type_name(),
)?;
}
writeln!(w, "}}")?;
}
writeln!(w, "}}")?;
self.render_set_signal(w, signal, dbc, msg)?;
for switch_index in multiplexer_indexes {
render_set_signal_multiplexer(w, signal, msg, switch_index)?;
}
Ok(())
}
fn write_enum(
&self,
w: &mut impl Write,
signal: &Signal,
msg: &Message,
variants: &[ValDescription],
) -> Result<()> {
let type_name = enum_name(msg, signal);
let signal_ty = ValType::from_signal(signal);
let variant_infos = generate_variant_info(variants, signal_ty);
writeln!(w, "/// Defined values for {}", signal.name)?;
writeln!(w, "{ALLOW_LINTS}")?;
self.write_allow_dead_code(w)?;
writeln!(w, "#[derive(Clone, Copy, PartialEq)]")?;
self.impl_debug.fmt_attr(w, "derive(Debug)")?;
self.impl_defmt.fmt_attr(w, "derive(defmt::Format)")?;
self.impl_serde.fmt_attr(w, "derive(Serialize)")?;
self.impl_serde.fmt_attr(w, "derive(Deserialize)")?;
writeln!(w, "pub enum {type_name} {{")?;
{
let mut w = PadAdapter::wrap(w);
for info in &variant_infos {
let variant = &info.base_name;
match info.dup_type {
DuplicateType::Unique => writeln!(w, "{variant},")?,
DuplicateType::FirstDuplicate => {
writeln!(w, "{variant}({}),", info.value_type)?;
}
DuplicateType::Duplicate => {}
}
}
writeln!(w, "_Other({signal_ty}),")?;
}
writeln!(w, "}}")?;
writeln!(w)?;
writeln!(w, "impl From<{type_name}> for {signal_ty} {{")?;
{
let match_on_raw_type = match ValType::from_signal(signal) {
ValType::Bool => |x: i64| format!("{}", x == 1),
ValType::F32 => |x: i64| format!("{x}_f32"),
_ => |x: i64| format!("{x}"),
};
let mut w = PadAdapter::wrap(w);
writeln!(w, "fn from(val: {type_name}) -> {signal_ty} {{")?;
{
let mut w = PadAdapter::wrap(&mut w);
writeln!(w, "match val {{")?;
{
let mut w = PadAdapter::wrap(&mut w);
for info in &variant_infos {
match info.dup_type {
DuplicateType::Unique => {
let literal = match_on_raw_type(info.value);
writeln!(w, "{type_name}::{} => {literal},", info.base_name)?;
}
DuplicateType::FirstDuplicate => {
writeln!(w, "{type_name}::{}(v) => v,", info.base_name)?;
}
DuplicateType::Duplicate => {}
}
}
writeln!(w, "{type_name}::_Other(x) => x,")?;
}
writeln!(w, "}}")?;
}
writeln!(w, "}}")?;
}
writeln!(w, "}}")?;
writeln!(w)?;
Ok(())
}
}
fn render_set_signal_multiplexer(
w: &mut impl Write,
multiplexor: &Signal,
msg: &Message,
switch_index: u64,
) -> Result<()> {
writeln!(w, "/// Sets the value of `{}`.", multiplexor.name)?;
writeln!(w, "#[inline(always)]")?;
writeln!(
w,
"pub fn set_{enum_variant_wrapper}(&mut self, value: {enum_variant}) -> Result<(), CanError> {{",
enum_variant_wrapper = multiplexed_enum_variant_wrapper_name(switch_index).to_snake_case(),
enum_variant = multiplexed_enum_variant_name(msg, multiplexor, switch_index)?,
)?;
{
let mut w = PadAdapter::wrap(w);
writeln!(w, "let b0 = BitArray::<_, LocalBits>::new(self.raw);")?;
writeln!(w, "let b1 = BitArray::<_, LocalBits>::new(value.raw);")?;
writeln!(w, "self.raw = b0.bitor(b1).into_inner();")?;
writeln!(w, "self.set_{}({switch_index})?;", multiplexor.field_name())?;
writeln!(w, "Ok(())")?;
}
writeln!(w, "}}")?;
writeln!(w)?;
Ok(())
}
fn be_start_end_bit(signal: &Signal, msg: &Message) -> Result<(u64, u64)> {
let err = "calculating start bit";
let x = signal.start_bit.checked_div(8).context(err)?;
let x = x.checked_mul(8).context(err)?;
let y = signal.start_bit.checked_rem(8).context(err)?;
let y = 7u64.checked_sub(y).context(err)?;
let start_bit = x.checked_add(y).context(err)?;
let end_bit = start_bit
.checked_add(signal.size)
.context("calculating last bit position")?;
let msg_bits = msg.size.checked_mul(8).context("message size overflow")?;
ensure!(
start_bit <= msg_bits,
"signal starts at {start_bit}, but message is only {msg_bits} bits",
);
ensure!(
end_bit <= msg_bits,
"signal ends at {end_bit}, but message is only {msg_bits} bits",
);
Ok((start_bit, end_bit))
}
fn le_start_end_bit(signal: &Signal, msg: &Message) -> Result<(u64, u64)> {
let msg_bits = msg.size.checked_mul(8).context("message size overflow")?;
let start_bit = signal.start_bit;
ensure!(
start_bit <= msg_bits,
"signal starts at {start_bit}, but message is only {msg_bits} bits",
);
let end_bit = signal
.start_bit
.checked_add(signal.size)
.context("overflow calculating last bit position")?;
ensure!(
end_bit <= msg_bits,
"signal ends at {end_bit}, but message is only {msg_bits} bits",
);
Ok((start_bit, end_bit))
}
fn signal_pub_type(dbc: &Dbc, msg: &Message, signal: &Signal) -> String {
if signal.multiplexer_indicator != Multiplexor
&& dbc
.value_descriptions_for_signal(msg.id, &signal.name)
.is_some()
{
enum_name(msg, signal)
} else {
ValType::from_signal(signal).to_string()
}
}
#[allow(clippy::float_cmp)]
fn is_unscaled(signal: &Signal) -> bool {
signal.factor == 1.0 && signal.offset == 0.0
}
fn signal_from_payload(w: &mut impl Write, signal: &Signal, msg: &Message) -> Result<()> {
if is_unscaled(signal) {
let field = signal.field_name();
if signal.size == 1 {
writeln!(w, "self.{field}_raw_val() == 1")?;
} else {
writeln!(w, "self.{field}_raw_val()")?;
}
return Ok(());
}
writeln!(w, r"let signal = {};", read_fn(signal, msg)?)?;
writeln!(w)?;
let typ = ValType::from_signal(signal);
match typ {
ValType::Bool => {
writeln!(w, "signal == 1")?;
}
ValType::F32 => {
writeln!(w, "let factor = {}_f32;", signal.factor)?;
writeln!(w, "let offset = {}_f32;", signal.offset)?;
writeln!(w, "(signal as f32) * factor + offset")?;
}
_ => {
writeln!(w, "let factor = {};", signal.factor)?;
if Some(typ) == ValType::from_signal_uint(signal).unsigned_to_signed() {
writeln!(w, "let signal = signal as {typ};")?;
}
if signal.offset >= 0.0 {
writeln!(
w,
"{typ}::from(signal).saturating_mul(factor).saturating_add({})",
signal.offset,
)?;
} else {
writeln!(
w,
"{typ}::from(signal).saturating_mul(factor).saturating_sub({})",
signal.offset.abs(),
)?;
}
}
}
Ok(())
}
fn read_fn(signal: &Signal, msg: &Message) -> Result<String> {
read_fn_with_type(signal, msg, ValType::from_signal_int(signal))
}
fn read_fn_with_type(signal: &Signal, msg: &Message, typ: ValType) -> Result<String> {
Ok(match signal.byte_order {
LittleEndian => {
let (start, end) = le_start_end_bit(signal, msg)?;
format!("self.raw.view_bits::<Lsb0>()[{start}..{end}].load_le::<{typ}>()")
}
BigEndian => {
let (start, end) = be_start_end_bit(signal, msg)?;
format!("self.raw.view_bits::<Msb0>()[{start}..{end}].load_be::<{typ}>()")
}
})
}
fn signal_to_payload(w: &mut impl Write, signal: &Signal, msg: &Message) -> Result<()> {
let typ = ValType::from_signal(signal);
match typ {
ValType::Bool => {
writeln!(w, "let value = value as u8;")?;
}
ValType::F32 => {
writeln!(w, "let factor = {}_f32;", signal.factor)?;
writeln!(w, "let offset = {}_f32;", signal.offset)?;
let typ = ValType::from_signal_int(signal);
writeln!(w, "let value = ((value - offset) / factor) as {typ};")?;
writeln!(w)?;
}
_ if is_unscaled(signal) => {
}
_ => {
writeln!(w, "let factor = {};", signal.factor)?;
if signal.offset >= 0.0 {
writeln!(w, "let value = value.checked_sub({})", signal.offset)?;
} else {
writeln!(w, "let value = value.checked_add({})", signal.offset.abs())?;
}
writeln!(
w,
" .ok_or(CanError::ParameterOutOfRange {{ message_id: {}::MESSAGE_ID }})?;",
msg.type_name(),
)?;
let typ = ValType::from_signal_int(signal);
writeln!(w, "let value = (value / factor) as {typ};")?;
writeln!(w)?;
}
}
pack_bits(w, signal, msg)?;
writeln!(w, "Ok(())")?;
Ok(())
}
fn pack_bits(w: &mut impl Write, signal: &Signal, msg: &Message) -> Result<()> {
if signal.value_type == Signed {
let typ = ValType::from_signal_uint(signal);
writeln!(w, "let value = {typ}::from_ne_bytes(value.to_ne_bytes());")?;
}
match signal.byte_order {
LittleEndian => {
let (start, end) = le_start_end_bit(signal, msg)?;
writeln!(
w,
r"self.raw.view_bits_mut::<Lsb0>()[{start}..{end}].store_le(value);",
)?;
}
BigEndian => {
let (start, end) = be_start_end_bit(signal, msg)?;
writeln!(
w,
r"self.raw.view_bits_mut::<Msb0>()[{start}..{end}].store_be(value);",
)?;
}
}
Ok(())
}
fn render_raw_accessors(w: &mut impl Write, signal: &Signal, msg: &Message) -> Result<()> {
let field = signal.field_name();
let typ = ValType::from_signal_int(signal);
writeln!(w, "/// Returns the raw value of `{}`.", signal.name)?;
writeln!(w, "///")?;
writeln!(w, "/// - Start bit: {}", signal.start_bit)?;
writeln!(w, "/// - Signal size: {} bits", signal.size)?;
writeln!(w, "/// - Byte order: {:?}", signal.byte_order)?;
writeln!(w, "/// - Value type: {:?}", signal.value_type)?;
writeln!(w, "#[inline(always)]")?;
writeln!(w, "pub fn {field}_raw_val(&self) -> {typ} {{")?;
{
let mut w = PadAdapter::wrap(&mut *w);
writeln!(w, "{}", read_fn(signal, msg)?)?;
}
writeln!(w, "}}")?;
writeln!(w)?;
let visibility = if signal.multiplexer_indicator == Multiplexor {
""
} else {
"pub "
};
writeln!(w, "/// Sets the raw value of `{}`.", signal.name)?;
if visibility.is_empty() {
writeln!(w, "#[allow(dead_code)]")?;
}
writeln!(w, "#[inline(always)]")?;
writeln!(
w,
"{visibility}fn set_{field}_raw_val(&mut self, value: {typ}) {{",
)?;
{
let mut w = PadAdapter::wrap(&mut *w);
pack_bits(&mut w, signal, msg)?;
}
writeln!(w, "}}")?;
writeln!(w)?;
Ok(())
}
enum DuplicateType {
Unique,
FirstDuplicate,
Duplicate,
}
struct VariantInfo {
base_name: String,
value: i64,
dup_type: DuplicateType,
value_type: String,
}
fn generate_variant_info(variants: &[ValDescription], signal_ty: ValType) -> Vec<VariantInfo> {
let mut name_counts: HashMap<String, usize> = HashMap::new();
for variant in variants {
let base_name = enum_variant_name(&variant.description);
name_counts
.entry(base_name)
.and_modify(|c| *c = c.saturating_add(1))
.or_insert(1);
}
let mut variant_infos = Vec::new();
let mut seen_names: HashMap<String, usize> = HashMap::new();
for variant in variants {
let base_name = enum_variant_name(&variant.description);
let count = name_counts.get(&base_name).copied().unwrap_or(0);
let seen_count = seen_names.entry(base_name.clone()).or_insert(0);
*seen_count = (*seen_count).saturating_add(1);
let dup_type = if count == 1 {
DuplicateType::Unique
} else if *seen_count == 1 {
DuplicateType::FirstDuplicate
} else {
DuplicateType::Duplicate
};
variant_infos.push(VariantInfo {
base_name,
value: variant.id,
dup_type,
value_type: signal_ty.to_string(),
});
}
variant_infos
}
impl Config<'_> {
fn render_embedded_can_frame(&self, w: &mut impl Write, msg: &Message) -> Result<()> {
self.impl_embedded_can_frame.fmt_cfg(w, |w| {
writeln!(
w,
"\
impl embedded_can::Frame for {0} {{
fn new(id: impl Into<Id>, data: &[u8]) -> Option<Self> {{
if id.into() != Self::MESSAGE_ID {{
None
}} else {{
data.try_into().ok()
}}
}}
fn new_remote(_id: impl Into<Id>, _dlc: usize) -> Option<Self> {{
unimplemented!()
}}
fn is_extended(&self) -> bool {{
match self.id() {{
Id::Standard(_) => false,
Id::Extended(_) => true,
}}
}}
fn is_remote_frame(&self) -> bool {{
false
}}
fn id(&self) -> Id {{
Self::MESSAGE_ID
}}
fn dlc(&self) -> usize {{
self.raw.len()
}}
fn data(&self) -> &[u8] {{
&self.raw
}}
}}",
msg.type_name(),
)
})
}
}
fn render_debug_impl(w: &mut impl Write, msg: &Message) -> Result<()> {
let typ = msg.type_name();
writeln!(w, r"impl core::fmt::Debug for {typ} {{")?;
{
let mut w = PadAdapter::wrap(w);
writeln!(
w,
"fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {{",
)?;
{
let mut w = PadAdapter::wrap(&mut w);
writeln!(w, r"if f.alternate() {{")?;
{
let mut w = PadAdapter::wrap(&mut w);
writeln!(w, r#"f.debug_struct("{typ}")"#)?;
{
let mut w = PadAdapter::wrap(&mut w);
for signal in &msg.signals {
if signal.multiplexer_indicator == Plain {
writeln!(w, r#".field("{0}", &self.{0}())"#, signal.field_name())?;
}
}
}
writeln!(w, r".finish()")?;
}
writeln!(w, r"}} else {{")?;
{
let mut w = PadAdapter::wrap(&mut w);
writeln!(w, r#"f.debug_tuple("{typ}").field(&self.raw).finish()"#)?;
}
writeln!(w, "}}")?;
}
writeln!(w, "}}")?;
}
writeln!(w, "}}")?;
writeln!(w)?;
Ok(())
}
fn render_defmt_impl(w: &mut impl Write, msg: &Message) -> Result<()> {
let typ = msg.type_name();
writeln!(w, r"impl defmt::Format for {typ} {{")?;
{
let mut w = PadAdapter::wrap(w);
writeln!(w, "fn format(&self, f: defmt::Formatter) {{")?;
{
let mut w = PadAdapter::wrap(&mut w);
writeln!(w, r"defmt::write!(f,")?;
{
let mut w = PadAdapter::wrap(&mut w);
write!(w, r#""{typ} {{{{"#)?;
{
for signal in &msg.signals {
if signal.multiplexer_indicator == Plain {
write!(w, r" {}={{:?}}", signal.name)?;
}
}
}
writeln!(w, r#" }}}}","#)?;
for signal in &msg.signals {
if signal.multiplexer_indicator == Plain {
writeln!(w, "self.{}(),", signal.field_name())?;
}
}
writeln!(w, r");")?;
}
writeln!(w, "}}")?;
}
}
writeln!(w, "}}")?;
writeln!(w)?;
Ok(())
}
impl Config<'_> {
fn render_multiplexor_enums(
&self,
w: &mut impl Write,
dbc: &Dbc,
msg: &Message,
multiplexor_signal: &Signal,
) -> Result<()> {
ensure!(
multiplexor_signal.multiplexer_indicator == Multiplexor,
"signal {} is not the multiplexor",
multiplexor_signal.name,
);
let mut multiplexed_signals = BTreeMap::new();
for signal in &msg.signals {
if let MultiplexedSignal(switch_index) = signal.multiplexer_indicator {
multiplexed_signals
.entry(switch_index)
.and_modify(|v: &mut Vec<&Signal>| v.push(signal))
.or_insert_with(|| vec![&signal]);
}
}
writeln!(w, "/// Defined values for multiplexed signal {}", msg.name)?;
writeln!(w, "{ALLOW_LINTS}")?;
self.write_allow_dead_code(w)?;
self.impl_debug.fmt_attr(w, "derive(Debug)")?;
self.impl_defmt.fmt_attr(w, "derive(defmt::Format)")?;
self.impl_serde.fmt_attr(w, "derive(Serialize)")?;
self.impl_serde.fmt_attr(w, "derive(Deserialize)")?;
let enum_name = multiplex_enum_name(msg, multiplexor_signal)?;
writeln!(w, "pub enum {enum_name} {{")?;
{
let mut w = PadAdapter::wrap(w);
for switch_index in multiplexed_signals.keys() {
writeln!(
w,
"{multiplexed_wrapper_name}({multiplexed_name}),",
multiplexed_wrapper_name = multiplexed_enum_variant_wrapper_name(*switch_index),
multiplexed_name =
multiplexed_enum_variant_name(msg, multiplexor_signal, *switch_index)?,
)?;
}
}
writeln!(w, "}}")?;
writeln!(w)?;
for (switch_index, multiplexed_signals) in &multiplexed_signals {
let struct_name =
multiplexed_enum_variant_name(msg, multiplexor_signal, *switch_index)?;
writeln!(w, "{ALLOW_LINTS}")?;
self.write_allow_dead_code(w)?;
self.impl_debug.fmt_attr(w, "derive(Debug)")?;
self.impl_defmt.fmt_attr(w, "derive(defmt::Format)")?;
self.impl_serde.fmt_attr(w, "derive(Serialize)")?;
self.impl_serde.fmt_attr(w, "derive(Deserialize)")?;
writeln!(w, r"#[derive(Default)]")?;
writeln!(w, "pub struct {struct_name} {{ raw: [u8; {}] }}", msg.size)?;
writeln!(w)?;
writeln!(w, "{ALLOW_LINTS}")?;
self.write_allow_dead_code(w)?;
writeln!(w, "impl {struct_name} {{")?;
writeln!(
w,
"pub fn new() -> Self {{ Self {{ raw: [0u8; {}] }} }}",
msg.size
)?;
for signal in multiplexed_signals {
self.render_signal(w, signal, dbc, msg)?;
}
writeln!(w, "}}")?;
writeln!(w)?;
}
Ok(())
}
fn render_arbitrary(&self, w: &mut impl Write, dbc: &Dbc, msg: &Message) -> Result<()> {
writeln!(w, "{ALLOW_LINTS}")?;
self.write_allow_dead_code(w)?;
let typ = msg.type_name();
writeln!(w, "impl<'a> Arbitrary<'a> for {typ} {{")?;
{
let filtered_signals: Vec<&Signal> = msg
.signals
.iter()
.filter(|v| matches!(v.multiplexer_indicator, Plain | Multiplexor))
.collect();
let mut w = PadAdapter::wrap(w);
writeln!(
w,
"fn arbitrary({}u: &mut Unstructured<'a>) -> Result<Self, arbitrary::Error> {{",
if filtered_signals.is_empty() { "_" } else { "" },
)?;
{
let mut w = PadAdapter::wrap(&mut w);
for signal in &filtered_signals {
writeln!(
w,
"let {field_name} = {arbitrary_value};",
field_name = signal.field_name(),
arbitrary_value = signal_to_arbitrary(signal),
)?;
}
let args: Vec<String> = filtered_signals
.iter()
.map(|signal| {
let field = signal.field_name();
let is_enum_backed = signal_pub_type(dbc, msg, signal)
!= ValType::from_signal(signal).to_string();
if is_enum_backed {
format!("{}::_Other({field})", enum_name(msg, signal))
} else {
field
}
})
.collect();
writeln!(
w,
"{typ}::new({args}).map_err(|_| arbitrary::Error::IncorrectFormat)",
typ = msg.type_name(),
args = args.join(","),
)?;
}
writeln!(w, "}}")?;
}
writeln!(w, "}}")?;
Ok(())
}
fn render_error(&self, w: &mut impl Write) -> Result<()> {
w.write_all(
r#"
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CanError {
UnknownMessageId(embedded_can::Id),
/// Signal parameter is not within the range
/// defined in the dbc
ParameterOutOfRange {
/// dbc message id
message_id: embedded_can::Id,
},
InvalidPayloadSize,
/// Multiplexor value not defined in the dbc
InvalidMultiplexor {
/// dbc message id
message_id: embedded_can::Id,
/// Multiplexor value not defined in the dbc
multiplexor: u16,
},
}
impl core::fmt::Display for CanError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{self:?}")
}
}
"#
.as_bytes(),
)?;
self.impl_error.fmt_cfg(w, |w| {
writeln!(w, "impl core::error::Error for CanError {{}}")
})
}
fn render_arbitrary_helpers(&self, w: &mut impl Write) -> Result<()> {
self.impl_arbitrary.fmt_cfg(&mut *w, |w| {
self.write_allow_dead_code(w)?;
writeln!(w, "trait UnstructuredFloatExt {{")?;
writeln!(w, " fn float_in_range(&mut self, range: core::ops::RangeInclusive<f32>) -> arbitrary::Result<f32>;")?;
writeln!(w, "}}")?;
writeln!(w)?;
Ok::<_, Error>(())
})?;
self.impl_arbitrary.fmt_cfg(w, |w| {
writeln!(w, "impl UnstructuredFloatExt for arbitrary::Unstructured<'_> {{")?;
writeln!(w, " fn float_in_range(&mut self, range: core::ops::RangeInclusive<f32>) -> arbitrary::Result<f32> {{")?;
writeln!(w, " let min = range.start();")?;
writeln!(w, " let max = range.end();")?;
writeln!(w, " let steps = u32::MAX;")?;
writeln!(w, " let factor = (max - min) / (steps as f32);")?;
writeln!(w, " let random_int: u32 = self.int_in_range(0..=steps)?;")?;
writeln!(w, " let random = min + factor * (random_int as f32);")?;
writeln!(w, " Ok(random)")?;
writeln!(w, " }}")?;
writeln!(w, "}}")?;
writeln!(w)?;
Ok::<_, Error>(())
})
}
}
fn signal_to_arbitrary(signal: &Signal) -> String {
let typ = ValType::from_signal(signal);
match typ {
ValType::Bool => "u.int_in_range(0..=1)? == 1".to_string(),
ValType::F32 => {
let min = signal.min;
let max = signal.max;
format!("u.float_in_range({min}_f32..={max}_f32)?")
}
_ => {
let min = signal.min;
let max = signal.max;
format!("u.int_in_range({min}..={max})?")
}
}
}
fn get_relevant_messages(dbc: &Dbc) -> impl Iterator<Item = &Message> {
dbc.messages.iter().filter(|v| !message_ignored(v))
}
fn message_ignored(message: &Message) -> bool {
message.name == "VECTOR__INDEPENDENT_SIG_MSG"
}
fn resolve_field_source(source: &FieldSource<'_>, target: &AttrTarget<'_>) -> Option<String> {
let AttrTarget {
msg,
dbc,
signal,
node,
} = *target;
match source {
FieldSource::Attr(name) => match (signal, node) {
(Some(s), None) => dbc.resolved_signal_attribute(msg.id, &s.name, name),
(None, None) => dbc.resolved_message_attribute(msg.id, name),
(Some(s), Some(n)) => {
resolved_node_relation_attribute(dbc, n, msg.id, Some(&s.name), name)
}
(None, Some(n)) => resolved_node_relation_attribute(dbc, n, msg.id, None, name),
}
.map(attr_value_literal),
FieldSource::MessageAttr(name) => dbc
.resolved_message_attribute(msg.id, name)
.map(attr_value_literal),
FieldSource::StartBit => signal.map(|s| s.start_bit.to_string()),
FieldSource::StartByte => signal.map(|s| signal_start_byte(s).to_string()),
FieldSource::BitWidth => signal.map(|s| s.size.to_string()),
FieldSource::MessageSize => Some(msg.size.to_string()),
FieldSource::Int(v) => Some(v.to_string()),
FieldSource::Str(v) => Some(format!("{v:?}")),
FieldSource::NodeName => node.map(|n| format!("{n:?}")),
}
}
fn node_relation_attribute<'a>(
dbc: &'a Dbc,
node_name: &str,
message_id: MessageId,
signal_name: Option<&str>,
name: &str,
) -> Option<&'a AttributeValue> {
dbc.relation_attribute_values.iter().find_map(|rel| {
if rel.name != name {
return None;
}
match (&rel.details, signal_name) {
(
AttributeValueForRelationType::NodeToSignal {
node_name: n,
message_id: mid,
signal_name: sig,
value,
},
Some(want_sig),
) if n == node_name && *mid == message_id && sig == want_sig => Some(value),
(
AttributeValueForRelationType::NodeToMessage {
node_name: n,
message_id: mid,
value,
},
None,
) if n == node_name && *mid == message_id => Some(value),
_ => None,
}
})
}
fn resolved_node_relation_attribute<'a>(
dbc: &'a Dbc,
node_name: &str,
message_id: MessageId,
signal_name: Option<&str>,
name: &str,
) -> Option<&'a AttributeValue> {
node_relation_attribute(dbc, node_name, message_id, signal_name, name).or_else(|| {
dbc.relation_attribute_defaults
.iter()
.find(|d| d.name == name)
.map(|d| &d.value)
})
}
fn signal_start_byte(signal: &Signal) -> u64 {
signal.start_bit.checked_div(8).unwrap_or(0)
}
fn attr_value_literal(value: &AttributeValue) -> String {
match value {
AttributeValue::Uint(v) => v.to_string(),
AttributeValue::Int(v) => v.to_string(),
AttributeValue::Double(v) => format!("{v:?}"),
AttributeValue::String(v) => format!("{v:?}"),
}
}
fn message_cycle_time_ms(dbc: &Dbc, id: MessageId) -> Option<u32> {
match dbc.message_attribute(id, "GenMsgCycleTime")? {
AttributeValue::Uint(v) => u32::try_from(*v).ok(),
AttributeValue::Int(v) => u32::try_from(*v).ok(),
AttributeValue::Double(_) | AttributeValue::String(_) => None,
}
}
impl Config<'_> {
pub fn generate(self) -> Result<String> {
let mut out = Vec::new();
self.codegen(&mut out)
.context("could not generate Rust code")?;
let out = std::str::from_utf8(&out).context("Failed to convert output to str")?;
let file = syn::parse_file(out).context("Failed to parse generated Rust code")?;
Ok(prettyplease::unparse(&file))
}
pub fn write(self, mut out: impl Write) -> Result<()> {
out.write_all(self.generate()?.as_bytes())?;
Ok(())
}
pub fn write_to_file<P: AsRef<Path>>(self, path: P) -> Result<()> {
let file = OpenOptions::new()
.write(true)
.truncate(true)
.create(true)
.open(path.as_ref())?;
self.write(file)
}
fn write_allow_dead_code(&self, w: &mut impl Write) -> Result<()> {
if self.allow_dead_code {
writeln!(w, "{ALLOW_DEADCODE}")?;
}
Ok(())
}
}