111 lines
No EOL
3.4 KiB
JavaScript
111 lines
No EOL
3.4 KiB
JavaScript
"use strict";
|
|
|
|
/**
|
|
* UI Logic for Data Visualization
|
|
* Handles rendering charts, updating stats, and applying filters.
|
|
*/
|
|
|
|
import { fetchData } from './api.js';
|
|
|
|
/**
|
|
* @typedef {Object} SensorData
|
|
* @property {string} timestamp - ISO time string
|
|
* @property {number} intensity - Measured light intensity
|
|
* @property {number} wavelength - Light wavelength in nm
|
|
* @property {string} sensor_id - Identifier of the sensor
|
|
*/
|
|
|
|
/**
|
|
* Renders chart visualization inside the DOM element with id 'chart-container'.
|
|
* @param {SensorData[]} dataArray
|
|
*/
|
|
export function renderChart(dataArray) {
|
|
const container = document.getElementById('chart-container');
|
|
if (!container) return;
|
|
|
|
container.innerHTML = '';
|
|
|
|
// Create canvas for chart
|
|
const canvas = document.createElement('canvas');
|
|
canvas.setAttribute('role', 'img');
|
|
canvas.setAttribute('aria-label', 'Diagramm der Intensitätswerte über Zeit');
|
|
container.appendChild(canvas);
|
|
|
|
const ctx = canvas.getContext('2d');
|
|
const timestamps = dataArray.map(d => new Date(d.timestamp).toLocaleTimeString());
|
|
const intensities = dataArray.map(d => d.intensity);
|
|
|
|
// Chart.js-like pseudo implementation (no external lib)
|
|
ctx.beginPath();
|
|
const maxIntensity = Math.max(...intensities, 1);
|
|
const minIntensity = Math.min(...intensities, 0);
|
|
const width = canvas.width = container.offsetWidth;
|
|
const height = canvas.height = Math.min(400, container.offsetHeight || 300);
|
|
|
|
ctx.strokeStyle = '#007acc';
|
|
ctx.lineWidth = 2;
|
|
|
|
intensities.forEach((value, i) => {
|
|
const x = (i / (intensities.length - 1)) * width;
|
|
const y = height - ((value - minIntensity) / (maxIntensity - minIntensity)) * height;
|
|
if (i === 0) ctx.moveTo(x, y);
|
|
else ctx.lineTo(x, y);
|
|
});
|
|
ctx.stroke();
|
|
|
|
updateSummary(dataArray);
|
|
}
|
|
|
|
/**
|
|
* Updates statistics panel with calculated summary data.
|
|
* @param {SensorData[]} dataArray
|
|
*/
|
|
export function updateSummary(dataArray) {
|
|
const avg = dataArray.length ? dataArray.reduce((acc, d) => acc + d.intensity, 0) / dataArray.length : 0;
|
|
const max = dataArray.length ? Math.max(...dataArray.map(d => d.intensity)) : 0;
|
|
const min = dataArray.length ? Math.min(...dataArray.map(d => d.intensity)) : 0;
|
|
|
|
const summaryEl = document.getElementById('summary-panel');
|
|
if (!summaryEl) return;
|
|
|
|
summaryEl.innerHTML = `
|
|
<div class="summary__item"><strong>Durchschnitt:</strong> ${avg.toFixed(2)}</div>
|
|
<div class="summary__item"><strong>Max:</strong> ${max.toFixed(2)}</div>
|
|
<div class="summary__item"><strong>Min:</strong> ${min.toFixed(2)}</div>
|
|
`;
|
|
}
|
|
|
|
/**
|
|
* Reads filter form values and triggers data reload.
|
|
*/
|
|
export async function applyFilters() {
|
|
const sensorSelect = document.getElementById('filter-sensor');
|
|
const timeStart = document.getElementById('filter-start');
|
|
const timeEnd = document.getElementById('filter-end');
|
|
|
|
const params = {};
|
|
if (sensorSelect?.value) params.sensor = sensorSelect.value;
|
|
if (timeStart?.value) params.start = timeStart.value;
|
|
if (timeEnd?.value) params.end = timeEnd.value;
|
|
|
|
const data = await fetchData(params).catch(() => []);
|
|
renderChart(data);
|
|
}
|
|
|
|
/**
|
|
* Binds UI event handlers.
|
|
*/
|
|
export function bindUIEvents() {
|
|
const form = document.getElementById('filter-form');
|
|
if (form) {
|
|
form.addEventListener('submit', e => {
|
|
e.preventDefault();
|
|
applyFilters();
|
|
});
|
|
}
|
|
}
|
|
|
|
// Auto-bind when DOM is ready
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
bindUIEvents();
|
|
}); |