Commit graph

11 commits

Author SHA1 Message Date
34589f12d1 Phase 2: Dauertest komplett neu — Live-Ansicht, fester Takt, Foreground-Service
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>
2026-08-15 00:39:36 +02:00
240d65d534 Phase 1: Messverfahren liefern keine falschen Aussagen mehr
Vier Werkzeuge haben nachweislich Unsinn ins Kundenprotokoll geschrieben. Belege
sind echte Prod-Messungen; jeder Fix wurde im Emulator gegen den passenden
Fehlerfall nachgestellt.

SNMP (Snmp.kt, snmp.ts)
  Snmp.get lieferte bei JEDEM Problem null -> "-" -> parseInt("-")||0 -> 0.
  Ein Switch, der gar nicht antwortet, stand damit als "0 Fehler" in Gruen im
  Kunden-PDF (Prod #95/#96). Neu: Ergebnistyp Value/Timeout/Failure statt
  String?, Request-ID- und error-status-Pruefung, v2c-Ausnahmen (0x80/0x81/0x82)
  erkannt, ein Wiederholversuch bei Timeout. Das Werkzeug meldet jetzt
  "nicht messbar" statt Zahlen zu erfinden.
  Test: SNMP gegen einen Host ohne Agent -> status 3 "keine SNMP-Antwort".

Traceroute (pingWithTtl, traceroute.ts)
  Der Fallback-Regex \((\d+\.\d+\.\d+\.\d+)\) traf IMMER zuerst die ping-Kopfzeile
  "PING 8.8.8.8 (8.8.8.8) ...". Ein stummer Hop wurde dadurch als Ziel ausgegeben
  und der Trace beendet — daher "2 Hops bis 8.8.8.8, beide 0 ms" (Prod #13).
  Zwischen-Hops liefern zudem nie ein time=, die Zeit war strukturell 0.
  Neu: Kopfzeile verworfen, zeilenweise geparst, Reverse-DNS-Antworten erkannt,
  RTT selbst per nanoTime gestoppt, 3 Proben je TTL, waitFor(3s)+destroyForcibly,
  "keine Antwort" ist null und nicht 0, Bewertung haengt an "Ziel erreicht".
  Test: 8.8.8.8 -> 1 Hop mit echten 13,3 ms (statt 0 ms);
        192.0.2.1 -> status 3, alle Hops "* (keine Antwort)" statt gruen.

Ping (measurePing, ping.ts)
  Kein WifiLock, keine Aufwaermproben: zwischen den Proben schlief das Funkmodul
  ein, die Aufwachzeit landete in der Messung — 296 ms zum LAN-Gateway bei 0 %
  Verlust und rotem Status (Prod #4). WAKE_LOCK fehlte im Manifest, weshalb
  WifiLock.acquire() eine SecurityException warf, die ein leerer catch schluckte.
  Neu: WAKE_LOCK im Manifest, WifiLock (LOW_LATENCY) + PARTIAL_WAKE_LOCK um jede
  Messung, 2 verworfene Aufwaermproben, Median und Messaufschlag (avg-min)
  zusaetzlich ausgewiesen, Verlust als Kommazahl statt Ganzzahl-Division,
  Bewertung auf den Median statt den Mittelwert, "keine Antwort" != "0 ms".
  Test: 20 Proben -> Median 0,3 ms, Messaufschlag 0,1 ms, Verfahren dokumentiert.

Durchsatz (measureThroughput, iperf.ts)
  Kein soTimeout: nahm die Gegenstelle an, ohne zu senden, blockierte read()
  unbegrenzt und das Werkzeug-Fenster hing dauerhaft auf "Messung laeuft ...".
  Neu: soTimeout 4 s je Phase, harter withTimeout-Guard, Ist-Zeit statt
  angenommener halber Dauer, measured-Flag — ohne uebertragene Bytes gibt es
  kein rotes "0 Mbit/s" mehr, sondern "nicht messbar".
  Test: Gegenstelle nimmt an und schweigt -> Ergebnis nach 30 s statt Dauerhaenger.

Netzerkennung (firstLocalIpv4, getLocalSubnet, hostsInSubnet)
  firstLocalIpv4() gab bei fehlendem Netz hart "192.168.1.1" zurueck; zusammen mit
  dem Praefix-Fallback /24 entstand das Phantom-Netz 192.168.1.0/24, das auch noch
  ins Protokoll geschrieben wurde. Der Fehlerzweig in ipscan.ts konnte deshalb nie
  greifen. Neu: "" statt Phantom-IP, Link-Local uebersprungen, getLocalSubnet
  meldet einen Fehler, DHCP-Fallback nur bei WLAN-Transport (sonst wurde bei
  gestecktem USB-RJ45 die Maske aus dem WLAN genommen), unlesbares CIDR-Praefix
  wird abgelehnt statt still durch /24 ersetzt.
  Test: Flugmodus -> "Kein Netzbereich — WLAN/LAN nicht aktiv?" statt Phantom-Netz.

Stresstest-Ziel (stresstest.ts)
  Vorgabe 192.168.1.1 im Kundennetz 192.168.178.0/24 -> 5 Minuten ins Leere
  gemessen, Ergebnis "100 % Verlust" rot im Protokoll (Prod #126). Neu: kein
  fester Vorgabewert, leer = Gateway des aktiven Adapters, und eine Vorlaufprobe
  bricht ab, bevor minutenlang gegen ein totes Ziel gemessen wird.
  Test: Ziel 192.168.1.1 -> nach 7 s "antwortet nicht — Test nicht gestartet"
        (vorher: 60 s Messung mit rotem 100-%-Ergebnis).

Neue Bewertung "nicht messbar" (MeasureStatus 3)
  Bisher gab es nur ok/warn/fail. Ein Test, der gar nicht durchgefuehrt werden
  konnte, musste sich als eines davon ausgeben — meist als Gruen. Status 3 wird
  neutral grau dargestellt (Messkarte und Geraetekarte) und ist keine Aussage
  ueber das Kundennetz.

Geprueft: gradle compileDebugKotlin ok, svelte-check 0 Fehler (2 vorbestehende
Warnungen), alle sieben Faelle im Emulator (Pixel 6 / Android 14) durchgespielt
und die Ergebnisse in der Datenbank gegengelesen statt am Bildschirm geraten.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 19:16:17 +02:00
3c95ff6b07 Phase 6: IP-Test (Dose prüfen) und WLAN-Empfangstracker [apk]
All checks were successful
Build APK / build-apk (push) Successful in 1m49s
IP-Test: USB-RJ45-Adapter in Netzwerkdose stecken und sofort IP-Adresse,
DHCP-Server, Gateway und Link-Geschwindigkeit (10/100/1000 Mbit) ablesen.
Auto-Refresh alle 2 s, Speichern mit optionalem Raum/Dose-Name ins Protokoll.

WLAN-Empfangstracker: Netz auswählen und beim Durchgehen live RSSI verfolgen.
Hybrid-Modus: 500 ms Polling bei verbundenem Netz (kein Scan-Throttling),
~30 s Scan-Sweep bei Fremd-BSSID. Sessions mit Samples, Min/Max/Avg und
Sparkline-Verlauf werden im Protokoll gespeichert.

Ersetzt DHCP-Info-Tool und WLAN-Scan-Tool (eigene Routen /iptest/ + /wifi/).
Kotlin-Plugin: linkInfo(), startWifiScan(), startWifiTrack/stop/status().

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 10:03:25 +02:00
d2df3ee929 Neues Werkzeug: Geräte-Monitor (Dauerüberwachung) [apk]
All checks were successful
Build APK / build-apk (push) Successful in 1m47s
Für das Kamera-Problem: mehrere Geräte auswählen und ihre Erreichbarkeit
über längere Zeit überwachen — jeder Ausfall wird mit Uhrzeit protokolliert.

- MonitorService: schlanker Vordergrund-Dienst, hält den Prozess am Leben,
  damit die Überwachung bei Display aus / App-Wechsel weiterläuft
- Plugin startMonitor/stopMonitor/getMonitorStatus: pingt die Geräte im
  gewählten Intervall, Wechsel erreichbar↔weg erzeugt ein monitorEvent;
  WifiLock gegen WLAN-Schlaf, Heads-up-Benachrichtigung bei Ausfall
- Monitor-Seite (protokoll/[id]/monitor): Geräte-Mehrfachauswahl,
  Intervallwahl, Live-Ereignisliste, frühere Überwachungen mit Ausfallzahl
- Überwachung läuft beim Verlassen der Seite weiter; Rückkehr nimmt den
  Stand wieder auf (getMonitorStatus)
- Manifest: MonitorService + FOREGROUND_SERVICE_DATA_SYNC, POST_NOTIFICATIONS
- Kachel "Geräte-Monitor" im Werkzeuge-Raster

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 23:12:26 +02:00
9ee9c954b2 Neues Werkzeug: IP-Konflikt-Prüfung
- arpConflictScan (Plugin): pingt das Subnetz über mehrere Runden und liest
  je Runde die ARP-Tabelle; mehrere MACs pro IP = Konflikt. Kein Root nötig
- Erkennt den Fall, dass /proc/net/arp nicht lesbar ist (Android-Limit) und
  meldet das ehrlich, statt fälschlich Entwarnung zu geben
- ipconflict.ts: neues Protokoll-Tool, in der Tool-Registry eingetragen —
  listet betroffene IPs samt der konkurrierenden MAC-Adressen

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 23:00:32 +02:00
1a0f1dc5ca IP-Scan: Geräte deutlich umfassender erkennen
- NetBIOS-Namensabfrage (UDP 137) je Host integriert
- mdnsScan: neue Plugin-Methode, mDNS/Bonjour via NsdManager — findet
  Drucker, Kameras, Chromecast, AirPlay; liefert Namen + Diensttypen
- Quick-Port-Probe (22/80/443/554/9100 …) speist eine deviceType-Heuristik
  (Kamera, Drucker, Router, Switch, NAS, Wallbox, Server …)
- OUI-Vendor-Tabelle von 6 auf ~150 kuratierte Einträge erweitert
- ipscan.ts führt IP-Scan + mDNS pro IP zusammen, mDNS-only-Geräte ergänzt
- neue DeviceCard-Komponente: zeigt Geräteart-Badge, offene Ports,
  mDNS-Dienste, mac, NetBIOS-Name; ersetzt die Inline-Geräteliste
- upsertDevice überschreibt vorhandene Daten nicht mehr mit undefined
  (Favorit/eigener Name bleiben bei magerem Re-Scan erhalten)
- Manifest: CHANGE_WIFI_MULTICAST_STATE für die mDNS-Suche

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 22:42:25 +02:00
53d91d1526 Bugfixes: IP-Scanner, DHCP, Aufträge, Messergebnisse [apk]
All checks were successful
Build APK / build-apk (push) Successful in 1m41s
- IP-Scanner: ConnectivityManager.getLinkProperties statt hartcodiertem /24 –
  erkennt jetzt das echte Subnetz inkl. Prefix-Länge und Gateway
- DHCP: dhcpDiscover durch dhcpInfo ersetzt (liest WifiManager.dhcpInfo,
  kein Root nötig) – zeigt Server, Gateway, Lease-Zeit, DNS
- Aufträge: tms-Feld ergänzt, Order by tms DESC – "zuletzt bearbeitet" zuerst;
  Checkbox-Logik invertiert (Standard: alle Aufträge, Haken = nur aktive)
- MeasurementResult-Komponente: Arrays (WLAN-Netze, Traceroute-Hops) als
  echte Liste statt Komma-String; Skalare kompakt in einer Zeile
- Traceroute: 5 aufeinanderfolgende Timeouts → Abbruch statt endlos warten
- tools/types.ts: MeasureStatus exportiert (behebt 5 Svelte-Check-Fehler)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 22:02:46 +02:00
34356f25ef Updater: APK direkt in App herunterladen und installieren [apk]
All checks were successful
Build APK / build-apk (push) Successful in 1m42s
Ersetzt den Browser-Umweg (window.open) durch einen echten In-App-Installer:
das native Plugin lädt die APK streamend herunter (Fortschritts-Events
updateProgress 0–100 %), prüft die Installationsberechtigung (Android 8+)
und öffnet den Paketinstaller über den vorhandenen FileProvider.

Versionsvergleich jetzt numerisch (YYYYMMDD-HHMM) statt lexikografisch.
Banner ist schließbar; Einstellungsseite zeigt separaten Fortschrittsbalken.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 21:31:42 +02:00
46226f0d78 IP-Scan: echte CIDR-Auswertung statt hardcodiertem /24 [apk]
All checks were successful
Build APK / build-apk (push) Successful in 4m11s
Der Scan lief stur ueber (1..254) einer aus dem Subnetz-String abgeschnittenen
base — die Netzmaske wurde komplett ignoriert. Ein /23, /22 oder /16 wurde
also nur zu einem Viertel/Achtel etc. gescannt.

Jetzt: hostsInSubnet() parst die CIDR (ip/praefix), berechnet Netz- und
Broadcast-Adresse und liefert ALLE Host-IPs des Bereichs. Unterstuetzt /0..32
(praktisch bis /16 = 65534 Hosts), /31+/32 ohne Netz/Broadcast-Sonderfall.

Sammel-Build: enthaelt ausserdem Titelleisten-Fix, App-Icon, Debug-Log,
IP-Scan-Logging.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 19:57:32 +02:00
d389ee924d App-Icon: eigenes NetDiag-Radar-Motiv [apk]
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 19:44:22 +02:00
27581cd080 android/ fest ins Repo aufgenommen — Build wie beim Adressmanager [apk]
All checks were successful
Build APK / build-apk (push) Successful in 5m25s
Der android-Ordner wurde bisher bei JEDEM CI-Lauf neu erzeugt (cap add android)
und Kotlin/MainActivity/Plugin jedes Mal frisch reingepatcht — genau das war die
Ursache der ganzen Kotlin-Build-Fehler.

Jetzt liegt android/ einmal sauber eingerichtet im Repo:
- kotlin-android-Plugin aktiviert (1.9.24, jvmTarget 17)
- NetDiagScannerPlugin.kt + Snmp.kt + MainActivity.kt (registriert das Plugin)
- AndroidManifest mit allen noetigen Permissions
- Signing-Config in app/build.gradle (Werte aus gradle.properties)

build.yml entschlackt: nur noch npm install, vite build, cap sync, gradle.
Build-Artefakte bleiben per .gitignore aussen vor.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 18:09:23 +02:00