Rename app and repository to Puter
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
import AppKit
|
||||
import Foundation
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
@MainActor
|
||||
enum SystemReportExporter {
|
||||
static func export(monitor: SystemMonitor, selectedResource: String? = nil) {
|
||||
let panel = NSSavePanel()
|
||||
panel.title = "Export system report"
|
||||
panel.message = "Save the current performance, process, power, battery, and connected-hardware snapshot as JSON."
|
||||
panel.prompt = "Export"
|
||||
panel.allowedContentTypes = [.json]
|
||||
panel.canCreateDirectories = true
|
||||
panel.nameFieldStringValue = "Task-Manager-Report-\(filenameDate()).json"
|
||||
|
||||
let data: Data
|
||||
do {
|
||||
data = try JSONSerialization.data(
|
||||
withJSONObject: report(monitor: monitor, selectedResource: selectedResource),
|
||||
options: [.prettyPrinted, .sortedKeys]
|
||||
)
|
||||
} catch {
|
||||
monitor.errorMessage = "Could not create the system report: \(error.localizedDescription)"
|
||||
return
|
||||
}
|
||||
|
||||
panel.begin { response in
|
||||
guard response == .OK, let destination = panel.url else { return }
|
||||
do {
|
||||
try data.write(to: destination, options: .atomic)
|
||||
} catch {
|
||||
monitor.errorMessage = "Could not export the system report: \(error.localizedDescription)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func report(monitor: SystemMonitor, selectedResource: String?) -> [String: Any] {
|
||||
let snapshot = monitor.snapshot
|
||||
let battery = monitor.hardware.battery
|
||||
return [
|
||||
"generatedAt": ISO8601DateFormatter().string(from: Date()),
|
||||
"selectedResource": selectedResource ?? "All",
|
||||
"system": [
|
||||
"cpuPercent": snapshot.cpuPercent,
|
||||
"logicalProcessors": snapshot.corePercents.count,
|
||||
"memoryBytes": snapshot.memoryTotal,
|
||||
"memoryUsedBytes": snapshot.memoryUsed,
|
||||
"memoryType": snapshot.memoryType,
|
||||
"memorySpeed": snapshot.memorySpeed,
|
||||
"memoryManufacturer": snapshot.memoryManufacturer,
|
||||
"diskReadBytesPerSecond": snapshot.diskReadRate,
|
||||
"diskWriteBytesPerSecond": snapshot.diskWriteRate,
|
||||
"diskActivePercent": snapshot.diskActivePercent,
|
||||
"gpuPercent": snapshot.gpuPercent,
|
||||
"gpuCoreCount": snapshot.gpuCoreCount,
|
||||
"networkInterface": snapshot.networkInterface,
|
||||
"networkReceiveBytesPerSecond": snapshot.networkReceiveRate,
|
||||
"networkSendBytesPerSecond": snapshot.networkSendRate,
|
||||
"processCount": snapshot.processCount,
|
||||
"threadCount": snapshot.threadCount,
|
||||
"uptimeSeconds": snapshot.uptime
|
||||
],
|
||||
"power": [
|
||||
"thermalState": monitor.hardware.thermalState,
|
||||
"systemPowerWatts": battery.systemPowerWatts,
|
||||
"externalPowerConnected": battery.externalPowerConnected,
|
||||
"lowPowerMode": ProcessInfo.processInfo.isLowPowerModeEnabled
|
||||
],
|
||||
"battery": batteryReport(battery),
|
||||
"ports": monitor.hardware.ports.map(portReport),
|
||||
"topProcesses": topProcesses(monitor),
|
||||
"topUsers": topUsers(monitor)
|
||||
]
|
||||
}
|
||||
|
||||
private static func batteryReport(_ battery: BatterySnapshot) -> [String: Any] {
|
||||
var result: [String: Any] = [
|
||||
"present": battery.isPresent,
|
||||
"chargePercent": battery.chargePercent,
|
||||
"charging": battery.isCharging,
|
||||
"fullyCharged": battery.isFullyCharged,
|
||||
"healthPercent": battery.healthPercent,
|
||||
"cycleCount": battery.cycleCount,
|
||||
"designCycleCount": battery.designCycleCount,
|
||||
"temperatureCelsius": battery.temperatureCelsius,
|
||||
"voltageVolts": battery.voltageVolts,
|
||||
"fullChargeCapacityMAh": battery.fullChargeCapacityMAh,
|
||||
"designCapacityMAh": battery.designCapacityMAh
|
||||
]
|
||||
if let adapter = battery.adapter {
|
||||
result["adapter"] = [
|
||||
"name": adapter.name,
|
||||
"manufacturer": adapter.manufacturer,
|
||||
"ratedWatts": adapter.ratedWatts,
|
||||
"negotiatedVoltage": adapter.negotiatedVoltage,
|
||||
"negotiatedCurrent": adapter.negotiatedCurrent,
|
||||
"negotiatedWatts": adapter.negotiatedWatts,
|
||||
"powerProfiles": adapter.profiles.map {
|
||||
["voltage": $0.voltageVolts, "current": $0.currentAmps, "watts": $0.watts]
|
||||
}
|
||||
]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private static func portReport(_ port: HardwarePortSnapshot) -> [String: Any] {
|
||||
[
|
||||
"name": port.name,
|
||||
"transport": port.transport,
|
||||
"maximumSpeed": port.maximumSpeed,
|
||||
"connected": port.isConnected,
|
||||
"status": port.status,
|
||||
"devices": port.devices.map {
|
||||
var device: [String: Any] = ["name": $0.name, "vendor": $0.vendor, "speed": $0.speed]
|
||||
if let value = $0.currentAvailableMA { device["currentAvailableMA"] = value }
|
||||
if let value = $0.currentRequiredMA { device["currentRequiredMA"] = value }
|
||||
return device
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
private static func topProcesses(_ monitor: SystemMonitor) -> [[String: Any]] {
|
||||
monitor.processes.sorted { $0.cpu > $1.cpu }.prefix(20).map { process in
|
||||
let activity = monitor.processActivity[process.pid] ?? ProcessActivityRate()
|
||||
return [
|
||||
"name": process.displayName,
|
||||
"pid": process.pid,
|
||||
"user": process.user,
|
||||
"cpuPercent": process.cpu,
|
||||
"residentBytes": process.residentBytes,
|
||||
"diskBytesPerSecond": activity.diskTotal,
|
||||
"networkBytesPerSecond": activity.networkTotal
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
private static func topUsers(_ monitor: SystemMonitor) -> [[String: Any]] {
|
||||
struct Totals {
|
||||
var processes = 0
|
||||
var cpu = 0.0
|
||||
var memory: UInt64 = 0
|
||||
var disk = 0.0
|
||||
var network = 0.0
|
||||
}
|
||||
var users: [String: Totals] = [:]
|
||||
for process in monitor.processes {
|
||||
let activity = monitor.processActivity[process.pid] ?? ProcessActivityRate()
|
||||
users[process.user, default: Totals()].processes += 1
|
||||
users[process.user, default: Totals()].cpu += process.cpu
|
||||
users[process.user, default: Totals()].memory += process.residentBytes
|
||||
users[process.user, default: Totals()].disk += activity.diskTotal
|
||||
users[process.user, default: Totals()].network += activity.networkTotal
|
||||
}
|
||||
return users.map { user, totals in
|
||||
[
|
||||
"user": user,
|
||||
"processes": totals.processes,
|
||||
"cpuPercent": totals.cpu,
|
||||
"residentBytes": totals.memory,
|
||||
"diskBytesPerSecond": totals.disk,
|
||||
"networkBytesPerSecond": totals.network
|
||||
]
|
||||
}
|
||||
.sorted { ($0["cpuPercent"] as? Double ?? 0) > ($1["cpuPercent"] as? Double ?? 0) }
|
||||
}
|
||||
|
||||
private static func filenameDate() -> String {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "yyyy-MM-dd-HHmmss"
|
||||
return formatter.string(from: Date())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user