use core::fmt;
use core::net::SocketAddrV4;
use bacnet_bip_codec::{BvlcResultCode, Bvll, FdtEntry};
use bacnet_time::Instant;
use super::fdt::{RegisterOutcome, RegistrationLimits, Reservation, StorageFull};
use super::forward::Tables;
use crate::node::Permissive;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Request {
Register {
sender: SocketAddrV4,
ttl: u16,
},
ReadBdt {
sender: SocketAddrV4,
},
ReadFdt {
sender: SocketAddrV4,
},
DeleteFdtEntry {
sender: SocketAddrV4,
entry: SocketAddrV4,
},
}
impl Request {
#[must_use]
pub const fn sender(&self) -> SocketAddrV4 {
match *self {
Self::Register { sender, .. }
| Self::ReadBdt { sender }
| Self::ReadFdt { sender }
| Self::DeleteFdtEntry { sender, .. } => sender,
}
}
}
pub trait Rules {
fn registration_limits(&mut self) -> RegistrationLimits {
RegistrationLimits::DEFAULT
}
fn reservation(&mut self, source: SocketAddrV4) -> Reservation {
let _ = source;
Reservation::Ordinary
}
fn admit_registration(&mut self, source: SocketAddrV4, now: Instant) -> bool {
let _ = (source, now);
true
}
fn admit_delete(&mut self, sender: SocketAddrV4, entry: SocketAddrV4) -> bool {
let _ = (sender, entry);
true
}
}
impl Rules for Permissive {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum DeleteRefusal {
NotRegistered,
NotAdmitted,
}
impl DeleteRefusal {
#[must_use]
pub const fn result_code(self) -> BvlcResultCode {
BvlcResultCode::DELETE_FOREIGN_DEVICE_TABLE_ENTRY_NAK
}
}
impl fmt::Display for DeleteRefusal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::NotRegistered => "no live registration for that entry",
Self::NotAdmitted => "deletion not admitted",
})
}
}
impl core::error::Error for DeleteRefusal {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Served {
Register(RegisterOutcome),
ReadBdt,
ReadFdt(Result<usize, StorageFull>),
DeleteFdtEntry(Result<(), DeleteRefusal>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct Answer<'t> {
pub to: SocketAddrV4,
pub message: Bvll<'t>,
pub served: Served,
}
const fn result(to: SocketAddrV4, code: BvlcResultCode, served: Served) -> Answer<'static> {
Answer {
to,
message: Bvll::BvlcResult(code),
served,
}
}
impl<const B: usize, const F: usize> Tables<B, F> {
pub fn serve<'t>(
&'t mut self,
request: Request,
now: Instant,
rules: &mut impl Rules,
rows: &'t mut [FdtEntry],
) -> Answer<'t> {
match request {
Request::Register { sender, ttl } => {
let limits = rules.registration_limits();
let reservation = rules.reservation(sender);
let outcome = self
.fdt
.register(sender, ttl, now, &limits, reservation, || {
rules.admit_registration(sender, now)
});
let code = match outcome.result {
Ok(_) => BvlcResultCode::SUCCESSFUL_COMPLETION,
Err(refusal) => refusal.result_code(),
};
result(sender, code, Served::Register(outcome))
}
Request::ReadBdt { sender } => {
let tables: &'t Self = self;
Answer {
to: sender,
message: Bvll::ReadBroadcastDistributionTableAck(tables.bdt.entries()),
served: Served::ReadBdt,
}
}
Request::ReadFdt { sender } => match self.fdt.wire_entries(now, rows) {
Ok(entries) => Answer {
to: sender,
message: Bvll::ReadForeignDeviceTableAck(entries),
served: Served::ReadFdt(Ok(entries.len())),
},
Err(full) => result(
sender,
BvlcResultCode::READ_FOREIGN_DEVICE_TABLE_NAK,
Served::ReadFdt(Err(full)),
),
},
Request::DeleteFdtEntry { sender, entry } => {
let deleted = if rules.admit_delete(sender, entry) {
let live = self.fdt.is_registered_at(entry, now);
let _ = self.fdt.delete(entry);
if live {
Ok(())
} else {
Err(DeleteRefusal::NotRegistered)
}
} else {
Err(DeleteRefusal::NotAdmitted)
};
let code = match deleted {
Ok(()) => BvlcResultCode::SUCCESSFUL_COMPLETION,
Err(refusal) => refusal.result_code(),
};
result(sender, code, Served::DeleteFdtEntry(deleted))
}
}
}
}