mecha10-diagnostics 0.6.2

Diagnostics and metrics collection for Mecha10 robotics framework
Documentation
//! Streaming pipeline diagnostics collector
//!
//! Tracks frame pipeline metrics, encoding performance, latency, and bandwidth.

use crate::metrics::{Counter, Gauge, Histogram, RateCalculator};
use std::sync::Mutex;

/// Streaming diagnostics collector
///
/// Designed for minimal overhead on hot paths:
/// - Uses atomic counters for frame counting
/// - Histogram updates in background thread
/// - Publishes aggregated metrics every 1-5 seconds
pub struct StreamingCollector {
    // Pipeline counters (atomic, zero-cost on hot path)
    frames_received: Counter,
    frames_encoded: Counter,
    frames_sent: Counter,
    frames_dropped: Counter,

    // Queue depth gauge
    encoder_queue_depth: Gauge,

    // Bandwidth tracking
    bytes_sent: Counter,

    // Rate calculators
    fps_calculator: RateCalculator,
    bandwidth_calculator: RateCalculator,

    // Encoding performance (requires mutex for histogram)
    encoding_histogram: Mutex<Option<Histogram>>,
    slow_frames: Counter,
}

impl StreamingCollector {
    /// Create a new streaming collector
    pub fn new(_source: impl Into<String>) -> Self {
        Self {
            frames_received: Counter::new(),
            frames_encoded: Counter::new(),
            frames_sent: Counter::new(),
            frames_dropped: Counter::new(),
            encoder_queue_depth: Gauge::new(),
            bytes_sent: Counter::new(),
            fps_calculator: RateCalculator::new(),
            bandwidth_calculator: RateCalculator::new(),
            encoding_histogram: Mutex::new(Histogram::for_duration().ok()),
            slow_frames: Counter::new(),
        }
    }

    // ===== Hot Path Methods (minimal overhead) =====

    /// Record frame received from camera
    #[inline]
    pub fn record_frame_received(&self) {
        self.frames_received.inc();
    }

    /// Record frame encoded
    #[inline]
    pub fn record_frame_encoded(&self, encode_time_us: u64) {
        self.frames_encoded.inc();

        // Record encoding time in histogram (requires lock but off hot path)
        if let Ok(mut hist) = self.encoding_histogram.try_lock() {
            if let Some(h) = hist.as_mut() {
                h.record(encode_time_us);
            }
        }

        // Track slow frames (>50ms for 20fps target)
        if encode_time_us > 50_000 {
            self.slow_frames.inc();
        }
    }

    /// Record frame sent via WebRTC
    #[inline]
    pub fn record_frame_sent(&self, frame_size_bytes: u64) {
        self.frames_sent.inc();
        self.bytes_sent.add(frame_size_bytes);
    }

    /// Record frame dropped
    #[inline]
    pub fn record_frame_dropped(&self) {
        self.frames_dropped.inc();
    }

    /// Reset all metrics (useful for testing)
    pub fn reset(&self) {
        self.frames_received.reset();
        self.frames_encoded.reset();
        self.frames_sent.reset();
        self.frames_dropped.reset();
        self.bytes_sent.reset();
        self.slow_frames.reset();
        self.encoder_queue_depth.set(0);
        self.fps_calculator.reset();
        self.bandwidth_calculator.reset();

        if let Ok(mut hist) = self.encoding_histogram.lock() {
            if let Some(h) = hist.as_mut() {
                h.reset();
            }
        }
    }
}