bacnet_objects/event_enrollment/
alert.rs1use std::borrow::Cow;
2
3use bacnet_types::enums::{EventState, NotifyType, ObjectType, PropertyIdentifier};
4use bacnet_types::error::Error;
5use bacnet_types::primitives::{ObjectIdentifier, PropertyValue};
6
7use crate::common;
8use crate::event::history::EventHistory;
9use crate::property_metadata::PropertyMetadata;
10use crate::traits::{BACnetObject, WritePropertyRollback};
11
12use super::metadata;
13use super::state::AlertEnrollmentWriteRollback;
14
15pub struct AlertEnrollmentObject {
21 oid: ObjectIdentifier,
22 name: String,
23 description: String,
24 pub(super) event_state: u32,
26 pub present_value: ObjectIdentifier,
28 pub event_detection_enable: bool,
37 pub(super) acked_transitions: u8,
39 pub(super) event_history: EventHistory,
40 pub event_enable: u8,
42 pub notification_class: u32,
44 notify_type: u32,
47}
48
49impl AlertEnrollmentObject {
50 pub fn new(
53 instance: u32,
54 name: impl Into<String>,
55 initial_source: ObjectIdentifier,
56 ) -> Result<Self, Error> {
57 let oid = ObjectIdentifier::new(ObjectType::ALERT_ENROLLMENT, instance)?;
58 Ok(Self {
59 oid,
60 name: name.into(),
61 description: String::new(),
62 event_state: 0,
63 present_value: initial_source,
64 event_detection_enable: true,
65 acked_transitions: 0b111,
66 event_history: EventHistory::default(),
67 event_enable: 0b111,
68 notification_class: 0,
69 notify_type: NotifyType::ALARM.to_raw(),
70 })
71 }
72
73 pub fn record_alert_source(&mut self, source: ObjectIdentifier) {
79 self.present_value = source;
80 }
81
82 pub fn set_event_detection_enable(&mut self, enabled: bool) {
86 if !enabled || !self.event_detection_enable {
87 self.event_state = EventState::NORMAL.to_raw();
88 self.acked_transitions = 0b111;
89 self.event_history.reset();
90 }
91 self.event_detection_enable = enabled;
92 }
93
94 fn read_alert_common_property(
99 &self,
100 property: PropertyIdentifier,
101 array_index: Option<u32>,
102 ) -> Option<Result<PropertyValue, Error>> {
103 match property {
104 PropertyIdentifier::OBJECT_IDENTIFIER => {
105 Some(Ok(PropertyValue::ObjectIdentifier(self.oid)))
106 }
107 PropertyIdentifier::OBJECT_NAME => {
108 Some(Ok(PropertyValue::CharacterString(self.name.clone())))
109 }
110 PropertyIdentifier::DESCRIPTION => {
111 Some(Ok(PropertyValue::CharacterString(self.description.clone())))
112 }
113 PropertyIdentifier::PROPERTY_LIST => {
114 let properties = self.property_list();
115 Some(common::read_property_list_property(
116 &properties,
117 array_index,
118 ))
119 }
120 _ => None,
121 }
122 }
123}
124
125impl BACnetObject for AlertEnrollmentObject {
126 fn object_identifier(&self) -> ObjectIdentifier {
127 self.oid
128 }
129
130 fn object_name(&self) -> &str {
131 &self.name
132 }
133
134 fn read_property(
135 &self,
136 property: PropertyIdentifier,
137 array_index: Option<u32>,
138 ) -> Result<PropertyValue, Error> {
139 if property == PropertyIdentifier::EVENT_TIME_STAMPS {
140 if !self.event_detection_enable {
141 return EventHistory::default()
142 .read(property, array_index)
143 .expect("EventHistory handles Event_Time_Stamps");
144 }
145 return self
146 .event_history
147 .read(property, array_index)
148 .expect("EventHistory handles Event_Time_Stamps");
149 }
150 if let Some(result) = self.read_alert_common_property(property, array_index) {
151 return result;
152 }
153 match property {
154 p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
155 ObjectType::ALERT_ENROLLMENT.to_raw(),
156 )),
157 p if p == PropertyIdentifier::PRESENT_VALUE => {
158 Ok(PropertyValue::ObjectIdentifier(self.present_value))
159 }
160 p if p == PropertyIdentifier::EVENT_DETECTION_ENABLE => {
161 Ok(PropertyValue::Boolean(self.event_detection_enable))
162 }
163 p if p == PropertyIdentifier::EVENT_ENABLE => Ok(PropertyValue::BitString {
164 unused_bits: 5,
165 data: vec![bacnet_types::bitstring::pack_octet(self.event_enable)],
166 }),
167 p if p == PropertyIdentifier::NOTIFICATION_CLASS => {
168 Ok(PropertyValue::Unsigned(self.notification_class as u64))
169 }
170 p if p == PropertyIdentifier::NOTIFY_TYPE => {
171 Ok(PropertyValue::Enumerated(self.notify_type))
172 }
173 p if p == PropertyIdentifier::EVENT_STATE => {
174 Ok(PropertyValue::Enumerated(if self.event_detection_enable {
175 self.event_state
176 } else {
177 EventState::NORMAL.to_raw()
178 }))
179 }
180 p if p == PropertyIdentifier::ACKED_TRANSITIONS => Ok(PropertyValue::BitString {
181 unused_bits: 5,
182 data: vec![bacnet_types::bitstring::pack_octet(
183 if self.event_detection_enable {
184 self.acked_transitions
185 } else {
186 0b111
187 },
188 )],
189 }),
190 _ => Err(common::unknown_property_error()),
191 }
192 }
193
194 fn write_property(
195 &mut self,
196 property: PropertyIdentifier,
197 _array_index: Option<u32>,
198 value: PropertyValue,
199 _priority: Option<u8>,
200 ) -> Result<(), Error> {
201 if property == PropertyIdentifier::EVENT_DETECTION_ENABLE {
202 if let PropertyValue::Boolean(v) = value {
203 self.set_event_detection_enable(v);
204 return Ok(());
205 }
206 return Err(common::invalid_data_type_error());
207 }
208 if property == PropertyIdentifier::EVENT_ENABLE {
209 if let PropertyValue::BitString { unused_bits, data } = &value {
212 let byte = common::check_fixed_width_bit_string(*unused_bits, data, 3)?;
213 self.event_enable = bacnet_types::bitstring::unpack_octet(&[byte], 3);
214 return Ok(());
215 }
216 return Err(common::invalid_data_type_error());
217 }
218 if property == PropertyIdentifier::NOTIFICATION_CLASS {
219 if let PropertyValue::Unsigned(v) = value {
220 self.notification_class = common::u64_to_u32(v)?;
221 return Ok(());
222 }
223 return Err(common::invalid_data_type_error());
224 }
225 if property == PropertyIdentifier::NOTIFY_TYPE {
226 if let PropertyValue::Enumerated(v) = value {
227 if v != NotifyType::ALARM.to_raw() && v != NotifyType::EVENT.to_raw() {
228 return Err(common::value_out_of_range_error());
229 }
230 self.notify_type = v;
231 return Ok(());
232 }
233 return Err(common::invalid_data_type_error());
234 }
235 if let Some(result) = common::write_description(&mut self.description, property, &value) {
236 return result;
237 }
238 Err(common::write_access_denied_error())
239 }
240
241 fn capture_write_property_rollback(
242 &mut self,
243 property: PropertyIdentifier,
244 _value: &PropertyValue,
245 ) -> Option<WritePropertyRollback> {
246 (property == PropertyIdentifier::EVENT_DETECTION_ENABLE).then(|| {
247 WritePropertyRollback::new(AlertEnrollmentWriteRollback {
248 enabled: self.event_detection_enable,
249 event_state: self.event_state,
250 acked_transitions: self.acked_transitions,
251 event_history: self.event_history.clone(),
252 })
253 })
254 }
255
256 fn restore_write_property_rollback(
257 &mut self,
258 rollback: WritePropertyRollback,
259 ) -> Result<(), Error> {
260 let AlertEnrollmentWriteRollback {
261 enabled,
262 event_state,
263 acked_transitions,
264 event_history,
265 } = rollback.downcast::<AlertEnrollmentWriteRollback>()?;
266 self.event_detection_enable = enabled;
267 self.event_state = event_state;
268 self.acked_transitions = acked_transitions;
269 self.event_history = event_history;
270 Ok(())
271 }
272
273 fn set_event_state_internal(&mut self, state: EventState) -> Result<(), Error> {
274 if !self.event_detection_enable && state != EventState::NORMAL {
275 return Err(common::write_access_denied_error());
276 }
277 self.event_state = state.to_raw();
278 Ok(())
279 }
280
281 fn set_acked_transitions_internal(
282 &mut self,
283 transition_bit: u8,
284 acknowledged: bool,
285 ) -> Result<(), Error> {
286 if !self.event_detection_enable {
287 return Err(common::write_access_denied_error());
288 }
289 let transition_bit = transition_bit & 0x07;
290 if acknowledged {
291 self.acked_transitions |= transition_bit;
292 } else {
293 self.acked_transitions &= !(transition_bit & 0x03);
297 }
298 self.acked_transitions |= 0x04;
299 Ok(())
300 }
301
302 fn is_writable_property(&self, property: PropertyIdentifier) -> bool {
303 matches!(
304 property,
305 PropertyIdentifier::DESCRIPTION
306 | PropertyIdentifier::EVENT_DETECTION_ENABLE
307 | PropertyIdentifier::EVENT_ENABLE
308 | PropertyIdentifier::NOTIFICATION_CLASS
309 | PropertyIdentifier::NOTIFY_TYPE
310 )
311 }
312
313 fn property_metadata(&self) -> Cow<'_, [PropertyMetadata]> {
314 Cow::Borrowed(metadata::ALERT_ENROLLMENT_PROPERTIES)
315 }
316
317 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
318 crate::property_metadata::property_list_from_metadata(metadata::ALERT_ENROLLMENT_PROPERTIES)
319 }
320}