This commit is contained in:
Mateusz Gruszczyński
2026-02-03 21:43:42 +01:00
commit e3724e1249
12 changed files with 474 additions and 0 deletions

76
static/css/style.css Normal file
View File

@@ -0,0 +1,76 @@
:root {
--bg-dark: #0d1117;
--card-bg: #161b22;
--border-color: #30363d;
--text-main: #c9d1d9;
--blue-accent: #58a6ff;
}
body {
background-color: var(--bg-dark);
color: var(--text-main);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
font-size: 14px;
margin: 0;
overflow-x: hidden;
}
.gauge-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
margin-bottom: 15px;
}
.gauge-card {
background-color: var(--card-bg);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 10px 5px;
text-align: center;
}
.gauge-canvas-container {
max-width: 80px;
margin: 0 auto;
}
.gauge-label {
font-size: 0.75rem;
font-weight: 600;
color: var(--blue-accent);
margin-top: 2px;
}
.voltage-value {
font-size: 1.1rem;
font-weight: 800;
color: #ffffff;
}
.time-btn-container {
display: flex;
flex-wrap: nowrap;
overflow-x: auto;
gap: 6px;
padding-bottom: 10px;
justify-content: center;
}
.time-btn {
font-size: 0.75rem !important;
padding: 5px 12px !important;
white-space: nowrap;
border-color: var(--border-color) !important;
color: var(--blue-accent) !important;
}
.time-btn.active {
background-color: #1f6feb !important;
color: white !important;
border-color: #1f6feb !important;
}
.main-chart-card {
background-color: var(--card-bg);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 15px;
height: 55vh;
min-height: 350px;
}
footer { padding: 20px 0; opacity: 0.7; }
@media (max-width: 576px) {
.voltage-value { font-size: 0.95rem; }
.main-chart-card { height: 50vh; padding: 10px; }
}

117
static/js/monitor.js Normal file
View File

@@ -0,0 +1,117 @@
const socket = io();
let currentTimeRange = '6h';
let phasesConfig = {};
const gauges = {};
let voltageChart = null;
const THRESHOLDS = { min: 207, max: 253 };
function initMonitor(phases, defaultRange) {
phasesConfig = phases;
currentTimeRange = defaultRange;
const gaugeConfig = {
type: 'doughnut',
data: {
datasets: [{
data: [0, 100],
backgroundColor: ['#198754', '#1a1d20'],
borderWidth: 0,
circumference: 180,
rotation: 270
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
cutout: '75%',
plugins: { legend: { display: false }, tooltip: { enabled: false } }
}
};
Object.keys(phasesConfig).forEach(id => {
const canvas = document.getElementById('gauge' + id);
if (canvas) {
gauges[id] = new Chart(canvas, JSON.parse(JSON.stringify(gaugeConfig)));
updateGaugeUI(id, 230);
}
});
const ctxChart = document.getElementById('voltageChart');
if (ctxChart) {
voltageChart = new Chart(ctxChart, {
type: 'line',
data: {
datasets: Object.keys(phasesConfig).map(id => ({
label: phasesConfig[id].label,
data: [],
borderColor: phasesConfig[id].color,
backgroundColor: phasesConfig[id].color + '20',
tension: 0.3,
pointRadius: 0,
borderWidth: 2
}))
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
type: 'time',
time: {
displayFormats: { hour: 'HH:mm', minute: 'HH:mm' },
tooltipFormat: 'HH:mm'
},
grid: { color: '#2d3139' }
},
y: { min: 190, max: 270, grid: { color: '#2d3139' }, ticks: { stepSize: 10 } }
}
}
});
}
socket.on('voltage_update', function(data) {
Object.keys(phasesConfig).forEach(id => {
const val = data['phase' + id];
const textElement = document.getElementById('value' + id);
if (val !== undefined && val !== null && val !== 0) {
const numVal = parseFloat(val);
if (textElement) textElement.textContent = numVal.toFixed(1) + 'V';
updateGaugeUI(id, numVal);
}
});
if (data.timestamp) {
const date = new Date(data.timestamp);
document.getElementById('lastUpdate').textContent = 'Odczyt: ' + date.toLocaleTimeString('pl-PL', {hour: '2-digit', minute:'2-digit', second:'2-digit', hour12: false});
}
});
window.changeTimeRange(currentTimeRange);
}
function updateGaugeUI(id, val) {
if (!gauges[id]) return;
const percentage = Math.max(0, Math.min(100, ((val - 190) / 80) * 100));
let color = '#198754';
if (val < THRESHOLDS.min || val > THRESHOLDS.max) color = '#dc3545';
else if (val < 212 || val > 248) color = '#ffc107';
gauges[id].data.datasets[0].data = [percentage, 100 - percentage];
gauges[id].data.datasets[0].backgroundColor = [color, '#1a1d20'];
gauges[id].update('none');
}
window.changeTimeRange = async function(range) {
currentTimeRange = range;
document.querySelectorAll('.time-btn').forEach(btn => btn.classList.remove('active'));
const activeBtn = document.querySelector(`[data-range="${range}"]`);
if (activeBtn) activeBtn.classList.add('active');
if (!voltageChart) return;
for (let i = 1; i <= Object.keys(phasesConfig).length; i++) {
try {
const response = await fetch(`/api/timeseries/${i}?range=${range}`);
const data = await response.json();
voltageChart.data.datasets[i-1].data = data.map(d => ({ x: new Date(d.time), y: d.voltage }));
} catch (e) { console.error(e); }
}
voltageChart.update();
};