/** * Gemeinsame Ampel-Bewertung für Latenz-/Verlustmessungen. * * Vorher bewerteten `ping.ts` und der alte Dauertest denselben Netzzustand * unterschiedlich: `ping.ts` schlug schon bei `avgMs > 100` auf Rot, der * Dauertest schaute nur auf `lossPct`/`maxMs`. Derselbe Zustand konnte damit * einmal Rot, einmal Gelb sein — verwirrend für den Techniker und schlecht * fürs Kundenprotokoll. Diese Funktion ist jetzt die einzige Stelle, die * entscheidet, und wird von beiden Werkzeugen aufgerufen. */ import type { MeasureStatus } from '../types'; /** Grobe Einordnung des Ziels — WAN-Latenzen sind strukturell höher als LAN. */ export type TargetClass = 'lan' | 'wlan' | 'wan'; export interface LatencyRating { /** Verlust in Prozent (Kommazahl) */ lossPct: number; /** Leitwert der Latenz — bevorzugt Median, sonst Mittelwert; null = nicht ermittelt */ latencyMs: number | null; /** längster einzelner Ausfall in Sekunden, falls bekannt (nur Dauertest) */ longestOutageSec?: number | null; /** Schwankung zwischen aufeinanderfolgenden Proben; > 10 ms zählt als Warnung */ jitterMs?: number | null; } const THRESHOLDS: Record = { lan: { warnMs: 20, failMs: 100 }, wlan: { warnMs: 50, failMs: 150 }, wan: { warnMs: 150, failMs: 400 }, }; /** * Bewertet eine Latenz-/Verlustmessung. `targetClass` default 'lan', weil die * meisten Ziele (Gateway, Switch, Gerät im Kundennetz) LAN sind — ein Anrufer * ins Internet (Traceroute-Ziel, WAN-Speedtest) sollte 'wan' übergeben. */ export function rateLatency(r: LatencyRating, targetClass: TargetClass = 'lan'): MeasureStatus { const t = THRESHOLDS[targetClass]; // Ein einzelner Ausfall über 5 s ist für den Kunden spürbar (Videocall bricht, // Kasse hängt) — unabhängig davon, wie klein der Verlust über die Gesamtdauer // gerechnet aussieht. Deshalb eigene, vorrangige Regel. if ((r.longestOutageSec ?? 0) > 5) return 2; if (r.lossPct >= 10) return 2; if (r.latencyMs !== null && r.latencyMs > t.failMs) return 2; let status: MeasureStatus = 0; if (r.lossPct > 0) status = 1; if ((r.jitterMs ?? 0) > 10) status = 1; if (r.latencyMs !== null && r.latencyMs > t.warnMs) status = 1; return status; }