mecha10-diagnostics 0.1.0

Diagnostics and metrics collection for Mecha10 robotics framework
Documentation
//! System-wide resource metrics collector

use crate::topics::*;
use crate::types::*;
use anyhow::Result;
use mecha10_core::prelude::*;
use mecha10_core::topics::Topic;
use std::sync::Mutex;
use sysinfo::{Disks, Networks, System};

/// System resource metrics collector
pub struct SystemCollector {
    source: String,
    system: Mutex<System>,
}

impl SystemCollector {
    /// Create a new system collector
    pub fn new(source: impl Into<String>) -> Self {
        Self {
            source: source.into(),
            system: Mutex::new(System::new_all()),
        }
    }

    /// Collect and publish system resource metrics
    pub async fn collect_metrics(&self, ctx: &Context) -> Result<()> {
        let metrics = {
            let mut sys = self.system.lock().unwrap();

            // Refresh all system information
            sys.refresh_all();

            // CPU metrics
            // Note: Need to call refresh_cpu_usage() for updated CPU stats
            sys.refresh_cpu_usage();

            let cpus = sys.cpus();
            let cpu_per_core: Vec<f64> = cpus.iter().map(|cpu| cpu.cpu_usage() as f64).collect();

            // Calculate average CPU usage
            let cpu_percent = if !cpu_per_core.is_empty() {
                cpu_per_core.iter().sum::<f64>() / cpu_per_core.len() as f64
            } else {
                0.0
            };

            // Memory metrics
            let memory_total_bytes = sys.total_memory();
            let memory_used_bytes = sys.used_memory();
            let memory_percent = if memory_total_bytes > 0 {
                (memory_used_bytes as f64 / memory_total_bytes as f64) * 100.0
            } else {
                0.0
            };

            // Disk metrics (using separate Disks struct in sysinfo 0.30+)
            let disks = Disks::new_with_refreshed_list();
            let (disk_total_bytes, disk_used_bytes, disk_percent) = disks
                .first()
                .map(|disk| {
                    let total = disk.total_space();
                    let available = disk.available_space();
                    let used = total.saturating_sub(available);
                    let percent = if total > 0 {
                        (used as f64 / total as f64) * 100.0
                    } else {
                        0.0
                    };
                    (total, used, percent)
                })
                .unwrap_or((0, 0, 0.0));

            // Network metrics (using separate Networks struct in sysinfo 0.30+)
            let networks = Networks::new_with_refreshed_list();
            let (network_rx_bytes, network_tx_bytes) = networks.iter().fold((0u64, 0u64), |(rx, tx), (_name, data)| {
                (rx + data.total_received(), tx + data.total_transmitted())
            });

            // Calculate network rate (bytes/sec)
            // Note: This gives cumulative values, not per-second rates
            // For accurate rates, we need to track deltas over time
            // For simplicity, using cumulative values here
            let network_rx_bytes_per_sec = network_rx_bytes;
            let network_tx_bytes_per_sec = network_tx_bytes;

            SystemResourceMetrics {
                cpu_percent,
                cpu_per_core,
                memory_total_bytes,
                memory_used_bytes,
                memory_percent,
                disk_total_bytes,
                disk_used_bytes,
                disk_percent,
                network_rx_bytes_per_sec,
                network_tx_bytes_per_sec,
            }
        };

        let msg = DiagnosticMessage::new(&self.source, metrics);
        ctx.publish_to(
            Topic::<DiagnosticMessage<SystemResourceMetrics>>::new(TOPIC_DIAGNOSTICS_SYSTEM_RESOURCES),
            &msg,
        )
        .await?;

        Ok(())
    }
}