c08fc277a9
A native macOS network monitor that renders live traffic as a top-down neon city: your Mac is downtown, remote orgs are glowing districts, and traffic is cars of light driving the roads. Architecture: - NetworkCityCore: swappable data layer behind a ConnectionSource protocol (nettop-backed today), plus pure/tested logic — traffic diffing, org classification (reverse-DNS + CIDR), traffic classes, hub-and-spoke expansion policy, label anti-overlap, and latency→distance layout. - NetworkCityApp: SwiftUI + SpriteKit city — auto-expanding districts, protocol-coloured cars, click-to-inspect panel, and latency-as-distance (districts glide to their measured RTT). - nettop-probe: CLI proof of the data layer. 44 tests passing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
61 lines
2.3 KiB
Swift
61 lines
2.3 KiB
Swift
import Foundation
|
|
|
|
/// Decides which districts auto-expand into hub-and-spoke each tick.
|
|
///
|
|
/// Rules: a district expands only if it has at least two endpoints (a single-IP
|
|
/// org has nothing to fan out) and ranks among the busiest `topK` above
|
|
/// `floorBytes`. Once chosen it stays expanded for `ttlTicks` ticks, so a burst
|
|
/// leaves a smooth tail instead of flickering collapsed the moment it idles.
|
|
///
|
|
/// Pure and deterministic given its input — no SpriteKit, fully unit-testable.
|
|
public final class ExpansionPolicy {
|
|
public struct District: Sendable {
|
|
public let key: String
|
|
public let bytes: UInt64
|
|
public let endpointCount: Int
|
|
public init(key: String, bytes: UInt64, endpointCount: Int) {
|
|
self.key = key
|
|
self.bytes = bytes
|
|
self.endpointCount = endpointCount
|
|
}
|
|
}
|
|
|
|
private let topK: Int
|
|
private let ttlTicks: Int
|
|
private let floorBytes: UInt64
|
|
private var ttl: [String: Int] = [:]
|
|
|
|
public init(topK: Int = 4, ttlTicks: Int = 4, floorBytes: UInt64 = 8 * 1024) {
|
|
self.topK = topK
|
|
self.ttlTicks = ttlTicks
|
|
self.floorBytes = floorBytes
|
|
}
|
|
|
|
/// Advance one tick over the full set of currently-known districts (include
|
|
/// idle ones so their tail can decay). Returns the set of keys to render
|
|
/// expanded.
|
|
public func update(_ districts: [District]) -> Set<String> {
|
|
// Decay every existing timer.
|
|
for key in ttl.keys { ttl[key] = max(0, (ttl[key] ?? 0) - 1) }
|
|
|
|
// Refresh the busiest multi-endpoint districts.
|
|
var winners = 0
|
|
for d in districts.sorted(by: { $0.bytes > $1.bytes }) {
|
|
guard winners < topK else { break }
|
|
if d.bytes >= floorBytes && d.endpointCount >= 2 {
|
|
ttl[d.key] = ttlTicks
|
|
winners += 1
|
|
}
|
|
}
|
|
|
|
// Expanded = live timer AND still has something to fan out.
|
|
let endpointCounts = Dictionary(districts.map { ($0.key, $0.endpointCount) },
|
|
uniquingKeysWith: { a, _ in a })
|
|
var expanded = Set<String>()
|
|
for (key, remaining) in ttl where remaining > 0 {
|
|
if (endpointCounts[key] ?? 0) >= 2 { expanded.insert(key) }
|
|
}
|
|
return expanded
|
|
}
|
|
}
|