1use crate::device::NetworkStats;
2use std::collections::VecDeque;
3use std::time::Duration;
4#[cfg(test)]
5use std::time::SystemTime;
6
7pub struct StatsCalculator {
8 history: VecDeque<NetworkStats>,
10 window_size: Duration,
11
12 current_speed_in: u64,
14 current_speed_out: u64,
15 avg_speed_in: u64,
16 avg_speed_out: u64,
17 min_speed_in: u64,
18 min_speed_out: u64,
19 max_speed_in: u64,
20 max_speed_out: u64,
21
22 graph_data_in: VecDeque<(f64, f64)>, graph_data_out: VecDeque<(f64, f64)>,
25
26 total_bytes_in: u64,
28 total_bytes_out: u64,
29 total_packets_in: u64,
30 total_packets_out: u64,
31
32 first_sample: bool,
34}
35
36impl StatsCalculator {
37 pub fn new(window_size: Duration) -> Self {
38 Self {
39 history: VecDeque::new(),
40 window_size,
41 current_speed_in: 0,
42 current_speed_out: 0,
43 avg_speed_in: 0,
44 avg_speed_out: 0,
45 min_speed_in: 0,
46 min_speed_out: 0,
47 max_speed_in: 0,
48 max_speed_out: 0,
49 graph_data_in: VecDeque::new(),
50 graph_data_out: VecDeque::new(),
51 total_bytes_in: 0,
52 total_bytes_out: 0,
53 total_packets_in: 0,
54 total_packets_out: 0,
55 first_sample: true,
56 }
57 }
58
59 pub fn add_sample(&mut self, stats: NetworkStats) {
60 self.total_bytes_in = stats.bytes_in;
62 self.total_bytes_out = stats.bytes_out;
63 self.total_packets_in = stats.packets_in;
64 self.total_packets_out = stats.packets_out;
65
66 if let Some(previous) = self.history.back() {
68 let time_diff = stats
69 .timestamp
70 .duration_since(previous.timestamp)
71 .unwrap_or_default()
72 .as_secs_f64();
73
74 if time_diff > 0.0 {
75 let bytes_in_diff = self.calculate_diff(stats.bytes_in, previous.bytes_in);
77 let bytes_out_diff = self.calculate_diff(stats.bytes_out, previous.bytes_out);
78
79 self.current_speed_in = (bytes_in_diff as f64 / time_diff) as u64;
80 self.current_speed_out = (bytes_out_diff as f64 / time_diff) as u64;
81
82 if !self.first_sample {
84 self.update_min_max();
85 }
86
87 self.add_graph_data(&stats);
89 }
90 }
91
92 self.history.push_back(stats);
93 self.trim_old_samples();
94 self.calculate_averages();
95
96 if self.first_sample {
97 self.first_sample = false;
98 }
99 }
100
101 fn calculate_diff(&self, current: u64, previous: u64) -> u64 {
102 if current >= previous {
103 current - previous
104 } else {
105 let diff_32 = (u32::MAX as u64) - previous + current + 1;
108 let diff_64 = (u64::MAX) - previous + current + 1;
109
110 if diff_32 < diff_64 / 1000 {
112 diff_32
113 } else {
114 diff_64
115 }
116 }
117 }
118
119 fn update_min_max(&mut self) {
120 if self.current_speed_in < self.min_speed_in || self.min_speed_in == 0 {
121 self.min_speed_in = self.current_speed_in;
122 }
123 if self.current_speed_in > self.max_speed_in {
124 self.max_speed_in = self.current_speed_in;
125 }
126 if self.current_speed_out < self.min_speed_out || self.min_speed_out == 0 {
127 self.min_speed_out = self.current_speed_out;
128 }
129 if self.current_speed_out > self.max_speed_out {
130 self.max_speed_out = self.current_speed_out;
131 }
132 }
133
134 fn add_graph_data(&mut self, _stats: &NetworkStats) {
135 for (time, _) in self.graph_data_in.iter_mut() {
137 *time += 0.5; }
139 for (time, _) in self.graph_data_out.iter_mut() {
140 *time += 0.5; }
142
143 self.graph_data_in.retain(|(time, _)| *time <= 60.0);
145 self.graph_data_out.retain(|(time, _)| *time <= 60.0);
146
147 self.graph_data_in
149 .push_back((0.0, self.current_speed_in as f64));
150 self.graph_data_out
151 .push_back((0.0, self.current_speed_out as f64));
152
153 while self.graph_data_in.len() > 120 {
155 self.graph_data_in.pop_front();
156 }
157 while self.graph_data_out.len() > 120 {
158 self.graph_data_out.pop_front();
159 }
160 }
161
162 fn trim_old_samples(&mut self) {
163 if let Some(latest) = self.history.back() {
164 let cutoff = latest.timestamp - self.window_size;
165 while let Some(oldest) = self.history.front() {
166 if oldest.timestamp < cutoff {
167 self.history.pop_front();
168 } else {
169 break;
170 }
171 }
172 }
173 }
174
175 fn calculate_averages(&mut self) {
176 if self.history.len() < 2 {
177 return;
178 }
179
180 let first = &self.history[0];
181 let last = &self.history[self.history.len() - 1];
182
183 let time_span = last
184 .timestamp
185 .duration_since(first.timestamp)
186 .unwrap_or_default()
187 .as_secs_f64();
188
189 if time_span > 0.0 {
190 let bytes_in_diff = self.calculate_diff(last.bytes_in, first.bytes_in);
191 let bytes_out_diff = self.calculate_diff(last.bytes_out, first.bytes_out);
192
193 self.avg_speed_in = (bytes_in_diff as f64 / time_span) as u64;
194 self.avg_speed_out = (bytes_out_diff as f64 / time_span) as u64;
195 }
196 }
197
198 pub fn current_speed(&self) -> (u64, u64) {
200 (self.current_speed_in, self.current_speed_out)
201 }
202
203 pub fn average_speed(&self) -> (u64, u64) {
204 (self.avg_speed_in, self.avg_speed_out)
205 }
206
207 pub fn min_speed(&self) -> (u64, u64) {
208 (self.min_speed_in, self.min_speed_out)
209 }
210
211 pub fn max_speed(&self) -> (u64, u64) {
212 (self.max_speed_in, self.max_speed_out)
213 }
214
215 pub fn total_bytes(&self) -> (u64, u64) {
216 (self.total_bytes_in, self.total_bytes_out)
217 }
218
219 pub fn total_packets(&self) -> (u64, u64) {
220 (self.total_packets_in, self.total_packets_out)
221 }
222
223 pub fn graph_data_in(&self) -> &VecDeque<(f64, f64)> {
224 &self.graph_data_in
225 }
226
227 pub fn graph_data_out(&self) -> &VecDeque<(f64, f64)> {
228 &self.graph_data_out
229 }
230
231 pub fn sample_count(&self) -> usize {
232 self.history.len()
233 }
234
235 pub fn reset(&mut self) {
236 self.history.clear();
237 self.graph_data_in.clear();
238 self.graph_data_out.clear();
239 self.current_speed_in = 0;
240 self.current_speed_out = 0;
241 self.avg_speed_in = 0;
242 self.avg_speed_out = 0;
243 self.min_speed_in = 0;
244 self.min_speed_out = 0;
245 self.max_speed_in = 0;
246 self.max_speed_out = 0;
247 self.first_sample = true;
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254
255 #[test]
256 fn test_stats_calculation() {
257 let mut calc = StatsCalculator::new(Duration::from_secs(60));
258
259 let stats1 = NetworkStats {
260 timestamp: SystemTime::now(),
261 bytes_in: 1000,
262 bytes_out: 500,
263 packets_in: 10,
264 packets_out: 5,
265 errors_in: 0,
266 errors_out: 0,
267 drops_in: 0,
268 drops_out: 0,
269 };
270
271 calc.add_sample(stats1);
272
273 assert_eq!(calc.current_speed(), (0, 0));
275
276 let stats2 = NetworkStats {
278 timestamp: SystemTime::now() + Duration::from_secs(1),
279 bytes_in: 2000,
280 bytes_out: 1000,
281 packets_in: 20,
282 packets_out: 10,
283 errors_in: 0,
284 errors_out: 0,
285 drops_in: 0,
286 drops_out: 0,
287 };
288
289 calc.add_sample(stats2);
290
291 let (in_speed, out_speed) = calc.current_speed();
293 assert!(in_speed > 0);
294 assert!(out_speed > 0);
295 }
296
297 #[test]
298 fn test_counter_overflow() {
299 let calc = StatsCalculator::new(Duration::from_secs(60));
300
301 let diff = calc.calculate_diff(100, u32::MAX as u64 - 50);
303 assert_eq!(diff, 151); }
305}