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