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)]
21pub struct ObjectIdentifier {
22 object_type: ObjectType,
23 instance_number: u32,
24}
25
26impl ObjectIdentifier {
27 pub const MAX_INSTANCE: u32 = 0x3F_FFFF;
29
30 pub const WILDCARD_INSTANCE: u32 = Self::MAX_INSTANCE;
32
33 pub fn new(object_type: ObjectType, instance_number: u32) -> Result<Self, Error> {
38 if instance_number > Self::MAX_INSTANCE {
39 return Err(Error::OutOfRange(alloc_or_std_format!(
40 "instance number {} exceeds max {}",
41 instance_number,
42 Self::MAX_INSTANCE
43 )));
44 }
45 Ok(Self {
46 object_type,
47 instance_number,
48 })
49 }
50
51 pub const fn new_unchecked(object_type: ObjectType, instance_number: u32) -> Self {
53 Self {
54 object_type,
55 instance_number,
56 }
57 }
58
59 pub const fn object_type(&self) -> ObjectType {
61 self.object_type
62 }
63
64 pub const fn instance_number(&self) -> u32 {
66 self.instance_number
67 }
68
69 pub fn encode(&self) -> [u8; 4] {
71 let value = ((self.object_type.to_raw() & 0x3FF) << 22)
72 | (self.instance_number & Self::MAX_INSTANCE);
73 value.to_be_bytes()
74 }
75
76 pub fn decode(data: &[u8]) -> Result<Self, Error> {
78 if data.len() < 4 {
79 return Err(Error::buffer_too_short(4, data.len()));
80 }
81 let value = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
82 let type_raw = (value >> 22) & 0x3FF;
83 let instance = value & Self::MAX_INSTANCE;
84 Ok(Self {
85 object_type: ObjectType::from_raw(type_raw),
86 instance_number: instance,
87 })
88 }
89}
90
91impl core::fmt::Debug for ObjectIdentifier {
92 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
93 write!(
94 f,
95 "ObjectIdentifier({:?}, {})",
96 self.object_type, self.instance_number
97 )
98 }
99}
100
101impl core::fmt::Display for ObjectIdentifier {
102 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
103 write!(f, "{},{}", self.object_type, self.instance_number)
104 }
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
118pub struct Date {
119 pub year: u8,
121 pub month: u8,
123 pub day: u8,
125 pub day_of_week: u8,
127}
128
129impl Date {
130 pub const UNSPECIFIED: u8 = 0xFF;
132
133 pub fn encode(&self) -> [u8; 4] {
135 [self.year, self.month, self.day, self.day_of_week]
136 }
137
138 pub fn decode(data: &[u8]) -> Result<Self, Error> {
140 if data.len() < 4 {
141 return Err(Error::buffer_too_short(4, data.len()));
142 }
143 Ok(Self {
144 year: data[0],
145 month: data[1],
146 day: data[2],
147 day_of_week: data[3],
148 })
149 }
150
151 pub fn actual_year(&self) -> Option<u16> {
153 if self.year == Self::UNSPECIFIED {
154 None
155 } else {
156 Some(1900 + self.year as u16)
157 }
158 }
159}
160
161#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
169pub struct Time {
170 pub hour: u8,
172 pub minute: u8,
174 pub second: u8,
176 pub hundredths: u8,
178}
179
180impl Time {
181 pub const UNSPECIFIED: u8 = 0xFF;
183
184 pub fn encode(&self) -> [u8; 4] {
186 [self.hour, self.minute, self.second, self.hundredths]
187 }
188
189 pub fn decode(data: &[u8]) -> Result<Self, Error> {
191 if data.len() < 4 {
192 return Err(Error::buffer_too_short(4, data.len()));
193 }
194 Ok(Self {
195 hour: data[0],
196 minute: data[1],
197 second: data[2],
198 hundredths: data[3],
199 })
200 }
201}
202
203#[derive(Debug, Clone, PartialEq)]
210pub enum BACnetTimeStamp {
211 Time(Time),
213 SequenceNumber(u64),
215 DateTime { date: Date, time: Time },
217}
218
219bitflags::bitflags! {
224 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
226 pub struct StatusFlags: u8 {
227 const IN_ALARM = 0b1000;
228 const FAULT = 0b0100;
229 const OVERRIDDEN = 0b0010;
230 const OUT_OF_SERVICE = 0b0001;
231 }
232}
233
234#[derive(Debug, Clone, PartialEq)]
244pub enum PropertyValue {
245 Null,
247 Boolean(bool),
249 Unsigned(u64),
251 Signed(i32),
253 Real(f32),
255 Double(f64),
257 OctetString(Vec<u8>),
259 CharacterString(String),
261 BitString {
263 unused_bits: u8,
265 data: Vec<u8>,
267 },
268 Enumerated(u32),
270 Date(Date),
272 Time(Time),
274 ObjectIdentifier(ObjectIdentifier),
276 List(Vec<PropertyValue>),
282}
283
284#[cfg(feature = "std")]
290macro_rules! alloc_or_std_format {
291 ($($arg:tt)*) => { format!($($arg)*) }
292}
293
294#[cfg(not(feature = "std"))]
295macro_rules! alloc_or_std_format {
296 ($($arg:tt)*) => { alloc::format!($($arg)*) }
297}
298
299use alloc_or_std_format;
301
302#[cfg(test)]
307mod tests {
308 use super::*;
309
310 #[test]
311 fn object_identifier_encode_decode_round_trip() {
312 let oid = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap();
313 let bytes = oid.encode();
314 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
315 assert_eq!(oid, decoded);
316 }
317
318 #[test]
319 fn object_identifier_wire_format() {
320 let oid = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap();
322 assert_eq!(oid.encode(), [0x00, 0x00, 0x00, 0x01]);
323
324 let oid = ObjectIdentifier::new(ObjectType::DEVICE, 1234).unwrap();
326 let expected = ((8u32 << 22) | 1234u32).to_be_bytes();
327 assert_eq!(oid.encode(), expected);
328 }
329
330 #[test]
331 fn object_identifier_max_instance() {
332 let oid =
333 ObjectIdentifier::new(ObjectType::DEVICE, ObjectIdentifier::MAX_INSTANCE).unwrap();
334 assert_eq!(oid.instance_number(), 0x3F_FFFF);
335 let bytes = oid.encode();
336 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
337 assert_eq!(decoded.instance_number(), 0x3F_FFFF);
338 }
339
340 #[test]
341 fn object_identifier_invalid_instance() {
342 let result = ObjectIdentifier::new(ObjectType::DEVICE, ObjectIdentifier::MAX_INSTANCE + 1);
343 assert!(result.is_err());
344 }
345
346 #[test]
347 fn object_identifier_buffer_too_short() {
348 let result = ObjectIdentifier::decode(&[0x00, 0x00]);
349 assert!(result.is_err());
350 }
351
352 #[test]
353 fn date_encode_decode_round_trip() {
354 let date = Date {
355 year: 124, month: 6,
357 day: 15,
358 day_of_week: 6, };
360 let bytes = date.encode();
361 let decoded = Date::decode(&bytes).unwrap();
362 assert_eq!(date, decoded);
363 assert_eq!(decoded.actual_year(), Some(2024));
364 }
365
366 #[test]
367 fn date_unspecified_year() {
368 let date = Date {
369 year: Date::UNSPECIFIED,
370 month: 1,
371 day: 1,
372 day_of_week: Date::UNSPECIFIED,
373 };
374 assert_eq!(date.actual_year(), None);
375 }
376
377 #[test]
378 fn time_encode_decode_round_trip() {
379 let time = Time {
380 hour: 14,
381 minute: 30,
382 second: 45,
383 hundredths: 50,
384 };
385 let bytes = time.encode();
386 let decoded = Time::decode(&bytes).unwrap();
387 assert_eq!(time, decoded);
388 }
389
390 #[test]
391 fn status_flags_operations() {
392 let flags = StatusFlags::IN_ALARM | StatusFlags::OUT_OF_SERVICE;
393 assert!(flags.contains(StatusFlags::IN_ALARM));
394 assert!(flags.contains(StatusFlags::OUT_OF_SERVICE));
395 assert!(!flags.contains(StatusFlags::FAULT));
396 assert!(!flags.contains(StatusFlags::OVERRIDDEN));
397 }
398
399 #[test]
402 fn object_identifier_instance_zero() {
403 let oid = ObjectIdentifier::new(ObjectType::DEVICE, 0).unwrap();
405 assert_eq!(oid.instance_number(), 0);
406 let bytes = oid.encode();
407 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
408 assert_eq!(decoded.instance_number(), 0);
409 assert_eq!(decoded.object_type(), ObjectType::DEVICE);
410 }
411
412 #[test]
413 fn object_identifier_all_types_instance_zero() {
414 for type_raw in [0u32, 1, 2, 3, 4, 5, 6, 8, 10, 13, 14, 17, 19] {
416 let obj_type = ObjectType::from_raw(type_raw);
417 let oid = ObjectIdentifier::new(obj_type, 0).unwrap();
418 let bytes = oid.encode();
419 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
420 assert_eq!(decoded.object_type(), obj_type, "type {type_raw} failed");
421 assert_eq!(
422 decoded.instance_number(),
423 0,
424 "type {type_raw} instance failed"
425 );
426 }
427 }
428
429 #[test]
430 fn object_identifier_wildcard_instance() {
431 let oid =
432 ObjectIdentifier::new(ObjectType::DEVICE, ObjectIdentifier::WILDCARD_INSTANCE).unwrap();
433 assert_eq!(oid.instance_number(), ObjectIdentifier::MAX_INSTANCE);
434 let bytes = oid.encode();
435 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
436 assert_eq!(decoded.instance_number(), ObjectIdentifier::MAX_INSTANCE);
437 }
438
439 #[test]
440 fn object_identifier_decode_extra_bytes_ignored() {
441 let oid = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 42).unwrap();
443 let mut bytes = oid.encode().to_vec();
444 bytes.extend_from_slice(&[0xFF, 0xFF]); let decoded = ObjectIdentifier::decode(&bytes).unwrap();
446 assert_eq!(decoded, oid);
447 }
448
449 #[test]
450 fn object_identifier_type_overflow_round_trip() {
451 let oid = ObjectIdentifier::new_unchecked(ObjectType::from_raw(1024), 0);
453 let bytes = oid.encode();
454 let decoded = ObjectIdentifier::decode(&bytes).unwrap();
455 assert_eq!(decoded.object_type(), ObjectType::from_raw(0));
457 }
458
459 #[test]
460 fn property_value_variants() {
461 let null = PropertyValue::Null;
462 let boolean = PropertyValue::Boolean(true);
463 let real = PropertyValue::Real(72.5);
464 let string = PropertyValue::CharacterString("test".into());
465
466 assert_eq!(null, PropertyValue::Null);
467 assert_eq!(boolean, PropertyValue::Boolean(true));
468 assert_ne!(real, PropertyValue::Real(73.0));
469 assert_eq!(string, PropertyValue::CharacterString("test".into()));
470 }
471}