1use super::*;
2use crate::common::{
3 read_analog_event_properties, read_generic_event_properties, write_analog_event_properties,
4 write_generic_event_properties,
5};
6use crate::object_profile::{ObjectProfile, ProfileState, TagsPersistence};
7use crate::prelude::*;
8use crate::present_value_access::PresentValueAccess;
9use crate::property_metadata::PropertyMetadata;
10use alloc::sync::Arc;
11
12mod metadata;
13#[cfg(test)]
14mod nonfinite_tests;
15
16pub struct AnalogValueObject {
22 profile: ProfileState,
23 audit_policy: crate::audit::ObjectAuditPolicy,
24 oid: ObjectIdentifier,
25 name: String,
26 description: String,
27 present_value: f32,
28 units: EngineeringUnits,
29 out_of_service: bool,
30 status_flags: StatusFlags,
31 access: PresentValueAccess,
32 priority_array: [Option<f32>; 16],
34 relinquish_default: f32,
35 cov_increment: f32,
39 event_detector: OutOfRangeDetector,
40 event_detection_enable: bool,
43 reliability: Reliability,
45 reliability_before_out_of_service: Option<Reliability>,
46 reliability_inhibit: common::ReliabilityInhibitState,
47 fault_out_of_range: FaultOutOfRangeState,
48 min_pres_value: Option<f32>,
49 max_pres_value: Option<f32>,
50 pub(crate) event_history: EventHistory,
51 value_source: crate::command_source::ValueSourceTracking,
53 write_source: crate::command_source::SingleValueSource,
55}
56
57impl AnalogValueObject {
58 pub fn set_audit_policy(&mut self, policy: crate::audit::ObjectAuditPolicy) {
62 self.audit_policy = policy;
63 }
64
65 pub fn new(
67 instance: u32,
68 name: impl Into<String>,
69 units: EngineeringUnits,
70 ) -> Result<Self, Error> {
71 Self::with_access(instance, name, units, PresentValueAccess::Commandable)
72 }
73
74 pub fn with_access(
76 instance: u32,
77 name: impl Into<String>,
78 units: EngineeringUnits,
79 access: PresentValueAccess,
80 ) -> Result<Self, Error> {
81 let oid = ObjectIdentifier::new(ObjectType::ANALOG_VALUE, instance)?;
82 Ok(Self {
83 profile: ProfileState::default(),
84 audit_policy: crate::audit::ObjectAuditPolicy::default(),
85 oid,
86 name: name.into(),
87 description: String::new(),
88 present_value: 0.0,
89 units,
90 out_of_service: false,
91 status_flags: StatusFlags::empty(),
92 access,
93 priority_array: [None; 16],
94 relinquish_default: 0.0,
95 cov_increment: 0.0,
96 event_detector: OutOfRangeDetector::default(),
97 event_detection_enable: true,
98 reliability: Reliability::NO_FAULT_DETECTED,
99 reliability_before_out_of_service: None,
100 reliability_inhibit: common::ReliabilityInhibitState::default(),
101 fault_out_of_range: FaultOutOfRangeState::default(),
102 min_pres_value: None,
103 max_pres_value: None,
104 event_history: EventHistory::default(),
105 value_source: crate::command_source::ValueSourceTracking::default(),
106 write_source: crate::command_source::SingleValueSource::default(),
107 })
108 }
109
110 pub fn with_tags_persistence(
115 instance: u32,
116 name: impl Into<String>,
117 units: EngineeringUnits,
118 access: PresentValueAccess,
119 persistence: Arc<dyn TagsPersistence>,
120 ) -> Result<Self, Error> {
121 let mut object = Self::with_access(instance, name, units, access)?;
122 object.profile = ProfileState::persistent(object.oid, persistence)?;
123 Ok(object)
124 }
125
126 pub fn set_profile(&mut self, profile: ObjectProfile) -> Result<(), Error> {
131 self.profile.provision(profile)
132 }
133
134 pub fn wait_for_tag_saves(&self) {
137 self.profile.wait_for_saves();
138 }
139
140 pub fn set_value_source_tracking(&mut self, enabled: bool) {
150 self.write_source.set_enabled(enabled);
151 }
152
153 pub fn set_description(&mut self, desc: impl Into<String>) {
155 self.description = desc.into();
156 }
157
158 pub fn set_min_pres_value(&mut self, value: f32) {
160 self.min_pres_value = Some(value);
161 }
162
163 pub fn set_max_pres_value(&mut self, value: f32) {
165 self.max_pres_value = Some(value);
166 }
167
168 pub fn configure_fault_out_of_range(&mut self, low: f32, high: f32) -> Result<(), Error> {
174 self.fault_out_of_range.configure(low, high)
175 }
176
177 fn recalculate_present_value(&mut self) {
179 self.present_value =
180 common::recalculate_from_priority_array(&self.priority_array, self.relinquish_default);
181 }
182
183 pub fn set_relinquish_default(&mut self, value: f32) -> Result<(), Error> {
189 if self.access != PresentValueAccess::Commandable {
190 return Err(common::unknown_property_error());
191 }
192 common::reject_non_finite(value)?;
193 self.relinquish_default = value;
194 self.recalculate_present_value();
195 Ok(())
196 }
197
198 fn write_direct_present_value(
200 &mut self,
201 array_index: Option<u32>,
202 value: PropertyValue,
203 ) -> Result<(), Error> {
204 if array_index.is_some() {
205 return Err(common::property_is_not_an_array_error());
206 }
207 if self.access == PresentValueAccess::ReadOnly && !self.out_of_service {
208 return Err(common::write_access_denied_error());
209 }
210 if value == PropertyValue::Null {
212 return Ok(());
213 }
214 self.present_value = Self::checked_present_value(value)?;
215 Ok(())
216 }
217
218 fn checked_present_value(value: PropertyValue) -> Result<f32, Error> {
219 let PropertyValue::Real(v) = value else {
220 return Err(common::invalid_data_type_error());
221 };
222 if !v.is_finite() {
223 return Err(common::value_out_of_range_error());
224 }
225 Ok(v)
226 }
227}
228
229impl BACnetObject for AnalogValueObject {
230 fn audit_policy_authority_internal(
231 &mut self,
232 ) -> Option<crate::audit::AuditPolicyAuthority<'_>> {
233 Some(crate::audit::AuditPolicyAuthority::new(
234 &mut self.audit_policy,
235 ))
236 }
237
238 fn audit_object_policy_internal(&self) -> crate::audit::ObjectAuditPolicy {
239 self.audit_policy
240 }
241
242 fn object_identifier(&self) -> ObjectIdentifier {
243 self.oid
244 }
245
246 fn object_name(&self) -> &str {
247 &self.name
248 }
249
250 fn read_property(
251 &self,
252 property: PropertyIdentifier,
253 array_index: Option<u32>,
254 ) -> Result<PropertyValue, Error> {
255 if metadata::excludes(self, property) {
256 return Err(common::unknown_property_error());
257 }
258 let source = match self.access {
259 PresentValueAccess::Commandable => {
260 self.value_source
261 .read(property, array_index, &self.priority_array)
262 }
263 _ => self.write_source.read(property, array_index),
264 };
265 if let Some(result) = source {
266 return result;
267 }
268 if let Some(result) = self.profile.read(property, array_index) {
269 return result;
270 }
271
272 if let Some(result) = self.audit_policy.read(property, array_index) {
273 return result;
274 }
275 if property == PropertyIdentifier::STATUS_FLAGS {
276 return Ok(common::compute_status_flags(
277 self.status_flags,
278 self.reliability,
279 self.out_of_service,
280 self.event_detector.event_state,
281 ));
282 }
283 if let Some(value) = self.reliability_inhibit.read(property) {
284 return Ok(value);
285 }
286 if let Some(result) = read_common_properties!(self, property, array_index) {
287 return result;
288 }
289 if let Some(result) = read_analog_event_properties!(self, property) {
290 return result;
291 }
292 if let Some(result) = self.event_history.read(property, array_index) {
293 return result;
294 }
295 if let Some(result) = read_generic_event_properties!(self, property) {
296 return result;
297 }
298 if property == PropertyIdentifier::EVENT_DETECTION_ENABLE {
299 return Ok(PropertyValue::Boolean(self.event_detection_enable));
300 }
301 if let Some(value) = self.fault_out_of_range.read_limit(property) {
302 return Ok(value);
303 }
304 match property {
305 p if p == PropertyIdentifier::OBJECT_TYPE => {
306 Ok(PropertyValue::Enumerated(ObjectType::ANALOG_VALUE.to_raw()))
307 }
308 p if p == PropertyIdentifier::PRESENT_VALUE => {
309 Ok(PropertyValue::Real(self.present_value))
310 }
311 p if p == PropertyIdentifier::UNITS => {
312 Ok(PropertyValue::Enumerated(self.units.to_raw()))
313 }
314 p if p == PropertyIdentifier::PRIORITY_ARRAY => {
315 common::read_priority_array!(self, array_index, PropertyValue::Real)
316 }
317 p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
318 Ok(PropertyValue::Real(self.relinquish_default))
319 }
320 p if p == PropertyIdentifier::CURRENT_COMMAND_PRIORITY => {
321 Ok(common::current_command_priority(&self.priority_array))
322 }
323 p if p == PropertyIdentifier::COV_INCREMENT => {
324 Ok(PropertyValue::Real(self.cov_increment))
325 }
326 p if p == PropertyIdentifier::MIN_PRES_VALUE => match self.min_pres_value {
327 Some(v) => Ok(PropertyValue::Real(v)),
328 None => Err(common::unknown_property_error()),
329 },
330 p if p == PropertyIdentifier::MAX_PRES_VALUE => match self.max_pres_value {
331 Some(v) => Ok(PropertyValue::Real(v)),
332 None => Err(common::unknown_property_error()),
333 },
334 _ => Err(common::unknown_property_error()),
335 }
336 }
337
338 fn write_property_from(
339 &mut self,
340 property: PropertyIdentifier,
341 array_index: Option<u32>,
342 value: PropertyValue,
343 priority: Option<u8>,
344 origin: &crate::command_source::CommandOrigin,
345 ) -> Result<(), Error> {
346 if matches!(
347 property,
348 PropertyIdentifier::PRESENT_VALUE | PropertyIdentifier::VALUE_SOURCE
349 ) && array_index.is_some()
350 {
351 return Err(common::property_is_not_an_array_error());
352 }
353 if property == PropertyIdentifier::VALUE_SOURCE {
354 return match self.access {
355 PresentValueAccess::Commandable => {
356 self.value_source.correct(value, priority, origin)
357 }
358 _ => self.write_source.correct(array_index, value, origin),
359 };
360 }
361 if property == PropertyIdentifier::PRESENT_VALUE {
362 return match self.access {
363 PresentValueAccess::Commandable => {
364 crate::command_source::write_sourced_priority!(
365 self,
366 value,
367 priority,
368 origin,
369 Self::checked_present_value
370 )
371 }
372 PresentValueAccess::ReadOnly | PresentValueAccess::Writable => {
373 self.write_source.admit(origin)?;
374 let null = value == PropertyValue::Null;
376 self.write_direct_present_value(array_index, value)?;
377 if !null {
378 self.write_source.record(origin);
379 }
380 Ok(())
381 }
382 };
383 }
384 self.write_property(property, array_index, value, priority)
385 }
386
387 fn write_property(
388 &mut self,
389 property: PropertyIdentifier,
390 array_index: Option<u32>,
391 value: PropertyValue,
392 priority: Option<u8>,
393 ) -> Result<(), Error> {
394 if metadata::excludes(self, property) {
395 return Err(common::unknown_property_error());
396 }
397 if let Some(result) = self.profile.write(property, array_index, &value) {
398 return result;
399 }
400 if let Some(result) = self
401 .audit_policy
402 .write(property, array_index, &value, priority)
403 {
404 return result;
405 }
406
407 if property == PropertyIdentifier::PRESENT_VALUE {
408 if self.access == PresentValueAccess::Commandable {
409 return Err(common::write_access_denied_error());
410 }
411 self.write_source.unsourced()?;
413 return self.write_direct_present_value(array_index, value);
414 }
415 if property == PropertyIdentifier::VALUE_SOURCE {
416 return Err(common::write_access_denied_error());
417 }
418 if let Some(result) = self.reliability_inhibit.write_inhibit(
419 &mut self.reliability,
420 self.out_of_service,
421 property,
422 &value,
423 ) {
424 return result;
425 }
426 if let Some(result) = self.reliability_inhibit.write_out_of_service(
427 &mut self.out_of_service,
428 &mut self.reliability,
429 &mut self.reliability_before_out_of_service,
430 property,
431 &value,
432 ) {
433 return result.map(|_| ());
434 }
435 if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
436 return result;
437 }
438 if let Some(result) = common::write_description(&mut self.description, property, &value) {
439 return result;
440 }
441 if let Some(result) = self.reliability_inhibit.write_client_reliability(
446 self.out_of_service,
447 &mut self.reliability,
448 property,
449 &value,
450 ) {
451 return result;
452 }
453 if property == PropertyIdentifier::RELINQUISH_DEFAULT {
454 if let PropertyValue::Real(v) = value {
455 return self.set_relinquish_default(v);
456 }
457 return Err(common::invalid_data_type_error());
458 }
459 if let Some(result) = common::write_cov_increment(&mut self.cov_increment, property, &value)
460 {
461 return result;
462 }
463 if property == PropertyIdentifier::EVENT_DETECTION_ENABLE {
464 if let PropertyValue::Boolean(v) = value {
465 self.event_detection_enable = v;
466 if !v {
467 self.event_detector.event_state = bacnet_types::enums::EventState::NORMAL;
468 self.event_detector.acked_transitions =
469 bacnet_types::bitstring::EventTransitionBits::all();
470 self.event_detector.pending = None;
471 self.event_detector.fault_reliability = None;
472 self.event_history.reset();
473 }
474 return Ok(());
475 }
476 return Err(common::invalid_data_type_error());
477 }
478 if let Some(result) = write_analog_event_properties!(self, property, value) {
479 return result;
480 }
481 if let Some(result) =
483 self.event_history
484 .write(property, array_index, &value, self.event_detection_enable)
485 {
486 return result;
487 }
488 if let Some(result) = write_generic_event_properties!(self, property, value) {
489 return result;
490 }
491 Err(crate::common::unhandled_write_error(
492 self.property_metadata().as_ref(),
493 property,
494 array_index,
495 ))
496 }
497
498 fn durable_writes_internal(&mut self) -> Option<&mut dyn crate::durable::DurableWrites> {
499 self.profile.capability()
500 }
501
502 fn advance_monotonic_time_internal(&mut self, now: core::time::Duration) -> bool {
503 self.profile.expire(now);
504 false
505 }
506
507 fn property_metadata(&self) -> Cow<'_, [PropertyMetadata]> {
508 metadata::for_object(self)
509 }
510
511 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
512 crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
513 }
514
515 fn supports_cov(&self) -> bool {
516 true
517 }
518
519 fn cov_increment(&self) -> Option<f64> {
520 Some(f64::from(self.cov_increment))
521 }
522
523 crate::event::impl_builtin_intrinsic_reporting!(
524 event_detector,
525 event_history,
526 [present_value],
527 reliability,
528 event_detection_enable,
529 OutOfRangeDetector::ALGORITHM
530 );
531
532 fn acknowledge_alarm(
533 &mut self,
534 transition_bit: bacnet_types::bitstring::EventTransitionBits,
535 ) -> Result<(), bacnet_types::error::Error> {
536 self.event_detector.acked_transitions |=
537 transition_bit & bacnet_types::bitstring::EventTransitionBits::all();
538 Ok(())
539 }
540
541 fn acknowledge_alarm_correlated_internal(
542 &mut self,
543 event_state: EventState,
544 timestamp: &BACnetTimeStamp,
545 ) -> Result<(), Error> {
546 if !self.event_detection_enable {
547 return Err(Error::protocol(
548 ErrorClass::OBJECT,
549 ErrorCode::NO_ALARM_CONFIGURED,
550 ));
551 }
552 self.event_history.acknowledge_correlated(
553 &mut self.event_detector.acked_transitions,
554 event_state,
555 timestamp,
556 )
557 }
558
559 fn set_reliability_internal(&mut self, reliability: Reliability) -> Result<(), Error> {
560 if self.out_of_service || self.reliability_inhibit.enabled() {
561 return Err(common::write_access_denied_error());
562 }
563 if !common::is_reliability_value_valid(reliability) {
564 return Err(common::value_out_of_range_error());
565 }
566 self.reliability = reliability;
567 self.fault_out_of_range.clear_ownership();
568 Ok(())
569 }
570
571 fn set_present_value_internal(&mut self, value: PropertyValue) -> Result<(), Error> {
572 match self.access {
573 PresentValueAccess::Commandable => {
574 Err(common::optional_functionality_not_supported_error())
575 }
576 _ if self.out_of_service => Err(common::write_access_denied_error()),
577 PresentValueAccess::ReadOnly | PresentValueAccess::Writable => {
578 self.present_value = Self::checked_present_value(value)?;
579 self.write_source.forget();
580 Ok(())
581 }
582 }
583 }
584
585 fn set_present_value_from_internal(
586 &mut self,
587 value: PropertyValue,
588 origin: &crate::command_source::CommandOrigin,
589 ) -> Result<(), Error> {
590 self.write_source.admit(origin)?;
591 self.set_present_value_internal(value)?;
592 self.write_source.record(origin);
593 Ok(())
594 }
595
596 fn evaluate_reliability_internal(&mut self) -> Result<ReliabilityEvaluation, Error> {
597 if self.out_of_service || self.reliability_inhibit.enabled() {
598 return Ok(ReliabilityEvaluation::Unchanged);
599 }
600 self.fault_out_of_range
601 .evaluate(self.present_value, &mut self.reliability)
602 }
603
604 fn reliability_evaluation_inhibited_internal(&self) -> bool {
605 self.reliability_inhibit.enabled()
606 }
607
608 fn is_createable(&self) -> bool {
611 false
612 }
613}
614
615#[cfg(test)]
616mod detection_enable_reset_tests {
617 use super::*;
618
619 #[test]
622 fn av_disabling_detection_resets_event_history() {
623 let mut av = AnalogValueObject::new(1, "AV-1", EngineeringUnits::DEGREES_CELSIUS).unwrap();
624 assert_eq!(
625 av.read_property(PropertyIdentifier::EVENT_DETECTION_ENABLE, None)
626 .unwrap(),
627 PropertyValue::Boolean(true)
628 );
629 av.event_detector.event_state = bacnet_types::enums::EventState::HIGH_LIMIT;
630 av.event_detector.acked_transitions = bacnet_types::bitstring::EventTransitionBits::empty();
631 av.event_detector.pending = Some(crate::event::PendingTransition {
632 state: bacnet_types::enums::EventState::HIGH_LIMIT,
633 remaining: 2,
634 });
635 av.event_detector.fault_reliability = Some(bacnet_types::enums::Reliability::NO_SENSOR);
636 av.event_history.time_stamps = [
637 BACnetTimeStamp::SequenceNumber(1),
638 BACnetTimeStamp::SequenceNumber(2),
639 BACnetTimeStamp::SequenceNumber(3),
640 ];
641 av.event_history.message_texts = ["offnormal".into(), "fault".into(), "normal".into()];
642
643 av.write_property(
644 PropertyIdentifier::EVENT_DETECTION_ENABLE,
645 None,
646 PropertyValue::Boolean(false),
647 None,
648 )
649 .unwrap();
650
651 assert_eq!(
652 av.read_property(PropertyIdentifier::EVENT_DETECTION_ENABLE, None)
653 .unwrap(),
654 PropertyValue::Boolean(false)
655 );
656 assert_eq!(
657 av.event_detector.event_state,
658 bacnet_types::enums::EventState::NORMAL
659 );
660 assert_eq!(
661 av.event_detector.acked_transitions,
662 bacnet_types::bitstring::EventTransitionBits::all()
663 );
664 assert!(av.event_detector.pending.is_none());
665 assert!(av.event_detector.fault_reliability.is_none());
666 assert_eq!(
667 av.event_history.time_stamps,
668 [
669 BACnetTimeStamp::SequenceNumber(0),
670 BACnetTimeStamp::SequenceNumber(0),
671 BACnetTimeStamp::SequenceNumber(0),
672 ]
673 );
674 assert_eq!(
675 av.event_history.message_texts,
676 [String::new(), String::new(), String::new()]
677 );
678 }
679}
680
681#[cfg(test)]
682mod fault_out_of_range_non_finite_tests {
683 use super::*;
684 use bacnet_types::enums::{EngineeringUnits, ErrorClass, ErrorCode, Reliability};
685 use bacnet_types::error::RemoteError;
686
687 fn assert_value_out_of_range(result: Result<ReliabilityEvaluation, Error>) {
688 assert!(matches!(
689 result,
690 Err(Error::Remote(RemoteError::Error { class, code }))
691 if class == ErrorClass::PROPERTY
692 && code == ErrorCode::VALUE_OUT_OF_RANGE
693 ));
694 }
695
696 #[test]
697 fn av_non_finite_monitored_values_preserve_reliability_status_and_ownership() {
698 for non_finite in [f32::NAN, f32::INFINITY, f32::NEG_INFINITY] {
699 let mut normal =
700 AnalogValueObject::new(1, "AV-normal", EngineeringUnits::DEGREES_CELSIUS).unwrap();
701 normal.configure_fault_out_of_range(10.0, 20.0).unwrap();
702 let status_before = normal
703 .read_property(PropertyIdentifier::STATUS_FLAGS, None)
704 .unwrap();
705 normal.present_value = non_finite;
706
707 assert_value_out_of_range(normal.evaluate_reliability_internal());
708 assert_eq!(normal.present_value.to_bits(), non_finite.to_bits());
709 assert_eq!(normal.reliability, Reliability::NO_FAULT_DETECTED);
710 assert_eq!(
711 normal
712 .read_property(PropertyIdentifier::STATUS_FLAGS, None)
713 .unwrap(),
714 status_before
715 );
716 assert!(normal.fault_out_of_range.owned_fault.is_none());
717
718 normal.present_value = 9.0;
719 assert_eq!(
720 normal.evaluate_reliability_internal().unwrap(),
721 ReliabilityEvaluation::Changed {
722 old_reliability: Reliability::NO_FAULT_DETECTED,
723 new_reliability: Reliability::UNDER_RANGE,
724 }
725 );
726
727 let mut owned =
728 AnalogValueObject::new(2, "AV-owned", EngineeringUnits::DEGREES_CELSIUS).unwrap();
729 owned.configure_fault_out_of_range(10.0, 20.0).unwrap();
730 owned.present_value = 9.0;
731 owned.evaluate_reliability_internal().unwrap();
732 let status_before = owned
733 .read_property(PropertyIdentifier::STATUS_FLAGS, None)
734 .unwrap();
735 owned.present_value = non_finite;
736
737 assert_value_out_of_range(owned.evaluate_reliability_internal());
738 assert_eq!(owned.present_value.to_bits(), non_finite.to_bits());
739 assert_eq!(owned.reliability, Reliability::UNDER_RANGE);
740 assert_eq!(
741 owned
742 .read_property(PropertyIdentifier::STATUS_FLAGS, None)
743 .unwrap(),
744 status_before
745 );
746 assert!(matches!(
747 owned.fault_out_of_range.owned_fault,
748 Some(OwnedRangeFault::UnderRange)
749 ));
750
751 owned.present_value = 21.0;
752 assert_eq!(
753 owned.evaluate_reliability_internal().unwrap(),
754 ReliabilityEvaluation::Changed {
755 old_reliability: Reliability::UNDER_RANGE,
756 new_reliability: Reliability::OVER_RANGE,
757 }
758 );
759 }
760 }
761}