mirror of
https://github.com/morgan9e/macos-stats
synced 2026-04-14 00:04:15 +09:00
Fix: No download speed (#157)
* Implemented alternative approach to get download speed * Early check * Reset data on interface change * Trigger alternative approach manually * Fix
This commit is contained in:
@@ -24,11 +24,15 @@ struct ipResponse: Decodable {
|
||||
}
|
||||
|
||||
internal class UsageReader: Reader<Network_Usage> {
|
||||
typealias BandwidthUsage = (upload: Int64, download: Int64)
|
||||
|
||||
public var store: UnsafePointer<Store>? = nil
|
||||
|
||||
private var reachability: Reachability? = nil
|
||||
private var usage: Network_Usage = Network_Usage()
|
||||
|
||||
private var shouldReportSelectedInterfaceBandwidthOnly = true
|
||||
|
||||
private var primaryInterface: String {
|
||||
get {
|
||||
if let global = SCDynamicStoreCopyValue(nil, "State:/Network/Global/IPv4" as CFString), let name = global["PrimaryInterface"] as? String {
|
||||
@@ -69,51 +73,23 @@ internal class UsageReader: Reader<Network_Usage> {
|
||||
}
|
||||
|
||||
public override func read() {
|
||||
var interfaceAddresses: UnsafeMutablePointer<ifaddrs>? = nil
|
||||
var upload: Int64 = 0
|
||||
var download: Int64 = 0
|
||||
guard getifaddrs(&interfaceAddresses) == 0 else { return }
|
||||
|
||||
var pointer = interfaceAddresses
|
||||
while pointer != nil {
|
||||
defer { pointer = pointer?.pointee.ifa_next }
|
||||
|
||||
if String(cString: pointer!.pointee.ifa_name) != self.interfaceID {
|
||||
continue
|
||||
}
|
||||
|
||||
if let ip = getLocalIP(pointer!), self.usage.laddr != ip {
|
||||
self.usage.laddr = ip
|
||||
}
|
||||
|
||||
if let info = getBytesInfo(pointer!) {
|
||||
upload += info.upload
|
||||
download += info.download
|
||||
}
|
||||
}
|
||||
freeifaddrs(interfaceAddresses)
|
||||
|
||||
if self.usage.upload != 0 {
|
||||
self.usage.upload = upload - self.usage.upload
|
||||
}
|
||||
if self.usage.download != 0 {
|
||||
self.usage.download = download - self.usage.download
|
||||
let currentUsage: BandwidthUsage
|
||||
if shouldReportSelectedInterfaceBandwidthOnly {
|
||||
currentUsage = interfaceBandwidthUsage()
|
||||
} else {
|
||||
currentUsage = allProcessesBandwidthUsage()
|
||||
}
|
||||
|
||||
if self.usage.upload < 0 {
|
||||
self.usage.upload = 0
|
||||
}
|
||||
if self.usage.download < 0 {
|
||||
self.usage.download = 0
|
||||
}
|
||||
self.usage.upload = max(currentUsage.upload - self.usage.upload, 0)
|
||||
self.usage.download = max(currentUsage.download - self.usage.download, 0)
|
||||
|
||||
self.usage.totalUpload += self.usage.upload
|
||||
self.usage.totalDownload += self.usage.download
|
||||
|
||||
self.callback(self.usage)
|
||||
|
||||
self.usage.upload = upload
|
||||
self.usage.download = download
|
||||
self.usage.upload = currentUsage.upload
|
||||
self.usage.download = currentUsage.download
|
||||
}
|
||||
|
||||
public func getDetails() {
|
||||
@@ -196,6 +172,89 @@ internal class UsageReader: Reader<Network_Usage> {
|
||||
let data: UnsafeMutablePointer<if_data>? = unsafeBitCast(pointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
|
||||
return (upload: Int64(data?.pointee.ifi_obytes ?? 0), download: Int64(data?.pointee.ifi_ibytes ?? 0))
|
||||
}
|
||||
|
||||
private func interfaceBandwidthUsage() -> BandwidthUsage {
|
||||
var interfaceAddresses: UnsafeMutablePointer<ifaddrs>? = nil
|
||||
var totalUpload: Int64 = 0
|
||||
var totalDownload: Int64 = 0
|
||||
guard getifaddrs(&interfaceAddresses) == 0 else {
|
||||
return (0, 0)
|
||||
}
|
||||
|
||||
var pointer = interfaceAddresses
|
||||
while pointer != nil {
|
||||
defer { pointer = pointer?.pointee.ifa_next }
|
||||
|
||||
if String(cString: pointer!.pointee.ifa_name) != self.interfaceID {
|
||||
continue
|
||||
}
|
||||
|
||||
if let ip = getLocalIP(pointer!), self.usage.laddr != ip {
|
||||
self.usage.laddr = ip
|
||||
}
|
||||
|
||||
if let info = getBytesInfo(pointer!) {
|
||||
totalUpload += info.upload
|
||||
totalDownload += info.download
|
||||
}
|
||||
}
|
||||
freeifaddrs(interfaceAddresses)
|
||||
|
||||
return (totalUpload, totalDownload)
|
||||
}
|
||||
|
||||
private func allProcessesBandwidthUsage() -> BandwidthUsage {
|
||||
let task = Process()
|
||||
task.launchPath = "/usr/bin/nettop"
|
||||
task.arguments = ["-P", "-L", "1", "-k", "time,interface,state,rx_dupe,rx_ooo,re-tx,rtt_avg,rcvsize,tx_win,tc_class,tc_mgt,cc_algo,P,C,R,W,arch"]
|
||||
|
||||
let outputPipe = Pipe()
|
||||
let errorPipe = Pipe()
|
||||
|
||||
task.standardOutput = outputPipe
|
||||
task.standardError = errorPipe
|
||||
|
||||
do {
|
||||
try task.run()
|
||||
} catch let error {
|
||||
print(error)
|
||||
return (0, 0)
|
||||
}
|
||||
|
||||
let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile()
|
||||
let errorData = errorPipe.fileHandleForReading.readDataToEndOfFile()
|
||||
let output = String(decoding: outputData, as: UTF8.self)
|
||||
_ = String(decoding: errorData, as: UTF8.self)
|
||||
|
||||
if output.isEmpty {
|
||||
return (0, 0)
|
||||
}
|
||||
|
||||
var totalUpload: Int64 = 0
|
||||
var totalDownload: Int64 = 0
|
||||
var firstLine = false
|
||||
output.enumerateLines { (line, _) -> () in
|
||||
if !firstLine {
|
||||
firstLine = true
|
||||
return
|
||||
}
|
||||
|
||||
let parsedLine = line.split(separator: ",")
|
||||
guard parsedLine.count >= 3 else {
|
||||
return
|
||||
}
|
||||
|
||||
if let download = Int(parsedLine[1]) {
|
||||
totalDownload += Int64(download)
|
||||
}
|
||||
if let upload = Int(parsedLine[2]) {
|
||||
totalUpload += Int64(upload)
|
||||
}
|
||||
}
|
||||
|
||||
return (totalUpload, totalDownload)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class ProcessReader: Reader<[Network_Process]> {
|
||||
|
||||
Reference in New Issue
Block a user