oxirs_canbus/dbc/
parser.rs

1//! DBC (Vector CANdb++) file parser
2//!
3//! Parses DBC files which define CAN message and signal definitions.
4//! The DBC format is the industry standard for CAN database files.
5//!
6//! # Supported Directives
7//!
8//! - `VERSION` - Database version string
9//! - `NS_` - New symbols section
10//! - `BS_` - Bus speed section
11//! - `BU_` - Node/ECU definitions
12//! - `BO_` - Message definitions
13//! - `SG_` - Signal definitions (within messages)
14//! - `CM_` - Comments for messages/signals
15//! - `BA_DEF_` - Attribute definitions
16//! - `BA_` - Attribute values
17//! - `VAL_` - Value descriptions (enumerations)
18//! - `VAL_TABLE_` - Standalone value tables
19
20use crate::error::{CanbusError, CanbusResult};
21use std::collections::HashMap;
22
23/// Byte order for signal extraction
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
25pub enum ByteOrder {
26    /// Little-endian (Intel byte order) - LSB first
27    #[default]
28    LittleEndian,
29    /// Big-endian (Motorola byte order) - MSB first
30    BigEndian,
31}
32
33/// Value type for signals
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
35pub enum ValueType {
36    /// Unsigned integer
37    #[default]
38    Unsigned,
39    /// Signed integer
40    Signed,
41}
42
43/// Multiplexer type for signals
44#[derive(Debug, Clone, PartialEq, Eq, Default)]
45pub enum MultiplexerType {
46    /// Not multiplexed
47    #[default]
48    None,
49    /// Multiplexer signal (switch)
50    Multiplexer,
51    /// Multiplexed signal with value
52    Multiplexed(u32),
53}
54
55/// Signal definition from DBC file
56#[derive(Debug, Clone)]
57pub struct DbcSignal {
58    /// Signal name
59    pub name: String,
60    /// Start bit position
61    pub start_bit: u32,
62    /// Signal length in bits
63    pub bit_length: u32,
64    /// Byte order (Intel/Motorola)
65    pub byte_order: ByteOrder,
66    /// Value type (signed/unsigned)
67    pub value_type: ValueType,
68    /// Scale factor for physical value calculation
69    pub factor: f64,
70    /// Offset for physical value calculation
71    pub offset: f64,
72    /// Minimum physical value
73    pub min: f64,
74    /// Maximum physical value
75    pub max: f64,
76    /// Unit string
77    pub unit: String,
78    /// Receiving nodes
79    pub receivers: Vec<String>,
80    /// Multiplexer information
81    pub multiplexer: MultiplexerType,
82    /// Value descriptions (enum mappings)
83    pub value_descriptions: HashMap<i64, String>,
84    /// Comment/description
85    pub comment: Option<String>,
86}
87
88impl DbcSignal {
89    /// Create a new signal with default values
90    pub fn new(name: &str) -> Self {
91        Self {
92            name: name.to_string(),
93            start_bit: 0,
94            bit_length: 1,
95            byte_order: ByteOrder::default(),
96            value_type: ValueType::default(),
97            factor: 1.0,
98            offset: 0.0,
99            min: 0.0,
100            max: 0.0,
101            unit: String::new(),
102            receivers: Vec::new(),
103            multiplexer: MultiplexerType::default(),
104            value_descriptions: HashMap::new(),
105            comment: None,
106        }
107    }
108
109    /// Calculate physical value from raw integer
110    pub fn to_physical(&self, raw: i64) -> f64 {
111        raw as f64 * self.factor + self.offset
112    }
113
114    /// Calculate raw value from physical value
115    pub fn to_raw(&self, physical: f64) -> i64 {
116        ((physical - self.offset) / self.factor).round() as i64
117    }
118
119    /// Get value description for raw value (enum lookup)
120    pub fn get_value_description(&self, raw: i64) -> Option<&str> {
121        self.value_descriptions.get(&raw).map(|s| s.as_str())
122    }
123}
124
125/// Message definition from DBC file
126#[derive(Debug, Clone)]
127pub struct DbcMessage {
128    /// CAN message ID (without extended flag)
129    pub id: u32,
130    /// Whether this is an extended (29-bit) CAN ID
131    pub is_extended: bool,
132    /// Message name
133    pub name: String,
134    /// Data Length Code (bytes)
135    pub dlc: u8,
136    /// Transmitting node name
137    pub transmitter: String,
138    /// Signals contained in this message
139    pub signals: Vec<DbcSignal>,
140    /// Comment/description
141    pub comment: Option<String>,
142    /// Custom attributes
143    pub attributes: HashMap<String, AttributeValue>,
144}
145
146impl DbcMessage {
147    /// Create a new message
148    pub fn new(id: u32, name: &str, dlc: u8) -> Self {
149        Self {
150            id,
151            is_extended: id > 0x7FF, // Heuristic: ID > 11 bits is extended
152            name: name.to_string(),
153            dlc,
154            transmitter: String::new(),
155            signals: Vec::new(),
156            comment: None,
157            attributes: HashMap::new(),
158        }
159    }
160
161    /// Get signal by name
162    pub fn get_signal(&self, name: &str) -> Option<&DbcSignal> {
163        self.signals.iter().find(|s| s.name == name)
164    }
165
166    /// Get mutable signal by name
167    pub fn get_signal_mut(&mut self, name: &str) -> Option<&mut DbcSignal> {
168        self.signals.iter_mut().find(|s| s.name == name)
169    }
170}
171
172/// Node (ECU) definition
173#[derive(Debug, Clone)]
174pub struct DbcNode {
175    /// Node name
176    pub name: String,
177    /// Comment/description
178    pub comment: Option<String>,
179    /// Custom attributes
180    pub attributes: HashMap<String, AttributeValue>,
181}
182
183impl DbcNode {
184    /// Create a new node
185    pub fn new(name: &str) -> Self {
186        Self {
187            name: name.to_string(),
188            comment: None,
189            attributes: HashMap::new(),
190        }
191    }
192}
193
194/// Attribute value types
195#[derive(Debug, Clone, PartialEq)]
196pub enum AttributeValue {
197    /// Integer value
198    Int(i64),
199    /// Floating point value
200    Float(f64),
201    /// String value
202    String(String),
203    /// Enumeration value
204    Enum(String),
205}
206
207/// Attribute definition
208#[derive(Debug, Clone)]
209pub struct AttributeDefinition {
210    /// Attribute name
211    pub name: String,
212    /// Object type (message, signal, node, or database)
213    pub object_type: AttributeObjectType,
214    /// Value type and constraints
215    pub value_type: AttributeValueType,
216    /// Default value
217    pub default_value: Option<AttributeValue>,
218}
219
220/// Attribute object type
221#[derive(Debug, Clone, Copy, PartialEq, Eq)]
222pub enum AttributeObjectType {
223    /// Database-level attribute
224    Database,
225    /// Node attribute
226    Node,
227    /// Message attribute
228    Message,
229    /// Signal attribute
230    Signal,
231}
232
233/// Attribute value type with constraints
234#[derive(Debug, Clone)]
235pub enum AttributeValueType {
236    /// Integer with min/max constraints
237    Int {
238        /// Minimum allowed value
239        min: i64,
240        /// Maximum allowed value
241        max: i64,
242    },
243    /// Float with min/max constraints
244    Float {
245        /// Minimum allowed value
246        min: f64,
247        /// Maximum allowed value
248        max: f64,
249    },
250    /// String value (no constraints)
251    String,
252    /// Enumeration with allowed values
253    Enum {
254        /// List of allowed enumeration values
255        values: Vec<String>,
256    },
257}
258
259/// Complete DBC database
260#[derive(Debug, Clone)]
261pub struct DbcDatabase {
262    /// Version string
263    pub version: String,
264    /// Nodes/ECUs
265    pub nodes: Vec<DbcNode>,
266    /// Messages
267    pub messages: Vec<DbcMessage>,
268    /// Attribute definitions
269    pub attribute_definitions: Vec<AttributeDefinition>,
270    /// Value tables (standalone enumerations)
271    pub value_tables: HashMap<String, HashMap<i64, String>>,
272    /// Database-level attributes
273    pub attributes: HashMap<String, AttributeValue>,
274}
275
276impl DbcDatabase {
277    /// Create an empty database
278    pub fn new() -> Self {
279        Self {
280            version: String::new(),
281            nodes: Vec::new(),
282            messages: Vec::new(),
283            attribute_definitions: Vec::new(),
284            value_tables: HashMap::new(),
285            attributes: HashMap::new(),
286        }
287    }
288
289    /// Get message by ID
290    pub fn get_message(&self, id: u32) -> Option<&DbcMessage> {
291        self.messages.iter().find(|m| m.id == id)
292    }
293
294    /// Get message by name
295    pub fn get_message_by_name(&self, name: &str) -> Option<&DbcMessage> {
296        self.messages.iter().find(|m| m.name == name)
297    }
298
299    /// Get node by name
300    pub fn get_node(&self, name: &str) -> Option<&DbcNode> {
301        self.nodes.iter().find(|n| n.name == name)
302    }
303
304    /// Get all signal names across all messages
305    pub fn all_signals(&self) -> impl Iterator<Item = (&DbcMessage, &DbcSignal)> {
306        self.messages
307            .iter()
308            .flat_map(|m| m.signals.iter().map(move |s| (m, s)))
309    }
310}
311
312impl Default for DbcDatabase {
313    fn default() -> Self {
314        Self::new()
315    }
316}
317
318/// DBC Parser
319pub struct DbcParser {
320    /// Current line number for error reporting
321    line_number: usize,
322    /// Current database being built
323    database: DbcDatabase,
324}
325
326impl DbcParser {
327    /// Create a new parser
328    pub fn new() -> Self {
329        Self {
330            line_number: 0,
331            database: DbcDatabase::new(),
332        }
333    }
334
335    /// Parse a DBC file from string content
336    pub fn parse(&mut self, content: &str) -> CanbusResult<DbcDatabase> {
337        self.database = DbcDatabase::new();
338        self.line_number = 0;
339
340        let mut lines = content.lines().peekable();
341
342        while let Some(line) = lines.next() {
343            self.line_number += 1;
344            let trimmed = line.trim();
345
346            if trimmed.is_empty() || trimmed.starts_with("//") {
347                continue;
348            }
349
350            // Check if this directive needs multiline handling
351            let needs_semicolon = Self::directive_needs_semicolon(trimmed);
352
353            let mut full_line = trimmed.to_string();
354
355            if needs_semicolon {
356                // Collect continuation lines until we find a semicolon
357                while !full_line.ends_with(';') {
358                    if let Some(next) = lines.next() {
359                        self.line_number += 1;
360                        let next_trimmed = next.trim();
361                        full_line.push(' ');
362                        full_line.push_str(next_trimmed);
363                    } else {
364                        break;
365                    }
366                }
367            }
368
369            self.parse_line(&full_line)?;
370        }
371
372        Ok(std::mem::take(&mut self.database))
373    }
374
375    /// Check if a directive requires a semicolon terminator
376    fn directive_needs_semicolon(line: &str) -> bool {
377        // Directives that can span multiple lines and end with semicolon
378        // Note: BO_ and SG_ do NOT end with semicolons in DBC format
379        line.starts_with("CM_ ")
380            || line.starts_with("BA_DEF_ ")
381            || line.starts_with("BA_DEF_DEF_ ")
382            || line.starts_with("BA_ ")
383            || line.starts_with("VAL_TABLE_ ")
384            || line.starts_with("VAL_ ")
385    }
386
387    /// Parse a single line/directive
388    fn parse_line(&mut self, line: &str) -> CanbusResult<()> {
389        // Trim the line to handle indented signals
390        let trimmed = line.trim();
391
392        if trimmed.starts_with("VERSION") {
393            self.parse_version(trimmed)
394        } else if trimmed.starts_with("NS_") {
395            // New symbols - ignored
396            Ok(())
397        } else if trimmed.starts_with("BS_") {
398            // Bus speed - ignored
399            Ok(())
400        } else if trimmed.starts_with("BU_") {
401            self.parse_nodes(trimmed)
402        } else if trimmed.starts_with("BO_ ") {
403            self.parse_message(trimmed)
404        } else if trimmed.starts_with("SG_ ") {
405            self.parse_standalone_signal(trimmed)
406        } else if trimmed.starts_with("CM_ ") {
407            self.parse_comment(line) // Keep original for semicolon-terminated lines
408        } else if trimmed.starts_with("BA_DEF_ ") {
409            self.parse_attribute_definition(line)
410        } else if trimmed.starts_with("BA_DEF_DEF_ ") {
411            self.parse_attribute_default(line)
412        } else if trimmed.starts_with("BA_ ") {
413            self.parse_attribute_value(line)
414        } else if trimmed.starts_with("VAL_TABLE_ ") {
415            self.parse_value_table(line)
416        } else if trimmed.starts_with("VAL_ ") {
417            self.parse_value_descriptions(line)
418        } else {
419            // Unknown directive - skip
420            Ok(())
421        }
422    }
423
424    /// Parse VERSION directive
425    fn parse_version(&mut self, line: &str) -> CanbusResult<()> {
426        // VERSION "1.0"
427        if let Some(start) = line.find('"') {
428            if let Some(end) = line[start + 1..].find('"') {
429                self.database.version = line[start + 1..start + 1 + end].to_string();
430            }
431        }
432        Ok(())
433    }
434
435    /// Parse BU_ (nodes) directive
436    fn parse_nodes(&mut self, line: &str) -> CanbusResult<()> {
437        // BU_: Node1 Node2 Node3
438        let content = line.strip_prefix("BU_:").unwrap_or("").trim();
439        for name in content.split_whitespace() {
440            if !name.is_empty() {
441                self.database.nodes.push(DbcNode::new(name));
442            }
443        }
444        Ok(())
445    }
446
447    /// Parse BO_ (message) directive
448    fn parse_message(&mut self, line: &str) -> CanbusResult<()> {
449        // BO_ 2024 EngineData: 8 Vector__XXX
450        //  SG_ EngineSpeed : 0|16@1+ (0.125,0) [0|8031.875] "rpm" Vector__XXX
451        let parts: Vec<&str> = line.split_whitespace().collect();
452        if parts.len() < 4 {
453            return Err(self.error("Invalid message format"));
454        }
455
456        let id = parts[1]
457            .parse::<u32>()
458            .map_err(|_| self.error("Invalid message ID"))?;
459
460        let name = parts[2].trim_end_matches(':');
461        let dlc = parts[3]
462            .parse::<u8>()
463            .map_err(|_| self.error("Invalid DLC"))?;
464
465        let mut message = DbcMessage::new(id, name, dlc);
466
467        if parts.len() > 4 {
468            message.transmitter = parts[4].to_string();
469        }
470
471        // Parse embedded signals
472        let signal_start = line.find("SG_");
473        if let Some(pos) = signal_start {
474            let signal_part = &line[pos..];
475            if let Ok(signal) = self.parse_signal_definition(signal_part) {
476                message.signals.push(signal);
477            }
478        }
479
480        self.database.messages.push(message);
481        Ok(())
482    }
483
484    /// Parse SG_ (signal) that appears on its own line
485    fn parse_standalone_signal(&mut self, line: &str) -> CanbusResult<()> {
486        if let Ok(signal) = self.parse_signal_definition(line) {
487            // Add to the last message
488            if let Some(msg) = self.database.messages.last_mut() {
489                msg.signals.push(signal);
490            }
491        }
492        Ok(())
493    }
494
495    /// Parse signal definition
496    fn parse_signal_definition(&self, line: &str) -> CanbusResult<DbcSignal> {
497        // SG_ EngineSpeed : 0|16@1+ (0.125,0) [0|8031.875] "rpm" Vector__XXX
498        // SG_ SignalName M : 0|8@1+ (1,0) [0|0] "" Vector__XXX  (multiplexer)
499        // SG_ SignalName m5 : 0|8@1+ (1,0) [0|0] "" Vector__XXX  (multiplexed value 5)
500
501        let parts: Vec<&str> = line.split_whitespace().collect();
502        if parts.len() < 7 {
503            return Err(self.error("Invalid signal format"));
504        }
505
506        let name = parts[1];
507        let mut signal = DbcSignal::new(name);
508
509        // Check for multiplexer indicator
510        let mut bit_spec_idx = 3;
511        if parts[2] == "M" || parts[2] == "m" {
512            signal.multiplexer = MultiplexerType::Multiplexer;
513            bit_spec_idx = 4;
514        } else if parts[2].starts_with('m') && parts[2].len() > 1 {
515            if let Ok(val) = parts[2][1..].parse::<u32>() {
516                signal.multiplexer = MultiplexerType::Multiplexed(val);
517            }
518            bit_spec_idx = 4;
519        } else if parts[2] != ":" {
520            // Skip if format doesn't match
521            bit_spec_idx = 3;
522        }
523
524        // Parse bit specification: start|length@byte_order+/-
525        // e.g., "0|16@1+"
526        let bit_spec = parts
527            .get(bit_spec_idx)
528            .ok_or_else(|| self.error("Missing bit spec"))?;
529        self.parse_bit_spec(bit_spec, &mut signal)?;
530
531        // Parse factor and offset: (factor,offset)
532        for part in &parts[bit_spec_idx + 1..] {
533            if part.starts_with('(') && part.contains(',') {
534                self.parse_factor_offset(part, &mut signal)?;
535            } else if part.starts_with('[') && part.contains('|') {
536                self.parse_min_max(part, &mut signal)?;
537            } else if part.starts_with('"') {
538                signal.unit = part.trim_matches('"').to_string();
539            } else if !part.starts_with('[') && !part.starts_with('(') && !part.starts_with('"') {
540                // Receiver nodes
541                let receiver = part.trim_matches(',');
542                if !receiver.is_empty() && receiver != "Vector__XXX" {
543                    signal.receivers.push(receiver.to_string());
544                }
545            }
546        }
547
548        Ok(signal)
549    }
550
551    /// Parse bit specification (start|length@byte_order_sign)
552    fn parse_bit_spec(&self, spec: &str, signal: &mut DbcSignal) -> CanbusResult<()> {
553        // Format: start|length@byte_order+/-
554        // e.g., "0|16@1+" or "7|8@0-"
555
556        let at_pos = spec
557            .find('@')
558            .ok_or_else(|| self.error("Invalid bit spec: missing @"))?;
559        let pipe_pos = spec
560            .find('|')
561            .ok_or_else(|| self.error("Invalid bit spec: missing |"))?;
562
563        signal.start_bit = spec[..pipe_pos]
564            .parse()
565            .map_err(|_| self.error("Invalid start bit"))?;
566
567        signal.bit_length = spec[pipe_pos + 1..at_pos]
568            .parse()
569            .map_err(|_| self.error("Invalid bit length"))?;
570
571        let order_sign = &spec[at_pos + 1..];
572        signal.byte_order = if order_sign.starts_with('1') {
573            ByteOrder::LittleEndian
574        } else {
575            ByteOrder::BigEndian
576        };
577
578        signal.value_type = if order_sign.ends_with('-') {
579            ValueType::Signed
580        } else {
581            ValueType::Unsigned
582        };
583
584        Ok(())
585    }
586
587    /// Parse factor and offset: (factor,offset)
588    fn parse_factor_offset(&self, spec: &str, signal: &mut DbcSignal) -> CanbusResult<()> {
589        let inner = spec.trim_matches(|c| c == '(' || c == ')');
590        let parts: Vec<&str> = inner.split(',').collect();
591        if parts.len() == 2 {
592            signal.factor = parts[0].parse().unwrap_or(1.0);
593            signal.offset = parts[1].parse().unwrap_or(0.0);
594        }
595        Ok(())
596    }
597
598    /// Parse min and max: [min|max]
599    fn parse_min_max(&self, spec: &str, signal: &mut DbcSignal) -> CanbusResult<()> {
600        let inner = spec.trim_matches(|c| c == '[' || c == ']');
601        let parts: Vec<&str> = inner.split('|').collect();
602        if parts.len() == 2 {
603            signal.min = parts[0].parse().unwrap_or(0.0);
604            signal.max = parts[1].parse().unwrap_or(0.0);
605        }
606        Ok(())
607    }
608
609    /// Parse CM_ (comment) directive
610    fn parse_comment(&mut self, line: &str) -> CanbusResult<()> {
611        // CM_ "Database comment";
612        // CM_ SG_ 2024 EngineSpeed "Engine speed in RPM";
613        // CM_ BO_ 2024 "Engine data message";
614        // CM_ BU_ ECU1 "Engine control unit";
615
616        let comment_start = line.find('"');
617        let comment_end = line.rfind('"');
618
619        if let (Some(start), Some(end)) = (comment_start, comment_end) {
620            if start < end {
621                let comment = line[start + 1..end].to_string();
622
623                if line.starts_with("CM_ SG_") {
624                    // Signal comment
625                    let parts: Vec<&str> = line[7..start].split_whitespace().collect();
626                    if parts.len() >= 2 {
627                        if let Ok(msg_id) = parts[0].parse::<u32>() {
628                            let sig_name = parts[1];
629                            if let Some(msg) =
630                                self.database.messages.iter_mut().find(|m| m.id == msg_id)
631                            {
632                                if let Some(sig) =
633                                    msg.signals.iter_mut().find(|s| s.name == sig_name)
634                                {
635                                    sig.comment = Some(comment);
636                                }
637                            }
638                        }
639                    }
640                } else if line.starts_with("CM_ BO_") {
641                    // Message comment
642                    let parts: Vec<&str> = line[7..start].split_whitespace().collect();
643                    if !parts.is_empty() {
644                        if let Ok(msg_id) = parts[0].parse::<u32>() {
645                            if let Some(msg) =
646                                self.database.messages.iter_mut().find(|m| m.id == msg_id)
647                            {
648                                msg.comment = Some(comment);
649                            }
650                        }
651                    }
652                } else if line.starts_with("CM_ BU_") {
653                    // Node comment
654                    let parts: Vec<&str> = line[7..start].split_whitespace().collect();
655                    if !parts.is_empty() {
656                        let node_name = parts[0];
657                        if let Some(node) =
658                            self.database.nodes.iter_mut().find(|n| n.name == node_name)
659                        {
660                            node.comment = Some(comment);
661                        }
662                    }
663                }
664            }
665        }
666
667        Ok(())
668    }
669
670    /// Parse BA_DEF_ (attribute definition)
671    fn parse_attribute_definition(&mut self, line: &str) -> CanbusResult<()> {
672        // BA_DEF_ BO_ "AttributeName" INT 0 100;
673        // BA_DEF_ SG_ "AttributeName" FLOAT 0.0 100.0;
674        // BA_DEF_ "DatabaseAttr" STRING;
675        // BA_DEF_ BO_ "GenMsgCycleTime" INT 0 10000;
676
677        let object_type = if line.contains(" BO_ ") {
678            AttributeObjectType::Message
679        } else if line.contains(" SG_ ") {
680            AttributeObjectType::Signal
681        } else if line.contains(" BU_ ") {
682            AttributeObjectType::Node
683        } else {
684            AttributeObjectType::Database
685        };
686
687        // Extract attribute name
688        let name_start = line.find('"');
689        let name_end = line[name_start.unwrap_or(0) + 1..].find('"');
690
691        if let (Some(start), Some(end)) = (name_start, name_end) {
692            let name = line[start + 1..start + 1 + end].to_string();
693            let remaining = &line[start + 2 + end..];
694
695            let value_type = self.parse_attribute_value_type(remaining)?;
696
697            self.database
698                .attribute_definitions
699                .push(AttributeDefinition {
700                    name,
701                    object_type,
702                    value_type,
703                    default_value: None,
704                });
705        }
706
707        Ok(())
708    }
709
710    /// Parse attribute value type
711    fn parse_attribute_value_type(&self, spec: &str) -> CanbusResult<AttributeValueType> {
712        let parts: Vec<&str> = spec.split_whitespace().collect();
713        if parts.is_empty() {
714            return Ok(AttributeValueType::String);
715        }
716
717        match parts[0] {
718            "INT" => {
719                let min = parts.get(1).and_then(|s| s.parse().ok()).unwrap_or(0);
720                let max = parts
721                    .get(2)
722                    .and_then(|s| s.trim_end_matches(';').parse().ok())
723                    .unwrap_or(0);
724                Ok(AttributeValueType::Int { min, max })
725            }
726            "FLOAT" | "HEX" => {
727                let min = parts.get(1).and_then(|s| s.parse().ok()).unwrap_or(0.0);
728                let max = parts
729                    .get(2)
730                    .and_then(|s| s.trim_end_matches(';').parse().ok())
731                    .unwrap_or(0.0);
732                Ok(AttributeValueType::Float { min, max })
733            }
734            "STRING" => Ok(AttributeValueType::String),
735            "ENUM" => {
736                let values: Vec<String> = parts[1..]
737                    .iter()
738                    .map(|s| {
739                        s.trim_matches(|c| c == '"' || c == ',' || c == ';')
740                            .to_string()
741                    })
742                    .filter(|s| !s.is_empty())
743                    .collect();
744                Ok(AttributeValueType::Enum { values })
745            }
746            _ => Ok(AttributeValueType::String),
747        }
748    }
749
750    /// Parse BA_DEF_DEF_ (attribute default value)
751    fn parse_attribute_default(&mut self, line: &str) -> CanbusResult<()> {
752        // BA_DEF_DEF_ "AttributeName" 100;
753        let name_start = line.find('"');
754        let name_end = line[name_start.unwrap_or(0) + 1..].find('"');
755
756        if let (Some(start), Some(end)) = (name_start, name_end) {
757            let name = &line[start + 1..start + 1 + end];
758            let remaining = line[start + 2 + end..].trim().trim_end_matches(';');
759
760            // First, find the value type without mutating
761            let value_type = self
762                .database
763                .attribute_definitions
764                .iter()
765                .find(|d| d.name == name)
766                .map(|d| d.value_type.clone());
767
768            // Now parse and update
769            if let Some(vt) = value_type {
770                let default_value = Self::parse_attribute_literal_static(remaining, &vt);
771                if let Some(def) = self
772                    .database
773                    .attribute_definitions
774                    .iter_mut()
775                    .find(|d| d.name == name)
776                {
777                    def.default_value = Some(default_value);
778                }
779            }
780        }
781
782        Ok(())
783    }
784
785    /// Parse attribute literal value (static version for borrow checker)
786    fn parse_attribute_literal_static(
787        value: &str,
788        value_type: &AttributeValueType,
789    ) -> AttributeValue {
790        let trimmed = value.trim().trim_matches('"');
791        match value_type {
792            AttributeValueType::Int { .. } => AttributeValue::Int(trimmed.parse().unwrap_or(0)),
793            AttributeValueType::Float { .. } => {
794                AttributeValue::Float(trimmed.parse().unwrap_or(0.0))
795            }
796            AttributeValueType::Enum { .. } => AttributeValue::Enum(trimmed.to_string()),
797            AttributeValueType::String => AttributeValue::String(trimmed.to_string()),
798        }
799    }
800
801    /// Parse BA_ (attribute value)
802    fn parse_attribute_value(&mut self, line: &str) -> CanbusResult<()> {
803        // BA_ "AttributeName" BO_ 2024 100;
804        // BA_ "AttributeName" SG_ 2024 SignalName 50;
805
806        let name_start = line.find('"');
807        let name_end = line[name_start.unwrap_or(0) + 1..].find('"');
808
809        if let (Some(start), Some(end)) = (name_start, name_end) {
810            let attr_name = line[start + 1..start + 1 + end].to_string();
811            let remaining = &line[start + 2 + end..];
812            let parts: Vec<&str> = remaining.split_whitespace().collect();
813
814            // Find attribute definition for type info
815            let value_type = self
816                .database
817                .attribute_definitions
818                .iter()
819                .find(|d| d.name == attr_name)
820                .map(|d| d.value_type.clone())
821                .unwrap_or(AttributeValueType::String);
822
823            if parts.len() >= 2 && parts[0] == "BO_" {
824                // Message attribute
825                if let Ok(msg_id) = parts[1].parse::<u32>() {
826                    if let Some(value_str) = parts.get(2) {
827                        let value = Self::parse_attribute_literal_static(
828                            value_str.trim_end_matches(';'),
829                            &value_type,
830                        );
831                        if let Some(msg) =
832                            self.database.messages.iter_mut().find(|m| m.id == msg_id)
833                        {
834                            msg.attributes.insert(attr_name, value);
835                        }
836                    }
837                }
838            } else if parts.len() >= 3 && parts[0] == "SG_" {
839                // Signal attribute - note: DbcSignal doesn't have attributes by default
840                // This is parsed but not stored currently
841                let _msg_id = parts[1].parse::<u32>().ok();
842                let _sig_name = parts[2];
843            }
844        }
845
846        Ok(())
847    }
848
849    /// Parse VAL_TABLE_ (standalone value table)
850    fn parse_value_table(&mut self, line: &str) -> CanbusResult<()> {
851        // VAL_TABLE_ TableName 0 "Value0" 1 "Value1" 2 "Value2";
852        let parts: Vec<&str> = line.split_whitespace().collect();
853        if parts.len() < 2 {
854            return Ok(());
855        }
856
857        let name = parts[1].to_string();
858        let mut values = HashMap::new();
859
860        let mut i = 2;
861        while i + 1 < parts.len() {
862            if let Ok(key) = parts[i].parse::<i64>() {
863                let value = parts[i + 1].trim_matches(|c| c == '"' || c == ';');
864                values.insert(key, value.to_string());
865                i += 2;
866            } else {
867                break;
868            }
869        }
870
871        self.database.value_tables.insert(name, values);
872        Ok(())
873    }
874
875    /// Parse VAL_ (value descriptions for signal)
876    fn parse_value_descriptions(&mut self, line: &str) -> CanbusResult<()> {
877        // VAL_ 2024 SignalName 0 "Off" 1 "On" 2 "Error";
878        let parts: Vec<&str> = line.split_whitespace().collect();
879        if parts.len() < 3 {
880            return Ok(());
881        }
882
883        let msg_id: u32 = match parts[1].parse() {
884            Ok(id) => id,
885            Err(_) => return Ok(()),
886        };
887
888        let sig_name = parts[2];
889        let mut values = HashMap::new();
890
891        let mut i = 3;
892        while i + 1 < parts.len() {
893            if let Ok(key) = parts[i].parse::<i64>() {
894                let value = parts[i + 1].trim_matches(|c| c == '"' || c == ';');
895                values.insert(key, value.to_string());
896                i += 2;
897            } else {
898                i += 1;
899            }
900        }
901
902        // Apply to signal
903        if let Some(msg) = self.database.messages.iter_mut().find(|m| m.id == msg_id) {
904            if let Some(sig) = msg.signals.iter_mut().find(|s| s.name == sig_name) {
905                sig.value_descriptions = values;
906            }
907        }
908
909        Ok(())
910    }
911
912    /// Create error with line number
913    fn error(&self, message: &str) -> CanbusError {
914        CanbusError::DbcParseError {
915            line: self.line_number,
916            message: message.to_string(),
917        }
918    }
919}
920
921impl Default for DbcParser {
922    fn default() -> Self {
923        Self::new()
924    }
925}
926
927/// Parse a DBC file from string content
928pub fn parse_dbc(content: &str) -> CanbusResult<DbcDatabase> {
929    DbcParser::new().parse(content)
930}
931
932/// Parse a DBC file from path
933pub fn parse_dbc_file(path: impl AsRef<std::path::Path>) -> CanbusResult<DbcDatabase> {
934    let content = std::fs::read_to_string(path.as_ref()).map_err(CanbusError::Io)?;
935    parse_dbc(&content)
936}
937
938#[cfg(test)]
939mod tests {
940    use super::*;
941
942    const TEST_DBC: &str = r#"
943VERSION ""
944
945NS_ :
946    NS_DESC_
947    CM_
948    BA_DEF_
949    BA_
950
951BS_:
952
953BU_: Engine Dashboard Transmission
954
955BO_ 2024 EngineData: 8 Engine
956 SG_ EngineSpeed : 0|16@1+ (0.125,0) [0|8031.875] "rpm" Dashboard
957 SG_ EngineTemp : 16|8@1+ (1,-40) [-40|215] "degC" Dashboard
958 SG_ ThrottlePos : 24|8@1+ (0.392157,0) [0|100] "%" Dashboard
959
960BO_ 2028 VehicleSpeed: 4 Transmission
961 SG_ Speed : 0|16@1+ (0.01,0) [0|655.35] "km/h" Dashboard
962
963CM_ BO_ 2024 "Engine data message containing RPM, temperature and throttle";
964CM_ SG_ 2024 EngineSpeed "Engine rotational speed in RPM";
965CM_ SG_ 2024 EngineTemp "Engine coolant temperature";
966CM_ BU_ Engine "Engine control unit";
967
968BA_DEF_ BO_ "GenMsgCycleTime" INT 0 10000;
969BA_DEF_DEF_ "GenMsgCycleTime" 100;
970BA_ "GenMsgCycleTime" BO_ 2024 50;
971
972VAL_ 2024 ThrottlePos 0 "Closed" 100 "WOT";
973"#;
974
975    #[test]
976    fn test_parse_dbc() {
977        let db = parse_dbc(TEST_DBC).unwrap();
978
979        assert_eq!(db.nodes.len(), 3);
980        assert_eq!(db.messages.len(), 2);
981    }
982
983    #[test]
984    fn test_parse_message() {
985        let db = parse_dbc(TEST_DBC).unwrap();
986
987        let engine_msg = db.get_message(2024).unwrap();
988        assert_eq!(engine_msg.name, "EngineData");
989        assert_eq!(engine_msg.dlc, 8);
990        assert_eq!(engine_msg.transmitter, "Engine");
991        assert_eq!(engine_msg.signals.len(), 3);
992    }
993
994    #[test]
995    fn test_parse_signal() {
996        let db = parse_dbc(TEST_DBC).unwrap();
997
998        let engine_msg = db.get_message(2024).unwrap();
999        let speed_sig = engine_msg.get_signal("EngineSpeed").unwrap();
1000
1001        assert_eq!(speed_sig.start_bit, 0);
1002        assert_eq!(speed_sig.bit_length, 16);
1003        assert_eq!(speed_sig.factor, 0.125);
1004        assert_eq!(speed_sig.offset, 0.0);
1005        assert_eq!(speed_sig.unit, "rpm");
1006        assert_eq!(speed_sig.byte_order, ByteOrder::LittleEndian);
1007        assert_eq!(speed_sig.value_type, ValueType::Unsigned);
1008    }
1009
1010    #[test]
1011    fn test_parse_signal_with_offset() {
1012        let db = parse_dbc(TEST_DBC).unwrap();
1013
1014        let engine_msg = db.get_message(2024).unwrap();
1015        let temp_sig = engine_msg.get_signal("EngineTemp").unwrap();
1016
1017        assert_eq!(temp_sig.start_bit, 16);
1018        assert_eq!(temp_sig.bit_length, 8);
1019        assert_eq!(temp_sig.factor, 1.0);
1020        assert_eq!(temp_sig.offset, -40.0);
1021        assert_eq!(temp_sig.min, -40.0);
1022        assert_eq!(temp_sig.max, 215.0);
1023    }
1024
1025    #[test]
1026    fn test_physical_value_conversion() {
1027        let db = parse_dbc(TEST_DBC).unwrap();
1028
1029        let engine_msg = db.get_message(2024).unwrap();
1030
1031        // Test engine speed: raw 16000 -> physical 2000 rpm
1032        let speed_sig = engine_msg.get_signal("EngineSpeed").unwrap();
1033        assert!((speed_sig.to_physical(16000) - 2000.0).abs() < 0.001);
1034
1035        // Test engine temp: raw 125 -> physical 85°C
1036        let temp_sig = engine_msg.get_signal("EngineTemp").unwrap();
1037        assert!((temp_sig.to_physical(125) - 85.0).abs() < 0.001);
1038    }
1039
1040    #[test]
1041    fn test_parse_comments() {
1042        let db = parse_dbc(TEST_DBC).unwrap();
1043
1044        let engine_msg = db.get_message(2024).unwrap();
1045        assert!(engine_msg.comment.is_some());
1046        assert!(engine_msg
1047            .comment
1048            .as_ref()
1049            .unwrap()
1050            .contains("Engine data message"));
1051
1052        let speed_sig = engine_msg.get_signal("EngineSpeed").unwrap();
1053        assert!(speed_sig.comment.is_some());
1054        assert!(speed_sig
1055            .comment
1056            .as_ref()
1057            .unwrap()
1058            .contains("rotational speed"));
1059    }
1060
1061    #[test]
1062    fn test_parse_value_descriptions() {
1063        let db = parse_dbc(TEST_DBC).unwrap();
1064
1065        let engine_msg = db.get_message(2024).unwrap();
1066        let throttle_sig = engine_msg.get_signal("ThrottlePos").unwrap();
1067
1068        assert_eq!(throttle_sig.get_value_description(0), Some("Closed"));
1069        assert_eq!(throttle_sig.get_value_description(100), Some("WOT"));
1070    }
1071
1072    #[test]
1073    fn test_parse_attributes() {
1074        let db = parse_dbc(TEST_DBC).unwrap();
1075
1076        assert_eq!(db.attribute_definitions.len(), 1);
1077        let attr_def = &db.attribute_definitions[0];
1078        assert_eq!(attr_def.name, "GenMsgCycleTime");
1079
1080        let engine_msg = db.get_message(2024).unwrap();
1081        assert!(engine_msg.attributes.contains_key("GenMsgCycleTime"));
1082    }
1083
1084    #[test]
1085    fn test_parse_nodes() {
1086        let db = parse_dbc(TEST_DBC).unwrap();
1087
1088        assert!(db.get_node("Engine").is_some());
1089        assert!(db.get_node("Dashboard").is_some());
1090        assert!(db.get_node("Transmission").is_some());
1091
1092        let engine_node = db.get_node("Engine").unwrap();
1093        assert!(engine_node.comment.is_some());
1094    }
1095
1096    #[test]
1097    fn test_vehicle_speed_message() {
1098        let db = parse_dbc(TEST_DBC).unwrap();
1099
1100        let speed_msg = db.get_message(2028).unwrap();
1101        assert_eq!(speed_msg.name, "VehicleSpeed");
1102        assert_eq!(speed_msg.dlc, 4);
1103
1104        let speed_sig = speed_msg.get_signal("Speed").unwrap();
1105        assert_eq!(speed_sig.factor, 0.01);
1106
1107        // Test: raw 10000 -> physical 100 km/h
1108        assert!((speed_sig.to_physical(10000) - 100.0).abs() < 0.001);
1109    }
1110
1111    #[test]
1112    fn test_byte_order_big_endian() {
1113        let dbc = r#"
1114BO_ 100 TestMsg: 8 ECU
1115 SG_ BigEndianSig : 7|16@0+ (1,0) [0|65535] "" Vector__XXX
1116"#;
1117        let db = parse_dbc(dbc).unwrap();
1118
1119        let msg = db.get_message(100).unwrap();
1120        let sig = msg.get_signal("BigEndianSig").unwrap();
1121        assert_eq!(sig.byte_order, ByteOrder::BigEndian);
1122    }
1123
1124    #[test]
1125    fn test_signed_signal() {
1126        let dbc = r#"
1127BO_ 100 TestMsg: 8 ECU
1128 SG_ SignedSig : 0|16@1- (1,0) [-32768|32767] "" Vector__XXX
1129"#;
1130        let db = parse_dbc(dbc).unwrap();
1131
1132        let msg = db.get_message(100).unwrap();
1133        let sig = msg.get_signal("SignedSig").unwrap();
1134        assert_eq!(sig.value_type, ValueType::Signed);
1135    }
1136
1137    #[test]
1138    fn test_all_signals() {
1139        let db = parse_dbc(TEST_DBC).unwrap();
1140
1141        let all_sigs: Vec<_> = db.all_signals().collect();
1142        assert_eq!(all_sigs.len(), 4); // 3 in EngineData + 1 in VehicleSpeed
1143    }
1144
1145    #[test]
1146    fn test_empty_dbc() {
1147        let dbc = "VERSION \"\"";
1148        let db = parse_dbc(dbc).unwrap();
1149        assert!(db.messages.is_empty());
1150    }
1151}