Add data_visualization/js/visualizations.js
This commit is contained in:
parent
ceee02fc48
commit
8d986d5564
1 changed files with 145 additions and 0 deletions
145
data_visualization/js/visualizations.js
Normal file
145
data_visualization/js/visualizations.js
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/**
|
||||
* @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<Object>} 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<Date>} labels Zeitwerte (X-Achse)
|
||||
* @param {Array<number>} 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<Object>} 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);
|
||||
});
|
||||
}
|
||||
Loading…
Reference in a new issue