p99_spike_analysis/results_visualization/js/app.js

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);