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