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