Files
OpenStats/Packages/OpenStatsKit/Sources/OpenStatsUI/State/MetricsStore.swift
T
gentpan 63c37b0e22 进程管理器:包括系统进程,可搜索、筛选、排序、按应用合并并结束进程;快捷开关改为开关样式
- 系统与其他用户的进程通过系统自带的 ps 读取 CPU、内存与 CPU 时间,自己的进程另有线程、唤醒次数与磁盘读写
- 列标题点击排序;搜索名称、PID 或用户;全部 / 我的 / 系统;按进程或按应用
- 选中后显示路径与详情,可在访达中显示、用 Apple 智能解释、退出或强制退出(二次确认,系统进程与 loginwindow 禁止结束)
- 底部汇总用户 / 系统 / 空闲与全系统进程数、线程数;窗口变窄时自动隐藏次要列
- 仪表盘快捷开关改为开关样式
2026-09-13 06:26:43 +05:00

94 lines
4.2 KiB
Swift

import Foundation
import Metrics
import Observation
import SMC
/// 主线程上的最新指标与短历史,只保留界面需要的数据
@MainActor
@Observable
public final class MetricsStore {
public static let historyCapacity = 60
public let topology = CPUSampler.topology()
public private(set) var system = SystemInfoReader.read()
public private(set) var cpu: CPULoad?
public private(set) var cpuTotal = History<Double>(capacity: historyCapacity)
/// 每次采样各核心的占用,用于核心热力图
public private(set) var coreHistory = History<[Double]>(capacity: historyCapacity)
public private(set) var memory: MemoryUsage?
public private(set) var memoryHistory = History<Double>(capacity: historyCapacity)
public private(set) var pressureHistory = History<MemoryPressure>(capacity: historyCapacity)
/// 交换区换入 / 换出速率(字节 / 秒)
public private(set) var swapRate: (swapIn: Double, swapOut: Double)?
@ObservationIgnored private var lastSwap: (date: Date, swapIn: UInt64, swapOut: UInt64)?
public private(set) var network: NetworkRate?
public private(set) var downloadHistory = History<Double>(capacity: historyCapacity)
public private(set) var uploadHistory = History<Double>(capacity: historyCapacity)
public private(set) var networkInterface: NetworkInterfaceInfo?
public private(set) var gpu: GPUUsage?
public private(set) var gpuHistory = History<Double>(capacity: historyCapacity)
public private(set) var disk: DiskUsage?
/// 启动时读取一次,确保面板首次打开时布局(是否有电池卡片)就已确定
public private(set) var battery: BatteryStatus? = BatterySampler.sample()
public private(set) var processes: [ProcessUsage] = []
public private(set) var systemCounts: SystemCounts?
public private(set) var sensors: SensorReadings?
public private(set) var lastUpdate: Date?
public init() {}
public func apply(_ snapshot: MetricsSnapshot) {
lastUpdate = snapshot.date
if let cpu = snapshot.cpu {
self.cpu = cpu
cpuTotal.append(cpu.total)
coreHistory.append(cpu.perCore)
}
if let memory = snapshot.memory {
self.memory = memory
memoryHistory.append(memory.usedFraction)
pressureHistory.append(memory.pressure)
if let last = lastSwap, snapshot.date > last.date {
let seconds = snapshot.date.timeIntervalSince(last.date)
swapRate = (Double(memory.swapInBytes >= last.swapIn ? memory.swapInBytes - last.swapIn : 0) / seconds,
Double(memory.swapOutBytes >= last.swapOut ? memory.swapOutBytes - last.swapOut : 0) / seconds)
}
lastSwap = (snapshot.date, memory.swapInBytes, memory.swapOutBytes)
}
if let network = snapshot.network {
self.network = network
downloadHistory.append(network.downloadBytesPerSecond)
uploadHistory.append(network.uploadBytesPerSecond)
}
if let interface = snapshot.networkInterface { networkInterface = interface }
if let gpu = snapshot.gpu {
self.gpu = gpu
gpuHistory.append(gpu.utilization)
}
if let disk = snapshot.disk { self.disk = disk }
if let battery = snapshot.battery { self.battery = battery }
if let processes = snapshot.processes { self.processes = processes }
if let counts = snapshot.systemCounts { systemCounts = counts }
if let sensors = snapshot.sensors { mergeSensors(sensors) }
}
/// 不同页面采集的温度分组不同,按组合并,避免切页时数据闪烁
private func mergeSensors(_ new: SensorReadings) {
guard let old = sensors else {
sensors = new
return
}
var byGroup = Dictionary(uniqueKeysWithValues: old.temperatures.map { ($0.group, $0) })
for summary in new.temperatures { byGroup[summary.group] = summary }
let ordered = TemperatureGroup.allCases.compactMap { byGroup[$0] }
sensors = SensorReadings(temperatures: ordered, fans: new.fans.isEmpty ? old.fans : new.fans)
}
var fastestFan: FanState? {
sensors?.fans.max { $0.current < $1.current }
}
}