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