178 lines
No EOL
4.6 KiB
JavaScript
178 lines
No EOL
4.6 KiB
JavaScript
'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<Array>} - 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<number>} 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); |