prns_config/plan/interface/
discovery.rs1use prns_core::interface_discovery::{StampCost, DEFAULT_STAMP_COST};
2use prns_core::interfaces::tcp::TcpWireFraming;
3use prns_core::units::DurationMillis;
4
5use super::medium::PlannedMedium;
6use crate::reference::keys::interface as interface_key;
7use crate::reference::{ReferenceConfigParams, ReferenceInterface};
8
9#[derive(Debug, Clone, PartialEq)]
10pub enum InterfaceDiscoveryPlan {
11 Disabled,
12 Announce(DiscoveryAnnouncementPlan),
13 Unpublishable(DiscoveryPublicationProblem),
14}
15
16#[derive(Debug, Clone, PartialEq)]
17pub struct DiscoveryAnnouncementPlan {
18 pub interval: DurationMillis,
19 pub stamp_cost: StampCost,
20 pub name: Option<String>,
21 pub encryption: DiscoveryEncryption,
22 pub ifac: DiscoveryIfacPublication,
23 pub location: DiscoveryLocationPlan,
24 pub advertisement: DiscoveryAdvertisementPlan,
25}
26
27#[derive(Debug, Clone, PartialEq, Eq)]
28pub enum DiscoveryAdvertisementPlan {
29 Backbone {
30 reachable_on: String,
31 port: u16,
32 },
33 TcpServer {
34 reachable_on: String,
35 port: u16,
36 },
37 RNode {
38 frequency_hz: u64,
39 bandwidth_hz: u32,
40 spreading_factor: u8,
41 coding_rate: u8,
42 },
43 Kiss {
44 frequency_hz: u64,
45 bandwidth_hz: u32,
46 modulation: String,
47 },
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51pub enum DiscoveryPublicationProblem {
52 UnsupportedInterfaceType,
53 MissingRequiredSetting { key: &'static str },
54 IncompatibleSetting { key: &'static str },
55}
56
57#[derive(Debug, Clone, PartialEq)]
58pub enum DiscoveryEncryption {
59 Plaintext,
60 NetworkIdentity,
61}
62
63#[derive(Debug, Clone, PartialEq)]
64pub enum DiscoveryIfacPublication {
65 Omit,
66 Include,
67}
68
69#[derive(Debug, Clone, PartialEq)]
70pub struct DiscoveryLocationPlan {
71 pub latitude: Option<f64>,
72 pub longitude: Option<f64>,
73 pub height: Option<f64>,
74}
75
76pub(in crate::plan) fn plan_interface_discovery(
77 interface: &ReferenceInterface,
78 medium: &PlannedMedium,
79) -> InterfaceDiscoveryPlan {
80 if interface.discovery.discoverable != Some(true) {
81 return InterfaceDiscoveryPlan::Disabled;
82 }
83 let advertisement = match plan_discovery_advertisement(interface, medium) {
84 Ok(advertisement) => advertisement,
85 Err(problem) => return InterfaceDiscoveryPlan::Unpublishable(problem),
86 };
87 let minutes = interface
88 .discovery
89 .announce_interval_minutes
90 .unwrap_or(6 * 60)
91 .max(5) as u64;
92 InterfaceDiscoveryPlan::Announce(DiscoveryAnnouncementPlan {
93 interval: DurationMillis(minutes.saturating_mul(60 * 1_000)),
94 stamp_cost: interface.discovery.stamp_cost.unwrap_or(DEFAULT_STAMP_COST),
95 name: interface.discovery.name.clone(),
96 encryption: if interface.discovery.encrypt == Some(true) {
97 DiscoveryEncryption::NetworkIdentity
98 } else {
99 DiscoveryEncryption::Plaintext
100 },
101 ifac: if interface.discovery.publish_ifac == Some(true) {
102 DiscoveryIfacPublication::Include
103 } else {
104 DiscoveryIfacPublication::Omit
105 },
106 location: DiscoveryLocationPlan {
107 latitude: interface.discovery.latitude,
108 longitude: interface.discovery.longitude,
109 height: interface.discovery.height,
110 },
111 advertisement,
112 })
113}
114
115fn plan_discovery_advertisement(
116 interface: &ReferenceInterface,
117 medium: &PlannedMedium,
118) -> Result<DiscoveryAdvertisementPlan, DiscoveryPublicationProblem> {
119 let reachable_on = || {
120 interface.discovery.reachable_on.clone().ok_or(
121 DiscoveryPublicationProblem::MissingRequiredSetting {
122 key: interface_key::REACHABLE_ON,
123 },
124 )
125 };
126 let kiss = || {
127 Ok(DiscoveryAdvertisementPlan::Kiss {
128 frequency_hz: interface.discovery.frequency_hz.ok_or(
129 DiscoveryPublicationProblem::MissingRequiredSetting {
130 key: interface_key::DISCOVERY_FREQUENCY,
131 },
132 )?,
133 bandwidth_hz: interface.discovery.bandwidth_hz.ok_or(
134 DiscoveryPublicationProblem::MissingRequiredSetting {
135 key: interface_key::DISCOVERY_BANDWIDTH,
136 },
137 )?,
138 modulation: interface.discovery.modulation.clone().ok_or(
139 DiscoveryPublicationProblem::MissingRequiredSetting {
140 key: interface_key::DISCOVERY_MODULATION,
141 },
142 )?,
143 })
144 };
145 match (medium, &interface.params) {
146 (
147 PlannedMedium::Backbone { .. },
148 ReferenceConfigParams::Backbone {
149 listen_port, port, ..
150 },
151 ) => Ok(DiscoveryAdvertisementPlan::Backbone {
152 reachable_on: reachable_on()?,
153 port: interface
154 .discovery
155 .reachable_port
156 .or(*port)
157 .or(*listen_port)
158 .ok_or(DiscoveryPublicationProblem::MissingRequiredSetting {
159 key: interface_key::LISTEN_PORT,
160 })?,
161 }),
162 (
163 PlannedMedium::TcpServer { .. },
164 ReferenceConfigParams::TcpServer {
165 listen_port, port, ..
166 },
167 ) => Ok(DiscoveryAdvertisementPlan::TcpServer {
168 reachable_on: reachable_on()?,
169 port: interface
170 .discovery
171 .reachable_port
172 .or(*port)
173 .or(*listen_port)
174 .ok_or(DiscoveryPublicationProblem::MissingRequiredSetting {
175 key: interface_key::LISTEN_PORT,
176 })?,
177 }),
178 (
179 PlannedMedium::Rnode {
180 frequency_hz,
181 bandwidth_hz,
182 spreading_factor,
183 coding_rate,
184 ..
185 },
186 ReferenceConfigParams::Rnode { .. },
187 ) => Ok(rnode_discovery_advertisement(
188 *frequency_hz,
189 *bandwidth_hz,
190 *spreading_factor,
191 *coding_rate,
192 )),
193 (PlannedMedium::RnodeMulti { member }, ReferenceConfigParams::RnodeMulti { .. }) => {
194 let radio = member.radio();
195 Ok(rnode_discovery_advertisement(
196 u64::from(radio.frequency().hz()),
197 radio.bandwidth_hz(),
198 radio.spreading_factor(),
199 radio.coding_rate(),
200 ))
201 }
202 (PlannedMedium::Kiss { .. }, ReferenceConfigParams::Kiss { .. }) => kiss(),
203 (
204 PlannedMedium::TcpClient {
205 framing: TcpWireFraming::Kiss,
206 ..
207 },
208 ReferenceConfigParams::TcpClient { .. },
209 ) => kiss(),
210 (PlannedMedium::TcpClient { .. }, ReferenceConfigParams::TcpClient { .. }) => {
211 Err(DiscoveryPublicationProblem::IncompatibleSetting {
212 key: interface_key::KISS_FRAMING,
213 })
214 }
215 _ => Err(DiscoveryPublicationProblem::UnsupportedInterfaceType),
216 }
217}
218
219fn rnode_discovery_advertisement(
220 frequency_hz: u64,
221 bandwidth_hz: u32,
222 spreading_factor: u8,
223 coding_rate: u8,
224) -> DiscoveryAdvertisementPlan {
225 DiscoveryAdvertisementPlan::RNode {
226 frequency_hz,
227 bandwidth_hz,
228 spreading_factor,
229 coding_rate,
230 }
231}