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