From 0fe938a764df7a78d06d0a27c3be2eef8694264d Mon Sep 17 00:00:00 2001 From: Eduard Wisch Date: Sun, 16 Aug 2026 19:27:33 +0200 Subject: [PATCH] Modbus/LPD in der Schnellprobe, Dosen-Bewertung, Loeschen ins Ueberlaufmenue Port-Schnellprobe um 502 (Modbus) und 515 (LPD) erweitert. 502 ist in diesem Betrieb der wichtigste Hinweis ueberhaupt - Wechselrichter, Batteriespeicher und Wallboxen sprechen Modbus/TCP. Die Geraeteart-Heuristik wertet 502 laengst aus ("IoT/SPS"), der Zweig war nur nachweislich unerreichbar, weil die Sonde den Port gar nicht geprueft hat. Dasselbe bei 515 fuer Drucker. Laufzeit vorher/nachher im Emulator gemessen (voller /24): 44,8 s -> 52,0 s, also +16 % bei 20 % mehr Ports. Proportional und vertretbar. 88 und 8443 bewusst NICHT in die Schnellprobe: sie kommen im Handwerksnetz praktisch nicht vor, und dieser Pfad laeuft je stiller Adresse in beiden Runden plus in der Anreicherung. Im Port-Scan auf ein einzelnes Geraet sind sie dagegen jetzt dabei, ebenso 554 (RTSP) und 9100. Servicenamen ergaenzt, damit im Protokoll nicht die nackte Portnummer steht. IP-Test bewertet jetzt die Dose statt nur "IP ja/nein". Das ist der Elektro-Abnahmebeleg schlechthin: 100 Mbit auf einer Cat-7-Leitung heisst so gut wie immer, dass zwei Aderpaare nicht aufgelegt sind oder die Dose falsch geklemmt ist - ein Befund, den der Techniker sofort beheben kann, solange er noch auf der Leiter steht. Vorher stand dort eine gruene Ampel, weil ja eine IP da war. Unbekannte Geschwindigkeit ergibt Status 3 statt 0: bei manchen Android-Versionen ist /sys/class/net//speed gesperrt, und eine Abnahme darf nicht auf einer Angabe beruhen, die es nicht gibt. "Protokoll loeschen" ist ins Ueberlaufmenue der Kopfzeile gewandert. Es stand als roter Link unmittelbar ueber dem gruenen "Abschliessen & synchronisieren" - bei offener Tastatur liegen beide im selben Daumenbereich, und ein Fehlgriff loescht ein noch nicht synchronisiertes Protokoll unwiederbringlich. AppHeader hat dafuer ein optionales Snippet bekommen (die Komponente wird auf zehn Seiten benutzt, das Zahnrad darf durch nichts verdraengt werden). Das Menue meldet sich als Overlay an, sonst haette der Hardware-Zurueck das Protokoll verlassen statt das Menue zu schliessen. Im Emulator geprueft. --- .../netdiag/NetDiagScannerPlugin.kt | 22 ++++++- src/lib/components/AppHeader.svelte | 14 ++++- src/lib/tools/netzwerk/portscan.ts | 13 +++- src/routes/protokoll/[id]/+page.svelte | 61 +++++++++++++++++-- src/routes/protokoll/[id]/iptest/+page.svelte | 56 ++++++++++++++++- 5 files changed, 152 insertions(+), 14 deletions(-) diff --git a/android/app/src/main/java/de/data_it_solution/netdiag/NetDiagScannerPlugin.kt b/android/app/src/main/java/de/data_it_solution/netdiag/NetDiagScannerPlugin.kt index 5458228..11c5f6c 100644 --- a/android/app/src/main/java/de/data_it_solution/netdiag/NetDiagScannerPlugin.kt +++ b/android/app/src/main/java/de/data_it_solution/netdiag/NetDiagScannerPlugin.kt @@ -499,7 +499,21 @@ class NetDiagScannerPlugin : Plugin() { * Geräteart-Heuristik (z.B. 554 → Kamera, 9100 → Drucker). 500 ms Timeout. */ private suspend fun quickPortProbe(ip: String): List { - val probe = listOf(22, 23, 80, 443, 445, 554, 1883, 3389, 8000, 9100) + /* + * 502 (Modbus) und 515 (LPD) ergaenzt. + * + * 502 ist in diesem Betrieb der wichtigste Hinweis ueberhaupt: + * Wechselrichter, Batteriespeicher und Wallboxen sprechen Modbus/TCP. + * Die Geraeteart-Heuristik weiter unten wertet 502 laengst aus ("IoT/SPS") + * — der Zweig war nur nachweislich unerreichbar, weil die Sonde den Port + * gar nicht geprueft hat. Dasselbe gilt fuer 515 (Drucker). + * + * 88 (Kerberos) und 8443 bewusst NICHT hier: sie kommen im Handwerksnetz + * praktisch nicht vor und dieser Pfad ist der teuerste im ganzen Scan + * (er laeuft je stiller Adresse in BEIDEN Runden plus in der + * Anreicherung). Wer sie braucht, nimmt den Port-Scan mit eigener Liste. + */ + val probe = listOf(22, 23, 80, 443, 445, 502, 515, 554, 1883, 3389, 8000, 9100) return withContext(Dispatchers.IO) { probe.map { port -> async { @@ -2461,8 +2475,10 @@ class NetDiagScannerPlugin : Plugin() { private fun serviceName(port: Int): String = when (port) { 21 -> "ftp"; 22 -> "ssh"; 23 -> "telnet"; 53 -> "dns"; 80 -> "http" - 139 -> "netbios"; 443 -> "https"; 445 -> "smb"; 502 -> "modbus" - 1883 -> "mqtt"; 3389 -> "rdp"; 8080 -> "http-alt"; 8443 -> "https-alt" + 88 -> "kerberos"; 139 -> "netbios"; 443 -> "https"; 445 -> "smb" + 502 -> "modbus"; 515 -> "lpd"; 554 -> "rtsp"; 1883 -> "mqtt" + 3389 -> "rdp"; 8000 -> "http-alt"; 8080 -> "http-alt" + 8443 -> "https-alt"; 9100 -> "jetdirect" else -> "" } diff --git a/src/lib/components/AppHeader.svelte b/src/lib/components/AppHeader.svelte index 39a35d6..025b07e 100644 --- a/src/lib/components/AppHeader.svelte +++ b/src/lib/components/AppHeader.svelte @@ -6,7 +6,18 @@ title, back = false, subtitle = '', - }: { title: string; back?: boolean; subtitle?: string } = $props(); + aktionen = undefined, + }: { + title: string; + back?: boolean; + subtitle?: string; + /** + * Zusätzliche Bedienelemente links vom Zahnrad (z.B. ein Überlaufmenü). + * Optional — die Kopfzeile wird auf zehn Seiten benutzt, und das Zahnrad + * darf durch nichts verdrängt werden. + */ + aktionen?: import('svelte').Snippet; + } = $props(); // Zahnrad auf der Einstellungs-Seite selbst ausblenden const onSettings = $derived($page.url.pathname.startsWith('/einstellungen')); @@ -24,6 +35,7 @@

