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