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