Atoll/DynamicIsland/managers/StatsManager.swift
Hariharan Mudaliar 3362796d10 Fixed Alignment
2025-07-15 10:39:46 +05:30

204 lines
6.8 KiB
Swift

//
// StatsManager.swift
// DynamicIsland
//
// System performance monitoring for the Dynamic Island Stats feature
//
import Foundation
import Combine
import SwiftUI
import IOKit
import IOKit.ps
class StatsManager: ObservableObject {
// MARK: - Properties
static let shared = StatsManager()
@Published var isMonitoring: Bool = false
@Published var cpuUsage: Double = 0.0
@Published var memoryUsage: Double = 0.0
@Published var gpuUsage: Double = 0.0
@Published var lastUpdated: Date = .distantPast
// Historical data for graphs (last 30 data points)
@Published var cpuHistory: [Double] = []
@Published var memoryHistory: [Double] = []
@Published var gpuHistory: [Double] = []
private var monitoringTimer: Timer?
private let maxHistoryPoints = 30
// MARK: - Initialization
private init() {
// Initialize with empty history
cpuHistory = Array(repeating: 0.0, count: maxHistoryPoints)
memoryHistory = Array(repeating: 0.0, count: maxHistoryPoints)
gpuHistory = Array(repeating: 0.0, count: maxHistoryPoints)
}
deinit {
stopMonitoring()
}
// MARK: - Public Methods
func startMonitoring() {
guard !isMonitoring else { return }
isMonitoring = true
lastUpdated = Date()
// Start periodic monitoring (every 1 second)
monitoringTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
guard let self = self else { return }
Task { @MainActor in
self.updateSystemStats()
}
}
}
func stopMonitoring() {
guard isMonitoring else { return }
monitoringTimer?.invalidate()
monitoringTimer = nil
isMonitoring = false
}
// MARK: - Private Methods
@MainActor
private func updateSystemStats() {
let newCpuUsage = getCPUUsage()
let newMemoryUsage = getMemoryUsage()
let newGpuUsage = getGPUUsage()
// Update current values
cpuUsage = newCpuUsage
memoryUsage = newMemoryUsage
gpuUsage = newGpuUsage
lastUpdated = Date()
// Update history arrays (sliding window)
updateHistory(value: newCpuUsage, history: &cpuHistory)
updateHistory(value: newMemoryUsage, history: &memoryHistory)
updateHistory(value: newGpuUsage, history: &gpuHistory)
}
private func updateHistory(value: Double, history: inout [Double]) {
// Remove first element and append new value
if history.count >= maxHistoryPoints {
history.removeFirst()
}
history.append(value)
}
// MARK: - System Monitoring Functions
private func getCPUUsage() -> Double {
// Simplified CPU usage monitoring using host_statistics
var hostStats = host_cpu_load_info()
var count = mach_msg_type_number_t(MemoryLayout<host_cpu_load_info>.size / MemoryLayout<integer_t>.size)
let result = withUnsafeMutablePointer(to: &hostStats) {
$0.withMemoryRebound(to: integer_t.self, capacity: Int(count)) {
host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, $0, &count)
}
}
guard result == KERN_SUCCESS else {
return 0.0
}
let totalTicks = hostStats.cpu_ticks.0 + hostStats.cpu_ticks.1 +
hostStats.cpu_ticks.2 + hostStats.cpu_ticks.3
guard totalTicks > 0 else { return 0.0 }
let idleTicks = hostStats.cpu_ticks.2 // CPU_STATE_IDLE
let usage = Double(totalTicks - idleTicks) / Double(totalTicks) * 100.0
return min(100.0, max(0.0, usage))
}
private func getMemoryUsage() -> Double {
// Simplified memory usage monitoring using host_statistics
var vmStatistics = vm_statistics64()
var size = mach_msg_type_number_t(MemoryLayout<vm_statistics64>.size / MemoryLayout<integer_t>.size)
let vmResult = withUnsafeMutablePointer(to: &vmStatistics) {
$0.withMemoryRebound(to: integer_t.self, capacity: Int(size)) {
host_statistics64(mach_host_self(), HOST_VM_INFO64, $0, &size)
}
}
guard vmResult == KERN_SUCCESS else {
return 0.0
}
let pageSize = UInt64(vm_kernel_page_size)
let totalMemory = (UInt64(vmStatistics.free_count) + UInt64(vmStatistics.active_count) +
UInt64(vmStatistics.inactive_count) + UInt64(vmStatistics.wire_count)) * pageSize
let usedMemory = (UInt64(vmStatistics.active_count) + UInt64(vmStatistics.inactive_count) +
UInt64(vmStatistics.wire_count)) * pageSize
guard totalMemory > 0 else { return 0.0 }
let usage = Double(usedMemory) / Double(totalMemory) * 100.0
return min(100.0, max(0.0, usage))
}
private func getGPUUsage() -> Double {
// GPU usage monitoring is complex on macOS and requires private APIs
// For now, we'll provide a placeholder that simulates GPU usage
// In a production app, this would need Metal Performance Shaders or IOKit GPU monitoring
// Simulate realistic GPU usage based on system load
let baseUsage = Double.random(in: 5...15)
let variance = Double.random(in: -5...25)
let simulatedUsage = baseUsage + variance
return min(100.0, max(0.0, simulatedUsage))
}
// MARK: - Computed Properties for UI
var cpuUsageString: String {
return String(format: "%.1f%%", cpuUsage)
}
var memoryUsageString: String {
return String(format: "%.1f%%", memoryUsage)
}
var gpuUsageString: String {
return String(format: "%.1f%%", gpuUsage)
}
var maxCpuUsage: Double {
return cpuHistory.max() ?? 0.0
}
var maxMemoryUsage: Double {
return memoryHistory.max() ?? 0.0
}
var maxGpuUsage: Double {
return gpuHistory.max() ?? 0.0
}
var avgCpuUsage: Double {
let nonZeroValues = cpuHistory.filter { $0 > 0 }
guard !nonZeroValues.isEmpty else { return 0.0 }
return nonZeroValues.reduce(0, +) / Double(nonZeroValues.count)
}
var avgMemoryUsage: Double {
let nonZeroValues = memoryHistory.filter { $0 > 0 }
guard !nonZeroValues.isEmpty else { return 0.0 }
return nonZeroValues.reduce(0, +) / Double(nonZeroValues.count)
}
var avgGpuUsage: Double {
let nonZeroValues = gpuHistory.filter { $0 > 0 }
guard !nonZeroValues.isEmpty else { return 0.0 }
return nonZeroValues.reduce(0, +) / Double(nonZeroValues.count)
}
}