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'); }