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 p if p == PropertyIdentifier::RECIPIENT_LIST => {
88 Ok(PropertyValue::List(Vec::new())) }
90 p if p == PropertyIdentifier::PROCESS_IDENTIFIER_FILTER => {
91 Ok(PropertyValue::List(Vec::new()))
92 }
93 _ => Err(common::unknown_property_error()),
94 }
95 }
96
97 fn write_property(
98 &mut self,
99 property: PropertyIdentifier,
100 _array_index: Option<u32>,
101 value: PropertyValue,
102 _priority: Option<u8>,
103 ) -> Result<(), Error> {
104 if property == PropertyIdentifier::LOCAL_FORWARDING_ONLY {
105 if let PropertyValue::Boolean(v) = value {
106 self.local_forwarding_only = v;
107 return Ok(());
108 }
109 return Err(common::invalid_data_type_error());
110 }
111 if property == PropertyIdentifier::EVENT_DETECTION_ENABLE {
112 if let PropertyValue::Boolean(v) = value {
113 self.event_detection_enable = v;
114 return Ok(());
115 }
116 return Err(common::invalid_data_type_error());
117 }
118 if let Some(result) =
119 common::write_out_of_service(&mut self.out_of_service, property, &value)
120 {
121 return result;
122 }
123 if let Some(result) = common::write_description(&mut self.description, property, &value) {
124 return result;
125 }
126 Err(common::write_access_denied_error())
127 }
128
129 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
130 static PROPS: &[PropertyIdentifier] = &[
131 PropertyIdentifier::OBJECT_IDENTIFIER,
132 PropertyIdentifier::OBJECT_NAME,
133 PropertyIdentifier::DESCRIPTION,
134 PropertyIdentifier::OBJECT_TYPE,
135 PropertyIdentifier::STATUS_FLAGS,
136 PropertyIdentifier::OUT_OF_SERVICE,
137 PropertyIdentifier::RELIABILITY,
138 PropertyIdentifier::PROCESS_IDENTIFIER_FILTER,
139 PropertyIdentifier::SUBSCRIBED_RECIPIENTS,
140 PropertyIdentifier::LOCAL_FORWARDING_ONLY,
141 PropertyIdentifier::EVENT_DETECTION_ENABLE,
142 ];
143 Cow::Borrowed(PROPS)
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn create_notification_forwarder() {
153 let nf = NotificationForwarderObject::new(1, "NF-1").unwrap();
154 assert_eq!(
155 nf.object_identifier().object_type(),
156 ObjectType::NOTIFICATION_FORWARDER
157 );
158 assert_eq!(nf.object_identifier().instance_number(), 1);
159 assert_eq!(nf.object_name(), "NF-1");
160 }
161
162 #[test]
163 fn object_type() {
164 let nf = NotificationForwarderObject::new(1, "NF").unwrap();
165 let val = nf
166 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
167 .unwrap();
168 assert_eq!(
169 val,
170 PropertyValue::Enumerated(ObjectType::NOTIFICATION_FORWARDER.to_raw())
171 );
172 }
173
174 #[test]
175 fn process_identifier_filter_empty() {
176 let nf = NotificationForwarderObject::new(1, "NF").unwrap();
177 let val = nf
178 .read_property(PropertyIdentifier::PROCESS_IDENTIFIER_FILTER, None)
179 .unwrap();
180 if let PropertyValue::List(items) = val {
181 assert!(items.is_empty());
182 } else {
183 panic!("Expected List");
184 }
185 }
186
187 #[test]
188 fn process_identifier_filter_with_values() {
189 let mut nf = NotificationForwarderObject::new(1, "NF").unwrap();
190 nf.process_identifier_filter.push(100);
191 nf.process_identifier_filter.push(200);
192
193 let val = nf
194 .read_property(PropertyIdentifier::PROCESS_IDENTIFIER_FILTER, None)
195 .unwrap();
196 if let PropertyValue::List(items) = val {
197 assert_eq!(items.len(), 2);
198 assert_eq!(items[0], PropertyValue::Unsigned(100));
199 assert_eq!(items[1], PropertyValue::Unsigned(200));
200 } else {
201 panic!("Expected List");
202 }
203 }
204
205 #[test]
206 fn local_forwarding_only_default() {
207 let nf = NotificationForwarderObject::new(1, "NF").unwrap();
208 let val = nf
209 .read_property(PropertyIdentifier::LOCAL_FORWARDING_ONLY, None)
210 .unwrap();
211 assert_eq!(val, PropertyValue::Boolean(false));
212 }
213
214 #[test]
215 fn write_local_forwarding_only() {
216 let mut nf = NotificationForwarderObject::new(1, "NF").unwrap();
217 nf.write_property(
218 PropertyIdentifier::LOCAL_FORWARDING_ONLY,
219 None,
220 PropertyValue::Boolean(true),
221 None,
222 )
223 .unwrap();
224 let val = nf
225 .read_property(PropertyIdentifier::LOCAL_FORWARDING_ONLY, None)
226 .unwrap();
227 assert_eq!(val, PropertyValue::Boolean(true));
228 }
229
230 #[test]
231 fn write_local_forwarding_only_wrong_type() {
232 let mut nf = NotificationForwarderObject::new(1, "NF").unwrap();
233 let result = nf.write_property(
234 PropertyIdentifier::LOCAL_FORWARDING_ONLY,
235 None,
236 PropertyValue::Unsigned(1),
237 None,
238 );
239 assert!(result.is_err());
240 }
241
242 #[test]
243 fn event_detection_enable_default() {
244 let nf = NotificationForwarderObject::new(1, "NF").unwrap();
245 let val = nf
246 .read_property(PropertyIdentifier::EVENT_DETECTION_ENABLE, None)
247 .unwrap();
248 assert_eq!(val, PropertyValue::Boolean(true));
249 }
250
251 #[test]
252 fn write_event_detection_enable() {
253 let mut nf = NotificationForwarderObject::new(1, "NF").unwrap();
254 nf.write_property(
255 PropertyIdentifier::EVENT_DETECTION_ENABLE,
256 None,
257 PropertyValue::Boolean(false),
258 None,
259 )
260 .unwrap();
261 let val = nf
262 .read_property(PropertyIdentifier::EVENT_DETECTION_ENABLE, None)
263 .unwrap();
264 assert_eq!(val, PropertyValue::Boolean(false));
265 }
266
267 #[test]
268 fn subscribed_recipients_default() {
269 let nf = NotificationForwarderObject::new(1, "NF").unwrap();
270 let val = nf
271 .read_property(PropertyIdentifier::SUBSCRIBED_RECIPIENTS, None)
272 .unwrap();
273 assert_eq!(val, PropertyValue::Unsigned(0));
274 }
275
276 #[test]
277 fn property_list() {
278 let nf = NotificationForwarderObject::new(1, "NF").unwrap();
279 let props = nf.property_list();
280 assert!(props.contains(&PropertyIdentifier::PROCESS_IDENTIFIER_FILTER));
281 assert!(props.contains(&PropertyIdentifier::SUBSCRIBED_RECIPIENTS));
282 assert!(props.contains(&PropertyIdentifier::LOCAL_FORWARDING_ONLY));
283 assert!(props.contains(&PropertyIdentifier::EVENT_DETECTION_ENABLE));
284 }
285}