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