use std::sync::Arc;
use std::sync::atomic::AtomicU64;
use std::task::{Context, Poll, ready};
use bacnet_router_core::node::Node;
use bacnet_transport::port::ReceivedNpdu;
use bacnet_transport::warn_throttle::WarnThrottle;
use std::sync::{Mutex, MutexGuard, PoisonError};
use tokio::sync::mpsc;
use tracing::debug;
use super::receive::{self, Inbound, NodeTotals};
use super::{AdmissionSender, ReceivedApdu, ReceivedNetworkControl, next_ingress_sequence};
pub(crate) struct Ingress {
node: Node,
totals: Arc<NodeTotals>,
drop_warning: WarnThrottle,
controls: Option<AdmissionSender<ReceivedNetworkControl>>,
control_sequence: Arc<AtomicU64>,
}
impl std::fmt::Debug for Ingress {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Ingress")
.field("controls", &self.controls.is_some())
.finish_non_exhaustive()
}
}
impl Ingress {
pub(crate) fn new(
totals: Arc<NodeTotals>,
controls: Option<AdmissionSender<ReceivedNetworkControl>>,
control_sequence: Arc<AtomicU64>,
) -> Self {
Self {
node: Node::new(),
totals,
drop_warning: WarnThrottle::default(),
controls,
control_sequence,
}
}
pub(crate) fn take(&mut self, mut received: ReceivedNpdu) -> Option<ReceivedApdu> {
let frame = std::mem::take(&mut received.npdu);
let inbound = receive::admit(
&mut self.node,
&self.totals,
&self.drop_warning,
&received.source_mac,
received.link_layer_group,
frame,
self.controls.is_some(),
);
match inbound {
Inbound::Apdu {
apdu,
source_network,
is_group,
global_broadcast,
} => Some(ReceivedApdu {
router_response: None,
direct_response: received.direct_response,
apdu,
source_mac: received.source_mac,
ingress_network: None,
source_network,
link_layer_group: received.link_layer_group,
is_group,
global_broadcast,
data_attributes: received.data_attributes,
provenance: received.provenance,
reply_tx: received.reply_tx,
}),
Inbound::Control(Some(npdu)) => {
let tx = self.controls.as_ref()?;
let control = ReceivedNetworkControl {
npdu,
source_mac: received.source_mac,
link_layer_group: received.link_layer_group,
data_attributes: received.data_attributes,
provenance: received.provenance,
ingress_sequence: next_ingress_sequence(&self.control_sequence),
};
if let Err(mpsc::error::TrySendError::Closed(_)) = tx.try_send(control) {
self.controls = None;
debug!("Network-control receiver closed; resuming discard behavior");
}
None
}
Inbound::Control(None) | Inbound::Dropped => None,
}
}
}
pub(crate) type SharedNpduQueue = Arc<Mutex<mpsc::Receiver<ReceivedNpdu>>>;
#[derive(Debug)]
pub struct ApduReceiver {
npdu_rx: SharedNpduQueue,
ingress: Ingress,
}
impl ApduReceiver {
pub(crate) fn new(npdu_rx: SharedNpduQueue, ingress: Ingress) -> Self {
Self { npdu_rx, ingress }
}
fn queue(&self) -> MutexGuard<'_, mpsc::Receiver<ReceivedNpdu>> {
self.npdu_rx.lock().unwrap_or_else(PoisonError::into_inner)
}
pub async fn recv(&mut self) -> Option<ReceivedApdu> {
std::future::poll_fn(|cx| self.poll_recv(cx)).await
}
pub fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<ReceivedApdu>> {
let mut queue = self.npdu_rx.lock().unwrap_or_else(PoisonError::into_inner);
loop {
let Some(received) = ready!(queue.poll_recv(cx)) else {
return Poll::Ready(None);
};
if let Some(apdu) = self.ingress.take(received) {
return Poll::Ready(Some(apdu));
}
}
}
pub fn try_recv(&mut self) -> Result<ReceivedApdu, mpsc::error::TryRecvError> {
let mut queue = self.npdu_rx.lock().unwrap_or_else(PoisonError::into_inner);
loop {
let received = queue.try_recv()?;
if let Some(apdu) = self.ingress.take(received) {
return Ok(apdu);
}
}
}
pub fn close(&mut self) {
self.queue().close();
}
pub fn is_closed(&self) -> bool {
self.queue().is_closed()
}
}
pub(crate) fn close_queue(queue: &std::sync::Weak<Mutex<mpsc::Receiver<ReceivedNpdu>>>) {
if let Some(queue) = queue.upgrade() {
queue.lock().unwrap_or_else(PoisonError::into_inner).close();
}
}