1use crate::data::{DataPoint, Point2D};
11use crate::error::{ChartError, ChartResult, DataError};
12use heapless::Vec as HeaplessVec;
13
14#[derive(Debug, Clone, Copy)]
16pub struct RingBufferConfig {
17 pub overflow_mode: OverflowMode,
19 pub enable_events: bool,
21 pub preallocate: bool,
23 pub track_bounds: bool,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum OverflowMode {
30 Overwrite,
32 Reject,
34 Callback,
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum RingBufferEvent {
41 DataAdded,
43 DataOverwritten,
45 BufferFull,
47 BufferEmpty,
49 BoundsChanged,
51}
52
53pub struct RingBuffer<T: DataPoint + Copy, const N: usize> {
55 data: HeaplessVec<T, N>,
57 write_pos: usize,
59 config: RingBufferConfig,
61 bounds: Option<DataBounds>,
63 event_handler: Option<fn(RingBufferEvent)>,
65 stats: RingBufferStats,
67}
68
69#[derive(Debug, Clone, Copy)]
71struct DataBounds {
72 min_x: f32,
73 max_x: f32,
74 min_y: f32,
75 max_y: f32,
76}
77
78#[derive(Debug, Clone, Copy, Default)]
80pub struct RingBufferStats {
81 pub total_writes: u64,
83 pub total_reads: u64,
85 pub overflow_count: u64,
87 pub peak_usage: usize,
89}
90
91impl Default for RingBufferConfig {
92 fn default() -> Self {
93 Self {
94 overflow_mode: OverflowMode::Overwrite,
95 enable_events: false,
96 preallocate: false,
97 track_bounds: true,
98 }
99 }
100}
101
102impl<T: DataPoint + Copy, const N: usize> RingBuffer<T, N> {
103 pub fn new() -> Self {
105 Self::with_config(RingBufferConfig::default())
106 }
107
108 pub fn with_config(config: RingBufferConfig) -> Self {
110 let mut data = HeaplessVec::new();
111
112 if config.preallocate {
113 for _ in 0..N {
115 if let Some(default) = Self::default_value() {
116 let _ = data.push(default);
117 }
118 }
119 data.clear();
120 }
121
122 Self {
123 data,
124 write_pos: 0,
125 config,
126 bounds: None,
127 event_handler: None,
128 stats: RingBufferStats::default(),
129 }
130 }
131
132 fn default_value() -> Option<T> {
134 None
136 }
137
138 pub fn set_event_handler(&mut self, handler: fn(RingBufferEvent)) {
140 self.event_handler = Some(handler);
141 }
142
143 pub fn push(&mut self, value: T) -> ChartResult<()> {
145 self.stats.total_writes += 1;
146
147 if self.is_full() {
148 match self.config.overflow_mode {
149 OverflowMode::Reject => {
150 return Err(ChartError::DataError(DataError::BUFFER_FULL));
151 }
152 OverflowMode::Callback => {
153 self.trigger_event(RingBufferEvent::DataOverwritten);
154 }
155 OverflowMode::Overwrite => {
156 self.stats.overflow_count += 1;
157 }
158 }
159 }
160
161 let was_empty = self.is_empty();
163
164 if self.data.len() < N {
165 self.data
167 .push(value)
168 .map_err(|_| ChartError::DataError(DataError::BUFFER_FULL))?;
169 } else {
170 let oldest_idx = self.write_pos % self.data.len();
172 self.data[oldest_idx] = value;
173 self.write_pos = (self.write_pos + 1) % N;
174 }
175
176 if self.data.len() > self.stats.peak_usage {
178 self.stats.peak_usage = self.data.len();
179 }
180
181 if was_empty {
183 self.trigger_event(RingBufferEvent::DataAdded);
184 }
185 if self.is_full() {
186 self.trigger_event(RingBufferEvent::BufferFull);
187 }
188
189 Ok(())
190 }
191
192 pub fn extend<I>(&mut self, iter: I) -> ChartResult<usize>
194 where
195 I: IntoIterator<Item = T>,
196 {
197 let mut count = 0;
198 for value in iter {
199 match self.push(value) {
200 Ok(()) => count += 1,
201 Err(_) if self.config.overflow_mode == OverflowMode::Reject => break,
202 _ => {}
203 }
204 }
205 Ok(count)
206 }
207
208 pub fn pop(&mut self) -> Option<T> {
210 if self.is_empty() {
211 return None;
212 }
213
214 self.stats.total_reads += 1;
215
216 let value = self.data.remove(0);
218
219 if self.is_empty() {
220 self.trigger_event(RingBufferEvent::BufferEmpty);
221 self.bounds = None; }
223
224 Some(value)
225 }
226
227 pub fn peek(&self) -> Option<&T> {
229 self.data.first()
230 }
231
232 pub fn peek_newest(&self) -> Option<&T> {
234 self.data.last()
235 }
236
237 pub fn iter(&self) -> impl Iterator<Item = &T> {
239 self.data.iter()
240 }
241
242 pub fn iter_chronological(&self) -> ChronologicalIter<'_, T, N> {
245 ChronologicalIter {
246 buffer: self,
247 index: 0,
248 }
249 }
250
251 pub fn recent(&self, n: usize) -> impl Iterator<Item = &T> {
253 let n = n.min(self.data.len());
254 let start = self.data.len().saturating_sub(n);
255 self.data[start..].iter()
256 }
257
258 pub fn clear(&mut self) {
260 self.data.clear();
261 self.write_pos = 0;
262 self.bounds = None;
263
264 self.trigger_event(RingBufferEvent::BufferEmpty);
265 }
266
267 pub fn len(&self) -> usize {
269 self.data.len()
270 }
271
272 pub fn is_empty(&self) -> bool {
274 self.data.is_empty()
275 }
276
277 pub fn is_full(&self) -> bool {
279 self.data.len() >= N
280 }
281
282 pub fn capacity(&self) -> usize {
284 N
285 }
286
287 pub fn remaining_capacity(&self) -> usize {
289 N - self.data.len()
290 }
291
292 pub fn bounds(&self) -> Option<crate::data::bounds::DataBounds<f32, f32>> {
294 self.bounds.map(|b| crate::data::bounds::DataBounds {
295 min_x: b.min_x,
296 max_x: b.max_x,
297 min_y: b.min_y,
298 max_y: b.max_y,
299 })
300 }
301
302 pub fn stats(&self) -> &RingBufferStats {
304 &self.stats
305 }
306
307 pub fn reset_stats(&mut self) {
309 self.stats = RingBufferStats::default();
310 }
311
312 pub fn for_each<F>(&self, mut f: F)
314 where
315 F: FnMut(&T),
316 {
317 for item in self.data.iter() {
318 f(item);
319 }
320 }
321
322 pub fn find<F>(&self, mut predicate: F) -> Option<&T>
324 where
325 F: FnMut(&T) -> bool,
326 {
327 self.data.iter().find(|item| predicate(item))
328 }
329
330 fn trigger_event(&self, event: RingBufferEvent) {
332 if self.config.enable_events {
333 if let Some(handler) = self.event_handler {
334 handler(event);
335 }
336 }
337 }
338}
339
340impl<T: DataPoint + Copy, const N: usize> Default for RingBuffer<T, N> {
341 fn default() -> Self {
342 Self::new()
343 }
344}
345
346pub type PointRingBuffer<const N: usize> = RingBuffer<Point2D, N>;
348
349impl<const N: usize> RingBuffer<Point2D, N> {
350 fn update_bounds_for_point(&mut self, point: &Point2D) {
352 match &mut self.bounds {
353 Some(bounds) => {
354 let changed = point.x < bounds.min_x
355 || point.x > bounds.max_x
356 || point.y < bounds.min_y
357 || point.y > bounds.max_y;
358
359 bounds.min_x = bounds.min_x.min(point.x);
360 bounds.max_x = bounds.max_x.max(point.x);
361 bounds.min_y = bounds.min_y.min(point.y);
362 bounds.max_y = bounds.max_y.max(point.y);
363
364 if changed {
365 self.trigger_event(RingBufferEvent::BoundsChanged);
366 }
367 }
368 None => {
369 self.bounds = Some(DataBounds {
370 min_x: point.x,
371 max_x: point.x,
372 min_y: point.y,
373 max_y: point.y,
374 });
375 self.trigger_event(RingBufferEvent::BoundsChanged);
376 }
377 }
378 }
379
380 pub fn push_point(&mut self, point: Point2D) -> ChartResult<()> {
382 self.push(point)?;
383 if self.config.track_bounds {
384 self.update_bounds_for_point(&point);
385 }
386 Ok(())
387 }
388
389 pub fn moving_average(&self, window_size: usize) -> Option<Point2D> {
391 let window_size = window_size.min(self.len());
392 if window_size == 0 {
393 return None;
394 }
395
396 let mut sum_x = 0.0;
397 let mut sum_y = 0.0;
398 let mut count = 0;
399
400 for point in self.recent(window_size) {
401 sum_x += point.x;
402 sum_y += point.y;
403 count += 1;
404 }
405
406 if count > 0 {
407 Some(Point2D::new(sum_x / count as f32, sum_y / count as f32))
408 } else {
409 None
410 }
411 }
412
413 pub fn downsample(&self, factor: usize) -> heapless::Vec<Point2D, N> {
415 let mut result = heapless::Vec::new();
416
417 for (i, point) in self.iter().enumerate() {
418 if i % factor == 0 {
419 let _ = result.push(*point);
420 }
421 }
422
423 result
424 }
425
426 pub fn rate_of_change(&self) -> Option<f32> {
428 let oldest = self.peek()?;
429 let newest = self.peek_newest()?;
430
431 let dx = newest.x - oldest.x;
432 if dx.abs() < f32::EPSILON {
433 None
434 } else {
435 Some((newest.y - oldest.y) / dx)
436 }
437 }
438}
439
440pub struct ChronologicalIter<'a, T: DataPoint + Copy, const N: usize> {
442 buffer: &'a RingBuffer<T, N>,
443 index: usize,
444}
445
446impl<'a, T: DataPoint + Copy, const N: usize> Iterator for ChronologicalIter<'a, T, N> {
447 type Item = &'a T;
448
449 fn next(&mut self) -> Option<Self::Item> {
450 if self.index >= self.buffer.len() {
451 return None;
452 }
453
454 let item = if self.buffer.data.len() < N {
455 self.buffer.data.get(self.index)
457 } else {
458 let actual_index = (self.buffer.write_pos + self.index) % self.buffer.data.len();
460 self.buffer.data.get(actual_index)
461 };
462
463 self.index += 1;
464 item
465 }
466}
467
468#[cfg(test)]
469mod tests {
470 use super::*;
471
472 #[test]
473 fn test_ring_buffer_basic() {
474 let mut buffer: RingBuffer<Point2D, 5> = RingBuffer::new();
475
476 assert!(buffer.is_empty());
477 assert_eq!(buffer.len(), 0);
478 assert_eq!(buffer.capacity(), 5);
479
480 buffer.push(Point2D::new(1.0, 2.0)).unwrap();
482 buffer.push(Point2D::new(2.0, 3.0)).unwrap();
483
484 assert_eq!(buffer.len(), 2);
485 assert!(!buffer.is_empty());
486 assert!(!buffer.is_full());
487
488 assert_eq!(buffer.peek().unwrap(), &Point2D::new(1.0, 2.0));
490 assert_eq!(buffer.peek_newest().unwrap(), &Point2D::new(2.0, 3.0));
491 }
492
493 #[test]
494 fn test_ring_buffer_overflow() {
495 let config = RingBufferConfig {
496 overflow_mode: OverflowMode::Overwrite,
497 ..Default::default()
498 };
499 let mut buffer: RingBuffer<Point2D, 3> = RingBuffer::with_config(config);
500
501 for i in 0..5 {
503 buffer.push(Point2D::new(i as f32, i as f32)).unwrap();
504 }
505
506 assert_eq!(buffer.len(), 3);
507 assert!(buffer.is_full());
508
509 let values: heapless::Vec<Point2D, 3> = buffer.iter().copied().collect();
511 assert_eq!(values.len(), 3);
512 }
515
516 #[test]
517 fn test_ring_buffer_reject_mode() {
518 let config = RingBufferConfig {
519 overflow_mode: OverflowMode::Reject,
520 ..Default::default()
521 };
522 let mut buffer: RingBuffer<Point2D, 2> = RingBuffer::with_config(config);
523
524 buffer.push(Point2D::new(1.0, 1.0)).unwrap();
526 buffer.push(Point2D::new(2.0, 2.0)).unwrap();
527
528 let result = buffer.push(Point2D::new(3.0, 3.0));
530 assert!(result.is_err());
531 assert_eq!(buffer.len(), 2);
532 }
533
534 #[test]
535 fn test_point_ring_buffer_features() {
536 let mut buffer: PointRingBuffer<10> = PointRingBuffer::new();
537
538 for i in 0..5 {
540 buffer.push(Point2D::new(i as f32, (i * 2) as f32)).unwrap();
541 }
542
543 let avg = buffer.moving_average(3).unwrap();
545 assert_eq!(avg.x, 3.0); assert_eq!(avg.y, 6.0); let downsampled = buffer.downsample(2);
550 assert_eq!(downsampled.len(), 3); let rate = buffer.rate_of_change().unwrap();
554 assert_eq!(rate, 2.0); }
556}