1use crate::models;
2use crate::stats::Stats;
3use crate::structs::{SeededData, TimePoint};
4
5use chrono::FixedOffset;
6use chrono::{DateTime, Timelike};
7use crossbeam_channel::Sender;
8use rand_core::{Rng, SeedableRng};
9use rand_distr::Distribution;
10use rand_distr::Poisson;
11use rand_distr::Uniform;
12use rand_pcg::Pcg32;
13use serde::Deserialize;
14use std::sync::Arc;
15use std::thread;
16use std::time::{Duration, SystemTime, UNIX_EPOCH};
17
18const WINDOW_WIDTH_IN_SECS: u64 = 5;
19
20pub trait Stage1:
24 Iterator<Item = SeededData<TimePoint>> + Clone + std::marker::Send + 'static
25{
26 fn get_initial_ts(&self) -> Duration;
28}
29
30#[derive(Debug, Clone)]
33pub struct BinBasedGenerator {
34 next_ts: Duration, initial_ts: Duration,
36 remaining_flows: u64,
37 lambdas: Vec<f64>,
38 current_distrib: Option<Poisson<f64>>,
39 total_flow_count: u64,
40 time_distrib: Uniform<u64>,
41 window_rng: Pcg32,
42 flow_rng: Pcg32,
43 net_injection: bool,
44 remaining_windows: Option<u64>,
45 dest_tz_offset: FixedOffset,
46}
47
48impl Stage1 for BinBasedGenerator {
49 fn get_initial_ts(&self) -> Duration {
50 self.initial_ts
51 }
52}
53
54impl Iterator for BinBasedGenerator {
55 type Item = SeededData<TimePoint>;
56
57 fn next(&mut self) -> Option<Self::Item> {
58 while self.remaining_flows == 0 {
59 if self.net_injection {
60 if SystemTime::now().duration_since(UNIX_EPOCH).unwrap() > self.next_ts {
62 log::warn!("Generation is too slow");
63 } else {
64 thread::sleep(
65 self.next_ts
66 .saturating_sub(SystemTime::now().duration_since(UNIX_EPOCH).unwrap()),
67 );
68 }
69 }
70
71 let ok = self.start_new_window();
72 if !ok {
73 return None;
75 }
76 }
77 self.remaining_flows -= 1;
78 self.total_flow_count += 1;
79 let unix_time = Duration::from_millis(self.time_distrib.sample(&mut self.flow_rng.clone()));
80 let date_time = DateTime::from_timestamp_secs(unix_time.as_secs() as i64)
81 .unwrap()
82 .with_timezone(&self.dest_tz_offset);
83 Some(SeededData {
84 seed: self.flow_rng.next_u64(),
85 data: TimePoint {
86 unix_time,
87 date_time,
88 },
89 })
90 }
91}
92
93fn get_poisson(lambdas: &[f64], dest_tz_offset: FixedOffset, ts: Duration) -> Option<Poisson<f64>> {
94 let secs = DateTime::from_timestamp_secs(ts.as_secs() as i64)
95 .unwrap()
96 .with_timezone(&dest_tz_offset)
97 .time()
98 .num_seconds_from_midnight();
99 let secs_per_bin = (60 * 60 * 24 / lambdas.len()) as u32;
101 let index = ((secs / secs_per_bin) as usize).min(lambdas.len() - 1);
102 Poisson::new(lambdas[index]).ok()
104}
105
106fn get_lambdas(flow_per_day: Option<u64>, bins: &[u64]) -> Vec<f64> {
110 let bin_count: f64 = bins.len() as f64;
111 let window_per_bin: f64 = 60. * 60. * 24. / (WINDOW_WIDTH_IN_SECS as f64) / bin_count;
112 match flow_per_day {
113 Some(flow_per_day) => {
114 let sum: f64 = bins.iter().sum::<u64>() as f64;
115 bins.iter()
119 .map(|val| (*val as f64) / sum * (flow_per_day as f64) / window_per_bin)
120 .collect()
121 }
122 None => bins
123 .iter()
124 .map(|val| (*val as f64) / window_per_bin)
125 .collect(),
126 }
127}
128
129impl BinBasedGenerator {
130 pub fn new(
131 seed: Option<u64>,
132 net_injection: bool,
133 flow_per_day: Option<u64>,
134 profile: Arc<TimeModel>,
135 initial_ts: Duration,
136 total_duration: Option<Duration>,
137 dest_tz_offset: FixedOffset,
138 ) -> Self {
139 let next_ts = initial_ts;
140 let time_distrib = Uniform::new(
141 next_ts.as_millis() as u64,
142 next_ts.as_millis() as u64 + 1000 * WINDOW_WIDTH_IN_SECS,
143 )
144 .unwrap();
145 let window_rng = match seed {
146 Some(s) => Pcg32::seed_from_u64(s),
147 None => Pcg32::from_rng(&mut rand::rng()),
148 };
149 let remaining_windows = total_duration.map(|d| {
150 d.div_duration_f32(Duration::from_secs(WINDOW_WIDTH_IN_SECS))
151 .ceil() as u64
152 });
153
154 let lambdas = get_lambdas(flow_per_day, &profile.bins);
155
156 let mut generator = BinBasedGenerator {
157 net_injection,
158 initial_ts,
159 next_ts,
160 lambdas,
161 current_distrib: None, remaining_flows: 0,
163 total_flow_count: 0,
164 window_rng,
165 flow_rng: Pcg32::seed_from_u64(0), time_distrib,
167 remaining_windows,
168 dest_tz_offset,
169 };
170 generator.start_new_window();
171 generator
172 }
173
174 #[cfg(feature = "net_injection")]
175 pub fn new_for_injection(
176 seed: Option<u64>,
177 total_duration: Option<Duration>,
178 flow_per_day: Option<u64>,
179 profile: TimeModel,
180 deterministic: bool,
181 ) -> Self {
182 let current_date = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
183 let window_count_since_unix_epoch =
184 ((current_date + Duration::from_secs(WINDOW_WIDTH_IN_SECS)).as_secs_f64()
185 / (WINDOW_WIDTH_IN_SECS as f64))
186 .ceil() as u64;
187 let mut window_rng = match seed {
188 Some(s) => Pcg32::seed_from_u64(s),
189 None => Pcg32::new(0xcafef00dd15ea5e5, 0xa02bdbf7bb3c0a7), };
191 if !deterministic {
192 window_rng.advance(2 * window_count_since_unix_epoch);
195 }
196 let next_ts = Duration::from_secs(window_count_since_unix_epoch * WINDOW_WIDTH_IN_SECS);
197 let time_distrib = Uniform::new(
198 next_ts.as_millis() as u64,
199 next_ts.as_millis() as u64 + 1000 * WINDOW_WIDTH_IN_SECS,
200 )
201 .unwrap();
202 let remaining_windows = total_duration.map(|d| {
203 d.div_duration_f32(Duration::from_secs(WINDOW_WIDTH_IN_SECS))
204 .ceil() as u64
205 });
206 let lambdas = get_lambdas(flow_per_day, profile.bins);
207
208 let mut generator = BinBasedGenerator {
209 net_injection: true,
210 initial_ts: next_ts,
211 next_ts,
212 lambdas,
213 current_distrib: None, remaining_flows: 0,
215 total_flow_count: 0,
216 window_rng,
217 flow_rng: Pcg32::seed_from_u64(0), time_distrib,
219 remaining_windows,
220 dest_tz_offset: *chrono::Local::now().fixed_offset().offset(), };
223 generator.start_new_window();
224 generator
225 }
226
227 fn start_new_window(&mut self) -> bool {
230 self.flow_rng = Pcg32::seed_from_u64(self.window_rng.next_u64());
231 if let Some(ref mut r) = self.remaining_windows {
232 if *r == 0 {
233 return false;
234 }
235 *r -= 1;
236 }
237
238 self.current_distrib = get_poisson(&self.lambdas, self.dest_tz_offset, self.next_ts);
239
240 if self.current_distrib.is_none() {
241 log_once::warn_once!("Time window without any flow according to the time model");
242 }
243
244 self.remaining_flows = self
245 .current_distrib
246 .map_or(0, |s| s.sample(&mut self.flow_rng.clone()) as u64);
247 self.time_distrib = Uniform::new(
248 self.next_ts.as_millis() as u64,
249 self.next_ts.as_millis() as u64 + 1000 * WINDOW_WIDTH_IN_SECS,
250 )
251 .unwrap();
252 self.next_ts += Duration::from_secs(WINDOW_WIDTH_IN_SECS);
253 true
254 }
255}
256
257#[derive(Deserialize, Debug, Clone)]
258#[serde(from = "Config")]
259pub struct TimeModel {
260 bins: Vec<u64>, metadata: Metadata,
262}
263
264impl TimeModel {
265 pub fn from_source(m: &models::ModelsSource) -> Result<Self, String> {
266 let mut models: Vec<TimeModel> = vec![];
267 for s in m.get_time_profiles()? {
268 models.push(
269 serde_json::from_str(&s)
270 .map_err(|e| format!("Cannot parse the time model: {e}"))?,
271 );
272 }
273 assert!(!models.is_empty());
274
275 let profile = Self::from_list(models);
276 log::info!(
277 "Time model learned on {} has been loaded",
278 profile.metadata.input_file
279 );
280 Ok(profile)
281 }
282
283 fn from_list(mut models: Vec<TimeModel>) -> Self {
284 let metadata = Metadata {
285 creation_time: "On-the-fly".to_string(),
286 input_file: models
287 .iter()
288 .map(|m| m.metadata.input_file.clone())
289 .collect::<Vec<String>>()
290 .join(", "),
291 };
292 let overall_sum: u64 = models
295 .iter()
296 .map(|m: &TimeModel| -> u64 { m.bins.iter().sum() })
297 .sum();
298 let nb_models = models.len() as u64;
299 for m in &mut models {
300 let sum: u64 = m.bins.iter().sum();
301 m.bins = m
302 .bins
303 .clone()
304 .into_iter()
305 .map(|v| -> u64 { v * overall_sum / (sum * nb_models) })
306 .collect();
307 }
308 let mut bins: Vec<u64> = vec![];
309 assert!(!models.is_empty());
310 for i in 0..models[0].bins.len() {
311 bins.push(models.iter().map(|m| m.bins[i]).sum());
312 }
313 TimeModel { bins, metadata }
314 }
315}
316
317impl From<Config> for TimeModel {
318 fn from(c: Config) -> TimeModel {
319 TimeModel {
320 bins: c.histogram,
321 metadata: c.metadata,
322 }
323 }
324}
325
326#[derive(Deserialize, Debug, Clone)]
327struct Config {
328 histogram: Vec<u64>,
329 metadata: Metadata,
330}
331
332#[derive(Deserialize, Debug, Clone)]
333#[allow(unused)]
334struct Metadata {
335 creation_time: String,
336 input_file: String,
337}
338
339pub fn run_channel(
341 generator: impl Stage1,
342 tx_s1: Sender<SeededData<TimePoint>>,
343 stats: Arc<Stats>,
344) -> Result<(), Box<dyn std::error::Error>> {
345 log::trace!("Start S1 (channel)");
346 let initial_ts = generator.get_initial_ts();
347 for ts in generator {
348 if stats.should_stop() {
349 break;
350 }
351 stats.set_current_duration(
352 ts.data.unix_time.as_secs() - initial_ts.as_secs() + 2 * WINDOW_WIDTH_IN_SECS,
353 ); tx_s1.send(ts)?;
356 }
357 log::trace!("S1 stops");
358 Ok(())
359}
360
361pub fn run_vec(generator: impl Stage1) -> Vec<SeededData<TimePoint>> {
363 log::trace!("Start S1 (vec)");
364 let mut vector = vec![];
365 for ts in generator {
366 vector.push(ts);
367 }
368 log::trace!("S1 stops");
369 vector
370}