netem_trace/model/bw.rs
1//! This module contains some predefined bandwidth trace models.
2//!
3//! Enabled with feature `bw-model` or `model`.
4//!
5//! ## Predefined models
6//!
7//! - [`StaticBw`]: A trace model with static bandwidth.
8//! - [`NormalizedBw`]: A trace model whose bandwidth subjects to a normal distribution (can set upper and lower bounds, and can configure it to be truncated with `truncated-normal` feature enabled).
9//! - [`RepeatedBwPattern`]: A trace model with a repeated bandwidth pattern.
10//! - [`TraceBw`]: A trace model to replay compact bandwidth changes from file, especially useful for online sampled records.
11//!
12//! ## Examples
13//!
14//! An example to build model from configuration:
15//!
16//! ```
17//! # use netem_trace::model::StaticBwConfig;
18//! # use netem_trace::{Bandwidth, Duration, BwTrace};
19//! let mut static_bw = StaticBwConfig::new()
20//! .bw(Bandwidth::from_mbps(24))
21//! .duration(Duration::from_secs(1))
22//! .build();
23//! assert_eq!(static_bw.next_bw(), Some((Bandwidth::from_mbps(24), Duration::from_secs(1))));
24//! assert_eq!(static_bw.next_bw(), None);
25//! ```
26//!
27//! A more common use case is to build model from a configuration file (e.g. json file):
28//!
29//! ```
30//! # use netem_trace::model::{StaticBwConfig, BwTraceConfig};
31//! # use netem_trace::{Bandwidth, Duration, BwTrace};
32//! # #[cfg(feature = "human")]
33//! # let config_file_content = "{\"RepeatedBwPatternConfig\":{\"pattern\":[{\"StaticBwConfig\":{\"bw\":\"12Mbps\",\"duration\":\"1s\"}},{\"StaticBwConfig\":{\"bw\":\"24Mbps\",\"duration\":\"1s\"}}],\"count\":2}}";
34//! // The content would be "{\"RepeatedBwPatternConfig\":{\"pattern\":[{\"StaticBwConfig\":{\"bw\":{\"gbps\":0,\"bps\":12000000},\"duration\":{\"secs\":1,\"nanos\":0}}},{\"StaticBwConfig\":{\"bw\":{\"gbps\":0,\"bps\":24000000},\"duration\":{\"secs\":1,\"nanos\":0}}}],\"count\":2}}"
35//! // if the `human` feature is not enabled.
36//! # #[cfg(not(feature = "human"))]
37//! let config_file_content = "{\"RepeatedBwPatternConfig\":{\"pattern\":[{\"StaticBwConfig\":{\"bw\":{\"gbps\":0,\"bps\":12000000},\"duration\":{\"secs\":1,\"nanos\":0}}},{\"StaticBwConfig\":{\"bw\":{\"gbps\":0,\"bps\":24000000},\"duration\":{\"secs\":1,\"nanos\":0}}}],\"count\":2}}";
38//! let des: Box<dyn BwTraceConfig> = serde_json::from_str(config_file_content).unwrap();
39//! let mut model = des.into_model();
40//! assert_eq!(
41//! model.next_bw(),
42//! Some((Bandwidth::from_mbps(12), Duration::from_secs(1)))
43//! );
44//! assert_eq!(
45//! model.next_bw(),
46//! Some((Bandwidth::from_mbps(24), Duration::from_secs(1)))
47//! );
48//! assert_eq!(
49//! model.next_bw(),
50//! Some((Bandwidth::from_mbps(12), Duration::from_secs(1)))
51//! );
52//! assert_eq!(
53//! model.next_bw(),
54//! Some((Bandwidth::from_mbps(24), Duration::from_secs(1)))
55//! );
56//! assert_eq!(model.next_bw(), None);
57//! ```
58use crate::{Bandwidth, BwTrace, Duration};
59use dyn_clone::DynClone;
60use rand::{rngs::StdRng, RngCore, SeedableRng};
61use rand_distr::{Distribution, Normal};
62
63const DEFAULT_RNG_SEED: u64 = 42;
64
65/// This trait is used to convert a bandwidth trace configuration into a bandwidth trace model.
66///
67/// Since trace model is often configured with files and often has inner states which
68/// is not suitable to be serialized/deserialized, this trait makes it possible to
69/// separate the configuration part into a simple struct for serialization/deserialization, and
70/// construct the model from the configuration.
71#[cfg_attr(feature = "serde", typetag::serde)]
72pub trait BwTraceConfig: DynClone + Send {
73 fn into_model(self: Box<Self>) -> Box<dyn BwTrace>;
74}
75
76dyn_clone::clone_trait_object!(BwTraceConfig);
77
78#[cfg(feature = "serde")]
79use serde::{Deserialize, Serialize};
80
81#[cfg(feature = "truncated-normal")]
82use super::solve_truncate::solve;
83
84/// The model of a static bandwidth trace.
85///
86/// ## Examples
87///
88/// ```
89/// # use netem_trace::model::StaticBwConfig;
90/// # use netem_trace::{Bandwidth, Duration, BwTrace};
91/// let mut static_bw = StaticBwConfig::new()
92/// .bw(Bandwidth::from_mbps(24))
93/// .duration(Duration::from_secs(1))
94/// .build();
95/// assert_eq!(static_bw.next_bw(), Some((Bandwidth::from_mbps(24), Duration::from_secs(1))));
96/// assert_eq!(static_bw.next_bw(), None);
97/// ```
98#[derive(Debug, Clone)]
99pub struct StaticBw {
100 pub bw: Bandwidth,
101 pub duration: Option<Duration>,
102}
103
104/// The configuration struct for [`StaticBw`].
105///
106/// See [`StaticBw`] for more details.
107#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(default))]
108#[derive(Debug, Clone, Default)]
109pub struct StaticBwConfig {
110 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
111 #[cfg_attr(
112 all(feature = "serde", feature = "human"),
113 serde(with = "human_bandwidth::serde")
114 )]
115 pub bw: Option<Bandwidth>,
116 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
117 #[cfg_attr(
118 all(feature = "serde", feature = "human"),
119 serde(with = "humantime_serde")
120 )]
121 pub duration: Option<Duration>,
122}
123
124/// The model of a bandwidth trace subjects to a normal distribution.
125///
126/// The bandwidth will subject to N(mean, std_dev), but bounded within [lower_bound, upper_bound] (optional)
127///
128/// ## Examples
129///
130/// A simple example without any bound on bandwidth:
131///
132/// ```
133/// # use netem_trace::model::NormalizedBwConfig;
134/// # use netem_trace::{Bandwidth, Duration, BwTrace};
135/// let mut normal_bw = NormalizedBwConfig::new()
136/// .mean(Bandwidth::from_mbps(12))
137/// .std_dev(Bandwidth::from_mbps(1))
138/// .duration(Duration::from_secs(1))
139/// .step(Duration::from_millis(100))
140/// .seed(42)
141/// .build();
142/// assert_eq!(normal_bw.next_bw(), Some((Bandwidth::from_bps(12069427), Duration::from_millis(100))));
143/// assert_eq!(normal_bw.next_bw(), Some((Bandwidth::from_bps(12132938), Duration::from_millis(100))));
144/// ```
145///
146/// A more complex example with bounds on bandwidth:
147///
148/// ```
149/// # use netem_trace::model::NormalizedBwConfig;
150/// # use netem_trace::{Bandwidth, Duration, BwTrace};
151/// let mut normal_bw = NormalizedBwConfig::new()
152/// .mean(Bandwidth::from_mbps(12))
153/// .std_dev(Bandwidth::from_mbps(1))
154/// .duration(Duration::from_secs(1))
155/// .step(Duration::from_millis(100))
156/// .seed(42)
157/// .upper_bound(Bandwidth::from_kbps(12100))
158/// .lower_bound(Bandwidth::from_kbps(11900))
159/// .build();
160/// assert_eq!(normal_bw.next_bw(), Some((Bandwidth::from_bps(12069427), Duration::from_millis(100))));
161/// assert_eq!(normal_bw.next_bw(), Some((Bandwidth::from_bps(12100000), Duration::from_millis(100))));
162/// ```
163#[derive(Debug, Clone)]
164pub struct NormalizedBw<Rng = StdRng>
165where
166 Rng: RngCore,
167{
168 pub mean: Bandwidth,
169 pub std_dev: Bandwidth,
170 pub upper_bound: Option<Bandwidth>,
171 pub lower_bound: Option<Bandwidth>,
172 pub duration: Duration,
173 pub step: Duration,
174 pub seed: u64,
175 rng: Rng,
176 normal: Normal<f64>,
177}
178
179/// The configuration struct for [`NormalizedBw`].
180///
181/// See [`NormalizedBw`] for more details.
182#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(default))]
183#[derive(Debug, Clone, Default)]
184pub struct NormalizedBwConfig {
185 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
186 #[cfg_attr(
187 all(feature = "serde", feature = "human"),
188 serde(with = "human_bandwidth::serde")
189 )]
190 pub mean: Option<Bandwidth>,
191 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
192 #[cfg_attr(
193 all(feature = "serde", feature = "human"),
194 serde(with = "human_bandwidth::serde")
195 )]
196 pub std_dev: Option<Bandwidth>,
197 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
198 #[cfg_attr(
199 all(feature = "serde", feature = "human"),
200 serde(with = "human_bandwidth::serde")
201 )]
202 pub upper_bound: Option<Bandwidth>,
203 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
204 #[cfg_attr(
205 all(feature = "serde", feature = "human"),
206 serde(with = "human_bandwidth::serde")
207 )]
208 pub lower_bound: Option<Bandwidth>,
209 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
210 #[cfg_attr(
211 all(feature = "serde", feature = "human"),
212 serde(with = "humantime_serde")
213 )]
214 pub duration: Option<Duration>,
215 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
216 #[cfg_attr(
217 all(feature = "serde", feature = "human"),
218 serde(with = "humantime_serde")
219 )]
220 pub step: Option<Duration>,
221 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
222 pub seed: Option<u64>,
223}
224
225/// The model of a bandwidth trace whose waveform is sawtooth.
226///
227/// The lowest value of the sawtooth is set by `bottom` while the highest value is set by `top`.
228/// The `interval` describes how long a sawtooth lasts. The `duty_ratio` describes how much the rising time of a sawtooth
229/// occupies the `interval`.
230///
231/// The `step` describes how long between two consecutive bandwidth samples.
232///
233/// The noise of the sawtooth bandwidth will subject to N(0, std_dev), but bounded within [-lower_noise_bound, upper_noise_bound] (optional)
234///
235/// ## Examples
236///
237/// A simple example without any bound on bandwidth:
238///
239/// ```
240/// # use netem_trace::model::SawtoothBwConfig;
241/// # use netem_trace::{Bandwidth, Duration, BwTrace};
242/// let mut sawtooth_bw = SawtoothBwConfig::new()
243/// .bottom(Bandwidth::from_mbps(12))
244/// .top(Bandwidth::from_mbps(16))
245/// .duration(Duration::from_secs(1))
246/// .step(Duration::from_millis(100))
247/// .interval(Duration::from_millis(500))
248/// .duty_ratio(0.8)
249/// .build();
250/// assert_eq!(
251/// sawtooth_bw.next_bw(),
252/// Some((Bandwidth::from_mbps(12), Duration::from_millis(100)))
253/// );
254/// assert_eq!(
255/// sawtooth_bw.next_bw(),
256/// Some((Bandwidth::from_mbps(13), Duration::from_millis(100)))
257/// );
258/// assert_eq!(
259/// sawtooth_bw.next_bw(),
260/// Some((Bandwidth::from_mbps(14), Duration::from_millis(100)))
261/// );
262/// assert_eq!(
263/// sawtooth_bw.next_bw(),
264/// Some((Bandwidth::from_mbps(15), Duration::from_millis(100)))
265/// );
266/// assert_eq!(
267/// sawtooth_bw.next_bw(),
268/// Some((Bandwidth::from_mbps(16), Duration::from_millis(100)))
269/// );
270/// assert_eq!(
271/// sawtooth_bw.next_bw(),
272/// Some((Bandwidth::from_mbps(12), Duration::from_millis(100)))
273/// );
274/// assert_eq!(
275/// sawtooth_bw.next_bw(),
276/// Some((Bandwidth::from_mbps(13), Duration::from_millis(100)))
277/// );
278/// assert_eq!(
279/// sawtooth_bw.next_bw(),
280/// Some((Bandwidth::from_mbps(14), Duration::from_millis(100)))
281/// );
282/// assert_eq!(
283/// sawtooth_bw.next_bw(),
284/// Some((Bandwidth::from_mbps(15), Duration::from_millis(100)))
285/// );
286/// ```
287///
288/// A more complex example with bounds on noise:
289///
290/// ```
291/// # use netem_trace::model::SawtoothBwConfig;
292/// # use netem_trace::{Bandwidth, Duration, BwTrace};
293/// let mut sawtooth_bw = SawtoothBwConfig::new()
294/// .bottom(Bandwidth::from_mbps(12))
295/// .top(Bandwidth::from_mbps(16))
296/// .duration(Duration::from_secs(1))
297/// .step(Duration::from_millis(100))
298/// .interval(Duration::from_millis(500))
299/// .duty_ratio(0.8)
300/// .std_dev(Bandwidth::from_mbps(5))
301/// .upper_noise_bound(Bandwidth::from_mbps(1))
302/// .lower_noise_bound(Bandwidth::from_kbps(500))
303/// .build();
304/// assert_eq!(
305/// sawtooth_bw.next_bw(),
306/// Some((Bandwidth::from_bps(12347139), Duration::from_millis(100)))
307/// );
308/// assert_eq!(
309/// sawtooth_bw.next_bw(),
310/// Some((Bandwidth::from_bps(13664690), Duration::from_millis(100)))
311/// );
312/// assert_eq!(
313/// sawtooth_bw.next_bw(),
314/// Some((Bandwidth::from_mbps(15), Duration::from_millis(100)))
315/// );
316/// assert_eq!(
317/// sawtooth_bw.next_bw(),
318/// Some((Bandwidth::from_bps(14500000), Duration::from_millis(100)))
319/// );
320/// ```
321#[derive(Debug, Clone)]
322pub struct SawtoothBw<Rng = StdRng>
323where
324 Rng: RngCore,
325{
326 pub bottom: Bandwidth,
327 pub top: Bandwidth,
328 pub interval: Duration,
329 pub duty_ratio: f64,
330 pub duration: Duration,
331 pub step: Duration,
332 pub seed: u64,
333 pub std_dev: Bandwidth,
334 pub upper_noise_bound: Option<Bandwidth>,
335 pub lower_noise_bound: Option<Bandwidth>,
336 current: Duration,
337 rng: Rng,
338 noise: Normal<f64>,
339}
340
341/// The configuration struct for [`SawtoothBw`].
342///
343/// See [`SawtoothBw`] for more details.
344#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(default))]
345#[derive(Debug, Clone, Default)]
346pub struct SawtoothBwConfig {
347 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
348 #[cfg_attr(
349 all(feature = "serde", feature = "human"),
350 serde(with = "human_bandwidth::serde")
351 )]
352 pub bottom: Option<Bandwidth>,
353 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
354 #[cfg_attr(
355 all(feature = "serde", feature = "human"),
356 serde(with = "human_bandwidth::serde")
357 )]
358 pub top: Option<Bandwidth>,
359 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
360 #[cfg_attr(
361 all(feature = "serde", feature = "human"),
362 serde(with = "humantime_serde")
363 )]
364 pub interval: Option<Duration>,
365 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
366 pub duty_ratio: Option<f64>,
367 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
368 #[cfg_attr(
369 all(feature = "serde", feature = "human"),
370 serde(with = "humantime_serde")
371 )]
372 pub duration: Option<Duration>,
373 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
374 #[cfg_attr(
375 all(feature = "serde", feature = "human"),
376 serde(with = "humantime_serde")
377 )]
378 pub step: Option<Duration>,
379 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
380 pub seed: Option<u64>,
381 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
382 #[cfg_attr(
383 all(feature = "serde", feature = "human"),
384 serde(with = "human_bandwidth::serde")
385 )]
386 pub std_dev: Option<Bandwidth>,
387 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
388 #[cfg_attr(
389 all(feature = "serde", feature = "human"),
390 serde(with = "human_bandwidth::serde")
391 )]
392 pub upper_noise_bound: Option<Bandwidth>,
393 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
394 #[cfg_attr(
395 all(feature = "serde", feature = "human"),
396 serde(with = "human_bandwidth::serde")
397 )]
398 pub lower_noise_bound: Option<Bandwidth>,
399}
400
401/// The model contains an array of bandwidth trace models.
402///
403/// Combine multiple bandwidth trace models into one bandwidth pattern,
404/// and repeat the pattern for `count` times.
405///
406/// If `count` is 0, the pattern will be repeated forever.
407///
408/// ## Examples
409///
410/// The most common use case is to read from a configuration file and
411/// deserialize it into a [`RepeatedBwPatternConfig`]:
412///
413/// ```
414/// # use netem_trace::model::{StaticBwConfig, BwTraceConfig};
415/// # use netem_trace::{Bandwidth, Duration, BwTrace};
416/// # #[cfg(feature = "human")]
417/// # let config_file_content = "{\"RepeatedBwPatternConfig\":{\"pattern\":[{\"StaticBwConfig\":{\"bw\":\"12Mbps\",\"duration\":\"1s\"}},{\"StaticBwConfig\":{\"bw\":\"24Mbps\",\"duration\":\"1s\"}}],\"count\":2}}";
418/// // The content would be "{\"RepeatedBwPatternConfig\":{\"pattern\":[{\"StaticBwConfig\":{\"bw\":{\"gbps\":0,\"bps\":12000000},\"duration\":{\"secs\":1,\"nanos\":0}}},{\"StaticBwConfig\":{\"bw\":{\"gbps\":0,\"bps\":24000000},\"duration\":{\"secs\":1,\"nanos\":0}}}],\"count\":2}}"
419/// // if the `human` feature is not enabled.
420/// # #[cfg(not(feature = "human"))]
421/// let config_file_content = "{\"RepeatedBwPatternConfig\":{\"pattern\":[{\"StaticBwConfig\":{\"bw\":{\"gbps\":0,\"bps\":12000000},\"duration\":{\"secs\":1,\"nanos\":0}}},{\"StaticBwConfig\":{\"bw\":{\"gbps\":0,\"bps\":24000000},\"duration\":{\"secs\":1,\"nanos\":0}}}],\"count\":2}}";
422/// let des: Box<dyn BwTraceConfig> = serde_json::from_str(config_file_content).unwrap();
423/// let mut model = des.into_model();
424/// assert_eq!(
425/// model.next_bw(),
426/// Some((Bandwidth::from_mbps(12), Duration::from_secs(1)))
427/// );
428/// assert_eq!(
429/// model.next_bw(),
430/// Some((Bandwidth::from_mbps(24), Duration::from_secs(1)))
431/// );
432/// assert_eq!(
433/// model.next_bw(),
434/// Some((Bandwidth::from_mbps(12), Duration::from_secs(1)))
435/// );
436/// assert_eq!(
437/// model.next_bw(),
438/// Some((Bandwidth::from_mbps(24), Duration::from_secs(1)))
439/// );
440/// assert_eq!(model.next_bw(), None);
441/// ```
442///
443/// You can also build manually:
444///
445/// ```
446/// # use netem_trace::model::{StaticBwConfig, BwTraceConfig, RepeatedBwPatternConfig};
447/// # use netem_trace::{Bandwidth, Duration, BwTrace};
448/// let pat = vec![
449/// Box::new(
450/// StaticBwConfig::new()
451/// .bw(Bandwidth::from_mbps(12))
452/// .duration(Duration::from_secs(1)),
453/// ) as Box<dyn BwTraceConfig>,
454/// Box::new(
455/// StaticBwConfig::new()
456/// .bw(Bandwidth::from_mbps(24))
457/// .duration(Duration::from_secs(1)),
458/// ) as Box<dyn BwTraceConfig>,
459/// ];
460/// let ser = Box::new(RepeatedBwPatternConfig::new().pattern(pat).count(2)) as Box<dyn BwTraceConfig>;
461/// let ser_str = serde_json::to_string(&ser).unwrap();
462/// # #[cfg(feature = "human")]
463/// # let json_str = "{\"RepeatedBwPatternConfig\":{\"pattern\":[{\"StaticBwConfig\":{\"bw\":\"12Mbps\",\"duration\":\"1s\"}},{\"StaticBwConfig\":{\"bw\":\"24Mbps\",\"duration\":\"1s\"}}],\"count\":2}}";
464/// // The json string would be "{\"RepeatedBwPatternConfig\":{\"pattern\":[{\"StaticBwConfig\":{\"bw\":{\"gbps\":0,\"bps\":12000000},\"duration\":{\"secs\":1,\"nanos\":0}}},{\"StaticBwConfig\":{\"bw\":{\"gbps\":0,\"bps\":24000000},\"duration\":{\"secs\":1,\"nanos\":0}}}],\"count\":2}}"
465/// // if the `human` feature is not enabled.
466/// # #[cfg(not(feature = "human"))]
467/// let json_str = "{\"RepeatedBwPatternConfig\":{\"pattern\":[{\"StaticBwConfig\":{\"bw\":{\"gbps\":0,\"bps\":12000000},\"duration\":{\"secs\":1,\"nanos\":0}}},{\"StaticBwConfig\":{\"bw\":{\"gbps\":0,\"bps\":24000000},\"duration\":{\"secs\":1,\"nanos\":0}}}],\"count\":2}}";
468/// assert_eq!(ser_str, json_str);
469/// ```
470pub struct RepeatedBwPattern {
471 pub pattern: Vec<Box<dyn BwTraceConfig>>,
472 pub count: usize,
473 current_model: Option<Box<dyn BwTrace>>,
474 current_cycle: usize,
475 current_pattern: usize,
476}
477
478/// The configuration struct for [`RepeatedBwPattern`].
479///
480/// See [`RepeatedBwPattern`] for more details.
481#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(default))]
482#[derive(Default, Clone)]
483pub struct RepeatedBwPatternConfig {
484 pub pattern: Vec<Box<dyn BwTraceConfig>>,
485 pub count: usize,
486}
487
488/// This model is used to enable a more compact trace.
489/// It replays the bandwidth changes according to a trace file,
490/// and is necessary for replaying sampled traces from Internet or production.
491///
492/// ## Examples
493///
494/// ```
495/// # use netem_trace::model::TraceBwConfig;
496/// # use netem_trace::{Bandwidth, Duration, BwTrace};
497/// use crate::netem_trace::model::Forever;
498/// use netem_trace::model::BwTraceConfig;
499///
500/// let mut tracebw = TraceBwConfig::new().pattern(
501/// vec![
502/// (Duration::from_millis(1), vec![Bandwidth::from_mbps(2),Bandwidth::from_mbps(4),]),
503/// (Duration::from_millis(2), vec![Bandwidth::from_mbps(1)]),
504/// ]
505/// ).build();
506///
507/// assert_eq!(tracebw.next_bw(), Some((Bandwidth::from_mbps(2), Duration::from_millis(1))));
508/// assert_eq!(tracebw.next_bw(), Some((Bandwidth::from_mbps(4), Duration::from_millis(1))));
509/// assert_eq!(tracebw.next_bw(), Some((Bandwidth::from_mbps(1), Duration::from_millis(2))));
510/// assert_eq!(tracebw.next_bw(), None);
511/// assert_eq!(tracebw.next_bw(), None);
512/// assert_eq!(tracebw.next_bw(), None);
513///
514/// let repeated_tracebw_config = TraceBwConfig::new().pattern(
515/// vec![
516/// (Duration::from_millis(1), vec![Bandwidth::from_mbps(2),Bandwidth::from_mbps(4),]),
517/// (Duration::from_millis(2), vec![Bandwidth::from_mbps(1)]),
518/// ]
519/// ).forever();
520///
521/// let mut repeated_tracebw = repeated_tracebw_config.clone().build();
522///
523/// assert_eq!(repeated_tracebw.next_bw(), Some((Bandwidth::from_mbps(2), Duration::from_millis(1))));
524/// assert_eq!(repeated_tracebw.next_bw(), Some((Bandwidth::from_mbps(4), Duration::from_millis(1))));
525/// assert_eq!(repeated_tracebw.next_bw(), Some((Bandwidth::from_mbps(1), Duration::from_millis(2))));
526///
527/// assert_eq!(repeated_tracebw.next_bw(), Some((Bandwidth::from_mbps(2), Duration::from_millis(1))));
528/// assert_eq!(repeated_tracebw.next_bw(), Some((Bandwidth::from_mbps(4), Duration::from_millis(1))));
529/// assert_eq!(repeated_tracebw.next_bw(), Some((Bandwidth::from_mbps(1), Duration::from_millis(2))));
530///
531/// assert_eq!(repeated_tracebw.next_bw(), Some((Bandwidth::from_mbps(2), Duration::from_millis(1))));
532/// assert_eq!(repeated_tracebw.next_bw(), Some((Bandwidth::from_mbps(4), Duration::from_millis(1))));
533/// assert_eq!(repeated_tracebw.next_bw(), Some((Bandwidth::from_mbps(1), Duration::from_millis(2))));
534///
535/// let ser : Box<dyn BwTraceConfig> = Box::new(repeated_tracebw_config);
536/// let ser_str = serde_json::to_string(&ser).unwrap();
537///
538/// # #[cfg(feature = "human")]
539/// let json_str = "{\"RepeatedBwPatternConfig\":{\"pattern\":[{\"TraceBwConfig\":{\"pattern\":[[\"1ms\",[\"2Mbps\",\"4Mbps\"]],[\"2ms\",[\"1Mbps\"]]]}}],\"count\":0}}";
540/// // The json string would be "{\"RepeatedBwPatternConfig\":{\"pattern\":[{\"TraceBwConfig\":{\"pattern\":[[{\"secs\":0,\"nanos\":1000000},[{\"gbps\":0,\"bps\":2000000},{\"gbps\":0,\"bps\":4000000}]],[{\"secs\":0,\"nanos\":2000000},[{\"gbps\":0,\"bps\":1000000}]]]}}],\"count\":0}}"
541/// // if the `human` feature is not enabled.
542/// #[cfg(not(feature = "human"))]
543/// let json_str = "{\"RepeatedBwPatternConfig\":{\"pattern\":[{\"TraceBwConfig\":{\"pattern\":[[{\"secs\":0,\"nanos\":1000000},[{\"gbps\":0,\"bps\":2000000},{\"gbps\":0,\"bps\":4000000}]],[{\"secs\":0,\"nanos\":2000000},[{\"gbps\":0,\"bps\":1000000}]]]}}],\"count\":0}}";
544/// assert_eq!(ser_str, json_str);
545///
546/// let des: Box<dyn BwTraceConfig> = serde_json::from_str(json_str).unwrap();
547/// let mut model = des.into_model();
548///
549/// assert_eq!(model.next_bw(), Some((Bandwidth::from_mbps(2), Duration::from_millis(1))));
550/// assert_eq!(model.next_bw(), Some((Bandwidth::from_mbps(4), Duration::from_millis(1))));
551/// assert_eq!(model.next_bw(), Some((Bandwidth::from_mbps(1), Duration::from_millis(2))));
552///
553/// assert_eq!(model.next_bw(), Some((Bandwidth::from_mbps(2), Duration::from_millis(1))));
554/// assert_eq!(model.next_bw(), Some((Bandwidth::from_mbps(4), Duration::from_millis(1))));
555/// assert_eq!(model.next_bw(), Some((Bandwidth::from_mbps(1), Duration::from_millis(2))));
556/// ```
557pub struct TraceBw {
558 pub pattern: Vec<(Duration, Vec<Bandwidth>)>, // inner vector is never empty
559 pub outer_index: usize,
560 pub inner_index: usize,
561}
562
563/// The configuration struct for [`TraceBw`].
564///
565/// See [`TraceBw`] for more details.
566///
567#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(default))]
568#[derive(Debug, Clone, Default)]
569pub struct TraceBwConfig {
570 #[cfg_attr(
571 all(feature = "serde", feature = "human"),
572 serde(with = "tracebw_serde")
573 )]
574 pub pattern: Vec<(Duration, Vec<Bandwidth>)>,
575}
576
577impl TraceBwConfig {
578 pub fn new() -> Self {
579 Self { pattern: vec![] }
580 }
581
582 pub fn pattern(mut self, pattern: Vec<(Duration, Vec<Bandwidth>)>) -> Self {
583 self.pattern = pattern;
584 self
585 }
586
587 pub fn build(self) -> TraceBw {
588 TraceBw {
589 pattern: self
590 .pattern
591 .into_iter()
592 .filter(|(_, bandwidths)| !bandwidths.is_empty())
593 .collect(),
594 outer_index: 0,
595 inner_index: 0,
596 }
597 }
598}
599
600#[cfg(all(feature = "serde", feature = "human"))]
601mod tracebw_serde {
602 use super::*;
603 use serde::ser::SerializeSeq;
604 use serde::{de, ser, Deserialize, Deserializer, Serialize, Serializer};
605 use std::fmt;
606 use std::ops::{Deref, DerefMut};
607 /// Deserializes a `Bandwidth` in human-readable format.
608 ///
609 /// This function can be used with `serde_derive`'s `with` and
610 /// `deserialize_with` annotations.
611 pub fn deserialize<'a, T, D>(d: D) -> Result<T, D::Error>
612 where
613 Serde<T>: Deserialize<'a>,
614 D: Deserializer<'a>,
615 {
616 Serde::deserialize(d).map(Serde::into_inner)
617 }
618
619 /// Serializes a `Bandwidth` in human-readable format.
620 ///
621 /// This function can be used with `serde_derive`'s `with` and
622 /// `serialize_with` annotations.
623 pub fn serialize<T, S>(d: &T, s: S) -> Result<S::Ok, S::Error>
624 where
625 for<'a> Serde<&'a T>: Serialize,
626 S: Serializer,
627 {
628 Serde::from(d).serialize(s)
629 }
630
631 /// A wrapper type which implements `Serialize` and `Deserialize` for
632 /// types involving `Bandwidth`.
633 #[derive(Copy, Clone, Eq, Hash, PartialEq)]
634 pub struct Serde<T>(T);
635
636 impl<T> fmt::Debug for Serde<T>
637 where
638 T: fmt::Debug,
639 {
640 fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
641 self.0.fmt(formatter)
642 }
643 }
644
645 impl<T> Deref for Serde<T> {
646 type Target = T;
647
648 fn deref(&self) -> &T {
649 &self.0
650 }
651 }
652
653 impl<T> DerefMut for Serde<T> {
654 fn deref_mut(&mut self) -> &mut T {
655 &mut self.0
656 }
657 }
658
659 impl<T> Serde<T> {
660 /// Consumes the `De`, returning the inner value.
661 pub fn into_inner(self) -> T {
662 self.0
663 }
664 }
665
666 impl<T> From<T> for Serde<T> {
667 fn from(val: T) -> Serde<T> {
668 Serde(val)
669 }
670 }
671
672 impl<'de> Deserialize<'de> for Serde<Vec<(Duration, Vec<Bandwidth>)>> {
673 fn deserialize<D>(d: D) -> Result<Serde<Vec<(Duration, Vec<Bandwidth>)>>, D::Error>
674 where
675 D: Deserializer<'de>,
676 {
677 struct V;
678
679 impl<'de> de::Visitor<'de> for V {
680 type Value = Vec<(Duration, Vec<Bandwidth>)>;
681
682 fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
683 fmt.write_str("a sequence of [str, [str, str, ...]]")
684 }
685
686 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
687 where
688 A: serde::de::SeqAccess<'de>,
689 {
690 let mut pattern = Vec::with_capacity(seq.size_hint().unwrap_or(0));
691
692 while let Some((duration_str, bandwidths_str)) =
693 seq.next_element::<(String, Vec<String>)>()?
694 {
695 let duration =
696 humantime_serde::re::humantime::parse_duration(duration_str.as_str())
697 .map_err(|e| {
698 serde::de::Error::custom(format!(
699 "Failed to parse duration '{}': {}",
700 duration_str, e
701 ))
702 })?;
703
704 let bandwidths = bandwidths_str
705 .into_iter()
706 .map(|b| {
707 human_bandwidth::parse_bandwidth(&b).map_err(|e| {
708 serde::de::Error::custom(format!(
709 "Failed to parse bandwidth '{}': {}",
710 b, e
711 ))
712 })
713 })
714 .collect::<Result<Vec<_>, _>>()?;
715 pattern.push((duration, bandwidths));
716 }
717 Ok(pattern)
718 }
719 }
720
721 d.deserialize_seq(V).map(Serde)
722 }
723 }
724
725 impl ser::Serialize for Serde<Vec<(Duration, Vec<Bandwidth>)>> {
726 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
727 where
728 S: ser::Serializer,
729 {
730 let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
731 for (duration, bandwidths) in &self.0 {
732 let time = humantime_serde::re::humantime::format_duration(*duration).to_string();
733 let bandwidths = bandwidths
734 .iter()
735 .map(|item| human_bandwidth::format_bandwidth(*item).to_string())
736 .collect::<Vec<_>>();
737 seq.serialize_element(&(time, bandwidths))?;
738 }
739 seq.end()
740 }
741 }
742
743 impl ser::Serialize for Serde<&Vec<(Duration, Vec<Bandwidth>)>> {
744 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
745 where
746 S: ser::Serializer,
747 {
748 let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
749 for (duration, bandwidths) in self.0 {
750 let time = humantime_serde::re::humantime::format_duration(*duration).to_string();
751 let bandwidths = bandwidths
752 .iter()
753 .map(|item| human_bandwidth::format_bandwidth(*item).to_string())
754 .collect::<Vec<_>>();
755 seq.serialize_element(&(time, bandwidths))?;
756 }
757 seq.end()
758 }
759 }
760}
761
762impl BwTrace for StaticBw {
763 fn next_bw(&mut self) -> Option<(Bandwidth, Duration)> {
764 if let Some(duration) = self.duration.take() {
765 if duration.is_zero() {
766 None
767 } else {
768 Some((self.bw, duration))
769 }
770 } else {
771 None
772 }
773 }
774}
775
776impl<Rng: RngCore + Send> BwTrace for NormalizedBw<Rng> {
777 fn next_bw(&mut self) -> Option<(Bandwidth, Duration)> {
778 if self.duration.is_zero() {
779 None
780 } else {
781 let bw = self.sample() as u64;
782 let mut bw = Bandwidth::from_bps(bw);
783 if let Some(lower_bound) = self.lower_bound {
784 bw = bw.max(lower_bound);
785 }
786 if let Some(upper_bound) = self.upper_bound {
787 bw = bw.min(upper_bound);
788 }
789 let duration = self.step.min(self.duration);
790 self.duration -= duration;
791 Some((bw, duration))
792 }
793 }
794}
795
796impl<Rng: RngCore + Send> BwTrace for SawtoothBw<Rng> {
797 fn next_bw(&mut self) -> Option<(Bandwidth, Duration)> {
798 if self.duration.is_zero() {
799 None
800 } else {
801 let current = self.current.as_secs_f64();
802 let change_point = self.interval.as_secs_f64() * self.duty_ratio;
803 let base_bw = if current < change_point {
804 let ratio = current / change_point;
805 self.bottom + (self.top - self.bottom).mul_f64(ratio)
806 } else {
807 let ratio = (current - change_point) / (self.interval.as_secs_f64() - change_point);
808 self.top - (self.top - self.bottom).mul_f64(ratio)
809 };
810 let mut offset = self.noise.sample(&mut self.rng);
811 if let Some(upper_noise_bound) = self.upper_noise_bound {
812 offset = offset.min(upper_noise_bound.as_bps() as f64);
813 }
814 if let Some(lower_noise_bound) = self.lower_noise_bound {
815 offset = offset.max(-(lower_noise_bound.as_bps() as f64));
816 }
817 let bw = Bandwidth::from_bps((base_bw.as_bps() as f64 + offset) as u64);
818 let duration = self.step.min(self.duration);
819 self.duration -= duration;
820 self.current += duration;
821 if self.current >= self.interval {
822 self.current -= self.interval;
823 }
824 Some((bw, duration))
825 }
826 }
827}
828
829impl BwTrace for RepeatedBwPattern {
830 fn next_bw(&mut self) -> Option<(Bandwidth, Duration)> {
831 if self.pattern.is_empty() || (self.count != 0 && self.current_cycle >= self.count) {
832 None
833 } else {
834 if self.current_model.is_none() {
835 self.current_model = Some(self.pattern[self.current_pattern].clone().into_model());
836 }
837 match self.current_model.as_mut().unwrap().next_bw() {
838 Some(bw) => Some(bw),
839 None => {
840 self.current_model = None;
841 self.current_pattern += 1;
842 if self.current_pattern >= self.pattern.len() {
843 self.current_pattern = 0;
844 self.current_cycle += 1;
845 if self.count != 0 && self.current_cycle >= self.count {
846 return None;
847 }
848 }
849 self.next_bw()
850 }
851 }
852 }
853 }
854}
855
856impl BwTrace for TraceBw {
857 fn next_bw(&mut self) -> Option<(Bandwidth, Duration)> {
858 let result = self
859 .pattern
860 .get(self.outer_index)
861 .and_then(|(duration, bandwidth)| {
862 bandwidth
863 .get(self.inner_index)
864 .map(|bandwidth| (*bandwidth, *duration))
865 });
866 if result.is_some() {
867 if self.pattern[self.outer_index].1.len() > self.inner_index + 1 {
868 self.inner_index += 1;
869 } else {
870 self.outer_index += 1;
871 self.inner_index = 0;
872 }
873 }
874 result
875 }
876}
877
878impl<Rng: RngCore> NormalizedBw<Rng> {
879 pub fn sample(&mut self) -> f64 {
880 self.normal.sample(&mut self.rng)
881 }
882}
883
884impl StaticBwConfig {
885 pub fn new() -> Self {
886 Self {
887 bw: None,
888 duration: None,
889 }
890 }
891
892 pub fn bw(mut self, bw: Bandwidth) -> Self {
893 self.bw = Some(bw);
894 self
895 }
896
897 pub fn duration(mut self, duration: Duration) -> Self {
898 self.duration = Some(duration);
899 self
900 }
901
902 pub fn build(self) -> StaticBw {
903 StaticBw {
904 bw: self.bw.unwrap_or_else(|| Bandwidth::from_mbps(12)),
905 duration: Some(self.duration.unwrap_or_else(|| Duration::from_secs(1))),
906 }
907 }
908}
909
910/// Convert a bandwidth to bps as u64 with saturating operation.
911macro_rules! saturating_bandwidth_as_bps_u64 {
912 ($bw:expr) => {
913 $bw.as_gbps()
914 .saturating_mul(1_000_000_000)
915 .saturating_add($bw.subgbps_bps() as u64)
916 };
917}
918
919impl NormalizedBwConfig {
920 /// Creates an uninitialized config
921 pub fn new() -> Self {
922 Self {
923 mean: None,
924 std_dev: None,
925 upper_bound: None,
926 lower_bound: None,
927 duration: None,
928 step: None,
929 seed: None,
930 }
931 }
932
933 /// Sets the mean
934 ///
935 /// If the mean is not set, 12Mbps will be used.
936 pub fn mean(mut self, mean: Bandwidth) -> Self {
937 self.mean = Some(mean);
938 self
939 }
940
941 /// Sets the standard deviation
942 ///
943 /// If the standard deviation is not set, 0Mbps will be used.
944 pub fn std_dev(mut self, std_dev: Bandwidth) -> Self {
945 self.std_dev = Some(std_dev);
946 self
947 }
948
949 /// Sets the upper bound
950 ///
951 /// If the upper bound is not set, the upper bound will be the one of [`Bandwidth`].
952 pub fn upper_bound(mut self, upper_bound: Bandwidth) -> Self {
953 self.upper_bound = Some(upper_bound);
954 self
955 }
956
957 /// Sets the lower bound
958 ///
959 /// If the lower bound is not set, the lower bound will be the one of [`Bandwidth`].
960 pub fn lower_bound(mut self, lower_bound: Bandwidth) -> Self {
961 self.lower_bound = Some(lower_bound);
962 self
963 }
964
965 /// Sets the total duration of the trace
966 ///
967 /// If the total duration is not set, 1 second will be used.
968 pub fn duration(mut self, duration: Duration) -> Self {
969 self.duration = Some(duration);
970 self
971 }
972
973 /// Sets the duration of each value
974 ///
975 /// If the step is not set, 1ms will be used.
976 pub fn step(mut self, step: Duration) -> Self {
977 self.step = Some(step);
978 self
979 }
980
981 /// Set the seed for a random generator
982 ///
983 /// If the seed is not set, `42` will be used.
984 pub fn seed(mut self, seed: u64) -> Self {
985 self.seed = Some(seed);
986 self
987 }
988
989 /// Allows to use a randomly generated seed
990 ///
991 /// This is equivalent to: `self.seed(rand::random())`
992 pub fn random_seed(mut self) -> Self {
993 self.seed = Some(rand::random());
994 self
995 }
996
997 /// Creates a new [`NormalizedBw`] corresponding to this config.
998 ///
999 /// The created model will use [`StdRng`] as source of randomness (the call is equivalent to `self.build_with_rng::<StdRng>()`).
1000 /// It should be sufficient for most cases, but [`StdRng`] is not a portable random number generator,
1001 /// so one may want to use a portable random number generator like [`ChaCha`](https://crates.io/crates/rand_chacha),
1002 /// to this end one can use [`build_with_rng`](Self::build_with_rng).
1003 pub fn build(self) -> NormalizedBw {
1004 self.build_with_rng()
1005 }
1006
1007 /// Creates a new [`NormalizedBw`] corresponding to this config.
1008 ///
1009 /// Unlike [`build`](Self::build), this method let you choose the random generator.
1010 ///
1011 /// # Example
1012 /// ```rust
1013 /// # use netem_trace::model::NormalizedBwConfig;
1014 /// # use netem_trace::{Bandwidth, BwTrace};
1015 /// # use std::time::Duration;
1016 /// # use rand::rngs::StdRng;
1017 /// # use rand_chacha::ChaCha20Rng;
1018 ///
1019 /// let normal_bw = NormalizedBwConfig::new()
1020 /// .mean(Bandwidth::from_mbps(12))
1021 /// .std_dev(Bandwidth::from_mbps(1))
1022 /// .duration(Duration::from_millis(3))
1023 /// .seed(42);
1024 ///
1025 /// let mut default_build = normal_bw.clone().build();
1026 /// let mut std_build = normal_bw.clone().build_with_rng::<StdRng>();
1027 /// // ChaCha is deterministic and portable, unlike StdRng
1028 /// let mut chacha_build = normal_bw.clone().build_with_rng::<ChaCha20Rng>();
1029 ///
1030 /// for cha in [12044676, 11754367, 11253775] {
1031 /// let default = default_build.next_bw();
1032 /// let std = std_build.next_bw();
1033 /// let chacha = chacha_build.next_bw();
1034 ///
1035 /// assert!(default.is_some());
1036 /// assert_eq!(default, std);
1037 /// assert_ne!(default, chacha);
1038 /// assert_eq!(chacha, Some((Bandwidth::from_bps(cha), Duration::from_millis(1))));
1039 /// }
1040 ///
1041 /// assert_eq!(default_build.next_bw(), None);
1042 /// assert_eq!(std_build.next_bw(), None);
1043 /// assert_eq!(chacha_build.next_bw(), None);
1044 /// ```
1045 pub fn build_with_rng<Rng: SeedableRng + RngCore>(self) -> NormalizedBw<Rng> {
1046 let mean = self.mean.unwrap_or_else(|| Bandwidth::from_mbps(12));
1047 let std_dev = self.std_dev.unwrap_or_else(|| Bandwidth::from_mbps(0));
1048 let upper_bound = self.upper_bound;
1049 let lower_bound = self.lower_bound;
1050 let duration = self.duration.unwrap_or_else(|| Duration::from_secs(1));
1051 let step = self.step.unwrap_or_else(|| Duration::from_millis(1));
1052 let seed = self.seed.unwrap_or(DEFAULT_RNG_SEED);
1053 let rng = Rng::seed_from_u64(seed);
1054 let bw_mean = saturating_bandwidth_as_bps_u64!(mean) as f64;
1055 let bw_std_dev = saturating_bandwidth_as_bps_u64!(std_dev) as f64;
1056 let normal: Normal<f64> = Normal::new(bw_mean, bw_std_dev).unwrap();
1057 NormalizedBw {
1058 mean,
1059 std_dev,
1060 upper_bound,
1061 lower_bound,
1062 duration,
1063 step,
1064 seed,
1065 rng,
1066 normal,
1067 }
1068 }
1069}
1070
1071#[cfg(feature = "truncated-normal")]
1072impl NormalizedBwConfig {
1073 /// This is another implementation for converting NormalizedBwConfig into NormalizedBw, where the impact
1074 /// of truncation (`lower_bound` and `upper_bound` field) on the mathematical expectation of the distribution
1075 /// is taking account by modifying the center of the distribution.
1076 ///
1077 /// ## Examples
1078 ///
1079 /// ```
1080 ///
1081 /// # use netem_trace::model::NormalizedBwConfig;
1082 /// # use netem_trace::{Bandwidth, Duration, BwTrace};
1083 /// # use crate::netem_trace::model::Forever;
1084 /// let normal_bw = NormalizedBwConfig::new()
1085 /// .mean(Bandwidth::from_mbps(12))
1086 /// .std_dev(Bandwidth::from_mbps(12))
1087 /// .duration(Duration::from_secs(100))
1088 /// .step(Duration::from_millis(1))
1089 /// .seed(42);
1090 ///
1091 /// let mut default_build = normal_bw.clone().build();
1092 /// let mut truncate_build = normal_bw.clone().build_truncated();
1093 ///
1094 /// fn avg_mbps(mut model: impl BwTrace) -> f64{
1095 /// let mut count = 0;
1096 /// std::iter::from_fn( move ||{
1097 /// model.next_bw().map(|b| b.0.as_gbps_f64() * 1000.0)
1098 /// }).inspect(|_| count += 1).sum::<f64>() / count as f64
1099 /// }
1100 ///
1101 /// assert_eq!(avg_mbps(default_build), 12.974758080079994); // significantly higher than the expected mean
1102 /// assert_eq!(avg_mbps(truncate_build), 11.97642456625989);
1103 ///
1104 /// let normal_bw = NormalizedBwConfig::new()
1105 /// .mean(Bandwidth::from_mbps(12))
1106 /// .std_dev(Bandwidth::from_mbps(12))
1107 /// .duration(Duration::from_secs(100))
1108 /// .lower_bound(Bandwidth::from_mbps(8))
1109 /// .upper_bound(Bandwidth::from_mbps(20))
1110 /// .step(Duration::from_millis(1))
1111 /// .seed(42);
1112 ///
1113 /// let mut default_build = normal_bw.clone().build();
1114 /// let mut truncate_build = normal_bw.clone().build_truncated();
1115 ///
1116 /// assert_eq!(avg_mbps(default_build), 13.221356471729928); // significantly higher than the expected mean
1117 /// assert_eq!(avg_mbps(truncate_build), 11.978819427569897);
1118 ///
1119 /// ```
1120 pub fn build_truncated(self) -> NormalizedBw {
1121 self.build_truncated_with_rng()
1122 }
1123
1124 /// Similar to [`build_truncated`](Self::build_truncated) but let you choose the random generator.
1125 ///
1126 /// See [`build`](Self::build) for details about the reason for using another random number generator than [`StdRng`].
1127 pub fn build_truncated_with_rng<Rng: SeedableRng + RngCore>(mut self) -> NormalizedBw<Rng> {
1128 let mean = self
1129 .mean
1130 .unwrap_or_else(|| Bandwidth::from_mbps(12))
1131 .as_gbps_f64();
1132 let sigma = self
1133 .std_dev
1134 .unwrap_or_else(|| Bandwidth::from_mbps(0))
1135 .as_gbps_f64()
1136 / mean;
1137 let lower = self
1138 .lower_bound
1139 .unwrap_or_else(|| Bandwidth::from_mbps(0))
1140 .as_gbps_f64()
1141 / mean;
1142 let upper = self.upper_bound.map(|upper| upper.as_gbps_f64() / mean);
1143 let new_mean = mean * solve(1f64, sigma, Some(lower), upper).unwrap_or(1f64);
1144 self.mean = Some(Bandwidth::from_gbps_f64(new_mean));
1145 self.build_with_rng()
1146 }
1147}
1148
1149impl SawtoothBwConfig {
1150 /// Creates an uninitialized config
1151 pub fn new() -> Self {
1152 Self {
1153 bottom: None,
1154 top: None,
1155 interval: None,
1156 duty_ratio: None,
1157 duration: None,
1158 step: None,
1159 seed: None,
1160 std_dev: None,
1161 upper_noise_bound: None,
1162 lower_noise_bound: None,
1163 }
1164 }
1165
1166 pub fn bottom(mut self, bottom: Bandwidth) -> Self {
1167 self.bottom = Some(bottom);
1168 self
1169 }
1170
1171 pub fn top(mut self, top: Bandwidth) -> Self {
1172 self.top = Some(top);
1173 self
1174 }
1175
1176 pub fn interval(mut self, interval: Duration) -> Self {
1177 self.interval = Some(interval);
1178 self
1179 }
1180
1181 pub fn duty_ratio(mut self, duty_ratio: f64) -> Self {
1182 self.duty_ratio = Some(duty_ratio);
1183 self
1184 }
1185
1186 /// Sets the total duration of the trace
1187 ///
1188 /// If the total duration is not set, 1 second will be used.
1189 pub fn duration(mut self, duration: Duration) -> Self {
1190 self.duration = Some(duration);
1191 self
1192 }
1193
1194 /// Sets the duration of each value
1195 ///
1196 /// If the step is not set, 1ms will be used.
1197 pub fn step(mut self, step: Duration) -> Self {
1198 self.step = Some(step);
1199 self
1200 }
1201
1202 /// Set the seed for a random generator
1203 ///
1204 /// If the seed is not set, `42` will be used.
1205 pub fn seed(mut self, seed: u64) -> Self {
1206 self.seed = Some(seed);
1207 self
1208 }
1209
1210 /// Allows to use a randomly generated seed
1211 ///
1212 /// This is equivalent to: `self.seed(rand::random())`
1213 pub fn random_seed(mut self) -> Self {
1214 self.seed = Some(rand::random());
1215 self
1216 }
1217
1218 /// Sets the standard deviation
1219 ///
1220 /// If the standard deviation is not set, 0Mbps will be used.
1221 pub fn std_dev(mut self, std_dev: Bandwidth) -> Self {
1222 self.std_dev = Some(std_dev);
1223 self
1224 }
1225
1226 pub fn upper_noise_bound(mut self, upper_noise_bound: Bandwidth) -> Self {
1227 self.upper_noise_bound = Some(upper_noise_bound);
1228 self
1229 }
1230
1231 pub fn lower_noise_bound(mut self, lower_noise_bound: Bandwidth) -> Self {
1232 self.lower_noise_bound = Some(lower_noise_bound);
1233 self
1234 }
1235
1236 /// Creates a new [`SawtoothBw`] corresponding to this config.
1237 ///
1238 /// The created model will use [`StdRng`] as source of randomness (the call is equivalent to `self.build_with_rng::<StdRng>()`).
1239 /// It should be sufficient for most cases, but [`StdRng`] is not a portable random number generator,
1240 /// so one may want to use a portable random number generator like [`ChaCha`](https://crates.io/crates/rand_chacha),
1241 /// to this end one can use [`build_with_rng`](Self::build_with_rng).
1242 pub fn build(self) -> SawtoothBw {
1243 self.build_with_rng()
1244 }
1245
1246 /// Creates a new [`SawtoothBw`] corresponding to this config.
1247 ///
1248 /// Unlike [`build`](Self::build), this method let you choose the random generator.
1249 ///
1250 /// # Example
1251 /// ```rust
1252 /// # use netem_trace::model::SawtoothBwConfig;
1253 /// # use netem_trace::{Bandwidth, BwTrace};
1254 /// # use std::time::Duration;
1255 /// # use rand::rngs::StdRng;
1256 /// # use rand_chacha::ChaCha20Rng;
1257 ///
1258 /// let sawtooth_bw = SawtoothBwConfig::new()
1259 /// .bottom(Bandwidth::from_mbps(12))
1260 /// .top(Bandwidth::from_mbps(16))
1261 /// .std_dev(Bandwidth::from_mbps(1))
1262 /// .duration(Duration::from_millis(3))
1263 /// .interval(Duration::from_millis(5))
1264 /// .duty_ratio(0.8)
1265 /// .seed(42);
1266 ///
1267 /// let mut default_build = sawtooth_bw.clone().build();
1268 /// let mut std_build = sawtooth_bw.clone().build_with_rng::<StdRng>();
1269 /// // ChaCha is deterministic and portable, unlike StdRng
1270 /// let mut chacha_build = sawtooth_bw.clone().build_with_rng::<ChaCha20Rng>();
1271 ///
1272 /// for cha in [12044676, 12754367, 13253775] {
1273 /// let default = default_build.next_bw();
1274 /// let std = std_build.next_bw();
1275 /// let chacha = chacha_build.next_bw();
1276 ///
1277 /// assert!(default.is_some());
1278 /// assert_eq!(default, std);
1279 /// assert_ne!(default, chacha);
1280 /// assert_eq!(chacha, Some((Bandwidth::from_bps(cha), Duration::from_millis(1))));
1281 /// }
1282 ///
1283 /// assert_eq!(default_build.next_bw(), None);
1284 /// assert_eq!(std_build.next_bw(), None);
1285 /// assert_eq!(chacha_build.next_bw(), None);
1286 /// ```
1287 pub fn build_with_rng<Rng: RngCore + SeedableRng>(self) -> SawtoothBw<Rng> {
1288 let bottom = self.bottom.unwrap_or_else(|| Bandwidth::from_mbps(0));
1289 let top = self.top.unwrap_or_else(|| Bandwidth::from_mbps(12));
1290 if bottom > top {
1291 panic!("SawtoothBw: bottom bw must be less than top bw");
1292 }
1293 let interval = self.interval.unwrap_or_else(|| Duration::from_secs(1));
1294 let duty_ratio = self.duty_ratio.unwrap_or(0.5);
1295 let duration = self.duration.unwrap_or_else(|| Duration::from_secs(1));
1296 let step = self.step.unwrap_or_else(|| Duration::from_millis(1));
1297 let seed = self.seed.unwrap_or(DEFAULT_RNG_SEED);
1298 let rng = Rng::seed_from_u64(seed);
1299 let std_dev = self.std_dev.unwrap_or_else(|| Bandwidth::from_mbps(0));
1300 let upper_noise_bound = self.upper_noise_bound;
1301 let lower_noise_bound = self.lower_noise_bound;
1302 let current = Duration::ZERO;
1303 let bw_std_dev = saturating_bandwidth_as_bps_u64!(std_dev) as f64;
1304 let noise: Normal<f64> = Normal::new(0.0, bw_std_dev).unwrap();
1305 SawtoothBw {
1306 bottom,
1307 top,
1308 interval,
1309 duty_ratio,
1310 duration,
1311 step,
1312 seed,
1313 std_dev,
1314 upper_noise_bound,
1315 lower_noise_bound,
1316 current,
1317 rng,
1318 noise,
1319 }
1320 }
1321}
1322
1323impl RepeatedBwPatternConfig {
1324 pub fn new() -> Self {
1325 Self {
1326 pattern: vec![],
1327 count: 0,
1328 }
1329 }
1330
1331 pub fn pattern(mut self, pattern: Vec<Box<dyn BwTraceConfig>>) -> Self {
1332 self.pattern = pattern;
1333 self
1334 }
1335
1336 pub fn count(mut self, count: usize) -> Self {
1337 self.count = count;
1338 self
1339 }
1340
1341 pub fn build(self) -> RepeatedBwPattern {
1342 RepeatedBwPattern {
1343 pattern: self.pattern,
1344 count: self.count,
1345 current_model: None,
1346 current_cycle: 0,
1347 current_pattern: 0,
1348 }
1349 }
1350}
1351
1352macro_rules! impl_bw_trace_config {
1353 ($name:ident) => {
1354 #[cfg_attr(feature = "serde", typetag::serde)]
1355 impl BwTraceConfig for $name {
1356 fn into_model(self: Box<$name>) -> Box<dyn BwTrace> {
1357 Box::new(self.build())
1358 }
1359 }
1360 };
1361}
1362
1363impl_bw_trace_config!(StaticBwConfig);
1364impl_bw_trace_config!(NormalizedBwConfig);
1365impl_bw_trace_config!(SawtoothBwConfig);
1366impl_bw_trace_config!(RepeatedBwPatternConfig);
1367impl_bw_trace_config!(TraceBwConfig);
1368
1369/// Turn a [`BwTraceConfig`] into a forever repeated [`RepeatedBwPatternConfig`].
1370pub trait Forever: BwTraceConfig {
1371 fn forever(self) -> RepeatedBwPatternConfig;
1372}
1373
1374/// Implement the [`Forever`] trait for the bandwidth trace model config (any struct implements [`BwTraceConfig`]).
1375#[macro_export]
1376macro_rules! impl_forever {
1377 ($name:ident) => {
1378 impl Forever for $name {
1379 fn forever(self) -> RepeatedBwPatternConfig {
1380 RepeatedBwPatternConfig::new()
1381 .pattern(vec![Box::new(self)])
1382 .count(0)
1383 }
1384 }
1385 };
1386}
1387
1388impl_forever!(StaticBwConfig);
1389impl_forever!(NormalizedBwConfig);
1390impl_forever!(SawtoothBwConfig);
1391impl_forever!(TraceBwConfig);
1392
1393impl Forever for RepeatedBwPatternConfig {
1394 fn forever(self) -> RepeatedBwPatternConfig {
1395 self.count(0)
1396 }
1397}