Skip to main content

bacnet_rs/object/
mod.rs

1//! BACnet Object Types and Property Management
2//!
3//! This module defines BACnet object types and their properties according to ASHRAE Standard 135.
4//! Objects are the fundamental modeling concept in BACnet, representing physical and logical
5//! entities in building automation systems such as sensors, actuators, controllers, and data points.
6//!
7//! # Overview
8//!
9//! BACnet objects are the core abstraction for all entities in a BACnet system. Each object consists of:
10//!
11//! - **Object Identifier**: A unique 32-bit identifier combining object type and instance number
12//! - **Properties**: A collection of named values that describe the object's state, configuration, and behavior
13//! - **Required Properties**: Properties that must be present in all instances of an object type
14//! - **Optional Properties**: Properties that may be present depending on the implementation
15//!
16//! # Object Hierarchy
17//!
18//! Objects are organized into categories based on their function:
19//!
20//! ## Input Objects
21//! - [`AnalogInput`](ObjectType::AnalogInput): Represents analog sensor readings (temperature, pressure, etc.)
22//! - [`BinaryInput`](ObjectType::BinaryInput): Represents digital sensor states (on/off, open/closed)
23//! - [`MultiStateInput`](ObjectType::MultiStateInput): Represents enumerated sensor states
24//!
25//! ## Output Objects  
26//! - [`AnalogOutput`](ObjectType::AnalogOutput): Controls analog actuators (valve position, damper angle)
27//! - [`BinaryOutput`](ObjectType::BinaryOutput): Controls digital actuators (pumps, fans, lights)
28//! - [`MultiStateOutput`](ObjectType::MultiStateOutput): Controls multi-position actuators
29//!
30//! ## Value Objects
31//! - [`AnalogValue`](ObjectType::AnalogValue): Software variables for calculations and setpoints
32//! - [`BinaryValue`](ObjectType::BinaryValue): Software flags and status indicators
33//! - [`MultiStateValue`](ObjectType::MultiStateValue): Software enumerated values
34//!
35//! ## System Objects
36//! - [`Device`](ObjectType::Device): Represents a BACnet device (required in every device)
37//! - [`Schedule`](ObjectType::Schedule): Time-based control schedules
38//! - [`Calendar`](ObjectType::Calendar): Date-based event definitions
39//! - [`TrendLog`](ObjectType::TrendLog): Historical data logging
40//!
41//! # Property System
42//!
43//! Properties are the attributes that describe an object's state and behavior. Common properties include:
44//!
45//! - **Present Value**: The current value or state of the object
46//! - **Object Name**: A human-readable name for the object
47//! - **Description**: Additional descriptive text
48//! - **Units**: Engineering units for analog values
49//! - **Reliability**: Indicates if the value is reliable
50//!
51//! # Examples
52//!
53//! ## Creating Object Identifiers
54//!
55//! ```rust
56//! use bacnet_rs::object::{ObjectIdentifier, ObjectType};
57//!
58//! // Create an object identifier for analog input #1
59//! let temp_sensor = ObjectIdentifier::new(ObjectType::AnalogInput, 1);
60//! assert_eq!(temp_sensor.object_type, ObjectType::AnalogInput);
61//! assert_eq!(temp_sensor.instance, 1);
62//!
63//! // Create device object (instance 123456)
64//! let device = ObjectIdentifier::new(ObjectType::Device, 123456);
65//! assert!(device.is_valid());
66//! ```
67//!
68//! ## Working with Properties
69//!
70//! ```rust
71//! use bacnet_rs::object::{PropertyIdentifier, PropertyValue};
72//!
73//! // Property identifiers for common properties
74//! let present_value = PropertyIdentifier::PresentValue;
75//! let object_name = PropertyIdentifier::ObjectName;
76//! let units = PropertyIdentifier::OutputUnits;
77//!
78//! // Property values can represent different data types
79//! let temperature = PropertyValue::Real(23.5);
80//! let name = PropertyValue::CharacterString("Temperature Sensor".to_string());
81//! let unit_enum = PropertyValue::Enumerated(64); // Degrees Celsius
82//! ```
83//!
84//! ## Object Database Usage
85//!
86//! ```rust,no_run
87//! use bacnet_rs::object::{database::ObjectDatabase, ObjectIdentifier, ObjectType, PropertyIdentifier, PropertyValue, analog::AnalogInput, Device};
88//!
89//! // Create a device and object database
90//! let device = Device::new(12345, "BACnet Device".to_string());
91//! let mut db = ObjectDatabase::new(device);
92//!
93//! // Create an analog input object
94//! let mut ai = AnalogInput::new(1, "Room Temperature".to_string());
95//! ai.set_present_value(23.5);
96//! let obj_id = ai.identifier;
97//!
98//! // Add the object to the database
99//! db.add_object(Box::new(ai)).expect("Failed to add object");
100//!
101//! // Set properties
102//! db.set_property(obj_id, PropertyIdentifier::ObjectName,
103//!     PropertyValue::CharacterString("Room Temperature".to_string()))
104//!     .expect("Failed to set property");
105//!
106//! // Read properties
107//! let name = db.get_property(obj_id, PropertyIdentifier::ObjectName)
108//!     .expect("Property not found");
109//! ```
110//!
111//! # Standards Compliance
112//!
113//! This implementation follows ASHRAE Standard 135-2020 and includes:
114//!
115//! - All standard object types defined in the specification
116//! - Complete property identifier enumeration
117//! - Proper object identifier encoding/decoding
118//! - Thread-safe object database implementation
119
120use 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/// Result type for object operations
135#[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/// Errors that can occur with object operations
145#[derive(Debug)]
146pub enum ObjectError {
147    /// Object not found
148    NotFound,
149    /// Object instance not found
150    InstanceNotFound,
151    /// Object type not supported
152    TypeNotSupported,
153    /// Property not found
154    PropertyNotFound,
155    /// Unknown property
156    UnknownProperty,
157    /// Property not writable
158    PropertyNotWritable,
159    /// Invalid property type
160    InvalidPropertyType,
161    /// Invalid property value
162    InvalidValue(String),
163    /// Write access denied
164    WriteAccessDenied,
165    /// Invalid object configuration
166    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/// Object identifier (type + instance number)
190#[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    /// Create a new object identifier
199    pub fn new(object_type: ObjectType, instance: u32) -> Self {
200        Self {
201            object_type,
202            instance,
203        }
204    }
205
206    /// Check if instance number is valid (0-4194302)
207    pub fn is_valid(&self) -> bool {
208        self.instance <= 0x3FFFFF
209    }
210}
211
212impl From<u32> for ObjectIdentifier {
213    /// Convert from 32-bit object identifier.
214    /// See clause 20.2.14 of the BACnet specification.
215    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
237/// Trait for all BACnet objects
238pub trait BacnetObject: Send + Sync {
239    /// Get the object identifier
240    fn identifier(&self) -> ObjectIdentifier;
241
242    /// Get a property value
243    fn get_property(&self, property: PropertyIdentifier) -> Result<PropertyValue>;
244
245    /// Set a property value
246    fn set_property(&mut self, property: PropertyIdentifier, value: PropertyValue) -> Result<()>;
247
248    /// Check if property is writable
249    fn is_property_writable(&self, property: PropertyIdentifier) -> bool;
250
251    /// Get list of all properties
252    fn property_list(&self) -> Vec<PropertyIdentifier>;
253}
254
255/// Property values can be of various types
256#[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/// BACnet date representation
276#[derive(Debug, Clone, Copy, PartialEq, Eq)]
277pub struct Date {
278    pub year: u16,   // 1900-2155, 255 = unspecified
279    pub month: u8,   // 1-12, 13 = odd months, 14 = even months, 255 = unspecified
280    pub day: u8,     // 1-31, 32 = last day of month, 255 = unspecified
281    pub weekday: u8, // 1-7 (Mon-Sun), 255 = unspecified
282}
283
284/// BACnet time representation
285#[derive(Debug, Clone, Copy, PartialEq, Eq)]
286pub struct Time {
287    pub hour: u8,       // 0-23, 255 = unspecified
288    pub minute: u8,     // 0-59, 255 = unspecified
289    pub second: u8,     // 0-59, 255 = unspecified
290    pub hundredths: u8, // 0-99, 255 = unspecified
291}
292
293/// Device object implementation
294#[derive(Debug, Clone)]
295pub struct Device {
296    /// Object identifier
297    pub identifier: ObjectIdentifier,
298    /// Object name (required property)
299    pub object_name: String,
300    /// Object type (always Device)
301    pub object_type: ObjectType,
302    /// System status
303    pub system_status: DeviceStatus,
304    /// Vendor name
305    pub vendor_name: String,
306    /// Vendor identifier
307    pub vendor_identifier: u16,
308    /// Model name
309    pub model_name: String,
310    /// Firmware revision
311    pub firmware_revision: String,
312    /// Application software version
313    pub application_software_version: String,
314    /// Protocol version (always 1)
315    pub protocol_version: u8,
316    /// Protocol revision
317    pub protocol_revision: u8,
318    /// Protocol services supported
319    pub protocol_services_supported: ProtocolServicesSupported,
320    /// Object types supported
321    pub object_types_supported: Vec<ObjectType>,
322    /// Maximum APDU length accepted
323    pub max_apdu_length_accepted: u16,
324    /// Segmentation support
325    pub segmentation_supported: Segmentation,
326    /// Device address binding (for routing)
327    pub device_address_binding: Vec<AddressBinding>,
328    /// Database revision
329    pub database_revision: u32,
330}
331
332impl Device {
333    /// Create a new Device object
334    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, // Reserved for ASHRAE - appropriate for open-source implementations
342            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, // Current BACnet protocol revision
347            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    /// Add an object type to the supported list
357    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    /// Get the vendor information for this device
364    pub fn get_vendor_info(&self) -> Option<crate::vendor::VendorInfo> {
365        crate::vendor::get_vendor_info(self.vendor_identifier)
366    }
367
368    /// Get the official vendor name from the vendor ID
369    pub fn get_official_vendor_name(&self) -> Option<&'static str> {
370        crate::vendor::get_vendor_name(self.vendor_identifier)
371    }
372
373    /// Set vendor information using an official vendor ID
374    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    /// Set vendor information with custom name (preserves vendor ID)
385    pub fn set_vendor_name(&mut self, name: String) {
386        self.vendor_name = name;
387    }
388
389    /// Check if the current vendor ID is officially assigned
390    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    /// Check if the current vendor ID is reserved for testing
396    pub fn is_vendor_id_test(&self) -> bool {
397        crate::vendor::is_vendor_id_reserved(self.vendor_identifier)
398    }
399
400    /// Get a formatted string showing vendor information
401    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/// Device status enumeration
546#[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/// Segmentation support enumeration
558#[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    /// Protocol services supported bitfield
598    #[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/// Address binding for device routing
677#[derive(Debug, Clone)]
678pub struct AddressBinding {
679    pub device_identifier: ObjectIdentifier,
680    pub network_address: Vec<u8>,
681}
682
683/// Analog object types (AI, AO, AV)
684pub mod analog;
685/// Binary object types (BI, BO, BV)
686pub mod binary;
687/// Object database for managing BACnet objects
688#[cfg(feature = "std")]
689pub mod database;
690/// Device object and object functions API
691pub mod device;
692/// Engineering units enumeration
693pub mod engineering_units;
694/// File object type
695pub mod file;
696/// Multi-state object types (MSI, MSO, MSV)
697pub 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        // Test getting properties
737        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        // Test setting properties
745        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}