1use monotonic_time_rs::Millis;
2use monotonic_time_rs::MillisDuration;
3use num_traits::Bounded;
8use num_traits::ToPrimitive;
9use std::cmp::PartialOrd;
10use std::fmt::Debug;
11use std::fmt::Display;
12use std::ops::{Add, Div};
13
14#[derive(Debug, PartialEq)]
15pub struct MinMaxAvg<T: Display> {
16 pub min: T,
17 pub avg: f32,
18 pub max: T,
19 pub unit: &'static str,
20}
21
22impl<T: Display> MinMaxAvg<T> {
23 pub const fn new(min: T, avg: f32, max: T) -> Self {
24 Self {
25 min,
26 avg,
27 max,
28 unit: "",
29 }
30 }
31
32 pub const fn with_unit(mut self, unit: &'static str) -> Self {
33 self.unit = unit;
34 self
35 }
36}
37
38impl<T: Display> Display for MinMaxAvg<T> {
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 write!(
41 f,
42 "min:{}{}, avg:{}{}, max:{}{}",
43 self.min, self.unit, self.avg, self.unit, self.max, self.unit,
44 )
45 }
46}
47
48#[derive(Debug)]
50pub struct RateMetric {
51 count: u32,
52 last_calculated_at: Millis,
53 average: f32,
54 measurement_interval: MillisDuration,
55}
56
57impl RateMetric {
58 #[must_use] pub const fn new(time: Millis) -> Self {
68 Self {
69 count: 0,
70 last_calculated_at: time,
71 measurement_interval: MillisDuration::from_millis(500),
72 average: 0.0,
73 }
74 }
75
76 #[must_use] pub fn with_interval(time: Millis, measurement_interval: f32) -> Self {
77 Self {
78 count: 0,
79 last_calculated_at: time,
80 measurement_interval: MillisDuration::from_secs(measurement_interval)
81 .expect("measurement interval should be positive"),
82 average: 0.0,
83 }
84 }
85
86 pub const fn increment(&mut self) {
90 self.count += 1;
91 }
92
93 pub const fn add(&mut self, count: u32) {
99 self.count += count;
100 }
101
102 pub fn update(&mut self, time: Millis) {
111 let elapsed_time = time - self.last_calculated_at;
112 if elapsed_time < self.measurement_interval {
113 return;
114 }
115
116 let rate = self.count as f32 / elapsed_time.as_secs();
117
118 self.count = 0;
120 self.last_calculated_at = time;
121 self.average = rate;
122 }
123
124 #[must_use] pub const fn rate(&self) -> f32 {
125 self.average
126 }
127}
128
129#[derive(Debug)]
131pub struct AggregateMetric<T> {
132 sum: T,
133 count: u8,
134 max: T,
135 min: T,
136 threshold: u8,
137 max_ack: T,
138 min_ack: T,
139 avg: f32,
140 avg_is_set: bool,
141 unit: &'static str,
142}
143
144impl<T> AggregateMetric<T>
145where
146 T: Add<Output = T>
147 + Div<Output = T>
148 + Copy
149 + PartialOrd
150 + Default
151 + Debug
152 + Display
153 + Bounded
154 + ToPrimitive,
155{
156 pub fn new(threshold: u8) -> Result<Self, String> {
158 if threshold == 0 {
159 Err("threshold can not be zero".to_string())
160 } else {
161 Ok(Self {
162 sum: T::default(),
163 count: 0,
164 max: T::default(),
165 min: T::default(),
166 threshold,
167 max_ack: T::min_value(),
168 min_ack: T::max_value(),
169 avg: 0.0,
170 avg_is_set: false,
171 unit: "",
172 })
173 }
174 }
175
176 pub const fn with_unit(mut self, unit: &'static str) -> Self {
177 self.unit = unit;
178 self
179 }
180
181 pub const fn average(&self) -> Option<f32> {
183 if self.avg_is_set {
184 Some(self.avg)
185 } else {
186 None
187 }
188 }
189
190 pub fn add(&mut self, value: T) {
192 self.sum = self.sum + value;
193 self.count += 1;
194
195 if value > self.max_ack {
197 self.max_ack = value;
198 }
199 if value < self.min_ack {
200 self.min_ack = value;
201 }
202
203 if self.count >= self.threshold {
205 let sum_f32 = self.sum.to_f32().unwrap_or(0.0);
206 let avg_f32 = sum_f32 / f32::from(self.count);
207
208 self.avg = avg_f32;
209
210 self.min = self.min_ack;
211 self.max = self.max_ack;
212 self.max_ack = T::min_value();
213 self.min_ack = T::max_value();
214 self.count = 0;
215 self.avg_is_set = true;
216 self.sum = T::default();
217 }
218 }
219
220 pub const fn values(&self) -> Option<MinMaxAvg<T>> {
222 if self.avg_is_set {
223 Some(MinMaxAvg::new(self.min, self.avg, self.max).with_unit(self.unit))
224 } else {
225 None
226 }
227 }
228}