{subtitle}

{/if} + {@render aktionen?.()} {#if !onSettings} (activeTool = null)); }); + // Dasselbe fürs Überlaufmenü — ohne diese Anmeldung würde der Zurück-Knopf + // das Protokoll verlassen, während das Menü noch offen ist. + $effect(() => { + if (!ueberlaufOffen) return; + return pushOverlay(() => (ueberlaufOffen = false)); + }); + /** Lucide-Icon dynamisch holen (Tool-Icon-Name ist kebab-case) */ function icon(name: string) { const pascal = name @@ -440,6 +447,7 @@ title={protocol.label} subtitle={protocol.socName || protocol.orderRef || 'Diagnose'} back + aktionen={ueberlaufMenue} />
@@ -710,11 +718,6 @@ {/if} -
- -
@@ -761,6 +764,54 @@ /> {/if} + + {#snippet ueberlaufMenue()} + + {/snippet} + + {#if ueberlaufOffen} + + + + {/if} + {#if confirmDelete} (null); let links = $state([]); @@ -104,10 +104,59 @@ saveOpen = true; } + /** + * Dose bewerten — nicht nur „IP ja/nein". + * + * Das ist der Elektro-Abnahmebeleg schlechthin: 100 Mbit auf einer + * Cat-7-Leitung heißt so gut wie immer, dass zwei Aderpaare nicht aufgelegt + * sind oder die Dose falsch geklemmt ist. Ein Befund, den der Techniker + * sofort beheben kann, solange er noch auf der Leiter steht — vorher stand + * dort eine grüne Ampel, weil ja eine IP da war. + * + * Unbekannte Geschwindigkeit ergibt Status 3 („nicht messbar"), nicht 0: + * bei manchen Android-Versionen ist /sys/class/net//speed gesperrt. + * Eine Abnahme darf nicht auf einer Angabe beruhen, die es nicht gibt. + */ + function bewerteDose(l: LinkInfo): { status: MeasureStatus; hinweis?: string } { + if (!l.ipv4) { + return { status: 2, hinweis: 'Keine IP-Adresse — Dose liefert kein Netz.' }; + } + const mbit = l.linkSpeedMbps; + if (l.type !== 'ethernet') { + // WLAN: die Aussage über die Dose betrifft nur Kabel + return { status: 0 }; + } + if (mbit == null || mbit <= 0) { + return { + status: 3, + hinweis: + 'Verbindungsgeschwindigkeit konnte auf diesem Gerät nicht ausgelesen ' + + 'werden — über die Leitungsqualität sagt die Messung damit nichts aus.', + }; + } + if (mbit < 100) { + return { + status: 2, + hinweis: `Nur ${mbit} Mbit — Leitung, Dose oder Patchfeld prüfen.`, + }; + } + if (mbit < 1000) { + return { + status: 1, + hinweis: + `Nur ${mbit} Mbit statt 1000. Typische Ursache: zwei der vier ` + + 'Aderpaare sind nicht aufgelegt (Dose oder Patchfeld), seltener eine ' + + 'beschädigte Leitung oder ein Gerät, das nur 100 Mbit kann.', + }; + } + return { status: 0 }; + } + async function doSave(name: string) { saveOpen = false; if (!protocol || !primary) return; const raum = name.trim(); + const bewertung = bewerteDose(primary); addMeasurement(protocol, { tool: 'iptest', category: 'netzwerk', @@ -122,12 +171,13 @@ result: { raum: raum || undefined, ...primary, + ...(bewertung.hinweis ? { hinweis: bewertung.hinweis } : {}), }, - measureStatus: primary.ipv4 ? 0 : 2, + measureStatus: bewertung.status, dateMeasure: Date.now(), }); await persist(); - toast.show('Messung gespeichert', 'success'); + toast.show(bewertung.hinweis ?? 'Messung gespeichert', bewertung.status >= 2 ? 'error' : 'success'); }