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 bacnet_types::enums::EngineeringUnits;
9use std::sync::Arc;
10
11mod metadata;
12
13pub struct AnalogOutputObject {
19 profile: ProfileState,
20 oid: ObjectIdentifier,
21 name: String,
22 description: String,
23 present_value: f32,
24 units: EngineeringUnits,
25 out_of_service: bool,
26 status_flags: StatusFlags,
27 priority_array: [Option<f32>; 16],
28 relinquish_default: f32,
29 cov_increment: f32,
33 event_detector: OutOfRangeDetector,
34 event_detection_enable: bool,
37 reliability: Reliability,
38 reliability_before_out_of_service: Option<Reliability>,
39 reliability_inhibit: common::ReliabilityInhibitState,
40 min_pres_value: Option<f32>,
41 max_pres_value: Option<f32>,
42 pub(crate) event_history: EventHistory,
43 value_source: crate::command_source::ValueSourceTracking,
45}
46
47impl AnalogOutputObject {
48 pub fn new(
50 instance: u32,
51 name: impl Into<String>,
52 units: EngineeringUnits,
53 ) -> Result<Self, Error> {
54 let oid = ObjectIdentifier::new(ObjectType::ANALOG_OUTPUT, instance)?;
55 Ok(Self {
56 profile: ProfileState::default(),
57 oid,
58 name: name.into(),
59 description: String::new(),
60 present_value: 0.0,
61 units,
62 out_of_service: false,
63 status_flags: StatusFlags::empty(),
64 priority_array: [None; 16],
65 relinquish_default: 0.0,
66 cov_increment: 0.0,
67 event_detector: OutOfRangeDetector::default(),
68 event_detection_enable: true,
69 reliability: Reliability::NO_FAULT_DETECTED,
70 reliability_before_out_of_service: None,
71 reliability_inhibit: common::ReliabilityInhibitState::default(),
72 min_pres_value: None,
73 max_pres_value: None,
74 event_history: EventHistory::default(),
75 value_source: crate::command_source::ValueSourceTracking::default(),
76 })
77 }
78
79 pub fn with_tags_persistence(
83 instance: u32,
84 name: impl Into<String>,
85 units: EngineeringUnits,
86 persistence: Arc<dyn TagsPersistence>,
87 ) -> Result<Self, Error> {
88 let mut object = Self::new(instance, name, units)?;
89 object.profile = ProfileState::persistent(object.oid, persistence)?;
90 Ok(object)
91 }
92
93 pub fn set_profile(&mut self, profile: ObjectProfile) -> Result<(), Error> {
98 self.profile.provision(profile)
99 }
100
101 pub fn wait_for_tag_saves(&self) {
104 self.profile.wait_for_saves();
105 }
106
107 pub fn set_description(&mut self, desc: impl Into<String>) {
109 self.description = desc.into();
110 }
111
112 pub fn set_min_pres_value(&mut self, value: f32) {
114 self.min_pres_value = Some(value);
115 }
116
117 pub fn set_max_pres_value(&mut self, value: f32) {
119 self.max_pres_value = Some(value);
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 common::reject_non_finite(value)?;
135 self.relinquish_default = value;
136 self.recalculate_present_value();
137 Ok(())
138 }
139}
140
141impl BACnetObject for AnalogOutputObject {
142 fn object_identifier(&self) -> ObjectIdentifier {
143 self.oid
144 }
145
146 fn object_name(&self) -> &str {
147 &self.name
148 }
149
150 fn read_property(
151 &self,
152 property: PropertyIdentifier,
153 array_index: Option<u32>,
154 ) -> Result<PropertyValue, Error> {
155 if let Some(result) = self
156 .value_source
157 .read(property, array_index, &self.priority_array)
158 {
159 return result;
160 }
161
162 if property == PropertyIdentifier::STATUS_FLAGS {
163 return Ok(common::compute_status_flags(
164 self.status_flags,
165 self.reliability,
166 self.out_of_service,
167 self.event_detector.event_state,
168 ));
169 }
170 if let Some(value) = self.reliability_inhibit.read(property) {
171 return Ok(value);
172 }
173 if let Some(result) = read_common_properties!(self, property, array_index) {
174 return result;
175 }
176 if let Some(result) = read_analog_event_properties!(self, property) {
177 return result;
178 }
179 if let Some(result) = self.event_history.read(property, array_index) {
180 return result;
181 }
182 if let Some(result) = read_generic_event_properties!(self, property) {
183 return result;
184 }
185 if property == PropertyIdentifier::EVENT_DETECTION_ENABLE {
186 return Ok(PropertyValue::Boolean(self.event_detection_enable));
187 }
188 if let Some(result) = self.profile.read(property, array_index) {
189 return result;
190 }
191 match property {
192 p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
193 ObjectType::ANALOG_OUTPUT.to_raw(),
194 )),
195 p if p == PropertyIdentifier::PRESENT_VALUE => {
196 Ok(PropertyValue::Real(self.present_value))
197 }
198 p if p == PropertyIdentifier::UNITS => {
199 Ok(PropertyValue::Enumerated(self.units.to_raw()))
200 }
201 p if p == PropertyIdentifier::PRIORITY_ARRAY => {
202 common::read_priority_array!(self, array_index, PropertyValue::Real)
203 }
204 p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
205 Ok(PropertyValue::Real(self.relinquish_default))
206 }
207 p if p == PropertyIdentifier::CURRENT_COMMAND_PRIORITY => {
208 Ok(common::current_command_priority(&self.priority_array))
209 }
210 p if p == PropertyIdentifier::COV_INCREMENT => {
211 Ok(PropertyValue::Real(self.cov_increment))
212 }
213 p if p == PropertyIdentifier::MIN_PRES_VALUE => match self.min_pres_value {
214 Some(v) => Ok(PropertyValue::Real(v)),
215 None => Err(common::unknown_property_error()),
216 },
217 p if p == PropertyIdentifier::MAX_PRES_VALUE => match self.max_pres_value {
218 Some(v) => Ok(PropertyValue::Real(v)),
219 None => Err(common::unknown_property_error()),
220 },
221 _ => Err(common::unknown_property_error()),
222 }
223 }
224
225 fn write_property_from(
226 &mut self,
227 property: PropertyIdentifier,
228 array_index: Option<u32>,
229 value: PropertyValue,
230 priority: Option<u8>,
231 origin: &crate::command_source::CommandOrigin,
232 ) -> Result<(), Error> {
233 if matches!(
234 property,
235 PropertyIdentifier::PRESENT_VALUE | PropertyIdentifier::VALUE_SOURCE
236 ) && array_index.is_some()
237 {
238 return Err(common::property_is_not_an_array_error());
239 }
240 if property == PropertyIdentifier::VALUE_SOURCE {
241 return self.value_source.correct(value, priority, origin);
242 }
243 if property == PropertyIdentifier::PRESENT_VALUE {
244 return crate::command_source::write_sourced_priority!(
245 self,
246 value,
247 priority,
248 origin,
249 |v| {
250 if let PropertyValue::Real(f) = v {
251 if !f.is_finite() {
252 return Err(common::value_out_of_range_error());
253 }
254 Ok(f)
255 } else {
256 Err(common::invalid_data_type_error())
257 }
258 }
259 );
260 }
261 self.write_property(property, array_index, value, priority)
262 }
263
264 fn write_property(
265 &mut self,
266 property: PropertyIdentifier,
267 array_index: Option<u32>,
268 value: PropertyValue,
269 _priority: Option<u8>,
270 ) -> Result<(), Error> {
271 if matches!(
272 property,
273 PropertyIdentifier::PRESENT_VALUE | PropertyIdentifier::VALUE_SOURCE
274 ) {
275 return Err(common::write_access_denied_error());
276 }
277 if let Some(result) = self.reliability_inhibit.write_inhibit(
278 &mut self.reliability,
279 self.out_of_service,
280 property,
281 &value,
282 ) {
283 return result;
284 }
285 if let Some(result) = self.reliability_inhibit.write_out_of_service(
286 &mut self.out_of_service,
287 &mut self.reliability,
288 &mut self.reliability_before_out_of_service,
289 property,
290 &value,
291 ) {
292 return result.map(|_| ());
293 }
294 if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
295 return result;
296 }
297 if let Some(result) = common::write_description(&mut self.description, property, &value) {
298 return result;
299 }
300 if let Some(result) = self.reliability_inhibit.write_client_reliability(
305 self.out_of_service,
306 &mut self.reliability,
307 property,
308 &value,
309 ) {
310 return result;
311 }
312 if property == PropertyIdentifier::RELINQUISH_DEFAULT {
313 if let PropertyValue::Real(v) = value {
314 return self.set_relinquish_default(v);
315 }
316 return Err(common::invalid_data_type_error());
317 }
318 if let Some(result) = common::write_cov_increment(&mut self.cov_increment, property, &value)
319 {
320 return result;
321 }
322 if property == PropertyIdentifier::EVENT_DETECTION_ENABLE {
323 if let PropertyValue::Boolean(v) = value {
324 self.event_detection_enable = v;
325 if !v {
326 self.event_detector.event_state = bacnet_types::enums::EventState::NORMAL;
327 self.event_detector.acked_transitions =
328 bacnet_types::bitstring::EventTransitionBits::all();
329 self.event_detector.pending = None;
330 self.event_detector.fault_reliability = None;
331 self.event_history.reset();
332 }
333 return Ok(());
334 }
335 return Err(common::invalid_data_type_error());
336 }
337 if let Some(result) = write_analog_event_properties!(self, property, value) {
338 return result;
339 }
340 if let Some(result) =
342 self.event_history
343 .write(property, array_index, &value, self.event_detection_enable)
344 {
345 return result;
346 }
347 if let Some(result) = write_generic_event_properties!(self, property, value) {
348 return result;
349 }
350 if let Some(result) = self.profile.write(property, array_index, &value) {
351 return result;
352 }
353 Err(crate::common::unhandled_write_error(
354 self.property_metadata().as_ref(),
355 property,
356 array_index,
357 ))
358 }
359
360 fn durable_writes_internal(&mut self) -> Option<&mut dyn crate::durable::DurableWrites> {
361 self.profile.capability()
362 }
363
364 fn advance_monotonic_time_internal(&mut self, now: std::time::Duration) -> bool {
365 self.profile.expire(now);
366 false
367 }
368
369 fn property_metadata(&self) -> Cow<'_, [PropertyMetadata]> {
370 metadata::for_object(self)
371 }
372
373 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
374 crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
375 }
376
377 fn supports_cov(&self) -> bool {
378 true
379 }
380
381 fn cov_increment(&self) -> Option<f64> {
382 Some(f64::from(self.cov_increment))
383 }
384
385 crate::event::impl_builtin_intrinsic_reporting!(
386 event_detector,
387 event_history,
388 [present_value],
389 reliability,
390 event_detection_enable,
391 OutOfRangeDetector::ALGORITHM
392 );
393
394 fn acknowledge_alarm(
395 &mut self,
396 transition_bit: bacnet_types::bitstring::EventTransitionBits,
397 ) -> Result<(), bacnet_types::error::Error> {
398 self.event_detector.acked_transitions |=
399 transition_bit & bacnet_types::bitstring::EventTransitionBits::all();
400 Ok(())
401 }
402
403 fn acknowledge_alarm_correlated_internal(
404 &mut self,
405 event_state: EventState,
406 timestamp: &BACnetTimeStamp,
407 ) -> Result<(), Error> {
408 if !self.event_detection_enable {
409 return Err(Error::Protocol {
410 class: ErrorClass::OBJECT,
411 code: ErrorCode::NO_ALARM_CONFIGURED,
412 });
413 }
414 self.event_history.acknowledge_correlated(
415 &mut self.event_detector.acked_transitions,
416 event_state,
417 timestamp,
418 )
419 }
420
421 fn set_reliability_internal(&mut self, reliability: Reliability) -> Result<(), Error> {
422 if self.out_of_service || self.reliability_inhibit.enabled() {
423 return Err(common::write_access_denied_error());
424 }
425 if !common::is_reliability_value_valid(reliability) {
426 return Err(common::value_out_of_range_error());
427 }
428 self.reliability = reliability;
429 Ok(())
430 }
431
432 fn reliability_evaluation_inhibited_internal(&self) -> bool {
433 self.reliability_inhibit.enabled()
434 }
435
436 fn is_createable(&self) -> bool {
437 true
438 }
439
440 fn creation_only_properties(&self) -> &'static [PropertyIdentifier] {
441 super::CREATION_ONLY
442 }
443
444 fn initialize_property(
445 &mut self,
446 property: PropertyIdentifier,
447 value: PropertyValue,
448 ) -> Result<(), Error> {
449 super::initialize_units(&mut self.units, property, value)
450 }
451}
452
453#[cfg(test)]
454mod detection_enable_reset_tests {
455 use super::*;
456
457 #[test]
460 fn ao_disabling_detection_resets_event_history() {
461 let mut ao = AnalogOutputObject::new(1, "AO-1", EngineeringUnits::DEGREES_CELSIUS).unwrap();
462 assert_eq!(
463 ao.read_property(PropertyIdentifier::EVENT_DETECTION_ENABLE, None)
464 .unwrap(),
465 PropertyValue::Boolean(true)
466 );
467 ao.event_detector.event_state = bacnet_types::enums::EventState::HIGH_LIMIT;
468 ao.event_detector.acked_transitions = bacnet_types::bitstring::EventTransitionBits::empty();
469 ao.event_detector.pending = Some(crate::event::PendingTransition {
470 state: bacnet_types::enums::EventState::HIGH_LIMIT,
471 remaining: 2,
472 });
473 ao.event_detector.fault_reliability = Some(bacnet_types::enums::Reliability::NO_SENSOR);
474 ao.event_history.time_stamps = [
475 BACnetTimeStamp::SequenceNumber(1),
476 BACnetTimeStamp::SequenceNumber(2),
477 BACnetTimeStamp::SequenceNumber(3),
478 ];
479 ao.event_history.message_texts = ["offnormal".into(), "fault".into(), "normal".into()];
480
481 ao.write_property(
482 PropertyIdentifier::EVENT_DETECTION_ENABLE,
483 None,
484 PropertyValue::Boolean(false),
485 None,
486 )
487 .unwrap();
488
489 assert_eq!(
490 ao.read_property(PropertyIdentifier::EVENT_DETECTION_ENABLE, None)
491 .unwrap(),
492 PropertyValue::Boolean(false)
493 );
494 assert_eq!(
495 ao.event_detector.event_state,
496 bacnet_types::enums::EventState::NORMAL
497 );
498 assert_eq!(
499 ao.event_detector.acked_transitions,
500 bacnet_types::bitstring::EventTransitionBits::all()
501 );
502 assert!(ao.event_detector.pending.is_none());
503 assert!(ao.event_detector.fault_reliability.is_none());
504 assert_eq!(
505 ao.event_history.time_stamps,
506 [
507 BACnetTimeStamp::SequenceNumber(0),
508 BACnetTimeStamp::SequenceNumber(0),
509 BACnetTimeStamp::SequenceNumber(0),
510 ]
511 );
512 assert_eq!(
513 ao.event_history.message_texts,
514 [String::new(), String::new(), String::new()]
515 );
516 }
517}