diff --git a/Modules/Net/main.swift b/Modules/Net/main.swift index d7c87214..4d1e15ec 100644 --- a/Modules/Net/main.swift +++ b/Modules/Net/main.swift @@ -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 { diff --git a/Modules/Net/popup.swift b/Modules/Net/popup.swift index a41d420b..d84d176a 100644 --- a/Modules/Net/popup.swift +++ b/Modules/Net/popup.swift @@ -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) } }) } diff --git a/Modules/Net/readers.swift b/Modules/Net/readers.swift index 91d56b97..c59b2e9d 100644 --- a/Modules/Net/readers.swift +++ b/Modules/Net/readers.swift @@ -304,9 +304,7 @@ internal class UsageReader: Reader { 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 { } } + 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 { } } + 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 { 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 { 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 { 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 { } 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.size - MemoryLayout.size var additional = [UInt8]() @@ -724,8 +749,7 @@ internal class ConnectivityReader: Reader { guard let checksum = computeChecksum(header: header, additionalPayload: additional) else { return nil } header.checksum = checksum - let package = Data(bytes: &header, count: MemoryLayout.size) + Data(additional) - return package + return Data(bytes: &header, count: MemoryLayout.size) + Data(additional) } private func computeChecksum(header: ICMPHeader, additionalPayload: [UInt8]) -> UInt16? {