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