use crate::stats::{Direction, StatsMap};
use ringbuf::traits::RingBuffer;
use std::time::Instant;
use super::StatsAggregator;
impl StatsAggregator {
pub fn tick(&mut self, hash_map: StatsMap) {
let now = Instant::now();
let elapsed_secs = match self.last_tick_time {
Some(last_time) => {
let duration = now.duration_since(last_time);
let secs = duration.as_secs_f64();
if secs < 0.001 {
1.0
} else if secs > 5.0 {
5.0
} else {
secs
}
}
None => 1.0, };
let init = [0u128; 4]; let sum =
hash_map
.iter()
.map(|(k, v)| (&k.direction, v.size))
.fold(init, |mut acc, (di, si)| {
match di {
Direction::Outgoing => acc[0] += si,
Direction::Internet => acc[0] += si, Direction::Incoming => acc[1] += si,
Direction::Local => acc[2] += si,
Direction::None => acc[3] += si,
}
acc
});
self.session_stats.total_bits_up += sum[0];
self.session_stats.total_bits_down += sum[1];
self.speed_buffer.push_overwrite(sum.to_vec());
hash_map.keys().for_each(|key| {
self.stat_keys_buffer.push_overwrite(*key);
});
self.stats_buffer.push_overwrite(hash_map);
self.update_pairs_stats_buffer();
self.update_hosts_stats_buffer();
self.update_total_speed();
self.update_tcp_states();
self.update_quality_metrics();
self.last_tick_time = Some(now);
self.last_elapsed_secs = elapsed_secs;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::stats::{Direction, StatKey, StatValues};
use std::collections::HashMap;
use std::net::Ipv4Addr;
macro_rules! ip {
($a:expr, $b:expr, $c:expr, $d:expr) => {
Ipv4Addr::new($a, $b, $c, $d)
};
}
#[test]
fn test_rate_calculation_with_normal_tick() {
let mut agg = StatsAggregator::new_with_window_size(10);
let mut stats = HashMap::new();
let key = StatKey {
src_port: 123,
dst_port: 456,
src_ip: ip!(192, 168, 1, 2),
dst_ip: ip!(10, 0, 0, 1),
direction: Direction::Incoming,
protocol: 6,
tcp_syn: false,
tcp_ack: false,
tcp_fin: false,
tcp_rst: false,
};
stats.insert(
key,
StatValues {
size: 1000,
last_timestamp: None,
last_seq: None,
last_ack: None,
},
);
agg.tick(stats);
let instant = agg.total_speed_instant();
assert_eq!(instant.input, 1000);
assert_eq!(agg.last_elapsed_secs, 1.0);
}
#[test]
fn test_elapsed_time_tracking() {
let mut agg = StatsAggregator::new_with_window_size(10);
agg.tick(HashMap::new());
assert!(agg.last_tick_time.is_some());
assert_eq!(agg.last_elapsed_secs, 1.0); }
#[test]
fn test_session_stats_accumulation() {
let mut agg = StatsAggregator::new_with_window_size(10);
let mut stats = HashMap::new();
let key = StatKey {
src_port: 123,
dst_port: 456,
src_ip: ip!(192, 168, 1, 2),
dst_ip: ip!(10, 0, 0, 1),
direction: Direction::Outgoing,
protocol: 6,
tcp_syn: false,
tcp_ack: false,
tcp_fin: false,
tcp_rst: false,
};
stats.insert(
key,
StatValues {
size: 1000,
last_timestamp: None,
last_seq: None,
last_ack: None,
},
);
agg.tick(stats);
assert_eq!(agg.session_stats.total_bits_up, 1000);
assert_eq!(agg.session_stats.total_bits_down, 0);
let mut stats2 = HashMap::new();
stats2.insert(
key,
StatValues {
size: 2000,
last_timestamp: None,
last_seq: None,
last_ack: None,
},
);
agg.tick(stats2);
assert_eq!(agg.session_stats.total_bits_up, 3000);
}
#[test]
fn test_elapsed_time_bounds() {
let mut agg = StatsAggregator::new_with_window_size(10);
agg.tick(HashMap::new());
assert_eq!(agg.last_elapsed_secs, 1.0);
agg.tick(HashMap::new());
assert!(agg.last_elapsed_secs >= 0.001); assert!(agg.last_elapsed_secs <= 5.0); }
}