1#[cfg(not(feature = "std"))]
7use alloc::{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(u64),
300 DateTime { date: Date, time: Time },
302}
303
304bitflags::bitflags! {
309 #[derive(Debug, 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
319bitflags::bitflags! {
320 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
324 pub struct DaysOfWeek: u8 {
325 const MONDAY = 0b0100_0000;
326 const TUESDAY = 0b0010_0000;
327 const WEDNESDAY = 0b0001_0000;
328 const THURSDAY = 0b0000_1000;
329 const FRIDAY = 0b0000_0100;
330 const SATURDAY = 0b0000_0010;
331 const SUNDAY = 0b0000_0001;
332 const ALL = 0b0111_1111;
333 }
334}
335
336#[derive(Debug, Clone, PartialEq)]
346pub enum PropertyValue {
347 Null,
349 Boolean(bool),
351 Unsigned(u64),
353 Signed(i32),
355 Real(f32),
357 Double(f64),
359 OctetString(Vec<u8>),
361 CharacterString(String),
363 BitString {
365 unused_bits: u8,
367 data: Vec<u8>,
369 },
370 Enumerated(u32),
372 Date(Date),
374 Time(Time),
376 ObjectIdentifier(ObjectIdentifier),
378 List(Vec<PropertyValue>),
384}
385
386#[cfg(feature = "std")]
392macro_rules! alloc_or_std_format {
393 ($($arg:tt)*) => { format!($($arg)*) }
394}
395
396#[cfg(not(feature = "std"))]
397macro_rules! alloc_or_std_format {
398 ($($arg:tt)*) => { alloc::format!($($arg)*) }
399}
400
401use alloc_or_std_format;
402
403#[cfg(test)]
408mod tests {
409 use super::*;
410
411 #[test]
412 fn object_identifier_encode_decode_round_trip() {
413 let oid = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap();
414 let bytes = oid.encode();
415 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
416 assert_eq!(oid, decoded);
417 }
418
419 #[test]
420 fn object_identifier_wire_format() {
421 let oid = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap();
423 assert_eq!(oid.encode(), [0x00, 0x00, 0x00, 0x01]);
424
425 let oid = ObjectIdentifier::new(ObjectType::DEVICE, 1234).unwrap();
427 let expected = ((8u32 << 22) | 1234u32).to_be_bytes();
428 assert_eq!(oid.encode(), expected);
429 }
430
431 #[test]
432 fn object_identifier_max_instance() {
433 let oid =
434 ObjectIdentifier::new(ObjectType::DEVICE, ObjectIdentifier::MAX_INSTANCE).unwrap();
435 assert_eq!(oid.instance_number(), 0x3F_FFFF);
436 let bytes = oid.encode();
437 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
438 assert_eq!(decoded.instance_number(), 0x3F_FFFF);
439 }
440
441 #[test]
442 fn object_identifier_invalid_instance() {
443 let result = ObjectIdentifier::new(ObjectType::DEVICE, ObjectIdentifier::MAX_INSTANCE + 1);
444 assert!(result.is_err());
445 }
446
447 #[test]
448 fn object_identifier_buffer_too_short() {
449 let result = ObjectIdentifier::decode(&[0x00, 0x00]);
450 assert!(result.is_err());
451 }
452
453 #[test]
454 fn object_identifier_overlong_errors() {
455 assert!(ObjectIdentifier::decode(&[0x00, 0x00, 0x00, 0x01, 0xFF]).is_err());
456 }
457
458 #[test]
459 fn date_encode_decode_round_trip() {
460 let date = Date {
461 year: 124, month: 6,
463 day: 15,
464 day_of_week: 6, };
466 let bytes = date.encode();
467 let decoded = Date::decode(&bytes).unwrap();
468 assert_eq!(date, decoded);
469 assert_eq!(decoded.actual_year(), Some(2024));
470 }
471
472 #[test]
473 fn date_unspecified_year() {
474 let date = Date {
475 year: Date::UNSPECIFIED,
476 month: 1,
477 day: 1,
478 day_of_week: Date::UNSPECIFIED,
479 };
480 assert_eq!(date.actual_year(), None);
481 }
482
483 #[test]
484 fn date_overlong_errors() {
485 assert!(Date::decode(&[124, 6, 15, 6, 0]).is_err());
486 }
487
488 #[test]
489 fn time_encode_decode_round_trip() {
490 let time = Time {
491 hour: 14,
492 minute: 30,
493 second: 45,
494 hundredths: 50,
495 };
496 let bytes = time.encode();
497 let decoded = Time::decode(&bytes).unwrap();
498 assert_eq!(time, decoded);
499 }
500
501 #[test]
502 fn time_overlong_errors() {
503 assert!(Time::decode(&[14, 30, 45, 50, 0]).is_err());
504 }
505
506 #[test]
507 fn status_flags_operations() {
508 let flags = StatusFlags::IN_ALARM | StatusFlags::OUT_OF_SERVICE;
509 assert!(flags.contains(StatusFlags::IN_ALARM));
510 assert!(flags.contains(StatusFlags::OUT_OF_SERVICE));
511 assert!(!flags.contains(StatusFlags::FAULT));
512 assert!(!flags.contains(StatusFlags::OVERRIDDEN));
513 }
514
515 #[test]
518 fn object_identifier_instance_zero() {
519 let oid = ObjectIdentifier::new(ObjectType::DEVICE, 0).unwrap();
521 assert_eq!(oid.instance_number(), 0);
522 let bytes = oid.encode();
523 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
524 assert_eq!(decoded.instance_number(), 0);
525 assert_eq!(decoded.object_type(), ObjectType::DEVICE);
526 }
527
528 #[test]
529 fn object_identifier_all_types_instance_zero() {
530 for type_raw in [0u32, 1, 2, 3, 4, 5, 6, 8, 10, 13, 14, 17, 19] {
532 let obj_type = ObjectType::from_raw(type_raw);
533 let oid = ObjectIdentifier::new(obj_type, 0).unwrap();
534 let bytes = oid.encode();
535 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
536 assert_eq!(decoded.object_type(), obj_type, "type {type_raw} failed");
537 assert_eq!(
538 decoded.instance_number(),
539 0,
540 "type {type_raw} instance failed"
541 );
542 }
543 }
544
545 #[test]
546 fn object_identifier_wildcard_instance() {
547 let oid =
548 ObjectIdentifier::new(ObjectType::DEVICE, ObjectIdentifier::WILDCARD_INSTANCE).unwrap();
549 assert_eq!(oid.instance_number(), ObjectIdentifier::MAX_INSTANCE);
550 let bytes = oid.encode();
551 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
552 assert_eq!(decoded.instance_number(), ObjectIdentifier::MAX_INSTANCE);
553
554 let raw_wire_value = ((ObjectType::DEVICE.to_raw() << 22)
555 | ObjectIdentifier::WILDCARD_INSTANCE)
556 .to_be_bytes();
557 let decoded = ObjectIdentifier::decode(&raw_wire_value).unwrap();
558 assert_eq!(decoded.object_type(), ObjectType::DEVICE);
559 assert_eq!(
560 decoded.instance_number(),
561 ObjectIdentifier::WILDCARD_INSTANCE
562 );
563 }
564
565 #[test]
566 fn object_identifier_new_addressable_rejects_wildcard() {
567 assert_eq!(
568 ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE,
569 ObjectIdentifier::WILDCARD_INSTANCE - 1
570 );
571
572 let oid = ObjectIdentifier::new_addressable(
573 ObjectType::DEVICE,
574 ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE,
575 )
576 .unwrap();
577 assert_eq!(
578 oid.instance_number(),
579 ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE
580 );
581
582 let analog_input = ObjectIdentifier::new_addressable(
583 ObjectType::ANALOG_INPUT,
584 ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE,
585 )
586 .unwrap();
587 assert_eq!(analog_input.object_type(), ObjectType::ANALOG_INPUT);
588 assert_eq!(
589 analog_input.instance_number(),
590 ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE
591 );
592
593 let wildcard =
594 ObjectIdentifier::new_addressable(ObjectType::DEVICE, ObjectIdentifier::MAX_INSTANCE)
595 .unwrap_err();
596 assert!(wildcard.to_string().contains("wildcard"));
597
598 let too_large = ObjectIdentifier::new_addressable(
599 ObjectType::DEVICE,
600 ObjectIdentifier::MAX_INSTANCE + 1,
601 );
602 assert!(too_large.is_err());
603 }
604
605 #[test]
606 fn object_identifier_decode_extra_bytes_errors() {
607 let oid = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 42).unwrap();
608 let mut bytes = oid.encode().to_vec();
609 bytes.extend_from_slice(&[0xFF, 0xFF]);
610 assert!(ObjectIdentifier::decode(&bytes).is_err());
611 }
612
613 #[test]
614 #[cfg_attr(debug_assertions, should_panic(expected = "exceeds 10-bit field"))]
615 fn object_identifier_type_overflow_round_trip() {
616 let oid = ObjectIdentifier::new_unchecked(ObjectType::from_raw(1024), 0);
619 let bytes = oid.encode();
620 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
621 assert_eq!(decoded.object_type(), ObjectType::from_raw(0));
622 }
623
624 #[test]
625 fn property_value_variants() {
626 let null = PropertyValue::Null;
627 let boolean = PropertyValue::Boolean(true);
628 let real = PropertyValue::Real(72.5);
629 let string = PropertyValue::CharacterString("test".into());
630
631 assert_eq!(null, PropertyValue::Null);
632 assert_eq!(boolean, PropertyValue::Boolean(true));
633 assert_ne!(real, PropertyValue::Real(73.0));
634 assert_eq!(string, PropertyValue::CharacterString("test".into()));
635 }
636}