using System; using System.IO; using System.Threading; using NLog; namespace VRCX { public class SystemMonitorElectron { public static readonly SystemMonitorElectron Instance; public float CpuUsage; public double UpTime; private bool _enabled; private long _lastTotalTime; private long _lastIdleTime; private bool _firstReading = true; // Add this flag private Thread _thread; private static readonly Logger logger = LogManager.GetCurrentClassLogger(); static SystemMonitorElectron() { Instance = new SystemMonitorElectron(); } public void Init() { } public void Start(bool enabled) { if (enabled == _enabled) return; _enabled = enabled; if (enabled) StartThread(); else Exit(); } internal void Exit() { CpuUsage = 0; UpTime = 0; _firstReading = true; // Reset flag try { if (_thread != null) { _thread.Interrupt(); _thread.Join(); _thread = null; } } catch (ThreadInterruptedException) { } } private void StartThread() { Exit(); try { ReadCpuInfo(); } catch (Exception ex) { logger.Error($"Failed to initialize CPU monitoring: {ex}"); return; } _thread = new Thread(ThreadProc) { IsBackground = true }; _thread.Start(); } private void ThreadProc() { try { while (_enabled) { UpdateMetrics(); Thread.Sleep(1000); } } catch (Exception ex) { logger.Warn($"SystemMonitor thread exception: {ex}"); } Exit(); } private void UpdateMetrics() { try { CpuUsage = GetCpuUsage(); UpTime = GetUptime(); } catch (Exception ex) { logger.Warn($"Error updating metrics: {ex}"); } } private float GetCpuUsage() { try { var cpuInfo = ReadCpuInfo(); if (cpuInfo == null) return 0; var totalTime = cpuInfo.Value.TotalTime; var idleTime = cpuInfo.Value.IdleTime; // Skip the first reading to establish baseline if (_firstReading) { _lastTotalTime = totalTime; _lastIdleTime = idleTime; _firstReading = false; return 0; // Return 0 for first reading } var totalTimeDiff = totalTime - _lastTotalTime; var idleTimeDiff = idleTime - _lastIdleTime; if (totalTimeDiff > 0) { var cpuUsage = 100.0f * (1.0f - (float)idleTimeDiff / totalTimeDiff); _lastTotalTime = totalTime; _lastIdleTime = idleTime; // Clamp to reasonable range return Math.Max(0, Math.Min(100, cpuUsage)); } return 0; } catch (Exception ex) { logger.Warn($"Error reading CPU usage: {ex}"); return 0; } } private double GetUptime() { try { var uptimeContent = File.ReadAllText("/proc/uptime"); var parts = uptimeContent.Split(' '); if (parts.Length > 0 && double.TryParse(parts[0], out var uptimeSeconds)) { return TimeSpan.FromSeconds(uptimeSeconds).TotalMilliseconds; } } catch (Exception ex) { logger.Warn($"Error reading uptime: {ex}"); } return 0; } private (long TotalTime, long IdleTime)? ReadCpuInfo() { try { var statContent = File.ReadAllText("/proc/stat"); var lines = statContent.Split('\n'); foreach (var line in lines) { if (line.StartsWith("cpu ")) { var parts = line.Split(' ', StringSplitOptions.RemoveEmptyEntries); if (parts.Length >= 5) { // CPU time values: user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice var user = long.Parse(parts[1]); var nice = long.Parse(parts[2]); var system = long.Parse(parts[3]); var idle = long.Parse(parts[4]); var iowait = parts.Length > 5 ? long.Parse(parts[5]) : 0; var irq = parts.Length > 6 ? long.Parse(parts[6]) : 0; var softirq = parts.Length > 7 ? long.Parse(parts[7]) : 0; var steal = parts.Length > 8 ? long.Parse(parts[8]) : 0; var totalTime = user + nice + system + idle + iowait + irq + softirq + steal; return (totalTime, idle); } } } } catch (Exception ex) { logger.Warn($"Error reading /proc/stat: {ex}"); } return null; } } }