feat: moved from JSON objects of metrics to simplified strings for the Stats Remote

This commit is contained in:
Serhiy Mytrovtsiy
2025-04-05 22:54:01 +02:00
parent d2688a35b2
commit f524f86429
8 changed files with 98 additions and 44 deletions

View File

@@ -12,6 +12,10 @@
import Foundation
import Cocoa
public protocol RemoteType {
func remote() -> Data?
}
public class Remote {
public static let shared = Remote()
static public var host = URL(string: "http://localhost:8008")! // https://api.system-stats.com http://localhost:8008
@@ -83,9 +87,9 @@ public class Remote {
NotificationCenter.default.post(name: .remoteState, object: nil, userInfo: ["auth": self.isAuthorized])
}
public func send(key: String, value: Codable) {
guard self.monitoring && self.isAuthorized, let blobData = try? JSONEncoder().encode(value) else { return }
self.ws.send(key: key, data: blobData)
public func send(key: String, value: Any) {
guard self.monitoring && self.isAuthorized, let v = value as? RemoteType, let data = v.remote() else { return }
self.ws.send(key: key, data: data)
}
@objc private func successLogin() {
@@ -462,9 +466,6 @@ class WebSocketManager: NSObject {
}
public func send(key: String, data: Data) {
if key != "details" && !key.contains("CPU@") && !key.contains("GPU@") && !key.contains("RAM@") && !key.contains("Network@") && !key.contains("Sensors@") {
return
}
if !self.isConnected { return }
let message = WebSocketMessage(name: key, data: data)
guard let messageData = try? JSONEncoder().encode(message) else { return }

View File

@@ -10,7 +10,7 @@ import Cocoa
import Kit
import WidgetKit
public struct CPU_Load: Codable {
public struct CPU_Load: Codable, RemoteType {
var totalUsage: Double = 0
var usagePerCore: [Double] = []
var usageECores: Double? = nil
@@ -19,6 +19,26 @@ public struct CPU_Load: Codable {
var systemLoad: Double = 0
var userLoad: Double = 0
var idleLoad: Double = 0
public func remote() -> Data? {
var string = "1,1,\(self.totalUsage),\(self.usagePerCore.count),"
for c in self.usagePerCore {
string += "\(c),"
}
string += "$"
return string.data(using: .utf8)
}
}
public struct CPU_Frequency: Codable, RemoteType {
var value: Double = 0
var eCore: Double = 0
var pCore: Double = 0
public func remote() -> Data? {
let string = "1,1,\(self.value)$"
return string.data(using: .utf8)
}
}
public struct CPU_Limit: Codable {

View File

@@ -421,9 +421,8 @@ internal class Popup: PopupWrapper {
})
}
public func frequencyCallback(_ value: [Double]?) {
public func frequencyCallback(_ value: CPU_Frequency?) {
guard let value else { return }
guard !value.filter({ $0 != 0 }).isEmpty else { return }
DispatchQueue.main.async(execute: {
if !self.initializedFrequency {
@@ -436,37 +435,19 @@ internal class Popup: PopupWrapper {
}
if (self.window?.isVisible ?? false) || !self.initializedFrequency {
if value.count == 1 {
let freq = value.first ?? 0
if freq > self.maxFreq {
self.maxFreq = freq
}
self.coresFreqField?.stringValue = "\(Int(freq)) MHz"
if let circle = self.frequencyCircle {
circle.setValue((100*freq)/self.maxFreq)
circle.setText("\((freq/1000).rounded(toPlaces: 2))")
circle.toolTip = "\(localizedString("CPU frequency")): \(Int(freq)) MHz - \(((100*freq)/self.maxFreq).rounded(toPlaces: 2))%"
}
} else if value.count == 2 {
let e = value.first ?? 0
let p = value.last ?? 0
self.eCoresFreqField?.stringValue = "\(Int(e)) MHz"
self.pCoresFreqField?.stringValue = "\(Int(p)) MHz"
if let eCoreCount = SystemKit.shared.device.info.cpu?.eCores, let pCoreCount = SystemKit.shared.device.info.cpu?.pCores {
let freq = ((e * Double(eCoreCount)) + (p * Double(pCoreCount))) / Double(eCoreCount + pCoreCount)
if freq > self.maxFreq {
self.maxFreq = freq
}
self.coresFreqField?.stringValue = "\(Int(freq)) MHz"
if let circle = self.frequencyCircle {
circle.setValue((100*freq)/self.maxFreq)
circle.setText("\((freq/1000).rounded(toPlaces: 2))")
circle.toolTip = "\(localizedString("CPU frequency")): \(Int(freq)) MHz - \(((100*freq)/self.maxFreq).rounded(toPlaces: 2))%"
}
}
if value.value > self.maxFreq {
self.maxFreq = value.value
}
self.coresFreqField?.stringValue = "\(Int(value.value)) MHz"
if let circle = self.frequencyCircle {
circle.setValue((100*value.value)/self.maxFreq)
circle.setText("\((value.value/1000).rounded(toPlaces: 2))")
circle.toolTip = "\(localizedString("CPU frequency")): \(Int(value.value)) MHz - \(((100*value.value)/self.maxFreq).rounded(toPlaces: 2))%"
}
self.eCoresFreqField?.stringValue = "\(Int(value.eCore)) MHz"
self.pCoresFreqField?.stringValue = "\(Int(value.pCore)) MHz"
self.initializedFrequency = true
}
})

View File

@@ -292,9 +292,11 @@ public class TemperatureReader: Reader<Double> {
}
// inspired by https://github.com/shank03/StatsBar/blob/e175aa71c914ce882ce2e90163f3eb18262a8e25/StatsBar/Service/IOReport.swift
public class FrequencyReader: Reader<[Double]> {
public class FrequencyReader: Reader<CPU_Frequency> {
private var eCoreFreqs: [Int32] = []
private var pCoreFreqs: [Int32] = []
private var eCoreCount: Double = 0
private var pCoreCount: Double = 0
private var channels: CFMutableDictionary? = nil
private var subscription: IOReportSubscriptionRef? = nil
@@ -314,6 +316,8 @@ public class FrequencyReader: Reader<[Double]> {
public override func setup() {
self.eCoreFreqs = SystemKit.shared.device.info.cpu?.eCoreFrequencies ?? []
self.pCoreFreqs = SystemKit.shared.device.info.cpu?.pCoreFrequencies ?? []
self.eCoreCount = Double(SystemKit.shared.device.info.cpu?.eCores ?? 0)
self.pCoreCount = Double(SystemKit.shared.device.info.cpu?.pCores ?? 0)
self.channels = self.getChannels()
var dict: Unmanaged<CFMutableDictionary>?
self.subscription = IOReportCreateSubscription(nil, self.channels, &dict, 0, nil)
@@ -352,8 +356,9 @@ public class FrequencyReader: Reader<[Double]> {
let eFreq: Double = eCores.reduce(0, { $0 + $1 }) / Double(self.measurementCount)
let pFreq: Double = pCores.reduce(0, { $0 + $1 }) / Double(self.measurementCount)
let value: Double = ((eFreq * self.eCoreCount) + (pFreq * self.pCoreCount)) / (self.eCoreCount + self.pCoreCount)
self.callback([eFreq, pFreq])
self.callback(CPU_Frequency(value: value, eCore: eFreq, pCore: pFreq))
self.isReading = false
}
}

View File

@@ -64,9 +64,13 @@ public struct drive: Codable {
public var popupState: Bool {
Store.shared.bool(key: "Disk_\(self.uuid)_popup", defaultValue: true)
}
public func remote() -> String {
return "\(self.uuid),\(self.size),\(self.size-self.free),\(self.free),\(self.activity.read),\(self.activity.write)"
}
}
public class Disks: Codable {
public class Disks: Codable, RemoteType {
private var queue: DispatchQueue = DispatchQueue(label: "eu.exelban.Stats.Disk.SynchronizedArray")
private var _array: [drive] = []
public var array: [drive] {
@@ -156,6 +160,18 @@ public class Disks: Codable {
func updateSMARTData(_ idx: Int, smart: smart_t?) {
self.array[idx].smart = smart
}
public func remote() -> Data? {
var string = "\(self.array.count),"
for (i, v) in self.array.enumerated() {
string += v.remote()
if i != self.array.count {
string += ","
}
}
string += "$"
return string.data(using: .utf8)
}
}
public struct Disk_process: Process_p, Codable {

View File

@@ -52,9 +52,17 @@ public struct GPU_Info: Codable {
self.renderUtilization = render
self.tilerUtilization = tiler
}
public func remote() -> String {
var id = self.id
if self.id.isEmpty {
id = "0"
}
return "\(id),1,\(self.utilization ?? 0),\(self.renderUtilization ?? 0),\(self.tilerUtilization ?? 0),,"
}
}
public class GPUs: Codable {
public class GPUs: Codable, RemoteType {
private var queue: DispatchQueue = DispatchQueue(label: "eu.exelban.Stats.GPU.SynchronizedArray")
private var _list: [GPU_Info] = []
@@ -82,6 +90,18 @@ public class GPUs: Codable {
internal func active() -> [GPU_Info] {
return self.list.filter{ $0.state && $0.utilization != nil }.sorted{ $0.utilization ?? 0 > $1.utilization ?? 0 }
}
public func remote() -> Data? {
var string = "\(self.list.count),"
for (i, v) in self.list.enumerated() {
string += v.remote()
if i != self.list.count {
string += ","
}
}
string += "$"
return string.data(using: .utf8)
}
}
public class GPU: Module {

View File

@@ -68,7 +68,7 @@ public struct Bandwidth: Codable {
var download: Int64 = 0
}
public struct Network_Usage: Codable {
public struct Network_Usage: Codable, RemoteType {
var bandwidth: Bandwidth = Bandwidth()
var total: Bandwidth = Bandwidth()
@@ -92,6 +92,12 @@ public struct Network_Usage: Codable {
self.wifiDetails.reset()
}
public func remote() -> Data? {
let addr = "\(self.laddr.v4 ?? ""),\(self.laddr.v6 ?? ""),\(self.raddr.v4 ?? ""),\(self.raddr.v6 ?? "")"
let string = "1,\(self.interface?.BSDName ?? ""),1,\(self.bandwidth.download),\(self.bandwidth.upload),\(addr)$"
return string.data(using: .utf8)
}
}
public struct Network_Connectivity: Codable {

View File

@@ -13,7 +13,7 @@ import Cocoa
import Kit
import WidgetKit
public struct RAM_Usage: Codable {
public struct RAM_Usage: Codable, RemoteType {
var total: Double
var used: Double
var free: Double
@@ -35,6 +35,11 @@ public struct RAM_Usage: Codable {
public var usage: Double {
get { Double((self.total - self.free) / self.total) }
}
public func remote() -> Data? {
let string = "\(self.total),\(self.used),\(self.pressure.level),\(self.swap.used)$"
return string.data(using: .utf8)
}
}
public struct Swap: Codable {