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