1use crate::prelude::*;
55use alloc::borrow::Cow;
56use alloc::sync::Arc;
57use core::time::Duration;
58
59use bacnet_types::constructed::{
60 BACnetDestination, BACnetEventNotificationSubscription, BACnetPortPermission,
61};
62use bacnet_types::enums::{ObjectType, PropertyIdentifier, Reliability};
63use bacnet_types::error::Error;
64use bacnet_types::primitives::{ObjectIdentifier, PropertyValue, StatusFlags};
65use bytes::BytesMut;
66
67use crate::common::{self, read_common_properties};
68use crate::durable::staged::{self, Step};
69use crate::durable::{DurableWrites, PendingWrite, SaveWait, StageStep};
70use crate::notification_class::recipient_list;
71use crate::subscribed_recipients::SubscribedRecipients;
72use crate::traits::{BACnetObject, MonotonicClock};
73
74mod metadata;
75mod persistence;
76mod port_filter;
77mod saving;
78mod selection;
79
80pub use crate::notification_class::MAX_RECIPIENT_LIST_DESTINATIONS;
81#[cfg(feature = "std")]
82pub use persistence::FileNotificationForwarderPersistence;
83pub use persistence::{ForwarderSnapshot, NotificationForwarderPersistence};
84pub use saving::ForwarderSaveCounters;
85pub use selection::{ForwardingInput, ForwardingTargets, forwarding_targets};
86
87pub struct NotificationForwarderObject {
99 oid: ObjectIdentifier,
100 name: String,
101 description: String,
102 status_flags: StatusFlags,
103 reliability: Reliability,
104 out_of_service: bool,
105 recipient_list: Vec<BACnetDestination>,
106 subscribed_recipients: SubscribedRecipients,
107 process_identifier_filter: Option<u32>,
108 local_forwarding_only: bool,
109 port_filter: Option<Vec<BACnetPortPermission>>,
110 storage: Option<saving::Storage>,
111 list_writes: u64,
114 recipient_list_written: bool,
117 save_counters: ForwarderSaveCounters,
118}
119
120impl NotificationForwarderObject {
121 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
124 Ok(Self {
125 oid: ObjectIdentifier::new(ObjectType::NOTIFICATION_FORWARDER, instance)?,
126 name: name.into(),
127 description: String::new(),
128 status_flags: StatusFlags::empty(),
129 reliability: Reliability::NO_FAULT_DETECTED,
130 out_of_service: false,
131 recipient_list: Vec::new(),
132 subscribed_recipients: SubscribedRecipients::new(),
133 process_identifier_filter: None,
134 local_forwarding_only: false,
135 port_filter: None,
136 storage: None,
137 list_writes: 0,
138 recipient_list_written: false,
139 save_counters: ForwarderSaveCounters::default(),
140 })
141 }
142
143 pub fn with_persistence(
152 instance: u32,
153 name: impl Into<String>,
154 persistence: Arc<dyn NotificationForwarderPersistence>,
155 ) -> Result<Self, Error> {
156 let mut forwarder = Self::new(instance, name)?;
157 if let Some(saved) = persistence.load(forwarder.oid)? {
158 if let Some(list) = saved.recipient_list {
159 if list.len() > MAX_RECIPIENT_LIST_DESTINATIONS {
160 return Err(recipient_list::no_space_error());
161 }
162 for destination in &list {
163 recipient_list::check_added(destination)?;
164 }
165 forwarder.recipient_list = list;
166 forwarder.recipient_list_written = true;
167 }
168 if !saved.subscribed_recipients.is_empty() {
169 forwarder
170 .subscribed_recipients
171 .write(framed(&saved.subscribed_recipients)?)?;
172 }
173 }
174 forwarder.storage = Some(saving::Storage::new(
175 forwarder.oid,
176 persistence,
177 forwarder.snapshot(),
178 forwarder.save_counters.clone(),
179 ));
180 Ok(forwarder)
181 }
182
183 pub fn save_counters(&self) -> ForwarderSaveCounters {
187 self.save_counters.clone()
188 }
189
190 pub fn set_description(&mut self, description: impl Into<String>) {
192 self.description = description.into();
193 }
194
195 pub fn process_identifier_filter(&self) -> Option<u32> {
198 self.process_identifier_filter
199 }
200
201 pub fn set_process_identifier_filter(&mut self, filter: Option<u32>) {
203 self.process_identifier_filter = filter;
204 }
205
206 pub fn local_forwarding_only(&self) -> bool {
209 self.local_forwarding_only
210 }
211
212 pub fn set_local_forwarding_only(&mut self, local_only: bool) {
214 self.local_forwarding_only = local_only;
215 }
216
217 pub fn add_destination(&mut self, destination: BACnetDestination) -> Result<(), Error> {
227 recipient_list::check_added(&destination)?;
228 if self.recipient_list_saved() {
229 log_debug!(
230 forwarder = %self.oid,
231 "Saved Recipient_List kept over a configured destination"
232 );
233 return Ok(());
234 }
235 if self.recipient_list.len() >= MAX_RECIPIENT_LIST_DESTINATIONS {
236 return Err(recipient_list::no_space_error());
237 }
238 self.recipient_list.push(destination);
239 Ok(())
240 }
241
242 pub fn recipient_list(&self) -> &[BACnetDestination] {
244 &self.recipient_list
245 }
246
247 pub fn subscriptions(&self) -> Vec<BACnetEventNotificationSubscription> {
250 self.subscribed_recipients.subscriptions()
251 }
252
253 pub fn set_port_filter(&mut self, ports: Option<Vec<BACnetPortPermission>>) {
257 self.port_filter = ports;
258 }
259
260 pub fn port_filter(&self) -> Option<&[BACnetPortPermission]> {
262 self.port_filter.as_deref()
263 }
264
265 pub fn recipient_list_saved(&self) -> bool {
269 self.storage.is_some() && self.recipient_list_written
270 }
271
272 pub fn wait_for_saves(&self) {
275 if let Some(storage) = &self.storage {
276 storage.wait_idle();
277 }
278 }
279
280 fn snapshot(&self) -> ForwarderSnapshot {
282 ForwarderSnapshot {
283 recipient_list: self
284 .recipient_list_written
285 .then(|| self.recipient_list.clone()),
286 subscribed_recipients: self.subscribed_recipients.subscriptions(),
287 }
288 }
289
290 fn next_list(
293 &self,
294 earlier: &[Step<saving::NextList>],
295 property: PropertyIdentifier,
296 value: PropertyValue,
297 ) -> Result<saving::NextList, Error> {
298 if property == PropertyIdentifier::RECIPIENT_LIST {
299 return recipient_list::decode_write(value).map(saving::NextList::RecipientList);
300 }
301 let current = earlier
304 .iter()
305 .rev()
306 .find_map(|step| match &step.next {
307 saving::NextList::SubscribedRecipients(store) => Some(store),
308 saving::NextList::RecipientList(_) => None,
309 })
310 .unwrap_or(&self.subscribed_recipients);
311 let mut next = current.clone();
312 next.write(value)?;
313 Ok(saving::NextList::SubscribedRecipients(next))
314 }
315
316 fn install(&mut self, next: saving::NextList) {
317 match next {
318 saving::NextList::RecipientList(list) => {
319 self.recipient_list = list;
320 self.recipient_list_written = true;
321 }
322 saving::NextList::SubscribedRecipients(store) => self.subscribed_recipients = store,
323 }
324 self.list_writes = self.list_writes.wrapping_add(1);
325 }
326
327 fn write_list(
333 &mut self,
334 property: PropertyIdentifier,
335 value: PropertyValue,
336 ) -> Result<(), Error> {
337 let refused = |_| {
338 common::protocol_error(
339 bacnet_types::enums::ErrorClass::DEVICE,
340 bacnet_types::enums::ErrorCode::OPERATIONAL_PROBLEM,
341 )
342 };
343 if let Some(taken) = self.take_staged(property, &value) {
344 return taken.map_err(refused);
345 }
346 let next = self.next_list(&[], property, value)?;
349 if self.storage.is_some() {
350 let mut snapshot = self.snapshot();
351 saving::apply(&mut snapshot, &next);
352 self.with_storage(|storage, _| storage.drop_staged());
355 let storage = self.storage.as_mut().expect("checked above");
356 storage.save_now(snapshot).map_err(refused)?;
357 }
358 self.install(next);
359 Ok(())
360 }
361
362 fn take_staged(
366 &mut self,
367 property: PropertyIdentifier,
368 value: &PropertyValue,
369 ) -> Option<Result<(), Error>> {
370 let mut storage = self.storage.take()?;
371 let taken = storage
372 .claim(property, value, self.list_writes)
373 .map(|claimed| claimed.map(|next| self.install(next)));
374 storage.correct(|| self.snapshot());
376 self.storage = Some(storage);
377 taken
378 }
379
380 fn after_time_passed(&mut self, lapsed: bool) -> bool {
382 let now = self.subscribed_recipients.current_time();
383 self.with_storage(|storage, served| storage.keep_current(now, lapsed, served));
384 lapsed
385 }
386
387 fn with_storage<R>(
391 &mut self,
392 f: impl FnOnce(&mut saving::Storage, &dyn Fn() -> ForwarderSnapshot) -> R,
393 ) -> Option<R> {
394 let mut storage = self.storage.take()?;
395 let served = || self.snapshot();
396 let result = f(&mut storage, &served);
397 storage.correct(served);
398 self.storage = Some(storage);
399 Some(result)
400 }
401}
402
403fn listed(property: PropertyIdentifier) -> bool {
405 matches!(
406 property,
407 PropertyIdentifier::RECIPIENT_LIST | PropertyIdentifier::SUBSCRIBED_RECIPIENTS
408 )
409}
410
411impl DurableWrites for NotificationForwarderObject {
412 fn stage_write(
413 &mut self,
414 property: PropertyIdentifier,
415 array_index: Option<u32>,
416 value: &PropertyValue,
417 ) -> StageStep {
418 self.stage_writes(&[PendingWrite {
419 property,
420 array_index,
421 value: value.clone(),
422 }])
423 }
424
425 fn stage_writes(&mut self, writes: &[PendingWrite]) -> StageStep {
428 if self.storage.is_none() || !writes.iter().any(|write| listed(write.property)) {
429 return StageStep::Skip;
430 }
431 if let Some(wait) = self.with_storage(|storage, _| storage.busy()).flatten() {
432 return StageStep::Busy(wait);
433 }
434 let steps = staged::steps(writes, |earlier, write| {
435 if !listed(write.property) {
436 return Ok(None);
437 }
438 if write.array_index.is_some() {
439 return Err(common::property_is_not_an_array_error());
440 }
441 self.next_list(earlier, write.property, write.value.clone())
442 .map(Some)
443 });
444 let served = self.snapshot();
445 let base = self.list_writes;
446 let storage = self.storage.as_mut().expect("checked above");
447 storage.stage(steps, base, served)
448 }
449
450 fn release_staged_write(&mut self, staged: &SaveWait) {
451 self.with_storage(|storage, _| storage.release(staged));
452 }
453
454 fn has_staged_write(&self) -> bool {
455 self.storage
456 .as_ref()
457 .is_some_and(saving::Storage::is_staged)
458 }
459
460 fn settle_forgotten_writes(&mut self) -> Option<SaveWait> {
461 self.with_storage(|storage, served| storage.drop_forgotten(served))
462 }
463}
464
465fn framed(subscriptions: &[BACnetEventNotificationSubscription]) -> Result<PropertyValue, Error> {
469 let mut buf = BytesMut::new();
470 bacnet_encoding::constructed::encode_event_notification_subscription_list(
471 &mut buf,
472 subscriptions,
473 )?;
474 Ok(PropertyValue::ApplicationData(buf.to_vec()))
475}
476
477fn read_bool(value: &PropertyValue) -> Result<bool, Error> {
478 match value {
479 PropertyValue::Boolean(value) => Ok(*value),
480 _ => Err(common::invalid_data_type_error()),
481 }
482}
483
484impl BACnetObject for NotificationForwarderObject {
485 fn object_identifier(&self) -> ObjectIdentifier {
486 self.oid
487 }
488
489 fn object_name(&self) -> &str {
490 &self.name
491 }
492
493 fn read_property(
494 &self,
495 property: PropertyIdentifier,
496 array_index: Option<u32>,
497 ) -> Result<PropertyValue, Error> {
498 if let Some(result) = read_common_properties!(self, property, array_index) {
499 return result;
500 }
501 match property {
502 PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
503 ObjectType::NOTIFICATION_FORWARDER.to_raw(),
504 )),
505 PropertyIdentifier::RECIPIENT_LIST => {
506 let mut buf = BytesMut::new();
507 bacnet_encoding::constructed::encode_destination_list(
508 &mut buf,
509 &self.recipient_list,
510 )?;
511 Ok(PropertyValue::ApplicationData(buf.to_vec()))
512 }
513 PropertyIdentifier::SUBSCRIBED_RECIPIENTS => Ok(self.subscribed_recipients.read()),
514 PropertyIdentifier::PROCESS_IDENTIFIER_FILTER => Ok(self
515 .process_identifier_filter
516 .map_or(PropertyValue::Null, |id| PropertyValue::Unsigned(id.into()))),
517 PropertyIdentifier::LOCAL_FORWARDING_ONLY => {
518 Ok(PropertyValue::Boolean(self.local_forwarding_only))
519 }
520 PropertyIdentifier::PORT_FILTER => match &self.port_filter {
521 Some(ports) => port_filter::read(ports, array_index),
522 None => Err(common::unknown_property_error()),
523 },
524 _ => Err(common::unknown_property_error()),
525 }
526 }
527
528 fn write_property(
529 &mut self,
530 property: PropertyIdentifier,
531 array_index: Option<u32>,
532 value: PropertyValue,
533 _priority: Option<u8>,
534 ) -> Result<(), Error> {
535 if listed(property) && array_index.is_some() {
536 return Err(common::property_is_not_an_array_error());
537 }
538 match property {
539 PropertyIdentifier::RECIPIENT_LIST | PropertyIdentifier::SUBSCRIBED_RECIPIENTS => {
540 return self.write_list(property, value);
541 }
542 PropertyIdentifier::PROCESS_IDENTIFIER_FILTER if array_index.is_none() => {
543 self.process_identifier_filter = match value {
544 PropertyValue::Null => None,
545 PropertyValue::Unsigned(id) => {
546 Some(u32::try_from(id).map_err(|_| common::value_out_of_range_error())?)
547 }
548 _ => return Err(common::invalid_data_type_error()),
549 };
550 return Ok(());
551 }
552 PropertyIdentifier::LOCAL_FORWARDING_ONLY if array_index.is_none() => {
553 self.local_forwarding_only = read_bool(&value)?;
554 return Ok(());
555 }
556 PropertyIdentifier::PORT_FILTER => {
557 if let Some(ports) = &mut self.port_filter {
558 return port_filter::write(ports, array_index, value);
559 }
560 }
561 _ => {}
562 }
563 if array_index.is_none() {
564 if let Some(result) =
565 common::write_out_of_service(&mut self.out_of_service, property, &value)
566 {
567 return result;
568 }
569 if let Some(result) = common::write_description(&mut self.description, property, &value)
570 {
571 return result;
572 }
573 }
574 Err(common::unhandled_write_error(
575 self.property_metadata().as_ref(),
576 property,
577 array_index,
578 ))
579 }
580
581 fn property_metadata(&self) -> Cow<'_, [crate::property_metadata::PropertyMetadata]> {
582 metadata::for_object(self)
583 }
584
585 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
586 crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
587 }
588
589 fn advance_time_internal(&mut self, elapsed: Duration) -> bool {
590 let lapsed = self.subscribed_recipients.advance_by(elapsed);
591 self.after_time_passed(lapsed)
592 }
593
594 fn bind_monotonic_clock_internal(&mut self, clock: Option<Arc<MonotonicClock>>) {
595 self.subscribed_recipients.bind_monotonic_clock(clock);
596 self.with_storage(|storage, _| storage.clock_changed());
597 }
598
599 fn advance_monotonic_time_internal(&mut self, now: Duration) -> bool {
600 let lapsed = self.subscribed_recipients.advance_to(now);
601 self.after_time_passed(lapsed)
602 }
603
604 fn next_monotonic_deadline_internal(&self) -> Option<Duration> {
605 self.subscribed_recipients.next_deadline()
606 }
607
608 fn durable_writes_internal(&mut self) -> Option<&mut dyn DurableWrites> {
609 Some(self)
610 }
611}
612
613#[cfg(test)]
614mod tests;