Kompletter Umbau (ROADMAP_UMSETZUNG.md Phase 2). Die alte Fassung hatte vier
Probleme, die den Test praktisch wertlos machten: kein festes Intervall (ein
"Messpunkt" dauerte so lange wie das Ziel zufaellig antwortete — Prod #126:
38 Punkte in 300 s statt der ueblichen ~250-300), keine Zeitreihe (nur
Summen), kein Foreground-Service trotz gegenteiliger Doku-Behauptung, kein
Abbruchpfad. Vor allem aber: waehrend der 1-60 Minuten sah man nichts ausser
"Messung laeuft ...".
Nativ (NetDiagScannerPlugin.kt, MonitorService.kt)
StressRun komplett neu: feste Taktung per naechstem Fixpunkt (nextTick +=
intervalSec*1000, kein Zeitverzug ueber eine Stunde), Rohzaehler
(sentTotal/receivedTotal/rttSum/Min/Max) + echte Zeitreihe (Liste aus
{ts,rtt}), Ausfallsegmente werden waehrend des Laufs erkannt und
geschlossen. Laeuft im MonitorService (Foreground, wie der Geraete-Monitor)
— dafuer wurde der Dienst auf eine Job-Registry umgestellt (jobId -> Text),
damit ein gleichzeitig laufender Geraete-Monitor und Dauertest sich beim
Beenden nicht gegenseitig die Benachrichtigung wegnehmen.
Events stressSample (live, pro Probe) und stressFinished (einmalig, Grund
completed/stopped) + getStressStatus zur Wiederaufnahme nach Seitenwechsel.
WICHTIG geloest: laeuft der Test natuerlich aus, waehrend niemand zuhoert
(App im Hintergrund, andere Seite offen), bleibt der Lauf-Eintrag in
stressRuns bestehen (wie monitorRuns) statt sofort geloescht zu werden —
sonst waere das Ergebnis unwiederbringlich weg. Neuer Aufraeum-Call
dismissStressRun.
Nebenbei: der geteilte io-Scope aller Plugin-Methoden lief bisher auf einem
gewoehnlichen Job — eine unbehandelte Exception in EINER Coroutine (z.B.
IP-Scan mit kaputtem Host) haette strukturell alle Geschwister-Coroutinen
mitgerissen, auch einen parallel laufenden Monitor/Dauertest. Jetzt
SupervisorJob + CoroutineExceptionHandler.
Kritischer Bug gefunden UND gefixt (im Emulator, echter Netzausfall-Test):
`.put("rtt", rtt as Any?)` mit Kotlin null wird von org.json.JSONObject
behandelt wie `remove(key)` — der Schluessel verschwindet komplett statt
als JSON null anzukommen. Die App bekam damit `undefined` statt `null` fuer
verlorene Proben; `!== null`-Filter hielten das faelschlich fuer "erhalten".
Ergebnis: ein per Flugmodus ausgeloester echter Ausfall zeigte live "0 %
Verlust" und "NaN ms" bei Min/Max/Oe. Betraf NUR die Live-Anzeige — die
gespeicherte Messung war unabhaengig davon korrekt, weil buildStressResult()
ausschliesslich aus dem nativen Kotlin-Zustand rechnet, nie ueber die
Pro-Probe-JSON-Bruecke. Fix: ueberall explizit JSONObject.NULL statt bloss
`null` (betraf auch die Traceroute-Korrektur aus Phase 1). Zusaetzlich
TS-seitig als zweite Verteidigungslinie `!= null` statt `!== null` (deckt
sowohl null als auch undefined ab).
Neu: gemeinsame Bewertung (tools/rating.ts)
ping.ts und der alte Dauertest bewerteten denselben Netzzustand
unterschiedlich (ping.ts: avgMs>100 -> Rot; Dauertest: nur lossPct/maxMs).
rateLatency() ist jetzt die einzige Stelle: Verlust, Jitter, Latenz nach
Zielklasse (LAN/WLAN/WAN) UND eine eigene Regel "jeder Ausfall > 5 s = Rot"
unabhaengig vom Gesamt-Verlustprozentsatz. ping.ts umgestellt, unveraendertes
Verhalten fuer die bisherigen Schwellen (inkl. Jitter>10ms, das beim
Portieren fast uebersehen wurde).
Neue Route statt Dialog (routes/protokoll/[id]/stresstest/)
Wie IP-Test/WLAN/Monitor eine eigene Seite statt des blockierenden
Werkzeug-Dialogs. Einrichtung (Ziel leer=Gateway, Dauer, Takt) mit
Vorlaufprobe wie zuvor in stresstest.ts. Laufend: grosse Live-Zahl mit
Ampelfarbe (rateLatency), Fortschrittsbalken mit Restzeit, Streifendiagramm
(StressChart.svelte, live rollierendes 120s-Fenster), Zaehler
gesendet/verloren/Ø/Min/Max, offener-Ausfall-Banner, Ereignisliste mit
Uhrzeit ("Ausfall — wieder da nach 16 s"), Abbrechen. Abgeschlossene Laeufe
darunter aufklappbar mit demselben Diagramm (kompletter Verlauf statt
Fenster). stresstest.ts (Tool-Registry-Eintrag) ist jetzt nur noch
Namens-Lookup fuer die Messungen-Liste, run() wirft bewusst einen Fehler,
falls doch noch etwas den alten Weg aufruft.
Sicherheitsnetz gegen verpasste Events (lib/stresstest.ts)
Ein natuerlich beendeter Test, waehrend niemand auf der Stresstest-Seite
ist, verpasst dort das stressFinished-Event. Die Hauptprotokollseite prueft
deshalb beim Oeffnen zusaetzlich per resumeFinishedStressSessions() nach
(praktisch immer besucht, bevor ein Protokoll abgeschlossen wird) und
traegt das Ergebnis nach. Bekannter Rest-Fall (dokumentiert): verlaesst der
Techniker das Protokoll komplett ohne eine der beiden Seiten noch einmal zu
oeffnen, bleibt der fertige Lauf bis zum naechsten Besuch liegen.
Ergebnis-Speicherung: Zeitreihe in 1-Minuten-Buckets + Ausfallsegmente +
laengster Ausfall als Measurement (tool=stresstest, sync-faehig) — Ausfaelle
werden beim Abschliessen mit dem nativen (autoritativen) Ergebnis abgeglichen,
nicht nur mit der lokal live mitgefuehrten Kopie.
Getestet im Emulator (Pixel 6 / Android 14):
- 1-Minuten-Lauf komplett bei ausgeschaltetem Display durchgelaufen
(Foreground-Service haelt den Prozess), automatischer Abschluss + Toast
- Echter Netzausfall per Flugmodus (15s) waehrend laufendem 5-Minuten-Test:
korrekt 23,2%/16,7% Verlust, Min/Max ohne NaN, Ereignisliste mit exakter
Uhrzeit und Dauer — nach dem NULL-Fix verifiziert
- Abbrechen-Pfad: sofortiger Stopp, korrektes Teil-Ergebnis
- Wiederaufnahme: laufender Test nach APK-Neuinstallation (Prozess-Kill)
korrekt als beendet erkannt
- gradle compileDebugKotlin ok, svelte-check 0 Fehler
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>