1#[cfg(not(feature = "std"))]
7use alloc::{format, string::String, vec::Vec};
8
9use crate::enums::ObjectType;
10use crate::error::Error;
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(Error::OutOfRange(alloc_or_std_format!(
71 "instance number {} exceeds max {}",
72 instance_number,
73 Self::MAX_INSTANCE
74 )));
75 }
76 if object_type.to_raw() > 0x3FF {
77 return Err(Error::OutOfRange(alloc_or_std_format!(
78 "object type {} exceeds max 1023",
79 object_type.to_raw()
80 )));
81 }
82 Ok(Self {
83 object_type,
84 instance_number,
85 })
86 }
87
88 pub fn new_addressable(object_type: ObjectType, instance_number: u32) -> Result<Self, Error> {
99 let identifier = Self::new(object_type, instance_number)?;
100 if instance_number == Self::WILDCARD_INSTANCE {
101 return Err(Error::OutOfRange(alloc_or_std_format!(
102 "instance number {} is reserved as the wildcard value; maximum addressable instance is {}",
103 instance_number,
104 Self::MAX_ADDRESSABLE_INSTANCE
105 )));
106 }
107 Ok(identifier)
108 }
109
110 pub const fn object_type(&self) -> ObjectType {
112 self.object_type
113 }
114
115 pub const fn instance_number(&self) -> u32 {
122 self.instance_number
123 }
124
125 pub fn encode(&self) -> [u8; 4] {
127 assert!(
128 self.object_type.to_raw() <= 0x3FF,
129 "ObjectType {} exceeds 10-bit field",
130 self.object_type.to_raw()
131 );
132 assert!(
133 self.instance_number <= Self::MAX_INSTANCE,
134 "Instance {} exceeds MAX_INSTANCE",
135 self.instance_number
136 );
137 let value = (self.object_type.to_raw() << 22) | self.instance_number;
138 value.to_be_bytes()
139 }
140
141 pub fn decode(data: &[u8]) -> Result<Self, Error> {
143 if data.len() != 4 {
144 return Err(Error::decoding(
145 0,
146 format!(
147 "ObjectIdentifier expects exactly 4 bytes, got {}",
148 data.len()
149 ),
150 ));
151 }
152 let value = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
153 let type_raw = (value >> 22) & 0x3FF;
154 let instance = value & Self::MAX_INSTANCE;
155 Ok(Self {
156 object_type: ObjectType::from_raw(type_raw),
157 instance_number: instance,
158 })
159 }
160}
161
162impl core::fmt::Debug for ObjectIdentifier {
163 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
164 write!(
165 f,
166 "ObjectIdentifier({:?}, {})",
167 self.object_type, self.instance_number
168 )
169 }
170}
171
172impl core::fmt::Display for ObjectIdentifier {
173 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
174 write!(f, "{},{}", self.object_type, self.instance_number)
175 }
176}
177
178#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
189pub struct Date {
190 pub year: u8,
192 pub month: u8,
194 pub day: u8,
196 pub day_of_week: u8,
198}
199
200impl Date {
201 pub const UNSPECIFIED: u8 = 0xFF;
203
204 pub fn encode(&self) -> [u8; 4] {
206 [self.year, self.month, self.day, self.day_of_week]
207 }
208
209 pub fn decode(data: &[u8]) -> Result<Self, Error> {
211 if data.len() != 4 {
212 return Err(Error::decoding(
213 0,
214 format!("Date expects exactly 4 bytes, got {}", data.len()),
215 ));
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(Error::decoding(
267 0,
268 format!("Time expects exactly 4 bytes, got {}", data.len()),
269 ));
270 }
271 Ok(Self {
272 hour: data[0],
273 minute: data[1],
274 second: data[2],
275 hundredths: data[3],
276 })
277 }
278}
279
280#[derive(Debug, Clone, PartialEq)]
286pub enum BACnetTimeStamp {
287 Time(Time),
289 SequenceNumber(u16),
291 DateTime {
293 date: Date,
295 time: Time,
297 },
298}
299
300bitflags::bitflags! {
305 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
307 pub struct StatusFlags: u8 {
308 const IN_ALARM = 0b1000;
310 const FAULT = 0b0100;
312 const OVERRIDDEN = 0b0010;
314 const OUT_OF_SERVICE = 0b0001;
316 }
317}
318
319impl core::fmt::Display for StatusFlags {
320 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
323 let mut first = true;
324 for (name, _) in self.iter_names() {
325 if !first {
326 f.write_str(" | ")?;
327 }
328 f.write_str(name)?;
329 first = false;
330 }
331 if first {
332 f.write_str("()")?;
333 }
334 Ok(())
335 }
336}
337
338impl core::fmt::Debug for StatusFlags {
339 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
340 core::fmt::Display::fmt(self, f)
341 }
342}
343
344#[derive(Debug, Clone, PartialEq)]
354pub enum PropertyValue {
355 Null,
357 Boolean(bool),
359 Unsigned(u64),
361 Signed(i32),
363 Real(f32),
365 Double(f64),
367 OctetString(Vec<u8>),
369 CharacterString(String),
371 BitString {
373 unused_bits: u8,
375 data: Vec<u8>,
377 },
378 Enumerated(u32),
380 Date(Date),
382 Time(Time),
384 ObjectIdentifier(ObjectIdentifier),
386 List(Vec<PropertyValue>),
392 ApplicationData(Vec<u8>),
406}
407
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(feature = "std")]
437macro_rules! alloc_or_std_format {
438 ($($arg:tt)*) => { format!($($arg)*) }
439}
440
441#[cfg(not(feature = "std"))]
442macro_rules! alloc_or_std_format {
443 ($($arg:tt)*) => { alloc::format!($($arg)*) }
444}
445
446use alloc_or_std_format;
447
448#[cfg(test)]
453mod tests {
454 use super::*;
455
456 #[test]
457 fn object_identifier_encode_decode_round_trip() {
458 let oid = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap();
459 let bytes = oid.encode();
460 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
461 assert_eq!(oid, decoded);
462 }
463
464 #[test]
465 fn object_identifier_wire_format() {
466 let oid = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap();
468 assert_eq!(oid.encode(), [0x00, 0x00, 0x00, 0x01]);
469
470 let oid = ObjectIdentifier::new(ObjectType::DEVICE, 1234).unwrap();
472 let expected = ((8u32 << 22) | 1234u32).to_be_bytes();
473 assert_eq!(oid.encode(), expected);
474 }
475
476 #[test]
477 fn object_identifier_max_instance() {
478 let oid =
479 ObjectIdentifier::new(ObjectType::DEVICE, ObjectIdentifier::MAX_INSTANCE).unwrap();
480 assert_eq!(oid.instance_number(), 0x3F_FFFF);
481 let bytes = oid.encode();
482 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
483 assert_eq!(decoded.instance_number(), 0x3F_FFFF);
484 }
485
486 #[test]
487 fn object_identifier_invalid_instance() {
488 let result = ObjectIdentifier::new(ObjectType::DEVICE, ObjectIdentifier::MAX_INSTANCE + 1);
489 assert!(result.is_err());
490 }
491
492 #[test]
493 fn object_identifier_buffer_too_short() {
494 let result = ObjectIdentifier::decode(&[0x00, 0x00]);
495 assert!(result.is_err());
496 }
497
498 #[test]
499 fn object_identifier_overlong_errors() {
500 assert!(ObjectIdentifier::decode(&[0x00, 0x00, 0x00, 0x01, 0xFF]).is_err());
501 }
502
503 #[test]
504 fn date_encode_decode_round_trip() {
505 let date = Date {
506 year: 124, month: 6,
508 day: 15,
509 day_of_week: 6, };
511 let bytes = date.encode();
512 let decoded = Date::decode(&bytes).unwrap();
513 assert_eq!(date, decoded);
514 assert_eq!(decoded.actual_year(), Some(2024));
515 }
516
517 #[test]
518 fn date_unspecified_year() {
519 let date = Date {
520 year: Date::UNSPECIFIED,
521 month: 1,
522 day: 1,
523 day_of_week: Date::UNSPECIFIED,
524 };
525 assert_eq!(date.actual_year(), None);
526 }
527
528 #[test]
529 fn date_overlong_errors() {
530 assert!(Date::decode(&[124, 6, 15, 6, 0]).is_err());
531 }
532
533 #[test]
534 fn time_encode_decode_round_trip() {
535 let time = Time {
536 hour: 14,
537 minute: 30,
538 second: 45,
539 hundredths: 50,
540 };
541 let bytes = time.encode();
542 let decoded = Time::decode(&bytes).unwrap();
543 assert_eq!(time, decoded);
544 }
545
546 #[test]
547 fn time_overlong_errors() {
548 assert!(Time::decode(&[14, 30, 45, 50, 0]).is_err());
549 }
550
551 #[test]
552 fn status_flags_operations() {
553 let flags = StatusFlags::IN_ALARM | StatusFlags::OUT_OF_SERVICE;
554 assert!(flags.contains(StatusFlags::IN_ALARM));
555 assert!(flags.contains(StatusFlags::OUT_OF_SERVICE));
556 assert!(!flags.contains(StatusFlags::FAULT));
557 assert!(!flags.contains(StatusFlags::OVERRIDDEN));
558 }
559
560 #[test]
563 fn object_identifier_instance_zero() {
564 let oid = ObjectIdentifier::new(ObjectType::DEVICE, 0).unwrap();
566 assert_eq!(oid.instance_number(), 0);
567 let bytes = oid.encode();
568 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
569 assert_eq!(decoded.instance_number(), 0);
570 assert_eq!(decoded.object_type(), ObjectType::DEVICE);
571 }
572
573 #[test]
574 fn object_identifier_all_types_instance_zero() {
575 for type_raw in [0u32, 1, 2, 3, 4, 5, 6, 8, 10, 13, 14, 17, 19] {
577 let obj_type = ObjectType::from_raw(type_raw);
578 let oid = ObjectIdentifier::new(obj_type, 0).unwrap();
579 let bytes = oid.encode();
580 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
581 assert_eq!(decoded.object_type(), obj_type, "type {type_raw} failed");
582 assert_eq!(
583 decoded.instance_number(),
584 0,
585 "type {type_raw} instance failed"
586 );
587 }
588 }
589
590 #[test]
591 fn object_identifier_wildcard_instance() {
592 let oid =
593 ObjectIdentifier::new(ObjectType::DEVICE, ObjectIdentifier::WILDCARD_INSTANCE).unwrap();
594 assert_eq!(oid.instance_number(), ObjectIdentifier::MAX_INSTANCE);
595 let bytes = oid.encode();
596 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
597 assert_eq!(decoded.instance_number(), ObjectIdentifier::MAX_INSTANCE);
598
599 let raw_wire_value = ((ObjectType::DEVICE.to_raw() << 22)
600 | ObjectIdentifier::WILDCARD_INSTANCE)
601 .to_be_bytes();
602 let decoded = ObjectIdentifier::decode(&raw_wire_value).unwrap();
603 assert_eq!(decoded.object_type(), ObjectType::DEVICE);
604 assert_eq!(
605 decoded.instance_number(),
606 ObjectIdentifier::WILDCARD_INSTANCE
607 );
608 }
609
610 #[test]
611 fn object_identifier_new_addressable_rejects_wildcard() {
612 assert_eq!(
613 ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE,
614 ObjectIdentifier::WILDCARD_INSTANCE - 1
615 );
616
617 let oid = ObjectIdentifier::new_addressable(
618 ObjectType::DEVICE,
619 ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE,
620 )
621 .unwrap();
622 assert_eq!(
623 oid.instance_number(),
624 ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE
625 );
626
627 let analog_input = ObjectIdentifier::new_addressable(
628 ObjectType::ANALOG_INPUT,
629 ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE,
630 )
631 .unwrap();
632 assert_eq!(analog_input.object_type(), ObjectType::ANALOG_INPUT);
633 assert_eq!(
634 analog_input.instance_number(),
635 ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE
636 );
637
638 let wildcard =
639 ObjectIdentifier::new_addressable(ObjectType::DEVICE, ObjectIdentifier::MAX_INSTANCE)
640 .unwrap_err();
641 assert!(wildcard.to_string().contains("wildcard"));
642
643 let too_large = ObjectIdentifier::new_addressable(
644 ObjectType::DEVICE,
645 ObjectIdentifier::MAX_INSTANCE + 1,
646 );
647 assert!(too_large.is_err());
648 }
649
650 #[test]
651 fn object_identifier_decode_extra_bytes_errors() {
652 let oid = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 42).unwrap();
653 let mut bytes = oid.encode().to_vec();
654 bytes.extend_from_slice(&[0xFF, 0xFF]);
655 assert!(ObjectIdentifier::decode(&bytes).is_err());
656 }
657
658 #[test]
659 fn object_identifier_new_rejects_type_overflow() {
660 for raw in [1024, u32::MAX] {
661 assert!(matches!(
662 ObjectIdentifier::new(ObjectType::from_raw(raw), 0),
663 Err(Error::OutOfRange(_))
664 ));
665 }
666 }
667
668 #[test]
669 fn object_identifier_addressable_rejects_type_overflow() {
670 for raw in [1024, u32::MAX] {
671 assert!(matches!(
672 ObjectIdentifier::new_addressable(ObjectType::from_raw(raw), 0),
673 Err(Error::OutOfRange(_))
674 ));
675 }
676 }
677
678 #[test]
679 fn object_identifier_checked_instance_boundaries() {
680 for instance in [ObjectIdentifier::MAX_INSTANCE + 1, u32::MAX] {
681 for constructor in [ObjectIdentifier::new, ObjectIdentifier::new_addressable] {
682 assert!(matches!(
683 constructor(ObjectType::from_raw(1023), instance),
684 Err(Error::OutOfRange(_))
685 ));
686 }
687 }
688 }
689
690 #[test]
691 fn object_identifier_wire_boundaries_preserve_identity() {
692 for (raw_type, instance, wire) in [
693 (0, 0, [0, 0, 0, 0]),
694 (1023, 0, [0xff, 0xc0, 0, 0]),
695 (
696 1023,
697 ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE,
698 [0xff, 0xff, 0xff, 0xfe],
699 ),
700 (
701 1023,
702 ObjectIdentifier::MAX_INSTANCE,
703 [0xff, 0xff, 0xff, 0xff],
704 ),
705 ] {
706 let object_type = ObjectType::from_raw(raw_type);
707 let oid = ObjectIdentifier::new(object_type, instance).unwrap();
708 assert_eq!(oid.encode(), wire);
709 assert_eq!(ObjectIdentifier::decode(&wire).unwrap(), oid);
710 if instance == ObjectIdentifier::WILDCARD_INSTANCE {
711 assert!(ObjectIdentifier::new_addressable(object_type, instance).is_err());
712 } else {
713 assert_eq!(
714 ObjectIdentifier::new_addressable(object_type, instance).unwrap(),
715 oid
716 );
717 }
718 }
719 assert_eq!(ObjectType::from_raw(u32::MAX).to_raw(), u32::MAX);
721 }
722
723 #[test]
724 fn property_value_variants() {
725 let null = PropertyValue::Null;
726 let boolean = PropertyValue::Boolean(true);
727 let real = PropertyValue::Real(72.5);
728 let string = PropertyValue::CharacterString("test".into());
729
730 assert_eq!(null, PropertyValue::Null);
731 assert_eq!(boolean, PropertyValue::Boolean(true));
732 assert_ne!(real, PropertyValue::Real(73.0));
733 assert_eq!(string, PropertyValue::CharacterString("test".into()));
734 }
735}