use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicU16, Ordering};
use bacnet_npdu::NpduLimits;
use bacnet_transport::port::{ReceivedNpdu, TransportPort};
use bacnet_types::MacAddr;
use bacnet_types::error::Error;
use bytes::Bytes;
use tokio::sync::{mpsc, watch};
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) struct Sent {
pub(super) npdu: Vec<u8>,
pub(super) to: Option<Vec<u8>>,
pub(super) taken: bool,
}
pub(super) struct Handle {
pub(super) frames: mpsc::Sender<ReceivedNpdu>,
pub(super) sent: mpsc::UnboundedReceiver<Sent>,
pub(super) limit: Arc<AtomicU16>,
pub(super) down: Arc<AtomicBool>,
pub(super) started: Arc<AtomicBool>,
}
impl Handle {
pub(super) async fn receive(&self, npdu: Vec<u8>, source: &[u8]) {
let frame = ReceivedNpdu::unverified(
Bytes::from(npdu),
MacAddr::from_slice(source),
false,
Vec::new(),
None,
);
self.frames.send(frame).await.unwrap();
}
}
pub(super) struct Driven {
incoming: Option<mpsc::Receiver<ReceivedNpdu>>,
mac: [u8; 1],
limit: Arc<AtomicU16>,
down: Arc<AtomicBool>,
started: Arc<AtomicBool>,
changes: Option<watch::Receiver<NpduLimits>>,
sent: mpsc::UnboundedSender<Sent>,
}
impl Driven {
pub(super) fn new(mac: u8, limit: u16) -> (Self, Handle) {
let (frames, incoming) = mpsc::channel(16);
let (sent, sent_rx) = mpsc::unbounded_channel();
let limit = Arc::new(AtomicU16::new(limit));
let down = Arc::new(AtomicBool::new(false));
let started = Arc::new(AtomicBool::new(false));
let port = Self {
incoming: Some(incoming),
mac: [mac],
limit: Arc::clone(&limit),
down: Arc::clone(&down),
started: Arc::clone(&started),
changes: None,
sent,
};
let handle = Handle {
frames,
sent: sent_rx,
limit,
down,
started,
};
(port, handle)
}
pub(super) fn publishing(mut self, changes: watch::Receiver<NpduLimits>) -> Self {
self.changes = Some(changes);
self
}
fn send(&self, npdu: &[u8], to: Option<&[u8]>) -> Result<(), Error> {
let refusal = if self.down.load(Ordering::SeqCst) {
Some(Error::Encoding("the port is down".into()))
} else if npdu.len() > usize::from(self.limit.load(Ordering::SeqCst)) {
Some(Error::SendTooLong {
length: npdu.len(),
limit: usize::from(self.limit.load(Ordering::SeqCst)),
})
} else {
None
};
let sent = Sent {
npdu: npdu.to_vec(),
to: to.map(<[u8]>::to_vec),
taken: refusal.is_none(),
};
let _ = self.sent.send(sent);
refusal.map_or(Ok(()), Err)
}
}
impl TransportPort for Driven {
async fn start(&mut self) -> Result<mpsc::Receiver<ReceivedNpdu>, Error> {
self.started.store(true, Ordering::SeqCst);
self.incoming
.take()
.ok_or_else(|| Error::Encoding("already started".into()))
}
async fn stop(&mut self) -> Result<(), Error> {
Ok(())
}
async fn send_unicast(&self, npdu: &[u8], mac: &[u8]) -> Result<(), Error> {
self.send(npdu, Some(mac))
}
async fn send_broadcast(&self, npdu: &[u8]) -> Result<(), Error> {
self.send(npdu, None)
}
fn local_mac(&self) -> &[u8] {
&self.mac
}
fn local_receive_apdu_capacity(&self) -> u16 {
1476
}
fn max_npdu(&self) -> NpduLimits {
NpduLimits::both(self.limit.load(Ordering::SeqCst))
}
fn max_npdu_changes(&self) -> Option<watch::Receiver<NpduLimits>> {
self.changes.clone()
}
}