Compare commits

..

No commits in common. "36dd18f53a43ab2e6aedfd81e214857b04b32071" and "9f07896b05f570379a1b1bed0ae8ac1e0abb3161" have entirely different histories.

4 changed files with 2 additions and 172 deletions

View file

@ -1,39 +1,5 @@
# Changelog NetDiag # Changelog NetDiag
## 1.1.2 — 2026-08-16
### Geräte-Monitor bekommt einen eigenen PDF-Zweig
Die Dauerüberwachung lief bisher in den generischen `else`-Zweig von
`netdiag_pdf.lib.php` und wurde von `netdiagPdfFlattenResult()` zu **einer**
`|`-getrennten Zeile zusammengeschoben. Bei zehn überwachten Geräten standen
damit die zehn Geräte-Zeilen UND sämtliche Einzelausfälle in einer einzigen
Tabellenzelle — vollständig, aber beim Kunden nicht lesbar. Dauertest und
WLAN-Kanal hatten längst je einen Tabellen-Renderer, der Monitor nicht.
- **`netdiagPdfMonitor()`**: Kennzahlenzeile, Tabelle „Überwachte Geräte",
Tabelle „Einzelne Ausfälle" mit Uhrzeiten, Hinweis zum Messverfahren am Ende.
- **Die Falle dabei**, gegen die es einen Kommentar im Code gibt: beim Dauertest
führt ein gesetztes `hinweis` zum sofortigen Abbruch der Ausgabe. Der Monitor
legt aber IMMER einen Hinweis an (das Messverfahren gehört zur Aussage dazu) —
derselbe Aufbau hätte hier bei **jedem** Lauf alle Zahlen verschluckt. Nur
`fehler` bricht ab, der Hinweis steht am Schluss.
- Tabellenköpfe wiederholen sich nach einem Seitenumbruch („— Fortsetzung"),
wie in der Geräteliste.
- Obergrenze 200 Ausfallzeilen gegen den pathologischen Fall (ein im
Sekundentakt flappendes Gerät erzeugt über einen Arbeitstag vierstellig viele
Segmente). Gekürzt wird **sichtbar**: „… und N weitere Ausfälle" steht im
Dokument, sonst läse sich das Protokoll als vollständig.
- **`ausfallzeitSek` heißt jetzt „Ausfallzeit (schlechtestes Gerät)"** — der
Wert ist der des schlechtesten Geräts, nicht die Summe über alle. Bei zehn
überwachten Geräten las sich „Ausfallzeit: 8 min" wie eine Gesamtaussage.
Gleiche Korrektur in `app/src/lib/messfelder.ts`, damit App und PDF dasselbe
Feld gleich benennen.
Geprüft gegen die lokale Testinstanz mit vier konstruierten Fällen (10 Geräte
mit Aussetzern, ein durchgehend totes Gerät, 250 Einzelausfälle über sechs
Seiten, abgerissener Lauf) und gegen die echte Monitor-Messung aus ND2026-0016.
## 1.1.1 — 2026-08-16 ## 1.1.1 — 2026-08-16
### Vier stille Fehler im Kundendokument und im Sync ### Vier stille Fehler im Kundendokument und im Sync

View file

@ -67,7 +67,7 @@ class modNetDiag extends DolibarrModules
$this->editor_url = ''; $this->editor_url = '';
$this->editor_squarred_logo = ''; $this->editor_squarred_logo = '';
$this->version = '1.1.2'; $this->version = '1.1.1';
// Konstanten-Name in llx_const für Modul-Status // Konstanten-Name in llx_const für Modul-Status
$this->const_name = 'MAIN_MODULE_'.strtoupper($this->name); $this->const_name = 'MAIN_MODULE_'.strtoupper($this->name);

View file

@ -338,10 +338,7 @@ function netdiagKundenfelder()
// vollständig ins Kundendokument, nicht nur die Ampel. // vollständig ins Kundendokument, nicht nur die Ampel.
'geraeteAnzahl' => array('Überwachte Geräte', ''), 'geraeteAnzahl' => array('Überwachte Geräte', ''),
'verfuegbarkeitProzent' => array('Verfügbarkeit (schlechtestes Gerät)', '%'), 'verfuegbarkeitProzent' => array('Verfügbarkeit (schlechtestes Gerät)', '%'),
// „Ausfallzeit" allein war irreführend: der Wert ist der des schlechtesten 'ausfallzeitSek' => array('Ausfallzeit', 's'),
// Geräts, nicht die Summe über alle. Bei zehn überwachten Geräten liest
// sich „Ausfallzeit: 8 min" sonst wie eine Gesamtaussage.
'ausfallzeitSek' => array('Ausfallzeit (schlechtestes Gerät)', 's'),
'aussetzer' => array('Aussetzer', ''), 'aussetzer' => array('Aussetzer', ''),
'geraete' => array('Je Gerät', ''), 'geraete' => array('Je Gerät', ''),
'ausfaelle' => array('Einzelne Ausfälle', ''), 'ausfaelle' => array('Einzelne Ausfälle', ''),

View file

@ -198,8 +198,6 @@ function netdiagGeneratePdf($db, $protocol, $outputlangs)
netdiagPdfStressTest($pdf, $m->result); netdiagPdfStressTest($pdf, $m->result);
} elseif ($m->tool === 'wifikanal') { } elseif ($m->tool === 'wifikanal') {
netdiagPdfWifiKanal($pdf, $m->result); netdiagPdfWifiKanal($pdf, $m->result);
} elseif ($m->tool === 'monitor') {
netdiagPdfMonitor($pdf, $m->result);
} else { } else {
$pdf->MultiCell(180, 5, netdiagPdfFlattenResult($m->result), 1, 'L'); $pdf->MultiCell(180, 5, netdiagPdfFlattenResult($m->result), 1, 'L');
} }
@ -373,137 +371,6 @@ function netdiagPdfStressTest($pdf, $json)
} }
} }
/**
* Geräte-Dauerüberwachung strukturiert ins PDF schreiben.
*
* Dritter eigener Zweig neben Dauertest und WLAN-Kanal, aus demselben Grund:
* `netdiagPdfFlattenResult()` schiebt alles zu EINER `|`-getrennten Zeile
* zusammen. Bei zehn überwachten Geräten standen damit die zehn Zeilen aus
* `geraete` UND sämtliche Einzelausfälle in einer einzigen Tabellenzelle
* vollständig, aber beim Kunden nicht lesbar.
*
* ACHTUNG, Unterschied zu netdiagPdfStressTest(): dort führt ein gesetztes
* `hinweis` zum sofortigen Abbruch. Hier darf es das NICHT der Monitor legt
* IMMER einen Hinweis an (das Messverfahren gehört zur Aussage dazu, siehe
* `buildMonitorMeasurement()` in app/src/lib/monitor.ts). Ein Frühabbruch
* würde also bei jedem Monitor-Lauf sämtliche Zahlen verschlucken. Der Hinweis
* steht deshalb am Ende, nur `fehler` bricht ab.
*
* @param TCPDF $pdf PDF-Objekt (Cursor steht direkt hinter der Titelzeile)
* @param string $json result-JSON der Messung
* @return void
*/
function netdiagPdfMonitor($pdf, $json)
{
$data = json_decode((string) $json, true);
if (!is_array($data)) {
$pdf->MultiCell(180, 5, netdiagPdfFlattenResult($json), 1, 'L');
return;
}
if (!empty($data['fehler']) || !empty($data['error'])) {
$pdf->MultiCell(180, 5, netdiagKundentext((string) ($data['fehler'] ?? $data['error'])), 1, 'L');
return;
}
// Kennzahlenzeile. Bezeichnungen bewusst wie in netdiagKundenfelder(),
// damit dasselbe Feld im PDF und in der Dolibarr-Ansicht gleich heißt.
$sum = array();
if (isset($data['dauerSekunden'])) {
$sum[] = 'Messdauer: '.netdiagDauerLesbar($data['dauerSekunden']);
}
if (isset($data['intervallSek'])) {
$sum[] = 'Messabstand: '.$data['intervallSek'].' s';
}
if (isset($data['geraeteAnzahl'])) {
$sum[] = 'Überwachte Geräte: '.$data['geraeteAnzahl'];
}
if (isset($data['verfuegbarkeitProzent']) && $data['verfuegbarkeitProzent'] !== null) {
$sum[] = 'Verfügbarkeit (schlechtestes Gerät): '.number_format((float) $data['verfuegbarkeitProzent'], 1, ',', '').' %';
}
if (isset($data['aussetzer'])) {
$sum[] = 'Aussetzer: '.$data['aussetzer'];
}
if (!empty($data['ausfallzeitSek'])) {
$sum[] = 'Ausfallzeit (schlechtestes Gerät): '.netdiagDauerLesbar($data['ausfallzeitSek']);
}
if (!empty($data['laengsterAusfallSek'])) {
$sum[] = 'Längster Ausfall: '.netdiagDauerLesbar($data['laengsterAusfallSek']);
}
if (!empty($sum)) {
$pdf->MultiCell(180, 5, implode(' | ', $sum), 1, 'L');
}
// Je Gerät eine eigene Zeile — das ist der eigentliche Beleg. Die Zeilen
// kommen vorformatiert aus der App (dieselben, die dort in der Messungs-
// liste stehen), wie beim Dauertest: eine Quelle der Wahrheit.
$geraete = (isset($data['geraete']) && is_array($data['geraete'])) ? $data['geraete'] : array();
if (!empty($geraete)) {
netdiagPdfEnsureSpace($pdf, 10);
$pdf->SetFont('', 'B', 8);
$pdf->Cell(180, 5, 'Überwachte Geräte ('.count($geraete).')', 1, 1, 'L');
$pdf->SetFont('', '', 8);
foreach ($geraete as $line) {
netdiagPdfEnsureSpace($pdf, 5, function ($p) use ($geraete) {
$p->SetFont('', 'B', 8);
$p->Cell(180, 5, 'Überwachte Geräte ('.count($geraete).') — Fortsetzung', 1, 1, 'L');
$p->SetFont('', '', 8);
});
$pdf->Cell(180, 5, dol_trunc((string) $line, 120), 1, 1, 'L');
}
}
// Einzelne Ausfälle mit Uhrzeit. Genau das ist der Nachweis bei der
// häufigsten Reklamation („die Kamera fällt ständig aus").
$ausfaelle = (isset($data['ausfaelle']) && is_array($data['ausfaelle'])) ? $data['ausfaelle'] : array();
netdiagPdfEnsureSpace($pdf, 10);
if (empty($ausfaelle)) {
$pdf->SetFont('', 'I', 8);
$pdf->Cell(180, 5, 'Kein Ausfall während der Überwachung.', 1, 1, 'L');
$pdf->SetFont('', '', 8);
} else {
$pdf->SetFont('', 'B', 8);
$pdf->Cell(180, 5, 'Einzelne Ausfälle ('.count($ausfaelle).')', 1, 1, 'L');
$pdf->SetFont('', '', 8);
/*
* Obergrenze gegen den pathologischen Fall: ein im Sekundentakt
* flappendes Gerät erzeugt über einen Arbeitstag vierstellig viele
* Segmente das wären zweistellig viele Seiten reine Ausfallliste.
* 200 Zeilen sind rund vier Seiten und belegen jede reale Störung
* ausreichend. Gekürzt wird NICHT stillschweigend: die Restzahl steht
* sichtbar im Dokument, sonst läse sich das Protokoll als vollständig.
*/
$max = 200;
$i = 0;
foreach ($ausfaelle as $line) {
if ($i >= $max) {
break;
}
netdiagPdfEnsureSpace($pdf, 5, function ($p) use ($ausfaelle) {
$p->SetFont('', 'B', 8);
$p->Cell(180, 5, 'Einzelne Ausfälle ('.count($ausfaelle).') — Fortsetzung', 1, 1, 'L');
$p->SetFont('', '', 8);
});
$pdf->Cell(180, 5, dol_trunc((string) $line, 120), 1, 1, 'L');
$i++;
}
$rest = count($ausfaelle) - $max;
if ($rest > 0) {
netdiagPdfEnsureSpace($pdf, 5);
$pdf->SetFont('', 'I', 8);
$pdf->Cell(180, 5, '… und '.$rest.' weitere Ausfälle, hier aus Platzgründen nicht einzeln aufgeführt.', 1, 1, 'L');
$pdf->SetFont('', '', 8);
}
}
// Hinweis zum Schluss: Messverfahren und ggf. abgerissener Lauf.
if (!empty($data['hinweis'])) {
netdiagPdfEnsureSpace($pdf, 10);
$pdf->SetFont('', 'I', 7);
$pdf->MultiCell(180, 4, netdiagKundentext((string) $data['hinweis']), 1, 'L');
$pdf->SetFont('', '', 8);
}
}
/** /**
* WLAN-Kanal-Momentaufnahme im PDF als Tabelle ausgeben. * WLAN-Kanal-Momentaufnahme im PDF als Tabelle ausgeben.
* *