1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
#![allow(dead_code)]
use embedded_time::duration::Milliseconds;
use kwap_macros::rfc_7252_doc;
use crate::retry::{Attempts, Strategy};
pub(crate) struct ConfigData {
pub(crate) token_seed: u16,
pub(crate) con_retry_strategy: Strategy,
pub(crate) default_leisure_millis: u32,
pub(crate) max_retransmit_attempts: u16,
pub(crate) nstart: u8,
pub(crate) probing_rate_bytes_per_sec: u16,
}
impl ConfigData {
pub(crate) fn max_transmit_span_millis(&self) -> u32 {
self.con_retry_strategy
.max_time(Attempts(self.max_retransmit_attempts - 1))
.0 as u32
}
pub(crate) fn max_transmit_wait_millis(&self) -> u32 {
self.con_retry_strategy
.max_time(Attempts(self.max_retransmit_attempts))
.0 as u32
}
// TODO: adjust these on the fly based on actual timings?
pub(crate) fn max_latency_millis(&self) -> u32 {
100_000
}
pub(crate) fn expected_processing_delay_millis(&self) -> u32 {
200
}
pub(crate) fn exchange_lifetime_millis(&self) -> u32 {
self.max_transmit_span_millis()
+ (2 * self.max_latency_millis())
+ self.expected_processing_delay_millis()
}
}
/// CoAP runtime config
///
/// Allows you to configure things like
/// "how many concurrent requests are we allowed
/// to send?" and "how long should we wait to resend
/// unacknowledged confirmable requests?"
///
/// For an example see [`Config::new`].
#[derive(Debug, Default, Clone, Copy)]
pub struct Config {
token_seed: Option<u16>,
con_retry_strategy: Option<Strategy>,
default_leisure_millis: Option<u32>,
max_retransmit_attempts: Option<u16>,
nstart: Option<u8>,
probing_rate_bytes_per_sec: Option<u16>,
/// Users who use a struct literal to initialize this
/// /must/ use ..Default::default(),
/// which makes adding fields to this struct non-breaking.
__non_exhaustive: (),
}
/// Bytes / Second
#[derive(Debug, Clone, Copy)]
pub struct BytesPerSecond(pub u16);
impl Config {
/// Creates a new (empty) runtime config
///
/// ```
/// use embedded_time::duration::Milliseconds as Millis;
/// use kwap::config::{BytesPerSecond, Config};
/// use kwap::retry::Attempts;
/// use kwap::retry::Strategy::Exponential;
///
/// let config = Config::new().token_seed(35718)
/// .max_concurrent_requests(142)
/// .probing_rate(BytesPerSecond(10_000))
/// .max_con_request_retries(Attempts(10))
/// .con_retry_strategy(Exponential { init_min: Millis(500),
/// init_max: Millis(750) });
/// ```
pub fn new() -> Self {
Default::default()
}
/// Set the retry strategy we should use to figure out when
/// we should resend outgoing CON requests that have not been
/// ACKed yet.
///
/// Default value:
/// ```ignore
/// Strategy::Exponential { init_min: Seconds(2), init_max: Seconds(3) }
/// ```
pub fn con_retry_strategy(mut self, strat: Strategy) -> Self {
self.con_retry_strategy = Some(strat);
self
}
/// Set the seed used to generate message [`Token`](kwap_msg::Token)s.
///
/// The default value is 0, although it is
/// best practice to set this to something else.
/// This could be a random integer, or a machine identifier.
///
/// _e.g. if you're developing a swarm of
/// smart CoAP-enabled thermostats, each one would ideally
/// have a distinct token_seed._
///
/// The purpose of the seed is to make it more
/// difficult for an observer of unencrypted
/// CoAP traffic to guess what the next token will be.
///
/// Tokens are generated by smooshing together
/// the 2-byte seed with an 8-byte timestamp from
/// the system clock.
///
/// ```text
/// Core.token_seed
/// ||
/// xx xxxxxxxx
/// | |
/// timestamp
/// ```
///
/// Then a hashing algorithm is used to make it opaque and
/// reduce the size to 8 bytes.
pub fn token_seed(mut self, token_seed: u16) -> Self {
self.token_seed = Some(token_seed);
self
}
/// Set the transmission rate that we should do our best
/// not to exceed when waiting for:
/// - responses to our NON requests
/// - responses to our acked CON requests
///
/// The default value is 1,000 (1KB)
pub fn probing_rate(mut self, probing_rate: BytesPerSecond) -> Self {
self.probing_rate_bytes_per_sec = Some(probing_rate.0);
self
}
/// Set the number of concurrent requests we are allowed
/// to have in-flight for each server.
///
/// The default value is 1 (no concurrency)
pub fn max_concurrent_requests(mut self, n: u8) -> Self {
self.nstart = Some(n);
self
}
/// Set the maximum number of times we should re-send
/// confirmable requests before getting a response.
///
/// The default value is 4 attempts
pub fn max_con_request_retries(mut self, max_tries: Attempts) -> Self {
self.max_retransmit_attempts = Some(max_tries.0);
self
}
/// Set the maximum amount of time we should wait to
/// respond to incoming multicast requests.
///
/// The default value is 5 seconds.
#[doc = rfc_7252_doc!("8.2")]
pub fn default_leisure(mut self, default_leisure: Milliseconds<u32>) -> Self {
self.default_leisure_millis = Some(default_leisure.0);
self
}
}
impl From<Config> for ConfigData {
fn from(Config { token_seed,
default_leisure_millis,
max_retransmit_attempts,
nstart,
probing_rate_bytes_per_sec,
con_retry_strategy,
.. }: Config)
-> Self {
ConfigData { token_seed: token_seed.unwrap_or(0),
default_leisure_millis: default_leisure_millis.unwrap_or(5_000),
max_retransmit_attempts: max_retransmit_attempts.unwrap_or(4),
nstart: nstart.unwrap_or(1),
probing_rate_bytes_per_sec: probing_rate_bytes_per_sec.unwrap_or(1_000),
con_retry_strategy:
con_retry_strategy.unwrap_or(Strategy::Exponential { init_min:
Milliseconds(2_000),
init_max:
Milliseconds(3_000) }) }
}
}