use alloc::sync::Arc;
use alloc::vec::Vec;
use core::fmt;
use crate::constructed::{BACnetBDTEntry, BACnetFDTEntry, BACnetHostNPort};
use crate::enums::{ErrorClass, ErrorCode, IPMode};
use crate::error::Error;
pub trait BbmdTables: Send + Sync {
fn broadcast_distribution_table(&self) -> Vec<BACnetBDTEntry>;
fn accepts_foreign_device_registrations(&self) -> bool;
fn foreign_device_table(&self) -> Vec<BACnetFDTEntry>;
fn check_broadcast_distribution_table(&self, table: &[BACnetBDTEntry]) -> Result<(), Error> {
let _ = table;
Err(write_access_denied())
}
fn nat_traversal(&self) -> Option<bool> {
None
}
fn global_address(&self) -> Option<BACnetHostNPort> {
None
}
fn check_value(&self, changes: &BbmdChanges) -> Result<(), Error> {
self.check(changes)
}
fn check(&self, changes: &BbmdChanges) -> Result<(), Error> {
if changes.nat_traversal.is_some()
|| changes.global_address.is_some()
|| changes.multicast_group.is_some()
{
return Err(write_access_denied());
}
match &changes.broadcast_distribution_table {
Some(table) => self.check_broadcast_distribution_table(table),
None => Ok(()),
}
}
fn activate(&self, changes: &BbmdChanges) -> Result<(), Error> {
let _ = changes;
Err(write_access_denied())
}
fn can_change(&self) -> bool {
false
}
fn changes_multicast_group(&self) -> bool {
false
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct BbmdChanges {
pub broadcast_distribution_table: Option<Vec<BACnetBDTEntry>>,
pub accept_foreign_device_registrations: Option<bool>,
pub nat_traversal: Option<bool>,
pub global_address: Option<BACnetHostNPort>,
pub multicast_group: Option<[u8; 4]>,
}
impl BbmdChanges {
pub fn is_empty(&self) -> bool {
self.broadcast_distribution_table.is_none()
&& self.accept_foreign_device_registrations.is_none()
&& self.nat_traversal.is_none()
&& self.global_address.is_none()
&& self.multicast_group.is_none()
}
}
pub trait ForeignRegistration: Send + Sync {
fn bbmd(&self) -> BACnetHostNPort;
fn subscription_lifetime(&self) -> u16;
fn check(&self, changes: &ForeignDeviceChanges) -> Result<(), Error> {
let _ = changes;
Err(write_access_denied())
}
fn activate(&self, changes: &ForeignDeviceChanges) -> Result<(), Error> {
let _ = changes;
Err(write_access_denied())
}
fn can_change(&self) -> bool {
false
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FixedForeignRegistration {
pub bbmd: BACnetHostNPort,
pub subscription_lifetime: u16,
}
impl ForeignRegistration for FixedForeignRegistration {
fn bbmd(&self) -> BACnetHostNPort {
self.bbmd.clone()
}
fn subscription_lifetime(&self) -> u16 {
self.subscription_lifetime
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct ForeignDeviceChanges {
pub bbmd: Option<BACnetHostNPort>,
pub subscription_lifetime: Option<u16>,
}
impl ForeignDeviceChanges {
pub fn is_empty(&self) -> bool {
self.bbmd.is_none() && self.subscription_lifetime.is_none()
}
}
pub trait IpMulticast: Send + Sync {
fn group(&self) -> [u8; 4];
fn check(&self, group: [u8; 4]) -> Result<(), Error> {
let _ = group;
Err(write_access_denied())
}
fn activate(&self, group: [u8; 4]) -> Result<(), Error> {
let _ = group;
Err(write_access_denied())
}
fn can_change(&self) -> bool {
false
}
}
fn write_access_denied() -> Error {
Error::protocol(ErrorClass::PROPERTY, ErrorCode::WRITE_ACCESS_DENIED)
}
#[derive(Clone)]
pub enum BipPortMode {
Normal,
Foreign {
registration: Arc<dyn ForeignRegistration>,
},
Bbmd {
tables: Option<Arc<dyn BbmdTables>>,
},
}
impl BipPortMode {
pub fn fixed_foreign(bbmd: BACnetHostNPort, subscription_lifetime: u16) -> Self {
Self::Foreign {
registration: Arc::new(FixedForeignRegistration {
bbmd,
subscription_lifetime,
}),
}
}
pub fn ip_mode(&self) -> IPMode {
match self {
Self::Normal => IPMode::NORMAL,
Self::Foreign { .. } => IPMode::FOREIGN,
Self::Bbmd { .. } => IPMode::BBMD,
}
}
}
impl fmt::Debug for BipPortMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Normal => f.write_str("Normal"),
Self::Foreign { registration } => f
.debug_struct("Foreign")
.field("bbmd", ®istration.bbmd())
.field(
"subscription_lifetime",
®istration.subscription_lifetime(),
)
.finish(),
Self::Bbmd { tables } => f
.debug_struct("Bbmd")
.field("started", &tables.is_some())
.finish(),
}
}
}