1#[cfg(all(feature = "alloc", not(feature = "std")))]
7use alloc::{string::String, vec::Vec};
8
9use crate::enums::ObjectType;
10use crate::error::{DecodeError, DecodingKind, Error, RangeError};
11
12#[derive(Clone, Copy, PartialEq, Eq, Hash)]
30pub struct ObjectIdentifier {
31 object_type: ObjectType,
32 instance_number: u32,
33}
34
35impl ObjectIdentifier {
36 pub const MAX_INSTANCE: u32 = 0x3F_FFFF;
44
45 pub const WILDCARD_INSTANCE: u32 = Self::MAX_INSTANCE;
52
53 pub const MAX_ADDRESSABLE_INSTANCE: u32 = Self::WILDCARD_INSTANCE - 1;
57
58 pub fn new(object_type: ObjectType, instance_number: u32) -> Result<Self, Error> {
69 if instance_number > Self::MAX_INSTANCE {
70 return Err(RangeError::new("instance number {} exceeds max 4194303")
71 .with_value(u64::from(instance_number))
72 .into());
73 }
74 if object_type.to_raw() > 0x3FF {
75 return Err(RangeError::new("object type {} exceeds max 1023")
76 .with_value(u64::from(object_type.to_raw()))
77 .into());
78 }
79 Ok(Self {
80 object_type,
81 instance_number,
82 })
83 }
84
85 pub fn new_addressable(object_type: ObjectType, instance_number: u32) -> Result<Self, Error> {
96 let identifier = Self::new(object_type, instance_number)?;
97 if instance_number == Self::WILDCARD_INSTANCE {
98 return Err(RangeError::new(
99 "instance number 4194303 is reserved as the wildcard value; \
100 maximum addressable instance is 4194302",
101 )
102 .into());
103 }
104 Ok(identifier)
105 }
106
107 pub const fn object_type(&self) -> ObjectType {
109 self.object_type
110 }
111
112 pub const fn instance_number(&self) -> u32 {
119 self.instance_number
120 }
121
122 pub fn encode(&self) -> [u8; 4] {
124 debug_assert!(
128 self.object_type.to_raw() <= 0x3FF,
129 "ObjectType exceeds the 10-bit field"
130 );
131 debug_assert!(
132 self.instance_number <= Self::MAX_INSTANCE,
133 "instance exceeds MAX_INSTANCE"
134 );
135 let value = (self.object_type.to_raw() << 22) | self.instance_number;
136 value.to_be_bytes()
137 }
138
139 pub fn decode(data: &[u8]) -> Result<Self, Error> {
141 if data.len() != 4 {
142 return Err(wrong_length(
143 "ObjectIdentifier expects exactly 4 bytes, got {}",
144 data,
145 ));
146 }
147 let value = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
148 let type_raw = (value >> 22) & 0x3FF;
149 let instance = value & Self::MAX_INSTANCE;
150 Ok(Self {
151 object_type: ObjectType::from_raw(type_raw),
152 instance_number: instance,
153 })
154 }
155}
156
157#[cold]
159fn wrong_length(label: &'static str, data: &[u8]) -> Error {
160 DecodeError::new(DecodingKind::InvalidEncoding, 0, label)
161 .with_value(data.len() as u64)
162 .into()
163}
164
165impl core::fmt::Debug for ObjectIdentifier {
166 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
167 write!(
168 f,
169 "ObjectIdentifier({:?}, {})",
170 self.object_type, self.instance_number
171 )
172 }
173}
174
175impl core::fmt::Display for ObjectIdentifier {
176 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
177 write!(f, "{},{}", self.object_type, self.instance_number)
178 }
179}
180
181#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
192pub struct Date {
193 pub year: u8,
195 pub month: u8,
197 pub day: u8,
199 pub day_of_week: u8,
201}
202
203impl Date {
204 pub const UNSPECIFIED: u8 = 0xFF;
206
207 pub fn encode(&self) -> [u8; 4] {
209 [self.year, self.month, self.day, self.day_of_week]
210 }
211
212 pub fn decode(data: &[u8]) -> Result<Self, Error> {
214 if data.len() != 4 {
215 return Err(wrong_length("Date expects exactly 4 bytes, got {}", data));
216 }
217 Ok(Self {
218 year: data[0],
219 month: data[1],
220 day: data[2],
221 day_of_week: data[3],
222 })
223 }
224
225 pub fn actual_year(&self) -> Option<u16> {
227 if self.year == Self::UNSPECIFIED {
228 None
229 } else {
230 Some(1900 + self.year as u16)
231 }
232 }
233}
234
235#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
243pub struct Time {
244 pub hour: u8,
246 pub minute: u8,
248 pub second: u8,
250 pub hundredths: u8,
252}
253
254impl Time {
255 pub const UNSPECIFIED: u8 = 0xFF;
257
258 pub fn encode(&self) -> [u8; 4] {
260 [self.hour, self.minute, self.second, self.hundredths]
261 }
262
263 pub fn decode(data: &[u8]) -> Result<Self, Error> {
265 if data.len() != 4 {
266 return Err(wrong_length("Time expects exactly 4 bytes, got {}", data));
267 }
268 Ok(Self {
269 hour: data[0],
270 minute: data[1],
271 second: data[2],
272 hundredths: data[3],
273 })
274 }
275}
276
277#[derive(Debug, Clone, PartialEq)]
283pub enum BACnetTimeStamp {
284 Time(Time),
286 SequenceNumber(u16),
288 DateTime {
290 date: Date,
292 time: Time,
294 },
295}
296
297bitflags::bitflags! {
302 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
304 pub struct StatusFlags: u8 {
305 const IN_ALARM = 0b1000;
307 const FAULT = 0b0100;
309 const OVERRIDDEN = 0b0010;
311 const OUT_OF_SERVICE = 0b0001;
313 }
314}
315
316impl core::fmt::Display for StatusFlags {
317 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
320 let mut first = true;
321 for (name, _) in self.iter_names() {
322 if !first {
323 f.write_str(" | ")?;
324 }
325 f.write_str(name)?;
326 first = false;
327 }
328 if first {
329 f.write_str("()")?;
330 }
331 Ok(())
332 }
333}
334
335impl core::fmt::Debug for StatusFlags {
336 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
337 core::fmt::Display::fmt(self, f)
338 }
339}
340
341#[cfg(feature = "alloc")]
352#[derive(Debug, Clone, PartialEq)]
353pub enum PropertyValue {
354 Null,
356 Boolean(bool),
358 Unsigned(u64),
360 Signed(i32),
362 Real(f32),
364 Double(f64),
366 OctetString(Vec<u8>),
368 CharacterString(String),
370 BitString {
372 unused_bits: u8,
374 data: Vec<u8>,
376 },
377 Enumerated(u32),
379 Date(Date),
381 Time(Time),
383 ObjectIdentifier(ObjectIdentifier),
385 List(Vec<PropertyValue>),
391 ApplicationData(Vec<u8>),
405}
406
407#[cfg(feature = "alloc")]
408impl PropertyValue {
409 pub fn is_primitive(&self) -> bool {
412 match self {
413 Self::Null
414 | Self::Boolean(_)
415 | Self::Unsigned(_)
416 | Self::Signed(_)
417 | Self::Real(_)
418 | Self::Double(_)
419 | Self::OctetString(_)
420 | Self::CharacterString(_)
421 | Self::BitString { .. }
422 | Self::Enumerated(_)
423 | Self::Date(_)
424 | Self::Time(_)
425 | Self::ObjectIdentifier(_) => true,
426 Self::List(_) | Self::ApplicationData(_) => false,
427 }
428 }
429}
430
431#[cfg(test)]
436mod tests {
437 use super::*;
438
439 #[test]
440 fn object_identifier_encode_decode_round_trip() {
441 let oid = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap();
442 let bytes = oid.encode();
443 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
444 assert_eq!(oid, decoded);
445 }
446
447 #[test]
448 fn object_identifier_wire_format() {
449 let oid = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap();
451 assert_eq!(oid.encode(), [0x00, 0x00, 0x00, 0x01]);
452
453 let oid = ObjectIdentifier::new(ObjectType::DEVICE, 1234).unwrap();
455 let expected = ((8u32 << 22) | 1234u32).to_be_bytes();
456 assert_eq!(oid.encode(), expected);
457 }
458
459 #[test]
460 fn object_identifier_max_instance() {
461 let oid =
462 ObjectIdentifier::new(ObjectType::DEVICE, ObjectIdentifier::MAX_INSTANCE).unwrap();
463 assert_eq!(oid.instance_number(), 0x3F_FFFF);
464 let bytes = oid.encode();
465 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
466 assert_eq!(decoded.instance_number(), 0x3F_FFFF);
467 }
468
469 #[test]
470 fn object_identifier_invalid_instance() {
471 let result = ObjectIdentifier::new(ObjectType::DEVICE, ObjectIdentifier::MAX_INSTANCE + 1);
472 assert!(result.is_err());
473 }
474
475 #[test]
476 fn object_identifier_buffer_too_short() {
477 let result = ObjectIdentifier::decode(&[0x00, 0x00]);
478 assert!(result.is_err());
479 }
480
481 #[test]
482 fn object_identifier_overlong_errors() {
483 assert!(ObjectIdentifier::decode(&[0x00, 0x00, 0x00, 0x01, 0xFF]).is_err());
484 }
485
486 #[test]
487 fn date_encode_decode_round_trip() {
488 let date = Date {
489 year: 124, month: 6,
491 day: 15,
492 day_of_week: 6, };
494 let bytes = date.encode();
495 let decoded = Date::decode(&bytes).unwrap();
496 assert_eq!(date, decoded);
497 assert_eq!(decoded.actual_year(), Some(2024));
498 }
499
500 #[test]
501 fn date_unspecified_year() {
502 let date = Date {
503 year: Date::UNSPECIFIED,
504 month: 1,
505 day: 1,
506 day_of_week: Date::UNSPECIFIED,
507 };
508 assert_eq!(date.actual_year(), None);
509 }
510
511 #[test]
512 fn date_overlong_errors() {
513 assert!(Date::decode(&[124, 6, 15, 6, 0]).is_err());
514 }
515
516 #[test]
517 fn time_encode_decode_round_trip() {
518 let time = Time {
519 hour: 14,
520 minute: 30,
521 second: 45,
522 hundredths: 50,
523 };
524 let bytes = time.encode();
525 let decoded = Time::decode(&bytes).unwrap();
526 assert_eq!(time, decoded);
527 }
528
529 #[test]
530 fn time_overlong_errors() {
531 assert!(Time::decode(&[14, 30, 45, 50, 0]).is_err());
532 }
533
534 #[test]
535 fn status_flags_operations() {
536 let flags = StatusFlags::IN_ALARM | StatusFlags::OUT_OF_SERVICE;
537 assert!(flags.contains(StatusFlags::IN_ALARM));
538 assert!(flags.contains(StatusFlags::OUT_OF_SERVICE));
539 assert!(!flags.contains(StatusFlags::FAULT));
540 assert!(!flags.contains(StatusFlags::OVERRIDDEN));
541 }
542
543 #[test]
546 fn object_identifier_instance_zero() {
547 let oid = ObjectIdentifier::new(ObjectType::DEVICE, 0).unwrap();
549 assert_eq!(oid.instance_number(), 0);
550 let bytes = oid.encode();
551 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
552 assert_eq!(decoded.instance_number(), 0);
553 assert_eq!(decoded.object_type(), ObjectType::DEVICE);
554 }
555
556 #[test]
557 fn object_identifier_all_types_instance_zero() {
558 for type_raw in [0u32, 1, 2, 3, 4, 5, 6, 8, 10, 13, 14, 17, 19] {
560 let obj_type = ObjectType::from_raw(type_raw);
561 let oid = ObjectIdentifier::new(obj_type, 0).unwrap();
562 let bytes = oid.encode();
563 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
564 assert_eq!(decoded.object_type(), obj_type, "type {type_raw} failed");
565 assert_eq!(
566 decoded.instance_number(),
567 0,
568 "type {type_raw} instance failed"
569 );
570 }
571 }
572
573 #[test]
574 fn object_identifier_wildcard_instance() {
575 let oid =
576 ObjectIdentifier::new(ObjectType::DEVICE, ObjectIdentifier::WILDCARD_INSTANCE).unwrap();
577 assert_eq!(oid.instance_number(), ObjectIdentifier::MAX_INSTANCE);
578 let bytes = oid.encode();
579 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
580 assert_eq!(decoded.instance_number(), ObjectIdentifier::MAX_INSTANCE);
581
582 let raw_wire_value = ((ObjectType::DEVICE.to_raw() << 22)
583 | ObjectIdentifier::WILDCARD_INSTANCE)
584 .to_be_bytes();
585 let decoded = ObjectIdentifier::decode(&raw_wire_value).unwrap();
586 assert_eq!(decoded.object_type(), ObjectType::DEVICE);
587 assert_eq!(
588 decoded.instance_number(),
589 ObjectIdentifier::WILDCARD_INSTANCE
590 );
591 }
592
593 #[test]
594 fn object_identifier_new_addressable_rejects_wildcard() {
595 assert_eq!(
596 ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE,
597 ObjectIdentifier::WILDCARD_INSTANCE - 1
598 );
599
600 let oid = ObjectIdentifier::new_addressable(
601 ObjectType::DEVICE,
602 ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE,
603 )
604 .unwrap();
605 assert_eq!(
606 oid.instance_number(),
607 ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE
608 );
609
610 let analog_input = ObjectIdentifier::new_addressable(
611 ObjectType::ANALOG_INPUT,
612 ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE,
613 )
614 .unwrap();
615 assert_eq!(analog_input.object_type(), ObjectType::ANALOG_INPUT);
616 assert_eq!(
617 analog_input.instance_number(),
618 ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE
619 );
620
621 let wildcard =
622 ObjectIdentifier::new_addressable(ObjectType::DEVICE, ObjectIdentifier::MAX_INSTANCE)
623 .unwrap_err();
624 assert!(wildcard.to_string().contains("wildcard"));
625
626 let too_large = ObjectIdentifier::new_addressable(
627 ObjectType::DEVICE,
628 ObjectIdentifier::MAX_INSTANCE + 1,
629 );
630 assert!(too_large.is_err());
631 }
632
633 #[test]
634 fn object_identifier_decode_extra_bytes_errors() {
635 let oid = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 42).unwrap();
636 let mut bytes = oid.encode().to_vec();
637 bytes.extend_from_slice(&[0xFF, 0xFF]);
638 assert!(ObjectIdentifier::decode(&bytes).is_err());
639 }
640
641 #[test]
642 fn object_identifier_new_rejects_type_overflow() {
643 for raw in [1024, u32::MAX] {
644 assert!(matches!(
645 ObjectIdentifier::new(ObjectType::from_raw(raw), 0),
646 Err(Error::Range(_))
647 ));
648 }
649 }
650
651 #[test]
652 fn object_identifier_addressable_rejects_type_overflow() {
653 for raw in [1024, u32::MAX] {
654 assert!(matches!(
655 ObjectIdentifier::new_addressable(ObjectType::from_raw(raw), 0),
656 Err(Error::Range(_))
657 ));
658 }
659 }
660
661 #[test]
662 fn object_identifier_checked_instance_boundaries() {
663 for instance in [ObjectIdentifier::MAX_INSTANCE + 1, u32::MAX] {
664 for constructor in [ObjectIdentifier::new, ObjectIdentifier::new_addressable] {
665 assert!(matches!(
666 constructor(ObjectType::from_raw(1023), instance),
667 Err(Error::Range(_))
668 ));
669 }
670 }
671 }
672
673 #[test]
675 fn object_identifier_refusals_name_the_value() {
676 let text = |result: Result<ObjectIdentifier, Error>| result.unwrap_err().to_string();
677 assert_eq!(
678 text(ObjectIdentifier::new(ObjectType::DEVICE, 4_194_304)),
679 "value out of range: instance number 4194304 exceeds max 4194303"
680 );
681 assert_eq!(
682 text(ObjectIdentifier::new(ObjectType::from_raw(1024), 0)),
683 "value out of range: object type 1024 exceeds max 1023"
684 );
685 assert_eq!(
686 text(ObjectIdentifier::new_addressable(
687 ObjectType::DEVICE,
688 ObjectIdentifier::WILDCARD_INSTANCE
689 )),
690 "value out of range: instance number 4194303 is reserved as the wildcard value; \
691 maximum addressable instance is 4194302"
692 );
693 assert_eq!(
694 ObjectIdentifier::decode(&[0; 5]).unwrap_err().to_string(),
695 "decoding error at offset 0: ObjectIdentifier expects exactly 4 bytes, got 5"
696 );
697 assert_eq!(
698 Date::decode(&[0; 3]).unwrap_err().to_string(),
699 "decoding error at offset 0: Date expects exactly 4 bytes, got 3"
700 );
701 assert_eq!(
702 Time::decode(&[]).unwrap_err().to_string(),
703 "decoding error at offset 0: Time expects exactly 4 bytes, got 0"
704 );
705 }
706
707 #[test]
708 fn object_identifier_wire_boundaries_preserve_identity() {
709 for (raw_type, instance, wire) in [
710 (0, 0, [0, 0, 0, 0]),
711 (1023, 0, [0xff, 0xc0, 0, 0]),
712 (
713 1023,
714 ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE,
715 [0xff, 0xff, 0xff, 0xfe],
716 ),
717 (
718 1023,
719 ObjectIdentifier::MAX_INSTANCE,
720 [0xff, 0xff, 0xff, 0xff],
721 ),
722 ] {
723 let object_type = ObjectType::from_raw(raw_type);
724 let oid = ObjectIdentifier::new(object_type, instance).unwrap();
725 assert_eq!(oid.encode(), wire);
726 assert_eq!(ObjectIdentifier::decode(&wire).unwrap(), oid);
727 if instance == ObjectIdentifier::WILDCARD_INSTANCE {
728 assert!(ObjectIdentifier::new_addressable(object_type, instance).is_err());
729 } else {
730 assert_eq!(
731 ObjectIdentifier::new_addressable(object_type, instance).unwrap(),
732 oid
733 );
734 }
735 }
736 assert_eq!(ObjectType::from_raw(u32::MAX).to_raw(), u32::MAX);
738 }
739
740 #[cfg(feature = "alloc")]
741 #[test]
742 fn property_value_variants() {
743 let null = PropertyValue::Null;
744 let boolean = PropertyValue::Boolean(true);
745 let real = PropertyValue::Real(72.5);
746 let string = PropertyValue::CharacterString("test".into());
747
748 assert_eq!(null, PropertyValue::Null);
749 assert_eq!(boolean, PropertyValue::Boolean(true));
750 assert_ne!(real, PropertyValue::Real(73.0));
751 assert_eq!(string, PropertyValue::CharacterString("test".into()));
752 }
753}