feat: moved all reader's values to the Codable protocol

This commit is contained in:
Serhiy Mytrovtsiy
2023-06-27 17:09:38 +02:00
parent 91603406a2
commit 1e0ba3b61f
17 changed files with 327 additions and 169 deletions

View File

@@ -95,16 +95,33 @@ public protocol KeyValue_p {
var additional: Any? { get }
}
public struct KeyValue_t: KeyValue_p {
public struct KeyValue_t: KeyValue_p, Codable {
public let key: String
public let value: String
public let additional: Any?
private enum CodingKeys: String, CodingKey {
case key, value
}
public init(key: String, value: String, additional: Any? = nil) {
self.key = key
self.value = value
self.additional = additional
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.key = try container.decode(String.self, forKey: .key)
self.value = try container.decode(String.self, forKey: .value)
self.additional = nil
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(key, forKey: .key)
try container.encode(value, forKey: .value)
}
}
public struct Units {
@@ -560,19 +577,26 @@ public func removeNotification(_ id: String) {
center.removeDeliveredNotifications(withIdentifiers: [id])
}
public struct TopProcess {
public struct TopProcess: Codable {
public var pid: Int
public var command: String
public var name: String?
public var usage: Double
public var icon: NSImage?
public init(pid: Int, command: String, name: String?, usage: Double, icon: NSImage?) {
public var icon: NSImage? {
get {
if let app = NSRunningApplication(processIdentifier: pid_t(self.pid) ) {
return app.icon
}
return Constants.defaultProcessIcon
}
}
public init(pid: Int, command: String, name: String?, usage: Double) {
self.pid = pid
self.command = command
self.name = name
self.usage = usage
self.icon = icon != nil ? icon : Constants.defaultProcessIcon
}
}

View File

@@ -11,7 +11,7 @@
import Cocoa
public enum Platform: String {
public enum Platform: String, Codable {
case intel
case m1

View File

@@ -13,7 +13,7 @@ import Cocoa
import Kit
import IOKit.ps
struct Battery_Usage: value_t {
struct Battery_Usage: value_t, Codable {
var powerSource: String = ""
var state: String? = nil
var isCharged: Bool = false

View File

@@ -221,13 +221,11 @@ public class ProcessReader: Reader<[TopProcess]> {
}
var name: String? = nil
var icon: NSImage? = nil
if let app = NSRunningApplication(processIdentifier: pid_t(pid) ) {
name = app.localizedName ?? nil
icon = app.icon
}
processes.append(TopProcess(pid: pid, command: command, name: name, usage: usage, icon: icon))
processes.append(TopProcess(pid: pid, command: command, name: name, usage: usage))
}
}

View File

@@ -13,7 +13,7 @@ import Foundation
import Kit
import CoreBluetooth
public struct BLEDevice {
public struct BLEDevice: Codable {
let address: String
var name: String
var uuid: UUID?
@@ -24,7 +24,7 @@ public struct BLEDevice {
var isConnected: Bool = false
var isPaired: Bool = false
var peripheral: CBPeripheral?
var peripheral: CBPeripheral? = nil
var isPeripheralInitialized: Bool = false
var id: String {
@@ -38,6 +38,42 @@ public struct BLEDevice {
return Store.shared.bool(key: "ble_\(self.id)", defaultValue: false)
}
}
private enum CodingKeys: String, CodingKey {
case address, name, uuid, RSSI, batteryLevel, isConnected, isPaired
}
init(address: String, name: String, uuid: UUID?, RSSI: Int?, batteryLevel: [KeyValue_t], isConnected: Bool, isPaired: Bool) {
self.address = address
self.name = name
self.uuid = uuid
self.RSSI = RSSI
self.batteryLevel = batteryLevel
self.isConnected = isConnected
self.isPaired = isPaired
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.address = try container.decode(String.self, forKey: .address)
self.name = try container.decode(String.self, forKey: .name)
self.uuid = try? container.decode(UUID.self, forKey: .uuid)
self.RSSI = try? container.decode(Int.self, forKey: .RSSI)
self.batteryLevel = try container.decode(Array<KeyValue_t>.self, forKey: .batteryLevel)
self.isConnected = try container.decode(Bool.self, forKey: .isConnected)
self.isPaired = try container.decode(Bool.self, forKey: .isPaired)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(address, forKey: .address)
try container.encode(name, forKey: .name)
try container.encode(uuid, forKey: .uuid)
try container.encode(RSSI, forKey: .RSSI)
try container.encode(batteryLevel, forKey: .batteryLevel)
try container.encode(isConnected, forKey: .isConnected)
try container.encode(isPaired, forKey: .isPaired)
}
}
public class Bluetooth: Module {

View File

@@ -9,7 +9,7 @@
import Cocoa
import Kit
public struct CPU_Load: value_t {
public struct CPU_Load: value_t, Codable {
var totalUsage: Double = 0
var usagePerCore: [Double] = []
var usageECores: Double? = nil
@@ -26,7 +26,7 @@ public struct CPU_Load: value_t {
}
}
public struct CPU_Limit {
public struct CPU_Limit: Codable {
var scheduler: Int = 0
var cpus: Int = 0
var speed: Int = 0

View File

@@ -232,13 +232,11 @@ public class ProcessReader: Reader<[TopProcess]> {
let usage = Double(usageString.replacingOccurrences(of: ",", with: ".")) ?? 0
var name: String? = nil
var icon: NSImage? = nil
if let app = NSRunningApplication(processIdentifier: pid_t(pid) ) {
name = app.localizedName ?? nil
icon = app.icon
}
processes.append(TopProcess(pid: pid, command: command, name: name, usage: usage, icon: icon))
processes.append(TopProcess(pid: pid, command: command, name: name, usage: usage))
}
if index == self.numberOfProcesses { stop = true }

View File

@@ -12,7 +12,7 @@
import Cocoa
import Kit
public struct stats {
public struct stats: Codable {
var read: Int64 = 0
var write: Int64 = 0
@@ -20,12 +20,12 @@ public struct stats {
var writeBytes: Int64 = 0
}
public struct smart_t {
public struct smart_t: Codable {
var temperature: Int = 0
var life: Int = 0
}
public struct drive {
public struct drive: Codable {
var parent: io_object_t = 0
var mediaName: String = ""
@@ -46,9 +46,25 @@ public struct drive {
var smart: smart_t? = nil
}
public class Disks {
fileprivate let queue = DispatchQueue(label: "eu.exelban.Stats.Disk.SynchronizedArray", attributes: .concurrent)
fileprivate var array = [drive]()
public class Disks: Codable {
private var queue: DispatchQueue = DispatchQueue(label: "eu.exelban.Stats.Disk.SynchronizedArray", attributes: .concurrent)
private var array: [drive] = []
enum CodingKeys: String, CodingKey {
case array
}
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.array = try container.decode(Array<drive>.self, forKey: CodingKeys.array)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(array, forKey: .array)
}
init() {}
public var count: Int {
var result = 0
@@ -140,14 +156,19 @@ public class Disks {
}
}
public struct Disk_process: IOProcess_p {
public struct Disk_process: IOProcess_p, Codable {
private var base: DataSizeBase {
DataSizeBase(rawValue: Store.shared.string(key: "\(Disk.name)_base", defaultValue: "byte")) ?? .byte
}
public var pid: Int32
public var name: String
public var icon: NSImage = Constants.defaultProcessIcon
public var icon: NSImage {
if let app = NSRunningApplication(processIdentifier: self.pid) {
return app.icon ?? Constants.defaultProcessIcon
}
return Constants.defaultProcessIcon
}
var read: Int
var write: Int
@@ -169,9 +190,6 @@ public struct Disk_process: IOProcess_p {
if let name = app.localizedName {
self.name = name
}
if let icon = app.icon {
self.icon = icon
}
}
}
}

View File

@@ -21,7 +21,7 @@ public enum GPU_types: GPU_type {
case discrete = "d"
}
public struct GPU_Info {
public struct GPU_Info: Codable {
public let id: String
public let type: GPU_type
@@ -50,7 +50,7 @@ public struct GPU_Info {
}
}
public struct GPUs: value_t {
public struct GPUs: value_t, Codable {
public var list: [GPU_Info] = []
internal func active() -> [GPU_Info] {

View File

@@ -13,25 +13,25 @@ import Cocoa
import Kit
import SystemConfiguration
public enum Network_t: String {
public enum Network_t: String, Codable {
case wifi
case ethernet
case bluetooth
case other
}
public struct Network_interface {
public struct Network_interface: Codable {
var displayName: String = ""
var BSDName: String = ""
var address: String = ""
}
public struct Network_addr {
public struct Network_addr: Codable {
var v4: String? = nil
var v6: String? = nil
}
public struct Network_wifi {
public struct Network_wifi: Codable {
var countryCode: String? = nil
var ssid: String? = nil
var bssid: String? = nil
@@ -61,9 +61,14 @@ public struct Network_wifi {
}
}
public struct Network_Usage: value_t {
var bandwidth: Bandwidth = (0, 0)
var total: Bandwidth = (0, 0)
public struct Bandwidth: Codable {
var upload: Int64 = 0
var download: Int64 = 0
}
public struct Network_Usage: value_t, Codable {
var bandwidth: Bandwidth = Bandwidth()
var total: Bandwidth = Bandwidth()
var laddr: String? = nil // local ip
var raddr: Network_addr = Network_addr() // remote ip
@@ -75,7 +80,7 @@ public struct Network_Usage: value_t {
var wifiDetails: Network_wifi = Network_wifi()
mutating func reset() {
self.bandwidth = (0, 0)
self.bandwidth = Bandwidth()
self.laddr = nil
self.raddr = Network_addr()
@@ -89,13 +94,24 @@ public struct Network_Usage: value_t {
public var widgetValue: Double = 0
}
public struct Network_Process {
public struct Network_Connectivity: Codable {
var status: Bool = false
}
public struct Network_Process: Codable {
var time: Date = Date()
var name: String = ""
var pid: String = ""
var download: Int = 0
var upload: Int = 0
var icon: NSImage? = nil
var icon: NSImage {
get {
if let pid = pid_t(self.pid), let app = NSRunningApplication(processIdentifier: pid) {
return app.icon ?? Constants.defaultProcessIcon
}
return Constants.defaultProcessIcon
}
}
}
public class Network: Module {
@@ -167,7 +183,7 @@ public class Network: Module {
self.settingsView.ICMPHostCallback = { [unowned self] isDisabled in
if isDisabled {
self.popupView.resetConnectivityView()
self.connectivityCallback(false)
self.connectivityCallback(Network_Connectivity(status: false))
}
}
self.settingsView.publicIPRefreshIntervalCallback = { [unowned self] in
@@ -222,14 +238,14 @@ public class Network: Module {
}
}
private func connectivityCallback(_ raw: Bool?) {
private func connectivityCallback(_ raw: Network_Connectivity?) {
guard let value = raw, self.enabled else { return }
self.popupView.connectivityCallback(value)
self.popupView.connectivityCallback(value.status)
self.menuBar.widgets.filter{ $0.isActive }.forEach { (w: Widget) in
switch w.item {
case let widget as StateWidget: widget.setValue(value)
case let widget as StateWidget: widget.setValue(value.status)
default: break
}
}

View File

@@ -215,7 +215,7 @@ internal class UsageReader: Reader<Network_Usage> {
var totalUpload: Int64 = 0
var totalDownload: Int64 = 0
guard getifaddrs(&interfaceAddresses) == 0 else {
return (0, 0)
return Bandwidth()
}
var pointer = interfaceAddresses
@@ -237,7 +237,7 @@ internal class UsageReader: Reader<Network_Usage> {
}
freeifaddrs(interfaceAddresses)
return (totalUpload, totalDownload)
return Bandwidth(upload: totalUpload, download: totalDownload)
}
private func readProcessBandwidth() -> Bandwidth {
@@ -260,7 +260,7 @@ internal class UsageReader: Reader<Network_Usage> {
try task.run()
} catch let err {
error("read bandwidth from processes: \(err)", log: self.log)
return (0, 0)
return Bandwidth()
}
let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile()
@@ -269,7 +269,7 @@ internal class UsageReader: Reader<Network_Usage> {
_ = String(decoding: errorData, as: UTF8.self)
if output.isEmpty {
return (0, 0)
return Bandwidth()
}
var totalUpload: Int64 = 0
@@ -294,7 +294,7 @@ internal class UsageReader: Reader<Network_Usage> {
}
}
return (totalUpload, totalDownload)
return Bandwidth(upload: totalUpload, download: totalDownload)
}
public func getDetails() {
@@ -416,7 +416,7 @@ internal class UsageReader: Reader<Network_Usage> {
}
@objc func resetTotalNetworkUsage() {
self.usage.total = (0, 0)
self.usage.total = Bandwidth()
}
}
@@ -492,10 +492,8 @@ public class ProcessReader: Reader<[Network_Process]> {
}
if let app = NSRunningApplication(processIdentifier: pid_t(process.pid) ?? 0) {
process.name = app.localizedName ?? nameArray.dropLast().joined(separator: ".")
process.icon = app.icon != nil ? app.icon : Constants.defaultProcessIcon
} else {
process.name = nameArray.dropLast().joined(separator: ".")
process.icon = Constants.defaultProcessIcon
}
if process.name == "" {
@@ -533,7 +531,7 @@ public class ProcessReader: Reader<[Network_Process]> {
upload = 0
}
processes.append(Network_Process(time: time, name: p.name, pid: p.pid, download: download, upload: upload, icon: p.icon))
processes.append(Network_Process(time: time, name: p.name, pid: p.pid, download: download, upload: upload))
}
}
self.previous = list
@@ -566,7 +564,7 @@ internal class ConnectivityReaderWrapper {
}
// inspired by https://github.com/samiyr/SwiftyPing
internal class ConnectivityReader: Reader<Bool> {
internal class ConnectivityReader: Reader<Network_Connectivity> {
private let variablesQueue = DispatchQueue(label: "eu.exelban.ConnectivityReaderQueue")
private let identifier = UInt16.random(in: 0..<UInt16.max)
@@ -582,6 +580,8 @@ internal class ConnectivityReader: Reader<Bool> {
private var socket: CFSocket?
private var socketSource: CFRunLoopSource?
private var wrapper: Network_Connectivity = Network_Connectivity(status: false)
private var _status: Bool? = nil
private var status: Bool? {
get {
@@ -672,7 +672,11 @@ internal class ConnectivityReader: Reader<Bool> {
if error != .success {
self.socketCallback(data: nil, error: error)
}
self.callback(self.status)
if let v = self.status {
self.wrapper.status = v
self.callback(self.wrapper)
}
}
@objc private func timeoutCallback() {

View File

@@ -12,7 +12,7 @@
import Cocoa
import Kit
public struct RAM_Usage: value_t {
public struct RAM_Usage: value_t, Codable {
var total: Double
var used: Double
var free: Double
@@ -26,7 +26,7 @@ public struct RAM_Usage: value_t {
var cache: Double
var pressure: Double
var pressureLevel: DispatchSource.MemoryPressureEvent
var rawPressureLevel: UInt
var swap: Swap
public var widgetValue: Double {
@@ -40,9 +40,13 @@ public struct RAM_Usage: value_t {
return Double((self.total - self.free) / self.total)
}
}
public var pressureLevel: DispatchSource.MemoryPressureEvent {
DispatchSource.MemoryPressureEvent(rawValue: self.rawPressureLevel)
}
}
public struct Swap {
public struct Swap: Codable {
var total: Double
var used: Double
var free: Double

View File

@@ -78,7 +78,7 @@ internal class UsageReader: Reader<RAM_Usage> {
cache: purgeable + external,
pressure: 100.0 * (wired + compressed) / self.totalSize,
pressureLevel: DispatchSource.MemoryPressureEvent(rawValue: UInt(pressureLevel)),
rawPressureLevel: UInt(pressureLevel),
swap: Swap(
total: Double(swap.xsu_total),
@@ -189,12 +189,10 @@ public class ProcessReader: Reader<[TopProcess]> {
}
var name: String = command
var icon: NSImage? = nil
if let app = NSRunningApplication(processIdentifier: pid_t(pid) ) {
name = app.localizedName ?? command
icon = app.icon
}
return TopProcess(pid: pid, command: command, name: name, usage: usage * Double(1024 * 1024), icon: icon)
return TopProcess(pid: pid, command: command, name: name, usage: usage * Double(1024 * 1024))
}
}

View File

@@ -23,7 +23,7 @@ public class Sensors: Module {
public init() {
self.sensorsReader = SensorsReader()
self.settingsView = Settings("Sensors", list: self.sensorsReader.list)
self.settingsView = Settings("Sensors", list: self.sensorsReader.list.sensors)
self.popupView = Popup()
super.init(
@@ -32,7 +32,7 @@ public class Sensors: Module {
)
guard self.available else { return }
self.popupView.setup(self.sensorsReader.list)
self.popupView.setup(self.sensorsReader.list.sensors)
self.settingsView.callback = { [unowned self] in
self.sensorsReader.read()
@@ -44,8 +44,8 @@ public class Sensors: Module {
DispatchQueue.global(qos: .background).async {
self.sensorsReader.HIDCallback()
DispatchQueue.main.async {
self.popupView.setup(self.sensorsReader.list)
self.settingsView.setList(list: self.sensorsReader.list)
self.popupView.setup(self.sensorsReader.list.sensors)
self.settingsView.setList(list: self.sensorsReader.list.sensors)
}
}
}
@@ -53,8 +53,8 @@ public class Sensors: Module {
DispatchQueue.global(qos: .background).async {
self.sensorsReader.unknownCallback()
DispatchQueue.main.async {
self.popupView.setup(self.sensorsReader.list)
self.settingsView.setList(list: self.sensorsReader.list)
self.popupView.setup(self.sensorsReader.list.sensors)
self.settingsView.setList(list: self.sensorsReader.list.sensors)
}
}
}
@@ -72,7 +72,7 @@ public class Sensors: Module {
public override func willTerminate() {
guard SMCHelper.shared.isActive() else { return }
self.sensorsReader.list.filter({ $0 is Fan }).forEach { (s: Sensor_p) in
self.sensorsReader.list.sensors.filter({ $0 is Fan }).forEach { (s: Sensor_p) in
if let f = s as? Fan, let mode = f.customMode {
if mode != .automatic {
SMCHelper.shared.setFanMode(f.id, mode: FanMode.automatic.rawValue)
@@ -82,16 +82,16 @@ public class Sensors: Module {
}
public override func isAvailable() -> Bool {
return !self.sensorsReader.list.isEmpty
return !self.sensorsReader.list.sensors.isEmpty
}
private func checkIfNoSensorsEnabled() {
if self.sensorsReader.list.filter({ $0.state }).isEmpty {
if self.sensorsReader.list.sensors.filter({ $0.state }).isEmpty {
NotificationCenter.default.post(name: .toggleModule, object: nil, userInfo: ["module": self.config.name, "state": false])
}
}
private func usageCallback(_ raw: [Sensor_p]?) {
private func usageCallback(_ raw: Sensors_List?) {
guard let value = raw, self.enabled else {
return
}
@@ -99,7 +99,7 @@ public class Sensors: Module {
var list: [Stack_t] = []
var flatList: [[ColorValue]] = []
value.forEach { (s: Sensor_p) in
value.sensors.forEach { (s: Sensor_p) in
if s.state {
var value = s.formattedMiniValue
if let f = s as? Fan {
@@ -112,7 +112,7 @@ public class Sensors: Module {
}
}
self.popupView.usageCallback(value)
self.popupView.usageCallback(value.sensors)
self.menuBar.widgets.filter{ $0.isActive }.forEach { (w: Widget) in
switch w.item {

View File

@@ -12,21 +12,10 @@
import Cocoa
import Kit
internal class SensorsReader: Reader<[Sensor_p]> {
internal class SensorsReader: Reader<Sensors_List> {
static let HIDtypes: [SensorType] = [.temperature, .voltage]
private let listQueue = DispatchQueue(label: "listQueue")
internal var listData: [Sensor_p] = []
internal var list: [Sensor_p] {
get {
self.listQueue.sync{self.listData}
}
set(newValue) {
self.listQueue.sync {
self.listData = newValue
}
}
}
internal var list: Sensors_List = Sensors_List()
private var lastRead: Date = Date()
private let firstRead: Date = Date()
@@ -39,7 +28,7 @@ internal class SensorsReader: Reader<[Sensor_p]> {
init() {
self.unknownSensorsState = Store.shared.bool(key: "Sensors_unknown", defaultValue: false)
super.init()
self.list = self.sensors()
self.list.sensors = self.sensors()
}
private func sensors() -> [Sensor_p] {
@@ -126,15 +115,15 @@ internal class SensorsReader: Reader<[Sensor_p]> {
}
public override func read() {
for i in self.list.indices {
guard self.list[i].group != .hid && !self.list[i].isComputed else { continue }
if !self.unknownSensorsState && self.list[i].group == .unknown { continue }
self.list[i].value = SMC.shared.getValue(self.list[i].key) ?? 0
for i in self.list.sensors.indices {
guard self.list.sensors[i].group != .hid && !self.list.sensors[i].isComputed else { continue }
if !self.unknownSensorsState && self.list.sensors[i].group == .unknown { continue }
self.list.sensors[i].value = SMC.shared.getValue(self.list.sensors[i].key) ?? 0
}
var cpuSensors = self.list.filter({ $0.group == .CPU && $0.type == .temperature && $0.average }).map{ $0.value }
var gpuSensors = self.list.filter({ $0.group == .GPU && $0.type == .temperature && $0.average }).map{ $0.value }
let fanSensors = self.list.filter({ $0.type == .fan && !$0.isComputed })
var cpuSensors = self.list.sensors.filter({ $0.group == .CPU && $0.type == .temperature && $0.average }).map{ $0.value }
var gpuSensors = self.list.sensors.filter({ $0.group == .GPU && $0.type == .temperature && $0.average }).map{ $0.value }
let fanSensors = self.list.sensors.filter({ $0.type == .fan && !$0.isComputed })
#if arch(arm64)
if self.HIDState {
@@ -145,23 +134,23 @@ internal class SensorsReader: Reader<[Sensor_p]> {
return
}
if let idx = self.list.firstIndex(where: { $0.group == .hid && $0.key == key }) {
self.list[idx].value = value
if let idx = self.list.sensors.firstIndex(where: { $0.group == .hid && $0.key == key }) {
self.list.sensors[idx].value = value
}
}
}
cpuSensors += self.list.filter({ $0.key.hasPrefix("pACC MTR Temp") || $0.key.hasPrefix("eACC MTR Temp") }).map{ $0.value }
gpuSensors += self.list.filter({ $0.key.hasPrefix("GPU MTR Temp") }).map{ $0.value }
cpuSensors += self.list.sensors.filter({ $0.key.hasPrefix("pACC MTR Temp") || $0.key.hasPrefix("eACC MTR Temp") }).map{ $0.value }
gpuSensors += self.list.sensors.filter({ $0.key.hasPrefix("GPU MTR Temp") }).map{ $0.value }
let socSensors = list.filter({ $0.key.hasPrefix("SOC MTR Temp") }).map{ $0.value }
let socSensors = self.list.sensors.filter({ $0.key.hasPrefix("SOC MTR Temp") }).map{ $0.value }
if !socSensors.isEmpty {
if let idx = self.list.firstIndex(where: { $0.key == "Average SOC" }) {
self.list[idx].value = socSensors.reduce(0, +) / Double(socSensors.count)
if let idx = self.list.sensors.firstIndex(where: { $0.key == "Average SOC" }) {
self.list.sensors[idx].value = socSensors.reduce(0, +) / Double(socSensors.count)
}
if let max = socSensors.max() {
if let idx = self.list.firstIndex(where: { $0.key == "Hottest SOC" }) {
self.list[idx].value = max
if let idx = self.list.sensors.firstIndex(where: { $0.key == "Hottest SOC" }) {
self.list.sensors[idx].value = max
}
}
}
@@ -169,46 +158,46 @@ internal class SensorsReader: Reader<[Sensor_p]> {
#endif
if !cpuSensors.isEmpty {
if let idx = self.list.firstIndex(where: { $0.key == "Average CPU" }) {
self.list[idx].value = cpuSensors.reduce(0, +) / Double(cpuSensors.count)
if let idx = self.list.sensors.firstIndex(where: { $0.key == "Average CPU" }) {
self.list.sensors[idx].value = cpuSensors.reduce(0, +) / Double(cpuSensors.count)
}
if let max = cpuSensors.max() {
if let idx = self.list.firstIndex(where: { $0.key == "Hottest CPU" }) {
self.list[idx].value = max
if let idx = self.list.sensors.firstIndex(where: { $0.key == "Hottest CPU" }) {
self.list.sensors[idx].value = max
}
}
}
if !gpuSensors.isEmpty {
if let idx = self.list.firstIndex(where: { $0.key == "Average GPU" }) {
self.list[idx].value = gpuSensors.reduce(0, +) / Double(gpuSensors.count)
if let idx = self.list.sensors.firstIndex(where: { $0.key == "Average GPU" }) {
self.list.sensors[idx].value = gpuSensors.reduce(0, +) / Double(gpuSensors.count)
}
if let max = gpuSensors.max() {
if let idx = self.list.firstIndex(where: { $0.key == "Hottest GPU" }) {
self.list[idx].value = max
if let idx = self.list.sensors.firstIndex(where: { $0.key == "Hottest GPU" }) {
self.list.sensors[idx].value = max
}
}
}
if !fanSensors.isEmpty && fanSensors.count > 1 {
if let f = fanSensors.max(by: { $0.value < $1.value }) as? Fan {
if let idx = self.list.firstIndex(where: { $0.key == "Fastest Fan" }) {
if var fan = self.list[idx] as? Fan {
if let idx = self.list.sensors.firstIndex(where: { $0.key == "Fastest Fan" }) {
if var fan = self.list.sensors[idx] as? Fan {
fan.value = f.value
fan.minSpeed = f.minSpeed
fan.maxSpeed = f.maxSpeed
self.list[idx] = fan
self.list.sensors[idx] = fan
}
}
}
}
if let PSTRSensor = self.list.first(where: { $0.key == "PSTR"}), PSTRSensor.value > 0 {
if let PSTRSensor = self.list.sensors.first(where: { $0.key == "PSTR"}), PSTRSensor.value > 0 {
let sinceLastRead = Date().timeIntervalSince(self.lastRead)
let sinceFirstRead = Date().timeIntervalSince(self.firstRead)
if let totalIdx = self.list.firstIndex(where: {$0.key == "Total System Consumption"}), sinceLastRead > 0 {
self.list[totalIdx].value += PSTRSensor.value * sinceLastRead / 3600
if let avgIdx = self.list.firstIndex(where: {$0.key == "Average System Total"}), sinceFirstRead > 0 {
self.list[avgIdx].value = self.list[totalIdx].value * 3600 / sinceFirstRead
if let totalIdx = self.list.sensors.firstIndex(where: {$0.key == "Total System Consumption"}), sinceLastRead > 0 {
self.list.sensors[totalIdx].value += PSTRSensor.value * sinceLastRead / 3600
if let avgIdx = self.list.sensors.firstIndex(where: {$0.key == "Average System Total"}), sinceFirstRead > 0 {
self.list.sensors[avgIdx].value = self.list.sensors[totalIdx].value * 3600 / sinceFirstRead
}
}
@@ -216,12 +205,12 @@ internal class SensorsReader: Reader<[Sensor_p]> {
}
// cut off low dc in voltage
if let idx = self.list.firstIndex(where: { $0.key == "VD0R" }), self.list[idx].value < 0.4 {
self.list[idx].value = 0
if let idx = self.list.sensors.firstIndex(where: { $0.key == "VD0R" }), self.list.sensors[idx].value < 0.4 {
self.list.sensors[idx].value = 0
}
// cut off low dc in current
if let idx = self.list.firstIndex(where: { $0.key == "ID0R" }), self.list[idx].value < 0.05 {
self.list[idx].value = 0
if let idx = self.list.sensors.firstIndex(where: { $0.key == "ID0R" }), self.list.sensors[idx].value < 0.05 {
self.list.sensors[idx].value = 0
}
self.callback(self.list)
@@ -447,9 +436,9 @@ extension SensorsReader {
public func HIDCallback() {
if self.HIDState {
self.list += self.initHIDSensors()
self.list.sensors += self.initHIDSensors()
} else {
self.list = self.list.filter({ $0.group != .hid })
self.list.sensors = self.list.sensors.filter({ $0.group != .hid })
}
}
}

View File

@@ -12,7 +12,7 @@
import Kit
import Cocoa
internal enum SensorGroup: String {
public enum SensorGroup: String, Codable {
case CPU = "CPU"
case GPU = "GPU"
case system = "Systems"
@@ -21,7 +21,7 @@ internal enum SensorGroup: String {
case unknown = "Unknown"
}
internal enum SensorType: String {
public enum SensorType: String, Codable {
case temperature = "Temperature"
case voltage = "Voltage"
case current = "Current"
@@ -30,7 +30,7 @@ internal enum SensorType: String {
case fan = "Fans"
}
internal protocol Sensor_p {
public protocol Sensor_p {
var key: String { get }
var name: String { get }
var value: Double { get set }
@@ -49,19 +49,92 @@ internal protocol Sensor_p {
var formattedPopupValue: String { get }
}
internal struct Sensor: Sensor_p {
var key: String
var name: String
public struct Sensors_List: Codable {
private var queue: DispatchQueue = DispatchQueue(label: "eu.exelban.Stats.Sensors.SynchronizedArray", attributes: .concurrent)
var value: Double = 0
private var list: [Sensor_p] = []
public var sensors: [Sensor_p] {
get {
self.queue.sync{self.list}
}
set(newValue) {
self.queue.sync {
self.list = newValue
}
}
}
var group: SensorGroup
var type: SensorType
var platforms: [Platform]
var isComputed: Bool = false
var average: Bool = false
private enum CodingKeys: String, CodingKey {
case sensors
}
var unit: String {
public init() {}
public func encode(to encoder: Encoder) throws {
let wrappers = sensors.map { Sensor_w($0) }
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(wrappers, forKey: .sensors)
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let wrappers = try container.decode([Sensor_w].self, forKey: .sensors)
self.sensors = wrappers.map { $0.sensor }
}
}
public struct Sensor_w: Codable {
let sensor: Sensor_p
private enum CodingKeys: String, CodingKey {
case base, payload
}
private enum Typ: Int, Codable {
case sensor
case fan
}
init(_ sensor: Sensor_p) {
self.sensor = sensor
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let base = try container.decode(Typ.self, forKey: .base)
switch base {
case .sensor: self.sensor = try container.decode(Sensor.self, forKey: .payload)
case .fan: self.sensor = try container.decode(Fan.self, forKey: .payload)
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch sensor {
case let payload as Sensor:
try container.encode(Typ.sensor, forKey: .base)
try container.encode(payload, forKey: .payload)
case let payload as Fan:
try container.encode(Typ.fan, forKey: .base)
try container.encode(payload, forKey: .payload)
default: break
}
}
}
public struct Sensor: Sensor_p, Codable {
public var key: String
public var name: String
public var value: Double = 0
public var group: SensorGroup
public var type: SensorType
public var platforms: [Platform]
public var isComputed: Bool = false
public var average: Bool = false
public var unit: String {
get {
switch self.type {
case .temperature:
@@ -80,7 +153,7 @@ internal struct Sensor: Sensor_p {
}
}
var formattedValue: String {
public var formattedValue: String {
get {
switch self.type {
case .temperature:
@@ -99,7 +172,7 @@ internal struct Sensor: Sensor_p {
}
}
}
var formattedPopupValue: String {
public var formattedPopupValue: String {
get {
switch self.type {
case .temperature:
@@ -118,7 +191,7 @@ internal struct Sensor: Sensor_p {
}
}
}
var formattedMiniValue: String {
public var formattedMiniValue: String {
get {
switch self.type {
case .temperature:
@@ -132,14 +205,14 @@ internal struct Sensor: Sensor_p {
}
}
var state: Bool {
public var state: Bool {
Store.shared.bool(key: "sensor_\(self.key)", defaultValue: false)
}
var popupState: Bool {
public var popupState: Bool {
Store.shared.bool(key: "sensor_\(self.key)_popup", defaultValue: true)
}
func copy() -> Sensor {
public func copy() -> Sensor {
Sensor(
key: self.key,
name: self.name,
@@ -152,48 +225,48 @@ internal struct Sensor: Sensor_p {
}
}
internal struct Fan: Sensor_p {
let id: Int
var key: String
var name: String
var minSpeed: Double
var maxSpeed: Double
var value: Double
var mode: FanMode
public struct Fan: Sensor_p, Codable {
public let id: Int
public var key: String
public var name: String
public var minSpeed: Double
public var maxSpeed: Double
public var value: Double
public var mode: FanMode
var percentage: Int {
public var percentage: Int {
if self.value != 0 && self.maxSpeed != 0 && self.value != 1 && self.maxSpeed != 1 {
return (100*Int(self.value)) / Int(self.maxSpeed)
}
return 0
}
var group: SensorGroup = .sensor
var type: SensorType = .fan
var platforms: [Platform] = Platform.all
var isIntelOnly: Bool = false
var isComputed: Bool = false
var average: Bool = false
var unit: String = "RPM"
public var group: SensorGroup = .sensor
public var type: SensorType = .fan
public var platforms: [Platform] = Platform.all
public var isIntelOnly: Bool = false
public var isComputed: Bool = false
public var average: Bool = false
public var unit: String = "RPM"
var formattedValue: String {
public var formattedValue: String {
"\(Int(value)) RPM"
}
var formattedMiniValue: String {
public var formattedMiniValue: String {
"\(Int(value))"
}
var formattedPopupValue: String {
public var formattedPopupValue: String {
"\(Int(value)) RPM"
}
var state: Bool {
public var state: Bool {
Store.shared.bool(key: "sensor_\(self.key)", defaultValue: false)
}
var popupState: Bool {
public var popupState: Bool {
Store.shared.bool(key: "sensor_\(self.key)_popup", defaultValue: true)
}
var customSpeed: Int? {
public var customSpeed: Int? {
get {
if !Store.shared.exist(key: "fan_\(self.id)_speed") {
return nil
@@ -208,7 +281,7 @@ internal struct Fan: Sensor_p {
}
}
}
var customMode: FanMode? {
public var customMode: FanMode? {
get {
if !Store.shared.exist(key: "fan_\(self.id)_mode") {
return nil

View File

@@ -42,7 +42,7 @@ internal enum SMCKeys: UInt8 {
case readVers = 12
}
public enum FanMode: Int {
public enum FanMode: Int, Codable {
case automatic = 0
case forced = 1
}