1use crate::{
2 ByteOrder, Parser, Receivers,
3 error::{ParseError, ParseResult},
4};
5
6#[derive(Debug, Clone, PartialEq)]
7pub struct Signal<'a> {
8 name: &'a str,
9 start_bit: u16,
10 length: u16,
11 byte_order: ByteOrder,
12 unsigned: bool,
13 factor: f64,
14 offset: f64,
15 min: f64,
16 max: f64,
17 unit: Option<&'a str>,
18 receivers: Receivers<'a>,
19}
20
21impl<'a> Signal<'a> {
22 pub(crate) fn validate(name: &str, length: u16, min: f64, max: f64) -> ParseResult<()> {
23 if name.trim().is_empty() {
24 return Err(ParseError::Version(crate::error::lang::SIGNAL_NAME_EMPTY));
25 }
26
27 if length == 0 {
33 #[cfg(feature = "alloc")]
34 {
35 use crate::error::messages;
36 let msg = messages::signal_length_too_small(name, length);
37 return Err(ParseError::Version(Box::leak(msg.into_boxed_str())));
38 }
39 #[cfg(not(feature = "alloc"))]
40 {
41 return Err(ParseError::Version(
42 crate::error::lang::SIGNAL_LENGTH_TOO_SMALL,
43 ));
44 }
45 }
46 if length > 512 {
47 #[cfg(feature = "alloc")]
48 {
49 use crate::error::messages;
50 let msg = messages::signal_length_too_large(name, length);
51 return Err(ParseError::Version(Box::leak(msg.into_boxed_str())));
52 }
53 #[cfg(not(feature = "alloc"))]
54 {
55 return Err(ParseError::Version(
56 crate::error::lang::SIGNAL_LENGTH_TOO_LARGE,
57 ));
58 }
59 }
60
61 if min > max {
69 return Err(ParseError::Version(
71 crate::error::lang::FORMAT_INVALID_RANGE,
72 ));
73 }
74
75 Ok(())
76 }
77
78 #[allow(clippy::too_many_arguments)] #[allow(dead_code)] pub(crate) fn new(
81 name: &'a str,
82 start_bit: u16,
83 length: u16,
84 byte_order: ByteOrder,
85 unsigned: bool,
86 factor: f64,
87 offset: f64,
88 min: f64,
89 max: f64,
90 unit: Option<&'a str>,
91 receivers: Receivers<'a>,
92 ) -> Self {
93 Self {
95 name,
96 start_bit,
97 length,
98 byte_order,
99 unsigned,
100 factor,
101 offset,
102 min,
103 max,
104 unit,
105 receivers,
106 }
107 }
108
109 fn parse_position<'b>(
110 parser: &mut Parser<'b>,
111 #[cfg_attr(not(feature = "alloc"), allow(unused_variables))] signal_name: &str,
112 ) -> ParseResult<(u16, u16, ByteOrder, bool)> {
113 let start_bit = match parser.parse_u32() {
115 Ok(v) => v as u16,
116 Err(_) => {
117 #[cfg(feature = "alloc")]
118 {
119 use crate::error::messages;
120 let msg = messages::signal_start_bit_invalid(signal_name, 0);
121 return Err(ParseError::Version(Box::leak(msg.into_boxed_str())));
122 }
123 #[cfg(not(feature = "alloc"))]
124 {
125 return Err(ParseError::Version(
126 crate::error::lang::SIGNAL_PARSE_INVALID_START_BIT,
127 ));
128 }
129 }
130 };
131
132 if start_bit > 511 {
134 #[cfg(feature = "alloc")]
135 {
136 use crate::error::messages;
137 let msg = messages::signal_start_bit_invalid(signal_name, start_bit);
138 return Err(ParseError::Version(Box::leak(msg.into_boxed_str())));
139 }
140 #[cfg(not(feature = "alloc"))]
141 {
142 return Err(ParseError::Version(
143 crate::error::lang::SIGNAL_PARSE_INVALID_START_BIT,
144 ));
145 }
146 }
147
148 parser.expect(b"|").map_err(|_| ParseError::Expected("Expected pipe"))?;
150
151 let length = parser
153 .parse_u32()
154 .map_err(|_| ParseError::Version(crate::error::lang::SIGNAL_PARSE_INVALID_LENGTH))?
155 as u16;
156
157 parser.expect(b"@").map_err(|_| ParseError::Expected("Expected @"))?;
159
160 let bo_byte = if parser.expect(b"0").is_ok() {
163 b'0'
164 } else if parser.expect(b"1").is_ok() {
165 b'1'
166 } else {
167 return Err(ParseError::Expected("Expected byte order"));
168 };
169
170 let byte_order = match bo_byte {
171 b'0' => ByteOrder::BigEndian, b'1' => ByteOrder::LittleEndian, _ => return Err(ParseError::InvalidChar(bo_byte as char)),
174 };
175
176 let sign_byte = if parser.expect(b"+").is_ok() {
178 b'+'
179 } else if parser.expect(b"-").is_ok() {
180 b'-'
181 } else {
182 return Err(ParseError::Expected("Expected sign (+ or -)"));
183 };
184
185 let unsigned = match sign_byte {
186 b'+' => true,
187 b'-' => false,
188 _ => return Err(ParseError::InvalidChar(sign_byte as char)),
189 };
190
191 Ok((start_bit, length, byte_order, unsigned))
192 }
193
194 fn parse_factor_offset<'b>(parser: &mut Parser<'b>) -> ParseResult<(f64, f64)> {
195 parser
197 .expect(b"(")
198 .map_err(|_| ParseError::Expected("Expected opening parenthesis"))?;
199
200 parser.skip_newlines_and_spaces();
202
203 let pos_before = parser.pos();
207 let factor = match parser.parse_f64() {
208 Ok(val) => val,
209 Err(_) => {
210 if parser.pos() == pos_before {
212 0.0 } else {
214 return Err(ParseError::Version(
216 crate::error::lang::SIGNAL_PARSE_INVALID_FACTOR,
217 ));
218 }
219 }
220 };
221
222 parser.expect(b",").map_err(|_| ParseError::Expected("Expected comma"))?;
224
225 parser.skip_newlines_and_spaces();
227
228 let pos_before = parser.pos();
230 let offset = match parser.parse_f64() {
231 Ok(val) => val,
232 Err(_) => {
233 if parser.pos() == pos_before {
235 0.0 } else {
237 return Err(ParseError::Version(
238 crate::error::lang::SIGNAL_PARSE_INVALID_OFFSET,
239 ));
240 }
241 }
242 };
243
244 parser.skip_newlines_and_spaces();
246
247 parser
249 .expect(b")")
250 .map_err(|_| ParseError::Expected("Expected closing parenthesis"))?;
251
252 Ok((factor, offset))
253 }
254
255 fn parse_range<'b>(parser: &mut Parser<'b>) -> ParseResult<(f64, f64)> {
256 parser
258 .expect(b"[")
259 .map_err(|_| ParseError::Expected("Expected opening bracket"))?;
260
261 parser.skip_newlines_and_spaces();
263
264 let pos_before = parser.pos();
266 let min = match parser.parse_f64() {
267 Ok(val) => val,
268 Err(_) => {
269 if parser.pos() == pos_before {
271 0.0 } else {
273 return Err(ParseError::Version(
274 crate::error::lang::SIGNAL_PARSE_INVALID_MIN,
275 ));
276 }
277 }
278 };
279
280 parser.expect(b"|").map_err(|_| ParseError::Expected("Expected pipe"))?;
282
283 parser.skip_newlines_and_spaces();
285
286 let pos_before = parser.pos();
288 let max = match parser.parse_f64() {
289 Ok(val) => val,
290 Err(_) => {
291 if parser.pos() == pos_before {
293 0.0 } else {
295 return Err(ParseError::Version(
296 crate::error::lang::SIGNAL_PARSE_INVALID_MAX,
297 ));
298 }
299 }
300 };
301
302 parser.skip_newlines_and_spaces();
304
305 parser
307 .expect(b"]")
308 .map_err(|_| ParseError::Expected("Expected closing bracket"))?;
309
310 Ok((min, max))
311 }
312
313 fn parse_unit<'b: 'a>(parser: &mut Parser<'b>) -> ParseResult<Option<&'a str>> {
314 const MAX_UNIT_LENGTH: u16 = 256;
315
316 parser
318 .expect(b"\"")
319 .map_err(|_| ParseError::Expected("Expected opening quote"))?;
320
321 let unit_bytes = parser.take_until_quote(false, MAX_UNIT_LENGTH).map_err(|e| match e {
323 ParseError::MaxStrLength(_) => {
324 ParseError::Version(crate::error::lang::SIGNAL_PARSE_UNIT_TOO_LONG)
325 }
326 _ => ParseError::Expected("Expected closing quote"),
327 })?;
328
329 let unit_str = core::str::from_utf8(unit_bytes)
331 .map_err(|_| ParseError::Expected("Invalid UTF-8 in unit"))?;
332
333 let unit = if unit_str.is_empty() {
334 None
335 } else {
336 Some(unit_str)
337 };
338
339 Ok(unit)
340 }
341
342 pub(crate) fn parse<'b: 'a>(parser: &mut Parser<'b>) -> ParseResult<Self> {
343 let _ = parser.expect(crate::SG_.as_bytes()).ok(); parser.skip_newlines_and_spaces();
351
352 let name = parser
354 .parse_identifier()
355 .map_err(|_| ParseError::Version(crate::error::lang::SIGNAL_NAME_EMPTY))?;
356
357 parser.skip_newlines_and_spaces();
361
362 if parser.expect(b"m").is_ok() || parser.expect(b"M").is_ok() {
365 loop {
367 let _pos_before = parser.pos();
368 if parser.expect(b"0").is_ok()
370 || parser.expect(b"1").is_ok()
371 || parser.expect(b"2").is_ok()
372 || parser.expect(b"3").is_ok()
373 || parser.expect(b"4").is_ok()
374 || parser.expect(b"5").is_ok()
375 || parser.expect(b"6").is_ok()
376 || parser.expect(b"7").is_ok()
377 || parser.expect(b"8").is_ok()
378 || parser.expect(b"9").is_ok()
379 {
380 } else {
382 break;
384 }
385 }
386 parser.skip_newlines_and_spaces();
388 }
389
390 parser.expect(b":").map_err(|_| ParseError::Expected("Expected colon"))?;
392
393 parser.skip_newlines_and_spaces();
395
396 let (start_bit, length, byte_order, unsigned) = Self::parse_position(parser, name)?;
398
399 parser.skip_newlines_and_spaces();
401
402 let (factor, offset) = Self::parse_factor_offset(parser)?;
404
405 parser.skip_newlines_and_spaces();
407
408 let (min, max) = Self::parse_range(parser)?;
410
411 parser.skip_newlines_and_spaces();
413
414 let unit = Self::parse_unit(parser)?;
416
417 let _ = parser.skip_whitespace().ok(); let receivers = Receivers::parse(parser)?;
423
424 Self::validate(name, length, min, max)?;
426 Ok(Self {
428 name,
429 start_bit,
430 length,
431 byte_order,
432 unsigned,
433 factor,
434 offset,
435 min,
436 max,
437 unit,
438 receivers,
439 })
440 }
441
442 #[inline]
443 #[must_use = "return value should be checked"]
444 pub fn name(&self) -> &str {
445 self.name
446 }
447
448 #[inline]
449 #[must_use]
450 pub fn start_bit(&self) -> u16 {
451 self.start_bit
452 }
453
454 #[inline]
455 #[must_use]
456 pub fn length(&self) -> u16 {
457 self.length
458 }
459
460 #[inline]
461 #[must_use]
462 pub fn byte_order(&self) -> ByteOrder {
463 self.byte_order
464 }
465
466 #[inline]
467 #[must_use]
468 pub fn is_unsigned(&self) -> bool {
469 self.unsigned
470 }
471
472 #[inline]
473 #[must_use]
474 pub fn factor(&self) -> f64 {
475 self.factor
476 }
477
478 #[inline]
479 #[must_use]
480 pub fn offset(&self) -> f64 {
481 self.offset
482 }
483
484 #[inline]
485 #[must_use]
486 pub fn min(&self) -> f64 {
487 self.min
488 }
489
490 #[inline]
491 #[must_use]
492 pub fn max(&self) -> f64 {
493 self.max
494 }
495
496 #[inline]
497 #[must_use]
498 pub fn unit(&self) -> Option<&'a str> {
499 self.unit
500 }
501
502 #[inline]
503 #[must_use]
504 pub fn receivers(&self) -> &Receivers<'a> {
505 &self.receivers
506 }
507
508 #[cfg(feature = "alloc")]
509 #[must_use]
510 pub fn to_dbc_string(&self) -> String {
511 let mut result = String::with_capacity(100); result.push_str(" SG_ ");
514 result.push_str(self.name());
515 result.push_str(" : ");
516 result.push_str(&self.start_bit().to_string());
517 result.push('|');
518 result.push_str(&self.length().to_string());
519 result.push('@');
520
521 match self.byte_order() {
524 ByteOrder::BigEndian => result.push('0'), ByteOrder::LittleEndian => result.push('1'), }
527
528 if self.is_unsigned() {
530 result.push('+');
531 } else {
532 result.push('-');
533 }
534
535 result.push_str(" (");
537 result.push_str(&format!("{}", self.factor()));
538 result.push(',');
539 result.push_str(&format!("{}", self.offset()));
540 result.push(')');
541
542 result.push_str(" [");
544 result.push_str(&format!("{}", self.min()));
545 result.push('|');
546 result.push_str(&format!("{}", self.max()));
547 result.push(']');
548
549 result.push(' ');
551 if let Some(unit) = self.unit() {
552 result.push('"');
553 result.push_str(unit);
554 result.push('"');
555 } else {
556 result.push_str("\"\"");
557 }
558
559 match self.receivers() {
561 Receivers::Broadcast => {
562 result.push(' ');
563 result.push('*');
564 }
565 Receivers::Nodes(_, count) => {
566 if *count > 0 {
567 result.push(' ');
568 for (i, node) in self.receivers().iter().enumerate() {
569 if i > 0 {
570 result.push(' ');
571 }
572 result.push_str(node);
573 }
574 }
575 }
576 Receivers::None => {
577 }
579 }
580
581 result
582 }
583}
584
585impl<'a> Eq for Signal<'a> {}
587
588impl<'a> core::hash::Hash for Signal<'a> {
590 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
591 self.name.hash(state);
592 self.start_bit.hash(state);
593 self.length.hash(state);
594 self.byte_order.hash(state);
595 self.unsigned.hash(state);
596 self.factor.to_bits().hash(state);
598 self.offset.to_bits().hash(state);
599 self.min.to_bits().hash(state);
600 self.max.to_bits().hash(state);
601 self.unit.hash(state);
602 self.receivers.hash(state);
603 }
604}
605
606#[cfg(feature = "alloc")]
607impl<'a> core::fmt::Display for Signal<'a> {
608 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
609 write!(f, "{}", self.to_dbc_string())
610 }
611}
612
613#[cfg(test)]
614mod tests {
615 #![allow(clippy::float_cmp)]
616 use super::*;
617 use crate::{
618 Error, Parser,
619 error::{ParseError, lang},
620 };
621 #[cfg(feature = "alloc")]
622 use crate::{MessageBuilder, ReceiversBuilder, SignalBuilder};
623
624 #[test]
625 #[cfg(feature = "alloc")]
626 fn test_signal_new_valid() {
627 let signal = SignalBuilder::new()
628 .name("RPM")
629 .start_bit(0)
630 .length(16)
631 .byte_order(ByteOrder::BigEndian)
632 .unsigned(true)
633 .factor(0.25)
634 .offset(0.0)
635 .min(0.0)
636 .max(8000.0)
637 .unit("rpm")
638 .receivers(ReceiversBuilder::new().broadcast().build().unwrap())
639 .build()
640 .unwrap();
641 assert_eq!(signal.name(), "RPM");
642 assert_eq!(signal.start_bit(), 0);
643 assert_eq!(signal.length(), 16);
644 assert_eq!(signal.byte_order(), ByteOrder::BigEndian);
645 assert!(signal.is_unsigned());
646 assert_eq!(signal.factor(), 0.25);
647 assert_eq!(signal.offset(), 0.0);
648 assert_eq!(signal.min(), 0.0);
649 assert_eq!(signal.max(), 8000.0);
650 assert_eq!(signal.unit(), Some("rpm"));
651 assert_eq!(signal.receivers(), &Receivers::Broadcast);
652 }
653
654 #[test]
655 #[cfg(feature = "alloc")]
656 fn test_signal_new_empty_name() {
657 let result = SignalBuilder::new()
658 .name("")
659 .start_bit(0)
660 .length(16)
661 .byte_order(ByteOrder::BigEndian)
662 .unsigned(true)
663 .factor(1.0)
664 .offset(0.0)
665 .min(0.0)
666 .max(100.0)
667 .receivers(ReceiversBuilder::new().none().build().unwrap())
668 .build();
669 assert!(result.is_err());
670 match result.unwrap_err() {
671 Error::Signal(msg) => assert!(msg.contains(lang::SIGNAL_NAME_EMPTY)),
672 _ => panic!("Expected Error::Signal"),
673 }
674 }
675
676 #[test]
677 #[cfg(feature = "alloc")]
678 fn test_signal_new_zero_length() {
679 let result = SignalBuilder::new()
680 .name("Test")
681 .start_bit(0)
682 .length(0)
683 .byte_order(ByteOrder::BigEndian)
684 .unsigned(true)
685 .factor(1.0)
686 .offset(0.0)
687 .min(0.0)
688 .max(100.0)
689 .receivers(ReceiversBuilder::new().none().build().unwrap())
690 .build();
691 assert!(result.is_err());
692 match result.unwrap_err() {
693 Error::Signal(msg) => {
694 assert!(
696 msg.contains(lang::SIGNAL_LENGTH_TOO_SMALL)
697 || msg.contains("Signal 'Test'")
698 || msg.contains("0 bits")
699 );
700 }
701 _ => panic!("Expected Error::Signal"),
702 }
703 }
704
705 #[test]
706 #[cfg(feature = "alloc")]
707 fn test_signal_new_length_too_large() {
708 let result = SignalBuilder::new()
710 .name("Test")
711 .start_bit(0)
712 .length(513)
713 .byte_order(ByteOrder::BigEndian)
714 .unsigned(true)
715 .factor(1.0)
716 .offset(0.0)
717 .min(0.0)
718 .max(100.0)
719 .receivers(ReceiversBuilder::new().none().build().unwrap())
720 .build();
721 assert!(result.is_err());
722 match result.unwrap_err() {
723 Error::Signal(msg) => {
724 assert!(
726 msg.contains(lang::SIGNAL_LENGTH_TOO_LARGE)
727 || msg.contains("Signal 'Test'")
728 || msg.contains("513")
729 );
730 }
731 _ => panic!("Expected Error::Signal"),
732 }
733 }
734
735 #[test]
736 #[cfg(feature = "alloc")]
737 fn test_signal_new_overflow() {
738 let signal = SignalBuilder::new()
742 .name("Test")
743 .start_bit(60)
744 .length(10) .byte_order(ByteOrder::BigEndian)
746 .unsigned(true)
747 .factor(1.0)
748 .offset(0.0)
749 .min(0.0)
750 .max(100.0)
751 .receivers(ReceiversBuilder::new().none().build().unwrap())
752 .build()
753 .unwrap();
754
755 let result = MessageBuilder::new()
757 .id(256)
758 .name("TestMessage")
759 .dlc(8)
760 .sender("ECM")
761 .add_signal(signal)
762 .build();
763 assert!(result.is_err());
764 match result.unwrap_err() {
765 Error::Dbc(msg) => {
766 let template_text =
768 lang::FORMAT_SIGNAL_EXTENDS_BEYOND_MESSAGE.split("{}").next().unwrap();
769 assert!(msg.contains(template_text.trim_end_matches(':').trim_end()));
770 }
771 _ => panic!("Expected Error::Dbc"),
772 }
773 }
774
775 #[test]
776 #[cfg(feature = "alloc")]
777 fn test_signal_new_invalid_range() {
778 let result = SignalBuilder::new()
779 .name("Test")
780 .start_bit(0)
781 .length(8)
782 .byte_order(ByteOrder::BigEndian)
783 .unsigned(true)
784 .factor(1.0)
785 .offset(0.0)
786 .min(100.0)
787 .max(50.0)
788 .receivers(ReceiversBuilder::new().none().build().unwrap())
789 .build();
790 assert!(result.is_err());
791 match result.unwrap_err() {
792 Error::Signal(msg) => {
793 let template_text = lang::FORMAT_INVALID_RANGE.split("{}").next().unwrap();
795 assert!(msg.contains(template_text.trim_end_matches(':').trim_end()));
796 }
797 _ => panic!("Expected Error::Signal"),
798 }
799 }
800
801 #[test]
802 #[cfg(feature = "alloc")]
803 fn test_signal_new_max_boundary() {
804 let signal = SignalBuilder::new()
806 .name("FullMessage")
807 .start_bit(0)
808 .length(64)
809 .byte_order(ByteOrder::BigEndian)
810 .unsigned(true)
811 .factor(1.0)
812 .offset(0.0)
813 .min(0.0)
814 .max(100.0)
815 .receivers(ReceiversBuilder::new().none().build().unwrap())
816 .build()
817 .unwrap();
818 assert_eq!(signal.length(), 64);
819 }
820
821 #[test]
822 #[cfg(feature = "alloc")]
823 fn test_signal_new_with_receivers() {
824 let signal = SignalBuilder::new()
825 .name("TestSignal")
826 .start_bit(8)
827 .length(16)
828 .byte_order(ByteOrder::LittleEndian)
829 .unsigned(false)
830 .factor(0.1)
831 .offset(-40.0)
832 .min(-40.0)
833 .max(215.0)
834 .unit("°C")
835 .receivers(ReceiversBuilder::new().add_node("ECM").add_node("TCM").build().unwrap())
836 .build()
837 .unwrap();
838 assert_eq!(signal.name(), "TestSignal");
839 assert!(!signal.is_unsigned());
840 assert_eq!(signal.unit(), Some("°C"));
841 match signal.receivers() {
842 Receivers::Nodes(_, count) => assert_eq!(*count, 2),
843 _ => panic!("Expected Nodes variant"),
844 }
845 }
846
847 #[test]
848 fn test_parse_valid_signal() {
849 let line = r#"SG_ RPM : 0|16@0+ (0.25,0) [0|8000] "rpm" TCM"#;
850 let mut parser = Parser::new(line.as_bytes()).unwrap();
851 let sig = Signal::parse(&mut parser).unwrap();
852 assert_eq!(sig.name(), "RPM");
853 assert_eq!(sig.start_bit(), 0);
854 assert_eq!(sig.length(), 16);
855 assert_eq!(sig.byte_order(), ByteOrder::BigEndian); assert!(sig.is_unsigned());
857 assert_eq!(sig.factor(), 0.25);
858 assert_eq!(sig.offset(), 0.);
859 assert_eq!(sig.min(), 0.);
860 assert_eq!(sig.max(), 8000.);
861 assert_eq!(sig.unit(), Some("rpm"));
862 let nodes: Vec<&str> = sig.receivers().iter().collect();
864 assert_eq!(nodes.len(), 1);
865 assert_eq!(nodes[0], "TCM");
866 }
867
868 #[test]
869 fn test_parse_signal_with_empty_unit_and_broadcast() {
870 let line = r#"SG_ ABSActive : 16|1@0+ (1,0) [0|1] "" *"#;
871 let mut parser = Parser::new(line.as_bytes()).unwrap();
872 let sig = Signal::parse(&mut parser).unwrap();
873 assert_eq!(sig.name(), "ABSActive");
874 assert_eq!(sig.start_bit(), 16);
875 assert_eq!(sig.length(), 1);
876 assert_eq!(sig.byte_order(), ByteOrder::BigEndian); assert!(sig.is_unsigned());
878 assert_eq!(sig.factor(), 1.);
879 assert_eq!(sig.offset(), 0.);
880 assert_eq!(sig.min(), 0.);
881 assert_eq!(sig.max(), 1.);
882 assert_eq!(sig.unit(), None);
883 assert_eq!(sig.receivers(), &Receivers::Broadcast);
884 }
885
886 #[test]
887 fn test_parse_signal_with_negative_offset_and_min() {
888 let line = r#"SG_ Temperature : 16|8@0- (1,-40) [-40|215] "°C" TCM BCM"#;
889 let mut parser = Parser::new(line.as_bytes()).unwrap();
890 let sig = Signal::parse(&mut parser).unwrap();
891 assert_eq!(sig.name(), "Temperature");
892 assert_eq!(sig.start_bit(), 16);
893 assert_eq!(sig.length(), 8);
894 assert_eq!(sig.byte_order(), ByteOrder::BigEndian); assert!(!sig.is_unsigned());
896 assert_eq!(sig.factor(), 1.);
897 assert_eq!(sig.offset(), -40.);
898 assert_eq!(sig.min(), -40.);
899 assert_eq!(sig.max(), 215.);
900 assert_eq!(sig.unit(), Some("°C"));
901 let nodes: Vec<&str> = sig.receivers().iter().collect();
903 assert_eq!(nodes.len(), 2);
904 assert_eq!(nodes[0], "TCM");
905 assert_eq!(nodes[1], "BCM");
906 }
907
908 #[test]
909 fn test_parse_signal_with_percent_unit() {
910 let line = r#"SG_ ThrottlePosition : 24|8@0+ (0.392157,0) [0|100] "%" *"#;
911 let mut parser = Parser::new(line.as_bytes()).unwrap();
912 let sig = Signal::parse(&mut parser).unwrap();
913 assert_eq!(sig.name(), "ThrottlePosition");
914 assert_eq!(sig.start_bit(), 24);
915 assert_eq!(sig.length(), 8);
916 assert_eq!(sig.byte_order(), ByteOrder::BigEndian); assert!(sig.is_unsigned());
918 assert_eq!(sig.factor(), 0.392_157);
919 assert_eq!(sig.offset(), 0.);
920 assert_eq!(sig.min(), 0.);
921 assert_eq!(sig.max(), 100.);
922 assert_eq!(sig.unit(), Some("%"));
923 assert_eq!(sig.receivers(), &Receivers::Broadcast);
924 }
925
926 #[test]
927 fn test_parse_signal_missing_factors_and_limits() {
928 let line = r#"SG_ Simple : 10|4@0+ ( , ) [ | ] "" *"#;
930 let mut parser = Parser::new(line.as_bytes()).unwrap();
931 let sig = Signal::parse(&mut parser).unwrap();
932 assert_eq!(sig.name(), "Simple");
933 assert_eq!(sig.start_bit(), 10);
934 assert_eq!(sig.length(), 4);
935 assert_eq!(sig.byte_order(), ByteOrder::BigEndian); assert!(sig.is_unsigned());
937 assert_eq!(sig.factor(), 0.);
938 assert_eq!(sig.offset(), 0.);
939 assert_eq!(sig.min(), 0.);
940 assert_eq!(sig.max(), 0.);
941 assert_eq!(sig.unit(), None);
942 assert_eq!(sig.receivers(), &Receivers::Broadcast);
943 }
944
945 #[test]
946 fn test_parse_signal_missing_start_bit() {
947 let line = r#"SG_ RPM : |16@0+ (0.25,0) [0|8000] "rpm" TCM"#;
948 let mut parser = Parser::new(line.as_bytes()).unwrap();
949 let err = Signal::parse(&mut parser).unwrap_err();
950 match err {
951 ParseError::Version(msg) => {
952 assert!(
954 msg.contains(lang::SIGNAL_PARSE_INVALID_START_BIT)
955 || msg.contains("Signal 'RPM'")
956 );
957 }
958 _ => panic!("Expected ParseError"),
959 }
960 }
961
962 #[test]
963 fn test_parse_signal_invalid_range() {
964 let line = r#"SG_ Test : 0|8@0+ (1,0) [100|50] "unit" *"#;
966 let mut parser = Parser::new(line.as_bytes()).unwrap();
967 let err = Signal::parse(&mut parser).unwrap_err();
968 match err {
969 ParseError::Version(msg) => {
970 let template_text = lang::FORMAT_INVALID_RANGE.split("{}").next().unwrap();
972 assert!(msg.contains(template_text.trim_end_matches(':').trim_end()));
973 }
974 e => panic!("Expected ParseError::Version, got: {:?}", e),
975 }
976 }
977
978 #[test]
979 #[cfg(feature = "alloc")]
980 fn test_parse_signal_overflow() {
981 let line = r#"SG_ Test : 60|10@0+ (1,0) [0|100] "unit" *"#;
985 let mut parser = Parser::new(line.as_bytes()).unwrap();
986 let signal = Signal::parse(&mut parser).unwrap();
987 assert_eq!(signal.start_bit(), 60);
988 assert_eq!(signal.length(), 10);
989
990 let result = MessageBuilder::new()
992 .id(256)
993 .name("TestMessage")
994 .dlc(8)
995 .sender("ECM")
996 .add_signal(signal)
997 .build();
998 assert!(result.is_err());
999 match result.unwrap_err() {
1000 Error::Dbc(msg) => {
1001 let template_text =
1003 lang::FORMAT_SIGNAL_EXTENDS_BEYOND_MESSAGE.split("{}").next().unwrap();
1004 assert!(msg.contains(template_text.trim_end_matches(':').trim_end()));
1005 }
1006 _ => panic!("Expected Error::Dbc"),
1007 }
1008 }
1009
1010 #[test]
1011 fn test_parse_signal_length_too_large() {
1012 let line = r#"SG_ Test : 0|513@0+ (1,0) [0|100] "unit" *"#;
1014 let mut parser = Parser::new(line.as_bytes()).unwrap();
1015 let err = Signal::parse(&mut parser).unwrap_err();
1016 match err {
1017 ParseError::Version(msg) => {
1018 assert!(
1020 msg.contains(lang::SIGNAL_LENGTH_TOO_LARGE)
1021 || msg.contains("Signal 'Test'")
1022 || msg.contains("513")
1023 );
1024 }
1025 e => panic!("Expected ParseError::Version, got: {:?}", e),
1026 }
1027 }
1028
1029 #[test]
1030 fn test_parse_signal_zero_length() {
1031 let line = r#"SG_ Test : 0|0@0+ (1,0) [0|100] "unit" *"#;
1033 let mut parser = Parser::new(line.as_bytes()).unwrap();
1034 let err = Signal::parse(&mut parser).unwrap_err();
1035 match err {
1036 ParseError::Version(msg) => {
1037 assert!(
1039 msg.contains(lang::SIGNAL_LENGTH_TOO_SMALL)
1040 || msg.contains("Signal 'Test'")
1041 || msg.contains("0 bits")
1042 );
1043 }
1044 e => panic!("Expected ParseError::Version, got: {:?}", e),
1045 }
1046 }
1047
1048 #[test]
1049 fn test_parse_signal_missing_length() {
1050 let line = r#"SG_ RPM : 0|@0+ (0.25,0) [0|8000] "rpm" TCM"#;
1051 let mut parser = Parser::new(line.as_bytes()).unwrap();
1052 let err = Signal::parse(&mut parser).unwrap_err();
1053 match err {
1054 ParseError::Version(msg) => assert!(msg.contains(lang::SIGNAL_PARSE_INVALID_LENGTH)),
1055 e => panic!("Expected ParseError::Version, got: {:?}", e),
1056 }
1057 }
1058
1059 #[test]
1060 #[cfg(feature = "alloc")]
1061 fn test_signal_to_dbc_string() {
1062 let signal1 = SignalBuilder::new()
1064 .name("RPM")
1065 .start_bit(0)
1066 .length(16)
1067 .byte_order(ByteOrder::BigEndian)
1068 .unsigned(true)
1069 .factor(0.25)
1070 .offset(0.0)
1071 .min(0.0)
1072 .max(8000.0)
1073 .unit("rpm")
1074 .receivers(ReceiversBuilder::new().broadcast().build().unwrap())
1075 .build()
1076 .unwrap();
1077 assert_eq!(
1078 signal1.to_dbc_string(),
1079 " SG_ RPM : 0|16@0+ (0.25,0) [0|8000] \"rpm\" *"
1080 );
1081
1082 let signal2 = SignalBuilder::new()
1084 .name("Temperature")
1085 .start_bit(16)
1086 .length(8)
1087 .byte_order(ByteOrder::LittleEndian)
1088 .unsigned(false)
1089 .factor(1.0)
1090 .offset(-40.0)
1091 .min(-40.0)
1092 .max(215.0)
1093 .unit("°C")
1094 .receivers(ReceiversBuilder::new().add_node("TCM").add_node("BCM").build().unwrap())
1095 .build()
1096 .unwrap();
1097 assert_eq!(
1098 signal2.to_dbc_string(),
1099 " SG_ Temperature : 16|8@1- (1,-40) [-40|215] \"°C\" TCM BCM"
1100 );
1101
1102 let signal3 = SignalBuilder::new()
1104 .name("Flag")
1105 .start_bit(24)
1106 .length(1)
1107 .byte_order(ByteOrder::BigEndian)
1108 .unsigned(true)
1109 .factor(1.0)
1110 .offset(0.0)
1111 .min(0.0)
1112 .max(1.0)
1113 .receivers(ReceiversBuilder::new().none().build().unwrap())
1114 .build()
1115 .unwrap();
1116 assert_eq!(
1117 signal3.to_dbc_string(),
1118 " SG_ Flag : 24|1@0+ (1,0) [0|1] \"\""
1119 );
1120 }
1121
1122 }