Die Ueberwachung endete bisher mit einem Toast und verschwand — fuer Kunde,
PDF und Server existierte sie nicht (Sitzungen werden nicht synchronisiert).
Damit fehlte ausgerechnet der Nachweis fuer die haeufigste Reklamation
("die Kamera faellt staendig aus"). Genau dafuer wird ueberwacht.
Vor der Auswertung mussten drei Luecken in der Datengrundlage geschlossen
werden. Alle drei haetten das Ergebnis systematisch beschoenigt:
1. Der Startzustand wurde gemessen, aber nicht gemeldet. Ein Geraet, das vom
Anfang bis zum Ende des Termins tot war, hatte NULL Ereignisse — die
Auswertung haette ihm 100 % Verfuegbarkeit ins Kundendokument geschrieben.
Das Plugin erzeugt dafuer jetzt beim Start ein down-Ereignis und setzt
downSince, damit ein spaeteres up eine echte Ausfalldauer bekommt statt 0.
2. Ein beim Beenden noch offener Ausfall wurde nirgends abgeschlossen und
zaehlte gar nicht. stopMonitor liefert jetzt startedAt/endedAt/zustand,
die Auswertung rechnet ihn bis zum Sitzungsende zu.
3. Die Gesamtaussage ist das SCHLECHTESTE Geraet, nicht der Mittelwert — ein
Totalausfall verschwindet sonst zwischen neun gesunden Geraeten.
Ausserdem am Monitor:
- PARTIAL_WAKE_LOCK (12 h Grenze). Der Monitor war der einzige Langzeitpfad
ohne CPU-Sperre; der WifiLock allein haelt nur das Funkmodul wach. Bei
ausgeschaltetem Display streckt Android die Schleife, und es fehlen genau
die Messpunkte, auf denen die Aussage "ueber sechs Stunden stabil" beruht.
- Thread.sleep -> delay(): blockierte den Dispatcher-Thread ueber die gesamte
Wartezeit und nahm ihn parallelen Messungen weg.
- Reisst der Lauf ab (Prozess tot, App beendet), wird trotzdem ein
Teilergebnis gespeichert und als unvollstaendig gekennzeichnet, mit dem
letzten echten Messpunkt als Endzeit. Vorher wurde die Endzeit auf "jetzt"
gesetzt — eine Verfuegbarkeit ueber Stunden auszuweisen, in denen gar nicht
gemessen wurde, waere eine Falschaussage im Abnahmeprotokoll.
- Der Browser-Mock ist jetzt eine echte Zustandsmaschine. Vorher wuerfelte er
je Tick unabhaengig up oder down und erzeugte Folgen wie up,up,down,down,
die es in der Wirklichkeit nicht gibt (ein Ereignis IST ein Wechsel) — eine
Auswertung liess sich damit nicht sinnvoll pruefen.
Dolibarr-Seite gleich mit: die sechs neuen Schluessel in BEIDE Feldtabellen
(netdiagKundenfelder + messfelder.ts). Ohne das stuende im Kunden-PDF nur die
Ampel — der Fehler, der bei der Whitelist schon einmal passiert ist.
Geprueft: neues Werkzeug tools/pruefe-monitor.mjs rechnet sechs konstruierte
Faelle gegen von Hand ermittelte Sollwerte durch, darunter die drei kritischen
(durchgehend totes Geraet -> 0 % statt 100 %, offener Ausfall am Ende,
ein totes unter neun gesunden). Alle korrekt. Danach im Emulator ueber eine
echte Ueberwachung von 59 s: Messung entsteht, alle Felder korrekt beschriftet.
89 lines
3.4 KiB
JavaScript
89 lines
3.4 KiB
JavaScript
/**
|
|
* Prueft die Monitor-Auswertung gegen konstruierte Faelle mit von Hand
|
|
* ausgerechnetem Sollwert. Die Rechnung landet als Verfuegbarkeitsaussage im
|
|
* Kundendokument — sie muss stimmen, nicht plausibel aussehen.
|
|
*/
|
|
import { readFileSync } from 'node:fs';
|
|
|
|
|
|
|
|
const { buildMonitorMeasurement } = await import('/tmp/claude-1000/-mnt-17---Entwicklungen-20---Projekte-NetzwerkDiagnose/c1dc785b-f713-4aae-a06d-1fe09a25cd1a/scratchpad/monitor.mjs');
|
|
|
|
const T0 = new Date('2026-08-16T09:00:00').getTime();
|
|
const min = (m) => T0 + m * 60000;
|
|
|
|
const faelle = [
|
|
{
|
|
name: 'Alles stabil, 60 min, 2 Geraete',
|
|
s: { startedAt: T0, intervalSec: 30,
|
|
targets: [{ip:'192.168.1.10',label:'Router'},{ip:'192.168.1.51',label:'Kamera Hof'}],
|
|
events: [] },
|
|
ende: min(60),
|
|
soll: { verf: 100, aussetzer: 0, status: 0 },
|
|
},
|
|
{
|
|
name: 'Kamera 6 min weg (2 Ausfaelle) in 60 min -> 90 %',
|
|
s: { startedAt: T0, intervalSec: 30,
|
|
targets: [{ip:'192.168.1.51',label:'Kamera Hof'}],
|
|
events: [
|
|
{ip:'192.168.1.51',ts:min(10),type:'down'},
|
|
{ip:'192.168.1.51',ts:min(13),type:'up'},
|
|
{ip:'192.168.1.51',ts:min(40),type:'down'},
|
|
{ip:'192.168.1.51',ts:min(43),type:'up'},
|
|
]},
|
|
ende: min(60),
|
|
soll: { verf: 90, aussetzer: 2, status: 2 }, // 6 von 60 min weg
|
|
},
|
|
{
|
|
name: 'DURCHGEHEND TOT (Start-Ereignis) -> 0 %, nicht 100 %',
|
|
s: { startedAt: T0, intervalSec: 30,
|
|
targets: [{ip:'192.168.1.51',label:'Kamera Hof'}],
|
|
events: [{ip:'192.168.1.51',ts:T0,type:'down'}]},
|
|
ende: min(60),
|
|
soll: { verf: 0, aussetzer: 1, status: 2 },
|
|
},
|
|
{
|
|
name: 'Beim Beenden NOCH OFFENER Ausfall (letzte 15 von 60 min)',
|
|
s: { startedAt: T0, intervalSec: 30,
|
|
targets: [{ip:'192.168.1.51',label:'Kamera Hof'}],
|
|
events: [{ip:'192.168.1.51',ts:min(45),type:'down'}]},
|
|
ende: min(60),
|
|
soll: { verf: 75, aussetzer: 1, status: 2 },
|
|
},
|
|
{
|
|
name: 'Ein totes Geraet unter neun gesunden (schlechtestes zaehlt)',
|
|
s: { startedAt: T0, intervalSec: 30,
|
|
targets: [...Array(9)].map((_,i)=>({ip:`192.168.1.${20+i}`,label:`PC ${i+1}`}))
|
|
.concat([{ip:'192.168.1.51',label:'Kamera Hof'}]),
|
|
events: [{ip:'192.168.1.51',ts:T0,type:'down'}]},
|
|
ende: min(60),
|
|
soll: { verf: 0, aussetzer: 1, status: 2 },
|
|
},
|
|
{
|
|
name: 'Ein kurzer Aussetzer (30 s in 60 min) -> gelb, nicht rot',
|
|
s: { startedAt: T0, intervalSec: 30,
|
|
targets: [{ip:'192.168.1.51',label:'Kamera Hof'}],
|
|
events: [
|
|
{ip:'192.168.1.51',ts:min(20),type:'down'},
|
|
{ip:'192.168.1.51',ts:min(20.5),type:'up'},
|
|
]},
|
|
ende: min(60),
|
|
soll: { verf: 99.2, aussetzer: 1, status: 1 },
|
|
},
|
|
];
|
|
|
|
let fehler = 0;
|
|
for (const f of faelle) {
|
|
const r = buildMonitorMeasurement(f.s, f.ende, false);
|
|
const verf = r.result.verfuegbarkeitProzent;
|
|
const aus = r.result.aussetzer;
|
|
const st = r.measureStatus;
|
|
const ok = Math.abs(verf - f.soll.verf) < 0.15 && aus === f.soll.aussetzer && st === f.soll.status;
|
|
if (!ok) fehler++;
|
|
console.log(`${ok ? 'OK ' : 'FEHL'} ${f.name}`);
|
|
console.log(` verf=${verf} (soll ${f.soll.verf}) aussetzer=${aus} (soll ${f.soll.aussetzer}) status=${st} (soll ${f.soll.status})`);
|
|
console.log(` Label: ${r.label}`);
|
|
if (r.result.geraete.length <= 3) r.result.geraete.forEach(g => console.log(' ', g));
|
|
console.log();
|
|
}
|
|
console.log(fehler === 0 ? 'ALLE FAELLE KORREKT' : `${fehler} FEHLER`);
|