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