Files
thetool/Layout/default/Device/LiveGraph.php
2025-06-29 20:33:54 +02:00

112 lines
4.7 KiB
PHP

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Live-Graph</title>
<style>
body, html { margin: 0; padding: 0; width: 100%; height: 100%; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; }
.chart-container { position: relative; width: 100%; height: 100%; padding: 10px; box-sizing: border-box; }
.loader { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
@keyframes blinker { 50% { opacity: 0; } }
.blink_me { animation: blinker 1s linear infinite; color: #dc3545; }
</style>
</head>
<body>
<div class="chart-container">
<canvas id="liveChartCanvas"></canvas>
<div class="loader" id="loader">
<p>Lade initiale Daten...</p>
</div>
</div>
<script src="/plugins/axios/axios.min.js"></script>
<script src="/plugins/moment/moment.min.js"></script>
<script src="/plugins/chart.js/chart.4.4.6.js"></script>
<script src="/plugins/chart.js/chartjs-adapter-moment.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
const params = new URLSearchParams(window.location.search);
const rxId = params.get('rx_id');
const txId = params.get('tx_id');
const interfaceName = params.get('name');
const apiBaseUrl = '<?= $API_BASE_URL ?>';
let chartInstance = null;
let pollInterval = null;
document.title = `Live: ${interfaceName}`;
const addPointToChart = (datasetIndex, newPoint) => {
const dataset = chartInstance.data.datasets[datasetIndex].data;
if (dataset.length > 0 && dataset[dataset.length - 1].x >= newPoint.x) return;
dataset.push(newPoint);
if (dataset.length > 120) dataset.shift(); // Keep rolling 10-minute window (120 points @ 5s interval)
};
const fetchLatestValue = async (itemId) => {
try {
const res = await axios.get(`${apiBaseUrl}/liveData`, { params: { itemId } });
return res.data;
} catch (e) {
console.error(`Failed to fetch live data for item ${itemId}`, e);
return null;
}
};
let isPolling = false;
const startPolling = () => {
pollInterval = setInterval(async () => {
if (isPolling) return; // Prevent overlapping requests
isPolling = true;
const [rxPoint, txPoint] = await Promise.all([
fetchLatestValue(rxId),
fetchLatestValue(txId)
]);
if (rxPoint) addPointToChart(0, rxPoint);
if (txPoint) addPointToChart(1, txPoint);
chartInstance.update('none');
isPolling = false;
}, 250);
};
const initChart = (initialData) => {
document.getElementById('loader').style.display = 'none';
const ctx = document.getElementById('liveChartCanvas').getContext('2d');
chartInstance = new Chart(ctx, {
type: 'line',
data: {
datasets: [
{ label: 'Empfangen', data: initialData[rxId] || [], borderColor: '#4CAF50', borderWidth: 1.5, pointRadius: 1, tension: 0.2, fill: true, backgroundColor: 'rgba(76, 175, 80, 0.1)' },
{ label: 'Gesendet', data: initialData[txId] || [], borderColor: '#2196F3', borderWidth: 1.5, pointRadius: 1, tension: 0.2, fill: true, backgroundColor: 'rgba(33, 150, 243, 0.1)' }
]
},
options: {
responsive: true, maintainAspectRatio: false,
scales: {
x: { type: 'time', time: { unit: 'minute', tooltipFormat: 'HH:mm:ss' } },
y: { beginAtZero: true, title: { display: true, text: 'Mbps' } }
},
plugins: {
title: { display: true, text: `Live-Traffic für ${interfaceName}`},
legend: { position: 'bottom' }
}
}
});
startPolling();
};
// Fetch initial data for the last 10 minutes
axios.post(`${apiBaseUrl}/interfaceData`, { itemIds: [rxId, txId], timeRange: '10m' })
.then(res => initChart(res.data))
.catch(err => {
document.getElementById('loader').innerText = "Fehler beim Laden der initialen Daten.";
console.error(err);
});
window.addEventListener('beforeunload', () => clearInterval(pollInterval));
});
</script>
</body>
</html>