dot na zanikach
This commit is contained in:
41
app.py
41
app.py
@@ -1,22 +1,20 @@
|
||||
import os
|
||||
import warnings
|
||||
|
||||
os.environ['EVENTLET_NO_GREENDNS'] = 'yes'
|
||||
import eventlet
|
||||
eventlet.monkey_patch()
|
||||
eventlet.monkey_patch(all=True)
|
||||
|
||||
from flask import Flask, render_template, jsonify, request
|
||||
from flask_socketio import SocketIO
|
||||
from influxdb import InfluxDBClient
|
||||
import threading
|
||||
import time
|
||||
from datetime import datetime
|
||||
import config
|
||||
import os
|
||||
|
||||
warnings.filterwarnings("ignore", category=DeprecationWarning, module="eventlet")
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['SECRET_KEY'] = config.FLASK_CONFIG['secret_key']
|
||||
socketio = SocketIO(app,
|
||||
cors_allowed_origins="*",
|
||||
async_mode='eventlet',
|
||||
ping_timeout=60,
|
||||
ping_interval=25)
|
||||
socketio = SocketIO(app, cors_allowed_origins="*", async_mode='eventlet')
|
||||
|
||||
def get_influx_client():
|
||||
client = InfluxDBClient(
|
||||
@@ -35,10 +33,10 @@ def get_current_voltage(phase_id):
|
||||
try:
|
||||
result = client.query(query)
|
||||
points = list(result.get_points())
|
||||
if points:
|
||||
if points and points[0].get('value') is not None:
|
||||
return {'voltage': round(float(points[0]['value']), 2), 'timestamp': points[0]['time']}
|
||||
except Exception as e:
|
||||
print(f"Influx Error Phase {phase_id}: {e}")
|
||||
print(f"Current Error: {e}")
|
||||
finally:
|
||||
client.close()
|
||||
return {'voltage': 0, 'timestamp': None}
|
||||
@@ -57,7 +55,13 @@ def api_timeseries(phase_id):
|
||||
query = config.PHASES[phase_id]['query'].replace('$timeFilter', cfg['filter']).replace('$__interval', cfg['interval'])
|
||||
try:
|
||||
result = client.query(query)
|
||||
data = [{'time': p['time'], 'voltage': round(p['mean'], 2)} for p in result.get_points() if p.get('mean') is not None]
|
||||
data = []
|
||||
for p in result.get_points():
|
||||
val = p.get('voltage') or p.get('min') or p.get('mean') or p.get('value')
|
||||
if val is not None:
|
||||
data.append({'time': p['time'], 'voltage': round(float(val), 2)})
|
||||
else:
|
||||
data.append({'time': p['time'], 'voltage': 0})
|
||||
return jsonify(data)
|
||||
except Exception as e:
|
||||
print(f"History Error: {e}")
|
||||
@@ -66,19 +70,16 @@ def api_timeseries(phase_id):
|
||||
client.close()
|
||||
|
||||
def background_voltage_update():
|
||||
print("Background worker started...")
|
||||
while True:
|
||||
try:
|
||||
voltages = {'timestamp': None}
|
||||
for pid in config.PHASES.keys():
|
||||
data = get_current_voltage(pid)
|
||||
voltages[f'phase{pid}'] = data['voltage']
|
||||
if data['timestamp']: voltages['timestamp'] = data['timestamp']
|
||||
|
||||
res = get_current_voltage(pid)
|
||||
voltages[f'phase{pid}'] = res['voltage']
|
||||
if res['timestamp']: voltages['timestamp'] = res['timestamp']
|
||||
socketio.emit('voltage_update', voltages)
|
||||
except Exception as e:
|
||||
print(f"Worker Loop Error: {e}")
|
||||
|
||||
print(f"Worker Error: {e}")
|
||||
eventlet.sleep(config.CHART_CONFIG['update_interval'] / 1000)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
12
config.py
12
config.py
@@ -15,20 +15,20 @@ PHASES = {
|
||||
1: {
|
||||
'entity_id': '0_electricity_meter_voltage_phase_1',
|
||||
'label': 'L1',
|
||||
'color': '#0d6efd',
|
||||
'query': 'SELECT mean("value") FROM "V" WHERE ("entity_id" = \'0_electricity_meter_voltage_phase_1\') AND time > now() - $timeFilter GROUP BY time($__interval) fill(null)'
|
||||
'color': '#3498db',
|
||||
'query': 'SELECT min("value") as voltage FROM "V" WHERE ("entity_id" = \'0_electricity_meter_voltage_phase_1\') AND time > now() - $timeFilter GROUP BY time($__interval) fill(null)'
|
||||
},
|
||||
2: {
|
||||
'entity_id': '0_electricity_meter_voltage_phase_2',
|
||||
'label': 'L2',
|
||||
'color': '#198754',
|
||||
'query': 'SELECT mean("value") FROM "V" WHERE ("entity_id" = \'0_electricity_meter_voltage_phase_2\') AND time > now() - $timeFilter GROUP BY time($__interval) fill(null)'
|
||||
'color': '#9112f3',
|
||||
'query': 'SELECT min("value") as voltage FROM "V" WHERE ("entity_id" = \'0_electricity_meter_voltage_phase_2\') AND time > now() - $timeFilter GROUP BY time($__interval) fill(null)'
|
||||
},
|
||||
3: {
|
||||
'entity_id': '0_electricity_meter_voltage_phase_3',
|
||||
'label': 'L3',
|
||||
'color': '#dc3545',
|
||||
'query': 'SELECT mean("value") FROM "V" WHERE ("entity_id" = \'0_electricity_meter_voltage_phase_3\') AND time > now() - $timeFilter GROUP BY time($__interval) fill(null)'
|
||||
'color': '#2ecc71',
|
||||
'query': 'SELECT min("value") as voltage FROM "V" WHERE ("entity_id" = \'0_electricity_meter_voltage_phase_3\') AND time > now() - $timeFilter GROUP BY time($__interval) fill(null)'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,30 +41,50 @@ function initMonitor(phases, defaultRange) {
|
||||
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
|
||||
}))
|
||||
},
|
||||
data: { datasets: [] },
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
x: {
|
||||
type: 'time',
|
||||
time: {
|
||||
displayFormats: { hour: 'HH:mm', minute: 'HH:mm' },
|
||||
tooltipFormat: 'HH:mm'
|
||||
},
|
||||
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 } }
|
||||
y: {
|
||||
beginAtZero: false,
|
||||
suggestedMin: 210,
|
||||
suggestedMax: 255,
|
||||
grid: { color: '#2d3139' },
|
||||
ticks: { stepSize: 5, color: '#c9d1d9' }
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
labels: {
|
||||
color: '#c9d1d9',
|
||||
filter: function(item) {
|
||||
return !item.text.includes('Zanik') && !item.text.includes('Powrot');
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function(context) {
|
||||
if (context.dataset.label.includes('Zanik')) {
|
||||
return 'ZANIK: ' + context.raw.realV.toFixed(1) + 'V';
|
||||
}
|
||||
if (context.dataset.label.includes('Powrot')) {
|
||||
return 'POWROT: ' + context.raw.realV.toFixed(1) + 'V';
|
||||
}
|
||||
let label = context.dataset.label || '';
|
||||
if (context.parsed.y !== null) {
|
||||
label += ': ' + context.parsed.y.toFixed(1) + 'V';
|
||||
}
|
||||
return label;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -74,15 +94,18 @@ function initMonitor(phases, defaultRange) {
|
||||
Object.keys(phasesConfig).forEach(id => {
|
||||
const val = data['phase' + id];
|
||||
const textElement = document.getElementById('value' + id);
|
||||
if (val !== undefined && val !== null && val !== 0) {
|
||||
if (val !== undefined && val !== null) {
|
||||
const numVal = parseFloat(val);
|
||||
if (textElement) textElement.textContent = numVal.toFixed(1) + 'V';
|
||||
if (textElement) {
|
||||
textElement.textContent = numVal.toFixed(1) + 'V';
|
||||
textElement.style.color = numVal < 200 ? '#dc3545' : '#fff';
|
||||
}
|
||||
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});
|
||||
document.getElementById('lastUpdate').textContent = 'Odczyt: ' + date.toLocaleTimeString('pl-PL', {hour12: false});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -105,12 +128,74 @@ window.changeTimeRange = async function(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++) {
|
||||
const phaseIds = Object.keys(phasesConfig);
|
||||
voltageChart.data.datasets = [];
|
||||
|
||||
for (let id of phaseIds) {
|
||||
try {
|
||||
const response = await fetch(`/api/timeseries/${i}?range=${range}`);
|
||||
const response = await fetch(`/api/timeseries/${id}?range=${range}`);
|
||||
const data = await response.json();
|
||||
voltageChart.data.datasets[i-1].data = data.map(d => ({ x: new Date(d.time), y: d.voltage }));
|
||||
const lineData = [];
|
||||
const outagePoints = [];
|
||||
const recoveryPoints = [];
|
||||
let wasOutage = false;
|
||||
|
||||
data.forEach(p => {
|
||||
const v = p.voltage;
|
||||
const t = new Date(p.time);
|
||||
const minY = voltageChart.scales.y.min || 190;
|
||||
|
||||
if (v < 200 && v !== null) {
|
||||
outagePoints.push({ x: t, y: minY + 0.5, realV: v });
|
||||
lineData.push({ x: t, y: null });
|
||||
wasOutage = true;
|
||||
} else {
|
||||
if (wasOutage) {
|
||||
recoveryPoints.push({ x: t, y: v, realV: v });
|
||||
wasOutage = false;
|
||||
}
|
||||
lineData.push({ x: t, y: v });
|
||||
}
|
||||
});
|
||||
|
||||
voltageChart.data.datasets.push({
|
||||
label: phasesConfig[id].label,
|
||||
data: lineData,
|
||||
borderColor: phasesConfig[id].color,
|
||||
backgroundColor: phasesConfig[id].color + '15',
|
||||
tension: 0,
|
||||
borderWidth: 2,
|
||||
spanGaps: false,
|
||||
pointRadius: 0
|
||||
});
|
||||
|
||||
if (outagePoints.length > 0) {
|
||||
voltageChart.data.datasets.push({
|
||||
label: 'Zanik ' + phasesConfig[id].label,
|
||||
data: outagePoints,
|
||||
type: 'scatter',
|
||||
pointRadius: 5,
|
||||
pointBackgroundColor: '#ff0000ff',
|
||||
pointBorderColor: phasesConfig[id].color,
|
||||
pointBorderWidth: 2,
|
||||
z: 999
|
||||
});
|
||||
}
|
||||
|
||||
if (recoveryPoints.length > 0) {
|
||||
voltageChart.data.datasets.push({
|
||||
label: 'Powrot ' + phasesConfig[id].label,
|
||||
data: recoveryPoints,
|
||||
type: 'scatter',
|
||||
pointRadius: 5,
|
||||
pointBackgroundColor: '#3fb99aff',
|
||||
pointBorderColor: '#ffffff',
|
||||
pointBorderWidth: 2,
|
||||
z: 999
|
||||
});
|
||||
}
|
||||
} catch (e) { console.error(e); }
|
||||
}
|
||||
voltageChart.update();
|
||||
|
||||
Reference in New Issue
Block a user