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