1use bitflags::bitflags;
121use core::fmt::Display;
122#[cfg(feature = "std")]
123use std::error::Error;
124
125#[cfg(feature = "std")]
126use std::fmt;
127
128#[cfg(not(feature = "std"))]
129use core::fmt;
130
131#[cfg(not(feature = "std"))]
132use alloc::{string::String, vec::Vec};
133
134#[cfg(feature = "std")]
136pub type Result<T> = std::result::Result<T, ObjectError>;
137
138#[cfg(not(feature = "std"))]
139pub type Result<T> = core::result::Result<T, ObjectError>;
140
141#[cfg(feature = "serde")]
142use serde::{Deserialize, Serialize};
143
144#[derive(Debug)]
146pub enum ObjectError {
147 NotFound,
149 InstanceNotFound,
151 TypeNotSupported,
153 PropertyNotFound,
155 UnknownProperty,
157 PropertyNotWritable,
159 InvalidPropertyType,
161 InvalidValue(String),
163 WriteAccessDenied,
165 InvalidConfiguration(String),
167}
168
169impl fmt::Display for ObjectError {
170 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171 match self {
172 ObjectError::NotFound => write!(f, "Object not found"),
173 ObjectError::InstanceNotFound => write!(f, "Object instance not found"),
174 ObjectError::TypeNotSupported => write!(f, "Object type not supported"),
175 ObjectError::PropertyNotFound => write!(f, "Property not found"),
176 ObjectError::UnknownProperty => write!(f, "Unknown property"),
177 ObjectError::PropertyNotWritable => write!(f, "Property not writable"),
178 ObjectError::InvalidPropertyType => write!(f, "Invalid property type"),
179 ObjectError::InvalidValue(msg) => write!(f, "Invalid value: {}", msg),
180 ObjectError::WriteAccessDenied => write!(f, "Write access denied"),
181 ObjectError::InvalidConfiguration(msg) => write!(f, "Invalid configuration: {}", msg),
182 }
183 }
184}
185
186#[cfg(feature = "std")]
187impl Error for ObjectError {}
188
189#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
191#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
192pub struct ObjectIdentifier {
193 pub object_type: ObjectType,
194 pub instance: u32,
195}
196
197impl ObjectIdentifier {
198 pub fn new(object_type: ObjectType, instance: u32) -> Self {
200 Self {
201 object_type,
202 instance,
203 }
204 }
205
206 pub fn is_valid(&self) -> bool {
208 self.instance <= 0x3FFFFF
209 }
210}
211
212impl From<u32> for ObjectIdentifier {
213 fn from(value: u32) -> Self {
216 let object_type = (value >> 22) & 0x3FF;
217 let object_type = object_type.into();
218 let instance = value & 0x3FFFFF;
219 Self::new(object_type, instance)
220 }
221}
222
223impl TryFrom<ObjectIdentifier> for u32 {
224 type Error = EncodingError;
225
226 fn try_from(value: ObjectIdentifier) -> std::result::Result<Self, Self::Error> {
227 let object_type: u32 = value.object_type.into();
228
229 if object_type > 0x3FF || value.instance > 0x3FFFFF {
230 Err(EncodingError::ValueOutOfRange)
231 } else {
232 Ok((object_type << 22) | (value.instance & 0x3FFFFF))
233 }
234 }
235}
236
237pub trait BacnetObject: Send + Sync {
239 fn identifier(&self) -> ObjectIdentifier;
241
242 fn get_property(&self, property: PropertyIdentifier) -> Result<PropertyValue>;
244
245 fn set_property(&mut self, property: PropertyIdentifier, value: PropertyValue) -> Result<()>;
247
248 fn is_property_writable(&self, property: PropertyIdentifier) -> bool;
250
251 fn property_list(&self) -> Vec<PropertyIdentifier>;
253}
254
255#[derive(Debug, Clone)]
257pub enum PropertyValue {
258 Null,
259 Boolean(bool),
260 UnsignedInteger(u32),
261 SignedInt(i32),
262 Real(f32),
263 Double(f64),
264 OctetString(Vec<u8>),
265 CharacterString(String),
266 BitString(Vec<bool>),
267 Enumerated(u32),
268 Date(Date),
269 Time(Time),
270 ObjectIdentifier(ObjectIdentifier),
271 Array(Vec<PropertyValue>),
272 List(Vec<PropertyValue>),
273}
274
275#[derive(Debug, Clone, Copy, PartialEq, Eq)]
277pub struct Date {
278 pub year: u16, pub month: u8, pub day: u8, pub weekday: u8, }
283
284#[derive(Debug, Clone, Copy, PartialEq, Eq)]
286pub struct Time {
287 pub hour: u8, pub minute: u8, pub second: u8, pub hundredths: u8, }
292
293#[derive(Debug, Clone)]
295pub struct Device {
296 pub identifier: ObjectIdentifier,
298 pub object_name: String,
300 pub object_type: ObjectType,
302 pub system_status: DeviceStatus,
304 pub vendor_name: String,
306 pub vendor_identifier: u16,
308 pub model_name: String,
310 pub firmware_revision: String,
312 pub application_software_version: String,
314 pub protocol_version: u8,
316 pub protocol_revision: u8,
318 pub protocol_services_supported: ProtocolServicesSupported,
320 pub object_types_supported: Vec<ObjectType>,
322 pub max_apdu_length_accepted: u16,
324 pub segmentation_supported: Segmentation,
326 pub device_address_binding: Vec<AddressBinding>,
328 pub database_revision: u32,
330}
331
332impl Device {
333 pub fn new(instance: u32, object_name: String) -> Self {
335 Self {
336 identifier: ObjectIdentifier::new(ObjectType::Device, instance),
337 object_name,
338 object_type: ObjectType::Device,
339 system_status: DeviceStatus::Operational,
340 vendor_name: String::from("BACnet-RS"),
341 vendor_identifier: 999, model_name: String::from("Rust BACnet Device"),
343 firmware_revision: String::from("1.0.0"),
344 application_software_version: String::from(env!("CARGO_PKG_VERSION")),
345 protocol_version: 1,
346 protocol_revision: 22, protocol_services_supported: ProtocolServicesSupported::default(),
348 object_types_supported: vec![ObjectType::Device],
349 max_apdu_length_accepted: 1476,
350 segmentation_supported: Segmentation::Both,
351 device_address_binding: Vec::new(),
352 database_revision: 1,
353 }
354 }
355
356 pub fn add_supported_object_type(&mut self, object_type: ObjectType) {
358 if !self.object_types_supported.contains(&object_type) {
359 self.object_types_supported.push(object_type);
360 }
361 }
362
363 pub fn get_vendor_info(&self) -> Option<crate::vendor::VendorInfo> {
365 crate::vendor::get_vendor_info(self.vendor_identifier)
366 }
367
368 pub fn get_official_vendor_name(&self) -> Option<&'static str> {
370 crate::vendor::get_vendor_name(self.vendor_identifier)
371 }
372
373 pub fn set_vendor_by_id(&mut self, vendor_id: u16) -> Result<()> {
375 if let Some(vendor_info) = crate::vendor::get_vendor_info(vendor_id) {
376 self.vendor_identifier = vendor_id;
377 self.vendor_name = vendor_info.name.to_string();
378 Ok(())
379 } else {
380 Err(ObjectError::InvalidPropertyType)
381 }
382 }
383
384 pub fn set_vendor_name(&mut self, name: String) {
386 self.vendor_name = name;
387 }
388
389 pub fn is_vendor_id_official(&self) -> bool {
391 crate::vendor::is_vendor_id_assigned(self.vendor_identifier)
392 && !crate::vendor::is_vendor_id_reserved(self.vendor_identifier)
393 }
394
395 pub fn is_vendor_id_test(&self) -> bool {
397 crate::vendor::is_vendor_id_reserved(self.vendor_identifier)
398 }
399
400 pub fn format_vendor_display(&self) -> String {
402 crate::vendor::format_vendor_display(self.vendor_identifier)
403 }
404}
405
406impl BacnetObject for Device {
407 fn identifier(&self) -> ObjectIdentifier {
408 self.identifier
409 }
410
411 fn get_property(&self, property: PropertyIdentifier) -> Result<PropertyValue> {
412 match property {
413 PropertyIdentifier::ObjectIdentifier => {
414 Ok(PropertyValue::ObjectIdentifier(self.identifier))
415 }
416 PropertyIdentifier::ObjectName => {
417 Ok(PropertyValue::CharacterString(self.object_name.clone()))
418 }
419 PropertyIdentifier::ObjectType => {
420 Ok(PropertyValue::Enumerated(u32::from(self.object_type)))
421 }
422 PropertyIdentifier::SystemStatus => {
423 Ok(PropertyValue::Enumerated(self.system_status as u32))
424 }
425 PropertyIdentifier::VendorName => {
426 Ok(PropertyValue::CharacterString(self.vendor_name.clone()))
427 }
428 PropertyIdentifier::VendorIdentifier => Ok(PropertyValue::UnsignedInteger(
429 self.vendor_identifier as u32,
430 )),
431 PropertyIdentifier::ModelName => {
432 Ok(PropertyValue::CharacterString(self.model_name.clone()))
433 }
434 PropertyIdentifier::FirmwareRevision => Ok(PropertyValue::CharacterString(
435 self.firmware_revision.clone(),
436 )),
437 PropertyIdentifier::ApplicationSoftwareVersion => Ok(PropertyValue::CharacterString(
438 self.application_software_version.clone(),
439 )),
440 PropertyIdentifier::ProtocolVersion => {
441 Ok(PropertyValue::UnsignedInteger(self.protocol_version as u32))
442 }
443 PropertyIdentifier::ProtocolRevision => Ok(PropertyValue::UnsignedInteger(
444 self.protocol_revision as u32,
445 )),
446 PropertyIdentifier::MaxApduLengthAccepted => Ok(PropertyValue::UnsignedInteger(
447 self.max_apdu_length_accepted as u32,
448 )),
449 PropertyIdentifier::SegmentationSupported => Ok(PropertyValue::Enumerated(
450 self.segmentation_supported as u32,
451 )),
452 PropertyIdentifier::DatabaseRevision => {
453 Ok(PropertyValue::UnsignedInteger(self.database_revision))
454 }
455 _ => Err(ObjectError::UnknownProperty),
456 }
457 }
458
459 fn set_property(&mut self, property: PropertyIdentifier, value: PropertyValue) -> Result<()> {
460 match property {
461 PropertyIdentifier::ObjectName => {
462 if let PropertyValue::CharacterString(name) = value {
463 self.object_name = name;
464 Ok(())
465 } else {
466 Err(ObjectError::InvalidPropertyType)
467 }
468 }
469 PropertyIdentifier::VendorName => {
470 if let PropertyValue::CharacterString(name) = value {
471 self.vendor_name = name;
472 Ok(())
473 } else {
474 Err(ObjectError::InvalidPropertyType)
475 }
476 }
477 PropertyIdentifier::ModelName => {
478 if let PropertyValue::CharacterString(name) = value {
479 self.model_name = name;
480 Ok(())
481 } else {
482 Err(ObjectError::InvalidPropertyType)
483 }
484 }
485 PropertyIdentifier::FirmwareRevision => {
486 if let PropertyValue::CharacterString(revision) = value {
487 self.firmware_revision = revision;
488 Ok(())
489 } else {
490 Err(ObjectError::InvalidPropertyType)
491 }
492 }
493 PropertyIdentifier::ApplicationSoftwareVersion => {
494 if let PropertyValue::CharacterString(version) = value {
495 self.application_software_version = version;
496 Ok(())
497 } else {
498 Err(ObjectError::InvalidPropertyType)
499 }
500 }
501 PropertyIdentifier::DatabaseRevision => {
502 if let PropertyValue::UnsignedInteger(revision) = value {
503 self.database_revision = revision;
504 Ok(())
505 } else {
506 Err(ObjectError::InvalidPropertyType)
507 }
508 }
509 _ => Err(ObjectError::PropertyNotWritable),
510 }
511 }
512
513 fn is_property_writable(&self, property: PropertyIdentifier) -> bool {
514 matches!(
515 property,
516 PropertyIdentifier::ObjectName
517 | PropertyIdentifier::VendorName
518 | PropertyIdentifier::ModelName
519 | PropertyIdentifier::FirmwareRevision
520 | PropertyIdentifier::ApplicationSoftwareVersion
521 | PropertyIdentifier::DatabaseRevision
522 )
523 }
524
525 fn property_list(&self) -> Vec<PropertyIdentifier> {
526 vec![
527 PropertyIdentifier::ObjectIdentifier,
528 PropertyIdentifier::ObjectName,
529 PropertyIdentifier::ObjectType,
530 PropertyIdentifier::SystemStatus,
531 PropertyIdentifier::VendorName,
532 PropertyIdentifier::VendorIdentifier,
533 PropertyIdentifier::ModelName,
534 PropertyIdentifier::FirmwareRevision,
535 PropertyIdentifier::ApplicationSoftwareVersion,
536 PropertyIdentifier::ProtocolVersion,
537 PropertyIdentifier::ProtocolRevision,
538 PropertyIdentifier::MaxApduLengthAccepted,
539 PropertyIdentifier::SegmentationSupported,
540 PropertyIdentifier::DatabaseRevision,
541 ]
542 }
543}
544
545#[derive(Debug, Clone, Copy, PartialEq, Eq)]
547#[repr(u32)]
548pub enum DeviceStatus {
549 Operational = 0,
550 OperationalReadOnly = 1,
551 DownloadRequired = 2,
552 DownloadInProgress = 3,
553 NonOperational = 4,
554 BackupInProgress = 5,
555}
556
557#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
559#[derive(Debug, Clone, Copy, PartialEq, Eq)]
560#[repr(u32)]
561pub enum Segmentation {
562 Both = 0,
563 Transmit = 1,
564 Receive = 2,
565 NoSegmentation = 3,
566}
567
568impl TryFrom<u32> for Segmentation {
569 type Error = ObjectError;
570
571 fn try_from(value: u32) -> Result<Self> {
572 match value {
573 0 => Ok(Self::Both),
574 1 => Ok(Self::Transmit),
575 2 => Ok(Self::Receive),
576 3 => Ok(Self::NoSegmentation),
577 _ => Err(ObjectError::InvalidConfiguration(format!(
578 "Unknown segmentation: {}",
579 value
580 ))),
581 }
582 }
583}
584
585impl Display for Segmentation {
586 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
587 match self {
588 Self::Both => write!(f, "Both"),
589 Self::Transmit => write!(f, "Transmit"),
590 Self::Receive => write!(f, "Receive"),
591 Self::NoSegmentation => write!(f, "None"),
592 }
593 }
594}
595
596bitflags! {
597 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
599 #[derive(Debug, Clone, Default, Eq, PartialEq)]
600 pub struct ProtocolServicesSupported: u64 {
601 const ACKNOWLEDGE_ALARM = 1 << 0;
602 const CONFIRMED_COV_NOTIFICATION = 1 << 1;
603 const CONFIRMED_EVENT_NOTIFICATION = 1 << 2;
604 const GET_ALARM_SUMMARY = 1 << 3;
605 const GET_ENROLLMENT_SUMMARY = 1 << 4;
606 const SUBSCRIBE_COV = 1 << 5;
607 const ATOMIC_READ_FILE = 1 << 6;
608 const ATOMIC_WRITE_FILE = 1 << 7;
609 const ADD_LIST_ELEMENT = 1 << 8;
610 const REMOVE_LIST_ELEMENT = 1 << 9;
611 const CREATE_OBJECT = 1 << 10;
612 const DELETE_OBJECT = 1 << 11;
613 const READ_PROPERTY = 1 << 12;
614 const READ_PROPERTY_CONDITIONAL = 1 << 13;
615 const READ_PROPERTY_MULTIPLE = 1 << 14;
616 const WRITE_PROPERTY = 1 << 15;
617 const WRITE_PROPERTY_MULTIPLE = 1 << 16;
618 const DEVICE_COMMUNICATION_CONTROL = 1 << 17;
619 const CONFIRMED_PRIVATE_TRANSFER = 1 << 18;
620 const CONFIRMED_TEXT_MESSAGE = 1 << 19;
621 const REINITIALIZE_DEVICE = 1 << 20;
622 const VT_OPEN = 1 << 21;
623 const VT_CLOSE = 1 << 22;
624 const VT_DATA = 1 << 23;
625 const AUTHENTICATE = 1 << 24;
626 const REQUEST_KEY = 1 << 25;
627 const I_AM = 1 << 26;
628 const I_HAVE = 1 << 27;
629 const UNCONFIRMED_COV_NOTIFICATION = 1 << 28;
630 const UNCONFIRMED_EVENT_NOTIFICATION = 1 << 29;
631 const UNCONFIRMED_PRIVATE_TRANSFER = 1 << 30;
632 const UNCONFIRMED_TEXT_MESSAGE = 1 << 31;
633 const TIME_SYNCHRONIZATION = 1 << 32;
634 const WHO_HAS = 1 << 33;
635 const WHO_IS = 1 << 34;
636 const READ_RANGE = 1 << 35;
637 const UTC_TIME_SYNCHRONIZATION = 1 << 36;
638 const LIFE_SAFETY_OPERATION = 1 << 37;
639 const SUBSCRIBE_COV_PROPERTY = 1 << 38;
640 const GET_EVENT_INFORMATION = 1 << 39;
641 const WRITE_GROUP = 1 << 40;
642 const SUBSCRIBE_COV_PROPERTY_MULTIPLE = 1 << 41;
643 const CONFIRMED_COV_NOTIFICATION_MULTIPLE = 1 << 42;
644 const UNCONFIRMED_COV_NOTIFICATION_MULTIPLE = 1 << 43;
645 const CONFIRMED_AUDIT_NOTIFICATION = 1 << 44;
646 const AUDIT_LOG_QUERY = 1 << 45;
647 const UNCONFIRMED_AUDIT_NOTIFICATION = 1 << 46;
648 const WHO_AM_I = 1 << 47;
649 const YOU_ARE = 1 << 48;
650 const AUTH_REQUEST = 1 << 49;
651 }
652}
653
654impl ProtocolServicesSupported {
655 pub fn to_bool_vec(&self) -> Vec<bool> {
656 let mut vec = Vec::new();
657 for i in 0..64 {
658 vec.push((self.bits() & (1 << i)) != 0);
659 }
660 vec
661 }
662}
663
664impl From<Vec<bool>> for ProtocolServicesSupported {
665 fn from(value: Vec<bool>) -> Self {
666 let mut services = ProtocolServicesSupported::empty();
667 for (v, f) in value.iter().zip(ProtocolServicesSupported::all().iter()) {
668 if *v {
669 services.insert(f);
670 }
671 }
672 services
673 }
674}
675
676#[derive(Debug, Clone)]
678pub struct AddressBinding {
679 pub device_identifier: ObjectIdentifier,
680 pub network_address: Vec<u8>,
681}
682
683pub mod analog;
685pub mod binary;
687#[cfg(feature = "std")]
689pub mod database;
690pub mod device;
692pub mod engineering_units;
694pub mod file;
696pub mod multistate;
698
699pub mod event_state;
700pub mod object_type;
701pub mod reliability;
702pub use object_type::ObjectType;
703pub mod property_identifier;
704pub use property_identifier::PropertyIdentifier;
705
706pub use analog::{AnalogInput, AnalogOutput, AnalogValue};
707pub use binary::{BinaryInput, BinaryOutput, BinaryPV, BinaryValue, Polarity};
708pub use device::{DeviceObject, ObjectFunctions};
709pub use engineering_units::EngineeringUnits;
710pub use event_state::EventState;
711pub use file::{File, FileAccessMethod};
712pub use multistate::{MultiStateInput, MultiStateOutput, MultiStateValue};
713pub use reliability::Reliability;
714
715#[cfg(feature = "std")]
716pub use database::{DatabaseBuilder, DatabaseStatistics, ObjectDatabase};
717
718use crate::EncodingError;
719
720#[cfg(test)]
721mod tests {
722 use super::*;
723
724 #[test]
725 fn test_device_creation() {
726 let device = Device::new(123, "Test Device".to_string());
727 assert_eq!(device.identifier.instance, 123);
728 assert_eq!(device.object_name, "Test Device");
729 assert_eq!(device.object_type, ObjectType::Device);
730 }
731
732 #[test]
733 fn test_device_properties() {
734 let mut device = Device::new(456, "Property Test".to_string());
735
736 let name = device.get_property(PropertyIdentifier::ObjectName).unwrap();
738 if let PropertyValue::CharacterString(n) = name {
739 assert_eq!(n, "Property Test");
740 } else {
741 panic!("Expected CharacterString");
742 }
743
744 device
746 .set_property(
747 PropertyIdentifier::ObjectName,
748 PropertyValue::CharacterString("New Name".to_string()),
749 )
750 .unwrap();
751
752 let name = device.get_property(PropertyIdentifier::ObjectName).unwrap();
753 if let PropertyValue::CharacterString(n) = name {
754 assert_eq!(n, "New Name");
755 } else {
756 panic!("Expected CharacterString");
757 }
758 }
759
760 #[test]
761 fn test_protocol_services_supported() {
762 let services = ProtocolServicesSupported::ACKNOWLEDGE_ALARM
763 | ProtocolServicesSupported::READ_PROPERTY
764 | ProtocolServicesSupported::WRITE_PROPERTY;
765
766 let bools = services.to_bool_vec();
767 let services_new = ProtocolServicesSupported::from(bools);
768 assert_eq!(services, services_new);
769 }
770}