1use crate::collections::HashMap;
8use crate::prelude::*;
9use alloc::borrow::Cow;
10use alloc::sync::Arc;
11
12use bacnet_encoding::primitives::encode_timestamp_choice;
13use bacnet_types::enums::{
14 BackupAndRestoreState, DeviceStatus, ErrorClass, ErrorCode, ObjectType, PropertyIdentifier,
15 Segmentation, ServiceSupported,
16};
17use bacnet_types::error::Error;
18use bacnet_types::primitives::{BACnetTimeStamp, ObjectIdentifier, PropertyValue};
19use bytes::BytesMut;
20
21use crate::clock::{ClockFrame, ClockReader, UNSPECIFIED_DATETIME};
22use crate::common::{
23 invalid_data_type_error, property_is_not_an_array_error, read_array,
24 read_property_list_property, value_out_of_range_error,
25};
26use crate::object_profile::{ObjectProfile, ProfileState, TagsPersistence};
27use crate::traits::BACnetObject;
28
29mod audit_recipient;
30mod metadata;
31pub use audit_recipient::{AuditRecipientChangeSink, AuditWriteSource, DeviceAuthority};
32
33pub const EXECUTED_SERVICES: &[ServiceSupported] = &[
43 ServiceSupported::ACKNOWLEDGE_ALARM,
44 ServiceSupported::CONFIRMED_EVENT_NOTIFICATION,
47 ServiceSupported::GET_ALARM_SUMMARY,
48 ServiceSupported::GET_ENROLLMENT_SUMMARY,
49 ServiceSupported::SUBSCRIBE_COV,
50 ServiceSupported::ATOMIC_READ_FILE,
51 ServiceSupported::ATOMIC_WRITE_FILE,
52 ServiceSupported::ADD_LIST_ELEMENT,
53 ServiceSupported::REMOVE_LIST_ELEMENT,
54 ServiceSupported::CREATE_OBJECT,
55 ServiceSupported::DELETE_OBJECT,
56 ServiceSupported::READ_PROPERTY,
57 ServiceSupported::READ_PROPERTY_MULTIPLE,
58 ServiceSupported::WRITE_PROPERTY,
59 ServiceSupported::WRITE_PROPERTY_MULTIPLE,
60 ServiceSupported::DEVICE_COMMUNICATION_CONTROL,
61 ServiceSupported::CONFIRMED_TEXT_MESSAGE,
62 ServiceSupported::REINITIALIZE_DEVICE,
63 ServiceSupported::UNCONFIRMED_EVENT_NOTIFICATION,
64 ServiceSupported::UNCONFIRMED_TEXT_MESSAGE,
65 ServiceSupported::TIME_SYNCHRONIZATION,
66 ServiceSupported::WHO_HAS,
67 ServiceSupported::WHO_IS,
68 ServiceSupported::READ_RANGE,
69 ServiceSupported::UTC_TIME_SYNCHRONIZATION,
70 ServiceSupported::LIFE_SAFETY_OPERATION,
73 ServiceSupported::SUBSCRIBE_COV_PROPERTY,
74 ServiceSupported::GET_EVENT_INFORMATION,
75 ServiceSupported::WRITE_GROUP,
77 ServiceSupported::SUBSCRIBE_COV_PROPERTY_MULTIPLE,
78 ServiceSupported::CONFIRMED_AUDIT_NOTIFICATION,
79 ServiceSupported::UNCONFIRMED_AUDIT_NOTIFICATION,
80 ServiceSupported::AUDIT_LOG_QUERY,
81];
82
83const SERVICES_SUPPORTED_BITS: usize = 49;
86
87fn compute_services_supported(services: &[ServiceSupported]) -> Vec<u8> {
91 let mut bits = vec![0u8; SERVICES_SUPPORTED_BITS.div_ceil(8)];
92 for service in services {
93 let n = service.to_raw() as usize;
94 if n < SERVICES_SUPPORTED_BITS {
95 bits[n / 8] |= 0x80 >> (n % 8);
96 }
97 }
98 bits
99}
100
101fn compute_object_types_supported(types: &[u32]) -> Vec<u8> {
104 let max_type = types.iter().copied().max().unwrap_or(0) as usize;
105 let num_bytes = (max_type / 8) + 1;
106 let mut bitstring = vec![0u8; num_bytes];
107 for &t in types {
108 let byte_idx = (t as usize) / 8;
109 let bit_pos = 7 - ((t as usize) % 8);
110 if byte_idx < bitstring.len() {
111 bitstring[byte_idx] |= 1 << bit_pos;
112 }
113 }
114 bitstring
115}
116
117pub struct DeviceConfig {
119 pub identity: crate::DeviceIdentity,
121 pub vendor_name: String,
123 pub model_name: String,
125 pub firmware_revision: String,
127 pub application_software_version: String,
129 pub apdu_timeout: u32,
131 pub apdu_retries: u32,
133 pub backup_and_restore: bool,
137 pub backup_failure_timeout: u16,
139}
140
141impl Default for DeviceConfig {
142 fn default() -> Self {
143 Self {
144 identity: crate::DeviceIdentity::default(),
145 vendor_name: "Rusty BACnet".into(),
146 model_name: "rusty-bacnet".into(),
147 firmware_revision: "0.1.0".into(),
148 application_software_version: "0.1.0".into(),
149 apdu_timeout: 6000,
150 apdu_retries: 3,
151 backup_and_restore: false,
152 backup_failure_timeout: 60,
153 }
154 }
155}
156
157fn timestamp_value(stamp: &BACnetTimeStamp) -> PropertyValue {
158 let mut encoded = BytesMut::new();
159 encode_timestamp_choice(&mut encoded, stamp)
160 .expect("BACnetTimeStamp CHOICE encoding is infallible");
161 PropertyValue::ApplicationData(encoded.to_vec())
162}
163
164pub struct DeviceObject {
166 oid: ObjectIdentifier,
167 recipient: audit_recipient::RecipientState,
168 properties: HashMap<PropertyIdentifier, PropertyValue>,
169 profile: ProfileState,
170 object_list: Vec<ObjectIdentifier>,
172 protocol_object_types_supported: Vec<u8>,
175 configured_services_supported: Vec<ServiceSupported>,
177 clock: Option<Arc<dyn ClockReader>>,
179}
180
181impl DeviceObject {
182 pub fn new(config: DeviceConfig) -> Result<Self, Error> {
184 config.identity.validate()?;
185 let oid = config.identity.device_oid();
186 let mut properties = HashMap::new();
187
188 properties.insert(
189 PropertyIdentifier::OBJECT_IDENTIFIER,
190 PropertyValue::ObjectIdentifier(oid),
191 );
192 properties.insert(
193 PropertyIdentifier::OBJECT_NAME,
194 PropertyValue::CharacterString(config.identity.name().to_owned()),
195 );
196 properties.insert(
197 PropertyIdentifier::OBJECT_TYPE,
198 PropertyValue::Enumerated(ObjectType::DEVICE.to_raw()),
199 );
200 properties.insert(
201 PropertyIdentifier::SYSTEM_STATUS,
202 PropertyValue::Enumerated(DeviceStatus::OPERATIONAL.to_raw()),
203 );
204 properties.insert(
205 PropertyIdentifier::VENDOR_NAME,
206 PropertyValue::CharacterString(config.vendor_name),
207 );
208 properties.insert(
209 PropertyIdentifier::VENDOR_IDENTIFIER,
210 PropertyValue::Unsigned(config.identity.vendor_id() as u64),
211 );
212 properties.insert(
213 PropertyIdentifier::MODEL_NAME,
214 PropertyValue::CharacterString(config.model_name),
215 );
216 properties.insert(
217 PropertyIdentifier::FIRMWARE_REVISION,
218 PropertyValue::CharacterString(config.firmware_revision),
219 );
220 properties.insert(
221 PropertyIdentifier::APPLICATION_SOFTWARE_VERSION,
222 PropertyValue::CharacterString(config.application_software_version),
223 );
224 properties.insert(
225 PropertyIdentifier::PROTOCOL_VERSION,
226 PropertyValue::Unsigned(u64::from(bacnet_types::PROTOCOL_VERSION)),
227 );
228 properties.insert(
229 PropertyIdentifier::PROTOCOL_REVISION,
230 PropertyValue::Unsigned(u64::from(bacnet_types::PROTOCOL_REVISION)),
231 );
232 properties.insert(
233 PropertyIdentifier::MAX_APDU_LENGTH_ACCEPTED,
234 PropertyValue::Unsigned(config.identity.max_apdu_length() as u64),
235 );
236 properties.insert(
237 PropertyIdentifier::SEGMENTATION_SUPPORTED,
238 PropertyValue::Enumerated(config.identity.segmentation().to_raw() as u32),
239 );
240 properties.insert(
241 PropertyIdentifier::APDU_TIMEOUT,
242 PropertyValue::Unsigned(config.apdu_timeout as u64),
243 );
244 properties.insert(
245 PropertyIdentifier::NUMBER_OF_APDU_RETRIES,
246 PropertyValue::Unsigned(config.apdu_retries as u64),
247 );
248 properties.insert(
249 PropertyIdentifier::DATABASE_REVISION,
250 PropertyValue::Unsigned(0),
251 );
252 properties.insert(
253 PropertyIdentifier::DESCRIPTION,
254 PropertyValue::CharacterString(String::new()),
255 );
256
257 properties.insert(
260 PropertyIdentifier::DEVICE_ADDRESS_BINDING,
261 PropertyValue::List(Vec::new()),
262 );
263
264 properties.insert(
266 PropertyIdentifier::LAST_RESTART_REASON,
267 PropertyValue::Enumerated(0), );
269
270 properties.insert(
272 PropertyIdentifier::DEVICE_UUID,
273 PropertyValue::OctetString(config.identity.device_uuid().to_vec()),
274 );
275
276 if config.backup_and_restore {
280 if config.backup_failure_timeout == 0 {
281 return Err(Error::Encoding(
282 "Backup_Failure_Timeout must be positive when backup and restore is on".into(),
283 ));
284 }
285 properties.insert(
286 PropertyIdentifier::CONFIGURATION_FILES,
287 PropertyValue::List(Vec::new()),
288 );
289 let (date, time) = UNSPECIFIED_DATETIME;
291 properties.insert(
292 PropertyIdentifier::LAST_RESTORE_TIME,
293 timestamp_value(&BACnetTimeStamp::DateTime { date, time }),
294 );
295 properties.insert(
296 PropertyIdentifier::BACKUP_FAILURE_TIMEOUT,
297 PropertyValue::Unsigned(u64::from(config.backup_failure_timeout)),
298 );
299 properties.insert(
300 PropertyIdentifier::BACKUP_AND_RESTORE_STATE,
301 PropertyValue::Enumerated(BackupAndRestoreState::IDLE.to_raw()),
302 );
303 }
304
305 if config.identity.segmentation() != Segmentation::NONE {
307 if config.identity.apdu_segment_timeout_ms() == 0 {
308 return Err(Error::Encoding(
309 "APDU_Segment_Timeout must be positive when segmentation is supported".into(),
310 ));
311 }
312 properties.insert(
313 PropertyIdentifier::APDU_SEGMENT_TIMEOUT,
314 PropertyValue::Unsigned(config.identity.apdu_segment_timeout_ms()),
315 );
316 let max_segments_accepted = if config.identity.segmentation() == Segmentation::TRANSMIT
317 {
318 1
319 } else {
320 65
321 };
322 properties.insert(
323 PropertyIdentifier::MAX_SEGMENTS_ACCEPTED,
324 PropertyValue::Unsigned(max_segments_accepted),
325 );
326 }
327
328 let protocol_object_types_supported = compute_object_types_supported(&[
332 ObjectType::ANALOG_INPUT.to_raw(),
333 ObjectType::ANALOG_OUTPUT.to_raw(),
334 ObjectType::ANALOG_VALUE.to_raw(),
335 ObjectType::BINARY_INPUT.to_raw(),
336 ObjectType::BINARY_OUTPUT.to_raw(),
337 ObjectType::BINARY_VALUE.to_raw(),
338 ObjectType::CALENDAR.to_raw(),
339 ObjectType::COMMAND.to_raw(),
340 ObjectType::DEVICE.to_raw(),
341 ObjectType::EVENT_ENROLLMENT.to_raw(),
342 ObjectType::FILE.to_raw(),
343 ObjectType::GROUP.to_raw(),
344 ObjectType::LOOP.to_raw(),
345 ObjectType::MULTI_STATE_INPUT.to_raw(),
346 ObjectType::MULTI_STATE_OUTPUT.to_raw(),
347 ObjectType::NOTIFICATION_CLASS.to_raw(),
348 ObjectType::PROGRAM.to_raw(),
349 ObjectType::SCHEDULE.to_raw(),
350 ObjectType::AVERAGING.to_raw(),
351 ObjectType::MULTI_STATE_VALUE.to_raw(),
352 ObjectType::TREND_LOG.to_raw(),
353 ObjectType::LIFE_SAFETY_POINT.to_raw(),
354 ObjectType::LIFE_SAFETY_ZONE.to_raw(),
355 ObjectType::ACCUMULATOR.to_raw(),
356 ObjectType::PULSE_CONVERTER.to_raw(),
357 ObjectType::EVENT_LOG.to_raw(),
358 ObjectType::GLOBAL_GROUP.to_raw(),
359 ObjectType::TREND_LOG_MULTIPLE.to_raw(),
360 ObjectType::LOAD_CONTROL.to_raw(),
361 ObjectType::STRUCTURED_VIEW.to_raw(),
362 ObjectType::ACCESS_DOOR.to_raw(),
363 ObjectType::TIMER.to_raw(),
364 ObjectType::ACCESS_CREDENTIAL.to_raw(),
365 ObjectType::ACCESS_POINT.to_raw(),
366 ObjectType::ACCESS_RIGHTS.to_raw(),
367 ObjectType::ACCESS_USER.to_raw(),
368 ObjectType::ACCESS_ZONE.to_raw(),
369 ObjectType::CREDENTIAL_DATA_INPUT.to_raw(),
370 ObjectType::BITSTRING_VALUE.to_raw(),
371 ObjectType::CHARACTERSTRING_VALUE.to_raw(),
372 ObjectType::DATEPATTERN_VALUE.to_raw(),
373 ObjectType::DATE_VALUE.to_raw(),
374 ObjectType::DATETIMEPATTERN_VALUE.to_raw(),
375 ObjectType::DATETIME_VALUE.to_raw(),
376 ObjectType::INTEGER_VALUE.to_raw(),
377 ObjectType::LARGE_ANALOG_VALUE.to_raw(),
378 ObjectType::OCTETSTRING_VALUE.to_raw(),
379 ObjectType::POSITIVE_INTEGER_VALUE.to_raw(),
380 ObjectType::TIMEPATTERN_VALUE.to_raw(),
381 ObjectType::TIME_VALUE.to_raw(),
382 ObjectType::NOTIFICATION_FORWARDER.to_raw(),
383 ObjectType::ALERT_ENROLLMENT.to_raw(),
384 ObjectType::CHANNEL.to_raw(),
385 ObjectType::LIGHTING_OUTPUT.to_raw(),
386 ObjectType::BINARY_LIGHTING_OUTPUT.to_raw(),
387 ObjectType::NETWORK_PORT.to_raw(),
388 ObjectType::ELEVATOR_GROUP.to_raw(),
389 ObjectType::ESCALATOR.to_raw(),
390 ObjectType::LIFT.to_raw(),
391 ObjectType::STAGING.to_raw(),
392 ObjectType::AUDIT_LOG.to_raw(),
393 ObjectType::AUDIT_REPORTER.to_raw(),
394 ObjectType::COLOR.to_raw(),
395 ObjectType::COLOR_TEMPERATURE.to_raw(),
396 ]);
397
398 Ok(Self {
399 oid,
400 recipient: Default::default(),
401 profile: ProfileState::default(),
402 properties,
403 object_list: vec![oid], protocol_object_types_supported,
405 configured_services_supported: EXECUTED_SERVICES.to_vec(),
406 clock: None,
407 })
408 }
409
410 pub fn with_tags_persistence(
414 config: DeviceConfig,
415 persistence: Arc<dyn TagsPersistence>,
416 ) -> Result<Self, Error> {
417 let mut object = Self::new(config)?;
418 object.profile = ProfileState::persistent(object.oid, persistence)?;
419 Ok(object)
420 }
421
422 pub fn set_profile(&mut self, profile: ObjectProfile) -> Result<(), Error> {
427 self.profile.provision(profile)
428 }
429
430 pub fn wait_for_tag_saves(&self) {
433 self.profile.wait_for_saves();
434 }
435
436 pub fn set_object_list(&mut self, oids: Vec<ObjectIdentifier>) {
438 self.object_list = oids;
439 }
440
441 pub fn set_system_status(&mut self, status: DeviceStatus) {
443 self.properties.insert(
444 PropertyIdentifier::SYSTEM_STATUS,
445 PropertyValue::Enumerated(status.to_raw()),
446 );
447 }
448
449 pub fn set_database_revision(&mut self, revision: u32) {
452 self.properties.insert(
453 PropertyIdentifier::DATABASE_REVISION,
454 PropertyValue::Unsigned(u64::from(revision)),
455 );
456 }
457
458 pub fn set_configuration_files(&mut self, files: Vec<ObjectIdentifier>) {
461 self.set_backup_property(
462 PropertyIdentifier::CONFIGURATION_FILES,
463 PropertyValue::List(
464 files
465 .into_iter()
466 .map(PropertyValue::ObjectIdentifier)
467 .collect(),
468 ),
469 );
470 }
471
472 pub fn set_backup_and_restore_state(&mut self, state: BackupAndRestoreState) {
475 self.set_backup_property(
476 PropertyIdentifier::BACKUP_AND_RESTORE_STATE,
477 PropertyValue::Enumerated(state.to_raw()),
478 );
479 }
480
481 pub fn set_last_restore_time(&mut self, time: BACnetTimeStamp) {
484 self.set_backup_property(
485 PropertyIdentifier::LAST_RESTORE_TIME,
486 timestamp_value(&time),
487 );
488 }
489
490 fn set_backup_property(&mut self, property: PropertyIdentifier, value: PropertyValue) {
491 if let Some(current) = self.properties.get_mut(&property) {
492 *current = value;
493 }
494 }
495
496 fn write_backup_failure_timeout(
497 &mut self,
498 array_index: Option<u32>,
499 value: PropertyValue,
500 ) -> Result<(), Error> {
501 if array_index.is_some() {
502 return Err(property_is_not_an_array_error());
503 }
504 match value {
507 PropertyValue::Null => Ok(()),
508 PropertyValue::Unsigned(seconds) if (1..=u64::from(u16::MAX)).contains(&seconds) => {
509 self.properties.insert(
510 PropertyIdentifier::BACKUP_FAILURE_TIMEOUT,
511 PropertyValue::Unsigned(seconds),
512 );
513 Ok(())
514 }
515 PropertyValue::Unsigned(_) => Err(value_out_of_range_error()),
516 _ => Err(invalid_data_type_error()),
517 }
518 }
519
520 pub fn set_services_supported(&mut self, services: &[ServiceSupported]) {
529 self.configured_services_supported = services.to_vec();
530 }
531
532 pub fn instance(&self) -> u32 {
534 self.oid.instance_number()
535 }
536
537 pub fn set_description(&mut self, desc: impl Into<String>) {
539 self.properties.insert(
540 PropertyIdentifier::DESCRIPTION,
541 PropertyValue::CharacterString(desc.into()),
542 );
543 }
544
545 pub fn set_device_uuid(&mut self, uuid: [u8; 16]) {
553 self.properties.insert(
554 PropertyIdentifier::DEVICE_UUID,
555 PropertyValue::OctetString(uuid.to_vec()),
556 );
557 }
558
559 fn clock_frame(&self) -> Option<ClockFrame> {
560 self.clock.as_ref()?.read_clock()
561 }
562
563 fn cov_property_present(&self, property: PropertyIdentifier) -> bool {
564 match property {
565 PropertyIdentifier::ACTIVE_COV_SUBSCRIPTIONS => {
566 self.configured_services_supported.iter().any(|service| {
567 matches!(
568 *service,
569 ServiceSupported::SUBSCRIBE_COV | ServiceSupported::SUBSCRIBE_COV_PROPERTY
570 )
571 })
572 }
573 PropertyIdentifier::ACTIVE_COV_MULTIPLE_SUBSCRIPTIONS => self
574 .configured_services_supported
575 .contains(&ServiceSupported::SUBSCRIBE_COV_PROPERTY_MULTIPLE),
576 _ => true,
577 }
578 }
579
580 fn services_supported(&self) -> Vec<u8> {
581 let clock_available = self.clock_frame().is_some();
582 let services = self
583 .configured_services_supported
584 .iter()
585 .copied()
586 .filter(|service| {
587 clock_available
588 || (*service != ServiceSupported::TIME_SYNCHRONIZATION
589 && *service != ServiceSupported::UTC_TIME_SYNCHRONIZATION)
590 })
591 .collect::<Vec<_>>();
592 compute_services_supported(&services)
593 }
594}
595
596impl BACnetObject for DeviceObject {
597 fn device_authority_internal(&mut self) -> Option<DeviceAuthority<'_>> {
598 Some(DeviceAuthority(self))
599 }
600
601 fn object_identifier(&self) -> ObjectIdentifier {
602 self.oid
603 }
604
605 fn object_name(&self) -> &str {
606 match self.properties.get(&PropertyIdentifier::OBJECT_NAME) {
607 Some(PropertyValue::CharacterString(s)) => s,
608 _ => "Unknown",
609 }
610 }
611
612 fn read_property(
613 &self,
614 property: PropertyIdentifier,
615 array_index: Option<u32>,
616 ) -> Result<PropertyValue, Error> {
617 if property == PropertyIdentifier::AUDIT_NOTIFICATION_RECIPIENT {
618 return self.read_audit_recipient(array_index);
619 }
620 if property == PropertyIdentifier::OBJECT_LIST {
621 return match array_index {
622 None => {
623 let elements = self
624 .object_list
625 .iter()
626 .map(|oid| PropertyValue::ObjectIdentifier(*oid))
627 .collect();
628 Ok(PropertyValue::List(elements))
629 }
630 Some(0) => {
631 Ok(PropertyValue::Unsigned(self.object_list.len() as u64))
633 }
634 Some(idx) => {
635 let i = (idx - 1) as usize; if i < self.object_list.len() {
637 Ok(PropertyValue::ObjectIdentifier(self.object_list[i]))
638 } else {
639 Err(Error::protocol(
640 ErrorClass::PROPERTY,
641 ErrorCode::INVALID_ARRAY_INDEX,
642 ))
643 }
644 }
645 };
646 }
647
648 if property == PropertyIdentifier::PROPERTY_LIST {
649 return read_property_list_property(&self.property_list(), array_index);
650 }
651
652 if property == PropertyIdentifier::CONFIGURATION_FILES
653 && let Some(PropertyValue::List(files)) = self.properties.get(&property)
654 {
655 return read_array(files.clone(), array_index);
656 }
657
658 if matches!(
659 property,
660 PropertyIdentifier::LOCAL_DATE
661 | PropertyIdentifier::LOCAL_TIME
662 | PropertyIdentifier::UTC_OFFSET
663 | PropertyIdentifier::DAYLIGHT_SAVINGS_STATUS
664 ) {
665 let frame = self.clock_frame().ok_or(Error::protocol(
666 ErrorClass::PROPERTY,
667 ErrorCode::UNKNOWN_PROPERTY,
668 ))?;
669 return Ok(match property {
670 PropertyIdentifier::LOCAL_DATE => PropertyValue::Date(frame.local_date),
671 PropertyIdentifier::LOCAL_TIME => PropertyValue::Time(frame.local_time),
672 PropertyIdentifier::UTC_OFFSET => {
673 PropertyValue::Signed(i32::from(frame.utc_offset))
674 }
675 PropertyIdentifier::DAYLIGHT_SAVINGS_STATUS => {
676 PropertyValue::Boolean(frame.daylight_savings_status)
677 }
678 _ => unreachable!(),
679 });
680 }
681
682 if property == PropertyIdentifier::PROTOCOL_OBJECT_TYPES_SUPPORTED {
683 let num_bytes = self.protocol_object_types_supported.len();
684 let total_bits = num_bytes * 8;
685 let mut max_type = 0u32;
687 for (byte_idx, &byte) in self.protocol_object_types_supported.iter().enumerate() {
688 for bit in 0..8 {
689 if byte & (1 << (7 - bit)) != 0 {
690 max_type = (byte_idx * 8 + bit) as u32;
691 }
692 }
693 }
694 let used_bits = max_type as usize + 1;
695 let unused = (total_bits - used_bits) as u8;
696 return Ok(PropertyValue::BitString {
697 unused_bits: unused,
698 data: self.protocol_object_types_supported.clone(),
699 });
700 }
701
702 if property == PropertyIdentifier::PROTOCOL_SERVICES_SUPPORTED {
703 let services_supported = self.services_supported();
704 let unused = (services_supported.len() * 8 - SERVICES_SUPPORTED_BITS) as u8;
705 return Ok(PropertyValue::BitString {
706 unused_bits: unused,
707 data: services_supported,
708 });
709 }
710
711 if matches!(
712 property,
713 PropertyIdentifier::ACTIVE_COV_SUBSCRIPTIONS
714 | PropertyIdentifier::ACTIVE_COV_MULTIPLE_SUBSCRIPTIONS
715 ) {
716 return if self.cov_property_present(property) {
721 Ok(PropertyValue::ApplicationData(Vec::new()))
722 } else {
723 Err(Error::protocol(
724 ErrorClass::PROPERTY,
725 ErrorCode::UNKNOWN_PROPERTY,
726 ))
727 };
728 }
729
730 if let Some(value) = self.profile.read(property, array_index) {
731 return value;
732 }
733 self.properties
734 .get(&property)
735 .cloned()
736 .ok_or(Error::protocol(
737 ErrorClass::PROPERTY,
738 ErrorCode::UNKNOWN_PROPERTY,
739 ))
740 }
741
742 fn write_property(
743 &mut self,
744 property: PropertyIdentifier,
745 array_index: Option<u32>,
746 value: PropertyValue,
747 _priority: Option<u8>,
748 ) -> Result<(), Error> {
749 if property == PropertyIdentifier::AUDIT_NOTIFICATION_RECIPIENT {
750 return self.write_audit_recipient(array_index, value, _priority, None);
751 }
752 if let Some(result) = self.profile.write(property, array_index, &value) {
753 return result;
754 }
755 if property == PropertyIdentifier::DESCRIPTION {
756 if array_index.is_some() {
757 return Err(Error::protocol(
758 ErrorClass::PROPERTY,
759 ErrorCode::PROPERTY_IS_NOT_AN_ARRAY,
760 ));
761 }
762 if value == PropertyValue::Null {
765 return Ok(());
766 }
767 if let PropertyValue::CharacterString(_) = &value {
768 self.properties.insert(property, value);
769 return Ok(());
770 }
771 return Err(Error::protocol(
772 ErrorClass::PROPERTY,
773 ErrorCode::INVALID_DATA_TYPE,
774 ));
775 }
776 if property == PropertyIdentifier::BACKUP_FAILURE_TIMEOUT
777 && self.properties.contains_key(&property)
778 {
779 return self.write_backup_failure_timeout(array_index, value);
780 }
781 Err(crate::common::unhandled_write_error(
782 self.property_metadata().as_ref(),
783 property,
784 array_index,
785 ))
786 }
787
788 fn durable_writes_internal(&mut self) -> Option<&mut dyn crate::durable::DurableWrites> {
789 self.profile.capability()
790 }
791
792 fn advance_monotonic_time_internal(&mut self, now: core::time::Duration) -> bool {
793 self.profile.expire(now);
794 false
795 }
796
797 fn property_metadata(&self) -> Cow<'_, [crate::property_metadata::PropertyMetadata]> {
798 metadata::for_object(self)
799 }
800
801 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
802 Cow::Owned(
805 self.property_metadata()
806 .iter()
807 .map(|row| row.property_identifier)
808 .collect(),
809 )
810 }
811
812 fn is_createable(&self) -> bool {
814 false
815 }
816 fn is_deleteable(&self) -> bool {
817 false
818 }
819
820 fn bind_clock_internal(&mut self, clock: Option<Arc<dyn ClockReader>>) {
821 self.clock = clock;
822 }
823}
824
825#[cfg(test)]
826mod tests;
827
828#[cfg(test)]
829mod service_profile_tests;