mirror of
https://github.com/morgan9e/macos-stats
synced 2026-04-14 00:04:15 +09:00
feat: added latency to the Network popup (based on the ICMP requests) (#1707)
feat: removed WiFi network name from the network popup (starting from macOS Sonoma it's not available without CoreLocation access)
This commit is contained in:
@@ -96,6 +96,7 @@ public struct Network_Usage: value_t, Codable {
|
||||
|
||||
public struct Network_Connectivity: Codable {
|
||||
var status: Bool = false
|
||||
var latency: Double = 0
|
||||
}
|
||||
|
||||
public struct Network_Process: Codable {
|
||||
@@ -239,7 +240,7 @@ public class Network: Module {
|
||||
private func connectivityCallback(_ raw: Network_Connectivity?) {
|
||||
guard let value = raw, self.enabled else { return }
|
||||
|
||||
self.popupView.connectivityCallback(value.status)
|
||||
self.popupView.connectivityCallback(value)
|
||||
|
||||
self.menuBar.widgets.filter{ $0.isActive }.forEach { (w: Widget) in
|
||||
switch w.item {
|
||||
|
||||
@@ -39,14 +39,13 @@ internal class Popup: PopupWrapper {
|
||||
private var totalDownloadField: ValueField? = nil
|
||||
private var statusField: ValueField? = nil
|
||||
private var connectivityField: ValueField? = nil
|
||||
private var latencyField: ValueField? = nil
|
||||
|
||||
private var publicIPStackView: NSStackView? = nil
|
||||
private var publicIPv4Field: ValueField? = nil
|
||||
private var publicIPv6Field: ValueField? = nil
|
||||
|
||||
private var ssidField: ValueField? = nil
|
||||
private var standardField: ValueField? = nil
|
||||
private var securityField: ValueField? = nil
|
||||
private var channelField: ValueField? = nil
|
||||
|
||||
private var processesView: NSView? = nil
|
||||
@@ -91,6 +90,8 @@ internal class Popup: PopupWrapper {
|
||||
return value
|
||||
}
|
||||
|
||||
private var latency: [Double] = []
|
||||
|
||||
public init(_ title: String) {
|
||||
self.title = title
|
||||
|
||||
@@ -241,13 +242,12 @@ internal class Popup: PopupWrapper {
|
||||
|
||||
self.statusField = popupRow(container, n: 0, title: "\(localizedString("Status")):", value: localizedString("Unknown")).1
|
||||
self.connectivityField = popupRow(container, n: 0, title: "\(localizedString("Internet connection")):", value: localizedString("Unknown")).1
|
||||
|
||||
self.ssidField = popupRow(container, n: 0, title: "\(localizedString("Network")):", value: localizedString("Unknown")).1
|
||||
self.standardField = popupRow(container, n: 0, title: "\(localizedString("Standard")):", value: localizedString("Unknown")).1
|
||||
self.securityField = popupRow(container, n: 0, title: "\(localizedString("Security")):", value: localizedString("Unknown")).1
|
||||
self.channelField = popupRow(container, n: 0, title: "\(localizedString("Channel")):", value: localizedString("Unknown")).1
|
||||
self.latencyField = popupRow(container, n: 0, title: "\(localizedString("Latency")):", value: "0 ms").1
|
||||
|
||||
self.interfaceField = popupRow(container, n: 0, title: "\(localizedString("Interface")):", value: localizedString("Unknown")).1
|
||||
self.standardField = popupRow(container, n: 0, title: "\(localizedString("Standard")):", value: localizedString("Unknown")).1
|
||||
self.channelField = popupRow(container, n: 0, title: "\(localizedString("Channel")):", value: localizedString("Unknown")).1
|
||||
|
||||
self.macAddressField = popupRow(container, n: 0, title: "\(localizedString("Physical address")):", value: localizedString("Unknown")).1
|
||||
self.localIPField = popupRow(container, n: 0, title: "\(localizedString("Local IP")):", value: localizedString("Unknown")).1
|
||||
|
||||
@@ -371,7 +371,11 @@ internal class Popup: PopupWrapper {
|
||||
}
|
||||
|
||||
if let interface = value.interface {
|
||||
self.interfaceField?.stringValue = "\(interface.displayName) (\(interface.BSDName))"
|
||||
var details = interface.BSDName
|
||||
if value.connectionType == .wifi, let v = value.wifiDetails.RSSI {
|
||||
details += ", \(v)"
|
||||
}
|
||||
self.interfaceField?.stringValue = "\(interface.displayName) (\(details))"
|
||||
self.macAddressField?.stringValue = interface.address
|
||||
} else {
|
||||
self.interfaceField?.stringValue = localizedString("Unknown")
|
||||
@@ -379,10 +383,6 @@ internal class Popup: PopupWrapper {
|
||||
}
|
||||
|
||||
if value.connectionType == .wifi {
|
||||
self.ssidField?.stringValue = value.wifiDetails.ssid ?? localizedString("Unknown")
|
||||
if let v = value.wifiDetails.RSSI {
|
||||
self.ssidField?.stringValue += " (\(v))"
|
||||
}
|
||||
var rssi = localizedString("Unknown")
|
||||
if let v = value.wifiDetails.RSSI {
|
||||
rssi = "\(v) dBm"
|
||||
@@ -395,20 +395,16 @@ internal class Popup: PopupWrapper {
|
||||
if let v = value.wifiDetails.transmitRate {
|
||||
txRate = "\(v) Mbps"
|
||||
}
|
||||
self.ssidField?.toolTip = "RSSI: \(rssi)\nNoise: \(noise)\nTransmit rate: \(txRate)"
|
||||
|
||||
self.standardField?.stringValue = value.wifiDetails.standard ?? localizedString("Unknown")
|
||||
self.securityField?.stringValue = value.wifiDetails.security ?? localizedString("Unknown")
|
||||
self.channelField?.stringValue = value.wifiDetails.channel ?? localizedString("Unknown")
|
||||
|
||||
let number = value.wifiDetails.channelNumber ?? localizedString("Unknown")
|
||||
let band = value.wifiDetails.channelBand ?? localizedString("Unknown")
|
||||
let width = value.wifiDetails.channelWidth ?? localizedString("Unknown")
|
||||
self.channelField?.toolTip = "Channel number: \(number)\nChannel band: \(band)\nChannel width: \(width)\nTransmit rate: \(txRate)"
|
||||
self.channelField?.toolTip = "RSSI: \(rssi)\nNoise: \(noise)\nChannel number: \(number)\nChannel band: \(band)\nChannel width: \(width)\nTransmit rate: \(txRate)"
|
||||
} else {
|
||||
self.ssidField?.stringValue = localizedString("Unavailable")
|
||||
self.standardField?.stringValue = localizedString("Unavailable")
|
||||
self.securityField?.stringValue = localizedString("Unavailable")
|
||||
self.channelField?.stringValue = localizedString("Unavailable")
|
||||
}
|
||||
|
||||
@@ -445,19 +441,27 @@ internal class Popup: PopupWrapper {
|
||||
})
|
||||
}
|
||||
|
||||
public func connectivityCallback(_ value: Bool?) {
|
||||
public func connectivityCallback(_ value: Network_Connectivity?) {
|
||||
if self.latency.count >= 90 {
|
||||
self.latency.remove(at: 0)
|
||||
}
|
||||
self.latency.append(value?.latency ?? 0)
|
||||
|
||||
DispatchQueue.main.async(execute: {
|
||||
if (self.window?.isVisible ?? false) || !self.connectionInitialized {
|
||||
var text = "Unknown"
|
||||
if let v = value {
|
||||
text = v ? "UP" : "DOWN"
|
||||
text = v.status ? "UP" : "DOWN"
|
||||
}
|
||||
self.connectivityField?.stringValue = localizedString(text)
|
||||
if !self.latency.isEmpty {
|
||||
self.latencyField?.stringValue = "\((self.latency.reduce(0, +) / Double(self.latency.count)).rounded(toPlaces: 2)) ms"
|
||||
}
|
||||
self.connectionInitialized = true
|
||||
}
|
||||
|
||||
if let value, let chart = self.connectivityChart {
|
||||
chart.addValue(value)
|
||||
chart.addValue(value.status)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -304,9 +304,7 @@ internal class UsageReader: Reader<Network_Usage> {
|
||||
self.getPublicIP()
|
||||
}
|
||||
|
||||
guard self.interfaceID != "" else {
|
||||
return
|
||||
}
|
||||
guard self.interfaceID != "" else { return }
|
||||
|
||||
for interface in SCNetworkInterfaceCopyAll() as NSArray {
|
||||
if let bsdName = SCNetworkInterfaceGetBSDName(interface as! SCNetworkInterface), bsdName as String == self.interfaceID,
|
||||
@@ -328,6 +326,8 @@ internal class UsageReader: Reader<Network_Usage> {
|
||||
}
|
||||
}
|
||||
|
||||
guard self.usage.interface != nil else { return }
|
||||
|
||||
if let interface = CWWiFiClient.shared().interface(withName: self.interfaceID), self.usage.connectionType == .wifi {
|
||||
self.usage.wifiDetails.ssid = interface.ssid()
|
||||
self.usage.wifiDetails.bssid = interface.bssid()
|
||||
@@ -612,6 +612,18 @@ internal class ConnectivityReader: Reader<Network_Connectivity> {
|
||||
}
|
||||
}
|
||||
|
||||
private var _latency: Double? = nil
|
||||
private var latency: Double? {
|
||||
get {
|
||||
self.variablesQueue.sync { self._latency }
|
||||
}
|
||||
set {
|
||||
self.variablesQueue.sync { self._latency = newValue }
|
||||
}
|
||||
}
|
||||
|
||||
var start: DispatchTime? = nil
|
||||
|
||||
private struct ICMPHeader {
|
||||
public var type: UInt8
|
||||
public var code: UInt8
|
||||
@@ -667,6 +679,7 @@ internal class ConnectivityReader: Reader<Network_Connectivity> {
|
||||
let timer = Timer(timeInterval: self.timeout, target: self, selector: #selector(self.timeoutCallback), userInfo: nil, repeats: false)
|
||||
RunLoop.main.add(timer, forMode: .common)
|
||||
self.timeoutTimer = timer
|
||||
self.start = DispatchTime.now()
|
||||
|
||||
let error = CFSocketSendData(socket, addr as CFData, data as CFData, self.timeout)
|
||||
if error != .success {
|
||||
@@ -675,6 +688,9 @@ internal class ConnectivityReader: Reader<Network_Connectivity> {
|
||||
|
||||
if let v = self.status {
|
||||
self.wrapper.status = v
|
||||
if let l = self.latency {
|
||||
self.wrapper.latency = l
|
||||
}
|
||||
self.callback(self.wrapper)
|
||||
}
|
||||
}
|
||||
@@ -686,7 +702,9 @@ internal class ConnectivityReader: Reader<Network_Connectivity> {
|
||||
|
||||
private func socketCallback(data: Data? = nil, error: CFSocketError? = nil) {
|
||||
guard let data = data, validateResponse(data) else { return }
|
||||
let end = DispatchTime.now()
|
||||
|
||||
self.latency = Double(end.uptimeNanoseconds - (self.start?.uptimeNanoseconds ?? 0)) / 1_000_000
|
||||
self.status = error == nil
|
||||
self.isPinging = false
|
||||
self.timeoutTimer?.invalidate()
|
||||
@@ -713,7 +731,14 @@ internal class ConnectivityReader: Reader<Network_Connectivity> {
|
||||
}
|
||||
|
||||
private func request() -> Data? {
|
||||
var header = ICMPHeader(type: 8, code: 0, checksum: 0, identifier: CFSwapInt16HostToBig(self.identifier), sequenceNumber: CFSwapInt16HostToBig(0), payload: self.fingerprint.uuid)
|
||||
var header = ICMPHeader(
|
||||
type: 8,
|
||||
code: 0,
|
||||
checksum: 0,
|
||||
identifier: CFSwapInt16HostToBig(self.identifier),
|
||||
sequenceNumber: CFSwapInt16HostToBig(0),
|
||||
payload: self.fingerprint.uuid
|
||||
)
|
||||
|
||||
let delta = MemoryLayout<uuid_t>.size - MemoryLayout<uuid_t>.size
|
||||
var additional = [UInt8]()
|
||||
@@ -724,8 +749,7 @@ internal class ConnectivityReader: Reader<Network_Connectivity> {
|
||||
guard let checksum = computeChecksum(header: header, additionalPayload: additional) else { return nil }
|
||||
header.checksum = checksum
|
||||
|
||||
let package = Data(bytes: &header, count: MemoryLayout<ICMPHeader>.size) + Data(additional)
|
||||
return package
|
||||
return Data(bytes: &header, count: MemoryLayout<ICMPHeader>.size) + Data(additional)
|
||||
}
|
||||
|
||||
private func computeChecksum(header: ICMPHeader, additionalPayload: [UInt8]) -> UInt16? {
|
||||
|
||||
Reference in New Issue
Block a user