Phase 5 abgeschlossen: Klarnamen, Parameter, Listen-Ampel, N+1, Standort
- 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.
This commit is contained in:
parent
221145736f
commit
d079431cdf
9 changed files with 172 additions and 14 deletions
|
|
@ -144,6 +144,18 @@ $protocol->fk_commande = !empty($p['orderId']) ? (int) $p['orderId'] : nul
|
|||
$protocol->date_diag = netdiag_api_timestamp($p['dateDiag'] ?? 0);
|
||||
$protocol->fk_user_techniker = (int) $user->id;
|
||||
$protocol->standort = isset($p['location']) ? (string) $p['location'] : '';
|
||||
// Standort leer? Dann aus der Kundenadresse vorbelegen. Der Techniker tippt
|
||||
// vor Ort selten etwas ein, im Protokoll stand der Ort deshalb fast immer
|
||||
// leer — obwohl er in Dolibarr am Kunden hinterlegt ist. Eine vom Techniker
|
||||
// eingetragene Angabe wird NIE überschrieben.
|
||||
if (trim($protocol->standort) === '' && !empty($protocol->fk_soc)) {
|
||||
require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
|
||||
$socTmp = new Societe($db);
|
||||
if ($socTmp->fetch((int) $protocol->fk_soc) > 0) {
|
||||
$teile = array_filter(array(trim($socTmp->address), trim($socTmp->zip.' '.$socTmp->town)));
|
||||
$protocol->standort = substr(implode(', ', $teile), 0, 255);
|
||||
}
|
||||
}
|
||||
$protocol->subnet = isset($p['subnet']) ? (string) $p['subnet'] : '';
|
||||
$protocol->status = isset($p['status']) ? (int) $p['status'] : NetDiagProtocol::STATUS_DRAFT;
|
||||
$protocol->note = isset($p['note']) ? (string) $p['note'] : '';
|
||||
|
|
|
|||
|
|
@ -135,18 +135,24 @@ class NetDiagDevice extends CommonObject
|
|||
*/
|
||||
public function fetchAllByProtocol($fk_protocol)
|
||||
{
|
||||
// Alle Spalten in EINER Abfrage holen. Vorher wurden nur die rowids
|
||||
// gelesen und je Zeile ein eigenes fetch() abgesetzt — bei 46 Geräten
|
||||
// also 47 Abfragen statt einer. Die Kartenansicht und der
|
||||
// PDF-Generator laden diese Liste jeweils komplett.
|
||||
$result = array();
|
||||
$sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element;
|
||||
$sql = "SELECT * FROM ".$this->db->prefix().$this->table_element;
|
||||
$sql .= " WHERE fk_protocol = ".((int) $fk_protocol);
|
||||
$sql .= " AND entity IN (".getEntity($this->element).")";
|
||||
$sql .= " ORDER BY INET_ATON(ip), rowid";
|
||||
$resql = $this->db->query($sql);
|
||||
if ($resql) {
|
||||
while ($obj = $this->db->fetch_object($resql)) {
|
||||
$dev = new self($this->db);
|
||||
if ($dev->fetch((int) $obj->rowid) > 0) {
|
||||
$result[] = $dev;
|
||||
}
|
||||
$dev->setVarsFromFetchObj($obj);
|
||||
$dev->id = (int) $obj->rowid;
|
||||
$result[] = $dev;
|
||||
}
|
||||
$this->db->free($resql);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,18 +128,21 @@ class NetDiagMeasurement extends CommonObject
|
|||
*/
|
||||
public function fetchAllByProtocol($fk_protocol)
|
||||
{
|
||||
// Eine Abfrage statt einer je Messung (siehe NetDiagDevice::fetchAllByProtocol)
|
||||
$result = array();
|
||||
$sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element;
|
||||
$sql = "SELECT * FROM ".$this->db->prefix().$this->table_element;
|
||||
$sql .= " WHERE fk_protocol = ".((int) $fk_protocol);
|
||||
$sql .= " AND entity IN (".getEntity($this->element).")";
|
||||
$sql .= " ORDER BY date_measure, rowid";
|
||||
$resql = $this->db->query($sql);
|
||||
if ($resql) {
|
||||
while ($obj = $this->db->fetch_object($resql)) {
|
||||
$m = new self($this->db);
|
||||
if ($m->fetch((int) $obj->rowid) > 0) {
|
||||
$result[] = $m;
|
||||
}
|
||||
$m->setVarsFromFetchObj($obj);
|
||||
$m->id = (int) $obj->rowid;
|
||||
$result[] = $m;
|
||||
}
|
||||
$this->db->free($resql);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,3 +74,4 @@ NetDiagFoundViaPort=Port
|
|||
NetDiagFoundViaArp=ARP
|
||||
NetDiagFoundViaMdns=mDNS
|
||||
NetDiagLastSeen=Zuletzt gesehen
|
||||
NetDiagOnlyWithFindings=nur mit Befund
|
||||
|
|
|
|||
|
|
@ -74,3 +74,4 @@ NetDiagFoundViaPort=Port
|
|||
NetDiagFoundViaArp=ARP
|
||||
NetDiagFoundViaMdns=mDNS
|
||||
NetDiagLastSeen=Last seen
|
||||
NetDiagOnlyWithFindings=only with findings
|
||||
|
|
|
|||
|
|
@ -430,3 +430,82 @@ function netdiagKundentext($text)
|
|||
}
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -181,10 +181,19 @@ function netdiagGeneratePdf($db, $protocol, $outputlangs)
|
|||
// wie ein abgebrochenes Dokument.
|
||||
netdiagPdfEnsureSpace($pdf, 11);
|
||||
$pdf->SetFont('', 'B', 9);
|
||||
$title = ($m->category ? '['.$m->category.'] ' : '').$m->tool.($m->label ? ' — '.$m->label : '');
|
||||
// Klarname statt interner ID — „ipscan" sagt dem Kunden nichts
|
||||
$title = netdiagToolName($m->tool).($m->label ? ' — '.$m->label : '');
|
||||
$pdf->Cell(150, 6, dol_trunc($title, 80), 1, 0, 'L');
|
||||
$pdf->Cell(30, 6, $statusmarks[$st].$statuslabels[$st], 1, 1, 'C', true);
|
||||
$pdf->SetFont('', '', 8);
|
||||
// Messparameter: ohne sie steht das Ergebnis ohne Bezug da — man sieht
|
||||
// nicht, gegen welches Ziel gemessen wurde (Prod-Messung #126).
|
||||
$params = netdiagFormatParams($m->params);
|
||||
if ($params !== '') {
|
||||
$pdf->SetFont('', 'I', 7);
|
||||
$pdf->MultiCell(180, 4, $params, 1, 'L');
|
||||
$pdf->SetFont('', '', 8);
|
||||
}
|
||||
if ($m->tool === 'stresstest') {
|
||||
netdiagPdfStressTest($pdf, $m->result);
|
||||
} elseif ($m->tool === 'wifikanal') {
|
||||
|
|
|
|||
|
|
@ -67,6 +67,10 @@ if (!$user->hasRight('netdiag', 'protocol', 'read')) {
|
|||
|
||||
$search_ref = GETPOST('search_ref', 'alpha');
|
||||
$search_soc = GETPOST('search_soc', 'alpha');
|
||||
// „nur mit Befund": zeigt Protokolle, in denen mindestens eine Messung
|
||||
// Warnung oder Fehler ist — beim Durchsehen vieler Protokolle die eigentliche
|
||||
// Frage („wo muss ich nochmal ran?")
|
||||
$search_problem = GETPOST('search_problem', 'int');
|
||||
$limit = GETPOSTINT('limit') ? GETPOSTINT('limit') : $conf->liste_limit;
|
||||
$page = GETPOSTINT('page');
|
||||
if ($page < 0) {
|
||||
|
|
@ -85,7 +89,16 @@ llxHeader('', $title, '', '', 0, 0, '', '', '', 'mod-netdiag page-list');
|
|||
// Datensätze laden
|
||||
$sql = "SELECT p.rowid, p.ref, p.label, p.date_diag, p.standort, p.subnet, p.status,";
|
||||
$sql .= " s.rowid as socid, s.nom as socname,";
|
||||
$sql .= " (SELECT COUNT(*) FROM ".$db->prefix()."netdiag_device d WHERE d.fk_protocol = p.rowid) as devcount";
|
||||
$sql .= " (SELECT COUNT(*) FROM ".$db->prefix()."netdiag_device d WHERE d.fk_protocol = p.rowid) as devcount,";
|
||||
// Schlechteste Einzelmessung als Protokoll-Ampel. Status 3 („nicht messbar")
|
||||
// darf dabei NICHT als schlechtester Wert gelten — er ist keine Aussage über
|
||||
// das Kundennetz; deshalb wird er separat gezählt statt in MAX() einzugehen.
|
||||
$sql .= " (SELECT MAX(m.measure_status) FROM ".$db->prefix()."netdiag_measurement m
|
||||
WHERE m.fk_protocol = p.rowid AND m.measure_status IN (0,1,2)) as worststatus,";
|
||||
$sql .= " (SELECT COUNT(*) FROM ".$db->prefix()."netdiag_measurement m
|
||||
WHERE m.fk_protocol = p.rowid) as meascount,";
|
||||
$sql .= " (SELECT COUNT(*) FROM ".$db->prefix()."netdiag_measurement m
|
||||
WHERE m.fk_protocol = p.rowid AND m.measure_status = 3) as unmesscount";
|
||||
$sql .= " FROM ".$db->prefix()."netdiag_protocol as p";
|
||||
$sql .= " LEFT JOIN ".$db->prefix()."societe as s ON s.rowid = p.fk_soc";
|
||||
$sql .= " WHERE p.entity IN (".getEntity('netdiagprotocol').")";
|
||||
|
|
@ -95,6 +108,10 @@ if ($search_ref) {
|
|||
if ($search_soc) {
|
||||
$sql .= natural_search('s.nom', $search_soc);
|
||||
}
|
||||
if ($search_problem) {
|
||||
$sql .= " AND EXISTS (SELECT 1 FROM ".$db->prefix()."netdiag_measurement m2
|
||||
WHERE m2.fk_protocol = p.rowid AND m2.measure_status IN (1,2))";
|
||||
}
|
||||
$sql .= " ORDER BY p.date_diag DESC, p.rowid DESC";
|
||||
|
||||
$sql .= $db->plimit($limit, $offset);
|
||||
|
|
@ -116,7 +133,8 @@ print '<table class="tagtable liste">';
|
|||
print '<tr class="liste_titre">';
|
||||
print '<td><input type="text" name="search_ref" class="maxwidth100" value="'.dol_escape_htmltag($search_ref).'" placeholder="'.$langs->trans("Ref").'"></td>';
|
||||
print '<td><input type="text" name="search_soc" class="maxwidth150" value="'.dol_escape_htmltag($search_soc).'" placeholder="'.$langs->trans("ThirdParty").'"></td>';
|
||||
print '<td colspan="4" class="right"><input type="submit" class="button small" value="'.$langs->trans("Search").'"></td>';
|
||||
print '<td colspan="3" class="right"><label class="opacitymedium small"><input type="checkbox" name="search_problem" value="1"'.($search_problem ? ' checked' : '').'> '.$langs->trans("NetDiagOnlyWithFindings").'</label></td>';
|
||||
print '<td class="right"><input type="submit" class="button small" value="'.$langs->trans("Search").'"></td>';
|
||||
print '</tr>';
|
||||
|
||||
print '<tr class="liste_titre">';
|
||||
|
|
@ -125,6 +143,7 @@ print '<th>'.$langs->trans("ThirdParty").'</th>';
|
|||
print '<th>'.$langs->trans("DateDiag").'</th>';
|
||||
print '<th>'.$langs->trans("Location").'</th>';
|
||||
print '<th class="right">'.$langs->trans("NetDiagDevices").'</th>';
|
||||
print '<th class="center">'.$langs->trans("NetDiagMeasurements").'</th>';
|
||||
print '<th class="center">'.$langs->trans("Status").'</th>';
|
||||
print '</tr>';
|
||||
|
||||
|
|
@ -143,11 +162,33 @@ foreach ($rows as $obj) {
|
|||
print '<td>'.dol_print_date($db->jdate($obj->date_diag), 'dayhour').'</td>';
|
||||
print '<td>'.dol_escape_htmltag($obj->standort).'</td>';
|
||||
print '<td class="right">'.((int) $obj->devcount).'</td>';
|
||||
// Messungs-Ampel: schlechteste Einzelmessung, plus Hinweis auf nicht
|
||||
// messbare Punkte. So sieht man in der Liste, wo noch etwas zu tun ist,
|
||||
// ohne jedes Protokoll einzeln zu öffnen.
|
||||
print '<td class="center">';
|
||||
$meascount = (int) $obj->meascount;
|
||||
if ($meascount === 0) {
|
||||
print '<span class="opacitymedium">-</span>';
|
||||
} else {
|
||||
$worst = ($obj->worststatus === null) ? 3 : (int) $obj->worststatus;
|
||||
if ($worst < 0 || $worst > 3) {
|
||||
$worst = 1;
|
||||
}
|
||||
$ampellabels = array(0 => 'NetDiagMeasureOk', 1 => 'NetDiagMeasureWarn', 2 => 'NetDiagMeasureFail', 3 => 'NetDiagMeasureUnmeasurable');
|
||||
$ampelcss = array(0 => 'badge-status4', 1 => 'badge-status1', 2 => 'badge-status8', 3 => 'badge-status0');
|
||||
print '<span class="badge '.$ampelcss[$worst].'">'.$langs->trans($ampellabels[$worst]).'</span>';
|
||||
print ' <span class="opacitymedium small">'.$meascount;
|
||||
if ((int) $obj->unmesscount > 0) {
|
||||
print ', '.((int) $obj->unmesscount).' n.m.';
|
||||
}
|
||||
print '</span>';
|
||||
}
|
||||
print '</td>';
|
||||
print '<td class="center">'.$proto->getLibStatut(3).'</td>';
|
||||
print '</tr>';
|
||||
}
|
||||
if (empty($rows)) {
|
||||
print '<tr><td colspan="6" class="opacitymedium center">'.$langs->trans("NoRecordFound").'</td></tr>';
|
||||
print '<tr><td colspan="7" class="opacitymedium center">'.$langs->trans("NoRecordFound").'</td></tr>';
|
||||
}
|
||||
|
||||
print '</table>';
|
||||
|
|
|
|||
|
|
@ -260,8 +260,14 @@ foreach ($measurements as $m) {
|
|||
print '<tr class="oddeven">';
|
||||
print '<td class="nowraponall">'.dol_print_date($m->date_measure, 'dayhour').'</td>';
|
||||
print '<td>'.dol_escape_htmltag($m->category).'</td>';
|
||||
print '<td>'.dol_escape_htmltag($m->tool).'</td>';
|
||||
print '<td>'.dol_escape_htmltag($m->label).'</td>';
|
||||
print '<td>'.dol_escape_htmltag(netdiagToolName($m->tool)).'</td>';
|
||||
print '<td>'.dol_escape_htmltag($m->label);
|
||||
// Messparameter darunter: sonst ist nicht erkennbar, wogegen gemessen wurde
|
||||
$params = netdiagFormatParams($m->params);
|
||||
if ($params !== '') {
|
||||
print '<br><span class="opacitymedium small">'.dol_escape_htmltag($params).'</span>';
|
||||
}
|
||||
print '</td>';
|
||||
print '<td>'.netdiagFormatResult($m->result, $m->tool).'</td>';
|
||||
print '<td class="center"><span class="badge '.$statuscss[$st].'">'.$langs->trans($statuslabels[$st]).'</span></td>';
|
||||
print '</tr>';
|
||||
|
|
|
|||
Loading…
Reference in a new issue