WLAN-Demodaten: Kanalanalyse ohne echten Empfang ansehen
Auf Eddys Vorschlag. Der Android-Emulator hat keine WLAN-Hardware und liefert bei jedem Scan null Netze — Kanalgraph, Gruppierung, Kanalempfehlung und die Warnregeln ließen sich damit auf dem Gerät überhaupt nicht ansehen. Genau deshalb ging die erste Fassung der Diagramme unbemerkt unlesbar in den Einsatz. Schalter in den Einstellungen (Diagnose → WLAN-Demodaten). Ist er an, zeigt die Kanalanalyse eine realistische Beispielumgebung statt echter Messwerte. Die Daten decken bewusst die Grenzfälle ab: Mesh über zwei Bänder, mehrere Netze auf demselben Kanal, ein 40-MHz-Netz im 2,4-GHz-Band und ein DFS- sowie ein 160-MHz-Netz im 5-GHz-Band (lösen beide Warnregeln aus), Netze in allen drei UNII-Blöcken, ein Netz am Bandrand, ein offenes Netz ohne Standard-Angabe. Leichtes Rauschen je Scan, damit mehrere Momentaufnahmen einen Zeitverlauf mit sichtbarem Auf und Ab ergeben. Damit keine Demo je für eine echte Messung gehalten wird: - deutlich sichtbarer Hinweis im Kopf der Seite, solange der Modus an ist - Messungs-Label beginnt mit "DEMODATEN — " - result-JSON trägt demodaten:true; Dolibarr zeigt das in der Kartenansicht und im PDF als eigene Zeile (PDF besonders wichtig, das Dokument geht zum Kunden) Im Emulator durchgespielt: Schalter an -> Kanalanalyse zeigt alle Diagramme mit Netzen, 5-GHz-Band in drei lesbaren UNII-Blöcken, Fazit-Zeile und alle drei Warnhinweise korrekt. Momentaufnahme gespeichert, synchronisiert und in der Test-DB geprüft: Label und result-Flag korrekt als Demo markiert. Zweitnutzen: Vorführung beim Kunden, unabhängig davon was gerade in der Luft ist.
This commit is contained in:
parent
c16badc275
commit
2d35c6b729
3 changed files with 194 additions and 0 deletions
131
src/lib/wifi/demo.ts
Normal file
131
src/lib/wifi/demo.ts
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
/**
|
||||||
|
* WLAN-Demodaten für Vorführung und Prüfung ohne echten Empfang.
|
||||||
|
*
|
||||||
|
* Warum das nötig ist: der Android-Emulator hat keine WLAN-Hardware und
|
||||||
|
* liefert bei jedem Scan null Netze. Kanalgraph, Gruppierung, Kanalempfehlung
|
||||||
|
* und die Warnregeln ließen sich damit auf dem Gerät überhaupt nicht ansehen —
|
||||||
|
* die erste Fassung der Diagramme ging deshalb unbemerkt unlesbar in den
|
||||||
|
* Einsatz. Mit diesem Schalter zeigt die App eine realistische Kundensituation
|
||||||
|
* und die gesamte Anzeigekette lässt sich vor Ort wie im Emulator prüfen.
|
||||||
|
*
|
||||||
|
* Zweiter Nutzen: Vorführung beim Kunden ohne Abhängigkeit davon, was gerade
|
||||||
|
* in der Luft liegt.
|
||||||
|
*
|
||||||
|
* Die Daten sind bewusst so gewählt, dass sie die Grenzfälle abdecken:
|
||||||
|
* - Mesh (dieselbe SSID auf mehreren BSSIDs und Bändern)
|
||||||
|
* - mehrere Netze auf demselben Kanal (Co-Channel)
|
||||||
|
* - ein 40-MHz-Netz im 2,4-GHz-Band (löst Warnregel 1 aus)
|
||||||
|
* - ein DFS-Kanal und ein 160-MHz-Netz im 5-GHz-Band (löst Warnregel 2 aus)
|
||||||
|
* - Netze in allen drei UNII-Blöcken, damit die 5-GHz-Aufteilung sichtbar wird
|
||||||
|
* - ein Netz am Bandrand und ein offenes Netz ohne Standard-Angabe
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Preferences } from '@capacitor/preferences';
|
||||||
|
import type { WifiNetwork } from '../scanner';
|
||||||
|
|
||||||
|
const KEY = 'netdiag_wifi_demo';
|
||||||
|
|
||||||
|
/** BSSID des Netzes, das im Demomodus als „verbunden" gilt */
|
||||||
|
export const DEMO_CONNECTED_BSSID = 'AA:BB:CC:10:20:01';
|
||||||
|
|
||||||
|
export const DEMO_NETWORKS: WifiNetwork[] = [
|
||||||
|
// Eigenes Netz (Mesh über 2,4 + 5 GHz)
|
||||||
|
{
|
||||||
|
ssid: 'FRITZ!Box 7590 SM', bssid: DEMO_CONNECTED_BSSID, channel: 6, frequency: 2437,
|
||||||
|
rssi: -48, band: '2.4 GHz', widthMhz: 20, standard: 'Wi-Fi 5 (802.11ac)', security: 'WPA2', ageMs: 800,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ssid: 'FRITZ!Box 7590 SM', bssid: 'AA:BB:CC:10:20:02', channel: 44, frequency: 5220,
|
||||||
|
rssi: -55, band: '5 GHz', widthMhz: 80, centerFreq0: 5210,
|
||||||
|
standard: 'Wi-Fi 6 (802.11ax)', security: 'WPA3', ageMs: 900,
|
||||||
|
},
|
||||||
|
// Gastnetz auf demselben Kanal wie das Hauptnetz (Co-Channel)
|
||||||
|
{
|
||||||
|
ssid: 'Sandmann-Gast', bssid: 'AA:BB:CC:10:20:03', channel: 6, frequency: 2437,
|
||||||
|
rssi: -52, band: '2.4 GHz', widthMhz: 20, standard: 'Wi-Fi 5 (802.11ac)', security: 'WPA2', ageMs: 1100,
|
||||||
|
},
|
||||||
|
// Repeater (Mesh, zweiter Standort) — 5 GHz auf DFS-Kanal (Warnregel)
|
||||||
|
{
|
||||||
|
ssid: 'Repeater-Halle', bssid: 'AA:BB:CC:10:20:04', channel: 11, frequency: 2462,
|
||||||
|
rssi: -61, band: '2.4 GHz', widthMhz: 20, standard: 'Wi-Fi 4 (802.11n)', security: 'WPA2', ageMs: 2200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ssid: 'Repeater-Halle', bssid: 'AA:BB:CC:10:20:05', channel: 100, frequency: 5500,
|
||||||
|
rssi: -70, band: '5 GHz', widthMhz: 80, centerFreq0: 5530,
|
||||||
|
standard: 'Wi-Fi 5 (802.11ac)', security: 'WPA2', ageMs: 2600,
|
||||||
|
},
|
||||||
|
// Nachbar mit 40 MHz im 2,4-GHz-Band (Warnregel)
|
||||||
|
{
|
||||||
|
ssid: 'Nachbar-WLAN-1', bssid: 'DD:EE:FF:30:40:01', channel: 1, frequency: 2412,
|
||||||
|
rssi: -74, band: '2.4 GHz', widthMhz: 40, centerFreq0: 2422,
|
||||||
|
standard: 'Wi-Fi 4 (802.11n)', security: 'WPA2', ageMs: 3400,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ssid: 'Telekom-2AB3F', bssid: 'DD:EE:FF:30:40:02', channel: 11, frequency: 2462,
|
||||||
|
rssi: -79, band: '2.4 GHz', widthMhz: 20, standard: 'Wi-Fi 4 (802.11n)', security: 'WPA2', ageMs: 4100,
|
||||||
|
},
|
||||||
|
// Offenes Netz am oberen Bandrand, ohne Standard-Angabe
|
||||||
|
{
|
||||||
|
ssid: 'Vodafone-Hotspot', bssid: 'DD:EE:FF:30:40:03', channel: 13, frequency: 2472,
|
||||||
|
rssi: -85, band: '2.4 GHz', widthMhz: 20, security: 'offen', ageMs: 5200,
|
||||||
|
},
|
||||||
|
// UNII-1: schwacher Nachbar + Drucker-Direktverbindung
|
||||||
|
{
|
||||||
|
ssid: 'o2-WLAN-9912', bssid: 'DD:EE:FF:30:40:04', channel: 36, frequency: 5180,
|
||||||
|
rssi: -76, band: '5 GHz', widthMhz: 20, standard: 'Wi-Fi 5 (802.11ac)', security: 'WPA2', ageMs: 3900,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ssid: 'DIRECT-hp-OfficeJet', bssid: 'DD:EE:FF:30:40:05', channel: 48, frequency: 5240,
|
||||||
|
rssi: -81, band: '5 GHz', widthMhz: 20, standard: 'Wi-Fi 5 (802.11ac)', security: 'WPA2', ageMs: 4400,
|
||||||
|
},
|
||||||
|
// UNII-3: Kamera + zweiter Büro-AP mit 160 MHz (Warnregel)
|
||||||
|
{
|
||||||
|
ssid: 'Kamera-Hof-5G', bssid: 'DD:EE:FF:30:40:06', channel: 149, frequency: 5745,
|
||||||
|
rssi: -72, band: '5 GHz', widthMhz: 40, centerFreq0: 5755,
|
||||||
|
standard: 'Wi-Fi 5 (802.11ac)', security: 'WPA2', ageMs: 2900,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ssid: 'Buero-AP-2', bssid: 'DD:EE:FF:30:40:07', channel: 157, frequency: 5785,
|
||||||
|
rssi: -68, band: '5 GHz', widthMhz: 160, centerFreq0: 5775,
|
||||||
|
standard: 'Wi-Fi 6 (802.11ax)', security: 'WPA2', ageMs: 1800,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demodaten mit leichtem Rauschen — so verändert sich das Bild zwischen zwei
|
||||||
|
* Scans wie in der Wirklichkeit, und mehrere Momentaufnahmen ergeben einen
|
||||||
|
* Zeitverlauf mit sichtbarem Auf und Ab statt völlig gerader Linien.
|
||||||
|
*/
|
||||||
|
export function demoNetworks(): WifiNetwork[] {
|
||||||
|
return DEMO_NETWORKS.map((n) => ({
|
||||||
|
...n,
|
||||||
|
rssi: Math.max(-92, Math.min(-30, n.rssi + Math.round((Math.random() - 0.5) * 6))),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Schalter (überlebt den App-Neustart) --- */
|
||||||
|
|
||||||
|
let aktiv = false;
|
||||||
|
|
||||||
|
export function isDemoAktiv(): boolean {
|
||||||
|
return aktiv;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function ladeDemoSchalter(): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const { value } = await Preferences.get({ key: KEY });
|
||||||
|
aktiv = value === '1';
|
||||||
|
} catch {
|
||||||
|
aktiv = false;
|
||||||
|
}
|
||||||
|
return aktiv;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setzeDemoSchalter(an: boolean): Promise<void> {
|
||||||
|
aktiv = an;
|
||||||
|
try {
|
||||||
|
await Preferences.set({ key: KEY, value: an ? '1' : '0' });
|
||||||
|
} catch {
|
||||||
|
/* Schalter gilt dann nur für diese Sitzung */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,11 +7,28 @@
|
||||||
import { sync } from '$lib/sync.svelte';
|
import { sync } from '$lib/sync.svelte';
|
||||||
import { toast } from '$lib/toast.svelte';
|
import { toast } from '$lib/toast.svelte';
|
||||||
import { APP_VERSION, checkForUpdate, installUpdate, type UpdateInfo } from '$lib/updater';
|
import { APP_VERSION, checkForUpdate, installUpdate, type UpdateInfo } from '$lib/updater';
|
||||||
|
import { ladeDemoSchalter, setzeDemoSchalter } from '$lib/wifi/demo';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
let checking = $state(false);
|
let checking = $state(false);
|
||||||
let available = $state<UpdateInfo | null>(null);
|
let available = $state<UpdateInfo | null>(null);
|
||||||
let installing = $state(false);
|
let installing = $state(false);
|
||||||
let percent = $state(0);
|
let percent = $state(0);
|
||||||
|
let wifiDemo = $state(false);
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
wifiDemo = await ladeDemoSchalter();
|
||||||
|
});
|
||||||
|
|
||||||
|
async function toggleWifiDemo(e: Event) {
|
||||||
|
const an = (e.currentTarget as HTMLInputElement).checked;
|
||||||
|
wifiDemo = an;
|
||||||
|
await setzeDemoSchalter(an);
|
||||||
|
toast.show(
|
||||||
|
an ? 'WLAN-Demodaten an — Kanalanalyse zeigt Beispieldaten' : 'WLAN-Demodaten aus',
|
||||||
|
an ? 'info' : 'success',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Sync-Status in Klartext
|
// Sync-Status in Klartext
|
||||||
const syncLabel = $derived(
|
const syncLabel = $derived(
|
||||||
|
|
@ -220,6 +237,26 @@
|
||||||
>
|
>
|
||||||
Debug-Log öffnen
|
Debug-Log öffnen
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<!-- WLAN-Demodaten: der Emulator (und ein Gerät ohne Empfang) liefert
|
||||||
|
keine Netze — damit ließe sich die Kanalanalyse gar nicht ansehen.
|
||||||
|
Auch für die Vorführung beim Kunden brauchbar. -->
|
||||||
|
<label class="mt-3 flex items-start gap-2.5">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="mt-0.5 h-4 w-4 shrink-0 accent-fuchsia-500"
|
||||||
|
checked={wifiDemo}
|
||||||
|
onchange={toggleWifiDemo}
|
||||||
|
/>
|
||||||
|
<span class="text-sm">
|
||||||
|
WLAN-Demodaten
|
||||||
|
<span class="mt-0.5 block text-xs text-zinc-500">
|
||||||
|
Die WLAN-Kanalanalyse zeigt dann eine erfundene Beispielumgebung statt echter
|
||||||
|
Messwerte — zum Ansehen und Vorführen ohne Empfang. Gespeicherte Momentaufnahmen
|
||||||
|
werden deutlich als Demo gekennzeichnet.
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
import { rssiRating } from '$lib/wifi/rating';
|
import { rssiRating } from '$lib/wifi/rating';
|
||||||
import { recommend24GhzChannels, channelWarnings } from '$lib/wifi/recommend';
|
import { recommend24GhzChannels, channelWarnings } from '$lib/wifi/recommend';
|
||||||
import { netColor } from '$lib/wifi/chart';
|
import { netColor } from '$lib/wifi/chart';
|
||||||
|
import { demoNetworks, ladeDemoSchalter, DEMO_CONNECTED_BSSID } from '$lib/wifi/demo';
|
||||||
import { sync } from '$lib/sync.svelte';
|
import { sync } from '$lib/sync.svelte';
|
||||||
import { toast } from '$lib/toast.svelte';
|
import { toast } from '$lib/toast.svelte';
|
||||||
import type { MeasureStatus, Protocol, WifiSurvey } from '$lib/types';
|
import type { MeasureStatus, Protocol, WifiSurvey } from '$lib/types';
|
||||||
|
|
@ -32,6 +33,7 @@
|
||||||
let locationError = $state(false);
|
let locationError = $state(false);
|
||||||
let errorMsg = $state('');
|
let errorMsg = $state('');
|
||||||
let expandedSurvey = $state<string | null>(null);
|
let expandedSurvey = $state<string | null>(null);
|
||||||
|
let demoAktiv = $state(false);
|
||||||
|
|
||||||
const BANDS = ['2.4 GHz', '5 GHz', '6 GHz'] as const;
|
const BANDS = ['2.4 GHz', '5 GHz', '6 GHz'] as const;
|
||||||
|
|
||||||
|
|
@ -88,6 +90,7 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
protocol = p;
|
protocol = p;
|
||||||
|
demoAktiv = await ladeDemoSchalter();
|
||||||
await refresh();
|
await refresh();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -104,6 +107,16 @@
|
||||||
locationError = false;
|
locationError = false;
|
||||||
errorMsg = '';
|
errorMsg = '';
|
||||||
try {
|
try {
|
||||||
|
// Demomodus: der Emulator hat keine WLAN-Hardware und liefert immer 0
|
||||||
|
// Netze — damit ließe sich die gesamte Anzeige nicht prüfen. Der
|
||||||
|
// Schalter steht in den Einstellungen und ist im Kopf der Seite deutlich
|
||||||
|
// sichtbar, damit keine Demomessung versehentlich als echte Messung im
|
||||||
|
// Kundenprotokoll landet.
|
||||||
|
if (demoAktiv) {
|
||||||
|
networks = demoNetworks();
|
||||||
|
connectedBssid = DEMO_CONNECTED_BSSID;
|
||||||
|
return;
|
||||||
|
}
|
||||||
const r = await scanner.wifiScan({ trigger: true });
|
const r = await scanner.wifiScan({ trigger: true });
|
||||||
networks = r.networks;
|
networks = r.networks;
|
||||||
try {
|
try {
|
||||||
|
|
@ -168,11 +181,16 @@
|
||||||
tool: 'wifikanal',
|
tool: 'wifikanal',
|
||||||
category: 'netzwerk',
|
category: 'netzwerk',
|
||||||
label:
|
label:
|
||||||
|
(demoAktiv ? 'DEMODATEN — ' : '') +
|
||||||
`${survey.networks.length} WLAN-Netze` +
|
`${survey.networks.length} WLAN-Netze` +
|
||||||
(eigenStk ? ` · eigenes Netz Kanal ${eigenStk.channel}, ${eigenStk.rssi} dBm` : '') +
|
(eigenStk ? ` · eigenes Netz Kanal ${eigenStk.channel}, ${eigenStk.rssi} dBm` : '') +
|
||||||
(recommendation.length > 0 ? ` · 2,4 GHz frei: Kanal ${recommendation[0].channel}` : ''),
|
(recommendation.length > 0 ? ` · 2,4 GHz frei: Kanal ${recommendation[0].channel}` : ''),
|
||||||
params: {},
|
params: {},
|
||||||
result: {
|
result: {
|
||||||
|
// Muss im Protokoll sichtbar bleiben: eine Demomessung darf nie mit
|
||||||
|
// einer echten Messung verwechselt werden — auch nicht Wochen später
|
||||||
|
// im Kunden-PDF.
|
||||||
|
...(demoAktiv ? { demodaten: true } : {}),
|
||||||
anzahlNetze: survey.networks.length,
|
anzahlNetze: survey.networks.length,
|
||||||
eigenesNetz: eigenStk
|
eigenesNetz: eigenStk
|
||||||
? {
|
? {
|
||||||
|
|
@ -278,6 +296,14 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{#if demoAktiv}
|
||||||
|
<p class="mt-2 rounded-lg bg-fuchsia-900/40 p-2.5 text-xs text-fuchsia-200">
|
||||||
|
<strong>Demodaten aktiv</strong> — die angezeigten Netze sind erfunden, nicht gemessen.
|
||||||
|
Eine hier gespeicherte Momentaufnahme wird als Demo gekennzeichnet. Abschalten unter
|
||||||
|
Einstellungen → WLAN-Demodaten.
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
|
|
||||||
{#if fazit}
|
{#if fazit}
|
||||||
<p class="mt-2 rounded-lg bg-zinc-800 p-2.5 text-sm font-medium text-zinc-100">{fazit}</p>
|
<p class="mt-2 rounded-lg bg-zinc-800 p-2.5 text-sm font-medium text-zinc-100">{fazit}</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue