1use crate::{
2 Error, MAX_MESSAGES, MAX_SIGNALS_PER_MESSAGE, Message, MessageList, Nodes, Parser, Result,
3 Signal, Version, compat::Vec, error::lang,
4};
5#[cfg(feature = "std")]
6use crate::{ValueDescriptions, ValueDescriptionsList};
7#[cfg(feature = "std")]
8use std::collections::BTreeMap;
9
10#[derive(Debug)]
35pub struct Dbc {
36 version: Option<Version>,
37 nodes: Nodes,
38 messages: MessageList,
39 #[cfg(feature = "std")]
40 value_descriptions: ValueDescriptionsList,
41}
42
43impl Dbc {
44 #[cfg(feature = "std")]
46 pub(crate) fn validate(
47 nodes: &Nodes,
48 messages: &[Message],
49 value_descriptions: Option<&ValueDescriptionsList>,
50 ) -> Result<()> {
51 Self::validate_common(nodes, messages)?;
52
53 if let Some(value_descriptions) = value_descriptions {
55 for ((message_id_opt, signal_name), _) in value_descriptions.iter() {
57 if let Some(message_id) = message_id_opt {
59 let message_exists = messages.iter().any(|msg| msg.id() == message_id);
60 if !message_exists {
61 return Err(Error::Validation(lang::VALUE_DESCRIPTION_MESSAGE_NOT_FOUND));
62 }
63
64 let signal_exists = messages.iter().any(|msg| {
66 msg.id() == message_id && msg.signals().find(signal_name).is_some()
67 });
68 if !signal_exists {
69 return Err(Error::Validation(lang::VALUE_DESCRIPTION_SIGNAL_NOT_FOUND));
70 }
71 } else {
72 let signal_exists =
74 messages.iter().any(|msg| msg.signals().find(signal_name).is_some());
75 if !signal_exists {
76 return Err(Error::Validation(lang::VALUE_DESCRIPTION_SIGNAL_NOT_FOUND));
77 }
78 }
79 }
80 }
81
82 Ok(())
83 }
84
85 #[cfg(not(feature = "std"))]
87 pub(crate) fn validate(nodes: &Nodes, messages: &[Message]) -> Result<()> {
88 Self::validate_common(nodes, messages)
89 }
90
91 fn validate_common(nodes: &Nodes, messages: &[Message]) -> Result<()> {
93 for (i, msg1) in messages.iter().enumerate() {
95 for msg2 in messages.iter().skip(i + 1) {
96 if msg1.id() == msg2.id() {
97 return Err(Error::Validation(lang::DUPLICATE_MESSAGE_ID));
98 }
99 }
100 }
101
102 if !nodes.is_empty() {
105 for msg in messages {
106 if !nodes.contains(msg.sender()) {
107 return Err(Error::Validation(lang::SENDER_NOT_IN_NODES));
108 }
109 }
110 }
111
112 Ok(())
113 }
114
115 #[cfg(feature = "std")]
116 pub(crate) fn new(
117 version: Option<Version>,
118 nodes: Nodes,
119 messages: MessageList,
120 value_descriptions: ValueDescriptionsList,
121 ) -> Self {
122 Self {
124 version,
125 nodes,
126 messages,
127 value_descriptions,
128 }
129 }
130
131 #[inline]
146 #[must_use]
147 pub fn version(&self) -> Option<&Version> {
148 self.version.as_ref()
149 }
150
151 #[inline]
169 #[must_use]
170 pub fn nodes(&self) -> &Nodes {
171 &self.nodes
172 }
173
174 #[inline]
190 #[must_use]
191 pub fn messages(&self) -> &MessageList {
192 &self.messages
193 }
194
195 #[cfg(feature = "std")]
238 #[inline]
239 #[must_use]
240 pub fn value_descriptions(&self) -> &ValueDescriptionsList {
241 &self.value_descriptions
242 }
243
244 #[cfg(feature = "std")]
245 #[must_use]
246 pub fn value_descriptions_for_signal(
247 &self,
248 message_id: u32,
249 signal_name: &str,
250 ) -> Option<&ValueDescriptions> {
251 self.value_descriptions.for_signal(message_id, signal_name)
252 }
253
254 pub fn parse(data: &str) -> Result<Self> {
273 let mut parser = Parser::new(data.as_bytes())?;
274
275 let mut messages_buffer: Vec<Message, { MAX_MESSAGES }> = Vec::new();
276
277 let mut message_count_actual = 0;
278
279 use crate::{
281 BA_, BA_DEF_, BA_DEF_DEF_, BO_, BO_TX_BU_, BS_, BU_, CM_, EV_, NS_, SG_, SIG_GROUP_,
282 SIG_VALTYPE_, VAL_, VAL_TABLE_, VERSION,
283 };
284
285 let mut version: Option<Version> = None;
286 let mut nodes: Option<Nodes> = None;
287
288 #[cfg(feature = "std")]
290 type ValueDescriptionsBufferEntry = (
291 Option<u32>,
292 std::string::String,
293 std::vec::Vec<(u64, std::string::String)>,
294 );
295 #[cfg(feature = "std")]
296 let mut value_descriptions_buffer: std::vec::Vec<ValueDescriptionsBufferEntry> =
297 std::vec::Vec::new();
298
299 loop {
300 parser.skip_newlines_and_spaces();
302 if parser.starts_with(b"//") {
303 parser.skip_to_end_of_line();
304 continue;
305 }
306
307 let keyword_result = parser.peek_next_keyword();
308 let keyword = match keyword_result {
309 Ok(kw) => kw,
310 Err(Error::UnexpectedEof) => break,
311 Err(Error::Expected(_)) => {
312 if parser.starts_with(b"//") {
313 parser.skip_to_end_of_line();
314 continue;
315 }
316 return Err(keyword_result.unwrap_err());
317 }
318 Err(e) => return Err(e),
319 };
320
321 let pos_at_keyword = parser.pos();
323
324 match keyword {
325 NS_ => {
326 parser
328 .expect(crate::NS_.as_bytes())
329 .map_err(|_| Error::Expected("Failed to consume NS_ keyword"))?;
330 parser.skip_newlines_and_spaces();
331 let _ = parser.expect(b":").ok();
332 loop {
333 parser.skip_newlines_and_spaces();
334 if parser.is_empty() {
335 break;
336 }
337 if parser.starts_with(b" ") || parser.starts_with(b"\t") {
338 parser.skip_to_end_of_line();
339 continue;
340 }
341 if parser.starts_with(b"//") {
342 parser.skip_to_end_of_line();
343 continue;
344 }
345 if parser.starts_with(BS_.as_bytes())
346 || parser.starts_with(BU_.as_bytes())
347 || parser.starts_with(BO_.as_bytes())
348 || parser.starts_with(SG_.as_bytes())
349 || parser.starts_with(VERSION.as_bytes())
350 {
351 break;
352 }
353 parser.skip_to_end_of_line();
354 }
355 continue;
356 }
357 CM_ | BS_ | VAL_TABLE_ | BA_DEF_ | BA_DEF_DEF_ | BA_ | SIG_GROUP_
358 | SIG_VALTYPE_ | EV_ | BO_TX_BU_ => {
359 let _ = parser.expect(keyword.as_bytes()).ok();
361 parser.skip_to_end_of_line();
362 continue;
363 }
364 VAL_ => {
365 #[cfg(feature = "std")]
366 {
367 let _ = parser.expect(crate::VAL_.as_bytes()).ok();
369 parser.skip_newlines_and_spaces();
373 let message_id = match parser.parse_i64() {
374 Ok(id) => {
375 if id == -1 {
377 None
378 } else if id >= 0 && id <= u32::MAX as i64 {
379 Some(id as u32)
380 } else {
381 parser.skip_to_end_of_line();
382 continue;
383 }
384 }
385 Err(_) => {
386 parser.skip_to_end_of_line();
387 continue;
388 }
389 };
390 parser.skip_newlines_and_spaces();
391 let signal_name = match parser.parse_identifier() {
392 Ok(name) => name.to_string(),
393 Err(_) => {
394 parser.skip_to_end_of_line();
395 continue;
396 }
397 };
398 let mut entries: std::vec::Vec<(u64, std::string::String)> =
400 std::vec::Vec::new();
401 loop {
402 parser.skip_newlines_and_spaces();
403 if parser.starts_with(b";") {
405 parser.expect(b";").ok();
406 break;
407 }
408 let value = match parser.parse_i64() {
412 Ok(v) => {
413 if v == -1 { 0xFFFF_FFFFu64 } else { v as u64 }
415 }
416 Err(_) => {
417 parser.skip_to_end_of_line();
418 break;
419 }
420 };
421 parser.skip_newlines_and_spaces();
422 if parser.expect(b"\"").is_err() {
424 parser.skip_to_end_of_line();
425 break;
426 }
427 let description_bytes = match parser.take_until_quote(false, 1024) {
428 Ok(bytes) => bytes,
429 Err(_) => {
430 parser.skip_to_end_of_line();
431 break;
432 }
433 };
434 let description = match core::str::from_utf8(description_bytes) {
435 Ok(s) => s.to_string(),
436 Err(_) => {
437 parser.skip_to_end_of_line();
438 break;
439 }
440 };
441 entries.push((value, description));
442 }
443 if !entries.is_empty() {
444 value_descriptions_buffer.push((message_id, signal_name, entries));
445 }
446 }
447 #[cfg(not(feature = "std"))]
448 {
449 let _ = parser.expect(crate::VAL_.as_bytes()).ok();
451 parser.skip_to_end_of_line();
452 }
453 continue;
454 }
455 VERSION => {
456 version = Some(Version::parse(&mut parser)?);
458 continue;
459 }
460 BU_ => {
461 parser.skip_to_end_of_line();
463 let bu_input = &data.as_bytes()[pos_at_keyword..parser.pos()];
464 let mut bu_parser = Parser::new(bu_input)?;
465 nodes = Some(Nodes::parse(&mut bu_parser)?);
466 continue;
467 }
468 BO_ => {
469 if message_count_actual >= MAX_MESSAGES {
471 return Err(Error::Nodes(lang::NODES_TOO_MANY));
472 }
473
474 let message_start_pos = pos_at_keyword;
476
477 let header_line_end = {
480 let mut temp_parser = Parser::new(&data.as_bytes()[pos_at_keyword..])?;
482 temp_parser.expect(crate::BO_.as_bytes()).ok();
484 temp_parser.skip_whitespace().ok();
485 temp_parser.parse_u32().ok(); temp_parser.skip_whitespace().ok();
487 temp_parser.parse_identifier().ok(); temp_parser.skip_whitespace().ok();
489 temp_parser.expect(b":").ok(); temp_parser.skip_whitespace().ok();
491 temp_parser.parse_u8().ok(); temp_parser.skip_whitespace().ok();
493 temp_parser.parse_identifier().ok(); pos_at_keyword + temp_parser.pos()
495 };
496
497 parser.skip_to_end_of_line(); let mut signals_array: Vec<Signal, { MAX_SIGNALS_PER_MESSAGE }> = Vec::new();
501
502 loop {
503 parser.skip_newlines_and_spaces();
504 if parser.starts_with(crate::SG_.as_bytes()) {
505 if let Some(next_byte) = parser.peek_byte_at(3) {
506 if matches!(next_byte, b' ' | b'\n' | b'\r' | b'\t') {
507 if signals_array.len() >= MAX_SIGNALS_PER_MESSAGE {
508 return Err(Error::Receivers(
509 lang::SIGNAL_RECEIVERS_TOO_MANY,
510 ));
511 }
512 let signal = Signal::parse(&mut parser)?;
514 signals_array.push(signal).map_err(|_| {
515 Error::Receivers(lang::SIGNAL_RECEIVERS_TOO_MANY)
516 })?;
517 continue;
518 }
519 }
520 }
521 break;
522 }
523
524 let message_input = &data.as_bytes()[message_start_pos..header_line_end];
528 let mut message_parser = Parser::new(message_input)?;
529
530 let message = Message::parse(&mut message_parser, signals_array.as_slice())?;
532
533 messages_buffer
534 .push(message)
535 .map_err(|_| Error::Message(lang::NODES_TOO_MANY))?;
536 message_count_actual += 1;
537 continue;
538 }
539 SG_ => {
540 parser.skip_to_end_of_line();
542 continue;
543 }
544 _ => {
545 parser.skip_to_end_of_line();
546 continue;
547 }
548 }
549 }
550
551 let nodes = nodes.unwrap_or_default();
553
554 let version = version.or_else(|| {
556 static EMPTY_VERSION: &[u8] = b"VERSION \"\"";
557 let mut parser = Parser::new(EMPTY_VERSION).ok()?;
558 Version::parse(&mut parser).ok()
559 });
560
561 #[cfg(feature = "std")]
563 let value_descriptions_list = {
564 let mut map: BTreeMap<(Option<u32>, std::string::String), ValueDescriptions> =
565 BTreeMap::new();
566 for (message_id, signal_name, entries) in value_descriptions_buffer {
567 let key = (message_id, signal_name);
568 let value_descriptions = ValueDescriptions::from_slice(&entries);
569 map.insert(key, value_descriptions);
570 }
571 ValueDescriptionsList::from_map(map)
572 };
573
574 let messages_slice: &[Message] = messages_buffer.as_slice();
576
577 #[cfg(feature = "std")]
579 Self::validate(&nodes, messages_slice, Some(&value_descriptions_list)).map_err(|e| {
580 crate::error::map_val_error(e, Error::Message, || {
581 Error::Message(crate::error::lang::MESSAGE_ERROR_PREFIX)
582 })
583 })?;
584 #[cfg(not(feature = "std"))]
585 Self::validate(&nodes, messages_slice).map_err(|e| {
586 crate::error::map_val_error(e, Error::Message, || {
587 Error::Message(crate::error::lang::MESSAGE_ERROR_PREFIX)
588 })
589 })?;
590
591 let messages = MessageList::new(messages_slice)?;
593 Ok(Self {
594 version,
595 nodes,
596 messages,
597 #[cfg(feature = "std")]
598 value_descriptions: value_descriptions_list,
599 })
600 }
601
602 pub fn parse_bytes(data: &[u8]) -> Result<Dbc> {
615 let content =
616 core::str::from_utf8(data).map_err(|_e| Error::Expected(lang::INVALID_UTF8))?;
617 Dbc::parse(content)
618 }
619
620 #[cfg(feature = "std")]
634 #[must_use]
635 pub fn to_dbc_string(&self) -> String {
636 let signal_count: usize = self.messages().iter().map(|m| m.signals().len()).sum();
639 let estimated_capacity = 200 + (self.messages.len() * 50) + (signal_count * 100);
640 let mut result = String::with_capacity(estimated_capacity);
641
642 if let Some(version) = &self.version {
644 result.push_str(&version.to_dbc_string());
645 result.push_str("\n\n");
646 }
647
648 result.push_str(&self.nodes.to_dbc_string());
650 result.push('\n');
651
652 for message in self.messages().iter() {
654 result.push('\n');
655 result.push_str(&message.to_string_full());
656 }
657
658 result
659 }
660}
661
662#[cfg(feature = "std")]
663impl core::fmt::Display for Dbc {
664 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
665 write!(f, "{}", self.to_dbc_string())
666 }
667}
668
669#[cfg(all(test, feature = "std"))]
670mod tests {
671 #![allow(clippy::float_cmp)]
672 use super::*;
673 use crate::{Error, error::lang};
674
675 #[test]
676 fn parses_real_dbc() {
677 let data = r#"VERSION "1.0"
678
679BU_: ECM TCM
680
681BO_ 256 Engine : 8 ECM
682 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
683 SG_ Temp : 16|8@0- (1,-40) [-40|215] "°C"
684
685BO_ 512 Brake : 4 TCM
686 SG_ Pressure : 0|16@1+ (0.1,0) [0|1000] "bar""#;
687
688 let dbc = Dbc::parse(data).unwrap();
689 assert_eq!(dbc.messages().len(), 2);
690 let mut messages_iter = dbc.messages().iter();
691 let msg0 = messages_iter.next().unwrap();
692 assert_eq!(msg0.signals().len(), 2);
693 let mut signals_iter = msg0.signals().iter();
694 assert_eq!(signals_iter.next().unwrap().name(), "RPM");
695 assert_eq!(signals_iter.next().unwrap().name(), "Temp");
696 let msg1 = messages_iter.next().unwrap();
697 assert_eq!(msg1.signals().len(), 1);
698 assert_eq!(msg1.signals().iter().next().unwrap().name(), "Pressure");
699 }
700
701 #[test]
702 fn test_parse_duplicate_message_id() {
703 let data = r#"VERSION "1.0"
705
706BU_: ECM
707
708BO_ 256 EngineData1 : 8 ECM
709 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
710
711BO_ 256 EngineData2 : 8 ECM
712 SG_ Temp : 16|8@0- (1,-40) [-40|215] "°C"
713"#;
714
715 let result = Dbc::parse(data);
716 assert!(result.is_err());
717 match result.unwrap_err() {
718 Error::Message(msg) => {
719 assert!(msg.contains(lang::DUPLICATE_MESSAGE_ID));
720 }
721 _ => panic!("Expected Error::Message"),
722 }
723 }
724
725 #[test]
726 fn test_parse_sender_not_in_nodes() {
727 let data = r#"VERSION "1.0"
729
730BU_: ECM
731
732BO_ 256 EngineData : 8 TCM
733 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
734"#;
735
736 let result = Dbc::parse(data);
737 assert!(result.is_err());
738 match result.unwrap_err() {
739 Error::Message(msg) => {
740 assert!(msg.contains(lang::SENDER_NOT_IN_NODES));
741 }
742 _ => panic!("Expected Error::Message"),
743 }
744 }
745
746 #[test]
747 fn test_parse_empty_file() {
748 let result = Dbc::parse("");
750 assert!(result.is_err());
751 match result.unwrap_err() {
752 Error::UnexpectedEof => {
753 }
755 _ => panic!("Expected Error::UnexpectedEof"),
756 }
757 }
758
759 #[test]
760 fn test_parse_missing_nodes() {
761 let data = r#"VERSION "1.0"
765
766BO_ 256 EngineData : 8 ECM
767 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
768"#;
769
770 let result = Dbc::parse(data);
771 assert!(result.is_ok());
773 let dbc = result.unwrap();
774 assert!(dbc.nodes().is_empty());
775 }
776
777 #[test]
778 fn test_parse_bytes() {
779 let data = r#"VERSION "1.0"
780
781BU_: ECM
782
783BO_ 256 Engine : 8 ECM
784 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
785"#;
786
787 let bytes = data.as_bytes();
788 let dbc = Dbc::parse_bytes(bytes).unwrap();
789 assert_eq!(
790 dbc.version().map(|v| v.to_string()),
791 Some("1.0".to_string())
792 );
793 assert_eq!(dbc.messages().len(), 1);
794 }
795
796 #[test]
797 fn test_parse_bytes_invalid_utf8() {
798 let invalid_bytes = &[0xFF, 0xFE, 0xFD];
800 let result = Dbc::parse_bytes(invalid_bytes);
801 assert!(result.is_err());
802 match result.unwrap_err() {
803 Error::Expected(msg) => {
804 assert_eq!(msg, lang::INVALID_UTF8);
805 }
806 _ => panic!("Expected Error::Expected with INVALID_UTF8"),
807 }
808 }
809
810 #[test]
811 fn test_save_round_trip() {
812 let original = r#"VERSION "1.0"
813
814BU_: ECM TCM
815
816BO_ 256 EngineData : 8 ECM
817 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
818 SG_ Temperature : 16|8@0- (1,-40) [-40|215] "°C" TCM
819
820BO_ 512 BrakeData : 4 TCM
821 SG_ Pressure : 0|16@0+ (0.1,0) [0|1000] "bar"
822"#;
823
824 let dbc = Dbc::parse(original).unwrap();
825 let saved = dbc.to_dbc_string();
826 let dbc2 = Dbc::parse(&saved).unwrap();
827
828 assert_eq!(
830 dbc.version().map(|v| v.to_string()),
831 dbc2.version().map(|v| v.to_string())
832 );
833 assert_eq!(dbc.messages().len(), dbc2.messages().len());
834
835 for (msg1, msg2) in dbc.messages().iter().zip(dbc2.messages().iter()) {
836 assert_eq!(msg1.id(), msg2.id());
837 assert_eq!(msg1.name(), msg2.name());
838 assert_eq!(msg1.dlc(), msg2.dlc());
839 assert_eq!(msg1.sender(), msg2.sender());
840 assert_eq!(msg1.signals().len(), msg2.signals().len());
841
842 for (sig1, sig2) in msg1.signals().iter().zip(msg2.signals().iter()) {
843 assert_eq!(sig1.name(), sig2.name());
844 assert_eq!(sig1.start_bit(), sig2.start_bit());
845 assert_eq!(sig1.length(), sig2.length());
846 assert_eq!(sig1.byte_order(), sig2.byte_order());
847 assert_eq!(sig1.is_unsigned(), sig2.is_unsigned());
848 assert_eq!(sig1.factor(), sig2.factor());
849 assert_eq!(sig1.offset(), sig2.offset());
850 assert_eq!(sig1.min(), sig2.min());
851 assert_eq!(sig1.max(), sig2.max());
852 assert_eq!(sig1.unit(), sig2.unit());
853 assert_eq!(sig1.receivers(), sig2.receivers());
854 }
855 }
856 }
857
858 #[test]
859 #[cfg(feature = "std")]
860 fn test_save_multiple_messages() {
861 let dbc_content = r#"VERSION "1.0"
863
864BU_: ECM TCM
865
866BO_ 256 EngineData : 8 ECM
867 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm" *
868
869BO_ 512 BrakeData : 4 TCM
870 SG_ Pressure : 0|16@1+ (0.1,0) [0|1000] "bar"
871"#;
872 let dbc = Dbc::parse(dbc_content).unwrap();
873 let saved = dbc.to_dbc_string();
874
875 assert!(saved.contains("BO_ 256 EngineData : 8 ECM"));
877 assert!(saved.contains("BO_ 512 BrakeData : 4 TCM"));
878 assert!(saved.contains("SG_ RPM"));
879 assert!(saved.contains("SG_ Pressure"));
880 }
881
882 #[test]
886 fn test_parse_without_version() {
887 let data = r#"
889BU_: ECM
890
891BO_ 256 Engine : 8 ECM
892 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
893"#;
894 let dbc = Dbc::parse(data).unwrap();
895 assert_eq!(dbc.version().map(|v| v.to_string()), Some("".to_string()));
896 }
897
898 #[test]
899 fn test_parse_without_version_with_comment() {
900 let data = r#"// This is a comment
902BU_: ECM
903
904BO_ 256 Engine : 8 ECM
905 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
906"#;
907 let dbc = Dbc::parse(data).unwrap();
908 assert_eq!(dbc.version().map(|v| v.to_string()), Some("".to_string()));
909 }
910
911 #[test]
912 fn test_parse_with_strict_boundary_check() {
913 let data = r#"VERSION "1.0"
915
916BU_: ECM
917
918BO_ 256 Test : 8 ECM
919 SG_ CHECKSUM : 63|8@1+ (1,0) [0|255] ""
920"#;
921
922 let result = Dbc::parse(data);
924 assert!(result.is_err());
925
926 let result = Dbc::parse(data);
928 assert!(result.is_err());
929 }
930
931 #[cfg(feature = "std")]
933 mod tests_std {
934 use super::*;
935
936 #[test]
937 fn test_parse_from_string() {
938 let data = String::from(
939 r#"VERSION "1.0"
940
941BU_: ECM
942
943BO_ 256 Engine : 8 ECM
944 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm"
945"#,
946 );
947
948 let dbc = Dbc::parse(&data).unwrap();
949 assert_eq!(
950 dbc.version().map(|v| v.to_string()),
951 Some("1.0".to_string())
952 );
953 assert_eq!(dbc.messages().len(), 1);
954 }
955
956 #[test]
957 fn test_save_basic() {
958 let dbc_content = r#"VERSION "1.0"
960
961BU_: ECM
962
963BO_ 256 EngineData : 8 ECM
964 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm" *
965"#;
966 let dbc = Dbc::parse(dbc_content).unwrap();
967
968 let saved = dbc.to_dbc_string();
969 assert!(saved.contains("VERSION \"1.0\""));
970 assert!(saved.contains("BU_: ECM"));
971 assert!(saved.contains("BO_ 256 EngineData : 8 ECM"));
972 assert!(saved.contains("SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\" *")); }
974
975 #[test]
976 fn test_save_round_trip() {
977 let original = r#"VERSION "1.0"
978
979BU_: ECM TCM
980
981BO_ 256 EngineData : 8 ECM
982 SG_ RPM : 0|16@1+ (0.25,0) [0|8000] "rpm" *
983 SG_ Temperature : 16|8@0- (1,-40) [-40|215] "°C" TCM
984
985BO_ 512 BrakeData : 4 TCM
986 SG_ Pressure : 0|16@0+ (0.1,0) [0|1000] "bar"
987"#;
988
989 let dbc = Dbc::parse(original).unwrap();
990 let saved = dbc.to_dbc_string();
991 let dbc2 = Dbc::parse(&saved).unwrap();
992
993 assert_eq!(
995 dbc.version().map(|v| v.to_string()),
996 dbc2.version().map(|v| v.to_string())
997 );
998 assert_eq!(dbc.messages().len(), dbc2.messages().len());
999
1000 for (msg1, msg2) in dbc.messages().iter().zip(dbc2.messages().iter()) {
1001 assert_eq!(msg1.id(), msg2.id());
1002 assert_eq!(msg1.name(), msg2.name());
1003 assert_eq!(msg1.dlc(), msg2.dlc());
1004 assert_eq!(msg1.sender(), msg2.sender());
1005 assert_eq!(msg1.signals().len(), msg2.signals().len());
1006
1007 for (sig1, sig2) in msg1.signals().iter().zip(msg2.signals().iter()) {
1008 assert_eq!(sig1.name(), sig2.name());
1009 assert_eq!(sig1.start_bit(), sig2.start_bit());
1010 assert_eq!(sig1.length(), sig2.length());
1011 assert_eq!(sig1.byte_order(), sig2.byte_order());
1012 assert_eq!(sig1.is_unsigned(), sig2.is_unsigned());
1013 assert_eq!(sig1.factor(), sig2.factor());
1014 assert_eq!(sig1.offset(), sig2.offset());
1015 assert_eq!(sig1.min(), sig2.min());
1016 assert_eq!(sig1.max(), sig2.max());
1017 assert_eq!(sig1.unit(), sig2.unit());
1018 assert_eq!(sig1.receivers(), sig2.receivers());
1019 }
1020 }
1021 }
1022
1023 #[test]
1024 fn test_save_multiple_messages() {
1025 let dbc_content = r#"VERSION "1.0"
1027
1028BU_: ECM TCM
1029
1030BO_ 256 EngineData : 8 ECM
1031 SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm"
1032
1033BO_ 512 BrakeData : 4 TCM
1034 SG_ Pressure : 0|16@1+ (0.1,0) [0|1000] "bar"
1035"#;
1036 let dbc = Dbc::parse(dbc_content).unwrap();
1037 let saved = dbc.to_dbc_string();
1038
1039 assert!(saved.contains("BO_ 256 EngineData : 8 ECM"));
1041 assert!(saved.contains("BO_ 512 BrakeData : 4 TCM"));
1042 assert!(saved.contains("SG_ RPM"));
1043 assert!(saved.contains("SG_ Pressure"));
1044 }
1045
1046 #[test]
1047 fn test_parse_val_value_descriptions() {
1048 let data = r#"VERSION ""
1049
1050NS_ :
1051
1052BS_:
1053
1054BU_: Node1 Node2
1055
1056BO_ 100 Message1 : 8 Node1
1057 SG_ Signal : 32|8@1- (1,0) [-1|4] "Gear" Node2
1058
1059VAL_ 100 Signal -1 "Reverse" 0 "Neutral" 1 "First" 2 "Second" 3 "Third" 4 "Fourth" ;
1060"#;
1061
1062 let dbc = match Dbc::parse(data) {
1063 Ok(dbc) => dbc,
1064 Err(e) => panic!("Failed to parse DBC: {:?}", e),
1065 };
1066
1067 assert_eq!(dbc.messages().len(), 1);
1069 let message = dbc.messages().iter().find(|m| m.id() == 100).unwrap();
1070 assert_eq!(message.name(), "Message1");
1071 assert_eq!(message.sender(), "Node1");
1072
1073 let signal = message.signals().find("Signal").unwrap();
1075 assert_eq!(signal.name(), "Signal");
1076 assert_eq!(signal.start_bit(), 32);
1077 assert_eq!(signal.length(), 8);
1078 assert_eq!(signal.unit(), Some("Gear"));
1079
1080 let value_descriptions = dbc
1082 .value_descriptions_for_signal(100, "Signal")
1083 .expect("Value descriptions should exist for signal");
1084
1085 assert_eq!(value_descriptions.get(0xFFFFFFFF), Some("Reverse")); assert_eq!(value_descriptions.get(0), Some("Neutral"));
1088 assert_eq!(value_descriptions.get(1), Some("First"));
1089 assert_eq!(value_descriptions.get(2), Some("Second"));
1090 assert_eq!(value_descriptions.get(3), Some("Third"));
1091 assert_eq!(value_descriptions.get(4), Some("Fourth"));
1092
1093 assert_eq!(value_descriptions.get(5), None);
1095 assert_eq!(value_descriptions.get(99), None);
1096
1097 let iter = value_descriptions.iter();
1099 let mut entries: std::vec::Vec<_> = iter.collect();
1100 entries.sort_by_key(|(v, _)| *v);
1101
1102 assert_eq!(entries.len(), 6);
1103 assert_eq!(entries[0], (0, "Neutral".to_string()));
1105 assert_eq!(entries[1], (1, "First".to_string()));
1106 assert_eq!(entries[2], (2, "Second".to_string()));
1107 assert_eq!(entries[3], (3, "Third".to_string()));
1108 assert_eq!(entries[4], (4, "Fourth".to_string()));
1109 assert_eq!(entries[5], (0xFFFFFFFF, "Reverse".to_string()));
1110 }
1111
1112 #[test]
1113 fn test_parse_val_global_value_descriptions() {
1114 let data = r#"VERSION "1.0"
1116
1117NS_ :
1118
1119 VAL_
1120
1121BS_:
1122
1123BU_: ECU DASH
1124
1125BO_ 256 EngineData: 8 ECU
1126 SG_ EngineRPM : 0|16@1+ (0.125,0) [0|8000] "rpm" Vector__XXX
1127 SG_ DI_gear : 24|3@1+ (1,0) [0|7] "" Vector__XXX
1128
1129BO_ 512 DashboardDisplay: 8 DASH
1130 SG_ DI_gear : 0|3@1+ (1,0) [0|7] "" Vector__XXX
1131 SG_ SpeedDisplay : 8|16@1+ (0.01,0) [0|300] "km/h" Vector__XXX
1132
1133VAL_ -1 DI_gear 0 "INVALID" 1 "P" 2 "R" 3 "N" 4 "D" 5 "S" 6 "L" 7 "SNA" ;
1134"#;
1135
1136 let dbc = match Dbc::parse(data) {
1137 Ok(dbc) => dbc,
1138 Err(e) => panic!("Failed to parse DBC: {:?}", e),
1139 };
1140
1141 assert_eq!(dbc.messages().len(), 2);
1143
1144 let engine_msg = dbc.messages().iter().find(|m| m.id() == 256).unwrap();
1146 assert_eq!(engine_msg.name(), "EngineData");
1147 assert_eq!(engine_msg.sender(), "ECU");
1148 let di_gear_signal1 = engine_msg.signals().find("DI_gear").unwrap();
1149 assert_eq!(di_gear_signal1.name(), "DI_gear");
1150 assert_eq!(di_gear_signal1.start_bit(), 24);
1151
1152 let dash_msg = dbc.messages().iter().find(|m| m.id() == 512).unwrap();
1154 assert_eq!(dash_msg.name(), "DashboardDisplay");
1155 assert_eq!(dash_msg.sender(), "DASH");
1156 let di_gear_signal2 = dash_msg.signals().find("DI_gear").unwrap();
1157 assert_eq!(di_gear_signal2.name(), "DI_gear");
1158 assert_eq!(di_gear_signal2.start_bit(), 0);
1159
1160 let value_descriptions1 = dbc
1162 .value_descriptions_for_signal(256, "DI_gear")
1163 .expect("Global value descriptions should exist for DI_gear in message 256");
1164
1165 assert_eq!(value_descriptions1.get(0), Some("INVALID"));
1166 assert_eq!(value_descriptions1.get(1), Some("P"));
1167 assert_eq!(value_descriptions1.get(2), Some("R"));
1168 assert_eq!(value_descriptions1.get(3), Some("N"));
1169 assert_eq!(value_descriptions1.get(4), Some("D"));
1170 assert_eq!(value_descriptions1.get(5), Some("S"));
1171 assert_eq!(value_descriptions1.get(6), Some("L"));
1172 assert_eq!(value_descriptions1.get(7), Some("SNA"));
1173
1174 let value_descriptions2 = dbc
1176 .value_descriptions_for_signal(512, "DI_gear")
1177 .expect("Global value descriptions should exist for DI_gear in message 512");
1178
1179 assert_eq!(value_descriptions2.get(0), Some("INVALID"));
1181 assert_eq!(value_descriptions2.get(1), Some("P"));
1182 assert_eq!(value_descriptions2.get(2), Some("R"));
1183 assert_eq!(value_descriptions2.get(3), Some("N"));
1184 assert_eq!(value_descriptions2.get(4), Some("D"));
1185 assert_eq!(value_descriptions2.get(5), Some("S"));
1186 assert_eq!(value_descriptions2.get(6), Some("L"));
1187 assert_eq!(value_descriptions2.get(7), Some("SNA"));
1188
1189 assert_eq!(value_descriptions1.len(), value_descriptions2.len());
1192 assert_eq!(value_descriptions1.len(), 8);
1193
1194 assert_eq!(dbc.value_descriptions_for_signal(256, "EngineRPM"), None);
1196 assert_eq!(dbc.value_descriptions_for_signal(512, "SpeedDisplay"), None);
1197 }
1198 }
1199}