- Werkzeug-Klarnamen statt interner IDs: "IP-Scanner — 46 Geräte im Netz …" statt "[netzwerk] ipscan — …". Bewusst eine kurze Zuordnung in netdiagToolName() statt eines zweiten Satzes Sprachschlüssel — die kanonischen Namen stehen in der App, doppelte Pflege wäre eine Fehlerquelle. Enthält auch dhcpcheck/wifiscan: in der App gibt es sie nicht mehr, in der PRODUKTIONSDATENBANK stehen dazu aber noch Messungen (4 bzw. 2). Die Gegenprüfung hielt den Punkt für gegenstandslos, hatte dabei aber nur die Testdatenbank angesehen. - Messparameter anzeigen (Prod-Messung #126): unter jeder Messung steht jetzt "Ziel: 192.168.1.1 · Dauer (s): 300" in Karte und PDF. Vorher stand das Ergebnis ohne Bezugspunkt da — man sah nicht, wogegen gemessen wurde. - Listenseite: Ampel je Protokoll (schlechteste Einzelmessung) plus Anzahl, dazu ein Filter "nur mit Befund". Status 3 "nicht messbar" geht bewusst NICHT ins Maximum ein — er ist keine Aussage über das Kundennetz — sondern wird separat als "n.m." ausgewiesen. Im Browser geprüft: der Filter liefert ausschließlich Protokolle mit Warnung oder Fehler. - N+1-Queries behoben: fetchAllByProtocol() las nur die rowids und setzte je Zeile ein eigenes fetch() ab. Jetzt eine Abfrage mit setVarsFromFetchObj(), zusätzlich mit Entity-Filter (fehlte bisher ganz). Nachgemessen über SHOW SESSION STATUS: 46 Geräte + 7 Messungen brauchen statt 55 Abfragen noch eine. - Standort aus der Kundenadresse vorbelegen, wenn der Techniker nichts eingetragen hat; eine vorhandene Angabe wird nie überschrieben. Über die echte API geprüft. Offen bleibt aus Phase 5 nur der Vergleich zweier Protokolle (Geräte-Diff nach MAC) — eigenes Feature mit eigener Ansicht.
511 lines
17 KiB
PHP
511 lines
17 KiB
PHP
<?php
|
|
/* Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/**
|
|
* \file htdocs/custom/netdiag/lib/netdiag.lib.php
|
|
* \ingroup netdiag
|
|
* \brief Hilfsfunktionen für das Modul NetDiag
|
|
*/
|
|
|
|
/**
|
|
* Tabs für die Admin-Seiten des Moduls vorbereiten
|
|
*
|
|
* @return array<int,array<int,string>> Tab-Array
|
|
*/
|
|
function netdiagAdminPrepareHead()
|
|
{
|
|
global $langs, $conf;
|
|
|
|
$langs->load("netdiag@netdiag");
|
|
|
|
$h = 0;
|
|
$head = array();
|
|
|
|
$head[$h][0] = dol_buildpath("/netdiag/admin/setup.php", 1);
|
|
$head[$h][1] = $langs->trans("Settings");
|
|
$head[$h][2] = 'settings';
|
|
$h++;
|
|
|
|
$head[$h][0] = dol_buildpath("/netdiag/admin/about.php", 1);
|
|
$head[$h][1] = $langs->trans("About");
|
|
$head[$h][2] = 'about';
|
|
$h++;
|
|
|
|
complete_head_from_modules($conf, $langs, null, $head, $h, 'netdiag@netdiag');
|
|
complete_head_from_modules($conf, $langs, null, $head, $h, 'netdiag@netdiag', 'remove');
|
|
|
|
return $head;
|
|
}
|
|
|
|
/**
|
|
* Tabs für die Detailansicht eines Diagnose-Protokolls vorbereiten
|
|
*
|
|
* @param NetDiagProtocol $object Protokoll-Objekt
|
|
* @return array<int,array<int,string>> Tab-Array
|
|
*/
|
|
function netdiagProtocolPrepareHead($object)
|
|
{
|
|
global $langs, $conf;
|
|
|
|
$langs->load("netdiag@netdiag");
|
|
|
|
$h = 0;
|
|
$head = array();
|
|
|
|
$head[$h][0] = dol_buildpath("/netdiag/netdiagprotocol_card.php", 1).'?id='.$object->id;
|
|
$head[$h][1] = $langs->trans("NetDiagProtocol");
|
|
$head[$h][2] = 'card';
|
|
$h++;
|
|
|
|
complete_head_from_modules($conf, $langs, $object, $head, $h, 'netdiagprotocol@netdiag');
|
|
complete_head_from_modules($conf, $langs, $object, $head, $h, 'netdiagprotocol@netdiag', 'remove');
|
|
|
|
return $head;
|
|
}
|
|
|
|
/**
|
|
* Ausgabeverzeichnis des Moduls ermitteln (für PDF-Dokumente)
|
|
*
|
|
* @return string Absoluter Pfad zum Dokumentenverzeichnis
|
|
*/
|
|
function netdiagGetOutputDir()
|
|
{
|
|
global $conf;
|
|
if (!empty($conf->netdiag->dir_output)) {
|
|
return $conf->netdiag->dir_output;
|
|
}
|
|
return DOL_DATA_ROOT.'/netdiag';
|
|
}
|
|
|
|
/**
|
|
* Ein Mess-Ergebnis (JSON) lesbar als HTML aufbereiten.
|
|
*
|
|
* Generisch: rendert flache Schlüssel/Wert-Paare und einfache Listen,
|
|
* damit auch künftige Tools ohne Code-Änderung dargestellt werden. Jeder Wert
|
|
* wird auf 200 Zeichen gekürzt (`dol_trunc`) — für Skalare unschädlich, aber
|
|
* ein Dauertest mit vielen Ausfällen/Minuten-Buckets würde dabei mitten im
|
|
* Satz abgeschnitten. Für `$tool === 'stresstest'` deshalb eigener, nicht
|
|
* gekürzter Zweig (siehe netdiagFormatStressTest) — dieselbe Notlösung wie
|
|
* netdiagPdfStressTest() im PDF-Generator.
|
|
*
|
|
* @param string $json JSON-String des Ergebnisses
|
|
* @param string $tool Werkzeug-ID der Messung (optional, für Sonderfälle)
|
|
* @return string HTML-Schnipsel
|
|
*/
|
|
function netdiagFormatResult($json, $tool = '')
|
|
{
|
|
if (empty($json)) {
|
|
return '<span class="opacitymedium">-</span>';
|
|
}
|
|
$data = json_decode($json, true);
|
|
if ($data === null) {
|
|
return dol_escape_htmltag(dol_trunc($json, 120));
|
|
}
|
|
if (!is_array($data)) {
|
|
return dol_escape_htmltag((string) $data);
|
|
}
|
|
if ($tool === 'stresstest') {
|
|
return netdiagFormatStressTest($data);
|
|
}
|
|
if ($tool === 'wifikanal') {
|
|
return netdiagFormatWifiKanal($data);
|
|
}
|
|
|
|
$out = '<div class="netdiag-result">';
|
|
foreach ($data as $key => $val) {
|
|
$label = dol_escape_htmltag(ucfirst((string) $key));
|
|
if (is_array($val)) {
|
|
$flat = array();
|
|
foreach ($val as $item) {
|
|
$flat[] = is_array($item) ? json_encode($item, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : (string) $item;
|
|
}
|
|
$valstr = implode(', ', $flat);
|
|
} elseif (is_bool($val)) {
|
|
$valstr = $val ? 'ja' : 'nein';
|
|
} else {
|
|
$valstr = (string) $val;
|
|
}
|
|
$out .= '<span class="netdiag-kv"><strong>'.$label.':</strong> '.dol_escape_htmltag(dol_trunc($valstr, 200)).'</span> ';
|
|
}
|
|
$out .= '</div>';
|
|
return $out;
|
|
}
|
|
|
|
/**
|
|
* Dauer-/Stresstest-Ergebnis strukturiert als HTML aufbereiten (Kennzahlen +
|
|
* Ausfallliste, ungekürzt) statt es wie ein generisches Werkzeug zu
|
|
* behandeln — sonst schneidet dol_trunc(...,200) die Ausfallliste eines
|
|
* längeren Laufs mitten im Satz ab.
|
|
*
|
|
* @param array<string,mixed> $data bereits dekodiertes result-JSON
|
|
* @return string HTML-Schnipsel
|
|
*/
|
|
function netdiagFormatStressTest($data)
|
|
{
|
|
if (!empty($data['hinweis']) || !empty($data['fehler'])) {
|
|
return '<span class="opacitymedium">'.dol_escape_htmltag((string) ($data['hinweis'] ?? $data['fehler'])).'</span>';
|
|
}
|
|
|
|
$sum = array();
|
|
if (isset($data['host'])) {
|
|
$sum[] = 'Ziel: '.$data['host'];
|
|
}
|
|
if (isset($data['dauerSekunden'])) {
|
|
$sum[] = 'Dauer: '.round($data['dauerSekunden'] / 60).' min';
|
|
}
|
|
if (isset($data['intervallSek'])) {
|
|
$sum[] = 'Takt: '.$data['intervallSek'].' s';
|
|
}
|
|
if (isset($data['gesendet']) && isset($data['empfangen'])) {
|
|
$sum[] = 'Proben: '.$data['empfangen'].'/'.$data['gesendet'];
|
|
}
|
|
if (isset($data['verlustProzent'])) {
|
|
$sum[] = 'Verlust: '.$data['verlustProzent'].' %';
|
|
}
|
|
if (isset($data['avgMs']) && $data['avgMs'] !== null) {
|
|
$sum[] = 'Ø '.$data['avgMs'].' ms';
|
|
}
|
|
if (isset($data['minMs']) && isset($data['maxMs']) && $data['minMs'] !== null) {
|
|
$sum[] = 'Min/Max '.$data['minMs'].'/'.$data['maxMs'].' ms';
|
|
}
|
|
if (isset($data['p95Ms']) && $data['p95Ms'] !== null) {
|
|
$sum[] = 'p95 '.$data['p95Ms'].' ms';
|
|
}
|
|
if (isset($data['laengsterAusfallSek']) && $data['laengsterAusfallSek'] !== null) {
|
|
$sum[] = 'längster Ausfall '.$data['laengsterAusfallSek'].' s';
|
|
}
|
|
|
|
$out = '<div class="netdiag-result"><div class="netdiag-kv">'.dol_escape_htmltag(implode(' | ', $sum)).'</div>';
|
|
|
|
$ausfaelle = (isset($data['ausfaelle']) && is_array($data['ausfaelle'])) ? $data['ausfaelle'] : array();
|
|
if (empty($ausfaelle)) {
|
|
$out .= '<div class="opacitymedium">Kein Ausfall während des Laufs.</div>';
|
|
} else {
|
|
$out .= '<div><strong>Ausfälle ('.count($ausfaelle).'):</strong></div><ul class="netdiag-outages">';
|
|
foreach ($ausfaelle as $line) {
|
|
$out .= '<li>'.dol_escape_htmltag((string) $line).'</li>';
|
|
}
|
|
$out .= '</ul>';
|
|
}
|
|
$out .= '</div>';
|
|
return $out;
|
|
}
|
|
|
|
/**
|
|
* WLAN-Kanal-Momentaufnahme strukturiert als HTML aufbereiten.
|
|
*
|
|
* Wie beim Dauertest ein eigener Zweig statt der generischen Darstellung:
|
|
* `netze` ist eine Liste von Objekten und würde dort als JSON-Text mit
|
|
* dol_trunc(…,200) mitten im Satz abgeschnitten.
|
|
*
|
|
* @param array<string,mixed> $data bereits dekodiertes result-JSON
|
|
* @return string HTML-Schnipsel
|
|
*/
|
|
function netdiagFormatWifiKanal($data)
|
|
{
|
|
$out = '<div class="netdiag-result">';
|
|
|
|
// Aus dem Demomodus der App: erfundene Netze zum Ansehen/Vorführen. Muss
|
|
// deutlich sichtbar bleiben, sonst wird eine Demo später für eine echte
|
|
// Messung am Kundenstandort gehalten.
|
|
if (!empty($data['demodaten'])) {
|
|
$out .= '<div class="netdiag-kv"><strong style="color:#b45309">'
|
|
.dol_escape_htmltag('DEMODATEN — keine echte Messung').'</strong></div>';
|
|
}
|
|
|
|
$sum = array();
|
|
if (isset($data['anzahlNetze'])) {
|
|
$sum[] = $data['anzahlNetze'].' Netze sichtbar';
|
|
}
|
|
if (!empty($data['eigenesNetz']) && is_array($data['eigenesNetz'])) {
|
|
$e = $data['eigenesNetz'];
|
|
$teil = 'Eigenes Netz: '.($e['ssid'] ?? '?').' — Kanal '.($e['kanal'] ?? '?');
|
|
if (!empty($e['band'])) {
|
|
$teil .= ' ('.$e['band'].')';
|
|
}
|
|
if (isset($e['rssi'])) {
|
|
$teil .= ', '.$e['rssi'].' dBm';
|
|
}
|
|
if (!empty($e['bewertung'])) {
|
|
$teil .= ' — '.$e['bewertung'];
|
|
}
|
|
$sum[] = $teil;
|
|
}
|
|
if (!empty($data['empfehlung24GHz']) && is_array($data['empfehlung24GHz'])) {
|
|
$best = reset($data['empfehlung24GHz']);
|
|
if (is_array($best) && isset($best['kanal'])) {
|
|
$sum[] = 'Störungsärmster 2,4-GHz-Kanal: '.$best['kanal'];
|
|
}
|
|
}
|
|
if (!empty($sum)) {
|
|
$out .= '<div class="netdiag-kv">'.dol_escape_htmltag(implode(' | ', $sum)).'</div>';
|
|
}
|
|
|
|
if (!empty($data['warnungen']) && is_array($data['warnungen'])) {
|
|
$out .= '<div><strong>Hinweise:</strong></div><ul class="netdiag-outages">';
|
|
foreach ($data['warnungen'] as $w) {
|
|
$out .= '<li>'.dol_escape_htmltag((string) $w).'</li>';
|
|
}
|
|
$out .= '</ul>';
|
|
}
|
|
|
|
$netze = (isset($data['netze']) && is_array($data['netze'])) ? $data['netze'] : array();
|
|
if (!empty($netze)) {
|
|
$out .= '<table class="noborder centpercent"><tr class="liste_titre">';
|
|
$out .= '<th>SSID</th><th class="center">Kanal</th><th>Band</th>';
|
|
$out .= '<th class="center">Breite</th><th>Standard</th><th>Sicherheit</th><th class="right">Pegel</th>';
|
|
$out .= '</tr>';
|
|
foreach ($netze as $n) {
|
|
if (!is_array($n)) {
|
|
continue;
|
|
}
|
|
$out .= '<tr class="oddeven">';
|
|
$out .= '<td>'.dol_escape_htmltag((string) ($n['ssid'] ?? '')).'</td>';
|
|
$out .= '<td class="center">'.dol_escape_htmltag((string) ($n['kanal'] ?? '')).'</td>';
|
|
$out .= '<td>'.dol_escape_htmltag((string) ($n['band'] ?? '')).'</td>';
|
|
$out .= '<td class="center">'.(isset($n['breiteMhz']) && $n['breiteMhz'] !== null ? dol_escape_htmltag($n['breiteMhz'].' MHz') : '-').'</td>';
|
|
$out .= '<td>'.dol_escape_htmltag((string) ($n['standard'] ?? '-')).'</td>';
|
|
$out .= '<td>'.dol_escape_htmltag((string) ($n['sicherheit'] ?? '-')).'</td>';
|
|
$out .= '<td class="right">'.dol_escape_htmltag((string) ($n['rssi'] ?? '')).' dBm</td>';
|
|
$out .= '</tr>';
|
|
}
|
|
$out .= '</table>';
|
|
}
|
|
|
|
$out .= '</div>';
|
|
return $out;
|
|
}
|
|
|
|
/**
|
|
* Klartext-Bezeichnung und Einheit für einen Ergebnis-Schlüssel.
|
|
*
|
|
* Vorher wurde der rohe JSON-Schlüssel nur großgeschrieben ausgegeben — das
|
|
* Kundendokument las sich wie ein Datenbank-Auszug: „VerlustProzent: 0 |
|
|
* MinMs: 4.4 | ArpAvailable: nein | MdnsOk: ja". Kennzahlen standen ohne
|
|
* Einheit da („UptimeSek: 8123456"), und interne Messwerkzeug-Details, die den
|
|
* Kunden nichts angehen, standen mitten im Dokument.
|
|
*
|
|
* Bewusst eine WHITELIST: was hier nicht steht, erscheint nicht im
|
|
* Kundendokument. Ein neues Werkzeug muss also einmalig hier eingetragen
|
|
* werden — dafür kann nie versehentlich ein internes Feld durchrutschen.
|
|
* Die Technikeransicht in Dolibarr zeigt weiterhin alle Felder.
|
|
*
|
|
* @return array<string,array{0:string,1:string}> Schlüssel => [Bezeichnung, Einheit]
|
|
*/
|
|
function netdiagKundenfelder()
|
|
{
|
|
return array(
|
|
// gemeinsam
|
|
'host' => array('Ziel', ''),
|
|
'subnet' => array('Netzbereich', ''),
|
|
'count' => array('Gefundene Geräte', ''),
|
|
// Ping / Laufzeit
|
|
'gesendet' => array('Gesendet', 'Pakete'),
|
|
'empfangen' => array('Empfangen', 'Pakete'),
|
|
'verlustProzent' => array('Paketverlust', '%'),
|
|
'minMs' => array('Kürzeste Antwortzeit', 'ms'),
|
|
'avgMs' => array('Mittlere Antwortzeit', 'ms'),
|
|
'medianMs' => array('Typische Antwortzeit', 'ms'),
|
|
'maxMs' => array('Längste Antwortzeit', 'ms'),
|
|
'p95Ms' => array('Antwortzeit (95 %)', 'ms'),
|
|
'jitterMs' => array('Schwankung', 'ms'),
|
|
'verfahren' => array('Messverfahren', ''),
|
|
// Durchsatz
|
|
'downMbps' => array('Download', 'Mbit/s'),
|
|
'upMbps' => array('Upload', 'Mbit/s'),
|
|
'mbitProSekunde' => array('Durchsatz', 'Mbit/s'),
|
|
// Dauertest
|
|
'dauerSekunden' => array('Messdauer', 's'),
|
|
'intervallSek' => array('Messabstand', 's'),
|
|
'laengsterAusfallSek' => array('Längster Ausfall', 's'),
|
|
// Netz/DHCP
|
|
'server' => array('DHCP-Server', ''),
|
|
'lease' => array('Lease-Dauer', 's'),
|
|
'gateway' => array('Gateway', ''),
|
|
'dns' => array('DNS-Server', ''),
|
|
// SNMP / Switch
|
|
'sysDescr' => array('Gerätebeschreibung', ''),
|
|
'uptimeSek' => array('Betriebszeit', 's'),
|
|
// Traceroute
|
|
'reachedTarget' => array('Ziel erreicht', ''),
|
|
// WLAN
|
|
'anzahlNetze' => array('Sichtbare WLAN-Netze', ''),
|
|
// Fehler/Hinweise — immer zeigen, sonst bliebe eine gelbe oder rote Ampel
|
|
// im Kundendokument ohne jede Erklärung stehen. Der Text läuft durch
|
|
// netdiagKundentext() und wird dort von Messgeräte-Interna befreit.
|
|
'fehler' => array('Fehler', ''),
|
|
'hinweis' => array('Hinweis', ''),
|
|
'kundenhinweis' => array('Hinweis', ''),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Sekundenwert lesbar machen (aus „8123456" wird „94 Tage 0 Std").
|
|
*
|
|
* @param int|float $sek Sekunden
|
|
* @return string lesbare Dauer
|
|
*/
|
|
function netdiagDauerLesbar($sek)
|
|
{
|
|
$sek = (int) $sek;
|
|
if ($sek < 60) {
|
|
return $sek.' s';
|
|
}
|
|
if ($sek < 3600) {
|
|
return round($sek / 60).' min';
|
|
}
|
|
if ($sek < 86400) {
|
|
return floor($sek / 3600).' Std '.round(($sek % 3600) / 60).' min';
|
|
}
|
|
return floor($sek / 86400).' Tage '.round(($sek % 86400) / 3600).' Std';
|
|
}
|
|
|
|
/**
|
|
* Ein Ergebnisfeld für das Kundendokument aufbereiten.
|
|
*
|
|
* @param string $key Schlüssel aus dem Ergebnis-JSON
|
|
* @param mixed $val Wert
|
|
* @return string|null „Bezeichnung: Wert Einheit" oder null, wenn das Feld
|
|
* nicht ins Kundendokument gehört
|
|
*/
|
|
function netdiagFeldFuerKunde($key, $val)
|
|
{
|
|
$felder = netdiagKundenfelder();
|
|
if (!isset($felder[$key])) {
|
|
return null; // internes Feld — bewusst nicht im Kundendokument
|
|
}
|
|
list($label, $einheit) = $felder[$key];
|
|
|
|
if (is_array($val)) {
|
|
$flat = array();
|
|
foreach ($val as $item) {
|
|
$flat[] = is_array($item) ? json_encode($item, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : (string) $item;
|
|
}
|
|
$val = implode(', ', $flat);
|
|
} elseif (is_bool($val)) {
|
|
$val = $val ? 'ja' : 'nein';
|
|
} elseif ($val === null || $val === '') {
|
|
return null; // nicht ermittelt — lieber weglassen als „: " drucken
|
|
}
|
|
|
|
// Sekundenwerte lesbar machen statt sechsstellige Zahlen zu drucken
|
|
if ($einheit === 's' && is_numeric($val) && $val >= 3600) {
|
|
return $label.': '.netdiagDauerLesbar($val);
|
|
}
|
|
$text = netdiagKundentext((string) $val);
|
|
return $label.': '.$text.($einheit !== '' ? ' '.$einheit : '');
|
|
}
|
|
|
|
/**
|
|
* Interne Formulierungen aus Alt-Datensätzen für das Kundendokument
|
|
* entschärfen.
|
|
*
|
|
* Beispiel aus einem echten Protokoll: „ARP-Tabelle nicht lesbar
|
|
* (/proc/net/arp) — braucht Root". Das ist eine Aussage über das Messgerät,
|
|
* nicht über das Kundennetz, und gehört nicht ins Abnahmedokument. Neue
|
|
* Messungen liefern gleich einen Kundentext; für die bereits gespeicherten
|
|
* Ergebnisse hilft nur ein Ersetzen beim Drucken.
|
|
*
|
|
* @param string $text Rohtext aus dem Ergebnis
|
|
* @return string kundentauglicher Text
|
|
*/
|
|
function netdiagKundentext($text)
|
|
{
|
|
if (stripos($text, '/proc/net/arp') !== false || stripos($text, 'braucht root') !== false) {
|
|
return 'Mit dem eingesetzten Messgerät nicht prüfbar — die Aussage bleibt offen.';
|
|
}
|
|
return $text;
|
|
}
|
|
|
|
/**
|
|
* Klarname eines Werkzeugs für die Anzeige.
|
|
*
|
|
* Die kanonischen Namen stehen eigentlich in der App (`Tool.name`), das
|
|
* Dolibarr-Modul kann darauf aber nicht zugreifen. Deshalb hier eine bewusst
|
|
* kurze Zuordnung statt eines zweiten Satzes Sprachschlüssel — mit Rückfall
|
|
* auf die ID, damit ein neues Werkzeug nie einen leeren Titel bekommt.
|
|
*
|
|
* Enthält auch Werkzeuge, die es in der App nicht mehr gibt („dhcpcheck",
|
|
* „wifiscan"): in der Produktionsdatenbank stehen dazu noch Messungen aus
|
|
* älteren Protokollen, und die sollen weiterhin lesbar sein.
|
|
*
|
|
* @param string $tool Werkzeug-ID aus der Messung
|
|
* @return string Klarname, oder die ID selbst wenn unbekannt
|
|
*/
|
|
function netdiagToolName($tool)
|
|
{
|
|
$map = array(
|
|
'ipscan' => 'IP-Scanner',
|
|
'portscan' => 'Port-Scan',
|
|
'ping' => 'Ping / Qualität',
|
|
'ipconflict' => 'IP-Konflikt',
|
|
'snmp' => 'SNMP-Switch',
|
|
'traceroute' => 'Traceroute',
|
|
'stresstest' => 'Dauer-/Stresstest',
|
|
'iperf' => 'Durchsatz-Test',
|
|
'wifikanal' => 'WLAN-Kanalanalyse',
|
|
'monitor' => 'Geräte-Monitor',
|
|
'iptest' => 'IP-Test (Dose)',
|
|
'wifitrack' => 'WLAN-Empfang',
|
|
// nicht mehr vorhandene Werkzeuge, aber in Altprotokollen enthalten
|
|
'dhcpcheck' => 'DHCP-Prüfung',
|
|
'wifiscan' => 'WLAN-Scan',
|
|
);
|
|
return $map[$tool] ?? $tool;
|
|
}
|
|
|
|
/**
|
|
* Messparameter für die Anzeige aufbereiten („Ziel: 192.168.1.1").
|
|
*
|
|
* Ohne sie steht im Protokoll zwar das Ergebnis, aber nicht, worauf es sich
|
|
* bezieht — bei Prod-Messung #126 war deshalb nicht erkennbar, gegen welches
|
|
* Ziel der Dauertest lief. Nur ausgefüllte Parameter erscheinen.
|
|
*
|
|
* @param string $json JSON-String der Parameter
|
|
* @return string lesbarer Text, leer wenn nichts anzuzeigen ist
|
|
*/
|
|
function netdiagFormatParams($json)
|
|
{
|
|
if (empty($json)) {
|
|
return '';
|
|
}
|
|
$data = json_decode($json, true);
|
|
if (!is_array($data) || empty($data)) {
|
|
return '';
|
|
}
|
|
$labels = array(
|
|
'host' => 'Ziel',
|
|
'subnet' => 'Netzbereich',
|
|
'ip' => 'Gerät',
|
|
'ports' => 'Ports',
|
|
'port' => 'Port',
|
|
'count' => 'Anzahl Proben',
|
|
'durationSec' => 'Dauer (s)',
|
|
'intervalSec' => 'Messabstand (s)',
|
|
'community' => 'SNMP-Community',
|
|
'rounds' => 'Runden',
|
|
'timeoutMs' => 'Zeitgrenze (ms)',
|
|
);
|
|
$teile = array();
|
|
foreach ($data as $k => $v) {
|
|
if ($v === null || $v === '' || is_array($v)) {
|
|
continue;
|
|
}
|
|
$teile[] = ($labels[$k] ?? ucfirst((string) $k)).': '.$v;
|
|
}
|
|
return implode(' · ', $teile);
|
|
}
|