'use strict'; // Globale Variable zum Halten der Spike-Daten let spikeData = []; let filteredData = []; let chartInstance = null; /** * Ruft Spike-Daten von der API ab. * @param {Object} params - Parameter für Filter (z. B. {time_range, event_type, cpu_id}). * @returns {Promise} - Promise mit den Spike-Daten. */ async function fetchSpikeData(params = {}) { const query = new URLSearchParams(params).toString(); const url = query ? `/api/spike_data?${query}` : '/api/spike_data'; try { const response = await fetch(url); if (!response.ok) { console.error('Fehler beim Abruf von /api/spike_data:', response.status); return []; } const data = await response.json(); return Array.isArray(data) ? data : []; } catch (err) { console.error('Netzwerkfehler oder ungültige Antwort:', err); return []; } } /** * Initialisiert die Anwendung, UI-Elemente und Chart. */ function initApp() { const eventFilter = document.getElementById('filter-event'); const timeRangeFilter = document.getElementById('filter-time'); const cpuFilter = document.getElementById('filter-cpu'); [eventFilter, timeRangeFilter, cpuFilter].forEach(el => { if (el) el.addEventListener('change', applyFilters); }); // Erste Datenladung und Chart-Setup fetchSpikeData().then(data => { spikeData = data; filteredData = data; renderChart(data); updateStats(data); }); } /** * Filtert Spike-Daten basierend auf Benutzerwahl und aktualisiert die Anzeige. */ function applyFilters() { const eventType = document.getElementById('filter-event')?.value || ''; const timeRange = document.getElementById('filter-time')?.value || ''; const cpuId = document.getElementById('filter-cpu')?.value || ''; let params = {}; if (eventType) params.event_type = eventType; if (timeRange) params.time_range = timeRange; if (cpuId) params.cpu_id = cpuId; fetchSpikeData(params).then(data => { filteredData = data; updateChart(data); updateStats(data); }); } /** * Rendert den Chart mit gegebenen Daten. * @param {Array} data */ function renderChart(data) { const ctx = document.getElementById('spikeChart'); if (!ctx) return; const timestamps = data.map(d => new Date(d.timestamp)); const values = data.map(d => d.value); if (chartInstance) { chartInstance.destroy(); } chartInstance = new Chart(ctx, { type: 'line', data: { labels: timestamps, datasets: [{ label: 'P99-Spikes', data: values, borderColor: '#007acc', backgroundColor: 'rgba(0, 122, 204, 0.2)', pointRadius: 2, fill: false, tension: 0.1 }] }, options: { responsive: true, scales: { x: { type: 'time', time: { unit: 'minute' } }, y: { beginAtZero: true } }, plugins: { legend: { position: 'top' } } } }); } /** * Aktualisiert bestehenden Chart mit neuen Daten. * @param {Array} data */ function updateChart(data) { if (!chartInstance) { renderChart(data); return; } chartInstance.data.labels = data.map(d => new Date(d.timestamp)); chartInstance.data.datasets[0].data = data.map(d => d.value); chartInstance.update(); } /** * Berechnet und zeigt statistische Kennzahlen an. * @param {Array} data */ function updateStats(data) { const medianEl = document.getElementById('stat-median'); const p99El = document.getElementById('stat-p99'); const deltaEl = document.getElementById('stat-delta'); if (!data || data.length === 0) { [medianEl, p99El, deltaEl].forEach(el => { if (el) el.textContent = '-'; }); return; } const values = [...data.map(d => d.value)].sort((a, b) => a - b); const median = computePercentile(values, 50); const p99 = computePercentile(values, 99); const deltaTail = (p99 - median).toFixed(2); if (medianEl) medianEl.textContent = median.toFixed(2); if (p99El) p99El.textContent = p99.toFixed(2); if (deltaEl) deltaEl.textContent = deltaTail; } /** * Berechnet Perzentilwert aus sortiertem Array. * @param {Array} sortedValues * @param {number} percentile * @returns {number} */ function computePercentile(sortedValues, percentile) { if (sortedValues.length === 0) return 0; const index = (percentile / 100) * (sortedValues.length - 1); const lower = Math.floor(index); const upper = lower + 1; const weight = index % 1; if (upper >= sortedValues.length) return sortedValues[lower]; return sortedValues[lower] * (1 - weight) + sortedValues[upper] * weight; } window.addEventListener('load', initApp);