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