use std::collections::HashMap;
use chrono::{DateTime, Duration, Utc};
use ratatui::style::Color;
use crate::custom::app::debug_log;
pub fn get_duration_text(duration: Duration) -> String {
return if duration.num_weeks() > 104 { format!("{} years", duration.num_days()/365) } else if
duration.num_weeks() > 4 { format!("{} weeks", duration.num_weeks()) } else if
duration.num_hours() > 48 { format!("{} days", duration.num_days()) } else if
duration.num_hours() > 2 { format!("{} hours", duration.num_hours()) } else if
duration.num_minutes() > 5 { format!("{} minutes", duration.num_minutes()) } else if
duration.num_seconds() > 0 { format!("{} seconds", duration.num_seconds()) } else
{ String::from("this(zero duration)") };
}
pub fn get_max_buckets_value(buckets: &Vec<u64>) -> u64 {
let mut max: u64 = 0;
for i in 0 .. buckets.len() - 1 {
if buckets[i] > max { max = buckets[i]; }
}
return max;
}
pub fn get_min_buckets_value(buckets: &Vec<u64>) -> u64 {
let mut min: u64 = u64::MAX;
for i in 0 .. buckets.len() - 1 {
if buckets[i] > 0 && buckets[i] < min { min = buckets[i]; }
}
return min;
}
#[derive(Default)]
pub enum MinMeanMax {
#[default]
Min = 1,
Mean = 2,
Max = 3,
}
pub struct Timeline {
pub name: String,
pub units_text: String,
pub is_mmm: bool,
pub is_cumulative: bool,
pub colour: Color,
pub last_non_zero_value: u64,
buckets: HashMap<&'static str, Buckets>,
}
impl Timeline {
pub fn new(name: String, units_text: String, is_mmm: bool, is_cumulative: bool, colour: Color) -> Timeline {
Timeline {
name,
units_text,
is_mmm,
is_cumulative,
buckets: HashMap::<&'static str, Buckets>::new(),
last_non_zero_value: 0,
colour,
}
}
pub fn get_name(&self) -> &String {
&self.name
}
pub fn add_bucket_set(&mut self, name: &'static str, duration: Duration, num_buckets: usize) {
self.buckets
.insert(name, Buckets::new(duration, num_buckets, self.is_mmm));
}
pub fn get_bucket_set(&self, timescale_name: &str) -> Option<&Buckets> {
return self.buckets.get(timescale_name);
}
pub fn get_bucket_set_mut(&mut self, timescale_name: &str) -> Option<&mut Buckets> {
return self.buckets.get_mut(timescale_name);
}
pub fn get_buckets_mut(&mut self, timescale_name: &str, mmm_ui_mode: Option<&MinMeanMax>) -> Option<&Vec<u64>> {
if let Some(bucket_set) = self.buckets.get(timescale_name) {
return Some(bucket_set.buckets(mmm_ui_mode));
} else {
return None;
}
}
pub fn get_buckets(&self, timescale_name: &str, mmm_ui_mode: Option<&MinMeanMax>) -> Option<&Vec<u64>> {
if let Some(bucket_set) = self.buckets.get(timescale_name) {
return Some(bucket_set.buckets(mmm_ui_mode));
} else {
return None;
}
}
pub fn update_current_time(&mut self, new_time: &DateTime<Utc>) {
for (_name, bs) in self.buckets.iter_mut() {
bs.update_current_time(new_time, self.is_cumulative);
}
}
pub fn update_value(&mut self, time: &DateTime<Utc>, value: u64) {
if value > 0 {self.last_non_zero_value = value;}
for (_name, bs) in self.buckets.iter_mut() {
let mut index = Some(bs.num_buckets() - 1);
if let Some(bucket_time) = bs.bucket_time {
if time.lt(&bucket_time) {
let time_difference = (bucket_time - *time).num_nanoseconds();
let bucket_duration = bs.bucket_duration.num_nanoseconds();
if time_difference.and(bucket_duration).is_some() {
let buckets_behind = time_difference.unwrap() / bucket_duration.unwrap();
if buckets_behind as usize >= bs.num_buckets() {
debug_log!(format!("increment DISCARDED buckets_behind: {}", buckets_behind).as_str());
index = None;
} else {
if bs.num_buckets() > 1 {
index = Some(bs.num_buckets() - 1 - buckets_behind as usize);
}
}
}
}
}
if let Some(index) = index {
bs.bucket_update_value(index, value, self.is_cumulative);
}
}
}
}
pub struct Buckets {
pub bucket_time: Option<DateTime<Utc>>, pub earliest_time: Option<DateTime<Utc>>, pub latest_time: Option<DateTime<Utc>>, pub total_duration: Duration,
pub bucket_duration: Duration,
pub num_buckets: usize,
pub values_total: u64,
pub values_min: u64,
pub values_max: u64,
pub is_mmm: bool,
pub buckets: Vec<u64>,
pub buckets_count: Vec<u64>, pub buckets_total: Vec<u64>, pub buckets_min: Vec<u64>, pub buckets_mean: Vec<u64>, pub buckets_max: Vec<u64>,
pub buckets_need_init: Vec<u64>, }
impl Buckets {
pub fn new(bucket_duration: Duration, num_buckets: usize, is_mmm: bool) -> Buckets {
let value_buckets_size = if is_mmm { 1 } else { num_buckets };
let mmm_buckets_size = if is_mmm { num_buckets } else { 1 };
return Buckets {
bucket_time: None,
earliest_time: None,
latest_time: None,
bucket_duration,
num_buckets,
values_total: 0,
values_min: u64::MAX,
values_max: 0,
total_duration: bucket_duration * num_buckets as i32,
is_mmm: is_mmm,
buckets: vec![0; value_buckets_size],
buckets_count: vec![0; mmm_buckets_size],
buckets_total: vec![0; mmm_buckets_size],
buckets_min: vec![0; mmm_buckets_size],
buckets_mean: vec![0; mmm_buckets_size],
buckets_max: vec![0; mmm_buckets_size],
buckets_need_init: vec![1; mmm_buckets_size],
}
}
pub fn update_current_time(&mut self, new_time: &DateTime<Utc>, is_cumulative: bool) {
if let Some(mut bucket_time) = self.bucket_time {
let mut end_time = bucket_time + self.bucket_duration;
while end_time.lt(&new_time) {
self.bucket_time = Some(end_time);
bucket_time = end_time;
end_time = bucket_time + self.bucket_duration;
if self.is_mmm {
for buckets in
&mut vec![
&mut self.buckets_count,
&mut self.buckets_total,
&mut self.buckets_min,
&mut self.buckets_mean,
&mut self.buckets_max].iter_mut() {
buckets.push(0);
if buckets.len() > self.num_buckets {
buckets.remove(0);
}
}
self.buckets_need_init.push(1);
if self.buckets_need_init.len() > self.num_buckets {
self.buckets_need_init.remove(0);
}
} else {
self.buckets.push(0);
if self.buckets.len() > self.num_buckets {
if is_cumulative {
self.values_total -= self.buckets[0];
}
self.buckets.remove(0);
}
}
}
} else {
self.bucket_time = Some(*new_time);
}
if let Some(earliest_time) = self.earliest_time {
if new_time.lt(&earliest_time) { self.earliest_time = Some(*new_time); }
} else {
self.earliest_time = Some(*new_time);
};
if let Some(latest_time) = self.latest_time {
if new_time.gt(&latest_time) { self.latest_time = Some(*new_time); }
} else {
self.latest_time = Some(*new_time);
};
}
pub fn bucket_update_value(&mut self, index: usize, value: u64, is_cumulative: bool) {
if self.is_mmm {
debug_log!(format!("is_mmm: bucket_update_value(index:{}, value:{}, is_cum:{})", index, value, is_cumulative).as_str());
if self.buckets_need_init[index] == 1 {
self.buckets_need_init[index] = 0;
self.buckets_count[index] = 0;
self.buckets_total[index] = 0;
self.buckets_min[index] = u64::MAX;
self.buckets_mean[index] = 0;
self.buckets_max[index] = 0;
}
self.buckets_count[index] += 1;
self.buckets_total[index] += value;
self.buckets_mean[index] = self.buckets_total[index] / self.buckets_count[index];
if value < self.buckets_min[index] { self.buckets_min[index] = value }
if value > self.buckets_max[index] { self.buckets_max[index] = value }
if value < self.values_min { self.values_min = value }
if value > self.values_max { self.values_max = value }
} else {
if is_cumulative {
self.buckets[index] += value;
if self.buckets[index] < self.values_min { self.values_min = self.buckets[index] }
if self.buckets[index] > self.values_max { self.values_max = self.buckets[index] }
self.values_total += value;
} else {
self.buckets[index] = value;
if value < self.values_min { self.values_min = value }
if value > self.values_max { self.values_max = value }
}
}
}
pub fn get_duration_text(&self) -> String {
let mut duration = self.total_duration;
if let Some(earliest_time) = self.earliest_time {
if let Some(latest_time) = self.latest_time {
if (latest_time - earliest_time).lt(&duration) &&
(latest_time - earliest_time).num_seconds() > 0 {
duration = latest_time - earliest_time;
} else if latest_time.eq(&earliest_time) {
duration = self.bucket_duration;
}
};
return get_duration_text(duration);
};
return String::from("(zero duration)");
}
pub fn num_buckets(&self) -> usize { return self.num_buckets; }
pub fn buckets(&self, mmm_ui_mode: Option<&MinMeanMax>) -> &Vec<u64> {
if self.is_mmm {
return match mmm_ui_mode {
None => &self.buckets,
Some(MinMeanMax::Min) => &self.buckets_min,
Some(MinMeanMax::Mean) => &self.buckets_mean,
Some(MinMeanMax::Max) => &self.buckets_max,
}
} else {
return &self.buckets;
}
}
}