1use std::collections::VecDeque;
10use std::sync::Arc;
11use std::time::{Duration, Instant};
12use tokio::sync::RwLock;
13use tracing::{debug, info};
14
15#[derive(Debug, Clone)]
17pub struct SpeedSample {
18 pub timestamp: Instant,
20 pub speed: f64,
22 pub bytes: u64,
24 pub duration: Duration,
26 pub concurrent_connections: u32,
28 pub chunk_size: u64,
30 pub server_url: String,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum NetworkCondition {
37 Excellent,
39 Good,
41 Fair,
43 Poor,
45 VeryPoor,
47}
48
49impl NetworkCondition {
50 pub fn from_speed(speed_mbps: f64) -> Self {
52 match speed_mbps {
53 s if s >= 50.0 => NetworkCondition::Excellent,
54 s if s >= 10.0 => NetworkCondition::Good,
55 s if s >= 1.0 => NetworkCondition::Fair,
56 s if s >= 0.1 => NetworkCondition::Poor,
57 _ => NetworkCondition::VeryPoor,
58 }
59 }
60
61 pub fn recommended_concurrency(&self) -> u32 {
63 match self {
64 NetworkCondition::Excellent => 64,
65 NetworkCondition::Good => 32,
66 NetworkCondition::Fair => 16,
67 NetworkCondition::Poor => 8,
68 NetworkCondition::VeryPoor => 4,
69 }
70 }
71
72 pub fn recommended_chunk_size(&self) -> u64 {
74 match self {
75 NetworkCondition::Excellent => 8 * 1024 * 1024, NetworkCondition::Good => 4 * 1024 * 1024, NetworkCondition::Fair => 2 * 1024 * 1024, NetworkCondition::Poor => 1024 * 1024, NetworkCondition::VeryPoor => 512 * 1024, }
81 }
82}
83
84#[derive(Debug, Clone)]
86pub struct AdaptiveParams {
87 pub concurrent_connections: u32,
89 pub chunk_size: u64,
91 pub timeout: Duration,
93 pub retry_attempts: u32,
95 pub network_condition: NetworkCondition,
97 pub confidence: f64,
99 pub last_adjusted: Instant,
101}
102
103impl Default for AdaptiveParams {
104 fn default() -> Self {
105 Self {
106 concurrent_connections: 16,
107 chunk_size: 2 * 1024 * 1024, timeout: Duration::from_secs(30),
109 retry_attempts: 3,
110 network_condition: NetworkCondition::Fair,
111 confidence: 0.5,
112 last_adjusted: Instant::now(),
113 }
114 }
115}
116
117pub struct AdaptiveSpeedController {
119 samples: Arc<RwLock<VecDeque<SpeedSample>>>,
121 params: Arc<RwLock<AdaptiveParams>>,
123 max_samples: usize,
125 min_samples_for_adaptation: usize,
127 adaptation_interval: Duration,
129 trend_analyzer: SpeedTrendAnalyzer,
131}
132
133impl AdaptiveSpeedController {
134 pub fn new() -> Self {
136 Self {
137 samples: Arc::new(RwLock::new(VecDeque::new())),
138 params: Arc::new(RwLock::new(AdaptiveParams::default())),
139 max_samples: 100,
140 min_samples_for_adaptation: 5,
141 adaptation_interval: Duration::from_secs(10),
142 trend_analyzer: SpeedTrendAnalyzer::new(),
143 }
144 }
145
146 pub async fn record_sample(&self, sample: SpeedSample) {
148 let mut samples = self.samples.write().await;
149
150 samples.push_back(sample.clone());
152
153 while samples.len() > self.max_samples {
155 samples.pop_front();
156 }
157
158 debug!(
159 "Recorded speed sample: {:.2} MB/s ({} bytes in {:?})",
160 sample.speed / 1024.0 / 1024.0,
161 sample.bytes,
162 sample.duration
163 );
164
165 let params = self.params.read().await;
167 if samples.len() >= self.min_samples_for_adaptation
168 && params.last_adjusted.elapsed() >= self.adaptation_interval
169 {
170 drop(params);
171 drop(samples);
172 self.adapt_parameters().await;
173 }
174 }
175
176 async fn adapt_parameters(&self) {
178 let samples = self.samples.read().await;
179 if samples.len() < self.min_samples_for_adaptation {
180 return;
181 }
182
183 let recent_samples: Vec<_> = samples.iter().rev().take(10).cloned().collect();
185 let avg_speed =
186 recent_samples.iter().map(|s| s.speed).sum::<f64>() / recent_samples.len() as f64;
187 let speed_mbps = avg_speed / 1024.0 / 1024.0;
188
189 let network_condition = NetworkCondition::from_speed(speed_mbps);
191
192 let trend = self.trend_analyzer.analyze_trend(&recent_samples);
194
195 let mut params = self.params.write().await;
196 let old_params = params.clone();
197
198 params.network_condition = network_condition;
200
201 let new_concurrency = self
203 .calculate_optimal_concurrency(&recent_samples, &trend)
204 .await;
205 params.concurrent_connections = new_concurrency;
206
207 let new_chunk_size = self
209 .calculate_optimal_chunk_size(&recent_samples, &trend)
210 .await;
211 params.chunk_size = new_chunk_size;
212
213 let avg_response_time = recent_samples
215 .iter()
216 .map(|s| s.duration.as_millis() as f64)
217 .sum::<f64>()
218 / recent_samples.len() as f64;
219 params.timeout =
220 Duration::from_millis((avg_response_time * 3.0) as u64).max(Duration::from_secs(10));
221
222 params.confidence = self.calculate_confidence(&recent_samples);
224 params.last_adjusted = Instant::now();
225
226 info!("Adapted parameters: concurrency {} -> {}, chunk_size {}KB -> {}KB, condition {:?}, confidence {:.2}",
227 old_params.concurrent_connections, params.concurrent_connections,
228 old_params.chunk_size / 1024, params.chunk_size / 1024,
229 params.network_condition, params.confidence);
230 }
231
232 async fn calculate_optimal_concurrency(
234 &self,
235 samples: &[SpeedSample],
236 trend: &SpeedTrend,
237 ) -> u32 {
238 if samples.is_empty() {
239 return 16; }
241
242 let mut concurrency_performance: std::collections::HashMap<u32, Vec<f64>> =
244 std::collections::HashMap::new();
245 for sample in samples {
246 concurrency_performance
247 .entry(sample.concurrent_connections)
248 .or_default()
249 .push(sample.speed);
250 }
251
252 let mut best_concurrency = 16;
254 let mut best_speed = 0.0;
255
256 for (concurrency, speeds) in concurrency_performance {
257 let avg_speed = speeds.iter().sum::<f64>() / speeds.len() as f64;
258 if avg_speed > best_speed {
259 best_speed = avg_speed;
260 best_concurrency = concurrency;
261 }
262 }
263
264 match trend {
266 SpeedTrend::Improving => (best_concurrency as f64 * 1.2) as u32,
267 SpeedTrend::Declining => (best_concurrency as f64 * 0.8) as u32,
268 SpeedTrend::Stable => best_concurrency,
269 }
270 .clamp(4, 128) }
272
273 async fn calculate_optimal_chunk_size(
275 &self,
276 samples: &[SpeedSample],
277 _trend: &SpeedTrend,
278 ) -> u64 {
279 if samples.is_empty() {
280 return 2 * 1024 * 1024; }
282
283 let mut chunk_performance: std::collections::HashMap<u64, Vec<f64>> =
285 std::collections::HashMap::new();
286 for sample in samples {
287 chunk_performance
288 .entry(sample.chunk_size)
289 .or_default()
290 .push(sample.speed);
291 }
292
293 let mut best_chunk_size = 2 * 1024 * 1024;
295 let mut best_speed = 0.0;
296
297 for (chunk_size, speeds) in chunk_performance {
298 let avg_speed = speeds.iter().sum::<f64>() / speeds.len() as f64;
299 if avg_speed > best_speed {
300 best_speed = avg_speed;
301 best_chunk_size = chunk_size;
302 }
303 }
304
305 let params = self.params.read().await;
307 let recommended = params.network_condition.recommended_chunk_size();
308
309 let blended = (best_chunk_size + recommended) / 2;
311 blended.clamp(128 * 1024, 16 * 1024 * 1024) }
313
314 fn calculate_confidence(&self, samples: &[SpeedSample]) -> f64 {
316 if samples.len() < 3 {
317 return 0.3; }
319
320 let speeds: Vec<f64> = samples.iter().map(|s| s.speed).collect();
322 let mean = speeds.iter().sum::<f64>() / speeds.len() as f64;
323 let variance = speeds.iter().map(|s| (s - mean).powi(2)).sum::<f64>() / speeds.len() as f64;
324 let std_dev = variance.sqrt();
325 let cv = std_dev / mean;
326
327 (1.0 - cv.min(1.0)).max(0.1)
329 }
330
331 pub async fn get_params(&self) -> AdaptiveParams {
333 self.params.read().await.clone()
334 }
335
336 pub async fn get_current_speed(&self) -> Option<f64> {
338 let samples = self.samples.read().await;
339 if samples.is_empty() {
340 return None;
341 }
342
343 let recent_samples: Vec<_> = samples.iter().rev().take(5).collect();
344 let avg_speed =
345 recent_samples.iter().map(|s| s.speed).sum::<f64>() / recent_samples.len() as f64;
346 Some(avg_speed)
347 }
348
349 pub async fn get_speed_stats(&self) -> SpeedStats {
351 let samples = self.samples.read().await;
352
353 if samples.is_empty() {
354 return SpeedStats::default();
355 }
356
357 let speeds: Vec<f64> = samples.iter().map(|s| s.speed).collect();
358 let min_speed = speeds.iter().cloned().fold(f64::INFINITY, f64::min);
359 let max_speed = speeds.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
360 let avg_speed = speeds.iter().sum::<f64>() / speeds.len() as f64;
361
362 SpeedStats {
363 min_speed,
364 max_speed,
365 avg_speed,
366 sample_count: samples.len(),
367 latest_speed: samples.back().map(|s| s.speed),
368 }
369 }
370
371 pub async fn reset(&self) {
373 let mut samples = self.samples.write().await;
374 samples.clear();
375
376 let mut params = self.params.write().await;
377 *params = AdaptiveParams::default();
378
379 info!("Adaptive speed controller reset");
380 }
381}
382
383#[derive(Debug, Clone, Copy, PartialEq, Eq)]
385pub enum SpeedTrend {
386 Improving,
387 Stable,
388 Declining,
389}
390
391struct SpeedTrendAnalyzer;
393
394impl SpeedTrendAnalyzer {
395 fn new() -> Self {
396 Self
397 }
398
399 fn analyze_trend(&self, samples: &[SpeedSample]) -> SpeedTrend {
400 if samples.len() < 3 {
401 return SpeedTrend::Stable;
402 }
403
404 let speeds: Vec<f64> = samples.iter().map(|s| s.speed).collect();
405 let first_half_avg =
406 speeds[..speeds.len() / 2].iter().sum::<f64>() / (speeds.len() / 2) as f64;
407 let second_half_avg = speeds[speeds.len() / 2..].iter().sum::<f64>()
408 / (speeds.len() - speeds.len() / 2) as f64;
409
410 let change_ratio = (second_half_avg - first_half_avg) / first_half_avg;
411
412 if change_ratio > 0.1 {
413 SpeedTrend::Improving
414 } else if change_ratio < -0.1 {
415 SpeedTrend::Declining
416 } else {
417 SpeedTrend::Stable
418 }
419 }
420}
421
422#[derive(Debug, Clone)]
424pub struct SpeedStats {
425 pub min_speed: f64,
426 pub max_speed: f64,
427 pub avg_speed: f64,
428 pub sample_count: usize,
429 pub latest_speed: Option<f64>,
430}
431
432impl Default for SpeedStats {
433 fn default() -> Self {
434 Self {
435 min_speed: 0.0,
436 max_speed: 0.0,
437 avg_speed: 0.0,
438 sample_count: 0,
439 latest_speed: None,
440 }
441 }
442}
443
444impl Default for AdaptiveSpeedController {
445 fn default() -> Self {
446 Self::new()
447 }
448}
449
450#[cfg(test)]
451mod tests {
452 use super::*;
453
454 #[tokio::test]
455 async fn test_speed_recording() {
456 let controller = AdaptiveSpeedController::new();
457
458 let sample = SpeedSample {
459 timestamp: Instant::now(),
460 speed: 10.0 * 1024.0 * 1024.0, bytes: 1024 * 1024,
462 duration: Duration::from_millis(100),
463 concurrent_connections: 16,
464 chunk_size: 2 * 1024 * 1024,
465 server_url: "https://example.com".to_string(),
466 };
467
468 controller.record_sample(sample).await;
469
470 let stats = controller.get_speed_stats().await;
471 assert_eq!(stats.sample_count, 1);
472 assert!(stats.avg_speed > 0.0);
473 }
474
475 #[test]
476 fn test_network_condition_classification() {
477 assert_eq!(
478 NetworkCondition::from_speed(100.0),
479 NetworkCondition::Excellent
480 );
481 assert_eq!(NetworkCondition::from_speed(20.0), NetworkCondition::Good);
482 assert_eq!(NetworkCondition::from_speed(5.0), NetworkCondition::Fair);
483 assert_eq!(NetworkCondition::from_speed(0.5), NetworkCondition::Poor);
484 assert_eq!(
485 NetworkCondition::from_speed(0.05),
486 NetworkCondition::VeryPoor
487 );
488 }
489}