1use core::net::SocketAddrV4;
6
7use bacnet_bip_codec::{BvlcResultCode, Bvll, BvllError};
8use bacnet_time::{Duration, Instant, earliest};
9
10use crate::LocalSubnet;
11
12use super::actions::{Action, Actions};
13use super::config::{ConfigError, Foreign, Link, Mode, Retry};
14use super::counters::{NodeCounters, RegisterAttempt, add, bump};
15use super::exchange::{
16 Early, ExchangeId, ExchangeRefusal, Exchanges, Key, LocalFailure, Operation, Phase,
17};
18use super::receive::{Datagram, Delivery, Discard, ForBbmd, Policy, Received, Response};
19use super::registration::{
20 Admission, AttemptOutcome, BroadcastDecision, HeldVerdict, Lifecycle, RegistrationEvent,
21 RegistrationSnapshot,
22};
23use super::send::{Outgoing, SendRefusal};
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum Inbound<'d> {
29 Deliver(Delivery<'d>),
31 Nak {
33 to: SocketAddrV4,
35 code: BvlcResultCode,
37 malformed: bool,
39 },
40 Bbmd(ForBbmd<'d>),
42 Discarded(Discard),
44 Matched {
48 exchange: ExchangeId,
50 message: Result<Bvll<'d>, BvllError>,
52 accepted: bool,
56 settled: bool,
60 },
61 Duplicate {
64 exchange: ExchangeId,
66 },
67 Unmatched {
70 peer: SocketAddrV4,
72 code: Option<BvlcResultCode>,
74 late: bool,
77 },
78}
79
80#[derive(Debug, Clone)]
94pub struct Node<const X: usize = 4> {
95 link: Link,
96 exchanges: Exchanges<X>,
97 lifecycle: Option<Lifecycle>,
98 actions: Actions<X>,
99 counters: NodeCounters,
100 latest_register: Option<(ExchangeId, RegisterAttempt)>,
102 now: Instant,
103}
104
105impl<const X: usize> Node<X> {
106 pub fn new(link: Link, now: Instant) -> Result<Self, ConfigError> {
109 const { assert!(X >= 1 && X <= 256, "a node needs 1 to 256 exchange slots") };
110 link.validate()?;
111 let lifecycle = match &link.mode {
112 Mode::Foreign(foreign) => Some(Lifecycle::new(foreign, now)),
113 Mode::Normal | Mode::Bbmd => None,
114 };
115 let mut node = Self {
116 link,
117 exchanges: Exchanges::new(),
118 lifecycle,
119 actions: Actions::new(),
120 counters: NodeCounters::default(),
121 latest_register: None,
122 now,
123 };
124 node.advance(now);
125 Ok(node)
126 }
127
128 #[must_use]
130 pub const fn link(&self) -> &Link {
131 &self.link
132 }
133
134 pub fn receive<'d>(
136 &mut self,
137 datagram: &Datagram<'d>,
138 now: Instant,
139 policy: &mut impl Policy,
140 ) -> Inbound<'d> {
141 let now = self.catch_up(now, false);
142 let inbound = match self.link.receive(datagram, now, policy) {
143 Received::Deliver(delivery) => Inbound::Deliver(delivery),
144 Received::Nak {
145 to,
146 code,
147 malformed,
148 } => Inbound::Nak {
149 to,
150 code,
151 malformed,
152 },
153 Received::Bbmd(share) => Inbound::Bbmd(share),
154 Received::Discarded(reason) => Inbound::Discarded(reason),
155 Received::Response(response) => self.match_response(response, now),
156 };
157 self.advance(now);
158 inbound
159 }
160
161 pub fn resolve<'d>(&mut self, response: Response<'d>, now: Instant) -> Inbound<'d> {
172 let now = self.catch_up(now, false);
175 let inbound = self.match_response(response, now);
176 self.advance(now);
177 inbound
178 }
179
180 fn match_response<'d>(&mut self, response: Response<'d>, now: Instant) -> Inbound<'d> {
181 let Response {
182 peer,
183 function,
184 message,
185 accepted,
186 } = response;
187 let code = match message {
188 Ok(Bvll::BvlcResult(code)) => Some(code),
189 _ => None,
190 };
191 let valid = message.is_ok() && accepted;
192 let key = if valid {
193 Key::of(function, code)
194 } else {
195 bump(&mut self.counters.malformed_responses);
196 if let Some(index) = self.exchanges.first_with(peer, function) {
197 let operation = self.exchanges.slot(index).operation;
198 bump(&mut self.counters.exchange_mut(operation).malformed_responses);
199 }
200 Operation::of_ack(function).map(Key::Operation)
202 };
203 let Some(index) = key.and_then(|key| self.exchanges.find(peer, key)) else {
204 if !valid {
205 return Inbound::Unmatched {
206 peer,
207 code: None,
208 late: false,
209 };
210 }
211 return self.unmatched(peer, code, key, now);
212 };
213 let exchange = self.exchanges.id(index);
214 let slot = self.exchanges.slot_mut(index);
215 if slot.phase == Phase::Admitted {
216 if slot.early.is_some() {
217 return Inbound::Duplicate { exchange };
218 }
219 slot.early = Some(Early { code, valid });
220 return Inbound::Matched {
221 exchange,
222 message,
223 accepted: valid,
224 settled: false,
225 };
226 }
227 self.settle(index, code, valid, now);
228 Inbound::Matched {
229 exchange,
230 message,
231 accepted: valid,
232 settled: true,
233 }
234 }
235
236 fn unmatched<'d>(
238 &mut self,
239 peer: SocketAddrV4,
240 code: Option<BvlcResultCode>,
241 key: Option<Key>,
242 now: Instant,
243 ) -> Inbound<'d> {
244 let late = key.is_some_and(|key| self.exchanges.is_late(peer, key, now));
245 bump(&mut self.counters.unmatched_responses);
246 if late {
247 bump(&mut self.counters.late_responses);
248 }
249 if code.is_some() {
250 self.counters.last_unmatched_result = code;
251 }
252 if code == Some(BvlcResultCode::DISTRIBUTE_BROADCAST_TO_NETWORK_NAK)
253 && let (Mode::Foreign(foreign), Some(lifecycle)) =
254 (&self.link.mode, &mut self.lifecycle)
255 && foreign.bbmd == peer
256 && foreign.retry == Retry::Backoff
257 {
258 bump(&mut self.counters.renewal_hints);
259 lifecycle.hint(now);
260 }
261 Inbound::Unmatched { peer, code, late }
262 }
263
264 fn settle(&mut self, index: usize, code: Option<BvlcResultCode>, valid: bool, now: Instant) {
267 let exchange = self.exchanges.id(index);
268 let slot = self.exchanges.retire(index, now);
269 if !valid {
270 return;
271 }
272 let counters = self.counters.exchange_mut(slot.operation);
273 match code {
274 Some(code) => {
275 bump(&mut counters.results);
276 counters.last_result = Some(code);
277 }
278 None => bump(&mut counters.acknowledgements),
279 }
280 let Some(code) = code else { return };
281 if slot.operation != Operation::RegisterForeignDevice {
282 return;
283 }
284 let accepted = code == BvlcResultCode::SUCCESSFUL_COMPLETION;
285 if accepted {
286 bump(&mut self.counters.registrations_accepted);
287 } else {
288 bump(&mut self.counters.registrations_refused);
289 }
290 let outcome = if accepted {
291 AttemptOutcome::Accepted
292 } else {
293 AttemptOutcome::Refused
294 };
295 self.record_register(exchange, outcome, Some(code));
296 if !slot.automatic {
297 return;
298 }
299 let Some(lifecycle) = &mut self.lifecycle else {
300 return;
301 };
302 let (event, verdict) = if accepted {
303 lifecycle.accepted(now)
304 } else {
305 let verdict = lifecycle.refused(now);
306 (Some(RegistrationEvent::Refused { code }), verdict)
307 };
308 if let Some(event) = event {
309 self.actions.event(event);
310 }
311 self.held_verdict(verdict);
312 }
313
314 pub fn admit_broadcast(&mut self, now: Instant) -> Result<Admission, SendRefusal> {
322 let now = self.advance(now);
323 let Some(lifecycle) = &mut self.lifecycle else {
324 return Ok(Admission::Send);
325 };
326 match lifecycle.admit_broadcast(now) {
327 BroadcastDecision::Hold => {
328 bump(&mut self.counters.broadcasts_held);
329 Ok(Admission::Hold)
330 }
331 BroadcastDecision::Send { lapsed } => {
332 if lapsed {
333 bump(&mut self.counters.broadcasts_unregistered);
334 }
335 Ok(Admission::Send)
336 }
337 BroadcastDecision::Full => {
338 bump(&mut self.counters.broadcasts_hold_full);
339 Err(SendRefusal::HoldFull)
340 }
341 }
342 }
343
344 pub fn begin_exchange<'r>(
353 &mut self,
354 peer: SocketAddrV4,
355 request: Bvll<'r>,
356 timeout: Duration,
357 now: Instant,
358 ) -> Result<(ExchangeId, Outgoing<'r>), ExchangeRefusal> {
359 self.advance(now);
360 let operation = Operation::of(request.function()).ok_or(ExchangeRefusal::NotARequest)?;
361 if let Mode::Foreign(foreign) = &self.link.mode
362 && foreign.bbmd == peer
363 && operation.answered_by_success()
364 {
365 return Err(ExchangeRefusal::ManagedRegistration);
366 }
367 let ttl = match request {
368 Bvll::RegisterForeignDevice { ttl } => ttl,
369 _ => 0,
370 };
371 let exchange = self.admit(peer, operation, timeout, false, ttl)?;
372 Ok((
373 exchange,
374 Outgoing {
375 to: peer,
376 message: request,
377 },
378 ))
379 }
380
381 fn admit(
382 &mut self,
383 peer: SocketAddrV4,
384 operation: Operation,
385 timeout: Duration,
386 automatic: bool,
387 ttl: u16,
388 ) -> Result<ExchangeId, ExchangeRefusal> {
389 let exchange = self
390 .exchanges
391 .admit(peer, operation, timeout, automatic, ttl)
392 .inspect_err(|_| bump(&mut self.counters.exchange_mut(operation).busy))?;
393 if operation == Operation::RegisterForeignDevice {
394 bump(&mut self.counters.registration_attempts);
395 let attempt = RegisterAttempt {
396 bbmd: peer,
397 ttl,
398 outcome: AttemptOutcome::Pending,
399 result: None,
400 };
401 self.latest_register = Some((exchange, attempt));
402 self.counters.last_registration_attempt = Some(attempt);
403 }
404 Ok(exchange)
405 }
406
407 pub fn exchange_sent(&mut self, exchange: ExchangeId, now: Instant) -> bool {
411 let now = self.advance(now);
412 let Some(index) = self.exchanges.live(exchange) else {
413 return false;
414 };
415 let slot = self.exchanges.slot_mut(index);
416 if slot.phase != Phase::Admitted {
417 return false;
418 }
419 slot.phase = Phase::Sent {
420 deadline: now + slot.timeout,
421 };
422 let (operation, automatic, early) = (slot.operation, slot.automatic, slot.early.take());
423 bump(&mut self.counters.exchange_mut(operation).sent);
424 if automatic && let Some(lifecycle) = &mut self.lifecycle {
425 lifecycle.sent();
426 }
427 let settled = early.is_some();
428 if let Some(early) = early {
429 self.settle(index, early.code, early.valid, now);
430 }
431 self.advance(now);
432 settled
433 }
434
435 pub fn exchange_failed(&mut self, exchange: ExchangeId, why: LocalFailure, now: Instant) {
438 let now = self.advance(now);
439 let Some(index) = self.exchanges.live(exchange) else {
440 return;
441 };
442 let slot = self.exchanges.retire(index, now);
443 let outcome = match why {
444 LocalFailure::SendFailed => {
445 bump(&mut self.counters.exchange_mut(slot.operation).local_errors);
446 AttemptOutcome::LocalError
447 }
448 LocalFailure::Cancelled => AttemptOutcome::Cancelled,
449 };
450 self.ended_unanswered(exchange, slot.operation, slot.automatic, outcome, now);
451 self.advance(now);
452 }
453
454 fn ended_unanswered(
456 &mut self,
457 exchange: ExchangeId,
458 operation: Operation,
459 automatic: bool,
460 outcome: AttemptOutcome,
461 now: Instant,
462 ) {
463 if operation == Operation::RegisterForeignDevice {
464 self.record_register(exchange, outcome, None);
465 }
466 if !automatic {
467 return;
468 }
469 if let Some(lifecycle) = &mut self.lifecycle {
470 let verdict = lifecycle.failed(now, Some(outcome));
471 self.actions.event(RegistrationEvent::Unanswered);
472 self.held_verdict(verdict);
473 }
474 }
475
476 fn record_register(
479 &mut self,
480 exchange: ExchangeId,
481 outcome: AttemptOutcome,
482 result: Option<BvlcResultCode>,
483 ) {
484 if let Some((latest, attempt)) = &mut self.latest_register
485 && *latest == exchange
486 {
487 attempt.outcome = outcome;
488 attempt.result = result;
489 self.counters.last_registration_attempt = Some(*attempt);
490 }
491 }
492
493 fn held_verdict(&mut self, verdict: Option<HeldVerdict>) {
494 let Some(HeldVerdict { release, count }) = verdict else {
495 return;
496 };
497 let counter = if release {
498 &mut self.counters.broadcasts_released
499 } else {
500 &mut self.counters.broadcasts_discarded
501 };
502 add(counter, count);
503 self.actions.held(release);
504 }
505
506 #[must_use]
510 pub fn next_deadline(&self) -> Option<Instant> {
511 let exchange = self.exchanges.next_deadline().map(|(_, deadline)| deadline);
512 let lifecycle = self.lifecycle.as_ref();
513 earliest([
514 exchange,
515 lifecycle.and_then(Lifecycle::lapse_at),
516 lifecycle.and_then(Lifecycle::due_at),
517 ])
518 }
519
520 pub fn on_deadline(&mut self, now: Instant) {
522 self.advance(now);
523 }
524
525 fn advance(&mut self, now: Instant) -> Instant {
529 self.catch_up(now, true)
530 }
531
532 fn catch_up(&mut self, now: Instant, start_due: bool) -> Instant {
535 let now = now.max(self.now);
536 self.now = now;
537 loop {
538 let timeout = self.exchanges.next_deadline();
539 let lifecycle = self.lifecycle.as_ref();
540 let lapse = lifecycle.and_then(Lifecycle::lapse_at);
541 let due = lifecycle.and_then(Lifecycle::due_at).filter(|_| start_due);
542 let first = earliest([timeout.map(|(_, at)| at), lapse, due]);
543 match first {
544 Some(at) if at <= now => {}
545 _ => return now,
546 }
547 if let Some((index, _)) = timeout.filter(|(_, at)| Some(*at) == first) {
548 self.time_out(index, now);
549 } else if lapse == first {
550 if let Some(lifecycle) = &mut self.lifecycle {
551 lifecycle.lapse();
552 }
553 bump(&mut self.counters.lapses);
554 self.actions.event(RegistrationEvent::Lapsed);
555 } else {
556 self.start_attempt(now);
557 }
558 }
559 }
560
561 fn time_out(&mut self, index: usize, now: Instant) {
562 let exchange = self.exchanges.id(index);
563 let slot = self.exchanges.retire(index, now);
564 bump(&mut self.counters.exchange_mut(slot.operation).timeouts);
565 if !slot.automatic {
566 self.actions.ended(exchange);
567 }
568 self.ended_unanswered(
569 exchange,
570 slot.operation,
571 slot.automatic,
572 AttemptOutcome::TimedOut,
573 now,
574 );
575 }
576
577 fn start_attempt(&mut self, now: Instant) {
579 let Mode::Foreign(foreign) = self.link.mode else {
580 return;
581 };
582 let timeout = foreign.effective_response_timeout();
583 let operation = Operation::RegisterForeignDevice;
584 match self.admit(foreign.bbmd, operation, timeout, true, foreign.ttl) {
585 Ok(exchange) => {
586 if let Some(lifecycle) = &mut self.lifecycle {
587 lifecycle.admitted(now);
588 }
589 self.actions.register(exchange, foreign.bbmd, foreign.ttl);
590 }
591 Err(_) => {
592 if let Some(lifecycle) = &mut self.lifecycle {
593 let verdict = lifecycle.failed(now, None);
594 self.held_verdict(verdict);
595 }
596 }
597 }
598 }
599
600 pub fn retarget(&mut self, foreign: Foreign, now: Instant) -> Result<(), ConfigError> {
615 let Mode::Foreign(old) = self.link.mode else {
616 return Err(ConfigError::NotForeign);
617 };
618 let mut link = self.link;
619 link.mode = Mode::Foreign(foreign);
620 link.validate()?;
621 let now = self.catch_up(now, false);
624 if let Some(index) = self.exchanges.automatic() {
625 let exchange = self.exchanges.id(index);
626 self.exchanges.retire(index, now);
627 self.actions.forget_register(exchange);
628 self.record_register(exchange, AttemptOutcome::Cancelled, None);
629 }
630 if let Some(lifecycle) = &mut self.lifecycle {
631 lifecycle.retarget(&foreign, now, old.bbmd == foreign.bbmd);
632 }
633 self.link = link;
634 self.advance(now);
635 Ok(())
636 }
637
638 pub fn set_subnet(&mut self, subnet: LocalSubnet) -> Result<(), ConfigError> {
646 let mut link = self.link;
647 link.subnet = subnet;
648 link.validate()?;
649 self.link = link;
650 Ok(())
651 }
652
653 pub fn poll_action(&mut self) -> Action {
655 self.actions.poll()
656 }
657
658 #[must_use]
661 pub fn registration(&self, now: Instant) -> Option<RegistrationSnapshot> {
662 self.lifecycle
663 .as_ref()
664 .map(|lifecycle| lifecycle.snapshot(now.max(self.now)))
665 }
666
667 #[must_use = "the counts are gone once taken; add them to your totals"]
671 pub fn take_counters(&mut self) -> NodeCounters {
672 core::mem::take(&mut self.counters)
673 }
674
675 #[must_use]
680 pub const fn counters(&self) -> NodeCounters {
681 self.counters
682 }
683}