dbc-data 0.1.12

Derive macro for CAN DBC code generation
Documentation
//! DBC Message information

use can_dbc::{AttributeValue, Dbc, MessageId};
use syn::{Attribute, Field, Ident, Type, Variant};

use crate::parse_attr;

pub struct MessageInfo<'a> {
    pub id: u32,
    pub extended: bool,
    pub index: usize,
    pub ident: &'a Ident,
    pub cycle_time: Option<usize>,
    signal_filter: Vec<String>,
}

impl<'a> MessageInfo<'a> {
    pub fn from_enum_variant(dbc: &Dbc, variant: &'a Variant) -> Option<Self> {
        Self::new(dbc, &variant.ident, &variant.attrs)
    }

    pub fn from_struct_field(dbc: &Dbc, field: &'a Field) -> Option<Self> {
        let stype = match &field.ty {
            Type::Path(v) => v,
            Type::Array(a) => match *a.elem {
                Type::Path(ref v) => v,
                _ => unimplemented!(),
            },
            _ => unimplemented!(),
        };
        Self::new(dbc, &stype.path.segments[0].ident, &field.attrs)
    }

    fn new(dbc: &Dbc, ident: &'a Ident, attrs: &[Attribute]) -> Option<Self> {
        let name = ident.to_string();

        for (index, message) in dbc.messages.iter().enumerate() {
            if message.name == name
                || Self::message_attr_string(
                    dbc,
                    message.id,
                    "SystemMessageLongSymbol",
                ) == Some(name.clone())
            {
                let id = message.id;
                let (id32, extended) = match id {
                    MessageId::Standard(id) => (u32::from(id), false),
                    MessageId::Extended(id) => (id, true),
                };

                let cycle_time =
                    Self::message_attr_value(dbc, id, "GenMsgCycleTime");

                // if a list of signals to include is provided, add that to the
                // filter
                let mut signal_filter: Vec<String> = vec![];
                if let Some(dbc_signals) = parse_attr(attrs, "dbc_signals") {
                    let list = dbc_signals.split(',');
                    for name in list {
                        signal_filter.push(name.trim().to_string());
                    }
                }

                if let Some(dbc_long_signals) =
                    parse_attr(attrs, "dbc_long_signals")
                {
                    eprintln!(
                        "dbc_long_signals {} for {}",
                        dbc_long_signals, name
                    );
                }

                return Some(Self {
                    id: id32,
                    extended,
                    index,
                    ident,
                    cycle_time,
                    signal_filter,
                });
            }
        }
        None
    }

    pub fn use_signal(&self, name: impl Into<String>) -> bool {
        if self.signal_filter.is_empty() {
            return true;
        }
        let name = name.into();
        self.signal_filter.contains(&name)
    }

    fn message_attr_string(
        dbc: &Dbc,
        id: MessageId,
        name: &str,
    ) -> Option<String> {
        if let Some(attr) = dbc
            .attribute_values_message
            .iter()
            .find(|a| a.message_id == id && a.name == name)
        {
            return match &attr.value {
                AttributeValue::String(s) => Some(s.clone()),
                _ => None,
            };
        }
        None
    }

    fn message_attr_value(
        dbc: &Dbc,
        id: MessageId,
        name: &str,
    ) -> Option<usize> {
        if let Some(attr) = dbc
            .attribute_values_message
            .iter()
            .find(|a| a.message_id == id && a.name == name)
        {
            return match attr.value {
                AttributeValue::Uint(v) => Some(v as usize),
                AttributeValue::Int(v) => Some(v as usize),
                _ => None,
            };
        }
        None
    }
}