1use bacnet_types::enums::{ObjectType, PropertyIdentifier};
4use bacnet_types::error::Error;
5use bacnet_types::primitives::{ObjectIdentifier, PropertyValue, StatusFlags};
6use std::borrow::Cow;
7
8use crate::common::{self, read_common_properties};
9use crate::traits::BACnetObject;
10
11pub struct NotificationForwarderObject {
16 oid: ObjectIdentifier,
17 name: String,
18 description: String,
19 status_flags: StatusFlags,
20 out_of_service: bool,
21 reliability: u32,
22 pub process_identifier_filter: Vec<u32>,
24 pub subscribed_recipients: u32,
26 pub local_forwarding_only: bool,
28 pub event_detection_enable: bool,
30}
31
32impl NotificationForwarderObject {
33 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
35 let oid = ObjectIdentifier::new(ObjectType::NOTIFICATION_FORWARDER, instance)?;
36 Ok(Self {
37 oid,
38 name: name.into(),
39 description: String::new(),
40 status_flags: StatusFlags::empty(),
41 out_of_service: false,
42 reliability: 0,
43 process_identifier_filter: Vec::new(),
44 subscribed_recipients: 0,
45 local_forwarding_only: false,
46 event_detection_enable: true,
47 })
48 }
49}
50
51impl BACnetObject for NotificationForwarderObject {
52 fn object_identifier(&self) -> ObjectIdentifier {
53 self.oid
54 }
55
56 fn object_name(&self) -> &str {
57 &self.name
58 }
59
60 fn read_property(
61 &self,
62 property: PropertyIdentifier,
63 array_index: Option<u32>,
64 ) -> Result<PropertyValue, Error> {
65 if let Some(result) = read_common_properties!(self, property, array_index) {
66 return result;
67 }
68 match property {
69 p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
70 ObjectType::NOTIFICATION_FORWARDER.to_raw(),
71 )),
72 p if p == PropertyIdentifier::PROCESS_IDENTIFIER_FILTER => Ok(PropertyValue::List(
73 self.process_identifier_filter
74 .iter()
75 .map(|id| PropertyValue::Unsigned(*id as u64))
76 .collect(),
77 )),
78 p if p == PropertyIdentifier::SUBSCRIBED_RECIPIENTS => {
79 Ok(PropertyValue::Unsigned(self.subscribed_recipients as u64))
80 }
81 p if p == PropertyIdentifier::LOCAL_FORWARDING_ONLY => {
82 Ok(PropertyValue::Boolean(self.local_forwarding_only))
83 }
84 p if p == PropertyIdentifier::EVENT_DETECTION_ENABLE => {
85 Ok(PropertyValue::Boolean(self.event_detection_enable))
86 }
87 _ => Err(common::unknown_property_error()),
88 }
89 }
90
91 fn write_property(
92 &mut self,
93 property: PropertyIdentifier,
94 _array_index: Option<u32>,
95 value: PropertyValue,
96 _priority: Option<u8>,
97 ) -> Result<(), Error> {
98 if property == PropertyIdentifier::LOCAL_FORWARDING_ONLY {
99 if let PropertyValue::Boolean(v) = value {
100 self.local_forwarding_only = v;
101 return Ok(());
102 }
103 return Err(common::invalid_data_type_error());
104 }
105 if property == PropertyIdentifier::EVENT_DETECTION_ENABLE {
106 if let PropertyValue::Boolean(v) = value {
107 self.event_detection_enable = v;
108 return Ok(());
109 }
110 return Err(common::invalid_data_type_error());
111 }
112 if let Some(result) =
113 common::write_out_of_service(&mut self.out_of_service, property, &value)
114 {
115 return result;
116 }
117 if let Some(result) = common::write_description(&mut self.description, property, &value) {
118 return result;
119 }
120 Err(common::write_access_denied_error())
121 }
122
123 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
124 static PROPS: &[PropertyIdentifier] = &[
125 PropertyIdentifier::OBJECT_IDENTIFIER,
126 PropertyIdentifier::OBJECT_NAME,
127 PropertyIdentifier::DESCRIPTION,
128 PropertyIdentifier::OBJECT_TYPE,
129 PropertyIdentifier::STATUS_FLAGS,
130 PropertyIdentifier::OUT_OF_SERVICE,
131 PropertyIdentifier::RELIABILITY,
132 PropertyIdentifier::PROCESS_IDENTIFIER_FILTER,
133 PropertyIdentifier::SUBSCRIBED_RECIPIENTS,
134 PropertyIdentifier::LOCAL_FORWARDING_ONLY,
135 PropertyIdentifier::EVENT_DETECTION_ENABLE,
136 ];
137 Cow::Borrowed(PROPS)
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 #[test]
146 fn create_notification_forwarder() {
147 let nf = NotificationForwarderObject::new(1, "NF-1").unwrap();
148 assert_eq!(
149 nf.object_identifier().object_type(),
150 ObjectType::NOTIFICATION_FORWARDER
151 );
152 assert_eq!(nf.object_identifier().instance_number(), 1);
153 assert_eq!(nf.object_name(), "NF-1");
154 }
155
156 #[test]
157 fn object_type() {
158 let nf = NotificationForwarderObject::new(1, "NF").unwrap();
159 let val = nf
160 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
161 .unwrap();
162 assert_eq!(
163 val,
164 PropertyValue::Enumerated(ObjectType::NOTIFICATION_FORWARDER.to_raw())
165 );
166 }
167
168 #[test]
169 fn process_identifier_filter_empty() {
170 let nf = NotificationForwarderObject::new(1, "NF").unwrap();
171 let val = nf
172 .read_property(PropertyIdentifier::PROCESS_IDENTIFIER_FILTER, None)
173 .unwrap();
174 if let PropertyValue::List(items) = val {
175 assert!(items.is_empty());
176 } else {
177 panic!("Expected List");
178 }
179 }
180
181 #[test]
182 fn process_identifier_filter_with_values() {
183 let mut nf = NotificationForwarderObject::new(1, "NF").unwrap();
184 nf.process_identifier_filter.push(100);
185 nf.process_identifier_filter.push(200);
186
187 let val = nf
188 .read_property(PropertyIdentifier::PROCESS_IDENTIFIER_FILTER, None)
189 .unwrap();
190 if let PropertyValue::List(items) = val {
191 assert_eq!(items.len(), 2);
192 assert_eq!(items[0], PropertyValue::Unsigned(100));
193 assert_eq!(items[1], PropertyValue::Unsigned(200));
194 } else {
195 panic!("Expected List");
196 }
197 }
198
199 #[test]
200 fn local_forwarding_only_default() {
201 let nf = NotificationForwarderObject::new(1, "NF").unwrap();
202 let val = nf
203 .read_property(PropertyIdentifier::LOCAL_FORWARDING_ONLY, None)
204 .unwrap();
205 assert_eq!(val, PropertyValue::Boolean(false));
206 }
207
208 #[test]
209 fn write_local_forwarding_only() {
210 let mut nf = NotificationForwarderObject::new(1, "NF").unwrap();
211 nf.write_property(
212 PropertyIdentifier::LOCAL_FORWARDING_ONLY,
213 None,
214 PropertyValue::Boolean(true),
215 None,
216 )
217 .unwrap();
218 let val = nf
219 .read_property(PropertyIdentifier::LOCAL_FORWARDING_ONLY, None)
220 .unwrap();
221 assert_eq!(val, PropertyValue::Boolean(true));
222 }
223
224 #[test]
225 fn write_local_forwarding_only_wrong_type() {
226 let mut nf = NotificationForwarderObject::new(1, "NF").unwrap();
227 let result = nf.write_property(
228 PropertyIdentifier::LOCAL_FORWARDING_ONLY,
229 None,
230 PropertyValue::Unsigned(1),
231 None,
232 );
233 assert!(result.is_err());
234 }
235
236 #[test]
237 fn event_detection_enable_default() {
238 let nf = NotificationForwarderObject::new(1, "NF").unwrap();
239 let val = nf
240 .read_property(PropertyIdentifier::EVENT_DETECTION_ENABLE, None)
241 .unwrap();
242 assert_eq!(val, PropertyValue::Boolean(true));
243 }
244
245 #[test]
246 fn write_event_detection_enable() {
247 let mut nf = NotificationForwarderObject::new(1, "NF").unwrap();
248 nf.write_property(
249 PropertyIdentifier::EVENT_DETECTION_ENABLE,
250 None,
251 PropertyValue::Boolean(false),
252 None,
253 )
254 .unwrap();
255 let val = nf
256 .read_property(PropertyIdentifier::EVENT_DETECTION_ENABLE, None)
257 .unwrap();
258 assert_eq!(val, PropertyValue::Boolean(false));
259 }
260
261 #[test]
262 fn subscribed_recipients_default() {
263 let nf = NotificationForwarderObject::new(1, "NF").unwrap();
264 let val = nf
265 .read_property(PropertyIdentifier::SUBSCRIBED_RECIPIENTS, None)
266 .unwrap();
267 assert_eq!(val, PropertyValue::Unsigned(0));
268 }
269
270 #[test]
271 fn property_list() {
272 let nf = NotificationForwarderObject::new(1, "NF").unwrap();
273 let props = nf.property_list();
274 assert!(props.contains(&PropertyIdentifier::PROCESS_IDENTIFIER_FILTER));
275 assert!(props.contains(&PropertyIdentifier::SUBSCRIBED_RECIPIENTS));
276 assert!(props.contains(&PropertyIdentifier::LOCAL_FORWARDING_ONLY));
277 assert!(props.contains(&PropertyIdentifier::EVENT_DETECTION_ENABLE));
278 }
279}