Skip to main content

dbc_rs/message/
impls.rs

1use super::{Message, Signals};
2use crate::compat::{Comment, Name};
3
4impl Message {
5    /// Message from validated parts.
6    pub(crate) fn new(
7        id: u32,
8        name: Name,
9        dlc: u8,
10        sender: Name,
11        signals: Signals,
12        comment: Option<Comment>,
13    ) -> Self {
14        // Validation should have been done prior (by builder or parse)
15        Self {
16            id,
17            name,
18            dlc,
19            sender,
20            signals,
21            comment,
22        }
23    }
24
25    /// Returns the CAN message ID.
26    ///
27    /// This returns the raw CAN ID as it would appear on the bus (11-bit or 29-bit).
28    /// For extended (29-bit) IDs, the internal flag bit is stripped.
29    /// Use [`is_extended()`](Self::is_extended) to check if this is an extended ID.
30    ///
31    /// # Examples
32    ///
33    /// ```rust,no_run
34    /// use dbc_rs::Dbc;
35    ///
36    /// let dbc = Dbc::parse(r#"VERSION "1.0"\n\nBU_: ECM\n\nBO_ 256 EngineData : 8 ECM"#)?;
37    /// let message = dbc.messages().at(0).unwrap();
38    /// assert_eq!(message.id(), 256);
39    /// # Ok::<(), dbc_rs::Error>(())
40    /// ```
41    #[inline]
42    #[must_use = "return value should be used"]
43    pub fn id(&self) -> u32 {
44        self.id & Self::MAX_EXTENDED_ID
45    }
46
47    /// Returns the raw internal ID including any extended ID flag.
48    ///
49    /// This is primarily for internal use. Most users should use [`id()`](Self::id) instead.
50    #[inline]
51    #[must_use = "return value should be used"]
52    pub(crate) fn id_with_flag(&self) -> u32 {
53        self.id
54    }
55
56    /// Returns `true` if this message uses an extended (29-bit) CAN ID.
57    ///
58    /// Standard CAN uses 11-bit identifiers (0-2047), while extended CAN uses 29-bit
59    /// identifiers (0-536870911).
60    ///
61    /// # Examples
62    ///
63    /// ```rust,no_run
64    /// use dbc_rs::Dbc;
65    ///
66    /// // Standard 11-bit ID
67    /// let dbc = Dbc::parse(r#"VERSION "1.0"\n\nBU_: ECM\n\nBO_ 256 EngineData : 8 ECM"#)?;
68    /// let message = dbc.messages().at(0).unwrap();
69    /// assert!(!message.is_extended());
70    ///
71    /// // Extended 29-bit ID (with flag bit set: 0x80000000 | 0x18DAF115)
72    /// let dbc = Dbc::parse(r#"VERSION "1.0"\n\nBU_: ECM\n\nBO_ 2564485397 OBD2 : 8 ECM"#)?;
73    /// let message = dbc.messages().at(0).unwrap();
74    /// assert!(message.is_extended());
75    /// assert_eq!(message.id(), 0x18DAF115);
76    /// # Ok::<(), dbc_rs::Error>(())
77    /// ```
78    #[inline]
79    #[must_use = "return value should be used"]
80    pub fn is_extended(&self) -> bool {
81        (self.id & Self::EXTENDED_ID_FLAG) != 0
82    }
83
84    /// Returns the message name.
85    ///
86    /// # Examples
87    ///
88    /// ```rust,no_run
89    /// use dbc_rs::Dbc;
90    ///
91    /// let dbc = Dbc::parse(r#"VERSION "1.0"\n\nBU_: ECM\n\nBO_ 256 EngineData : 8 ECM"#)?;
92    /// let message = dbc.messages().at(0).unwrap();
93    /// assert_eq!(message.name(), "EngineData");
94    /// # Ok::<(), dbc_rs::Error>(())
95    /// ```
96    #[inline]
97    #[must_use = "return value should be used"]
98    pub fn name(&self) -> &str {
99        self.name.as_str()
100    }
101
102    /// Returns the Data Length Code (DLC) in bytes.
103    ///
104    /// DLC specifies the size of the message payload. For classic CAN, this is 1-8 bytes.
105    /// For CAN FD, this can be up to 64 bytes.
106    ///
107    /// # Examples
108    ///
109    /// ```rust,no_run
110    /// use dbc_rs::Dbc;
111    ///
112    /// let dbc = Dbc::parse(r#"VERSION "1.0"\n\nBU_: ECM\n\nBO_ 256 EngineData : 8 ECM"#)?;
113    /// let message = dbc.messages().at(0).unwrap();
114    /// assert_eq!(message.dlc(), 8);
115    /// # Ok::<(), dbc_rs::Error>(())
116    /// ```
117    #[inline]
118    #[must_use = "return value should be used"]
119    pub fn dlc(&self) -> u8 {
120        self.dlc
121    }
122
123    /// Get the sender node name for this message.
124    ///
125    /// The sender is the node that transmits this message on the CAN bus.
126    ///
127    /// # Examples
128    ///
129    /// ```rust,no_run
130    /// use dbc_rs::Dbc;
131    ///
132    /// let dbc = Dbc::parse(r#"VERSION "1.0"
133    ///
134    /// BU_: ECM TCM
135    ///
136    /// BO_ 256 Engine : 8 ECM
137    ///  SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
138    /// "#)?;
139    ///
140    /// let message = dbc.messages().iter().next().unwrap();
141    /// assert_eq!(message.sender(), "ECM");
142    /// # Ok::<(), dbc_rs::Error>(())
143    /// ```
144    #[inline]
145    #[must_use = "return value should be used"]
146    pub fn sender(&self) -> &str {
147        self.sender.as_str()
148    }
149
150    /// Returns a reference to the signals collection for this message.
151    ///
152    /// The [`Signals`] collection provides methods to iterate, search, and access signals by index.
153    ///
154    /// # Examples
155    ///
156    /// ```rust,no_run
157    /// use dbc_rs::Dbc;
158    ///
159    /// let dbc = Dbc::parse(r#"VERSION "1.0"
160    ///
161    /// BU_: ECM
162    ///
163    /// BO_ 256 Engine : 8 ECM
164    ///  SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" ECM
165    ///  SG_ Torque : 16|16@1+ (0.1,0) [0|500] "Nm" ECM
166    /// "#)?;
167    ///
168    /// let message = dbc.messages().find("Engine").unwrap();
169    /// let signals = message.signals();
170    /// assert_eq!(signals.len(), 2);
171    /// assert!(signals.find("RPM").is_some());
172    /// # Ok::<(), dbc_rs::Error>(())
173    /// ```
174    #[inline]
175    #[must_use = "return value should be used"]
176    pub fn signals(&self) -> &Signals {
177        &self.signals
178    }
179
180    /// Returns the minimum number of bytes required to decode all signals in this message.
181    ///
182    /// This calculates the actual byte coverage of all signals, which may be less than
183    /// the declared DLC. Use this when validating frame payloads for decoding - the
184    /// payload must have at least this many bytes to decode all signals successfully.
185    ///
186    /// # Examples
187    ///
188    /// ```rust,no_run
189    /// use dbc_rs::Dbc;
190    ///
191    /// let dbc = Dbc::parse(r#"VERSION "1.0"
192    ///
193    /// BU_: ECM
194    ///
195    /// BO_ 256 Engine : 8 ECM
196    ///  SG_ Temp : 0|8@1+ (1,0) [0|255] "" ECM
197    ///  SG_ Pressure : 8|8@1+ (1,0) [0|255] "" ECM
198    /// "#)?;
199    ///
200    /// let message = dbc.messages().find("Engine").unwrap();
201    /// assert_eq!(message.dlc(), 8);              // Declared DLC is 8
202    /// assert_eq!(message.min_bytes_required(), 2); // But signals only need 2 bytes
203    /// # Ok::<(), dbc_rs::Error>(())
204    /// ```
205    #[must_use = "return value should be used"]
206    pub fn min_bytes_required(&self) -> u8 {
207        if self.signals.is_empty() {
208            return 0;
209        }
210
211        let mut max_bit: u16 = 0;
212        for signal in self.signals.iter() {
213            let (_lsb, msb) =
214                Self::bit_range(signal.start_bit(), signal.length(), signal.byte_order());
215            if msb > max_bit {
216                max_bit = msb;
217            }
218        }
219
220        // Convert max bit position to bytes: (max_bit / 8) + 1
221        ((max_bit / 8) + 1) as u8
222    }
223
224    /// Returns the message comment from CM_ BO_ entry, if present.
225    #[inline]
226    #[must_use = "return value should be used"]
227    pub fn comment(&self) -> Option<&str> {
228        self.comment.as_ref().map(|c| c.as_ref())
229    }
230
231    /// Sets the message comment (from CM_ BO_ entry).
232    /// Used internally during parsing when CM_ entries are processed after messages.
233    #[inline]
234    pub(crate) fn set_comment(&mut self, comment: Comment) {
235        self.comment = Some(comment);
236    }
237
238    /// Returns a mutable reference to the signals collection.
239    /// Used internally during parsing when CM_ entries are processed after signals.
240    #[inline]
241    pub(crate) fn signals_mut(&mut self) -> &mut Signals {
242        &mut self.signals
243    }
244}
245
246#[cfg(test)]
247mod tests {
248    use super::*;
249    use crate::{Parser, Signal};
250
251    #[test]
252    fn test_message_getters_edge_cases() {
253        // Test with minimum values
254        let data = b"BO_ 0 A : 1 B";
255        let mut parser = Parser::new(data).unwrap();
256        let signals: &[Signal] = &[];
257        let message = Message::parse(&mut parser, signals).unwrap();
258
259        assert_eq!(message.id(), 0);
260        assert_eq!(message.name(), "A");
261        assert_eq!(message.dlc(), 1);
262        assert_eq!(message.sender(), "B");
263    }
264}