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)]
28pub struct ObjectIdentifier {
29 object_type: ObjectType,
30 instance_number: u32,
31}
32
33impl ObjectIdentifier {
34 pub const MAX_INSTANCE: u32 = 0x3F_FFFF;
42
43 pub const WILDCARD_INSTANCE: u32 = Self::MAX_INSTANCE;
50
51 pub const MAX_ADDRESSABLE_INSTANCE: u32 = Self::WILDCARD_INSTANCE - 1;
55
56 pub fn new(object_type: ObjectType, instance_number: u32) -> Result<Self, Error> {
66 if instance_number > Self::MAX_INSTANCE {
67 return Err(Error::OutOfRange(alloc_or_std_format!(
68 "instance number {} exceeds max {}",
69 instance_number,
70 Self::MAX_INSTANCE
71 )));
72 }
73 Ok(Self {
74 object_type,
75 instance_number,
76 })
77 }
78
79 pub fn new_addressable(object_type: ObjectType, instance_number: u32) -> Result<Self, Error> {
90 if instance_number > Self::MAX_INSTANCE {
91 return Err(Error::OutOfRange(alloc_or_std_format!(
92 "instance number {} exceeds max {}",
93 instance_number,
94 Self::MAX_INSTANCE
95 )));
96 }
97 if instance_number == Self::WILDCARD_INSTANCE {
98 return Err(Error::OutOfRange(alloc_or_std_format!(
99 "instance number {} is reserved as the wildcard value; maximum addressable instance is {}",
100 instance_number,
101 Self::MAX_ADDRESSABLE_INSTANCE
102 )));
103 }
104 Ok(Self {
105 object_type,
106 instance_number,
107 })
108 }
109
110 pub const fn new_unchecked(object_type: ObjectType, instance_number: u32) -> Self {
112 Self {
113 object_type,
114 instance_number,
115 }
116 }
117
118 pub const fn object_type(&self) -> ObjectType {
120 self.object_type
121 }
122
123 pub const fn instance_number(&self) -> u32 {
130 self.instance_number
131 }
132
133 pub fn encode(&self) -> [u8; 4] {
135 debug_assert!(
136 self.object_type.to_raw() <= 0x3FF,
137 "ObjectType {} exceeds 10-bit field",
138 self.object_type.to_raw()
139 );
140 debug_assert!(
141 self.instance_number <= Self::MAX_INSTANCE,
142 "Instance {} exceeds MAX_INSTANCE",
143 self.instance_number
144 );
145 let value = ((self.object_type.to_raw() & 0x3FF) << 22)
146 | (self.instance_number & Self::MAX_INSTANCE);
147 value.to_be_bytes()
148 }
149
150 pub fn decode(data: &[u8]) -> Result<Self, Error> {
152 if data.len() != 4 {
153 return Err(Error::decoding(
154 0,
155 format!(
156 "ObjectIdentifier expects exactly 4 bytes, got {}",
157 data.len()
158 ),
159 ));
160 }
161 let value = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
162 let type_raw = (value >> 22) & 0x3FF;
163 let instance = value & Self::MAX_INSTANCE;
164 Ok(Self {
165 object_type: ObjectType::from_raw(type_raw),
166 instance_number: instance,
167 })
168 }
169}
170
171impl core::fmt::Debug for ObjectIdentifier {
172 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
173 write!(
174 f,
175 "ObjectIdentifier({:?}, {})",
176 self.object_type, self.instance_number
177 )
178 }
179}
180
181impl core::fmt::Display for ObjectIdentifier {
182 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
183 write!(f, "{},{}", self.object_type, self.instance_number)
184 }
185}
186
187#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
198pub struct Date {
199 pub year: u8,
201 pub month: u8,
203 pub day: u8,
205 pub day_of_week: u8,
207}
208
209impl Date {
210 pub const UNSPECIFIED: u8 = 0xFF;
212
213 pub fn encode(&self) -> [u8; 4] {
215 [self.year, self.month, self.day, self.day_of_week]
216 }
217
218 pub fn decode(data: &[u8]) -> Result<Self, Error> {
220 if data.len() != 4 {
221 return Err(Error::decoding(
222 0,
223 format!("Date expects exactly 4 bytes, got {}", data.len()),
224 ));
225 }
226 Ok(Self {
227 year: data[0],
228 month: data[1],
229 day: data[2],
230 day_of_week: data[3],
231 })
232 }
233
234 pub fn actual_year(&self) -> Option<u16> {
236 if self.year == Self::UNSPECIFIED {
237 None
238 } else {
239 Some(1900 + self.year as u16)
240 }
241 }
242}
243
244#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
252pub struct Time {
253 pub hour: u8,
255 pub minute: u8,
257 pub second: u8,
259 pub hundredths: u8,
261}
262
263impl Time {
264 pub const UNSPECIFIED: u8 = 0xFF;
266
267 pub fn encode(&self) -> [u8; 4] {
269 [self.hour, self.minute, self.second, self.hundredths]
270 }
271
272 pub fn decode(data: &[u8]) -> Result<Self, Error> {
274 if data.len() != 4 {
275 return Err(Error::decoding(
276 0,
277 format!("Time expects exactly 4 bytes, got {}", data.len()),
278 ));
279 }
280 Ok(Self {
281 hour: data[0],
282 minute: data[1],
283 second: data[2],
284 hundredths: data[3],
285 })
286 }
287}
288
289#[derive(Debug, Clone, PartialEq)]
295pub enum BACnetTimeStamp {
296 Time(Time),
298 SequenceNumber(u16),
300 DateTime { date: Date, time: Time },
302}
303
304bitflags::bitflags! {
309 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
311 pub struct StatusFlags: u8 {
312 const IN_ALARM = 0b1000;
313 const FAULT = 0b0100;
314 const OVERRIDDEN = 0b0010;
315 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}
343bitflags::bitflags! {
344 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
348 pub struct DaysOfWeek: u8 {
349 const MONDAY = 0b0100_0000;
350 const TUESDAY = 0b0010_0000;
351 const WEDNESDAY = 0b0001_0000;
352 const THURSDAY = 0b0000_1000;
353 const FRIDAY = 0b0000_0100;
354 const SATURDAY = 0b0000_0010;
355 const SUNDAY = 0b0000_0001;
356 const ALL = 0b0111_1111;
357 }
358}
359
360#[derive(Debug, Clone, PartialEq)]
370pub enum PropertyValue {
371 Null,
373 Boolean(bool),
375 Unsigned(u64),
377 Signed(i32),
379 Real(f32),
381 Double(f64),
383 OctetString(Vec<u8>),
385 CharacterString(String),
387 BitString {
389 unused_bits: u8,
391 data: Vec<u8>,
393 },
394 Enumerated(u32),
396 Date(Date),
398 Time(Time),
400 ObjectIdentifier(ObjectIdentifier),
402 List(Vec<PropertyValue>),
408 ApplicationData(Vec<u8>),
423}
424
425#[cfg(feature = "std")]
431macro_rules! alloc_or_std_format {
432 ($($arg:tt)*) => { format!($($arg)*) }
433}
434
435#[cfg(not(feature = "std"))]
436macro_rules! alloc_or_std_format {
437 ($($arg:tt)*) => { alloc::format!($($arg)*) }
438}
439
440use alloc_or_std_format;
441
442#[cfg(test)]
447mod tests {
448 use super::*;
449
450 #[test]
451 fn object_identifier_encode_decode_round_trip() {
452 let oid = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap();
453 let bytes = oid.encode();
454 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
455 assert_eq!(oid, decoded);
456 }
457
458 #[test]
459 fn object_identifier_wire_format() {
460 let oid = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap();
462 assert_eq!(oid.encode(), [0x00, 0x00, 0x00, 0x01]);
463
464 let oid = ObjectIdentifier::new(ObjectType::DEVICE, 1234).unwrap();
466 let expected = ((8u32 << 22) | 1234u32).to_be_bytes();
467 assert_eq!(oid.encode(), expected);
468 }
469
470 #[test]
471 fn object_identifier_max_instance() {
472 let oid =
473 ObjectIdentifier::new(ObjectType::DEVICE, ObjectIdentifier::MAX_INSTANCE).unwrap();
474 assert_eq!(oid.instance_number(), 0x3F_FFFF);
475 let bytes = oid.encode();
476 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
477 assert_eq!(decoded.instance_number(), 0x3F_FFFF);
478 }
479
480 #[test]
481 fn object_identifier_invalid_instance() {
482 let result = ObjectIdentifier::new(ObjectType::DEVICE, ObjectIdentifier::MAX_INSTANCE + 1);
483 assert!(result.is_err());
484 }
485
486 #[test]
487 fn object_identifier_buffer_too_short() {
488 let result = ObjectIdentifier::decode(&[0x00, 0x00]);
489 assert!(result.is_err());
490 }
491
492 #[test]
493 fn object_identifier_overlong_errors() {
494 assert!(ObjectIdentifier::decode(&[0x00, 0x00, 0x00, 0x01, 0xFF]).is_err());
495 }
496
497 #[test]
498 fn date_encode_decode_round_trip() {
499 let date = Date {
500 year: 124, month: 6,
502 day: 15,
503 day_of_week: 6, };
505 let bytes = date.encode();
506 let decoded = Date::decode(&bytes).unwrap();
507 assert_eq!(date, decoded);
508 assert_eq!(decoded.actual_year(), Some(2024));
509 }
510
511 #[test]
512 fn date_unspecified_year() {
513 let date = Date {
514 year: Date::UNSPECIFIED,
515 month: 1,
516 day: 1,
517 day_of_week: Date::UNSPECIFIED,
518 };
519 assert_eq!(date.actual_year(), None);
520 }
521
522 #[test]
523 fn date_overlong_errors() {
524 assert!(Date::decode(&[124, 6, 15, 6, 0]).is_err());
525 }
526
527 #[test]
528 fn time_encode_decode_round_trip() {
529 let time = Time {
530 hour: 14,
531 minute: 30,
532 second: 45,
533 hundredths: 50,
534 };
535 let bytes = time.encode();
536 let decoded = Time::decode(&bytes).unwrap();
537 assert_eq!(time, decoded);
538 }
539
540 #[test]
541 fn time_overlong_errors() {
542 assert!(Time::decode(&[14, 30, 45, 50, 0]).is_err());
543 }
544
545 #[test]
546 fn status_flags_operations() {
547 let flags = StatusFlags::IN_ALARM | StatusFlags::OUT_OF_SERVICE;
548 assert!(flags.contains(StatusFlags::IN_ALARM));
549 assert!(flags.contains(StatusFlags::OUT_OF_SERVICE));
550 assert!(!flags.contains(StatusFlags::FAULT));
551 assert!(!flags.contains(StatusFlags::OVERRIDDEN));
552 }
553
554 #[test]
557 fn object_identifier_instance_zero() {
558 let oid = ObjectIdentifier::new(ObjectType::DEVICE, 0).unwrap();
560 assert_eq!(oid.instance_number(), 0);
561 let bytes = oid.encode();
562 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
563 assert_eq!(decoded.instance_number(), 0);
564 assert_eq!(decoded.object_type(), ObjectType::DEVICE);
565 }
566
567 #[test]
568 fn object_identifier_all_types_instance_zero() {
569 for type_raw in [0u32, 1, 2, 3, 4, 5, 6, 8, 10, 13, 14, 17, 19] {
571 let obj_type = ObjectType::from_raw(type_raw);
572 let oid = ObjectIdentifier::new(obj_type, 0).unwrap();
573 let bytes = oid.encode();
574 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
575 assert_eq!(decoded.object_type(), obj_type, "type {type_raw} failed");
576 assert_eq!(
577 decoded.instance_number(),
578 0,
579 "type {type_raw} instance failed"
580 );
581 }
582 }
583
584 #[test]
585 fn object_identifier_wildcard_instance() {
586 let oid =
587 ObjectIdentifier::new(ObjectType::DEVICE, ObjectIdentifier::WILDCARD_INSTANCE).unwrap();
588 assert_eq!(oid.instance_number(), ObjectIdentifier::MAX_INSTANCE);
589 let bytes = oid.encode();
590 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
591 assert_eq!(decoded.instance_number(), ObjectIdentifier::MAX_INSTANCE);
592
593 let raw_wire_value = ((ObjectType::DEVICE.to_raw() << 22)
594 | ObjectIdentifier::WILDCARD_INSTANCE)
595 .to_be_bytes();
596 let decoded = ObjectIdentifier::decode(&raw_wire_value).unwrap();
597 assert_eq!(decoded.object_type(), ObjectType::DEVICE);
598 assert_eq!(
599 decoded.instance_number(),
600 ObjectIdentifier::WILDCARD_INSTANCE
601 );
602 }
603
604 #[test]
605 fn object_identifier_new_addressable_rejects_wildcard() {
606 assert_eq!(
607 ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE,
608 ObjectIdentifier::WILDCARD_INSTANCE - 1
609 );
610
611 let oid = ObjectIdentifier::new_addressable(
612 ObjectType::DEVICE,
613 ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE,
614 )
615 .unwrap();
616 assert_eq!(
617 oid.instance_number(),
618 ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE
619 );
620
621 let analog_input = ObjectIdentifier::new_addressable(
622 ObjectType::ANALOG_INPUT,
623 ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE,
624 )
625 .unwrap();
626 assert_eq!(analog_input.object_type(), ObjectType::ANALOG_INPUT);
627 assert_eq!(
628 analog_input.instance_number(),
629 ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE
630 );
631
632 let wildcard =
633 ObjectIdentifier::new_addressable(ObjectType::DEVICE, ObjectIdentifier::MAX_INSTANCE)
634 .unwrap_err();
635 assert!(wildcard.to_string().contains("wildcard"));
636
637 let too_large = ObjectIdentifier::new_addressable(
638 ObjectType::DEVICE,
639 ObjectIdentifier::MAX_INSTANCE + 1,
640 );
641 assert!(too_large.is_err());
642 }
643
644 #[test]
645 fn object_identifier_decode_extra_bytes_errors() {
646 let oid = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 42).unwrap();
647 let mut bytes = oid.encode().to_vec();
648 bytes.extend_from_slice(&[0xFF, 0xFF]);
649 assert!(ObjectIdentifier::decode(&bytes).is_err());
650 }
651
652 #[test]
653 #[cfg_attr(debug_assertions, should_panic(expected = "exceeds 10-bit field"))]
654 fn object_identifier_type_overflow_round_trip() {
655 let oid = ObjectIdentifier::new_unchecked(ObjectType::from_raw(1024), 0);
658 let bytes = oid.encode();
659 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
660 assert_eq!(decoded.object_type(), ObjectType::from_raw(0));
661 }
662
663 #[test]
664 fn property_value_variants() {
665 let null = PropertyValue::Null;
666 let boolean = PropertyValue::Boolean(true);
667 let real = PropertyValue::Real(72.5);
668 let string = PropertyValue::CharacterString("test".into());
669
670 assert_eq!(null, PropertyValue::Null);
671 assert_eq!(boolean, PropertyValue::Boolean(true));
672 assert_ne!(real, PropertyValue::Real(73.0));
673 assert_eq!(string, PropertyValue::CharacterString("test".into()));
674 }
675}