Phase 0: doppelte Plugin-Quelle aufgeloest, falsche Doku-Zusagen korrigiert
Aufraeumen vor den eigentlichen Korrekturen — ohne Verhaltensaenderung am Code. Doppelte Plugin-Quelle: Die drei Kotlin-Dateien lagen byte-identisch in native-plugin/ UND in android/app/src/main/java/de/data_it_solution/netdiag/. Gebaut wurde immer nur die Kopie unter android/ — eine Aenderung an der falschen Datei sah deshalb aus wie "der Fix wirkt nicht". native-plugin/ enthaelt jetzt nur noch die Anleitung, die auf den echten Ort verweist. Doku sagte drei Dinge, die nicht stimmen: - stresstest.ts behauptete "laeuft nativ als Foreground-Service". Tut es nicht — im Plugin steht an der Stelle nur ein Kommentar, dass man einen ergaenzen sollte. Bei ausgeschaltetem Display friert Android den Lauf ein. - iperf.ts/scanner.ts/README nannten den Durchsatztest "iperf-kompatibel". Er spricht kein iperf3-Protokoll (kein Cookie, kein Parameterblock) und scheitert gegen einen echten iperf3-Server. - ipscan.ts beschrieb sich als "ARP + Ping + Namen". Gefunden wird nur, wer auf Ping antwortet oder sich per mDNS meldet; die ARP-Tabelle wird lediglich nachgeschlagen und ist ab Android 10 meist gar nicht lesbar. Statt der falschen Zusagen stehen dort jetzt die tatsaechlichen Grenzen plus Verweis auf die Phase, die sie behebt. README: DHCP-Check und WLAN-Scan als Werkzeuge entfernt (sind nicht mehr in tools/index.ts registriert, tauchen aber in alten Protokollen noch auf). Geprueft: vite build ok, svelte-check 0 Fehler (2 vorbestehende Warnungen). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
780115c67e
commit
881ca7956f
9 changed files with 80 additions and 1825 deletions
|
|
@ -24,7 +24,7 @@ Vite-Proxy an den Dolibarr-Testserver (`192.168.155.11`, siehe `vite.config.ts`)
|
|||
```bash
|
||||
npm run build
|
||||
npx cap add android # einmalig
|
||||
# native-plugin/ einbinden -> siehe native-plugin/README.md
|
||||
# android/ liegt komplett im Repo — cap sync kopiert nur die Web-Assets
|
||||
npx cap sync android
|
||||
npx cap open android
|
||||
```
|
||||
|
|
@ -45,12 +45,13 @@ src/lib/
|
|||
updater.ts APK-Auto-Update-Prüfung (KB #363)
|
||||
tools/ erweiterbare Tool-Plattform
|
||||
index.ts Registry — neues Tool hier eintragen
|
||||
netzwerk/ IP-Scan, Port, Ping, WLAN, DHCP, SNMP, Traceroute, Stress
|
||||
netzwerk/ IP-Scan, Port, Ping, IP-Konflikt, SNMP, Traceroute, Stress
|
||||
internet/ Durchsatz-Test
|
||||
telefonie/ (folgt: SIP, FreePBX, RTP)
|
||||
src/routes/
|
||||
login/ auftraege/ kunden/ protokoll/[id]/ einstellungen/
|
||||
native-plugin/ Kotlin-Plugin NetDiagScanner (+ Integrationsanleitung)
|
||||
android/ natives Android-Projekt inkl. Kotlin-Plugin (einzige Quelle)
|
||||
native-plugin/ nur noch Anleitung/Hinweise zum Plugin
|
||||
```
|
||||
|
||||
## Neues Tool hinzufügen
|
||||
|
|
|
|||
|
|
@ -1,81 +0,0 @@
|
|||
package de.data_it_solution.netdiag
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import androidx.core.app.NotificationCompat
|
||||
|
||||
/**
|
||||
* Schlanker Vordergrund-Dienst für den Geräte-Monitor.
|
||||
*
|
||||
* Er hält den App-Prozess am Leben, solange die Überwachung läuft — damit
|
||||
* Android die Mess-Schleife bei ausgeschaltetem Display oder App-Wechsel nicht
|
||||
* beendet. Die eigentliche Ping-Logik läuft im NetDiagScannerPlugin; dieser
|
||||
* Dienst zeigt nur die dauerhafte Benachrichtigung.
|
||||
*/
|
||||
class MonitorService : Service() {
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? = null
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
val text = intent?.getStringExtra(EXTRA_TEXT) ?: "Geräte-Überwachung läuft"
|
||||
val notification = buildNotification(text)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
startForeground(NOTIF_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC)
|
||||
} else {
|
||||
startForeground(NOTIF_ID, notification)
|
||||
}
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
private fun buildNotification(text: String): Notification {
|
||||
ensureChannel(this)
|
||||
return NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
.setContentTitle("NetDiag — Geräte-Monitor")
|
||||
.setContentText(text)
|
||||
.setSmallIcon(android.R.drawable.ic_menu_compass)
|
||||
.setOngoing(true)
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.build()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val CHANNEL_ID = "netdiag-monitor"
|
||||
const val NOTIF_ID = 4711
|
||||
const val EXTRA_TEXT = "text"
|
||||
|
||||
/** Benachrichtigungskanal anlegen (idempotent) */
|
||||
fun ensureChannel(ctx: Context) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
||||
val mgr = ctx.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
if (mgr.getNotificationChannel(CHANNEL_ID) == null) {
|
||||
mgr.createNotificationChannel(
|
||||
NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
"Geräte-Monitor",
|
||||
NotificationManager.IMPORTANCE_LOW,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun start(ctx: Context, text: String) {
|
||||
val i = Intent(ctx, MonitorService::class.java).putExtra(EXTRA_TEXT, text)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
ctx.startForegroundService(i)
|
||||
} else {
|
||||
ctx.startService(i)
|
||||
}
|
||||
}
|
||||
|
||||
fun stop(ctx: Context) {
|
||||
ctx.stopService(Intent(ctx, MonitorService::class.java))
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,36 +1,45 @@
|
|||
# Natives Scan-Plugin `NetDiagScanner`
|
||||
|
||||
Der WebView kann keine Raw-Sockets/ICMP/ARP — die eigentliche Netzwerk-Messung
|
||||
läuft in diesem nativen Android-Plugin (Kotlin).
|
||||
läuft in einem nativen Android-Plugin (Kotlin).
|
||||
|
||||
## Dateien
|
||||
## Wo der Code liegt
|
||||
|
||||
- `NetDiagScannerPlugin.kt` — Capacitor-Plugin mit allen Scan-Methoden
|
||||
- `Snmp.kt` — minimaler SNMP-v2c-GET-Client
|
||||
**Einzige Quelle:**
|
||||
|
||||
## Integration (einmalig, nach `npx cap add android`)
|
||||
```
|
||||
android/app/src/main/java/de/data_it_solution/netdiag/
|
||||
├── NetDiagScannerPlugin.kt Capacitor-Plugin mit allen Scan-Methoden
|
||||
├── MonitorService.kt Vordergrund-Dienst des Geräte-Monitors
|
||||
├── Snmp.kt minimaler SNMP-v2c-GET-Client
|
||||
└── MainActivity.kt registriert das Plugin
|
||||
```
|
||||
|
||||
1. **Android-Projekt erzeugen** (falls noch nicht vorhanden):
|
||||
```bash
|
||||
npm install
|
||||
npm run build
|
||||
npx cap add android
|
||||
```
|
||||
> **Bis 08/2026 lagen dieselben Dateien zusätzlich hier im Ordner `native-plugin/`.**
|
||||
> Gebaut wurde aber immer nur die Kopie unter `android/` — eine Änderung an der
|
||||
> falschen Datei sah deshalb aus wie „der Fix wirkt nicht". Die Doppelablage ist
|
||||
> aufgelöst; dieser Ordner enthält nur noch diese Anleitung.
|
||||
|
||||
2. **Plugin-Dateien kopieren** nach:
|
||||
```
|
||||
android/app/src/main/java/de/data_it_solution/netdiag/
|
||||
├── NetDiagScannerPlugin.kt
|
||||
└── Snmp.kt
|
||||
```
|
||||
Das Verzeichnis `android/` liegt vollständig im Repo (Kotlin, Manifest, Gradle,
|
||||
Signing-Config sind dort fest eingerichtet). `npx cap sync android` kopiert nur die
|
||||
Web-Assets hinein und aktualisiert die Plugin-Liste — es überschreibt **keinen**
|
||||
nativen Code.
|
||||
|
||||
3. **Plugin registrieren** in `android/app/src/main/java/de/data_it_solution/netdiag/MainActivity.kt`:
|
||||
## Ein neues Plugin-Kommando ergänzen
|
||||
|
||||
1. Methode in `NetDiagScannerPlugin.kt` mit `@PluginMethod` anlegen.
|
||||
2. Im TS-Wrapper `src/lib/scanner.ts` die Signatur ergänzen (inkl. Browser-Mock,
|
||||
sonst lässt sich die Seite im Dev-Server nicht mehr bedienen).
|
||||
3. Werkzeug unter `src/lib/tools/<kategorie>/` anlegen und in `tools/index.ts` eintragen.
|
||||
|
||||
## Erstmalige Einrichtung (nur relevant, wenn `android/` neu erzeugt wird)
|
||||
|
||||
Normalerweise **nicht nötig** — `android/` ist eingecheckt. Falls doch:
|
||||
|
||||
1. `npm install && npx vite build && npx cap add android`
|
||||
2. Kotlin-Dateien nach `android/app/src/main/java/de/data_it_solution/netdiag/` legen
|
||||
3. Plugin in `MainActivity.kt` registrieren:
|
||||
```kotlin
|
||||
package de.data_it_solution.netdiag
|
||||
|
||||
import android.os.Bundle
|
||||
import com.getcapacitor.BridgeActivity
|
||||
|
||||
class MainActivity : BridgeActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
registerPlugin(NetDiagScannerPlugin::class.java)
|
||||
|
|
@ -38,35 +47,37 @@ läuft in diesem nativen Android-Plugin (Kotlin).
|
|||
}
|
||||
}
|
||||
```
|
||||
|
||||
4. **Berechtigungen** in `android/app/src/main/AndroidManifest.xml` (innerhalb `<manifest>`):
|
||||
4. **`kotlin-android` im Gradle-Build aktivieren** — sonst werden die `.kt`-Dateien
|
||||
stillschweigend ignoriert, der Build ist grün und das Plugin fehlt im APK
|
||||
(„plugin is not implemented on android", siehe KB #660).
|
||||
5. Berechtigungen im `AndroidManifest.xml` (Stand heute):
|
||||
```xml
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
```
|
||||
`ACCESS_FINE_LOCATION` ist Pflicht, damit Android WLAN-Scan-Ergebnisse liefert.
|
||||
|
||||
5. **Kotlin-Coroutines** sicherstellen — in `android/app/build.gradle`:
|
||||
```gradle
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0"
|
||||
}
|
||||
```
|
||||
|
||||
## Bekannte Einschränkungen
|
||||
|
||||
- **ARP/MAC**: `/proc/net/arp` ist ab Android 10 teils nicht mehr lesbar — dann
|
||||
bleiben MAC/Hersteller leer; Geräte werden trotzdem über Ping/Hostname erkannt.
|
||||
- **Hersteller-Lookup**: nur kleiner OUI-Auszug eingebaut. Für volle Abdeckung
|
||||
die IEEE-OUI-Datei als Asset einbinden und in `ouiVendor()` nutzen.
|
||||
- **DHCP-Discover**: Best-effort — Port 68 kann vom System-DHCP-Client belegt sein.
|
||||
- **Durchsatz-Test**: benötigt eine TCP-Sink/Source-Gegenstelle (2. Gerät bzw.
|
||||
iperf3-kompatibler Server). Misst sonst nur Fehlversuche.
|
||||
- **Stresstest**: für lange Läufe sollte ein Foreground-Service ergänzt werden,
|
||||
sonst kann Android den Prozess im Hintergrund beenden.
|
||||
- **ARP/MAC**: `/proc/net/arp` ist ab Android 10 in der Regel nicht mehr lesbar —
|
||||
dann bleiben MAC und Hersteller leer. Das ist der Grund, warum in den bisherigen
|
||||
Kunden-PDFs die Spalten MAC/Hersteller leer sind.
|
||||
- **Hersteller-Lookup**: nur kleiner OUI-Auszug eingebaut. Für volle Abdeckung die
|
||||
IEEE-OUI-Datei als Asset einbinden und in `ouiVendor()` nutzen.
|
||||
- **Durchsatz-Test**: spricht **kein** iperf3-Protokoll, sondern schreibt/liest roh
|
||||
auf einen TCP-Socket. Gegen einen echten iperf3-Server schlägt er fehl; er braucht
|
||||
eine schlichte TCP-Sink/Source-Gegenstelle. Umbau steht in
|
||||
`../../ROADMAP_UMSETZUNG.md` (Phase 1).
|
||||
- **Stresstest**: läuft **nicht** in einem Foreground-Service — bei ausgeschaltetem
|
||||
Display friert Android den Lauf ein. Umbau in Phase 2.
|
||||
- **IP-Konflikt-Prüfung**: hängt an der ARP-Tabelle und ist damit auf aktuellen
|
||||
Android-Versionen praktisch nicht durchführbar.
|
||||
- **PoE-/Strommessung**: bewusst nicht enthalten (Hardware-Grenze) — späteres Modul.
|
||||
|
|
|
|||
|
|
@ -1,138 +0,0 @@
|
|||
package de.data_it_solution.netdiag
|
||||
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.net.DatagramPacket
|
||||
import java.net.DatagramSocket
|
||||
import java.net.InetAddress
|
||||
|
||||
/**
|
||||
* Minimaler SNMP-v2c-GET-Client.
|
||||
*
|
||||
* Reicht für das Auslesen einzelner OIDs (Link-Speed, Fehlerzähler) von
|
||||
* gemanagten Switches. Implementiert nur so viel BER-Kodierung wie nötig.
|
||||
*/
|
||||
object Snmp {
|
||||
|
||||
/**
|
||||
* Eine OID per SNMP v2c GET abfragen.
|
||||
*
|
||||
* @return Wert als String oder null bei Fehler/Timeout
|
||||
*/
|
||||
fun get(host: String, community: String, oid: String): String? {
|
||||
return try {
|
||||
val request = buildGetRequest(community, oid)
|
||||
val socket = DatagramSocket()
|
||||
socket.soTimeout = 2500
|
||||
socket.use {
|
||||
it.send(DatagramPacket(request, request.size, InetAddress.getByName(host), 161))
|
||||
val buf = ByteArray(2048)
|
||||
val resp = DatagramPacket(buf, buf.size)
|
||||
it.receive(resp)
|
||||
parseFirstValue(buf, resp.length)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- BER-Kodierung ---- */
|
||||
|
||||
private fun tlv(tag: Int, value: ByteArray): ByteArray {
|
||||
val out = ByteArrayOutputStream()
|
||||
out.write(tag)
|
||||
when {
|
||||
value.size < 0x80 -> out.write(value.size)
|
||||
value.size < 0x100 -> { out.write(0x81); out.write(value.size) }
|
||||
else -> { out.write(0x82); out.write(value.size shr 8); out.write(value.size and 0xFF) }
|
||||
}
|
||||
out.write(value)
|
||||
return out.toByteArray()
|
||||
}
|
||||
|
||||
private fun integer(v: Int): ByteArray {
|
||||
val bytes = when {
|
||||
v == 0 -> byteArrayOf(0)
|
||||
v < 0x80 -> byteArrayOf(v.toByte())
|
||||
v < 0x8000 -> byteArrayOf((v shr 8).toByte(), v.toByte())
|
||||
else -> byteArrayOf((v shr 24).toByte(), (v shr 16).toByte(), (v shr 8).toByte(), v.toByte())
|
||||
}
|
||||
return tlv(0x02, bytes)
|
||||
}
|
||||
|
||||
private fun octetString(s: String): ByteArray = tlv(0x04, s.toByteArray())
|
||||
|
||||
private fun oid(o: String): ByteArray {
|
||||
val parts = o.trim().trimStart('.').split('.').map { it.toInt() }
|
||||
val out = ByteArrayOutputStream()
|
||||
out.write(parts[0] * 40 + parts[1]) // erste zwei Subidentifier zusammengefasst
|
||||
for (i in 2 until parts.size) {
|
||||
var v = parts[i]
|
||||
if (v < 0x80) {
|
||||
out.write(v)
|
||||
} else {
|
||||
val stack = ArrayDeque<Int>()
|
||||
stack.addFirst(v and 0x7F)
|
||||
v = v shr 7
|
||||
while (v > 0) { stack.addFirst((v and 0x7F) or 0x80); v = v shr 7 }
|
||||
stack.forEach { out.write(it) }
|
||||
}
|
||||
}
|
||||
return tlv(0x06, out.toByteArray())
|
||||
}
|
||||
|
||||
private fun buildGetRequest(community: String, oidStr: String): ByteArray {
|
||||
val varbind = tlv(0x30, oid(oidStr) + tlv(0x05, ByteArray(0))) // OID + NULL
|
||||
val varbindList = tlv(0x30, varbind)
|
||||
val requestId = (System.currentTimeMillis() and 0x7FFF).toInt()
|
||||
val pdu = tlv(
|
||||
0xA0, // GetRequest-PDU
|
||||
integer(requestId) + integer(0) + integer(0) + varbindList,
|
||||
)
|
||||
val message = tlv(
|
||||
0x30,
|
||||
integer(1) + octetString(community) + pdu, // version 1 = SNMPv2c
|
||||
)
|
||||
return message
|
||||
}
|
||||
|
||||
/* ---- Antwort parsen: ersten Variablen-Wert herausziehen ---- */
|
||||
|
||||
private fun parseFirstValue(buf: ByteArray, len: Int): String? {
|
||||
var i = 0
|
||||
// Durch die Struktur navigieren bis zum ersten primitiven Wert nach einer OID
|
||||
var lastWasOid = false
|
||||
while (i < len) {
|
||||
val tag = buf[i].toInt() and 0xFF
|
||||
i++
|
||||
if (i >= len) break
|
||||
var l = buf[i].toInt() and 0xFF
|
||||
i++
|
||||
if (l and 0x80 != 0) {
|
||||
val n = l and 0x7F
|
||||
l = 0
|
||||
for (k in 0 until n) { l = (l shl 8) or (buf[i].toInt() and 0xFF); i++ }
|
||||
}
|
||||
// Konstruierte Typen (SEQUENCE, PDU) aufsteigen
|
||||
if (tag == 0x30 || tag == 0xA2 || tag == 0xA0) continue
|
||||
if (tag == 0x06) { lastWasOid = true; i += l; continue }
|
||||
if (lastWasOid) {
|
||||
return when (tag) {
|
||||
0x02, 0x41, 0x42, 0x43, 0x44, 0x46 -> { // INTEGER, Counter, Gauge, TimeTicks ...
|
||||
var v = 0L
|
||||
for (k in 0 until l) v = (v shl 8) or (buf[i + k].toLong() and 0xFF)
|
||||
v.toString()
|
||||
}
|
||||
0x04 -> String(buf, i, l) // OCTET STRING
|
||||
0x05 -> "" // NULL -> kein Wert
|
||||
else -> {
|
||||
val sb = StringBuilder()
|
||||
for (k in 0 until l) sb.append("%02X".format(buf[i + k]))
|
||||
sb.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
i += l
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
|
@ -126,9 +126,9 @@ export interface NetDiagScannerPlugin {
|
|||
}>;
|
||||
/** Traceroute zu einem Host */
|
||||
traceroute(opts: { host: string }): Promise<{ hops: TracerouteHop[] }>;
|
||||
/** Durchsatztest gegen eine Gegenstelle (iperf-kompatibel) */
|
||||
/** Durchsatztest gegen eine TCP-Sink/Source-Gegenstelle (KEIN iperf3-Protokoll) */
|
||||
throughput(opts: { host: string; port: number; durationSec: number }): Promise<ThroughputResult>;
|
||||
/** Dauer-/Stresstest starten (läuft als Foreground-Service) */
|
||||
/** Dauer-/Stresstest starten (läuft NICHT im Foreground-Service, s. stresstest.ts) */
|
||||
startStressTest(opts: { host: string; durationSec: number }): Promise<{ runId: string }>;
|
||||
/** Laufenden Stresstest beenden und Ergebnis holen */
|
||||
stopStressTest(opts: { runId: string }): Promise<{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
/**
|
||||
* Tool: Durchsatz-Test — misst die Bandbreite gegen eine Gegenstelle.
|
||||
*
|
||||
* Benötigt eine iperf-kompatible Gegenstelle (2. Gerät oder iperf3-Server).
|
||||
* ACHTUNG: Der Test spricht **kein** iperf3-Protokoll. Nativ wird schlicht auf
|
||||
* einen TCP-Socket geschrieben bzw. von ihm gelesen — ohne Cookie, ohne
|
||||
* Parameterblock, ohne Zustandsautomat. Gegen einen echten iperf3-Server bricht
|
||||
* die Verbindung deshalb ab; er braucht eine einfache TCP-Sink/Source-Gegenstelle.
|
||||
* Umbau (eigenes Protokoll oder mitgeliefertes iperf3-Binary) steht in
|
||||
* ROADMAP_UMSETZUNG.md Phase 1.
|
||||
*/
|
||||
|
||||
import { scanner } from '../../scanner';
|
||||
|
|
|
|||
|
|
@ -31,7 +31,11 @@ export const ipScanTool: Tool = {
|
|||
category: 'netzwerk',
|
||||
name: 'IP-Scanner',
|
||||
icon: 'radar',
|
||||
description: 'Sucht alle Geräte im Netzbereich (ARP + Ping + Namen).',
|
||||
// Ehrliche Beschreibung: gefunden wird ein Gerät nur, wenn es auf Ping
|
||||
// antwortet — oder wenn es sich per mDNS meldet. Die ARP-Tabelle wird
|
||||
// lediglich nachgeschlagen, um MAC/Hersteller zu ergänzen, und ist ab
|
||||
// Android 10 meist gar nicht lesbar. Mehrgleisige Suche: Phase 3.
|
||||
description: 'Sucht Geräte, die auf Ping antworten oder sich per mDNS melden.',
|
||||
scope: 'protocol',
|
||||
params: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,14 @@
|
|||
/**
|
||||
* Tool: Dauer-/Stresstest — Langzeitmessung von Verlust und Latenz.
|
||||
*
|
||||
* Läuft nativ als Foreground-Service, damit Android den Lauf nicht beendet.
|
||||
* ACHTUNG, bekannte Schwächen (Umbau in ROADMAP_UMSETZUNG.md Phase 2):
|
||||
* - Der Lauf hängt an einer nackten Coroutine im Plugin, NICHT an einem
|
||||
* Foreground-Service. Bei ausgeschaltetem Display friert Android ihn ein.
|
||||
* - Die native Schleife hat kein festes Intervall — die Zahl der Messpunkte
|
||||
* hängt davon ab, wie schnell das Ziel antwortet (guter Link ~250 Punkte in
|
||||
* 5 min, Totalausfall ~38). Sie ist damit keine Messgröße.
|
||||
* - Es wird keine Zeitreihe gespeichert, nur Summen. „Wann war die Störung?"
|
||||
* lässt sich aus dem Ergebnis nicht beantworten.
|
||||
* Die Dauer wird als Parameter vorgegeben; run() wartet auf das Ergebnis.
|
||||
*/
|
||||
|
||||
|
|
@ -35,7 +42,9 @@ export const stressTestTool: Tool = {
|
|||
const durationSec = Number(ctx.params.duration || 300);
|
||||
|
||||
const { runId } = await scanner.startStressTest({ host, durationSec });
|
||||
// Auf das Ende des Laufs warten (Foreground-Service misst weiter)
|
||||
// Auf das Ende des Laufs warten. Der Timer läuft in der WebView und wird von
|
||||
// Chromium gedrosselt, sobald die Seite in den Hintergrund gerät — das Ergebnis
|
||||
// kann dadurch später kommen als die angegebene Dauer.
|
||||
await new Promise((r) => setTimeout(r, durationSec * 1000));
|
||||
const res = await scanner.stopStressTest({ runId });
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue