Files
RouterOS/RouterOSAssistant/Core/Services/InterfaceTrafficMonitor.swift
T
KayandClaude Sonnet 5 31607b7270 M17-M19: Bekannte Router, Live-Traffic-Anzeige, Übersicht-Animation+Drag
M17: "Bekannte Router" im Verbinden-Tab (SavedRouter/SavedRoutersStore),
Standort-Freitextfeld, Scroll-Cap ab 4 Einträgen. Bugfix: Umbenennen-
TextField steckte in einem sich selbst deaktivierenden Button.

M18: Live-Traffic-Punkt an Interfaces (InterfaceTrafficMonitor, eigene
SSH-Verbindung, monitor-traffic-Polling). Dabei zwei reale CLI-Parser-Bugs
gefunden und gefixt: running/disabled-Flags werden als Buchstaben vor dem
ersten Feld codiert, nicht als key=value; monitor-traffic liefert
"50.7kbps" statt einer reinen Zahl.

M19: Übersicht-Tab — animierte Flussrichtung auf allen Verbindungslinien
(TimelineView+dashPhase), frei verschiebbare Knoten mit Live-folgenden
Linien, Zurücksetzen-Button.

Zusätzlich (noch nicht live getestet, nur Build+Unit-Tests grün):
LAN-Port-Konflikt-Prüfung im Einrichten-Assistenten mit doppelter
Sicherheitsbestätigung, "Fertig"-Button nach erfolgreichem Anwenden.

82 Tests grün. HANDOFF.md/README.md/Manual.md/CHATLOG.md aktualisiert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YDmUd93KxsYGr2kLTotWnG
2026-09-15 21:40:46 +02:00

47 lines
2.1 KiB
Swift

import Foundation
/// Polls live per-interface throughput so the Verbinden-Tab's interface list can show which
/// ports are actually carrying traffic right now, not just "link up" (`NetworkInterface.running`
/// stays true as soon as a link is negotiated, even sitting completely idle).
///
/// Always opens its own dedicated SSH connection, independent of whichever transport (REST or
/// SSH) the live session is actually using — same reasoning as `BackupService`/`UpdateService`:
/// `/interface monitor-traffic` is a CLI-only RouterOS command with no REST equivalent. The
/// connection is kept open across repeated `fetchTraffic` calls (one new SSH connection per
/// poll tick would be far too slow for a few-times-a-second-feeling refresh) and only reopened
/// if the credentials change or a previous attempt failed.
actor InterfaceTrafficMonitor {
private var transport: SSHTransport?
private var connectedCredentials: RouterOSCredentials?
/// Best-effort per interface — one failing/renamed-mid-session interface doesn't take down
/// the whole poll, it's just missing from the result (callers treat "no entry" as "unknown",
/// not "idle").
func fetchTraffic(interfaceNames: [String], for credentials: RouterOSCredentials) async -> [String: InterfaceTraffic] {
if connectedCredentials != credentials {
await disconnect()
}
if transport == nil {
let newTransport = SSHTransport(credentials: credentials)
guard (try? await newTransport.connect()) != nil else { return [:] }
transport = newTransport
connectedCredentials = credentials
}
guard let transport else { return [:] }
var result: [String: InterfaceTraffic] = [:]
for name in interfaceNames {
if let traffic = try? await transport.fetchInterfaceTraffic(interfaceName: name) {
result[name] = traffic
}
}
return result
}
func disconnect() async {
await transport?.disconnect()
transport = nil
connectedCredentials = nil
}
}