dbc_rs/dbc/
decode.rs

1use crate::{Dbc, Error, MAX_SIGNALS_PER_MESSAGE, Message, Result, compat::Vec};
2#[cfg(feature = "embedded-can")]
3use embedded_can::{Frame, Id};
4
5/// A decoded signal from a CAN message.
6///
7/// Contains the signal name, its decoded physical value, unit, and optional value description.
8#[derive(Debug, Clone, PartialEq)]
9pub struct DecodedSignal<'a> {
10    /// The name of the signal as defined in the DBC file.
11    pub name: &'a str,
12    /// The decoded physical value after applying factor and offset.
13    pub value: f64,
14    /// The unit of the signal (e.g., "rpm", "°C"), if defined.
15    pub unit: Option<&'a str>,
16    /// The value description text if defined in the DBC file (e.g., "Park", "Drive").
17    /// This maps the raw signal value to a human-readable description.
18    pub description: Option<&'a str>,
19}
20
21impl<'a> DecodedSignal<'a> {
22    /// Creates a new `DecodedSignal` with the given name, value, unit, and description.
23    ///
24    /// # Arguments
25    ///
26    /// * `name` - The signal name
27    /// * `value` - The decoded physical value (after applying factor and offset)
28    /// * `unit` - The optional unit of measurement (e.g., "rpm", "km/h")
29    /// * `description` - The optional value description text (e.g., "Park", "Drive")
30    ///
31    /// # Examples
32    ///
33    /// ```rust,no_run
34    /// use dbc_rs::DecodedSignal;
35    ///
36    /// let signal = DecodedSignal::new("Gear", 3.0, Some(""), Some("Drive"));
37    /// assert_eq!(signal.name, "Gear");
38    /// assert_eq!(signal.value, 3.0);
39    /// assert_eq!(signal.description, Some("Drive"));
40    /// ```
41    #[inline]
42    pub fn new(
43        name: &'a str,
44        value: f64,
45        unit: Option<&'a str>,
46        description: Option<&'a str>,
47    ) -> Self {
48        Self {
49            name,
50            value,
51            unit,
52            description,
53        }
54    }
55}
56
57/// Maximum number of multiplexer switches in a single message.
58/// Most CAN messages have 0-2 switches; 8 is generous.
59const MAX_SWITCHES: usize = 8;
60
61/// Pre-allocated buffer for switch values during decode.
62/// Uses fixed-size arrays to avoid heap allocation.
63struct SwitchValues<'a> {
64    /// Switch names (references to signal names, valid for decode lifetime)
65    names: [Option<&'a str>; MAX_SWITCHES],
66    /// Switch values corresponding to each name
67    values: [u64; MAX_SWITCHES],
68    /// Number of switches stored.
69    count: usize,
70}
71
72impl<'a> SwitchValues<'a> {
73    #[inline]
74    const fn new() -> Self {
75        Self {
76            names: [None; MAX_SWITCHES],
77            values: [0; MAX_SWITCHES],
78            count: 0,
79        }
80    }
81
82    /// Store a switch value. Returns Err if capacity exceeded.
83    #[inline]
84    fn push(&mut self, name: &'a str, value: u64) -> Result<()> {
85        if self.count >= MAX_SWITCHES {
86            return Err(Error::Decoding(Error::MESSAGE_TOO_MANY_SIGNALS));
87        }
88        let idx = self.count;
89        self.names[idx] = Some(name);
90        self.values[idx] = value;
91        self.count += 1;
92        Ok(())
93    }
94
95    /// Find switch value by name. O(n) where n is number of switches (typically 1-2).
96    #[inline]
97    fn get_by_name(&self, name: &str) -> Option<u64> {
98        for i in 0..self.count {
99            if self.names[i] == Some(name) {
100                return Some(self.values[i]);
101            }
102        }
103        None
104    }
105
106    /// Check if any switch has the given value.
107    #[inline]
108    fn any_has_value(&self, target: u64) -> bool {
109        for i in 0..self.count {
110            if self.values[i] == target && self.names[i].is_some() {
111                return true;
112            }
113        }
114        false
115    }
116}
117
118/// Decoding functionality for DBC structures
119impl Dbc {
120    /// Decode a CAN message payload using the message ID to find the corresponding message definition.
121    ///
122    /// This is a high-performance method for decoding CAN messages in `no_std` environments.
123    /// It finds the message by ID, then decodes all signals in the message from the payload bytes.
124    ///
125    /// # Arguments
126    ///
127    /// * `id` - The raw CAN message ID (without extended flag)
128    /// * `payload` - The CAN message payload bytes (up to 64 bytes for CAN FD)
129    /// * `is_extended` - Whether this is an extended (29-bit) CAN ID
130    ///
131    /// # Returns
132    ///
133    /// * `Ok(Vec<DecodedSignal>)` - A vector of decoded signals with name, value, and unit
134    /// * `Err(Error)` - If the message ID is not found, payload length doesn't match DLC, or signal decoding fails
135    ///
136    /// # Examples
137    ///
138    /// ```rust,no_run
139    /// use dbc_rs::Dbc;
140    ///
141    /// let dbc = Dbc::parse(r#"VERSION "1.0"
142    ///
143    /// BU_: ECM
144    ///
145    /// BO_ 256 Engine : 8 ECM
146    ///  SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
147    /// "#)?;
148    ///
149    /// // Decode a CAN message with RPM value of 2000 (raw: 8000 = 0x1F40)
150    /// let payload = [0x40, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
151    /// let decoded = dbc.decode(256, &payload, false)?; // false = standard CAN ID
152    /// assert_eq!(decoded.len(), 1);
153    /// assert_eq!(decoded[0].name, "RPM");
154    /// assert_eq!(decoded[0].value, 2000.0);
155    /// assert_eq!(decoded[0].unit, Some("rpm"));
156    /// # Ok::<(), dbc_rs::Error>(())
157    /// ```
158    /// High-performance CAN message decoding optimized for throughput.
159    ///
160    /// Performance optimizations:
161    /// - O(1) or O(log n) message lookup via feature-flagged index
162    /// - Single-pass signal iteration with inline switch processing
163    /// - Stack-allocated switch value storage (no heap allocation)
164    /// - Message-level extended multiplexing check to skip per-signal lookups
165    /// - Inlined hot paths with early returns
166    #[inline]
167    pub fn decode(
168        &self,
169        id: u32,
170        payload: &[u8],
171        is_extended: bool,
172    ) -> Result<Vec<DecodedSignal<'_>, { MAX_SIGNALS_PER_MESSAGE }>> {
173        // If it's an extended ID, add the extended ID flag
174        let id = if is_extended {
175            id | Message::EXTENDED_ID_FLAG
176        } else {
177            id
178        };
179
180        // Find message by ID (performance-critical lookup)
181        let message = self
182            .messages()
183            .find_by_id(id)
184            .ok_or(Error::Decoding(Error::MESSAGE_NOT_FOUND))?;
185
186        // Validate payload length matches message DLC (early return before any decoding)
187        let dlc = message.dlc() as usize;
188        if payload.len() < dlc {
189            return Err(Error::Decoding(Error::PAYLOAD_LENGTH_MISMATCH));
190        }
191
192        // Pre-allocate result vector
193        let mut decoded_signals: Vec<DecodedSignal<'_>, { MAX_SIGNALS_PER_MESSAGE }> = Vec::new();
194
195        // Stack-allocated switch values (no heap allocation)
196        let mut switch_values = SwitchValues::<'_>::new();
197
198        let signals = message.signals();
199
200        // Check once if this message has ANY extended multiplexing
201        // Fast path: skip if DBC has no extended multiplexing at all (O(1) check)
202        // Slow path: check if this specific message has entries (O(m) where m = unique message IDs)
203        let message_has_extended_mux = !self.extended_multiplexing.is_empty()
204            && self.has_extended_multiplexing_for_message(id);
205
206        // Check once if DBC has ANY value descriptions - skip all lookups if empty (common case)
207        // This avoids O(n) linear scans per signal when no value descriptions exist
208        let has_any_value_descriptions = !self.value_descriptions.is_empty();
209
210        // PASS 1: Decode multiplexer switches first (needed before multiplexed signals)
211        // This is necessary because multiplexed signals depend on switch values
212        for signal in signals.iter() {
213            if signal.is_multiplexer_switch() {
214                // decode_raw() returns (raw_value, physical_value) in one pass
215                let (raw_value, physical_value) = signal.decode_raw(payload)?;
216
217                // Multiplexer switch values must be non-negative
218                if raw_value < 0 {
219                    return Err(Error::Decoding(Error::MULTIPLEXER_SWITCH_NEGATIVE));
220                }
221
222                // Store switch value for later multiplexing checks
223                switch_values.push(signal.name(), raw_value as u64)?;
224
225                // Lookup value description only if any exist (skip O(n) scan otherwise)
226                let description = if has_any_value_descriptions {
227                    self.value_descriptions_for_signal(id, signal.name())
228                        .and_then(|vd| vd.get(raw_value as u64))
229                } else {
230                    None
231                };
232
233                // Add to decoded signals
234                decoded_signals
235                    .push(DecodedSignal::new(
236                        signal.name(),
237                        physical_value,
238                        signal.unit(),
239                        description,
240                    ))
241                    .map_err(|_| Error::Decoding(Error::MESSAGE_TOO_MANY_SIGNALS))?;
242            }
243        }
244
245        // PASS 2: Decode non-switch signals based on multiplexing rules
246        for signal in signals.iter() {
247            // Skip multiplexer switches (already decoded in pass 1)
248            if signal.is_multiplexer_switch() {
249                continue;
250            }
251
252            // Determine if this signal should be decoded based on multiplexing
253            let should_decode = if let Some(mux_value) = signal.multiplexer_switch_value() {
254                // This is a multiplexed signal (m0, m1, etc.)
255                if message_has_extended_mux {
256                    // Check extended multiplexing only if message has any
257                    self.check_extended_multiplexing(id, signal.name(), &switch_values)
258                        .unwrap_or_else(|| {
259                            // No extended entries for this signal - use basic multiplexing
260                            switch_values.any_has_value(mux_value)
261                        })
262                } else {
263                    // No extended multiplexing for this message - use basic check
264                    switch_values.any_has_value(mux_value)
265                }
266            } else {
267                // Normal signal (not multiplexed) - always decode
268                true
269            };
270
271            if should_decode {
272                // decode_raw() returns (raw_value, physical_value) in one pass
273                let (raw_value, physical_value) = signal.decode_raw(payload)?;
274
275                // Lookup value description only if any exist (skip O(n) scan otherwise)
276                let description = if has_any_value_descriptions {
277                    self.value_descriptions_for_signal(id, signal.name())
278                        .and_then(|vd| vd.get(raw_value as u64))
279                } else {
280                    None
281                };
282
283                decoded_signals
284                    .push(DecodedSignal::new(
285                        signal.name(),
286                        physical_value,
287                        signal.unit(),
288                        description,
289                    ))
290                    .map_err(|_| Error::Decoding(Error::MESSAGE_TOO_MANY_SIGNALS))?;
291            }
292        }
293
294        Ok(decoded_signals)
295    }
296
297    /// Check extended multiplexing rules for a signal.
298    /// Returns Some(true) if signal should be decoded, Some(false) if not,
299    /// or None if no extended multiplexing entries exist for this signal.
300    #[inline]
301    fn check_extended_multiplexing(
302        &self,
303        message_id: u32,
304        signal_name: &str,
305        switch_values: &SwitchValues,
306    ) -> Option<bool> {
307        // Get extended entries for this signal
308        let indices = self.ext_mux_index.get(message_id, signal_name)?;
309        if indices.is_empty() {
310            return None;
311        }
312
313        // Collect entries without allocation by working with indices directly
314        // Check ALL switches referenced - all must match (AND logic)
315
316        // First, collect unique switch names referenced by this signal's extended entries
317        let mut unique_switches: [Option<&str>; MAX_SWITCHES] = [None; MAX_SWITCHES];
318        let mut unique_count = 0;
319
320        for &idx in indices {
321            if let Some(entry) = self.extended_multiplexing.get(idx) {
322                let switch_name = entry.multiplexer_switch();
323                // Check if already in unique_switches
324                let found =
325                    unique_switches.iter().take(unique_count).any(|&s| s == Some(switch_name));
326                if !found && unique_count < MAX_SWITCHES {
327                    unique_switches[unique_count] = Some(switch_name);
328                    unique_count += 1;
329                }
330            }
331        }
332
333        // All referenced switches must have matching values
334        for switch_opt in unique_switches.iter().take(unique_count) {
335            let switch_name = match switch_opt {
336                Some(name) => *name,
337                None => continue,
338            };
339
340            // Get the current switch value
341            let switch_val = match switch_values.get_by_name(switch_name) {
342                Some(v) => v,
343                None => return Some(false), // Switch not found, signal not active
344            };
345
346            // Check if any extended entry for this switch has a matching value range
347            let mut has_match = false;
348            for &idx in indices {
349                if let Some(entry) = self.extended_multiplexing.get(idx) {
350                    if entry.multiplexer_switch() == switch_name {
351                        for &(min, max) in entry.value_ranges() {
352                            if switch_val >= min && switch_val <= max {
353                                has_match = true;
354                                break;
355                            }
356                        }
357                        if has_match {
358                            break;
359                        }
360                    }
361                }
362            }
363
364            if !has_match {
365                return Some(false); // This switch doesn't match, signal not active
366            }
367        }
368
369        Some(true) // All switches match
370    }
371
372    /// Check if any extended multiplexing entries exist for a message.
373    /// O(n) scan over extended multiplexing entries.
374    #[inline]
375    fn has_extended_multiplexing_for_message(&self, message_id: u32) -> bool {
376        self.extended_multiplexing
377            .iter()
378            .any(|ext_mux| ext_mux.message_id() == message_id)
379    }
380
381    /// Decode a CAN frame using the embedded-can [`Frame`] trait.
382    ///
383    /// This is a convenience method that automatically extracts the CAN ID and payload
384    /// from any type implementing the embedded-can [`Frame`] trait, and determines
385    /// whether the ID is standard (11-bit) or extended (29-bit).
386    ///
387    /// # Arguments
388    ///
389    /// * `frame` - Any type implementing the embedded-can [`Frame`] trait
390    ///
391    /// # Returns
392    ///
393    /// * `Ok(Vec<DecodedSignal>)` - A vector of decoded signals with name, value, and unit
394    /// * `Err(Error)` - If the message ID is not found, payload length doesn't match DLC, or signal decoding fails
395    ///
396    /// # Examples
397    ///
398    /// ```rust,ignore
399    /// use dbc_rs::Dbc;
400    /// use embedded_can::{Frame, Id, StandardId};
401    ///
402    /// // Assuming you have a CAN frame from your hardware driver
403    /// let frame = MyCanFrame::new(StandardId::new(256).unwrap(), &[0x40, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
404    ///
405    /// let dbc = Dbc::parse(r#"VERSION "1.0"
406    ///
407    /// BU_: ECM
408    ///
409    /// BO_ 256 Engine : 8 ECM
410    ///  SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
411    /// "#)?;
412    ///
413    /// let decoded = dbc.decode_frame(frame)?;
414    /// assert_eq!(decoded[0].name, "RPM");
415    /// assert_eq!(decoded[0].value, 2000.0);
416    /// # Ok::<(), dbc_rs::Error>(())
417    /// ```
418    ///
419    /// # Feature
420    ///
421    /// This method is only available when the `embedded-can` feature is enabled:
422    ///
423    /// ```toml
424    /// [dependencies]
425    /// dbc-rs = { version = "1", features = ["embedded-can"] }
426    /// ```
427    #[cfg(feature = "embedded-can")]
428    #[inline]
429    pub fn decode_frame<T: Frame>(
430        &self,
431        frame: T,
432    ) -> Result<Vec<DecodedSignal<'_>, { MAX_SIGNALS_PER_MESSAGE }>> {
433        let payload = frame.data();
434        match frame.id() {
435            Id::Standard(id) => self.decode(id.as_raw() as u32, payload, false),
436            Id::Extended(id) => self.decode(id.as_raw(), payload, true),
437        }
438    }
439}
440
441#[cfg(test)]
442mod tests {
443    use crate::Dbc;
444
445    #[test]
446    fn test_decode_basic() {
447        let dbc = Dbc::parse(
448            r#"VERSION "1.0"
449
450BU_: ECM
451
452BO_ 256 Engine : 8 ECM
453 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
454"#,
455        )
456        .unwrap();
457
458        // Decode a CAN message with RPM value of 2000 (raw: 8000 = 0x1F40)
459        let payload = [0x40, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
460        let decoded = dbc.decode(256, &payload, false).unwrap();
461        assert_eq!(decoded.len(), 1);
462        assert_eq!(decoded[0].name, "RPM");
463        assert_eq!(decoded[0].value, 2000.0);
464        assert_eq!(decoded[0].unit, Some("rpm"));
465    }
466
467    #[test]
468    fn test_decode_message_not_found() {
469        let dbc = Dbc::parse(
470            r#"VERSION "1.0"
471
472BU_: ECM
473
474BO_ 256 Engine : 8 ECM
475"#,
476        )
477        .unwrap();
478
479        let payload = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
480        let result = dbc.decode(512, &payload, false);
481        assert!(result.is_err());
482    }
483
484    #[test]
485    fn test_decode_message() {
486        let data = r#"VERSION "1.0"
487
488BU_: ECM
489
490BO_ 256 Engine : 8 ECM
491 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
492 SG_ Temp : 16|8@1- (1,-40) [-40|215] "°C" *
493"#;
494
495        let dbc = Dbc::parse(data).unwrap();
496
497        // Decode a CAN message with RPM = 2000 (raw: 8000 = 0x1F40) and Temp = 50°C (raw: 90)
498        // Little-endian: RPM at bits 0-15, Temp at bits 16-23
499        let payload = [0x40, 0x1F, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00];
500        let decoded = dbc.decode(256, &payload, false).unwrap();
501
502        assert_eq!(decoded.len(), 2);
503        assert_eq!(decoded[0].name, "RPM");
504        assert_eq!(decoded[0].value, 2000.0);
505        assert_eq!(decoded[0].unit, Some("rpm"));
506        assert_eq!(decoded[1].name, "Temp");
507        assert_eq!(decoded[1].value, 50.0);
508        assert_eq!(decoded[1].unit, Some("°C"));
509    }
510
511    #[test]
512    fn test_decode_payload_length_mismatch() {
513        use crate::Error;
514        let data = r#"VERSION "1.0"
515
516BU_: ECM
517
518BO_ 256 Engine : 8 ECM
519 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
520"#;
521
522        let dbc = Dbc::parse(data).unwrap();
523
524        // Try to decode with payload shorter than DLC (DLC is 8, payload is 4)
525        let payload = [0x40, 0x1F, 0x00, 0x00];
526        let result = dbc.decode(256, &payload, false);
527        assert!(result.is_err());
528        match result.unwrap_err() {
529            Error::Decoding(msg) => {
530                assert!(msg.contains(Error::PAYLOAD_LENGTH_MISMATCH));
531            }
532            _ => panic!("Expected Error::Decoding"),
533        }
534    }
535
536    #[test]
537    fn test_decode_big_endian_signal() {
538        let data = r#"VERSION "1.0"
539
540BU_: ECM
541
542BO_ 256 Engine : 8 ECM
543 SG_ RPM : 0|16@0+ (1.0,0) [0|65535] "rpm" *
544"#;
545
546        let dbc = Dbc::parse(data).unwrap();
547
548        // Decode a big-endian signal: RPM = 256 (raw: 256 = 0x0100)
549        // For big-endian at bit 0-15, the bytes are arranged as [0x01, 0x00]
550        // Testing with a simple value that's easier to verify
551        let payload = [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
552        let decoded = dbc.decode(256, &payload, false).unwrap();
553
554        assert_eq!(decoded.len(), 1);
555        assert_eq!(decoded[0].name, "RPM");
556        // The exact value depends on big-endian bit extraction implementation
557        // We just verify that decoding doesn't crash and returns a value
558        assert!(decoded[0].value >= 0.0);
559        assert_eq!(decoded[0].unit, Some("rpm"));
560    }
561
562    #[test]
563    fn test_decode_multiplexed_signal() {
564        let dbc = Dbc::parse(
565            r#"VERSION "1.0"
566
567BU_: ECM
568
569BO_ 256 Engine : 8 ECM
570 SG_ MuxId M : 0|8@1+ (1,0) [0|255] ""
571 SG_ Signal0 m0 : 8|16@1+ (0.1,0) [0|6553.5] "unit" *
572 SG_ Signal1 m1 : 24|16@1+ (0.01,0) [0|655.35] "unit" *
573 SG_ NormalSignal : 40|8@1+ (1,0) [0|255] ""
574"#,
575        )
576        .unwrap();
577
578        // Test with MuxId = 0: Should decode Signal0 and NormalSignal, but not Signal1
579        let payload = [0x00, 0x64, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00];
580        // MuxId=0, Signal0=100 (raw: 1000 = 0x03E8, but little-endian: 0xE8, 0x03), NormalSignal=50
581        // Payload: [MuxId=0, Signal0_low=0x64, Signal0_high=0x00, padding, NormalSignal=0x32, ...]
582        let decoded = dbc.decode(256, &payload, false).unwrap();
583
584        // Helper to find value by signal name
585        let find_signal = |name: &str| decoded.iter().find(|s| s.name == name).map(|s| s.value);
586
587        // MuxId should always be decoded
588        assert!(find_signal("MuxId").is_some());
589        // Signal0 should be decoded (MuxId == 0)
590        assert!(find_signal("Signal0").is_some());
591        // Signal1 should NOT be decoded (MuxId != 1)
592        assert!(find_signal("Signal1").is_none());
593        // NormalSignal should always be decoded (not multiplexed)
594        assert!(find_signal("NormalSignal").is_some());
595    }
596
597    #[test]
598    fn test_decode_multiplexed_signal_switch_one() {
599        let dbc = Dbc::parse(
600            r#"VERSION "1.0"
601
602BU_: ECM
603
604BO_ 256 Engine : 8 ECM
605 SG_ MuxId M : 0|8@1+ (1,0) [0|255] ""
606 SG_ Signal0 m0 : 8|16@1+ (0.1,0) [0|6553.5] "unit" *
607 SG_ Signal1 m1 : 24|16@1+ (0.01,0) [0|655.35] "unit" *
608"#,
609        )
610        .unwrap();
611
612        // Test with MuxId = 1: Should decode Signal1, but not Signal0
613        let payload = [0x01, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00];
614        // MuxId=1 (at byte 0), Signal1 at bits 24-39 (bytes 3-4, little-endian)
615        // Signal1 value: 100 (raw: 100, little-endian bytes: 0x64, 0x00)
616        let decoded = dbc.decode(256, &payload, false).unwrap();
617
618        // Helper to find value by signal name
619        let find_signal = |name: &str| decoded.iter().find(|s| s.name == name).map(|s| s.value);
620
621        // MuxId should always be decoded
622        assert_eq!(find_signal("MuxId"), Some(1.0));
623        // Signal0 should NOT be decoded (MuxId != 0)
624        assert!(find_signal("Signal0").is_none());
625        // Signal1 should be decoded (MuxId == 1)
626        assert!(find_signal("Signal1").is_some());
627    }
628
629    #[test]
630    fn test_decode_mixed_byte_order() {
631        let dbc = Dbc::parse(
632            r#"VERSION "1.0"
633
634BU_: ECM
635
636BO_ 256 MixedByteOrder : 8 ECM
637 SG_ LittleEndianSignal : 0|16@1+ (1.0,0) [0|65535] ""
638 SG_ BigEndianSignal : 16|16@0+ (1.0,0) [0|65535] ""
639 SG_ AnotherLittleEndian : 32|8@1+ (1.0,0) [0|255] ""
640 SG_ AnotherBigEndian : 40|8@0+ (1.0,0) [0|255] ""
641"#,
642        )
643        .unwrap();
644
645        // Test payload with both big-endian and little-endian signals:
646        // - LittleEndianSignal at bits 0-15 (bytes 0-1): [0x34, 0x12] = 0x1234 = 4660
647        // - BigEndianSignal at bits 16-31 (bytes 2-3): [0x00, 0x01] = decoded based on BE bit mapping
648        // - AnotherLittleEndian at bits 32-39 (byte 4): 0xAB = 171
649        // - AnotherBigEndian at bits 40-47 (byte 5): 0xCD = decoded based on BE bit mapping
650        let payload = [
651            0x34, 0x12, // Bytes 0-1: LittleEndianSignal
652            0x00, 0x01, // Bytes 2-3: BigEndianSignal
653            0xAB, // Byte 4: AnotherLittleEndian
654            0xCD, // Byte 5: AnotherBigEndian
655            0x00, 0x00, // Padding
656        ];
657        let decoded = dbc.decode(256, &payload, false).unwrap();
658
659        // Helper to find value by signal name
660        let find_signal = |name: &str| decoded.iter().find(|s| s.name == name).map(|s| s.value);
661
662        // Verify little-endian 16-bit signal: bytes [0x34, 0x12] = 0x1234 = 4660
663        assert_eq!(find_signal("LittleEndianSignal"), Some(4660.0)); // 0x1234
664
665        // For big-endian, verify it decodes correctly (exact value depends on BE bit mapping)
666        let big_endian_value = find_signal("BigEndianSignal").unwrap();
667        // Big-endian signal should decode to a reasonable value
668        assert!((0.0..=65535.0).contains(&big_endian_value));
669
670        // Verify little-endian 8-bit signal at byte 4
671        assert_eq!(find_signal("AnotherLittleEndian"), Some(171.0)); // 0xAB
672
673        // For big-endian 8-bit signal, verify it decoded (exact value depends on BE bit mapping)
674        let big_endian_8bit = find_signal("AnotherBigEndian");
675        assert!(big_endian_8bit.is_some());
676        assert!(big_endian_8bit.unwrap() >= 0.0 && big_endian_8bit.unwrap() <= 255.0);
677
678        // All signals should be decoded
679        assert_eq!(decoded.len(), 4);
680
681        // Verify both 16-bit signals decoded successfully (proves both byte orders work)
682        assert!(find_signal("LittleEndianSignal").is_some());
683        assert!(find_signal("BigEndianSignal").is_some());
684    }
685
686    #[test]
687    fn test_decode_extended_multiplexing_simple() {
688        let dbc = Dbc::parse(
689            r#"VERSION "1.0"
690
691BU_: ECM
692
693BO_ 500 ComplexMux : 8 ECM
694 SG_ Mux1 M : 0|8@1+ (1,0) [0|255] ""
695 SG_ Signal_A m0 : 16|16@1+ (0.1,0) [0|100] "unit" *
696
697SG_MUL_VAL_ 500 Signal_A Mux1 5-10 ;
698"#,
699        )
700        .unwrap();
701
702        // Test with Mux1 = 5: Should decode Signal_A (within range 5-10)
703        // Mux1=5 (byte 0), Signal_A at bits 16-31 (bytes 2-3, little-endian)
704        // Signal_A=100 (raw: 1000 = 0x03E8, little-endian bytes: 0xE8, 0x03)
705        let payload = [0x05, 0x00, 0xE8, 0x03, 0x00, 0x00, 0x00, 0x00];
706        let decoded = dbc.decode(500, &payload, false).unwrap();
707
708        let find_signal = |name: &str| decoded.iter().find(|s| s.name == name).map(|s| s.value);
709
710        assert_eq!(find_signal("Mux1"), Some(5.0));
711        // Extended multiplexing: Signal_A should decode when Mux1 is in range 5-10
712        assert_eq!(
713            dbc.extended_multiplexing_for_message(500).count(),
714            1,
715            "Extended multiplexing entries should be parsed"
716        );
717        assert!(
718            find_signal("Signal_A").is_some(),
719            "Signal_A should be decoded when Mux1=5 (within range 5-10)"
720        );
721        assert_eq!(find_signal("Signal_A").unwrap(), 100.0);
722
723        // Test with Mux1 = 15: Should NOT decode Signal_A (outside range 5-10)
724        let payload2 = [0x0F, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00];
725        let decoded2 = dbc.decode(500, &payload2, false).unwrap();
726        let find_signal2 = |name: &str| decoded2.iter().find(|s| s.name == name).map(|s| s.value);
727
728        assert_eq!(find_signal2("Mux1"), Some(15.0));
729        assert!(find_signal2("Signal_A").is_none());
730    }
731
732    #[test]
733    fn test_decode_extended_multiplexing_multiple_ranges() {
734        let dbc = Dbc::parse(
735            r#"VERSION "1.0"
736
737BU_: ECM
738
739BO_ 501 MultiRangeMux : 8 ECM
740 SG_ Mux1 M : 0|8@1+ (1,0) [0|255] ""
741 SG_ Signal_B m0 : 16|16@1+ (1,0) [0|65535] "unit" *
742
743SG_MUL_VAL_ 501 Signal_B Mux1 0-5,10-15,20-25 ;
744"#,
745        )
746        .unwrap();
747
748        // Test with Mux1 = 3: Should decode (within range 0-5)
749        // Signal_B at bits 16-31, value 4096 (raw, little-endian: 0x00, 0x10)
750        let payload1 = [0x03, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00];
751        let decoded1 = dbc.decode(501, &payload1, false).unwrap();
752        let find1 = |name: &str| decoded1.iter().find(|s| s.name == name).map(|s| s.value);
753        assert_eq!(find1("Mux1"), Some(3.0));
754        assert!(find1("Signal_B").is_some());
755
756        // Test with Mux1 = 12: Should decode (within range 10-15)
757        // Signal_B at bits 16-31, value 8192 (raw, little-endian: 0x00, 0x20)
758        let payload2 = [0x0C, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00];
759        let decoded2 = dbc.decode(501, &payload2, false).unwrap();
760        let find2 = |name: &str| decoded2.iter().find(|s| s.name == name).map(|s| s.value);
761        assert_eq!(find2("Mux1"), Some(12.0));
762        assert!(find2("Signal_B").is_some());
763
764        // Test with Mux1 = 22: Should decode (within range 20-25)
765        // Signal_B at bits 16-31, value 12288 (raw, little-endian: 0x00, 0x30)
766        let payload3 = [0x16, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00];
767        let decoded3 = dbc.decode(501, &payload3, false).unwrap();
768        let find3 = |name: &str| decoded3.iter().find(|s| s.name == name).map(|s| s.value);
769        assert_eq!(find3("Mux1"), Some(22.0));
770        assert!(find3("Signal_B").is_some());
771
772        // Test with Mux1 = 8: Should NOT decode (not in any range)
773        // Signal_B at bits 16-31, value 16384 (raw, little-endian: 0x00, 0x40)
774        let payload4 = [0x08, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00];
775        let decoded4 = dbc.decode(501, &payload4, false).unwrap();
776        let find4 = |name: &str| decoded4.iter().find(|s| s.name == name).map(|s| s.value);
777        assert_eq!(find4("Mux1"), Some(8.0));
778        assert!(find4("Signal_B").is_none());
779    }
780
781    /// Test extended multiplexing with multiple switches (AND logic - all must match).
782    /// Note: Depends on SG_MUL_VAL_ parsing working correctly.
783    #[test]
784    fn test_decode_extended_multiplexing_multiple_switches() {
785        let dbc = Dbc::parse(
786            r#"VERSION "1.0"
787
788BU_: ECM
789
790BO_ 502 MultiSwitchMux : 8 ECM
791 SG_ Mux1 M : 0|8@1+ (1,0) [0|255] ""
792 SG_ Mux2 M : 8|8@1+ (1,0) [0|255] ""
793 SG_ Signal_C m0 : 16|16@1+ (1,0) [0|65535] "unit" *
794
795SG_MUL_VAL_ 502 Signal_C Mux1 5-10 ;
796SG_MUL_VAL_ 502 Signal_C Mux2 20-25 ;
797"#,
798        )
799        .unwrap();
800
801        // Test with Mux1=7 and Mux2=22: Should decode (both switches match their ranges)
802        // Mux1=7 (byte 0), Mux2=22 (byte 1), Signal_C at bits 16-31 (bytes 2-3, little-endian)
803        let payload1 = [0x07, 0x16, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00];
804        let decoded1 = dbc.decode(502, &payload1, false).unwrap();
805        let find1 = |name: &str| decoded1.iter().find(|s| s.name == name).map(|s| s.value);
806        assert_eq!(find1("Mux1"), Some(7.0));
807        assert_eq!(find1("Mux2"), Some(22.0));
808        assert!(find1("Signal_C").is_some());
809
810        // Test with Mux1=7 and Mux2=30: Should NOT decode (Mux2 outside range)
811        let payload2 = [0x07, 0x1E, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00];
812        let decoded2 = dbc.decode(502, &payload2, false).unwrap();
813        let find2 = |name: &str| decoded2.iter().find(|s| s.name == name).map(|s| s.value);
814        assert_eq!(find2("Mux1"), Some(7.0));
815        assert_eq!(find2("Mux2"), Some(30.0));
816        assert!(find2("Signal_C").is_none());
817
818        // Test with Mux1=15 and Mux2=22: Should NOT decode (Mux1 outside range)
819        let payload3 = [0x0F, 0x16, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00];
820        let decoded3 = dbc.decode(502, &payload3, false).unwrap();
821        let find3 = |name: &str| decoded3.iter().find(|s| s.name == name).map(|s| s.value);
822        assert_eq!(find3("Mux1"), Some(15.0));
823        assert_eq!(find3("Mux2"), Some(22.0));
824        assert!(find3("Signal_C").is_none());
825    }
826
827    /// Test that extended multiplexing takes precedence over basic m0/m1 values.
828    /// Note: Depends on SG_MUL_VAL_ parsing working correctly.
829    #[test]
830    fn test_decode_extended_multiplexing_takes_precedence() {
831        let dbc = Dbc::parse(
832            r#"VERSION "1.0"
833
834BU_: ECM
835
836BO_ 503 PrecedenceTest : 8 ECM
837 SG_ Mux1 M : 0|8@1+ (1,0) [0|255] ""
838 SG_ Signal_D m0 : 16|16@1+ (1,0) [0|65535] "unit" *
839
840SG_MUL_VAL_ 503 Signal_D Mux1 10-15 ;
841"#,
842        )
843        .unwrap();
844
845        // Test with Mux1 = 0: Should NOT decode
846        // Even though basic multiplexing would match (m0 means decode when switch=0),
847        // extended multiplexing takes precedence and requires Mux1 to be 10-15
848        let payload1 = [0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00];
849        let decoded1 = dbc.decode(503, &payload1, false).unwrap();
850        let find1 = |name: &str| decoded1.iter().find(|s| s.name == name).map(|s| s.value);
851        assert_eq!(find1("Mux1"), Some(0.0));
852        assert!(find1("Signal_D").is_none());
853
854        // Test with Mux1 = 12: Should decode (within extended range 10-15)
855        let payload2 = [0x0C, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00];
856        let decoded2 = dbc.decode(503, &payload2, false).unwrap();
857        let find2 = |name: &str| decoded2.iter().find(|s| s.name == name).map(|s| s.value);
858        assert_eq!(find2("Mux1"), Some(12.0));
859        assert!(find2("Signal_D").is_some());
860    }
861
862    /// Test extended multiplexing with signals that are both multiplexed and multiplexer switches (m65M pattern).
863    /// Note: Depends on SG_MUL_VAL_ parsing working correctly.
864    #[test]
865    fn test_decode_extended_multiplexing_with_extended_mux_signal() {
866        // Test extended multiplexing where the signal itself is also a multiplexer (m65M pattern)
867        let dbc = Dbc::parse(
868            r#"VERSION "1.0"
869
870BU_: ECM
871
872BO_ 504 ExtendedMuxSignal : 8 ECM
873 SG_ Mux1 M : 0|8@1+ (1,0) [0|255] ""
874 SG_ Mux2 m65M : 8|8@1+ (1,0) [0|255] ""
875 SG_ Signal_E m0 : 16|16@1+ (1,0) [0|65535] "unit" *
876
877SG_MUL_VAL_ 504 Signal_E Mux1 65-65 ;
878SG_MUL_VAL_ 504 Signal_E Mux2 10-15 ;
879"#,
880        )
881        .unwrap();
882
883        // Test with Mux1=65 and Mux2=12: Should decode Signal_E
884        // Mux2 is both multiplexed (m65 - active when Mux1=65) and a multiplexer (M)
885        let payload = [0x41, 0x0C, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x00];
886        // Mux1=65 (0x41), Mux2=12 (0x0C), Signal_E at bits 16-31
887        let decoded = dbc.decode(504, &payload, false).unwrap();
888        let find = |name: &str| decoded.iter().find(|s| s.name == name).map(|s| s.value);
889
890        assert_eq!(find("Mux1"), Some(65.0));
891        assert_eq!(find("Mux2"), Some(12.0));
892        assert!(find("Signal_E").is_some());
893
894        // Test with Mux1=64 and Mux2=12: Should NOT decode (Mux1 not 65)
895        let payload2 = [0x40, 0x0C, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x00];
896        let decoded2 = dbc.decode(504, &payload2, false).unwrap();
897        let find2 = |name: &str| decoded2.iter().find(|s| s.name == name).map(|s| s.value);
898        assert_eq!(find2("Mux1"), Some(64.0));
899        assert_eq!(find2("Mux2"), Some(12.0));
900        assert!(find2("Signal_E").is_none());
901    }
902
903    #[test]
904    fn test_decode_negative_multiplexer_switch() {
905        use crate::Error;
906        // Create a DBC with a signed multiplexer switch signal
907        // 8-bit signed signal: values -128 to 127
908        let dbc = Dbc::parse(
909            r#"VERSION "1.0"
910
911BU_: ECM
912
913BO_ 256 MuxMessage : 8 ECM
914 SG_ MuxSwitch M : 0|8@1- (1,0) [-128|127] ""
915 SG_ SignalA m0 : 8|8@1+ (1,0) [0|255] ""
916"#,
917        )
918        .unwrap();
919
920        // Try to decode with a negative raw value for the multiplexer switch
921        // -5 in 8-bit two's complement is 0xFB
922        // Little-endian: MuxSwitch at bits 0-7, SignalA at bits 8-15
923        let payload = [0xFB, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
924        let result = dbc.decode(256, &payload, false);
925        assert!(result.is_err());
926        match result.unwrap_err() {
927            Error::Decoding(msg) => {
928                assert_eq!(msg, Error::MULTIPLEXER_SWITCH_NEGATIVE);
929            }
930            _ => panic!("Expected Error::Decoding with MULTIPLEXER_SWITCH_NEGATIVE"),
931        }
932    }
933
934    #[test]
935    fn test_decode_too_many_unique_switches() {
936        use crate::{Error, MAX_SIGNALS_PER_MESSAGE};
937        // Skip this test if MAX_SIGNALS_PER_MESSAGE is too low to create 17 signals
938        // (16 multiplexer switches + 1 signal = 17 total signals needed)
939        if MAX_SIGNALS_PER_MESSAGE < 17 {
940            return;
941        }
942
943        // Create a DBC with more than 16 unique switches in extended multiplexing
944        // This should trigger an error when trying to decode
945        // Using string literal concatenation to avoid std features
946        let dbc_str = r#"VERSION "1.0"
947
948BU_: ECM
949
950BO_ 600 TooManySwitches : 18 ECM
951 SG_ Mux1 M : 0|8@1+ (1,0) [0|255] ""
952 SG_ Mux2 M : 8|8@1+ (1,0) [0|255] ""
953 SG_ Mux3 M : 16|8@1+ (1,0) [0|255] ""
954 SG_ Mux4 M : 24|8@1+ (1,0) [0|255] ""
955 SG_ Mux5 M : 32|8@1+ (1,0) [0|255] ""
956 SG_ Mux6 M : 40|8@1+ (1,0) [0|255] ""
957 SG_ Mux7 M : 48|8@1+ (1,0) [0|255] ""
958 SG_ Mux8 M : 56|8@1+ (1,0) [0|255] ""
959 SG_ Mux9 M : 64|8@1+ (1,0) [0|255] ""
960 SG_ Mux10 M : 72|8@1+ (1,0) [0|255] ""
961 SG_ Mux11 M : 80|8@1+ (1,0) [0|255] ""
962 SG_ Mux12 M : 88|8@1+ (1,0) [0|255] ""
963 SG_ Mux13 M : 96|8@1+ (1,0) [0|255] ""
964 SG_ Mux14 M : 104|8@1+ (1,0) [0|255] ""
965 SG_ Mux15 M : 112|8@1+ (1,0) [0|255] ""
966 SG_ Mux16 M : 120|8@1+ (1,0) [0|255] ""
967 SG_ Mux17 M : 128|8@1+ (1,0) [0|255] ""
968 SG_ Signal_X m0 : 136|8@1+ (1,0) [0|255] "unit" *
969
970SG_MUL_VAL_ 600 Signal_X Mux1 0-255 ;
971SG_MUL_VAL_ 600 Signal_X Mux2 0-255 ;
972SG_MUL_VAL_ 600 Signal_X Mux3 0-255 ;
973SG_MUL_VAL_ 600 Signal_X Mux4 0-255 ;
974SG_MUL_VAL_ 600 Signal_X Mux5 0-255 ;
975SG_MUL_VAL_ 600 Signal_X Mux6 0-255 ;
976SG_MUL_VAL_ 600 Signal_X Mux7 0-255 ;
977SG_MUL_VAL_ 600 Signal_X Mux8 0-255 ;
978SG_MUL_VAL_ 600 Signal_X Mux9 0-255 ;
979SG_MUL_VAL_ 600 Signal_X Mux10 0-255 ;
980SG_MUL_VAL_ 600 Signal_X Mux11 0-255 ;
981SG_MUL_VAL_ 600 Signal_X Mux12 0-255 ;
982SG_MUL_VAL_ 600 Signal_X Mux13 0-255 ;
983SG_MUL_VAL_ 600 Signal_X Mux14 0-255 ;
984SG_MUL_VAL_ 600 Signal_X Mux15 0-255 ;
985SG_MUL_VAL_ 600 Signal_X Mux16 0-255 ;
986SG_MUL_VAL_ 600 Signal_X Mux17 0-255 ;
987"#;
988
989        let dbc = Dbc::parse(dbc_str).unwrap();
990
991        // Try to decode - should fail with MESSAGE_TOO_MANY_SIGNALS error
992        // because we have 17 unique switches (exceeding the limit of 16)
993        let payload = [0x00; 18];
994        let result = dbc.decode(600, &payload, false);
995        assert!(
996            result.is_err(),
997            "Decode should fail when there are more than 16 unique switches"
998        );
999        match result.unwrap_err() {
1000            Error::Decoding(msg) => {
1001                assert_eq!(
1002                    msg,
1003                    Error::MESSAGE_TOO_MANY_SIGNALS,
1004                    "Expected MESSAGE_TOO_MANY_SIGNALS error, got: {}",
1005                    msg
1006                );
1007            }
1008            e => panic!(
1009                "Expected Error::Decoding with MESSAGE_TOO_MANY_SIGNALS, got: {:?}",
1010                e
1011            ),
1012        }
1013    }
1014
1015    #[test]
1016    fn test_decode_extended_can_id() {
1017        // Test decoding with extended CAN ID (29-bit)
1018        // In DBC files, extended IDs have bit 31 set (0x80000000)
1019        // 0x80000000 + 0x400 = 2147484672
1020        let dbc = Dbc::parse(
1021            r#"VERSION "1.0"
1022
1023BU_: ECM
1024
1025BO_ 2147484672 ExtendedMsg : 8 ECM
1026 SG_ Speed : 0|16@1+ (0.1,0) [0|6553.5] "km/h" *
1027"#,
1028        )
1029        .unwrap();
1030        // 2147484672 = 0x80000400 = extended ID 0x400 (1024)
1031
1032        let payload = [0xE8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
1033        // Speed = 1000 * 0.1 = 100.0 km/h
1034
1035        // Decode using raw CAN ID (0x400) with is_extended=true
1036        let decoded = dbc.decode(0x400, &payload, true).unwrap();
1037        assert_eq!(decoded.len(), 1);
1038        assert_eq!(decoded[0].name, "Speed");
1039        assert_eq!(decoded[0].value, 100.0);
1040        assert_eq!(decoded[0].unit, Some("km/h"));
1041    }
1042
1043    #[test]
1044    fn test_decode_extended_can_id_not_found_without_flag() {
1045        // Verify that extended CAN messages are NOT found when is_extended=false
1046        // 0x80000000 + 0x400 = 2147484672
1047        let dbc = Dbc::parse(
1048            r#"VERSION "1.0"
1049
1050BU_: ECM
1051
1052BO_ 2147484672 ExtendedMsg : 8 ECM
1053 SG_ Speed : 0|16@1+ (0.1,0) [0|6553.5] "km/h" *
1054"#,
1055        )
1056        .unwrap();
1057
1058        let payload = [0xE8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
1059
1060        // Without is_extended=true, the message should NOT be found
1061        let result = dbc.decode(0x400, &payload, false);
1062        assert!(result.is_err());
1063    }
1064
1065    #[test]
1066    fn test_decode_standard_vs_extended_same_base_id() {
1067        // Test that standard and extended messages with same base ID are distinct
1068        let dbc = Dbc::parse(
1069            r#"VERSION "1.0"
1070
1071BU_: ECM
1072
1073BO_ 256 StandardMsg : 8 ECM
1074 SG_ StdSignal : 0|8@1+ (1,0) [0|255] "" *
1075
1076BO_ 2147483904 ExtendedMsg : 8 ECM
1077 SG_ ExtSignal : 0|8@1+ (2,0) [0|510] "" *
1078"#,
1079        )
1080        .unwrap();
1081        // 2147483904 = 0x80000100 = extended ID 0x100 (256)
1082
1083        let payload = [0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; // raw = 100
1084
1085        // Decode standard message (ID 256, is_extended=false)
1086        let decoded_std = dbc.decode(256, &payload, false).unwrap();
1087        assert_eq!(decoded_std.len(), 1);
1088        assert_eq!(decoded_std[0].name, "StdSignal");
1089        assert_eq!(decoded_std[0].value, 100.0); // factor 1
1090
1091        // Decode extended message (ID 256, is_extended=true)
1092        let decoded_ext = dbc.decode(256, &payload, true).unwrap();
1093        assert_eq!(decoded_ext.len(), 1);
1094        assert_eq!(decoded_ext[0].name, "ExtSignal");
1095        assert_eq!(decoded_ext[0].value, 200.0); // factor 2
1096    }
1097
1098    #[test]
1099    fn test_decode_with_value_descriptions() {
1100        // Test that value descriptions are included in decoded signals
1101        // Reference: SPECIFICATIONS.md Section 18.2 and 18.3
1102        let dbc = Dbc::parse(
1103            r#"VERSION "1.0"
1104
1105BU_: ECM
1106
1107BO_ 200 GearboxData : 4 ECM
1108 SG_ GearActual : 0|8@1+ (1,0) [0|5] "" *
1109
1110VAL_ 200 GearActual 0 "Park" 1 "Reverse" 2 "Neutral" 3 "Drive" 4 "Sport" 5 "Manual" ;
1111"#,
1112        )
1113        .unwrap();
1114
1115        // Test Gear = 0 (Park)
1116        let payload = [0x00, 0x00, 0x00, 0x00];
1117        let decoded = dbc.decode(200, &payload, false).unwrap();
1118        assert_eq!(decoded.len(), 1);
1119        assert_eq!(decoded[0].name, "GearActual");
1120        assert_eq!(decoded[0].value, 0.0);
1121        assert_eq!(decoded[0].description, Some("Park"));
1122
1123        // Test Gear = 3 (Drive)
1124        let payload = [0x03, 0x00, 0x00, 0x00];
1125        let decoded = dbc.decode(200, &payload, false).unwrap();
1126        assert_eq!(decoded[0].value, 3.0);
1127        assert_eq!(decoded[0].description, Some("Drive"));
1128
1129        // Test Gear = 5 (Manual)
1130        let payload = [0x05, 0x00, 0x00, 0x00];
1131        let decoded = dbc.decode(200, &payload, false).unwrap();
1132        assert_eq!(decoded[0].value, 5.0);
1133        assert_eq!(decoded[0].description, Some("Manual"));
1134    }
1135
1136    #[test]
1137    fn test_decode_without_value_descriptions() {
1138        // Test that signals without value descriptions have None
1139        let dbc = Dbc::parse(
1140            r#"VERSION "1.0"
1141
1142BU_: ECM
1143
1144BO_ 256 Engine : 8 ECM
1145 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
1146"#,
1147        )
1148        .unwrap();
1149
1150        let payload = [0x40, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
1151        let decoded = dbc.decode(256, &payload, false).unwrap();
1152        assert_eq!(decoded.len(), 1);
1153        assert_eq!(decoded[0].name, "RPM");
1154        assert_eq!(decoded[0].value, 2000.0);
1155        assert_eq!(decoded[0].unit, Some("rpm"));
1156        assert_eq!(decoded[0].description, None);
1157    }
1158
1159    #[test]
1160    fn test_decode_value_description_not_found() {
1161        // Test that values not in the description table return None
1162        let dbc = Dbc::parse(
1163            r#"VERSION "1.0"
1164
1165BU_: ECM
1166
1167BO_ 200 GearboxData : 4 ECM
1168 SG_ GearActual : 0|8@1+ (1,0) [0|255] "" *
1169
1170VAL_ 200 GearActual 0 "Park" 1 "Reverse" 2 "Neutral" ;
1171"#,
1172        )
1173        .unwrap();
1174
1175        // Test Gear = 10 (not in description table)
1176        let payload = [0x0A, 0x00, 0x00, 0x00];
1177        let decoded = dbc.decode(200, &payload, false).unwrap();
1178        assert_eq!(decoded.len(), 1);
1179        assert_eq!(decoded[0].value, 10.0);
1180        assert_eq!(decoded[0].description, None); // Value 10 not in table
1181    }
1182
1183    #[test]
1184    fn test_decode_multiplexer_with_value_descriptions() {
1185        // Test that multiplexer switch signals also get value descriptions
1186        // Reference: SPECIFICATIONS.md Section 18.3
1187        let dbc = Dbc::parse(
1188            r#"VERSION "1.0"
1189
1190BU_: ECM
1191
1192BO_ 300 MultiplexedSensors : 8 ECM
1193 SG_ SensorID M : 0|8@1+ (1,0) [0|3] "" *
1194 SG_ Temperature m0 : 8|16@1- (0.1,-40) [-40|125] "°C" *
1195 SG_ Pressure m1 : 8|16@1+ (0.01,0) [0|655.35] "kPa" *
1196
1197VAL_ 300 SensorID 0 "Temperature Sensor" 1 "Pressure Sensor" 2 "Humidity Sensor" 3 "Voltage Sensor" ;
1198"#,
1199        )
1200        .unwrap();
1201
1202        // Test with SensorID = 0 (Temperature Sensor)
1203        // Temperature raw = 500 => physical = 500 * 0.1 + (-40) = 10.0 °C
1204        let payload = [0x00, 0xF4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00];
1205        let decoded = dbc.decode(300, &payload, false).unwrap();
1206
1207        // Find the SensorID signal
1208        let sensor_id = decoded.iter().find(|s| s.name == "SensorID").unwrap();
1209        assert_eq!(sensor_id.value, 0.0);
1210        assert_eq!(sensor_id.description, Some("Temperature Sensor"));
1211
1212        // Temperature should be decoded (m0 matches SensorID=0)
1213        let temp = decoded.iter().find(|s| s.name == "Temperature").unwrap();
1214        assert!(temp.description.is_none()); // No value descriptions for Temperature
1215
1216        // Test with SensorID = 1 (Pressure Sensor)
1217        let payload = [0x01, 0x10, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00];
1218        let decoded = dbc.decode(300, &payload, false).unwrap();
1219
1220        let sensor_id = decoded.iter().find(|s| s.name == "SensorID").unwrap();
1221        assert_eq!(sensor_id.value, 1.0);
1222        assert_eq!(sensor_id.description, Some("Pressure Sensor"));
1223    }
1224
1225    #[cfg(feature = "embedded-can")]
1226    mod embedded_can_tests {
1227        use super::*;
1228        use embedded_can::{ExtendedId, Frame, Id, StandardId};
1229
1230        /// A simple test frame for embedded-can tests
1231        struct TestFrame {
1232            id: Id,
1233            data: [u8; 8],
1234            dlc: usize,
1235        }
1236
1237        impl TestFrame {
1238            fn new_standard(id: u16, data: &[u8]) -> Self {
1239                let mut frame_data = [0u8; 8];
1240                let dlc = data.len().min(8);
1241                frame_data[..dlc].copy_from_slice(&data[..dlc]);
1242                Self {
1243                    id: Id::Standard(StandardId::new(id).unwrap()),
1244                    data: frame_data,
1245                    dlc,
1246                }
1247            }
1248
1249            fn new_extended(id: u32, data: &[u8]) -> Self {
1250                let mut frame_data = [0u8; 8];
1251                let dlc = data.len().min(8);
1252                frame_data[..dlc].copy_from_slice(&data[..dlc]);
1253                Self {
1254                    id: Id::Extended(ExtendedId::new(id).unwrap()),
1255                    data: frame_data,
1256                    dlc,
1257                }
1258            }
1259        }
1260
1261        impl Frame for TestFrame {
1262            fn new(id: impl Into<Id>, data: &[u8]) -> Option<Self> {
1263                let mut frame_data = [0u8; 8];
1264                let dlc = data.len().min(8);
1265                frame_data[..dlc].copy_from_slice(&data[..dlc]);
1266                Some(Self {
1267                    id: id.into(),
1268                    data: frame_data,
1269                    dlc,
1270                })
1271            }
1272
1273            fn new_remote(_id: impl Into<Id>, _dlc: usize) -> Option<Self> {
1274                None // Not used in tests
1275            }
1276
1277            fn is_extended(&self) -> bool {
1278                matches!(self.id, Id::Extended(_))
1279            }
1280
1281            fn is_remote_frame(&self) -> bool {
1282                false
1283            }
1284
1285            fn id(&self) -> Id {
1286                self.id
1287            }
1288
1289            fn dlc(&self) -> usize {
1290                self.dlc
1291            }
1292
1293            fn data(&self) -> &[u8] {
1294                &self.data[..self.dlc]
1295            }
1296        }
1297
1298        #[test]
1299        fn test_decode_frame_standard() {
1300            let dbc = Dbc::parse(
1301                r#"VERSION "1.0"
1302
1303BU_: ECM
1304
1305BO_ 256 Engine : 8 ECM
1306 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
1307"#,
1308            )
1309            .unwrap();
1310
1311            // Create a standard CAN frame
1312            let frame =
1313                TestFrame::new_standard(256, &[0x40, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
1314
1315            let decoded = dbc.decode_frame(frame).unwrap();
1316            assert_eq!(decoded.len(), 1);
1317            assert_eq!(decoded[0].name, "RPM");
1318            assert_eq!(decoded[0].value, 2000.0);
1319            assert_eq!(decoded[0].unit, Some("rpm"));
1320        }
1321
1322        #[test]
1323        fn test_decode_frame_extended() {
1324            // 0x80000000 + 0x400 = 2147484672
1325            let dbc = Dbc::parse(
1326                r#"VERSION "1.0"
1327
1328BU_: ECM
1329
1330BO_ 2147484672 ExtendedMsg : 8 ECM
1331 SG_ Speed : 0|16@1+ (0.1,0) [0|6553.5] "km/h" *
1332"#,
1333            )
1334            .unwrap();
1335            // 2147484672 = 0x80000400 = extended ID 0x400
1336
1337            // Create an extended CAN frame with raw ID 0x400
1338            let frame =
1339                TestFrame::new_extended(0x400, &[0xE8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
1340
1341            let decoded = dbc.decode_frame(frame).unwrap();
1342            assert_eq!(decoded.len(), 1);
1343            assert_eq!(decoded[0].name, "Speed");
1344            assert_eq!(decoded[0].value, 100.0);
1345            assert_eq!(decoded[0].unit, Some("km/h"));
1346        }
1347
1348        #[test]
1349        fn test_decode_frame_standard_vs_extended() {
1350            // Test that decode_frame correctly distinguishes standard vs extended frames
1351            let dbc = Dbc::parse(
1352                r#"VERSION "1.0"
1353
1354BU_: ECM
1355
1356BO_ 256 StandardMsg : 8 ECM
1357 SG_ StdSignal : 0|8@1+ (1,0) [0|255] "" *
1358
1359BO_ 2147483904 ExtendedMsg : 8 ECM
1360 SG_ ExtSignal : 0|8@1+ (2,0) [0|510] "" *
1361"#,
1362            )
1363            .unwrap();
1364            // 2147483904 = 0x80000100 = extended ID 0x100 (256)
1365
1366            let payload = [0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; // raw = 100
1367
1368            // Standard frame with ID 256
1369            let std_frame = TestFrame::new_standard(256, &payload);
1370            let decoded_std = dbc.decode_frame(std_frame).unwrap();
1371            assert_eq!(decoded_std[0].name, "StdSignal");
1372            assert_eq!(decoded_std[0].value, 100.0);
1373
1374            // Extended frame with ID 256
1375            let ext_frame = TestFrame::new_extended(256, &payload);
1376            let decoded_ext = dbc.decode_frame(ext_frame).unwrap();
1377            assert_eq!(decoded_ext[0].name, "ExtSignal");
1378            assert_eq!(decoded_ext[0].value, 200.0);
1379        }
1380
1381        #[test]
1382        fn test_decode_frame_message_not_found() {
1383            let dbc = Dbc::parse(
1384                r#"VERSION "1.0"
1385
1386BU_: ECM
1387
1388BO_ 256 Engine : 8 ECM
1389"#,
1390            )
1391            .unwrap();
1392
1393            // Try to decode a frame with unknown ID
1394            let frame =
1395                TestFrame::new_standard(512, &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
1396            let result = dbc.decode_frame(frame);
1397            assert!(result.is_err());
1398        }
1399    }
1400}