use core::net::SocketAddrV4;
use bacnet_bip_codec::Bvll;
use super::exchange::ExchangeId;
use super::registration::RegistrationEvent;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Action {
Idle,
Register {
exchange: ExchangeId,
to: SocketAddrV4,
ttl: u16,
},
ExchangeEnded {
exchange: ExchangeId,
},
ReleaseHeld,
DiscardHeld,
Registration(RegistrationEvent),
}
impl Action {
#[must_use]
pub const fn message(&self) -> Option<Bvll<'static>> {
match *self {
Self::Register { ttl, .. } => Some(Bvll::RegisterForeignDevice { ttl }),
_ => None,
}
}
}
const EVENTS: usize = 3;
#[derive(Debug, Clone)]
pub(crate) struct Actions<const X: usize> {
ended: [Option<u32>; X],
events: [Option<RegistrationEvent>; EVENTS],
held: Option<bool>,
register: Option<(ExchangeId, SocketAddrV4, u16)>,
}
impl<const X: usize> Actions<X> {
pub(crate) const fn new() -> Self {
Self {
ended: [None; X],
events: [None; EVENTS],
held: None,
register: None,
}
}
pub(crate) fn ended(&mut self, exchange: ExchangeId) {
self.ended[exchange.slot()] = Some(exchange.generation());
}
pub(crate) fn event(&mut self, event: RegistrationEvent) {
match self.events.iter_mut().find(|slot| slot.is_none()) {
Some(slot) => *slot = Some(event),
None => self.events[EVENTS - 1] = Some(event),
}
}
pub(crate) fn held(&mut self, release: bool) {
self.held = Some(release);
}
pub(crate) fn register(&mut self, exchange: ExchangeId, to: SocketAddrV4, ttl: u16) {
self.register = Some((exchange, to, ttl));
}
pub(crate) fn poll(&mut self) -> Action {
for (slot, ended) in self.ended.iter_mut().enumerate() {
if let Some(generation) = ended.take() {
return Action::ExchangeEnded {
exchange: ExchangeId::new(slot, generation),
};
}
}
if let Some(event) = self.events[0].take() {
for index in 1..EVENTS {
self.events.swap(index - 1, index);
}
return Action::Registration(event);
}
if let Some(release) = self.held.take() {
return if release {
Action::ReleaseHeld
} else {
Action::DiscardHeld
};
}
match self.register.take() {
Some((exchange, to, ttl)) => Action::Register { exchange, to, ttl },
None => Action::Idle,
}
}
}
#[cfg(test)]
mod tests {
use bacnet_time::Instant;
use super::{Action, Actions, RegistrationEvent, EVENTS};
const fn renewed(secs: u64) -> RegistrationEvent {
RegistrationEvent::Renewed {
until: Instant::from_secs(secs),
}
}
fn polled(actions: &mut Actions<1>) -> [Option<RegistrationEvent>; EVENTS + 1] {
let mut out = [None; EVENTS + 1];
for slot in &mut out {
match actions.poll() {
Action::Registration(event) => *slot = Some(event),
Action::Idle => break,
other => panic!("{other:?}"),
}
}
out
}
#[test]
fn events_are_polled_in_order() {
let mut actions = Actions::<1>::new();
for secs in 1..=3 {
actions.event(renewed(secs));
}
assert_eq!(
polled(&mut actions),
[Some(renewed(1)), Some(renewed(2)), Some(renewed(3)), None]
);
actions.event(RegistrationEvent::Lapsed);
actions.event(RegistrationEvent::Unanswered);
let next = [RegistrationEvent::Lapsed, RegistrationEvent::Unanswered];
assert_eq!(
polled(&mut actions),
[Some(next[0]), Some(next[1]), None, None]
);
}
#[test]
fn the_newest_event_replaces_the_last_when_the_list_is_full() {
let mut actions = Actions::<1>::new();
for secs in 1..=4 {
actions.event(renewed(secs));
}
assert_eq!(
polled(&mut actions),
[Some(renewed(1)), Some(renewed(2)), Some(renewed(4)), None]
);
}
}