Skip to main content

dbc_rs/dbc/
decode.rs

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