bacnet_client/client/cov_target.rs
1//! [`CovTarget`]: the device a managed COV subscription goes to (#1790).
2
3use super::*;
4
5/// The device a managed COV subscription goes to: on this network at a MAC,
6/// or on another network behind a router.
7///
8/// A device keeps a subscription under the address the request came from
9/// (Clause 13.14.2), and its notifications carry that subscription's process
10/// identifier back from the device's own address (Clauses 13.6.1 and
11/// 13.7.1). So a notification belongs to this target when it comes from the
12/// same address: the MAC with no source network for a local device, or the
13/// source network and station address for a routed one. The router a
14/// routed notification came through is not part of the device's address and
15/// isn't compared.
16#[derive(Debug, Clone, PartialEq, Eq, Hash)]
17#[non_exhaustive]
18pub enum CovTarget {
19 /// A device on this network.
20 Local {
21 /// Its MAC address.
22 mac: MacAddr,
23 },
24 /// A device on another network, reached through a router on this one.
25 Routed {
26 /// The MAC of the router the requests go to.
27 router_mac: MacAddr,
28 /// The device's network number, 1 to 65534.
29 network: u16,
30 /// The device's MAC address on that network.
31 address: MacAddr,
32 },
33}
34
35impl CovTarget {
36 /// A device on this network at `mac`.
37 pub fn local(mac: &[u8]) -> Self {
38 Self::Local {
39 mac: MacAddr::from_slice(mac),
40 }
41 }
42
43 /// A device at `address` on `network`, reached through the router at
44 /// `router_mac`.
45 pub fn routed(router_mac: &[u8], network: u16, address: &[u8]) -> Self {
46 Self::Routed {
47 router_mac: MacAddr::from_slice(router_mac),
48 network,
49 address: MacAddr::from_slice(address),
50 }
51 }
52
53 /// Where a discovered device is: routed when its I-Am came with a source
54 /// network and address (through the router it came from), local
55 /// otherwise.
56 pub fn of_device(device: &DiscoveredDevice) -> Self {
57 match (device.source_network, &device.source_address) {
58 (Some(network), Some(address)) => Self::Routed {
59 router_mac: device.mac_address.clone(),
60 network,
61 address: address.clone(),
62 },
63 _ => Self::Local {
64 mac: device.mac_address.clone(),
65 },
66 }
67 }
68
69 pub(super) fn confirmed(&self) -> ConfirmedTarget<'_> {
70 match self {
71 Self::Local { mac } => ConfirmedTarget::Local { mac },
72 Self::Routed {
73 router_mac,
74 network,
75 address,
76 } => ConfirmedTarget::Routed {
77 router_mac,
78 dest_network: *network,
79 dest_mac: address,
80 },
81 }
82 }
83
84 /// True when `received` came from this device's address. A routed
85 /// target on the client's own network (`local_network`, once learned)
86 /// is sent to as a local device (#1358), so its notifications may come
87 /// with no source network, from its address as the MAC.
88 pub(super) fn sent(
89 &self,
90 received: &ReceivedCOVNotification,
91 local_network: Option<u16>,
92 ) -> bool {
93 let local_from = |mac: &MacAddr| {
94 received.source_network.is_none()
95 && received.source_address.is_none()
96 && received.source_mac == *mac
97 };
98 match self {
99 Self::Local { mac } => local_from(mac),
100 Self::Routed {
101 network, address, ..
102 } => {
103 (received.source_network == Some(*network)
104 && received.source_address.as_ref() == Some(address))
105 || (local_network == Some(*network) && local_from(address))
106 }
107 }
108 }
109}