Skip to main content

rill_telemetry/
collector.rs

1use std::sync::Arc;
2
3use rill_core::math::Transcendental;
4use rill_core::queues::spsc::SpscQueue;
5use rill_core::queues::TelemetryBlock;
6
7/// Non-real-time consumer of telemetry frames from a shared `SpscQueue`.
8///
9/// Runs on the control thread (or any non-RT context). Drains available
10/// frames from the ring buffer and delivers them to a user-supplied callback.
11///
12/// # Example
13/// ```ignore
14/// use std::sync::Arc;
15/// use rill_core::queues::spsc::SpscQueue;
16/// use rill_core::queues::TelemetryBlock;
17/// use rill_telemetry::collector::TelemetryCollector;
18///
19/// let queue = Arc::new(SpscQueue::<TelemetryBlock<f32, 64>, 8>::new());
20/// let mut collector = TelemetryCollector::new(queue, |frame| {
21///     println!("peak={} rms={} dc={}", frame.peak, frame.rms, frame.dc_offset);
22/// });
23/// collector.poll();  // drain available frames
24/// ```
25pub struct TelemetryCollector<T: Transcendental, const BUF_SIZE: usize, const QUEUE_CAP: usize> {
26    queue: Arc<SpscQueue<TelemetryBlock<T, BUF_SIZE>, QUEUE_CAP>>,
27    callback: Box<dyn FnMut(TelemetryBlock<T, BUF_SIZE>)>,
28}
29
30impl<T: Transcendental, const BUF_SIZE: usize, const QUEUE_CAP: usize>
31    TelemetryCollector<T, BUF_SIZE, QUEUE_CAP>
32{
33    /// Create a new collector backed by the shared ring buffer.
34    ///
35    /// The `callback` is invoked for every frame drained from the queue.
36    pub fn new(
37        queue: Arc<SpscQueue<TelemetryBlock<T, BUF_SIZE>, QUEUE_CAP>>,
38        callback: impl FnMut(TelemetryBlock<T, BUF_SIZE>) + 'static,
39    ) -> Self {
40        Self {
41            queue,
42            callback: Box::new(callback),
43        }
44    }
45
46    /// Drain all available telemetry frames from the ring buffer.
47    ///
48    /// Returns the number of frames processed.
49    pub fn poll(&mut self) -> usize {
50        let mut count = 0;
51        while let Some(frame) = self.queue.pop() {
52            (self.callback)(frame);
53            count += 1;
54        }
55        count
56    }
57
58    /// Drain at most `max` frames from the ring buffer.
59    ///
60    /// Useful when you want to bound processing time per tick.
61    pub fn poll_n(&mut self, max: usize) -> usize {
62        let mut count = 0;
63        while count < max {
64            match self.queue.pop() {
65                Some(frame) => {
66                    (self.callback)(frame);
67                    count += 1;
68                }
69                None => break,
70            }
71        }
72        count
73    }
74
75    /// Number of frames currently available in the ring buffer (approximate).
76    pub fn available(&self) -> usize {
77        self.queue.len()
78    }
79
80    /// Consume the collector and return the underlying queue Arc.
81    pub fn into_queue(self) -> Arc<SpscQueue<TelemetryBlock<T, BUF_SIZE>, QUEUE_CAP>> {
82        self.queue
83    }
84}