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()) } }