use crate::aws::realtime::ChunkType;
use chrono::Duration;
use nexrad_decode::messages::volume_coverage_pattern::{ChannelConfiguration, WaveformType};
use std::collections::{HashMap, VecDeque};
use std::hash::{Hash, Hasher};
const MAX_TIMING_SAMPLES: usize = 10;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct ChunkCharacteristics {
pub chunk_type: ChunkType,
pub waveform_type: WaveformType,
pub channel_configuration: ChannelConfiguration,
}
impl Hash for ChunkCharacteristics {
fn hash<H: Hasher>(&self, state: &mut H) {
std::mem::discriminant(&self.chunk_type).hash(state);
std::mem::discriminant(&self.waveform_type).hash(state);
std::mem::discriminant(&self.channel_configuration).hash(state);
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct TimingStat {
duration: Duration,
attempts: usize,
}
#[derive(Debug, Clone, Default)]
pub struct ChunkTimingStats {
timings: HashMap<ChunkCharacteristics, VecDeque<TimingStat>>,
}
impl ChunkTimingStats {
pub fn new() -> Self {
Self {
timings: HashMap::new(),
}
}
pub fn add_timing(
&mut self,
characteristics: ChunkCharacteristics,
duration: Duration,
attempts: usize,
) {
let entry = self.timings.entry(characteristics).or_default();
entry.push_back(TimingStat { duration, attempts });
if entry.len() > MAX_TIMING_SAMPLES {
entry.pop_front();
}
}
pub(crate) fn get_average_timing(
&self,
characteristics: &ChunkCharacteristics,
) -> Option<Duration> {
self.timings.get(characteristics).and_then(|timings| {
if timings.is_empty() {
return None;
}
let total_millis: i64 = timings
.iter()
.map(|timing| timing.duration.num_milliseconds())
.sum();
let avg_millis = total_millis / timings.len() as i64;
Some(Duration::milliseconds(avg_millis))
})
}
pub(crate) fn get_average_attempts(
&self,
characteristics: &ChunkCharacteristics,
) -> Option<f64> {
self.timings.get(characteristics).and_then(|timings| {
if timings.is_empty() {
return None;
}
let total_attempts: usize = timings.iter().map(|timing| timing.attempts).sum();
Some(total_attempts as f64 / timings.len() as f64)
})
}
pub fn get_statistics(&self) -> Vec<(ChunkCharacteristics, Option<Duration>, Option<f64>)> {
self.timings
.keys()
.map(|characteristics| {
(
*characteristics,
self.get_average_timing(characteristics),
self.get_average_attempts(characteristics),
)
})
.collect()
}
}