mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
204 lines
6.8 KiB
Swift
204 lines
6.8 KiB
Swift
//
|
|
// StatsManager.swift
|
|
// DynamicIsland
|
|
//
|
|
// System performance monitoring for the Dynamic Island Stats feature
|
|
// Created by Hariharan Mudaliar
|
|
|
|
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)
|
|
}
|
|
}
|