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::property_metadata::PropertyMetadata;
8use std::sync::Arc;
9
10mod metadata;
11
12pub struct AnalogInputObject {
18 profile: ProfileState,
19 oid: ObjectIdentifier,
20 name: String,
21 description: String,
22 present_value: f32,
23 units: EngineeringUnits,
24 out_of_service: bool,
25 status_flags: StatusFlags,
26 cov_increment: f32,
30 event_detector: OutOfRangeDetector,
31 event_detection_enable: bool,
34 reliability: Reliability,
36 reliability_before_out_of_service: Option<Reliability>,
37 reliability_inhibit: common::ReliabilityInhibitState,
38 fault_out_of_range: FaultOutOfRangeState,
39 min_pres_value: Option<f32>,
41 max_pres_value: Option<f32>,
43 pub(crate) event_history: EventHistory,
44}
45
46impl AnalogInputObject {
47 pub fn new(
49 instance: u32,
50 name: impl Into<String>,
51 units: EngineeringUnits,
52 ) -> Result<Self, Error> {
53 let _oid = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, instance)?;
54 Ok(Self {
55 profile: ProfileState::default(),
56 oid: _oid,
57 name: name.into(),
58 description: String::new(),
59 present_value: 0.0,
60 units,
61 out_of_service: false,
62 status_flags: StatusFlags::empty(),
63 cov_increment: 0.0,
64 event_detector: OutOfRangeDetector::default(),
65 event_detection_enable: true,
66 reliability: Reliability::NO_FAULT_DETECTED,
67 reliability_before_out_of_service: None,
68 reliability_inhibit: common::ReliabilityInhibitState::default(),
69 fault_out_of_range: FaultOutOfRangeState::default(),
70 min_pres_value: None,
71 max_pres_value: None,
72 event_history: EventHistory::default(),
73 })
74 }
75
76 pub fn with_tags_persistence(
80 instance: u32,
81 name: impl Into<String>,
82 units: EngineeringUnits,
83 persistence: Arc<dyn TagsPersistence>,
84 ) -> Result<Self, Error> {
85 let mut object = Self::new(instance, name, units)?;
86 object.profile = ProfileState::persistent(object.oid, persistence)?;
87 Ok(object)
88 }
89
90 pub fn set_profile(&mut self, profile: ObjectProfile) -> Result<(), Error> {
95 self.profile.provision(profile)
96 }
97
98 pub fn wait_for_tag_saves(&self) {
101 self.profile.wait_for_saves();
102 }
103
104 pub fn set_present_value(&mut self, value: f32) {
110 debug_assert!(
111 value.is_finite(),
112 "set_present_value called with non-finite value"
113 );
114 self.present_value = value;
115 }
116
117 fn apply_present_value(&mut self, value: PropertyValue) -> Result<(), Error> {
122 let PropertyValue::Real(v) = value else {
123 return Err(common::invalid_data_type_error());
124 };
125 if !v.is_finite() {
126 return Err(common::value_out_of_range_error());
127 }
128 self.present_value = v;
129 Ok(())
130 }
131
132 pub fn set_description(&mut self, desc: impl Into<String>) {
134 self.description = desc.into();
135 }
136
137 pub fn set_min_pres_value(&mut self, value: f32) {
139 self.min_pres_value = Some(value);
140 }
141
142 pub fn set_max_pres_value(&mut self, value: f32) {
144 self.max_pres_value = Some(value);
145 }
146
147 pub fn configure_fault_out_of_range(&mut self, low: f32, high: f32) -> Result<(), Error> {
153 self.fault_out_of_range.configure(low, high)
154 }
155}
156
157impl BACnetObject for AnalogInputObject {
158 fn object_identifier(&self) -> ObjectIdentifier {
159 self.oid
160 }
161
162 fn object_name(&self) -> &str {
163 &self.name
164 }
165
166 fn read_property(
167 &self,
168 property: PropertyIdentifier,
169 array_index: Option<u32>,
170 ) -> Result<PropertyValue, Error> {
171 if property == PropertyIdentifier::STATUS_FLAGS {
173 return Ok(common::compute_status_flags(
174 self.status_flags,
175 self.reliability,
176 self.out_of_service,
177 self.event_detector.event_state,
178 ));
179 }
180 if let Some(value) = self.reliability_inhibit.read(property) {
181 return Ok(value);
182 }
183 if let Some(result) = read_common_properties!(self, property, array_index) {
184 return result;
185 }
186 if let Some(result) = read_analog_event_properties!(self, property) {
187 return result;
188 }
189 if let Some(result) = self.event_history.read(property, array_index) {
190 return result;
191 }
192 if let Some(result) = read_generic_event_properties!(self, property) {
193 return result;
194 }
195 if property == PropertyIdentifier::EVENT_DETECTION_ENABLE {
196 return Ok(PropertyValue::Boolean(self.event_detection_enable));
197 }
198 if let Some(value) = self.fault_out_of_range.read_limit(property) {
199 return Ok(value);
200 }
201 if let Some(result) = self.profile.read(property, array_index) {
202 return result;
203 }
204 match property {
205 p if p == PropertyIdentifier::OBJECT_TYPE => {
206 Ok(PropertyValue::Enumerated(ObjectType::ANALOG_INPUT.to_raw()))
207 }
208 p if p == PropertyIdentifier::PRESENT_VALUE => {
209 Ok(PropertyValue::Real(self.present_value))
210 }
211 p if p == PropertyIdentifier::UNITS => {
212 Ok(PropertyValue::Enumerated(self.units.to_raw()))
213 }
214 p if p == PropertyIdentifier::COV_INCREMENT => {
215 Ok(PropertyValue::Real(self.cov_increment))
216 }
217 p if p == PropertyIdentifier::MIN_PRES_VALUE => match self.min_pres_value {
218 Some(v) => Ok(PropertyValue::Real(v)),
219 None => Err(common::unknown_property_error()),
220 },
221 p if p == PropertyIdentifier::MAX_PRES_VALUE => match self.max_pres_value {
222 Some(v) => Ok(PropertyValue::Real(v)),
223 None => Err(common::unknown_property_error()),
224 },
225 _ => Err(common::unknown_property_error()),
226 }
227 }
228
229 fn write_property(
230 &mut self,
231 property: PropertyIdentifier,
232 array_index: Option<u32>,
233 value: PropertyValue,
234 _priority: Option<u8>,
235 ) -> Result<(), Error> {
236 if property == PropertyIdentifier::PRESENT_VALUE {
238 if !self.out_of_service {
239 return Err(common::write_access_denied_error());
240 }
241 return self.apply_present_value(value);
242 }
243 if let Some(result) = self.profile.write(property, array_index, &value) {
244 return result;
245 }
246 if let Some(result) = self.reliability_inhibit.write_inhibit(
247 &mut self.reliability,
248 self.out_of_service,
249 property,
250 &value,
251 ) {
252 return result;
253 }
254 if let Some(result) = self.reliability_inhibit.write_out_of_service(
255 &mut self.out_of_service,
256 &mut self.reliability,
257 &mut self.reliability_before_out_of_service,
258 property,
259 &value,
260 ) {
261 return result.map(|_| ());
262 }
263 if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
264 return result;
265 }
266 if let Some(result) = common::write_description(&mut self.description, property, &value) {
267 return result;
268 }
269 if let Some(result) = self.reliability_inhibit.write_client_reliability(
270 self.out_of_service,
271 &mut self.reliability,
272 property,
273 &value,
274 ) {
275 return result;
276 }
277 if let Some(result) = common::write_cov_increment(&mut self.cov_increment, property, &value)
278 {
279 return result;
280 }
281 if property == PropertyIdentifier::EVENT_DETECTION_ENABLE {
282 if let PropertyValue::Boolean(v) = value {
283 self.event_detection_enable = v;
284 if !v {
285 self.event_detector.event_state = bacnet_types::enums::EventState::NORMAL;
286 self.event_detector.acked_transitions =
287 bacnet_types::bitstring::EventTransitionBits::all();
288 self.event_detector.pending = None;
289 self.event_detector.fault_reliability = None;
290 self.event_history.reset();
291 }
292 return Ok(());
293 }
294 return Err(common::invalid_data_type_error());
295 }
296 if let Some(result) = write_analog_event_properties!(self, property, value) {
297 return result;
298 }
299 if let Some(result) =
301 self.event_history
302 .write(property, array_index, &value, self.event_detection_enable)
303 {
304 return result;
305 }
306 if let Some(result) = write_generic_event_properties!(self, property, value) {
307 return result;
308 }
309 Err(crate::common::unhandled_write_error(
310 self.property_metadata().as_ref(),
311 property,
312 array_index,
313 ))
314 }
315
316 fn durable_writes_internal(&mut self) -> Option<&mut dyn crate::durable::DurableWrites> {
317 self.profile.capability()
318 }
319
320 fn advance_monotonic_time_internal(&mut self, now: std::time::Duration) -> bool {
321 self.profile.expire(now);
322 false
323 }
324
325 fn property_metadata(&self) -> Cow<'_, [PropertyMetadata]> {
326 metadata::for_object(self)
327 }
328
329 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
330 crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
331 }
332
333 fn supports_cov(&self) -> bool {
334 true
335 }
336
337 fn cov_increment(&self) -> Option<f64> {
338 Some(f64::from(self.cov_increment))
339 }
340
341 crate::event::impl_builtin_intrinsic_reporting!(
342 event_detector,
343 event_history,
344 [present_value],
345 reliability,
346 event_detection_enable,
347 OutOfRangeDetector::ALGORITHM
348 );
349
350 fn acknowledge_alarm(
351 &mut self,
352 transition_bit: bacnet_types::bitstring::EventTransitionBits,
353 ) -> Result<(), bacnet_types::error::Error> {
354 self.event_detector.acked_transitions |=
355 transition_bit & bacnet_types::bitstring::EventTransitionBits::all();
356 Ok(())
357 }
358
359 fn acknowledge_alarm_correlated_internal(
360 &mut self,
361 event_state: EventState,
362 timestamp: &BACnetTimeStamp,
363 ) -> Result<(), Error> {
364 if !self.event_detection_enable {
365 return Err(Error::Protocol {
366 class: ErrorClass::OBJECT,
367 code: ErrorCode::NO_ALARM_CONFIGURED,
368 });
369 }
370 self.event_history.acknowledge_correlated(
371 &mut self.event_detector.acked_transitions,
372 event_state,
373 timestamp,
374 )
375 }
376
377 fn set_reliability_internal(&mut self, reliability: Reliability) -> Result<(), Error> {
378 if self.out_of_service || self.reliability_inhibit.enabled() {
379 return Err(common::write_access_denied_error());
380 }
381 if !common::is_reliability_value_valid(reliability) {
382 return Err(common::value_out_of_range_error());
383 }
384 self.reliability = reliability;
385 self.fault_out_of_range.clear_ownership();
386 Ok(())
387 }
388
389 fn set_present_value_internal(&mut self, value: PropertyValue) -> Result<(), Error> {
390 if self.out_of_service {
392 return Err(common::write_access_denied_error());
393 }
394 self.apply_present_value(value)
395 }
396
397 fn evaluate_reliability_internal(&mut self) -> Result<ReliabilityEvaluation, Error> {
398 if self.out_of_service || self.reliability_inhibit.enabled() {
399 return Ok(ReliabilityEvaluation::Unchanged);
400 }
401 self.fault_out_of_range
402 .evaluate(self.present_value, &mut self.reliability)
403 }
404
405 fn reliability_evaluation_inhibited_internal(&self) -> bool {
406 self.reliability_inhibit.enabled()
407 }
408
409 fn is_createable(&self) -> bool {
410 true
411 }
412
413 fn creation_only_properties(&self) -> &'static [PropertyIdentifier] {
414 super::CREATION_ONLY
415 }
416
417 fn initialize_property(
418 &mut self,
419 property: PropertyIdentifier,
420 value: PropertyValue,
421 ) -> Result<(), Error> {
422 super::initialize_units(&mut self.units, property, value)
423 }
424}
425
426#[cfg(test)]
427mod detection_enable_reset_tests {
428 use super::*;
429
430 #[test]
431 fn built_in_intrinsic_transition_is_a_proposal_until_committed() {
432 use crate::event::EventTransitionCommit;
433
434 let mut ai = AnalogInputObject::new(1, "AI-1", EngineeringUnits::DEGREES_CELSIUS).unwrap();
435 ai.event_detector.high_limit = 80.0;
436 ai.event_detector.limit_enable = bacnet_types::bitstring::LimitEnable::all();
437 ai.event_detector.event_enable = bacnet_types::bitstring::EventTransitionBits::all();
438 ai.set_present_value(81.0);
439
440 let outcome = ai
441 .evaluate_intrinsic_reporting()
442 .expect("out-of-range value should propose TO_OFFNORMAL");
443 assert_eq!(
444 outcome.change.to,
445 bacnet_types::enums::EventState::HIGH_LIMIT
446 );
447 assert_eq!(
448 ai.event_detector.event_state,
449 bacnet_types::enums::EventState::NORMAL,
450 "built-in evaluation must not confirm its own proposal"
451 );
452 assert_eq!(
453 ai.event_detector.acked_transitions,
454 bacnet_types::bitstring::EventTransitionBits::all()
455 );
456 assert!(ai.event_detector.pending.is_none());
457
458 ai.commit_event_transition_internal(EventTransitionCommit {
459 coordinate: outcome.change.transition(),
460 change: outcome.change,
461 ack_required: true,
462 timestamp: BACnetTimeStamp::SequenceNumber(41),
463 message_text: None,
464 })
465 .expect("built-in object should lend all transition state to the kernel");
466
467 assert_eq!(
468 ai.event_detector.event_state,
469 bacnet_types::enums::EventState::HIGH_LIMIT
470 );
471 assert_eq!(
472 ai.event_detector.acked_transitions,
473 bacnet_types::bitstring::EventTransitionBits::TO_FAULT
474 | bacnet_types::bitstring::EventTransitionBits::TO_NORMAL
475 );
476 assert_eq!(
477 ai.event_history.time_stamps[0],
478 BACnetTimeStamp::SequenceNumber(41)
479 );
480 }
481
482 #[test]
483 fn rejected_delayed_and_fault_reindication_proposals_remain_retryable() {
484 use crate::event::{
485 EventStateChange, EventTransition, EventTransitionCommit, EventTransitionCommitError,
486 };
487 use bacnet_types::enums::{EngineeringUnits, EventState, Reliability};
488
489 let mut delayed =
490 AnalogInputObject::new(1, "AI-delayed", EngineeringUnits::DEGREES_CELSIUS).unwrap();
491 delayed.event_detector.high_limit = 80.0;
492 delayed.event_detector.limit_enable = bacnet_types::bitstring::LimitEnable::all();
493 delayed.event_detector.time_delay = 1;
494 delayed.set_present_value(81.0);
495 assert_eq!(delayed.evaluate_intrinsic_reporting(), None);
496 let proposal = delayed.tick_intrinsic_reporting().unwrap();
497 assert_eq!(
498 delayed.event_detector.pending.as_ref().unwrap().remaining,
499 1
500 );
501
502 let stale = EventTransitionCommit {
503 change: EventStateChange {
504 from: EventState::FAULT,
505 to: proposal.change.to,
506 },
507 coordinate: proposal.change.transition(),
508 ack_required: false,
509 timestamp: BACnetTimeStamp::SequenceNumber(9),
510 message_text: None,
511 };
512 assert_eq!(
513 delayed.commit_event_transition_internal(stale),
514 Err(EventTransitionCommitError::CurrentStateMismatch {
515 expected: EventState::FAULT,
516 actual: EventState::NORMAL,
517 })
518 );
519 assert_eq!(
520 delayed.event_detector.pending.as_ref().unwrap().remaining,
521 1
522 );
523 assert_eq!(delayed.tick_intrinsic_reporting(), Some(proposal));
524
525 let mut faulted =
526 AnalogInputObject::new(2, "AI-fault", EngineeringUnits::DEGREES_CELSIUS).unwrap();
527 faulted.reliability = Reliability::OVER_RANGE;
528 let entry = faulted.evaluate_intrinsic_reporting().unwrap();
529 crate::event::commit_test_proposal(&mut faulted, entry);
530 faulted.reliability = Reliability::NO_SENSOR;
531 let reindication = faulted.evaluate_intrinsic_reporting().unwrap();
532 assert_eq!(
533 reindication.change,
534 EventStateChange {
535 from: EventState::FAULT,
536 to: EventState::FAULT,
537 }
538 );
539 assert_eq!(
540 faulted.commit_event_transition_internal(EventTransitionCommit {
541 change: reindication.change.clone(),
542 coordinate: EventTransition::ToNormal,
543 ack_required: false,
544 timestamp: BACnetTimeStamp::SequenceNumber(10),
545 message_text: None,
546 }),
547 Err(EventTransitionCommitError::CoordinateTargetMismatch {
548 coordinate: EventTransition::ToNormal,
549 target: EventState::FAULT,
550 })
551 );
552 assert_eq!(
553 faulted.event_detector.fault_reliability,
554 Some(Reliability::OVER_RANGE)
555 );
556 assert_eq!(faulted.evaluate_intrinsic_reporting(), Some(reindication));
557 }
558
559 #[test]
562 fn ai_disabling_detection_resets_event_history() {
563 let mut ai = AnalogInputObject::new(1, "AI-1", EngineeringUnits::DEGREES_CELSIUS).unwrap();
564 assert_eq!(
565 ai.read_property(PropertyIdentifier::EVENT_DETECTION_ENABLE, None)
566 .unwrap(),
567 PropertyValue::Boolean(true)
568 );
569 ai.event_detector.event_state = bacnet_types::enums::EventState::HIGH_LIMIT;
570 ai.event_detector.acked_transitions = bacnet_types::bitstring::EventTransitionBits::empty();
571 ai.event_detector.pending = Some(crate::event::PendingTransition {
572 state: bacnet_types::enums::EventState::HIGH_LIMIT,
573 remaining: 2,
574 });
575 ai.event_detector.fault_reliability = Some(bacnet_types::enums::Reliability::NO_SENSOR);
576 ai.event_history.time_stamps = [
577 BACnetTimeStamp::SequenceNumber(1),
578 BACnetTimeStamp::SequenceNumber(2),
579 BACnetTimeStamp::SequenceNumber(3),
580 ];
581 ai.event_history.message_texts = ["offnormal".into(), "fault".into(), "normal".into()];
582
583 ai.write_property(
584 PropertyIdentifier::EVENT_DETECTION_ENABLE,
585 None,
586 PropertyValue::Boolean(false),
587 None,
588 )
589 .unwrap();
590
591 assert_eq!(
592 ai.read_property(PropertyIdentifier::EVENT_DETECTION_ENABLE, None)
593 .unwrap(),
594 PropertyValue::Boolean(false)
595 );
596 assert_eq!(
597 ai.event_detector.event_state,
598 bacnet_types::enums::EventState::NORMAL
599 );
600 assert_eq!(
601 ai.event_detector.acked_transitions,
602 bacnet_types::bitstring::EventTransitionBits::all()
603 );
604 assert!(ai.event_detector.pending.is_none());
605 assert!(ai.event_detector.fault_reliability.is_none());
606 assert_eq!(
607 ai.event_history.time_stamps,
608 [
609 BACnetTimeStamp::SequenceNumber(0),
610 BACnetTimeStamp::SequenceNumber(0),
611 BACnetTimeStamp::SequenceNumber(0),
612 ]
613 );
614 assert_eq!(ai.event_history.original_from_states, [None, None, None]);
615 assert_eq!(ai.event_history.original_to_states, [None, None, None]);
616 assert_eq!(
617 ai.event_history.message_texts,
618 [String::new(), String::new(), String::new()]
619 );
620 }
621
622 #[test]
623 fn ai_rejected_detection_write_preserves_hidden_state() {
624 let mut ai = AnalogInputObject::new(1, "AI-1", EngineeringUnits::DEGREES_CELSIUS).unwrap();
625 ai.event_detector.event_state = bacnet_types::enums::EventState::HIGH_LIMIT;
626 ai.event_detector.acked_transitions =
627 bacnet_types::bitstring::EventTransitionBits::TO_FAULT;
628 ai.event_detector.pending = Some(crate::event::PendingTransition {
629 state: bacnet_types::enums::EventState::NORMAL,
630 remaining: 2,
631 });
632 ai.event_detector.fault_reliability = Some(bacnet_types::enums::Reliability::NO_SENSOR);
633 ai.event_history.time_stamps[0] = BACnetTimeStamp::SequenceNumber(7);
634 ai.event_history.original_from_states[0] = Some(EventState::NORMAL);
635 ai.event_history.original_to_states[0] = Some(EventState::HIGH_LIMIT);
636 ai.event_history.message_texts[0] = "offnormal".into();
637 let error = ai
638 .write_property(
639 PropertyIdentifier::EVENT_DETECTION_ENABLE,
640 None,
641 PropertyValue::Unsigned(0),
642 None,
643 )
644 .unwrap_err();
645 assert!(
646 matches!(error, bacnet_types::error::Error::Protocol { class, code }
647 if class == ErrorClass::PROPERTY
648 && code == ErrorCode::INVALID_DATA_TYPE)
649 );
650
651 assert!(ai.event_detection_enable);
652 assert_eq!(
653 ai.event_detector.event_state,
654 bacnet_types::enums::EventState::HIGH_LIMIT
655 );
656 assert_eq!(
657 ai.event_detector.acked_transitions,
658 bacnet_types::bitstring::EventTransitionBits::TO_FAULT
659 );
660 assert_eq!(ai.event_detector.pending.unwrap().remaining, 2);
661 assert_eq!(
662 ai.event_detector.fault_reliability,
663 Some(bacnet_types::enums::Reliability::NO_SENSOR)
664 );
665 assert_eq!(
666 ai.event_history.time_stamps[0],
667 BACnetTimeStamp::SequenceNumber(7)
668 );
669 assert_eq!(
670 ai.event_history.original_from_states[0],
671 Some(EventState::NORMAL)
672 );
673 assert_eq!(
674 ai.event_history.original_to_states[0],
675 Some(EventState::HIGH_LIMIT)
676 );
677 assert_eq!(ai.event_history.message_texts[0], "offnormal");
678 }
679}
680
681#[cfg(test)]
682mod acknowledge_alarm_correlation_tests;
683
684#[cfg(test)]
685mod fault_out_of_range_non_finite_tests {
686 use super::*;
687 use bacnet_types::enums::{ErrorClass, ErrorCode, Reliability};
688
689 fn assert_value_out_of_range(result: Result<ReliabilityEvaluation, Error>) {
690 assert!(matches!(
691 result,
692 Err(Error::Protocol { class, code })
693 if class == ErrorClass::PROPERTY
694 && code == ErrorCode::VALUE_OUT_OF_RANGE
695 ));
696 }
697
698 #[test]
699 fn ai_non_finite_monitored_values_preserve_reliability_status_and_ownership() {
700 for non_finite in [f32::NAN, f32::INFINITY, f32::NEG_INFINITY] {
701 let mut normal =
702 AnalogInputObject::new(1, "AI-normal", EngineeringUnits::DEGREES_CELSIUS).unwrap();
703 normal.configure_fault_out_of_range(10.0, 20.0).unwrap();
704 let status_before = normal
705 .read_property(PropertyIdentifier::STATUS_FLAGS, None)
706 .unwrap();
707 normal.present_value = non_finite;
708
709 assert_value_out_of_range(normal.evaluate_reliability_internal());
710 assert_eq!(normal.present_value.to_bits(), non_finite.to_bits());
711 assert_eq!(normal.reliability, Reliability::NO_FAULT_DETECTED);
712 assert_eq!(
713 normal
714 .read_property(PropertyIdentifier::STATUS_FLAGS, None)
715 .unwrap(),
716 status_before
717 );
718 assert!(normal.fault_out_of_range.owned_fault.is_none());
719
720 normal.present_value = 9.0;
721 assert_eq!(
722 normal.evaluate_reliability_internal().unwrap(),
723 ReliabilityEvaluation::Changed {
724 old_reliability: Reliability::NO_FAULT_DETECTED,
725 new_reliability: Reliability::UNDER_RANGE,
726 }
727 );
728
729 let mut owned =
730 AnalogInputObject::new(2, "AI-owned", EngineeringUnits::DEGREES_CELSIUS).unwrap();
731 owned.configure_fault_out_of_range(10.0, 20.0).unwrap();
732 owned.present_value = 9.0;
733 owned.evaluate_reliability_internal().unwrap();
734 let status_before = owned
735 .read_property(PropertyIdentifier::STATUS_FLAGS, None)
736 .unwrap();
737 owned.present_value = non_finite;
738
739 assert_value_out_of_range(owned.evaluate_reliability_internal());
740 assert_eq!(owned.present_value.to_bits(), non_finite.to_bits());
741 assert_eq!(owned.reliability, Reliability::UNDER_RANGE);
742 assert_eq!(
743 owned
744 .read_property(PropertyIdentifier::STATUS_FLAGS, None)
745 .unwrap(),
746 status_before
747 );
748 assert!(matches!(
749 owned.fault_out_of_range.owned_fault,
750 Some(OwnedRangeFault::UnderRange)
751 ));
752
753 owned.present_value = 21.0;
754 assert_eq!(
755 owned.evaluate_reliability_internal().unwrap(),
756 ReliabilityEvaluation::Changed {
757 old_reliability: Reliability::UNDER_RANGE,
758 new_reliability: Reliability::OVER_RANGE,
759 }
760 );
761 }
762 }
763}