Files
networkcity/Sources/nettop-probe/main.swift
T

46 lines
1.8 KiB
Swift
Raw Normal View History

import Foundation
import NetworkCityCore
// Phase-1 proof of concept. Polls nettop, diffs successive snapshots, and prints
// the busiest external flows as live KB/s i.e. the raw material that will
// become cars driving to buildings.
setvbuf(stdout, nil, _IONBF, 0) // unbuffered: see output live even when piped
let interval: TimeInterval = 2.0
let source = NettopSource()
let differ = TrafficDiffer()
var warmedUp = false
func pad(_ s: String, _ width: Int) -> String {
s.count >= width ? String(s.prefix(width)) : s + String(repeating: " ", count: width - s.count)
}
print("📡 NetworkCity probe — sampling every \(Int(interval))s. Ctrl-C to stop.\n")
for await snap in source.snapshots(every: interval) {
let deltas = differ.ingest(snap).filter { $0.connection.isExternal }
let external = snap.connections.filter(\.isExternal).count
if !warmedUp {
warmedUp = true
print("… warming up (need two samples to compute rates) …\n")
continue
}
let active = deltas.sorted { ($0.inBytesPerSec + $0.outBytesPerSec) > ($1.inBytesPerSec + $1.outBytesPerSec) }
let ts = DateFormatter.localizedString(from: snap.timestamp, dateStyle: .none, timeStyle: .medium)
print("─── \(ts) \(active.count) active / \(external) external connections ───")
print("\(pad("PROCESS", 18)) \(pad("DESTINATION", 24)) ↓ KB/s ↑ KB/s")
if active.isEmpty {
print(" (quiet — no measurable traffic this interval)")
}
for d in active.prefix(15) {
let dest = d.connection.remote.display
print("\(pad(d.connection.processName, 18)) \(pad(dest, 24)) "
+ "\(String(format: "%9.1f", d.inBytesPerSec / 1024)) \(String(format: "%9.1f", d.outBytesPerSec / 1024))")
}
print("")
}