1#[cfg(not(feature = "std"))]
22use alloc::{format, vec::Vec};
23
24use crate::enums::{AuditOperation, ObjectType, ServiceSupported};
25use crate::error::{Error, Result};
26
27pub use crate::primitives::StatusFlags;
28pub use priority_filter::BACnetPriorityFilter;
29
30mod priority_filter;
31
32fn wire_bit(data: &[u8], n: usize) -> bool {
37 let mask = 0x80u8 >> (n % 8);
38 data.get(n / 8).is_some_and(|b| b & mask != 0)
39}
40
41pub fn pack_octet(bits_lsb0: u8) -> u8 {
49 bits_lsb0.reverse_bits()
50}
51
52pub fn unpack_octet(data: &[u8], defined_bits: u32) -> u8 {
58 let mask = if defined_bits >= 8 {
59 u8::MAX
60 } else {
61 (1u8 << defined_bits) - 1
62 };
63 data.first().copied().unwrap_or(0).reverse_bits() & mask
64}
65
66pub fn status_flags_from_bacnet(data: &[u8]) -> StatusFlags {
73 let mut flags = StatusFlags::empty();
74 flags.set(StatusFlags::IN_ALARM, wire_bit(data, 0));
75 flags.set(StatusFlags::FAULT, wire_bit(data, 1));
76 flags.set(StatusFlags::OVERRIDDEN, wire_bit(data, 2));
77 flags.set(StatusFlags::OUT_OF_SERVICE, wire_bit(data, 3));
78 flags
79}
80
81macro_rules! impl_named_bit_display {
84 ($t:ty) => {
85 impl core::fmt::Display for $t {
86 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
87 let mut first = true;
88 for (name, _) in self.iter_names() {
89 if !first {
90 f.write_str(" | ")?;
91 }
92 f.write_str(name)?;
93 first = false;
94 }
95 if first {
96 f.write_str("()")?;
97 }
98 Ok(())
99 }
100 }
101
102 impl core::fmt::Debug for $t {
103 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
104 core::fmt::Display::fmt(self, f)
105 }
106 }
107 };
108}
109
110fn write_joined<T: core::fmt::Display>(
112 f: &mut core::fmt::Formatter<'_>,
113 items: impl Iterator<Item = T>,
114) -> core::fmt::Result {
115 let mut first = true;
116 for item in items {
117 if !first {
118 f.write_str(" | ")?;
119 }
120 write!(f, "{item}")?;
121 first = false;
122 }
123 if first {
124 f.write_str("()")?;
125 }
126 Ok(())
127}
128
129bitflags::bitflags! {
130 #[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
133 pub struct EventTransitionBits: u8 {
134 const TO_OFFNORMAL = 1 << 0;
136 const TO_FAULT = 1 << 1;
138 const TO_NORMAL = 1 << 2;
140 }
141}
142
143impl EventTransitionBits {
144 pub fn from_bacnet(data: &[u8]) -> Self {
146 let mut bits = Self::empty();
147 bits.set(Self::TO_OFFNORMAL, wire_bit(data, 0));
148 bits.set(Self::TO_FAULT, wire_bit(data, 1));
149 bits.set(Self::TO_NORMAL, wire_bit(data, 2));
150 bits
151 }
152
153 pub fn to_bacnet(self) -> u8 {
156 pack_octet(self.bits())
157 }
158}
159
160impl_named_bit_display!(EventTransitionBits);
161
162bitflags::bitflags! {
163 #[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
166 pub struct LimitEnable: u8 {
167 const LOW_LIMIT_ENABLE = 1 << 0;
169 const HIGH_LIMIT_ENABLE = 1 << 1;
171 }
172}
173
174impl LimitEnable {
175 pub fn from_bacnet(data: &[u8]) -> Self {
177 let mut bits = Self::empty();
178 bits.set(Self::LOW_LIMIT_ENABLE, wire_bit(data, 0));
179 bits.set(Self::HIGH_LIMIT_ENABLE, wire_bit(data, 1));
180 bits
181 }
182
183 pub fn to_bacnet(self) -> u8 {
186 pack_octet(self.bits())
187 }
188}
189
190impl_named_bit_display!(LimitEnable);
191
192#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
202pub struct ServicesSupported {
203 bits: u64,
204}
205
206impl ServicesSupported {
207 pub fn from_bacnet(data: &[u8]) -> Self {
210 let mut bits = 0u64;
211 for (i, &byte) in data.iter().take(8).enumerate() {
212 bits |= (byte.reverse_bits() as u64) << (i * 8);
213 }
214 Self { bits }
215 }
216
217 pub fn contains(&self, service: ServiceSupported) -> bool {
219 let n = service.to_raw() as u32;
220 n < 64 && self.bits & (1u64 << n) != 0
221 }
222
223 pub fn iter(&self) -> impl Iterator<Item = ServiceSupported> + '_ {
225 let bits = self.bits;
226 (0..64)
227 .filter(move |&n| bits & (1u64 << n) != 0)
228 .map(|n| ServiceSupported::from_raw(n as u8))
229 }
230}
231
232impl core::fmt::Display for ServicesSupported {
233 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
234 write_joined(f, self.iter())
235 }
236}
237
238impl core::fmt::Debug for ServicesSupported {
239 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
240 core::fmt::Display::fmt(self, f)
241 }
242}
243
244#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
257pub struct ObjectTypesSupported {
258 bits: [u64; 16],
259}
260
261impl ObjectTypesSupported {
262 pub fn from_bacnet(data: &[u8]) -> Self {
265 let mut bits = [0u64; 16];
266 for (i, &byte) in data.iter().take(128).enumerate() {
267 bits[i / 8] |= (byte.reverse_bits() as u64) << ((i % 8) * 8);
268 }
269 Self { bits }
270 }
271
272 pub fn contains(&self, object_type: ObjectType) -> bool {
274 let n = object_type.to_raw();
275 n < 1024 && self.bits[(n / 64) as usize] & (1u64 << (n % 64)) != 0
276 }
277
278 pub fn iter(&self) -> impl Iterator<Item = ObjectType> + '_ {
280 (0..1024u32)
281 .filter(move |&n| self.bits[(n / 64) as usize] & (1u64 << (n % 64)) != 0)
282 .map(ObjectType::from_raw)
283 }
284}
285
286impl core::fmt::Display for ObjectTypesSupported {
287 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
288 write_joined(f, self.iter())
289 }
290}
291
292impl core::fmt::Debug for ObjectTypesSupported {
293 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
294 core::fmt::Display::fmt(self, f)
295 }
296}
297
298#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
306pub struct AuditOperationFlags {
307 bits: u64,
308}
309
310impl AuditOperationFlags {
311 const RESERVED_BITS: u64 = 0x0000_0000_ffff_0000;
312
313 #[inline]
315 pub const fn empty() -> Self {
316 Self { bits: 0 }
317 }
318
319 #[inline]
324 pub fn from_bits(bits: u64) -> Result<Self> {
325 if bits & Self::RESERVED_BITS != 0 {
326 return Err(Error::OutOfRange(
327 "BACnetAuditOperationFlags: reserved bits 16-31 must be zero".into(),
328 ));
329 }
330 Ok(Self { bits })
331 }
332
333 #[inline]
335 pub const fn bits(self) -> u64 {
336 self.bits
337 }
338
339 #[inline]
341 pub const fn is_empty(self) -> bool {
342 self.bits == 0
343 }
344
345 #[inline]
350 pub const fn contains(self, operation: AuditOperation) -> bool {
351 let bit = operation.to_raw();
352 Self::valid_bit(bit) && self.bits & (1u64 << bit) != 0
353 }
354
355 #[inline]
358 pub fn insert(&mut self, operation: AuditOperation) -> bool {
359 let bit = operation.to_raw();
360 if !Self::valid_bit(bit) {
361 return false;
362 }
363 self.bits |= 1u64 << bit;
364 true
365 }
366
367 #[inline]
370 pub fn remove(&mut self, operation: AuditOperation) -> bool {
371 let bit = operation.to_raw();
372 if !Self::valid_bit(bit) {
373 return false;
374 }
375 self.bits &= !(1u64 << bit);
376 true
377 }
378
379 pub fn iter(self) -> impl Iterator<Item = AuditOperation> {
382 let bits = self.bits;
383 (0..64)
384 .filter(move |&bit| bits & (1u64 << bit) != 0)
385 .map(AuditOperation::from_raw)
386 }
387
388 const fn valid_bit(bit: u32) -> bool {
389 bit <= 15 || (bit >= 32 && bit <= 63)
390 }
391
392 pub fn from_bacnet(unused_bits: u8, data: &[u8]) -> Result<Self> {
399 if unused_bits > 7 {
400 return Err(Error::decoding(
401 0,
402 format!("BACnetAuditOperationFlags: unused_bits must be 0-7, got {unused_bits}"),
403 ));
404 }
405 if data.is_empty() {
406 if unused_bits != 0 {
407 return Err(Error::decoding(
408 0,
409 "BACnetAuditOperationFlags: an empty bit string must have zero unused bits",
410 ));
411 }
412 return Ok(Self::empty());
413 }
414
415 let meaningful_bits = data.len() * 8 - usize::from(unused_bits);
416 if meaningful_bits > 64 {
417 return Err(Error::decoding(
418 0,
419 format!(
420 "BACnetAuditOperationFlags: {meaningful_bits} bits exceeds the 64-bit limit"
421 ),
422 ));
423 }
424 if unused_bits != 0 {
425 let padding_mask = (1u8 << unused_bits) - 1;
426 if data.last().is_some_and(|last| last & padding_mask != 0) {
427 return Err(Error::decoding(
428 0,
429 "BACnetAuditOperationFlags: final-octet padding bits must be zero",
430 ));
431 }
432 }
433
434 let mut bits = 0u64;
435 for (index, &byte) in data.iter().enumerate() {
436 bits |= u64::from(byte.reverse_bits()) << (index * 8);
437 }
438 if bits & Self::RESERVED_BITS != 0 {
439 return Err(Error::decoding(
440 0,
441 "BACnetAuditOperationFlags: reserved bits 16-31 must be zero",
442 ));
443 }
444 Ok(Self { bits })
445 }
446
447 pub fn to_bacnet(self) -> (u8, Vec<u8>) {
452 if self.bits == 0 {
453 return (0, Vec::new());
454 }
455
456 let bit_len = (u64::BITS - self.bits.leading_zeros()) as usize;
457 let byte_len = bit_len.div_ceil(8);
458 let unused_bits = (byte_len * 8 - bit_len) as u8;
459 let mut data = Vec::with_capacity(byte_len);
460 for index in 0..byte_len {
461 data.push(((self.bits >> (index * 8)) as u8).reverse_bits());
462 }
463 (unused_bits, data)
464 }
465}
466
467impl core::fmt::Display for AuditOperationFlags {
468 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
469 write_joined(f, self.iter())
470 }
471}
472
473impl core::fmt::Debug for AuditOperationFlags {
474 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
475 core::fmt::Display::fmt(self, f)
476 }
477}
478
479#[cfg(test)]
480mod tests {
481 use super::*;
482 use crate::primitives::StatusFlags;
483
484 #[test]
485 fn pack_unpack_octet_spec_vectors() {
486 assert_eq!(pack_octet(0b001), 0x80); assert_eq!(pack_octet(0b100), 0x20); assert_eq!(pack_octet(0b0100_0000), 0x02); assert_eq!(unpack_octet(&[0x80], 3), 0b001);
493 assert_eq!(unpack_octet(&[0x20], 3), 0b100);
494 assert_eq!(unpack_octet(&[0xFE], 7), 0x7F); assert_eq!(unpack_octet(&[], 3), 0);
496 assert_eq!(unpack_octet(&[0xFF], 3), 0b111);
498 assert_eq!(
500 EventTransitionBits::from_bacnet(&[pack_octet(0b001)]),
501 EventTransitionBits::TO_OFFNORMAL
502 );
503 assert_eq!(EventTransitionBits::TO_OFFNORMAL.to_bacnet(), 0x80);
504 assert_eq!(LimitEnable::LOW_LIMIT_ENABLE.to_bacnet(), 0x80);
505 }
506
507 #[test]
508 fn event_transition_bits_msb_first() {
509 let bits = EventTransitionBits::from_bacnet(&[0b1010_0000]);
511 assert_eq!(
512 bits,
513 EventTransitionBits::TO_OFFNORMAL | EventTransitionBits::TO_NORMAL
514 );
515 assert_eq!(bits.to_string(), "TO_OFFNORMAL | TO_NORMAL");
516 }
517
518 #[test]
519 fn limit_enable_msb_first() {
520 assert_eq!(
521 LimitEnable::from_bacnet(&[0x80]),
522 LimitEnable::LOW_LIMIT_ENABLE
523 );
524 assert_eq!(
525 LimitEnable::from_bacnet(&[0xC0]),
526 LimitEnable::LOW_LIMIT_ENABLE | LimitEnable::HIGH_LIMIT_ENABLE
527 );
528 assert_eq!(LimitEnable::from_bacnet(&[]).to_string(), "()");
529 }
530
531 #[test]
532 fn object_types_supported_matches_device_layout() {
533 let mut data = vec![0u8; 9];
535 data[8] = 0x80;
536 let ots = ObjectTypesSupported::from_bacnet(&data);
537 assert!(ots.contains(ObjectType::COLOR_TEMPERATURE));
538 assert!(!ots.contains(ObjectType::ANALOG_INPUT));
539 assert_eq!(
540 ots.iter().collect::<Vec<_>>(),
541 vec![ObjectType::COLOR_TEMPERATURE]
542 );
543
544 let ss = ObjectTypesSupported::from_bacnet(&[0, 0, 0, 0, 0xFD]);
546 assert!(ss.contains(ObjectType::from_raw(32)));
547 assert!(!ss.contains(ObjectType::NETWORK_SECURITY)); assert!(ss.contains(ObjectType::from_raw(39)));
549 }
550
551 #[test]
552 fn resolved_value_holds_names_not_bytes() {
553 let ots = ObjectTypesSupported::from_bacnet(&[0b1110_0000]); assert_eq!(
557 ots.iter().collect::<Vec<_>>(),
558 [
559 ObjectType::ANALOG_INPUT,
560 ObjectType::ANALOG_OUTPUT,
561 ObjectType::ANALOG_VALUE
562 ]
563 );
564 let dbg = format!("{ots:?}");
565 assert!(
566 dbg.contains("ANALOG_INPUT"),
567 "Debug should name types: {dbg}"
568 );
569 assert!(
570 !dbg.contains("224"),
571 "Debug must not show raw byte 0xE0: {dbg}"
572 );
573 }
574
575 #[test]
576 fn services_supported_follows_clause_21() {
577 let ss = ServicesSupported::from_bacnet(&[0xA4, 0x0B, 0x80, 0x35, 0x80, 0x00]);
580 assert!(ss.contains(ServiceSupported::ACKNOWLEDGE_ALARM)); assert!(ss.contains(ServiceSupported::SUBSCRIBE_COV)); assert!(ss.contains(ServiceSupported::READ_PROPERTY)); assert!(ss.contains(ServiceSupported::I_AM)); assert!(ss.contains(ServiceSupported::TIME_SYNCHRONIZATION)); assert!(!ss.contains(ServiceSupported::WHO_IS)); }
587
588 #[test]
589 fn status_flags_render_names_not_numbers() {
590 assert_eq!(
591 (StatusFlags::IN_ALARM | StatusFlags::FAULT).to_string(),
592 "IN_ALARM | FAULT"
593 );
594 assert_eq!(StatusFlags::empty().to_string(), "()");
596 assert_eq!(format!("{:?}", StatusFlags::empty()), "()");
597 assert_eq!(format!("{:?}", StatusFlags::IN_ALARM), "IN_ALARM");
598 }
599
600 #[test]
601 fn small_types_debug_is_named_never_a_number() {
602 assert_eq!(format!("{:?}", EventTransitionBits::empty()), "()");
604 assert_eq!(format!("{:?}", LimitEnable::empty()), "()");
605 assert_eq!(format!("{:?}", EventTransitionBits::TO_FAULT), "TO_FAULT");
606 }
607
608 #[test]
609 fn audit_operation_flags_empty_and_named_bits_use_msb_first_wire_order() {
610 let empty = AuditOperationFlags::empty();
611 assert!(empty.is_empty());
612 assert_eq!(empty.to_bacnet(), (0, Vec::new()));
613 assert_eq!(AuditOperationFlags::from_bacnet(0, &[]).unwrap(), empty);
614
615 let mut flags = AuditOperationFlags::empty();
616 assert!(flags.insert(AuditOperation::READ));
617 assert!(flags.insert(AuditOperation::GENERAL));
618 assert!(flags.contains(AuditOperation::READ));
619 assert!(flags.contains(AuditOperation::GENERAL));
620 assert_eq!(flags.bits(), (1 << 0) | (1 << 15));
621 assert_eq!(flags.to_bacnet(), (0, vec![0x80, 0x01]));
622 assert_eq!(
623 AuditOperationFlags::from_bacnet(0, &[0x80, 0x01]).unwrap(),
624 flags
625 );
626 assert_eq!(flags.to_string(), "READ | GENERAL");
627 }
628
629 #[test]
630 fn audit_operation_flags_preserve_proprietary_bit_63() {
631 let proprietary_32 = AuditOperation::from_raw(32);
632 let proprietary = AuditOperation::from_raw(63);
633 let mut flags = AuditOperationFlags::from_bits(1u64 << 63).unwrap();
634 assert!(flags.insert(proprietary_32));
635 assert!(flags.contains(proprietary_32));
636 assert!(flags.contains(proprietary));
637 assert_eq!(flags.to_bacnet(), (0, vec![0, 0, 0, 0, 0x80, 0, 0, 1]));
638 assert_eq!(
639 AuditOperationFlags::from_bacnet(0, &[0, 0, 0, 0, 0x80, 0, 0, 1]).unwrap(),
640 flags
641 );
642 assert!(!flags.insert(AuditOperation::from_raw(64)));
643 assert!(!flags.contains(AuditOperation::from_raw(64)));
644 assert!(flags.remove(proprietary_32));
645 assert!(flags.remove(proprietary));
646 assert!(flags.is_empty());
647 }
648
649 #[test]
650 fn audit_operation_flags_reject_overlong_and_nonzero_padding() {
651 assert!(AuditOperationFlags::from_bacnet(7, &[0; 9]).is_err());
652 assert!(AuditOperationFlags::from_bacnet(1, &[0x01]).is_err());
653 assert!(AuditOperationFlags::from_bacnet(1, &[]).is_err());
654 assert!(AuditOperationFlags::from_bacnet(8, &[0]).is_err());
655 assert!(AuditOperationFlags::from_bits(1 << 16).is_err());
656 assert!(AuditOperationFlags::from_bits(1 << 31).is_err());
657 assert!(AuditOperationFlags::from_bacnet(7, &[0, 0, 0x80]).is_err());
658
659 let mut flags = AuditOperationFlags::empty();
660 assert!(!flags.insert(AuditOperation::from_raw(16)));
661 assert!(!flags.insert(AuditOperation::from_raw(31)));
662 assert!(flags.is_empty());
663
664 assert_eq!(
666 AuditOperationFlags::from_bacnet(7, &[0x80]).unwrap(),
667 AuditOperationFlags::from_bits(1).unwrap()
668 );
669 }
670}