Geraete-Aktionen nach Geraetetyp sortieren statt sie zu verstecken
"SNMP-Switch" stand an jeder IP-Kamera und erzeugte dort zuverlaessig eine Messung "nicht messbar" im Kundenprotokoll - eine Zeile, die niemandem hilft und den Abnahmebeleg verwaessert. Bewusst KEIN hartes Filtern: Device.deviceType ist freier Text aus zwei unabhaengigen Quellen (Kotlin guessDeviceType, TS typeFromMdns) und wird aus MAC-Hersteller, mDNS-Diensten und offenen Ports geraten. Ein managed Switch, der als "Linux-Geraet" durchgeht, verloere beim Filtern ausgerechnet SNMP - schlimmer als die ueberfluessige Zeile, die wir loswerden wollen. Unpassende Werkzeuge stehen deshalb nachrangig (kleiner, blasser, mit Titel-Hinweis) hinter den passenden, statt zu verschwinden. Leerer oder unbekannter Typ: alles gleichrangig, nie weniger. Die SNMP-Liste ist absichtlich grosszuegig (Switch, Router, Gateway, NAS, Drucker, Server, USV, Accesspoint, Firewall) - Drucker sind in der Praxis die haeufigste SNMP-Quelle ueberhaupt. tools/pruefe-geraeteaktionen.mjs rechnet 13 Faelle nach und prueft dabei zwei Zusicherungen: die erwartete Einsortierung UND dass kein Werkzeug verschwindet. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c954afc889
commit
7d79358c02
3 changed files with 152 additions and 1 deletions
|
|
@ -13,6 +13,7 @@
|
|||
import AmpelBadge from './AmpelBadge.svelte';
|
||||
import type { Device, Measurement } from '$lib/types';
|
||||
import type { Tool } from '$lib/tools/types';
|
||||
import { teileNachGeraetetyp } from '$lib/tools/geraeteaktionen';
|
||||
|
||||
let {
|
||||
device,
|
||||
|
|
@ -30,6 +31,9 @@
|
|||
onrename?: () => void;
|
||||
} = $props();
|
||||
|
||||
// Werkzeuge nach Gerätetyp sortieren (passend vorn, Rest nachrangig)
|
||||
const aufgeteilt = $derived(teileNachGeraetetyp(tools, device.deviceType));
|
||||
|
||||
// Index = MeasureStatus (0 ok, 1 warn, 2 fail, 3 nicht messbar)
|
||||
const ampel = ['ampel-ok', 'ampel-warn', 'ampel-fail', 'ampel-unmess'];
|
||||
const ampelDot = ['bg-emerald-500', 'bg-amber-400', 'bg-red-500', 'bg-zinc-500'];
|
||||
|
|
@ -132,8 +136,14 @@
|
|||
{/each}
|
||||
|
||||
{#if tools.length && onrun}
|
||||
<!--
|
||||
Nach Gerätetyp getrennt: passende Werkzeuge vorn, unpassende blasser
|
||||
dahinter. Bewusst NICHT ausgeblendet — `deviceType` ist geraten (MAC-
|
||||
Hersteller, mDNS, offene Ports), und ein als „Linux-Gerät" erkannter
|
||||
Switch verlöre sonst ausgerechnet SNMP. Siehe tools/geraeteaktionen.ts.
|
||||
-->
|
||||
<div class="mt-2 flex flex-wrap gap-1.5">
|
||||
{#each tools as tool (tool.id)}
|
||||
{#each aufgeteilt.passend as tool (tool.id)}
|
||||
<button
|
||||
class="rounded bg-zinc-800 px-2 py-1 text-xs text-sky-300 active:bg-zinc-700"
|
||||
onclick={() => onrun?.(tool)}
|
||||
|
|
@ -141,6 +151,15 @@
|
|||
{tool.name}
|
||||
</button>
|
||||
{/each}
|
||||
{#each aufgeteilt.nachrangig as tool (tool.id)}
|
||||
<button
|
||||
class="rounded bg-zinc-900 px-2 py-1 text-xs text-zinc-500 active:bg-zinc-700"
|
||||
title="Passt selten zu diesem Gerätetyp — trotzdem ausführbar"
|
||||
onclick={() => onrun?.(tool)}
|
||||
>
|
||||
{tool.name}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
|||
86
src/lib/tools/geraeteaktionen.ts
Normal file
86
src/lib/tools/geraeteaktionen.ts
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/**
|
||||
* Welche Geräte-Werkzeuge passen zu welchem Gerätetyp — bewusst OHNE
|
||||
* Laufzeit-Import, damit `tools/pruefe-geraeteaktionen.mjs` die Regeln
|
||||
* nachrechnen kann.
|
||||
*
|
||||
* Anlass: „SNMP-Switch" stand an jeder IP-Kamera und erzeugte dort zuverlässig
|
||||
* eine Messung „nicht messbar" im Kundenprotokoll — eine Zeile, die niemandem
|
||||
* hilft und den Abnahmebeleg verwässert.
|
||||
*
|
||||
* WICHTIG — warum hier nichts verschwindet: `Device.deviceType` ist freier Text
|
||||
* aus zwei unabhängigen Quellen (Kotlin `guessDeviceType`, TS `typeFromMdns`)
|
||||
* und rät anhand von MAC-Hersteller, mDNS-Diensten und offenen Ports. Ein
|
||||
* managed Switch, der als „Linux-Gerät" durchgeht, verlöre bei hartem Filtern
|
||||
* ausgerechnet SNMP — das wäre schlimmer als die überflüssige Zeile, die wir
|
||||
* loswerden wollen. Unpassende Werkzeuge werden deshalb **nachrangig**
|
||||
* einsortiert (die Karte zeigt sie kleiner und blasser), nicht entfernt.
|
||||
* Unbekannter oder leerer Typ ⇒ alles gleichrangig, nie weniger.
|
||||
*/
|
||||
|
||||
/** Werkzeuge, die nur bei bestimmten Gerätetypen vorn stehen sollen */
|
||||
const NUR_BEI: Record<string, string[]> = {
|
||||
/*
|
||||
* SNMP beantwortet praktisch nur verwaltbare Netz- und Bürotechnik. Die Liste
|
||||
* ist absichtlich großzügig: lieber ein Werkzeug zu viel vorn als eines, das
|
||||
* der Techniker beim Kunden suchen muss. Drucker gehören dazu — sie sind in
|
||||
* der Praxis die häufigste SNMP-Quelle überhaupt (Tonerstand, Zählerstand).
|
||||
*/
|
||||
snmp: [
|
||||
'switch',
|
||||
'router',
|
||||
'gateway',
|
||||
'nas',
|
||||
'drucker',
|
||||
'printer',
|
||||
'server',
|
||||
'usv',
|
||||
'accesspoint',
|
||||
'access point',
|
||||
'firewall',
|
||||
],
|
||||
};
|
||||
|
||||
/** Gerätetyp auf einen Vergleichswert bringen (Kleinschreibung, ohne Beiwerk) */
|
||||
function normiere(typ: string | undefined | null): string {
|
||||
return (typ ?? '').toLowerCase().trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Passt ein Werkzeug zum Gerätetyp?
|
||||
*
|
||||
* @param toolId ID aus der Werkzeug-Registry
|
||||
* @param deviceType freier Text aus `Device.deviceType` (darf leer sein)
|
||||
* @returns true = gleichrangig anzeigen, false = nachrangig
|
||||
*/
|
||||
export function passtZuGeraet(toolId: string, deviceType: string | undefined | null): boolean {
|
||||
const nurBei = NUR_BEI[toolId];
|
||||
if (!nurBei) {
|
||||
return true; // keine Einschränkung hinterlegt
|
||||
}
|
||||
const typ = normiere(deviceType);
|
||||
if (typ === '') {
|
||||
return true; // unbekannt ⇒ nie weniger zeigen
|
||||
}
|
||||
// Teilstring-Vergleich: die Quellen liefern u.a. „SNMP-Switch", „IP-Kamera",
|
||||
// „Windows-PC" — ein Gleichheitsvergleich ginge an der Wirklichkeit vorbei.
|
||||
return nurBei.some((k) => typ.includes(k));
|
||||
}
|
||||
|
||||
/**
|
||||
* Werkzeugliste in „passend" und „nachrangig" teilen, Reihenfolge bleibt sonst
|
||||
* unverändert.
|
||||
*
|
||||
* @param tools Werkzeuge mit `scope: 'device'`
|
||||
* @param deviceType freier Text aus `Device.deviceType`
|
||||
*/
|
||||
export function teileNachGeraetetyp<T extends { id: string }>(
|
||||
tools: T[],
|
||||
deviceType: string | undefined | null,
|
||||
): { passend: T[]; nachrangig: T[] } {
|
||||
const passend: T[] = [];
|
||||
const nachrangig: T[] = [];
|
||||
for (const t of tools) {
|
||||
(passtZuGeraet(t.id, deviceType) ? passend : nachrangig).push(t);
|
||||
}
|
||||
return { passend, nachrangig };
|
||||
}
|
||||
46
tools/pruefe-geraeteaktionen.mjs
Normal file
46
tools/pruefe-geraeteaktionen.mjs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* Prueft die Zuordnung Geraetetyp -> Werkzeuge.
|
||||
*
|
||||
* Anlass: "SNMP-Switch" stand an jeder IP-Kamera und erzeugte dort eine
|
||||
* Messung "nicht messbar" im Kundenprotokoll. Wichtiger als das Wegsortieren
|
||||
* ist aber, dass NICHTS verschwindet: deviceType ist geraten.
|
||||
*
|
||||
* Start: node tools/pruefe-geraeteaktionen.mjs
|
||||
*/
|
||||
const { passtZuGeraet, teileNachGeraetetyp } =
|
||||
await import('../src/lib/tools/geraeteaktionen.ts');
|
||||
|
||||
const WERKZEUGE = [{ id: 'ping' }, { id: 'portscan' }, { id: 'snmp' }];
|
||||
|
||||
const faelle = [
|
||||
{ typ: 'Kamera', snmpVorn: false, name: 'IP-Kamera -> SNMP nachrangig' },
|
||||
{ typ: 'IP-Kamera', snmpVorn: false, name: 'Schreibweise mit Praefix' },
|
||||
{ typ: 'Switch', snmpVorn: true, name: 'Switch -> SNMP vorn' },
|
||||
{ typ: 'SNMP-Switch', snmpVorn: true, name: 'Teilstring trifft' },
|
||||
{ typ: 'Drucker', snmpVorn: true, name: 'Drucker koennen SNMP (Toner/Zaehler)' },
|
||||
{ typ: 'NAS', snmpVorn: true, name: 'NAS -> SNMP vorn' },
|
||||
{ typ: 'Router', snmpVorn: true, name: 'Router -> SNMP vorn' },
|
||||
{ typ: 'Windows-PC', snmpVorn: false, name: 'PC -> SNMP nachrangig' },
|
||||
{ typ: 'Wallbox', snmpVorn: false, name: 'Wallbox spricht Modbus, nicht SNMP' },
|
||||
{ typ: 'IoT/SPS', snmpVorn: false, name: 'SPS spricht Modbus, nicht SNMP' },
|
||||
{ typ: '', snmpVorn: true, name: 'LEER -> alles gleichrangig (nie weniger)' },
|
||||
{ typ: undefined, snmpVorn: true, name: 'UNBEKANNT -> alles gleichrangig' },
|
||||
{ typ: 'Linux-Gerät', snmpVorn: false, name: 'Linux-Geraet -> nachrangig, aber erreichbar' },
|
||||
];
|
||||
|
||||
let fehler = 0;
|
||||
for (const f of faelle) {
|
||||
const { passend, nachrangig } = teileNachGeraetetyp(WERKZEUGE, f.typ);
|
||||
const snmpVorn = passend.some((t) => t.id === 'snmp');
|
||||
// Zwei harte Zusicherungen: die erwartete Einsortierung UND dass kein
|
||||
// Werkzeug verschwindet.
|
||||
const vollzaehlig = passend.length + nachrangig.length === WERKZEUGE.length;
|
||||
const pingVorn = passend.some((t) => t.id === 'ping');
|
||||
const portVorn = passend.some((t) => t.id === 'portscan');
|
||||
const ok = snmpVorn === f.snmpVorn && vollzaehlig && pingVorn && portVorn;
|
||||
if (!ok) fehler++;
|
||||
console.log(`${ok ? 'OK ' : 'FEHL'} ${f.name}`);
|
||||
console.log(` typ="${f.typ}" vorn=[${passend.map((t) => t.id)}] nachrangig=[${nachrangig.map((t) => t.id)}]`);
|
||||
}
|
||||
console.log(fehler === 0 ? 'ALLE FAELLE KORREKT' : `${fehler} FEHLER`);
|
||||
process.exit(fehler === 0 ? 0 : 1);
|
||||
Loading…
Reference in a new issue