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 AnalogValueObject {
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 priority_array: [Option<f32>; 16],
22 relinquish_default: f32,
23 cov_increment: f32,
27 event_detector: OutOfRangeDetector,
28 event_detection_enable: bool,
31 reliability: u32,
33 reliability_before_out_of_service: Option<u32>,
34 reliability_inhibit: common::ReliabilityInhibitState,
35 fault_out_of_range: FaultOutOfRangeState,
36 min_pres_value: Option<f32>,
37 max_pres_value: Option<f32>,
38 pub(crate) event_history: EventHistory,
39 value_source: common::ValueSourceTracking,
41}
42
43impl AnalogValueObject {
44 pub fn new(instance: u32, name: impl Into<String>, units: u32) -> Result<Self, Error> {
46 let oid = ObjectIdentifier::new(ObjectType::ANALOG_VALUE, instance)?;
47 Ok(Self {
48 oid,
49 name: name.into(),
50 description: String::new(),
51 present_value: 0.0,
52 units,
53 out_of_service: false,
54 status_flags: StatusFlags::empty(),
55 priority_array: [None; 16],
56 relinquish_default: 0.0,
57 cov_increment: 0.0,
58 event_detector: OutOfRangeDetector::default(),
59 event_detection_enable: true,
60 reliability: 0,
61 reliability_before_out_of_service: None,
62 reliability_inhibit: common::ReliabilityInhibitState::default(),
63 fault_out_of_range: FaultOutOfRangeState::default(),
64 min_pres_value: None,
65 max_pres_value: None,
66 event_history: EventHistory::default(),
67 value_source: common::ValueSourceTracking::default(),
68 })
69 }
70
71 pub fn set_present_value(&mut self, value: f32) {
74 debug_assert!(
75 value.is_finite(),
76 "set_present_value called with non-finite value"
77 );
78 self.present_value = value;
79 }
80
81 pub fn set_description(&mut self, desc: impl Into<String>) {
83 self.description = desc.into();
84 }
85
86 pub fn set_min_pres_value(&mut self, value: f32) {
88 self.min_pres_value = Some(value);
89 }
90
91 pub fn set_max_pres_value(&mut self, value: f32) {
93 self.max_pres_value = Some(value);
94 }
95
96 pub fn configure_fault_out_of_range(&mut self, low: f32, high: f32) -> Result<(), Error> {
102 self.fault_out_of_range.configure(low, high)
103 }
104
105 fn recalculate_present_value(&mut self) {
107 self.present_value =
108 common::recalculate_from_priority_array(&self.priority_array, self.relinquish_default);
109 }
110
111 pub fn set_relinquish_default(&mut self, value: f32) -> Result<(), Error> {
117 common::reject_non_finite(value)?;
118 self.relinquish_default = value;
119 self.recalculate_present_value();
120 Ok(())
121 }
122}
123
124impl BACnetObject for AnalogValueObject {
125 fn object_identifier(&self) -> ObjectIdentifier {
126 self.oid
127 }
128
129 fn object_name(&self) -> &str {
130 &self.name
131 }
132
133 fn read_property(
134 &self,
135 property: PropertyIdentifier,
136 array_index: Option<u32>,
137 ) -> Result<PropertyValue, Error> {
138 if property == PropertyIdentifier::STATUS_FLAGS {
139 return Ok(common::compute_status_flags(
140 self.status_flags,
141 self.reliability,
142 self.out_of_service,
143 self.event_detector.event_state.to_raw(),
144 ));
145 }
146 if let Some(value) = self.reliability_inhibit.read(property) {
147 return Ok(value);
148 }
149 if let Some(result) = read_common_properties!(self, property, array_index) {
150 return result;
151 }
152 if let Some(result) = read_analog_event_properties!(self, property) {
153 return result;
154 }
155 if let Some(result) = self.event_history.read(property, array_index) {
156 return result;
157 }
158 if let Some(result) = read_generic_event_properties!(self, property) {
159 return result;
160 }
161 if property == PropertyIdentifier::EVENT_DETECTION_ENABLE {
162 return Ok(PropertyValue::Boolean(self.event_detection_enable));
163 }
164 if let Some(value) = self.fault_out_of_range.read_limit(property) {
165 return Ok(value);
166 }
167 match property {
168 p if p == PropertyIdentifier::OBJECT_TYPE => {
169 Ok(PropertyValue::Enumerated(ObjectType::ANALOG_VALUE.to_raw()))
170 }
171 p if p == PropertyIdentifier::PRESENT_VALUE => {
172 Ok(PropertyValue::Real(self.present_value))
173 }
174 p if p == PropertyIdentifier::UNITS => Ok(PropertyValue::Enumerated(self.units)),
175 p if p == PropertyIdentifier::PRIORITY_ARRAY => {
176 common::read_priority_array!(self, array_index, PropertyValue::Real)
177 }
178 p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
179 Ok(PropertyValue::Real(self.relinquish_default))
180 }
181 p if p == PropertyIdentifier::CURRENT_COMMAND_PRIORITY => {
182 Ok(common::current_command_priority(&self.priority_array))
183 }
184 p if p == PropertyIdentifier::VALUE_SOURCE => {
185 Ok(self.value_source.value_source.clone())
186 }
187 p if p == PropertyIdentifier::LAST_COMMAND_TIME => Ok(PropertyValue::Unsigned(
188 match self.value_source.last_command_time {
189 BACnetTimeStamp::SequenceNumber(n) => u64::from(n),
190 _ => 0,
191 },
192 )),
193 p if p == PropertyIdentifier::COV_INCREMENT => {
194 Ok(PropertyValue::Real(self.cov_increment))
195 }
196 p if p == PropertyIdentifier::MIN_PRES_VALUE => match self.min_pres_value {
197 Some(v) => Ok(PropertyValue::Real(v)),
198 None => Err(common::unknown_property_error()),
199 },
200 p if p == PropertyIdentifier::MAX_PRES_VALUE => match self.max_pres_value {
201 Some(v) => Ok(PropertyValue::Real(v)),
202 None => Err(common::unknown_property_error()),
203 },
204 _ => Err(common::unknown_property_error()),
205 }
206 }
207
208 fn write_property(
209 &mut self,
210 property: PropertyIdentifier,
211 array_index: Option<u32>,
212 value: PropertyValue,
213 priority: Option<u8>,
214 ) -> Result<(), Error> {
215 common::write_priority_array_direct!(self, property, array_index, value, |v| {
216 if let PropertyValue::Real(f) = v {
217 if !f.is_finite() {
218 return Err(common::value_out_of_range_error());
219 }
220 Ok(f)
221 } else {
222 Err(common::invalid_data_type_error())
223 }
224 });
225 if property == PropertyIdentifier::PRESENT_VALUE {
226 return common::write_priority_array!(self, value, priority, |v| {
227 if let PropertyValue::Real(f) = v {
228 if !f.is_finite() {
229 return Err(common::value_out_of_range_error());
230 }
231 Ok(f)
232 } else {
233 Err(common::invalid_data_type_error())
234 }
235 });
236 }
237 if let Some(result) = self.reliability_inhibit.write_inhibit(
238 &mut self.reliability,
239 self.out_of_service,
240 property,
241 &value,
242 ) {
243 return result;
244 }
245 if let Some(result) = self.reliability_inhibit.write_out_of_service(
246 &mut self.out_of_service,
247 &mut self.reliability,
248 &mut self.reliability_before_out_of_service,
249 property,
250 &value,
251 ) {
252 return result;
253 }
254 if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
255 return result;
256 }
257 if let Some(result) = common::write_description(&mut self.description, property, &value) {
258 return result;
259 }
260 if let Some(result) = self.reliability_inhibit.write_client_reliability(
266 self.out_of_service,
267 &mut self.reliability,
268 property,
269 &value,
270 ) {
271 return result;
272 }
273 if property == PropertyIdentifier::RELINQUISH_DEFAULT {
274 if let PropertyValue::Real(v) = value {
275 return self.set_relinquish_default(v);
276 }
277 return Err(common::invalid_data_type_error());
278 }
279 if let Some(result) = common::write_cov_increment(&mut self.cov_increment, property, &value)
280 {
281 return result;
282 }
283 if property == PropertyIdentifier::EVENT_DETECTION_ENABLE {
284 if let PropertyValue::Boolean(v) = value {
285 self.event_detection_enable = v;
286 if !v {
287 self.event_detector.event_state = bacnet_types::enums::EventState::NORMAL;
288 self.event_detector.acked_transitions = 0b111;
289 self.event_detector.pending = None;
290 self.event_detector.fault_reliability = None;
291 self.event_history.reset();
292 }
293 return Ok(());
294 }
295 return Err(common::invalid_data_type_error());
296 }
297 if let Some(result) = write_analog_event_properties!(self, property, value) {
298 return result;
299 }
300 if let Some(result) = write_generic_event_properties!(self, property, value) {
301 return result;
302 }
303 Err(common::write_access_denied_error())
304 }
305
306 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
307 static PROPS: &[PropertyIdentifier] = &[
308 PropertyIdentifier::OBJECT_IDENTIFIER,
309 PropertyIdentifier::OBJECT_NAME,
310 PropertyIdentifier::DESCRIPTION,
311 PropertyIdentifier::OBJECT_TYPE,
312 PropertyIdentifier::PRESENT_VALUE,
313 PropertyIdentifier::STATUS_FLAGS,
314 PropertyIdentifier::EVENT_STATE,
315 PropertyIdentifier::EVENT_DETECTION_ENABLE,
316 PropertyIdentifier::OUT_OF_SERVICE,
317 PropertyIdentifier::UNITS,
318 PropertyIdentifier::PRIORITY_ARRAY,
319 PropertyIdentifier::RELINQUISH_DEFAULT,
320 PropertyIdentifier::CURRENT_COMMAND_PRIORITY,
321 PropertyIdentifier::COV_INCREMENT,
322 PropertyIdentifier::HIGH_LIMIT,
323 PropertyIdentifier::LOW_LIMIT,
324 PropertyIdentifier::DEADBAND,
325 PropertyIdentifier::LIMIT_ENABLE,
326 PropertyIdentifier::EVENT_ENABLE,
327 PropertyIdentifier::NOTIFY_TYPE,
328 PropertyIdentifier::NOTIFICATION_CLASS,
329 PropertyIdentifier::TIME_DELAY,
330 PropertyIdentifier::TIME_DELAY_NORMAL,
331 PropertyIdentifier::RELIABILITY,
332 PropertyIdentifier::RELIABILITY_EVALUATION_INHIBIT,
333 PropertyIdentifier::ACKED_TRANSITIONS,
334 PropertyIdentifier::EVENT_TIME_STAMPS,
335 PropertyIdentifier::EVENT_MESSAGE_TEXTS,
336 ];
337 self.fault_out_of_range.property_list(PROPS)
338 }
339
340 fn supports_cov(&self) -> bool {
341 true
342 }
343
344 fn cov_increment(&self) -> Option<f32> {
345 Some(self.cov_increment)
346 }
347
348 crate::event::impl_builtin_intrinsic_reporting!(
349 event_detector,
350 event_history,
351 [present_value],
352 reliability,
353 event_detection_enable,
354 OutOfRangeDetector::ALGORITHM
355 );
356 impl_intrinsic_write_rollback!(
357 event_detector,
358 event_detection_enable,
359 event_history,
360 reliability_inhibit,
361 reliability,
362 out_of_service,
363 reliability_before_out_of_service,
364 fault_out_of_range
365 );
366
367 fn acknowledge_alarm(&mut self, transition_bit: u8) -> Result<(), bacnet_types::error::Error> {
368 self.event_detector.acked_transitions |= transition_bit & 0x07;
369 Ok(())
370 }
371
372 fn acknowledge_alarm_correlated_internal(
373 &mut self,
374 event_state: EventState,
375 timestamp: &BACnetTimeStamp,
376 ) -> Result<(), Error> {
377 if !self.event_detection_enable {
378 return Err(Error::Protocol {
379 class: ErrorClass::OBJECT.to_raw() as u32,
380 code: ErrorCode::NO_ALARM_CONFIGURED.to_raw() as u32,
381 });
382 }
383 self.event_history.acknowledge_correlated(
384 &mut self.event_detector.acked_transitions,
385 event_state,
386 timestamp,
387 )
388 }
389
390 fn set_reliability_internal(&mut self, reliability: u32) -> Result<(), Error> {
391 if self.out_of_service || self.reliability_inhibit.enabled() {
392 return Err(common::write_access_denied_error());
393 }
394 if !common::is_reliability_value_valid(reliability) {
395 return Err(common::value_out_of_range_error());
396 }
397 self.reliability = reliability;
398 self.fault_out_of_range.clear_ownership();
399 Ok(())
400 }
401
402 fn evaluate_reliability_internal(&mut self) -> Result<ReliabilityEvaluation, Error> {
403 if self.out_of_service || self.reliability_inhibit.enabled() {
404 return Ok(ReliabilityEvaluation::Unchanged);
405 }
406 self.fault_out_of_range
407 .evaluate(self.present_value, &mut self.reliability)
408 }
409
410 fn reliability_evaluation_inhibited_internal(&self) -> bool {
411 self.reliability_inhibit.enabled()
412 }
413
414 fn is_createable(&self) -> bool {
417 false
418 }
419
420 fn is_writable_property(&self, property: PropertyIdentifier) -> bool {
421 common::is_commandable_property_writable(property)
424 || common::is_common_writable(property)
425 || property == PropertyIdentifier::RELIABILITY
426 || property == PropertyIdentifier::RELIABILITY_EVALUATION_INHIBIT
427 || property == PropertyIdentifier::COV_INCREMENT
428 || common::is_event_property_writable(property)
429 || property == PropertyIdentifier::EVENT_DETECTION_ENABLE
430 }
431}
432
433#[cfg(test)]
434mod detection_enable_reset_tests {
435 use super::*;
436
437 #[test]
440 fn av_disabling_detection_resets_event_history() {
441 let mut av = AnalogValueObject::new(1, "AV-1", 62).unwrap();
442 assert_eq!(
443 av.read_property(PropertyIdentifier::EVENT_DETECTION_ENABLE, None)
444 .unwrap(),
445 PropertyValue::Boolean(true)
446 );
447 av.event_detector.event_state = bacnet_types::enums::EventState::HIGH_LIMIT;
448 av.event_detector.acked_transitions = 0;
449 av.event_detector.pending = Some(crate::event::PendingTransition {
450 state: bacnet_types::enums::EventState::HIGH_LIMIT,
451 remaining: 2,
452 });
453 av.event_detector.fault_reliability = Some(1);
454 av.event_history.time_stamps = [
455 BACnetTimeStamp::SequenceNumber(1),
456 BACnetTimeStamp::SequenceNumber(2),
457 BACnetTimeStamp::SequenceNumber(3),
458 ];
459 av.event_history.message_texts = ["offnormal".into(), "fault".into(), "normal".into()];
460
461 av.write_property(
462 PropertyIdentifier::EVENT_DETECTION_ENABLE,
463 None,
464 PropertyValue::Boolean(false),
465 None,
466 )
467 .unwrap();
468
469 assert_eq!(
470 av.read_property(PropertyIdentifier::EVENT_DETECTION_ENABLE, None)
471 .unwrap(),
472 PropertyValue::Boolean(false)
473 );
474 assert_eq!(
475 av.event_detector.event_state,
476 bacnet_types::enums::EventState::NORMAL
477 );
478 assert_eq!(av.event_detector.acked_transitions, 0b111);
479 assert!(av.event_detector.pending.is_none());
480 assert!(av.event_detector.fault_reliability.is_none());
481 assert_eq!(
482 av.event_history.time_stamps,
483 [
484 BACnetTimeStamp::SequenceNumber(0),
485 BACnetTimeStamp::SequenceNumber(0),
486 BACnetTimeStamp::SequenceNumber(0),
487 ]
488 );
489 assert_eq!(
490 av.event_history.message_texts,
491 [String::new(), String::new(), String::new()]
492 );
493 }
494}
495
496#[cfg(test)]
497mod fault_out_of_range_non_finite_tests {
498 use super::*;
499 use bacnet_types::enums::{ErrorClass, ErrorCode, Reliability};
500
501 fn assert_value_out_of_range(result: Result<ReliabilityEvaluation, Error>) {
502 assert!(matches!(
503 result,
504 Err(Error::Protocol { class, code })
505 if class == ErrorClass::PROPERTY.to_raw() as u32
506 && code == ErrorCode::VALUE_OUT_OF_RANGE.to_raw() as u32
507 ));
508 }
509
510 #[test]
511 fn av_non_finite_monitored_values_preserve_reliability_status_and_ownership() {
512 for non_finite in [f32::NAN, f32::INFINITY, f32::NEG_INFINITY] {
513 let mut normal = AnalogValueObject::new(1, "AV-normal", 62).unwrap();
514 normal.configure_fault_out_of_range(10.0, 20.0).unwrap();
515 let status_before = normal
516 .read_property(PropertyIdentifier::STATUS_FLAGS, None)
517 .unwrap();
518 normal.present_value = non_finite;
519
520 assert_value_out_of_range(normal.evaluate_reliability_internal());
521 assert_eq!(normal.present_value.to_bits(), non_finite.to_bits());
522 assert_eq!(normal.reliability, Reliability::NO_FAULT_DETECTED.to_raw());
523 assert_eq!(
524 normal
525 .read_property(PropertyIdentifier::STATUS_FLAGS, None)
526 .unwrap(),
527 status_before
528 );
529 assert!(normal.fault_out_of_range.owned_fault.is_none());
530
531 normal.present_value = 9.0;
532 assert_eq!(
533 normal.evaluate_reliability_internal().unwrap(),
534 ReliabilityEvaluation::Changed {
535 old_reliability: Reliability::NO_FAULT_DETECTED.to_raw(),
536 new_reliability: Reliability::UNDER_RANGE.to_raw(),
537 }
538 );
539
540 let mut owned = AnalogValueObject::new(2, "AV-owned", 62).unwrap();
541 owned.configure_fault_out_of_range(10.0, 20.0).unwrap();
542 owned.present_value = 9.0;
543 owned.evaluate_reliability_internal().unwrap();
544 let status_before = owned
545 .read_property(PropertyIdentifier::STATUS_FLAGS, None)
546 .unwrap();
547 owned.present_value = non_finite;
548
549 assert_value_out_of_range(owned.evaluate_reliability_internal());
550 assert_eq!(owned.present_value.to_bits(), non_finite.to_bits());
551 assert_eq!(owned.reliability, Reliability::UNDER_RANGE.to_raw());
552 assert_eq!(
553 owned
554 .read_property(PropertyIdentifier::STATUS_FLAGS, None)
555 .unwrap(),
556 status_before
557 );
558 assert!(matches!(
559 owned.fault_out_of_range.owned_fault,
560 Some(OwnedRangeFault::UnderRange)
561 ));
562
563 owned.present_value = 21.0;
564 assert_eq!(
565 owned.evaluate_reliability_internal().unwrap(),
566 ReliabilityEvaluation::Changed {
567 old_reliability: Reliability::UNDER_RANGE.to_raw(),
568 new_reliability: Reliability::OVER_RANGE.to_raw(),
569 }
570 );
571 }
572 }
573}