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::present_value_access::PresentValueAccess;
7use crate::property_metadata::PropertyMetadata;
8
9mod metadata;
10#[cfg(test)]
11mod nonfinite_tests;
12
13pub struct AnalogValueObject {
19 audit_policy: crate::audit::ObjectAuditPolicy,
20 oid: ObjectIdentifier,
21 name: String,
22 description: String,
23 present_value: f32,
24 units: u32,
25 out_of_service: bool,
26 status_flags: StatusFlags,
27 access: PresentValueAccess,
28 priority_array: [Option<f32>; 16],
30 relinquish_default: f32,
31 cov_increment: f32,
35 event_detector: OutOfRangeDetector,
36 event_detection_enable: bool,
39 reliability: Reliability,
41 reliability_before_out_of_service: Option<Reliability>,
42 reliability_inhibit: common::ReliabilityInhibitState,
43 fault_out_of_range: FaultOutOfRangeState,
44 min_pres_value: Option<f32>,
45 max_pres_value: Option<f32>,
46 pub(crate) event_history: EventHistory,
47 value_source: crate::command_source::ValueSourceTracking,
49}
50
51impl AnalogValueObject {
52 pub fn set_audit_policy(&mut self, policy: crate::audit::ObjectAuditPolicy) {
56 self.audit_policy = policy;
57 }
58
59 pub fn new(instance: u32, name: impl Into<String>, units: u32) -> Result<Self, Error> {
61 Self::with_access(instance, name, units, PresentValueAccess::Commandable)
62 }
63
64 pub fn with_access(
66 instance: u32,
67 name: impl Into<String>,
68 units: u32,
69 access: PresentValueAccess,
70 ) -> Result<Self, Error> {
71 let oid = ObjectIdentifier::new(ObjectType::ANALOG_VALUE, instance)?;
72 Ok(Self {
73 audit_policy: crate::audit::ObjectAuditPolicy::default(),
74 oid,
75 name: name.into(),
76 description: String::new(),
77 present_value: 0.0,
78 units,
79 out_of_service: false,
80 status_flags: StatusFlags::empty(),
81 access,
82 priority_array: [None; 16],
83 relinquish_default: 0.0,
84 cov_increment: 0.0,
85 event_detector: OutOfRangeDetector::default(),
86 event_detection_enable: true,
87 reliability: Reliability::NO_FAULT_DETECTED,
88 reliability_before_out_of_service: None,
89 reliability_inhibit: common::ReliabilityInhibitState::default(),
90 fault_out_of_range: FaultOutOfRangeState::default(),
91 min_pres_value: None,
92 max_pres_value: None,
93 event_history: EventHistory::default(),
94 value_source: crate::command_source::ValueSourceTracking::default(),
95 })
96 }
97
98 pub fn set_description(&mut self, desc: impl Into<String>) {
100 self.description = desc.into();
101 }
102
103 pub fn set_min_pres_value(&mut self, value: f32) {
105 self.min_pres_value = Some(value);
106 }
107
108 pub fn set_max_pres_value(&mut self, value: f32) {
110 self.max_pres_value = Some(value);
111 }
112
113 pub fn configure_fault_out_of_range(&mut self, low: f32, high: f32) -> Result<(), Error> {
119 self.fault_out_of_range.configure(low, high)
120 }
121
122 fn recalculate_present_value(&mut self) {
124 self.present_value =
125 common::recalculate_from_priority_array(&self.priority_array, self.relinquish_default);
126 }
127
128 pub fn set_relinquish_default(&mut self, value: f32) -> Result<(), Error> {
134 if self.access != PresentValueAccess::Commandable {
135 return Err(common::unknown_property_error());
136 }
137 common::reject_non_finite(value)?;
138 self.relinquish_default = value;
139 self.recalculate_present_value();
140 Ok(())
141 }
142
143 fn checked_present_value(value: PropertyValue) -> Result<f32, Error> {
144 let PropertyValue::Real(v) = value else {
145 return Err(common::invalid_data_type_error());
146 };
147 if !v.is_finite() {
148 return Err(common::value_out_of_range_error());
149 }
150 Ok(v)
151 }
152}
153
154impl BACnetObject for AnalogValueObject {
155 fn audit_policy_authority_internal(
156 &mut self,
157 ) -> Option<crate::audit::AuditPolicyAuthority<'_>> {
158 Some(crate::audit::AuditPolicyAuthority::new(
159 &mut self.audit_policy,
160 ))
161 }
162
163 fn audit_object_policy_internal(&self) -> crate::audit::ObjectAuditPolicy {
164 self.audit_policy
165 }
166
167 fn object_identifier(&self) -> ObjectIdentifier {
168 self.oid
169 }
170
171 fn object_name(&self) -> &str {
172 &self.name
173 }
174
175 fn read_property(
176 &self,
177 property: PropertyIdentifier,
178 array_index: Option<u32>,
179 ) -> Result<PropertyValue, Error> {
180 if metadata::excludes(self, property) {
181 return Err(common::unknown_property_error());
182 }
183 if let Some(result) = self
184 .value_source
185 .read(property, array_index, &self.priority_array)
186 {
187 return result;
188 }
189
190 if let Some(result) = self.audit_policy.read(property, array_index) {
191 return result;
192 }
193 if property == PropertyIdentifier::STATUS_FLAGS {
194 return Ok(common::compute_status_flags(
195 self.status_flags,
196 self.reliability,
197 self.out_of_service,
198 self.event_detector.event_state,
199 ));
200 }
201 if let Some(value) = self.reliability_inhibit.read(property) {
202 return Ok(value);
203 }
204 if let Some(result) = read_common_properties!(self, property, array_index) {
205 return result;
206 }
207 if let Some(result) = read_analog_event_properties!(self, property) {
208 return result;
209 }
210 if let Some(result) = self.event_history.read(property, array_index) {
211 return result;
212 }
213 if let Some(result) = read_generic_event_properties!(self, property) {
214 return result;
215 }
216 if property == PropertyIdentifier::EVENT_DETECTION_ENABLE {
217 return Ok(PropertyValue::Boolean(self.event_detection_enable));
218 }
219 if let Some(value) = self.fault_out_of_range.read_limit(property) {
220 return Ok(value);
221 }
222 match property {
223 p if p == PropertyIdentifier::OBJECT_TYPE => {
224 Ok(PropertyValue::Enumerated(ObjectType::ANALOG_VALUE.to_raw()))
225 }
226 p if p == PropertyIdentifier::PRESENT_VALUE => {
227 Ok(PropertyValue::Real(self.present_value))
228 }
229 p if p == PropertyIdentifier::UNITS => Ok(PropertyValue::Enumerated(self.units)),
230 p if p == PropertyIdentifier::PRIORITY_ARRAY => {
231 common::read_priority_array!(self, array_index, PropertyValue::Real)
232 }
233 p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
234 Ok(PropertyValue::Real(self.relinquish_default))
235 }
236 p if p == PropertyIdentifier::CURRENT_COMMAND_PRIORITY => {
237 Ok(common::current_command_priority(&self.priority_array))
238 }
239 p if p == PropertyIdentifier::COV_INCREMENT => {
240 Ok(PropertyValue::Real(self.cov_increment))
241 }
242 p if p == PropertyIdentifier::MIN_PRES_VALUE => match self.min_pres_value {
243 Some(v) => Ok(PropertyValue::Real(v)),
244 None => Err(common::unknown_property_error()),
245 },
246 p if p == PropertyIdentifier::MAX_PRES_VALUE => match self.max_pres_value {
247 Some(v) => Ok(PropertyValue::Real(v)),
248 None => Err(common::unknown_property_error()),
249 },
250 _ => Err(common::unknown_property_error()),
251 }
252 }
253
254 fn write_property_from(
255 &mut self,
256 property: PropertyIdentifier,
257 array_index: Option<u32>,
258 value: PropertyValue,
259 priority: Option<u8>,
260 origin: &crate::command_source::CommandOrigin,
261 ) -> Result<(), Error> {
262 if matches!(
263 property,
264 PropertyIdentifier::PRESENT_VALUE | PropertyIdentifier::VALUE_SOURCE
265 ) && array_index.is_some()
266 {
267 return Err(common::property_is_not_an_array_error());
268 }
269 if property == PropertyIdentifier::VALUE_SOURCE
270 && self.access == PresentValueAccess::Commandable
271 {
272 return self.value_source.correct(value, priority, origin);
273 }
274 if property == PropertyIdentifier::PRESENT_VALUE {
275 return match self.access {
276 PresentValueAccess::Commandable => {
277 crate::command_source::write_sourced_priority!(
278 self,
279 value,
280 priority,
281 origin,
282 Self::checked_present_value
283 )
284 }
285 PresentValueAccess::ReadOnly | PresentValueAccess::Writable => {
286 self.write_property(property, array_index, value, priority)
287 }
288 };
289 }
290 self.write_property(property, array_index, value, priority)
291 }
292
293 fn write_property(
294 &mut self,
295 property: PropertyIdentifier,
296 array_index: Option<u32>,
297 value: PropertyValue,
298 priority: Option<u8>,
299 ) -> Result<(), Error> {
300 if metadata::excludes(self, property) {
301 return Err(common::unknown_property_error());
302 }
303 if let Some(result) = self
304 .audit_policy
305 .write(property, array_index, &value, priority)
306 {
307 return result;
308 }
309
310 if property == PropertyIdentifier::PRESENT_VALUE {
311 if self.access == PresentValueAccess::Commandable {
312 return Err(common::write_access_denied_error());
313 }
314 if array_index.is_some() {
315 return Err(common::property_is_not_an_array_error());
316 }
317 if self.access == PresentValueAccess::ReadOnly && !self.out_of_service {
318 return Err(common::write_access_denied_error());
319 }
320 if value == PropertyValue::Null {
322 return Ok(());
323 }
324 self.present_value = Self::checked_present_value(value)?;
325 return Ok(());
326 }
327 if property == PropertyIdentifier::VALUE_SOURCE {
328 return Err(common::write_access_denied_error());
329 }
330 if let Some(result) = self.reliability_inhibit.write_inhibit(
331 &mut self.reliability,
332 self.out_of_service,
333 property,
334 &value,
335 ) {
336 return result;
337 }
338 if let Some(result) = self.reliability_inhibit.write_out_of_service(
339 &mut self.out_of_service,
340 &mut self.reliability,
341 &mut self.reliability_before_out_of_service,
342 property,
343 &value,
344 ) {
345 return result.map(|_| ());
346 }
347 if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
348 return result;
349 }
350 if let Some(result) = common::write_description(&mut self.description, property, &value) {
351 return result;
352 }
353 if let Some(result) = self.reliability_inhibit.write_client_reliability(
358 self.out_of_service,
359 &mut self.reliability,
360 property,
361 &value,
362 ) {
363 return result;
364 }
365 if property == PropertyIdentifier::RELINQUISH_DEFAULT {
366 if let PropertyValue::Real(v) = value {
367 return self.set_relinquish_default(v);
368 }
369 return Err(common::invalid_data_type_error());
370 }
371 if let Some(result) = common::write_cov_increment(&mut self.cov_increment, property, &value)
372 {
373 return result;
374 }
375 if property == PropertyIdentifier::EVENT_DETECTION_ENABLE {
376 if let PropertyValue::Boolean(v) = value {
377 self.event_detection_enable = v;
378 if !v {
379 self.event_detector.event_state = bacnet_types::enums::EventState::NORMAL;
380 self.event_detector.acked_transitions =
381 bacnet_types::bitstring::EventTransitionBits::all();
382 self.event_detector.pending = None;
383 self.event_detector.fault_reliability = None;
384 self.event_history.reset();
385 }
386 return Ok(());
387 }
388 return Err(common::invalid_data_type_error());
389 }
390 if let Some(result) = write_analog_event_properties!(self, property, value) {
391 return result;
392 }
393 if let Some(result) =
395 self.event_history
396 .write(property, array_index, &value, self.event_detection_enable)
397 {
398 return result;
399 }
400 if let Some(result) = write_generic_event_properties!(self, property, value) {
401 return result;
402 }
403 Err(crate::common::unhandled_write_error(
404 self.property_metadata().as_ref(),
405 property,
406 array_index,
407 ))
408 }
409
410 fn property_metadata(&self) -> Cow<'_, [PropertyMetadata]> {
411 metadata::for_object(self)
412 }
413
414 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
415 crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
416 }
417
418 fn supports_cov(&self) -> bool {
419 true
420 }
421
422 fn cov_increment(&self) -> Option<f64> {
423 Some(f64::from(self.cov_increment))
424 }
425
426 crate::event::impl_builtin_intrinsic_reporting!(
427 event_detector,
428 event_history,
429 [present_value],
430 reliability,
431 event_detection_enable,
432 OutOfRangeDetector::ALGORITHM
433 );
434
435 fn acknowledge_alarm(
436 &mut self,
437 transition_bit: bacnet_types::bitstring::EventTransitionBits,
438 ) -> Result<(), bacnet_types::error::Error> {
439 self.event_detector.acked_transitions |=
440 transition_bit & bacnet_types::bitstring::EventTransitionBits::all();
441 Ok(())
442 }
443
444 fn acknowledge_alarm_correlated_internal(
445 &mut self,
446 event_state: EventState,
447 timestamp: &BACnetTimeStamp,
448 ) -> Result<(), Error> {
449 if !self.event_detection_enable {
450 return Err(Error::Protocol {
451 class: ErrorClass::OBJECT.to_raw() as u32,
452 code: ErrorCode::NO_ALARM_CONFIGURED.to_raw() as u32,
453 });
454 }
455 self.event_history.acknowledge_correlated(
456 &mut self.event_detector.acked_transitions,
457 event_state,
458 timestamp,
459 )
460 }
461
462 fn set_reliability_internal(&mut self, reliability: Reliability) -> Result<(), Error> {
463 if self.out_of_service || self.reliability_inhibit.enabled() {
464 return Err(common::write_access_denied_error());
465 }
466 if !common::is_reliability_value_valid(reliability) {
467 return Err(common::value_out_of_range_error());
468 }
469 self.reliability = reliability;
470 self.fault_out_of_range.clear_ownership();
471 Ok(())
472 }
473
474 fn set_present_value_internal(&mut self, value: PropertyValue) -> Result<(), Error> {
475 match self.access {
476 PresentValueAccess::Commandable => {
477 Err(common::optional_functionality_not_supported_error())
478 }
479 _ if self.out_of_service => Err(common::write_access_denied_error()),
480 PresentValueAccess::ReadOnly | PresentValueAccess::Writable => {
481 self.present_value = Self::checked_present_value(value)?;
482 Ok(())
483 }
484 }
485 }
486
487 fn evaluate_reliability_internal(&mut self) -> Result<ReliabilityEvaluation, Error> {
488 if self.out_of_service || self.reliability_inhibit.enabled() {
489 return Ok(ReliabilityEvaluation::Unchanged);
490 }
491 self.fault_out_of_range
492 .evaluate(self.present_value, &mut self.reliability)
493 }
494
495 fn reliability_evaluation_inhibited_internal(&self) -> bool {
496 self.reliability_inhibit.enabled()
497 }
498
499 fn is_createable(&self) -> bool {
502 false
503 }
504}
505
506#[cfg(test)]
507mod detection_enable_reset_tests {
508 use super::*;
509
510 #[test]
513 fn av_disabling_detection_resets_event_history() {
514 let mut av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
515 assert_eq!(
516 av.read_property(PropertyIdentifier::EVENT_DETECTION_ENABLE, None)
517 .unwrap(),
518 PropertyValue::Boolean(true)
519 );
520 av.event_detector.event_state = bacnet_types::enums::EventState::HIGH_LIMIT;
521 av.event_detector.acked_transitions = bacnet_types::bitstring::EventTransitionBits::empty();
522 av.event_detector.pending = Some(crate::event::PendingTransition {
523 state: bacnet_types::enums::EventState::HIGH_LIMIT,
524 remaining: 2,
525 });
526 av.event_detector.fault_reliability = Some(bacnet_types::enums::Reliability::NO_SENSOR);
527 av.event_history.time_stamps = [
528 BACnetTimeStamp::SequenceNumber(1),
529 BACnetTimeStamp::SequenceNumber(2),
530 BACnetTimeStamp::SequenceNumber(3),
531 ];
532 av.event_history.message_texts = ["offnormal".into(), "fault".into(), "normal".into()];
533
534 av.write_property(
535 PropertyIdentifier::EVENT_DETECTION_ENABLE,
536 None,
537 PropertyValue::Boolean(false),
538 None,
539 )
540 .unwrap();
541
542 assert_eq!(
543 av.read_property(PropertyIdentifier::EVENT_DETECTION_ENABLE, None)
544 .unwrap(),
545 PropertyValue::Boolean(false)
546 );
547 assert_eq!(
548 av.event_detector.event_state,
549 bacnet_types::enums::EventState::NORMAL
550 );
551 assert_eq!(
552 av.event_detector.acked_transitions,
553 bacnet_types::bitstring::EventTransitionBits::all()
554 );
555 assert!(av.event_detector.pending.is_none());
556 assert!(av.event_detector.fault_reliability.is_none());
557 assert_eq!(
558 av.event_history.time_stamps,
559 [
560 BACnetTimeStamp::SequenceNumber(0),
561 BACnetTimeStamp::SequenceNumber(0),
562 BACnetTimeStamp::SequenceNumber(0),
563 ]
564 );
565 assert_eq!(
566 av.event_history.message_texts,
567 [String::new(), String::new(), String::new()]
568 );
569 }
570}
571
572#[cfg(test)]
573mod fault_out_of_range_non_finite_tests {
574 use super::*;
575 use bacnet_types::enums::{ErrorClass, ErrorCode, Reliability};
576
577 fn assert_value_out_of_range(result: Result<ReliabilityEvaluation, Error>) {
578 assert!(matches!(
579 result,
580 Err(Error::Protocol { class, code })
581 if class == ErrorClass::PROPERTY.to_raw() as u32
582 && code == ErrorCode::VALUE_OUT_OF_RANGE.to_raw() as u32
583 ));
584 }
585
586 #[test]
587 fn av_non_finite_monitored_values_preserve_reliability_status_and_ownership() {
588 for non_finite in [f32::NAN, f32::INFINITY, f32::NEG_INFINITY] {
589 let mut normal = AnalogValueObject::new(1, "AV-normal", 62).unwrap();
590 normal.configure_fault_out_of_range(10.0, 20.0).unwrap();
591 let status_before = normal
592 .read_property(PropertyIdentifier::STATUS_FLAGS, None)
593 .unwrap();
594 normal.present_value = non_finite;
595
596 assert_value_out_of_range(normal.evaluate_reliability_internal());
597 assert_eq!(normal.present_value.to_bits(), non_finite.to_bits());
598 assert_eq!(normal.reliability, Reliability::NO_FAULT_DETECTED);
599 assert_eq!(
600 normal
601 .read_property(PropertyIdentifier::STATUS_FLAGS, None)
602 .unwrap(),
603 status_before
604 );
605 assert!(normal.fault_out_of_range.owned_fault.is_none());
606
607 normal.present_value = 9.0;
608 assert_eq!(
609 normal.evaluate_reliability_internal().unwrap(),
610 ReliabilityEvaluation::Changed {
611 old_reliability: Reliability::NO_FAULT_DETECTED,
612 new_reliability: Reliability::UNDER_RANGE,
613 }
614 );
615
616 let mut owned = AnalogValueObject::new(2, "AV-owned", 62).unwrap();
617 owned.configure_fault_out_of_range(10.0, 20.0).unwrap();
618 owned.present_value = 9.0;
619 owned.evaluate_reliability_internal().unwrap();
620 let status_before = owned
621 .read_property(PropertyIdentifier::STATUS_FLAGS, None)
622 .unwrap();
623 owned.present_value = non_finite;
624
625 assert_value_out_of_range(owned.evaluate_reliability_internal());
626 assert_eq!(owned.present_value.to_bits(), non_finite.to_bits());
627 assert_eq!(owned.reliability, Reliability::UNDER_RANGE);
628 assert_eq!(
629 owned
630 .read_property(PropertyIdentifier::STATUS_FLAGS, None)
631 .unwrap(),
632 status_before
633 );
634 assert!(matches!(
635 owned.fault_out_of_range.owned_fault,
636 Some(OwnedRangeFault::UnderRange)
637 ));
638
639 owned.present_value = 21.0;
640 assert_eq!(
641 owned.evaluate_reliability_internal().unwrap(),
642 ReliabilityEvaluation::Changed {
643 old_reliability: Reliability::UNDER_RANGE,
644 new_reliability: Reliability::OVER_RANGE,
645 }
646 );
647 }
648 }
649}