use core::fmt;
use bacnet_mstp_core::codec::BROADCAST_MAC;
use bacnet_mstp_core::{
CoreError, MasterCore, MstpCoreConfig, NetworkPriority, QueueRefusal, QueueStorage,
ReplyDecision, RequestId,
};
use crate::router::{Dest, Group, Limits, PortConfig, Send, SendRefusal};
use crate::{Arrived, Instant, Mac, PortId};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NotExtended;
impl fmt::Display for NotExtended {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("a router's MS/TP port needs extended frames")
}
}
impl core::error::Error for NotExtended {}
#[must_use]
pub fn group(mac: &[u8]) -> Group {
if mac == [BROADCAST_MAC] {
Group::Broadcast
} else {
Group::No
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MstpPort {
port: PortId,
}
impl MstpPort {
#[must_use]
pub const fn new(port: PortId) -> Self {
Self { port }
}
#[must_use]
pub const fn port(&self) -> PortId {
self.port
}
pub const fn config(config: &MstpCoreConfig, network: u16) -> Result<PortConfig, NotExtended> {
if !config.extended_frames {
return Err(NotExtended);
}
Ok(PortConfig {
network,
mac: Mac::from_array([config.station]),
max_npdu: Limits::both(config.max_npdu() as u16),
})
}
#[must_use]
pub fn arrived<'d>(&self, source: u8, destination: u8, npdu: &'d [u8]) -> Arrived<'d> {
Arrived {
port: self.port,
source: Mac::from_array([source]),
link_group: destination == BROADCAST_MAC,
npdu,
}
}
pub fn release<Q: QueueStorage, const NPDU: usize>(
core: &mut MasterCore<'_, Q, NPDU>,
request: Option<RequestId>,
now: Instant,
) -> Result<(), CoreError> {
let Some(request) = request else {
return Ok(());
};
match core.complete_request(request, ReplyDecision::Postponed, now) {
Err(CoreError::NoPendingRequest | CoreError::StaleRequest) => Ok(()),
result => result,
}
}
pub fn send<Q: QueueStorage, const NPDU: usize>(
&self,
core: &mut MasterCore<'_, Q, NPDU>,
send: &Send<'_>,
now: Instant,
) -> Result<(), SendRefusal> {
if send.port != self.port {
return Err(SendRefusal::Other);
}
let destination = match send.to {
Dest::Broadcast => BROADCAST_MAC,
Dest::Unicast(&[station]) if station != BROADCAST_MAC => station,
Dest::Unicast(_) => return Err(SendRefusal::Other),
};
let priority = NetworkPriority::try_from(send.priority).map_err(|_| SendRefusal::Other)?;
core.enqueue_npdu(destination, send.npdu, priority, now)
.map_err(|why| match why {
QueueRefusal::Full => SendRefusal::Full,
QueueRefusal::Oversize => SendRefusal::TooLong,
_ => SendRefusal::Other,
})
}
}