dbc_data/
lib.rs

1//! A derive-macro which produces code to access signals within CAN
2//! messages, as described by a `.dbc` file.  The generated code has
3//! very few dependencies: just core primitives and `[u8]` slices, and
4//! is `#[no_std]` compatible.
5//!
6//! # Changelog
7//! [CHANGELOG.md]
8//!
9//! # Example
10//! Given a `.dbc` file containing:
11//!
12//! ```text
13//! BO_ 1023 SomeMessage: 4 Ecu1
14//!  SG_ Unsigned16 : 16|16@0+ (1,0) [0|0] "" Vector__XXX
15//!  SG_ Unsigned8 : 8|8@1+ (1,0) [0|0] "" Vector__XXX
16//!  SG_ Signed8 : 0|8@1- (1,0) [0|0] "" Vector__XXX
17//! ```
18//! The following code can be written to access the fields of the
19//! message:
20//!
21//! ```
22//! pub use dbc_data::*;
23//!
24//! #[derive(DbcData, Default)]
25//! #[dbc_file = "tests/example.dbc"]
26//! struct TestData {
27//!     some_message: SomeMessage,
28//! }
29//!
30//! fn test() {
31//!     let mut t = TestData::default();
32//!
33//!     assert_eq!(SomeMessage::ID, 1023);
34//!     assert_eq!(SomeMessage::DLC, 4);
35//!     assert!(t.some_message.decode(&[0x12, 0x34, 0x56, 0x78]));
36//!     assert_eq!(t.some_message.Signed8, 0x12);
37//!     assert_eq!(t.some_message.Unsigned8, 0x34);
38//!     assert_eq!(t.some_message.Unsigned16, 0x5678); // big-endian
39//! }
40//! ```
41//! See the test cases in this crate for examples of usage.
42//!
43//! # Code Generation
44//! This crate is aimed at embedded systems where typically some
45//! subset of the messages and signals defined in the `.dbc` file are
46//! of interest, and the rest can be ignored for a minimal footpint.
47//! If you need to decode the entire DBC into rich (possibly `std`-dependent)
48//! types to run on a host system, there are other crates for that
49//! such as `dbc_codegen`.
50//!
51//! ## Messages
52//! As `.dbc` files typically contain multiple messages, each of these
53//! can be brought into scope by referencing their name as a type
54//! (e.g. `SomeMessage` as shown above) and this determines what code
55//! is generated.  Messages not referenced will not generate any code.
56//!
57//! When a range of message IDs contain the same signals, such as a
58//! series of readings which do not fit into a single message, then
59//! declaring an array will allow that type to be used for all of them.
60//!
61//! # Signals
62//! For cases where only certain signals within a message are needed, the
63//! `#[dbc_signals]` attribute lets you specify which ones are used.
64//!
65//! ## Types
66//! Single-bit signals generate `bool` types, and signals with a scale factor
67//! generate `f32` types.  All other signals generate signed or unsigned
68//! native types which are large enough to fit the contained values, e.g.
69//! 13-bit signals will be stored in a `u16` and 17-bit signals will be
70//! stored in a `u32`.
71//!
72//! # Functionality
73//! * Decode signals from PDU into native types
74//!     * const definitions for `ID: u32`, `DLC: u8`, `EXTENDED: bool`,
75//!       and `CYCLE_TIME: usize` when present
76//! * Encode signal into PDU (except unaligned BE)
77//!
78//! # TODO
79//! * Encode unaligned BE signals
80//! * Generate dispatcher for decoding based on ID (including ranges)
81//! * Enforce that arrays of messages contain the same signals
82//! * Support multiplexed signals
83//! * (Maybe) scope generated types to a module
84//!
85//! # License
86//! [LICENSE-MIT]
87//!
88
89extern crate proc_macro;
90use can_dbc::{
91    AttributeValuedForObjectType, ByteOrder, MessageId, Signal, ValueType, DBC,
92};
93use proc_macro2::TokenStream;
94use quote::{quote, TokenStreamExt};
95use std::{collections::BTreeMap, fs::read};
96use syn::{
97    parse_macro_input, spanned::Spanned, Attribute, Data, DeriveInput, Expr,
98    Field, Fields, Ident, Lit, Meta, Result, Type,
99};
100
101struct DeriveData<'a> {
102    /// Name of the struct we are deriving for
103    #[allow(dead_code)]
104    name: &'a Ident,
105    /// The parsed DBC file
106    dbc: can_dbc::DBC,
107    /// All of the messages to derive
108    messages: BTreeMap<String, MessageInfo<'a>>,
109}
110
111struct MessageInfo<'a> {
112    id: u32,
113    extended: bool,
114    index: usize,
115    ident: &'a Ident,
116    attrs: &'a Vec<Attribute>,
117    cycle_time: Option<usize>,
118}
119
120/// Filter signals based on #[dbc_signals] list
121struct SignalFilter {
122    names: Vec<String>,
123}
124
125impl SignalFilter {
126    /// Create a signal filter from a message's attribute
127    fn new(message: &MessageInfo) -> Self {
128        let mut names: Vec<String> = vec![];
129        if let Some(attrs) = parse_attr(message.attrs, "dbc_signals") {
130            let list = attrs.split(",");
131            for name in list {
132                let name = name.trim();
133                names.push(name.to_string());
134            }
135        }
136        Self { names }
137    }
138
139    /// Return whether a signal should be used, i.e. whether it is
140    /// in the filter list or the list is empty
141    fn use_signal(&self, name: impl Into<String>) -> bool {
142        if self.names.is_empty() {
143            return true;
144        }
145        let name = name.into();
146        self.names.contains(&name)
147    }
148}
149
150/// Information about signal within message
151struct SignalInfo<'a> {
152    signal: &'a Signal,
153    ident: Ident,
154    ntype: Ident,
155    utype: Ident,
156    start: usize,
157    width: usize,
158    nwidth: usize,
159    scale: f32,
160    signed: bool,
161}
162
163impl<'a> SignalInfo<'a> {
164    fn new(signal: &'a Signal, message: &MessageInfo) -> Self {
165        // TODO: sanitize and/or change name format
166        let name = signal.name();
167        let signed = matches!(signal.value_type(), ValueType::Signed);
168        let width = *signal.signal_size() as usize;
169        let scale = *signal.factor() as f32;
170
171        // get storage width of signal data
172        let nwidth = match width {
173            1 => 1,
174            2..=8 => 8,
175            9..=16 => 16,
176            17..=32 => 32,
177            _ => 64,
178        };
179
180        let utype = if width == 1 {
181            "bool"
182        } else {
183            &format!("{}{}", if signed { "i" } else { "u" }, nwidth)
184        };
185
186        // get native type for signal
187        let ntype = if scale == 1.0 { utype } else { "f32" };
188
189        Self {
190            signal,
191            ident: Ident::new(name, message.ident.span()),
192            ntype: Ident::new(ntype, message.ident.span()),
193            utype: Ident::new(utype, message.ident.span()),
194            start: *signal.start_bit() as usize,
195            scale,
196            signed,
197            width,
198            nwidth,
199        }
200    }
201
202    /// Generate the code for extracting signal bits
203    fn extract_bits(&self) -> TokenStream {
204        let low = self.start / 8;
205        let left = self.start % 8;
206        let high = (self.start + self.width - 1) / 8;
207        let right = (self.start + self.width) % 8;
208        let utype = &self.utype;
209        let le = self.signal.byte_order() == &ByteOrder::LittleEndian;
210
211        let mut ts = TokenStream::new();
212        if self.width == self.nwidth && left == 0 {
213            // aligned
214            let ext = if le {
215                Ident::new("from_le_bytes", utype.span())
216            } else {
217                Ident::new("from_be_bytes", utype.span())
218            };
219            let tokens = match self.width {
220                8 => quote! {
221                    #utype::#ext([pdu[#low]])
222                },
223                16 => quote! {
224                    #utype::#ext([pdu[#low],
225                                  pdu[#low + 1]])
226                },
227                32 => quote! {
228                    #utype::#ext([pdu[#low + 0],
229                                  pdu[#low + 1],
230                                  pdu[#low + 2],
231                                  pdu[#low + 3]])
232                },
233                // NOTE: this compiles to very small code and does not
234                // involve actually fetching 8 separate bytes; e.g. on
235                // armv7 an `ldrd` to get both 32-bit values followed by
236                // two `rev` instructions to reverse the bytes.
237                64 => quote! {
238                    #utype::#ext([pdu[#low + 0],
239                                  pdu[#low + 1],
240                                  pdu[#low + 2],
241                                  pdu[#low + 3],
242                                  pdu[#low + 4],
243                                  pdu[#low + 5],
244                                  pdu[#low + 6],
245                                  pdu[#low + 7],
246                    ])
247                },
248                _ => unimplemented!(),
249            };
250            ts.append_all(tokens);
251        } else {
252            if le {
253                let count = high - low;
254                for o in 0..=count {
255                    let byte = low + o;
256                    if o == 0 {
257                        // first byte
258                        ts.append_all(quote! {
259                            let v = pdu[#byte] as #utype;
260                        });
261                        if left != 0 {
262                            if count == 0 {
263                                ts.append_all(quote! {
264                                    let v = (v >> #left) & ((1 << #left) - 1);
265                                });
266                            } else {
267                                ts.append_all(quote! {
268                                    let v = v >> #left;
269                                });
270                            }
271                        }
272                    } else {
273                        let shift = (o * 8) - left;
274                        if o == count && right != 0 {
275                            ts.append_all(quote! {
276                                let v = v | (((pdu[#byte]
277                                               & ((1 << #right) - 1))
278                                              as #utype) << #shift);
279                            });
280                        } else {
281                            ts.append_all(quote! {
282                                let v = v | ((pdu[#byte] as #utype) << #shift);
283                            });
284                        }
285                    }
286                }
287            } else {
288                // big-endian
289                let mut rem = self.width;
290                let mut byte = low;
291                while rem > 0 {
292                    if byte == low {
293                        // first byte
294                        ts.append_all(quote! {
295                            let v = pdu[#byte] as #utype;
296                        });
297                        if rem < 8 {
298                            // single byte
299                            let mask = rem - 1;
300                            let shift = left + 1 - rem;
301                            ts.append_all(quote! {
302                                let mask: #utype = (1 << #mask)
303                                    | ((1 << #mask) - 1);
304                                let v = (v >> #shift) & mask;
305                            });
306                            rem = 0;
307                        } else {
308                            // first of multiple bytes
309                            let mask = left;
310                            let shift = rem - left - 1;
311                            if mask < 7 {
312                                ts.append_all(quote! {
313                                    let mask: #utype = (1 << #mask)
314                                        | ((1 << #mask) - 1);
315                                    let v = (v & mask) << #shift;
316                                });
317                            } else {
318                                ts.append_all(quote! {
319                                    let v = v << #shift;
320                                });
321                            }
322                            rem -= left + 1;
323                        }
324                        byte += 1;
325                    } else {
326                        if rem < 8 {
327                            // last byte: take top bits
328                            let shift = 8 - rem;
329                            ts.append_all(quote! {
330                                let v = v |
331                                ((pdu[#byte] as #utype) >> #shift);
332                            });
333                            rem = 0;
334                        } else {
335                            rem -= 8;
336                            ts.append_all(quote! {
337                                let v = v |
338                                ((pdu[#byte] as #utype) << #rem);
339                            });
340                            byte += 1;
341                        }
342                    };
343                }
344            }
345            // perform sign-extension for values with fewer bits than
346            // the storage type
347            if self.signed && self.width < self.nwidth {
348                let mask = self.width - 1;
349                ts.append_all(quote! {
350                    let mask: #utype = (1 << #mask);
351                    let v = if (v & mask) != 0 {
352                        let mask = mask | (mask - 1);
353                        v | !mask
354                    } else {
355                        v
356                    };
357                });
358            }
359            ts.append_all(quote! { v });
360        }
361        quote! { { #ts } }
362    }
363
364    fn gen_decoder(&self) -> TokenStream {
365        let name = &self.ident;
366        if self.width == 1 {
367            // boolean
368            let byte = self.start / 8;
369            let bit = self.start % 8;
370            quote! {
371                self.#name = (pdu[#byte] & (1 << #bit)) != 0;
372            }
373        } else {
374            let value = self.extract_bits();
375            let ntype = &self.ntype;
376            if !self.is_float() {
377                quote! {
378                    self.#name = #value as #ntype;
379                }
380            } else {
381                let scale = self.scale;
382                let offset = *self.signal.offset() as f32;
383                quote! {
384                    self.#name = ((#value as f32) * #scale) + #offset;
385                }
386            }
387        }
388    }
389
390    fn gen_encoder(&self) -> TokenStream {
391        let name = &self.ident;
392        let low = self.start / 8;
393        let mut byte = low;
394        let bit = self.start % 8;
395        if self.width == 1 {
396            // boolean
397            quote! {
398                let mask: u8 = (1 << #bit);
399                if self.#name {
400                    pdu[#byte] |= mask;
401                } else {
402                    pdu[#byte] &= !mask;
403                }
404            }
405        } else {
406            let utype = &self.utype;
407            let left = self.start % 8;
408            // let right = (self.start + self.width) % 8;
409            let le = self.signal.byte_order() == &ByteOrder::LittleEndian;
410
411            let mut ts = TokenStream::new();
412            if self.is_float() {
413                let scale = self.scale;
414                let offset = self.signal.offset as f32;
415                ts.append_all(quote! {
416                    let v = ((self.#name - #offset) / #scale) as #utype;
417                });
418            } else {
419                ts.append_all(quote! {
420                    let v = self.#name;
421                });
422            }
423            if le {
424                if self.width == self.nwidth && left == 0 {
425                    // aligned little-endian
426                    let mut bits = self.nwidth;
427                    let mut shift = 0;
428                    while bits >= 8 {
429                        ts.append_all(quote! {
430                            pdu[#byte] = ((v >> #shift) as u8) & 0xff;
431                        });
432                        bits -= 8;
433                        byte += 1;
434                        shift += 8;
435                    }
436                } else {
437                    // unaligned little-endian
438                    let mut rem = self.width;
439                    let mut lshift = left;
440                    let mut rshift = 0;
441                    while rem > 0 {
442                        if rem < 8 {
443                            let mask: u8 = (1 << rem) - 1;
444                            let mask = mask << lshift;
445                            ts.append_all(quote! {
446                                pdu[#byte] = (pdu[#byte] & !#mask) |
447                                ((((v >> #rshift) << (#lshift)) as u8) & #mask);
448                            });
449                            break;
450                        }
451
452                        if lshift != 0 {
453                            let mask: u8 = (1 << (8 - left)) - 1;
454                            let mask = mask << lshift;
455                            ts.append_all(quote! {
456                                pdu[#byte] = (pdu[#byte] & !#mask) |
457                                ((((v >> #rshift) << (#lshift)) as u8) & #mask);
458                            });
459                        } else {
460                            ts.append_all(quote! {
461                                pdu[#byte] = ((v >> #rshift) & 0xff) as u8;
462                            });
463                        }
464
465                        if byte == low {
466                            rem -= 8 - left;
467                            rshift += 8 - left;
468                        } else {
469                            rem -= 8;
470                            rshift += 8;
471                        }
472                        byte += 1;
473                        lshift = 0;
474                    }
475                }
476            } else {
477                if self.width == self.nwidth && left == 7 {
478                    // aligned big-endian
479                    let mut bits = self.nwidth;
480                    let mut shift = bits - 8;
481                    let mut byte = (self.start - 7) / 8;
482                    while bits >= 8 {
483                        ts.append_all(quote! {
484                            pdu[#byte] = ((v >> #shift) as u8) & 0xff;
485                        });
486                        bits -= 8;
487                        byte += 1;
488                        if shift >= 8 {
489                            shift -= 8;
490                        }
491                    }
492                } else {
493                    // unaligned big-endian
494                    //                    todo!();
495                }
496            }
497            ts
498        }
499    }
500
501    fn is_float(&self) -> bool {
502        self.scale != 1.0
503    }
504}
505
506impl<'a> MessageInfo<'a> {
507    fn new(dbc: &DBC, field: &'a Field) -> Option<Self> {
508        let stype = match &field.ty {
509            Type::Path(v) => v,
510            Type::Array(a) => match *a.elem {
511                // TODO: validate that all signals match in ID range
512                Type::Path(ref v) => v,
513                _ => unimplemented!(),
514            },
515            _ => unimplemented!(),
516        };
517        let ident = &stype.path.segments[0].ident;
518        let name = ident.to_string();
519
520        for (index, message) in dbc.messages().iter().enumerate() {
521            if message.message_name() == &name {
522                let id = message.message_id();
523                let (id32, extended) = match *id {
524                    MessageId::Standard(id) => (id as u32, false),
525                    MessageId::Extended(id) => (id, true),
526                };
527                let mut cycle_time: Option<usize> = None;
528                for attr in dbc.attribute_values().iter() {
529                    let value = attr.attribute_value();
530                    use AttributeValuedForObjectType as AV;
531                    match value {
532                        AV::MessageDefinitionAttributeValue(aid, Some(av)) => {
533                            if aid == id
534                                && attr.attribute_name() == "GenMsgCycleTime"
535                            {
536                                cycle_time = Some(Self::attr_value(av));
537                            }
538                        }
539                        _ => {}
540                    }
541                }
542
543                return Some(Self {
544                    id: id32,
545                    extended,
546                    index,
547                    ident,
548                    cycle_time,
549                    attrs: &field.attrs,
550                });
551            }
552        }
553        None
554    }
555
556    // TODO: revisit this to handle type conversion better; we
557    // expect that the value fits in a usize for e.g. GenMsgCycleTime
558    fn attr_value(v: &can_dbc::AttributeValue) -> usize {
559        use can_dbc::AttributeValue as AV;
560        match v {
561            AV::AttributeValueU64(x) => *x as usize,
562            AV::AttributeValueI64(x) => *x as usize,
563            AV::AttributeValueF64(x) => *x as usize,
564            AV::AttributeValueCharString(_) => 0usize, // TODO: parse as int?
565        }
566    }
567}
568
569impl<'a> DeriveData<'a> {
570    fn from(input: &'a DeriveInput) -> Result<Self> {
571        // load the DBC file
572        let dbc_file = parse_attr(&input.attrs, "dbc_file")
573            .expect("No DBC file specified");
574        let contents = read(&dbc_file).expect("Could not read DBC");
575        let dbc = DBC::from_slice(&contents).expect("Could not parse DBC");
576
577        // gather all of the messages and associated attributes
578        let mut messages: BTreeMap<String, MessageInfo<'_>> =
579            Default::default();
580        match &input.data {
581            Data::Struct(data) => match &data.fields {
582                Fields::Named(fields) => {
583                    for field in &fields.named {
584                        if let Some(info) = MessageInfo::new(&dbc, field) {
585                            messages.insert(info.ident.to_string(), info);
586                        } else {
587                            return Err(syn::Error::new(
588                                field.span(),
589                                "Unknown message",
590                            ));
591                        }
592                    }
593                }
594                Fields::Unnamed(_) | Fields::Unit => unimplemented!(),
595            },
596            _ => unimplemented!(),
597        }
598
599        Ok(Self {
600            name: &input.ident,
601            dbc,
602            messages,
603        })
604    }
605
606    fn build(self) -> TokenStream {
607        let mut out = TokenStream::new();
608
609        for (name, message) in self.messages.iter() {
610            let m = self
611                .dbc
612                .messages()
613                .get(message.index)
614                .unwrap_or_else(|| panic!("Unknown message {name}"));
615
616            let filter = SignalFilter::new(message);
617
618            let mut signals: Vec<Ident> = vec![];
619            let mut types: Vec<Ident> = vec![];
620            let mut infos: Vec<SignalInfo> = vec![];
621            for s in m.signals().iter() {
622                if !filter.use_signal(s.name()) {
623                    continue;
624                }
625
626                let signal = SignalInfo::new(s, message);
627                signals.push(signal.ident.clone());
628                types.push(signal.ntype.clone());
629                infos.push(signal);
630            }
631
632            let id = message.id;
633            let extended = message.extended;
634
635            let dlc = *m.message_size() as usize;
636            let dlc8 = dlc as u8;
637            let ident = message.ident;
638
639            // build signal decoders and encoders
640            let mut decoders = TokenStream::new();
641            let mut encoders = TokenStream::new();
642            for info in infos.iter() {
643                decoders.append_all(info.gen_decoder());
644                encoders.append_all(info.gen_encoder());
645            }
646            let cycle_time = if let Some(c) = message.cycle_time {
647                quote! {
648                    const CYCLE_TIME: usize = #c;
649                }
650            } else {
651                quote! {}
652            };
653
654            out.append_all(quote! {
655                #[allow(dead_code)]
656                #[allow(non_snake_case)]
657                #[derive(Default)]
658                pub struct #ident {
659                    #(
660                        pub #signals: #types
661                    ),*
662                }
663
664                impl #ident {
665                    const ID: u32 = #id;
666                    const DLC: u8 = #dlc8;
667                    const EXTENDED: bool = #extended;
668                    #cycle_time
669
670                    pub fn decode(&mut self, pdu: &[u8])
671                                  -> bool {
672                        if pdu.len() != #dlc {
673                            return false
674                        }
675                        #decoders
676                        true
677                    }
678
679                    pub fn encode(&mut self, pdu: &mut [u8])
680                                  -> bool {
681                        if pdu.len() != #dlc {
682                            return false
683                        }
684                        #encoders
685                        true
686                    }
687                }
688            });
689        }
690        out
691    }
692}
693
694#[proc_macro_derive(DbcData, attributes(dbc_file, dbc_signals))]
695pub fn dbc_data_derive(
696    input: proc_macro::TokenStream,
697) -> proc_macro::TokenStream {
698    derive_data(&parse_macro_input!(input as DeriveInput))
699        .unwrap_or_else(|err| err.to_compile_error())
700        .into()
701}
702
703fn derive_data(input: &DeriveInput) -> Result<TokenStream> {
704    Ok(DeriveData::from(input)?.build())
705}
706
707fn parse_attr(attrs: &[Attribute], name: &str) -> Option<String> {
708    let attr = attrs
709        .iter()
710        .filter(|a| {
711            a.path().segments.len() == 1 && a.path().segments[0].ident == name
712        })
713        .nth(0)?;
714
715    let expr = match &attr.meta {
716        Meta::NameValue(n) => Some(&n.value),
717        _ => None,
718    };
719
720    match &expr {
721        Some(Expr::Lit(e)) => match &e.lit {
722            Lit::Str(s) => Some(s.value()),
723            _ => None,
724        },
725        _ => None,
726    }
727}