/** * @module visualizations * Zeichnet Diagramme und analysiert Ereignisse basierend auf Sensor- und Zustandsdaten. * Dunkles Design für Nachtbetrieb, responsiv ausgelegt. */ /** * Erstellt Diagramme für IR-Gain, Spannung und Drift. * @param {Array} telemetryData - Array von Telemetrie-Datenpunkten {timestamp, vbat, ir_gain, drift, event_flag} */ export function renderCharts(telemetryData = []) { const container = document.querySelector('.chart-container'); if (!container) return; container.innerHTML = ''; if (!Array.isArray(telemetryData) || telemetryData.length === 0) { container.textContent = 'Keine Telemetrie-Daten verfügbar.'; return; } // Zeitachse vorbereiten const timestamps = telemetryData.map(d => new Date(d.timestamp)); const irGainValues = telemetryData.map(d => d.ir_gain); const vbatValues = telemetryData.map(d => d.vbat); const driftValues = telemetryData.map(d => d.drift); // Canvas-Elemente erzeugen const chartSpecs = [ { key: 'ir_gain', label: 'IR-Gain', values: irGainValues, color: '#ff9800' }, { key: 'vbat', label: 'Batteriespannung (V)', values: vbatValues, color: '#03a9f4' }, { key: 'drift', label: 'Drift', values: driftValues, color: '#8bc34a' } ]; chartSpecs.forEach(spec => { const chartBlock = document.createElement('section'); chartBlock.className = `chart-block chart-block--${spec.key}`; const h3 = document.createElement('h3'); h3.textContent = spec.label; chartBlock.appendChild(h3); const canvas = document.createElement('canvas'); canvas.width = container.offsetWidth; canvas.height = 180; chartBlock.appendChild(canvas); container.appendChild(chartBlock); drawLineChart(canvas, timestamps, spec.values, spec.color); }); // Ereignis-Log aktualisieren const events = telemetryData.filter(d => d.event_flag); renderEventLog(events); } /** * Zeichnet ein einfaches Liniendiagramm auf Canvas. * @param {HTMLCanvasElement} canvas * @param {Array} labels Zeitwerte (X-Achse) * @param {Array} data Werte für Y-Achse * @param {string} color Linienfarbe */ function drawLineChart(canvas, labels, data, color) { const ctx = canvas.getContext('2d'); const width = canvas.width; const height = canvas.height; // Hintergrund dunkel ctx.fillStyle = '#111'; ctx.fillRect(0, 0, width, height); if (data.length === 0) return; const min = Math.min(...data); const max = Math.max(...data); const xStep = width / (data.length - 1); ctx.strokeStyle = color; ctx.lineWidth = 2; ctx.beginPath(); data.forEach((val, i) => { const x = i * xStep; const y = height - ((val - min) / (max - min)) * (height - 20) - 10; if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); }); ctx.stroke(); ctx.strokeStyle = '#333'; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(0, height - 1); ctx.lineTo(width, height - 1); ctx.stroke(); } /** * Befüllt Tabelleneinträge für Ereignislog. * @param {Array} events - Array von Objekten mit event_flag TRUE und zusätzlichen Daten */ export function renderEventLog(events = []) { const table = document.querySelector('.event-log'); if (!table) return; table.innerHTML = ''; const headerRow = document.createElement('tr'); ['Zeit', 'Typ', 'Details'].forEach(label => { const th = document.createElement('th'); th.textContent = label; headerRow.appendChild(th); }); table.appendChild(headerRow); if (events.length === 0) { const row = document.createElement('tr'); const cell = document.createElement('td'); cell.colSpan = 3; cell.textContent = 'Keine Ereignisse erkannt.'; row.appendChild(cell); table.appendChild(row); return; } events.forEach(evt => { const row = document.createElement('tr'); const timeCell = document.createElement('td'); timeCell.textContent = new Date(evt.timestamp).toLocaleTimeString(); row.appendChild(timeCell); const typeCell = document.createElement('td'); if (evt.vbat < 6.0) typeCell.textContent = 'Spannungseinbruch'; else if (evt.event_flag === 'reinit') typeCell.textContent = 'Reinitialisierung'; else typeCell.textContent = 'Unbekanntes Ereignis'; row.appendChild(typeCell); const detailsCell = document.createElement('td'); detailsCell.textContent = `IR-Gain: ${evt.ir_gain}, Drift: ${evt.drift.toFixed(2)}`; row.appendChild(detailsCell); table.appendChild(row); }); }