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