еще косметические исправления, на страницу теперь приятно смотреть

This commit is contained in:
VladislavOstapov 2024-01-04 00:26:02 +03:00
parent 13ef139b66
commit 5de199e35a
4 changed files with 158 additions and 56 deletions

View File

@ -24,7 +24,8 @@ def view_stats(request):
def view_tank_chart(request): def view_tank_chart(request):
# только для тестирования! # только для тестирования!
res = requests.get(TEST_BASE_FETCH + "?tank_chart", headers={'Authorization': os.getenv("TEST_AUTH")}) days = request.GET.get('days', '7')
res = requests.get(TEST_BASE_FETCH + "?tank_chart=" + days, headers={'Authorization': os.getenv("TEST_AUTH")})
response = HttpResponse(res.content) response = HttpResponse(res.content)
response.headers["Content-type"] = response.headers["Content-type"] response.headers["Content-type"] = response.headers["Content-type"]
return response return response

View File

@ -2,6 +2,8 @@
body { body {
--text-color: #262626; --text-color: #262626;
--text-color2: #3d3d3d; --text-color2: #3d3d3d;
--text-good: green;
--text-bad: red;
--brand-bg: #EDF3FE; --brand-bg: #EDF3FE;
--brand-text: #5488F7; --brand-text: #5488F7;
@ -15,6 +17,8 @@ body {
body { body {
--text-color: #eee; --text-color: #eee;
--text-color2: #bbb; --text-color2: #bbb;
--text-good: greenyellow;
--text-bad: orangered;
--brand-bg: #393E50; --brand-bg: #393E50;
--brand-text: #5F93F3; --brand-text: #5F93F3;

View File

@ -3,11 +3,6 @@
// интервал обновления статуса в миллисекундах // интервал обновления статуса в миллисекундах
const RELOAD_STATS_INTERVAL = 10000 const RELOAD_STATS_INTERVAL = 10000
// интервал обновления графика - 1% показываемого времени (2 недели)
// const RELOAD_CHART_INTERVAL = (3600 * 1000 * 24 * 14) / 100
// const RELOAD_CHART_INTERVAL = (3600 * 1000 * 24) / 100
const RELOAD_CHART_INTERVAL = 30000
const DATETIME_FORMAT = "DD MMM YYYY HH:mm:ss" const DATETIME_FORMAT = "DD MMM YYYY HH:mm:ss"
function unpackBits(num, desc) { function unpackBits(num, desc) {
@ -78,42 +73,116 @@ async function makeRequest(url) {
return await response.json() return await response.json()
} }
async function loadAllData() {
return await makeRequest('/fetch/all')
}
async function loadChartData() { async function loadChartData() {
return (await makeRequest('/fetch/tank-chart'))['tank_chart'] let chartTime = localStorage.getItem("settings-chart-time")
return (await makeRequest('/fetch/tank-chart?days=' + chartTime))['tank_chart']
} }
async function loadLastUpdates() { async function loadLastUpdates() {
return (await makeRequest('/fetch/stats'))['stats'] return (await makeRequest('/fetch/stats'))['stats']
} }
/**
* Функция, устанавливающая классы CSS 'value-good' и 'value-bad'.
* @param element
* @param good
* @private
*/
function _setIndicatorClass(element, good) {
if (good) {
element.classList.remove("value-bad")
element.classList.add("value-good")
} else {
element.classList.remove("value-good")
element.classList.add("value-bad")
}
}
const pumpStageDescription = {
0: "отключен",
2: "инициализация: установка задвижек в начальное состояние",
21: "инициализация: закрытие задвижек 23.5 и 23.6",
31: "инициализация: открытие задвижек 23.5 и 23.6",
99: "<span class=\"value-bad\">авария</span>",
100: "ожидание сигнала на перекачку воды",
102: "запуск: открытие задвижки 23.6",
110: "запуск: ожидание сигнала от датчика уровня поз.36",
121: "запуск: открытие задвижек насоса",
131: "запуск: закрытие задвижки 23.6",
141: "запуск: пуск ПЧ",
200: "<span class=\"value-good\">работает</span>",
202: "остановка: закрытие задвижек 23.3 и 23.4",
211: "остановка: ожидание остановки ПЧ",
221: "остановка: перевод запорной арматуры в исходное состояние",
231: "остановка: работа компрессора",
235: "остановка: сброс конденсата клапанами 25.*"
}
const pumpAlarmRegister = [
"общая авария",
"реле контроля фаз",
"насос",
"ошибка ПЧ",
"датчик потока",
"датчик уровня воды",
"датчик наличия воды",
"задвижки"
]
const vfdErrorsDescription = {
5: "превышение напряжения",
8: "пониженное напряжение",
11: "обрыв фазы питания",
12: "отказ выходной цепи",
15: "перегрев ПЧ",
17: "замыкание двигателя на землю",
19: "двигатель без нагрузки",
34: "перегрузка по току"
}
async function updateStatus() { async function updateStatus() {
let dataset = await loadLastUpdates() let dataset = await loadLastUpdates()
const now = Date.now() / 1000
// последнее обновление // последнее обновление
document.getElementById("tank-last-update").innerHTML = moment(new Date(dataset['tank']['last_update'] * 1000)).format(DATETIME_FORMAT)
document.getElementById("pump-last-update").innerHTML = moment(new Date(dataset['pump']['last_update'] * 1000)).format(DATETIME_FORMAT) // сначала резервуар
let tmp = document.getElementById("tank-last-update")
tmp.innerHTML = moment(new Date(dataset['tank']['last_update'] * 1000)).format(DATETIME_FORMAT)
// для резервуара нормально, если обновление было меньше получаса назад
_setIndicatorClass(tmp, now - dataset['tank']['last_update'] < 1800)
// теперь то же самое, только для насосной
tmp = document.getElementById("pump-last-update")
tmp.innerHTML = moment(new Date(dataset['pump']['last_update'] * 1000)).format(DATETIME_FORMAT)
// для насосной можно допустить последнее обновление 10 минут назад
_setIndicatorClass(tmp, now - dataset['tank']['last_update'] < 600)
//<p>Уровень воды <span id="tank-level-dir"></span></p> //<p>Уровень воды <span id="tank-level-dir"></span></p>
const last_radar_values = dataset['tank']['last_radar_values'] const last_radar_values = dataset['tank']['last_radar_values']
if (last_radar_values.length === 0) { if (last_radar_values.length === 0) {
document.getElementById("tank-level-dir").innerHTML = "(?)"; document.getElementById("tank-level-dir").innerHTML = "(?)"
} else { } else {
document.getElementById("tank-level-dir").innerHTML = approximateWithTimestamps(last_radar_values) < 0 ? '↘' : '↗'; document.getElementById("tank-level-dir").innerHTML = approximateWithTimestamps(last_radar_values) < 0 ? '↘' : '↗'
} }
//<p>Текущий уровень воды: <span id="tank-level-now"></span>%</p> //<p>Текущий уровень воды: <span id="tank-level-now"></span>%</p>
document.getElementById("tank-level-now").innerHTML = dataset['tank']['last_percentage'] tmp = document.getElementById("tank-level-now")
tmp.innerHTML = dataset['tank']['last_percentage']
// тут все хорошо если влезаем в установленные рамки +-2% (69-80%)
_setIndicatorClass(tmp, 67 <= dataset['tank']['last_percentage'] <= 82)
//<p>Текущее значение с радара: <span id="tank-raw-now"></span></p> //<p>Текущее значение с радара: <span id="tank-raw-now"></span></p>
document.getElementById("tank-raw-now").innerHTML = dataset['tank']['last_radar'] document.getElementById("tank-raw-now").innerHTML = dataset['tank']['last_radar']
//<p>Статус: <span id="tank-status"></span></p> //<p>Статус: <span id="tank-status"></span></p>
const shur_status_bits = ['нужна вода', 'поплавок нижний', 'поплавок верхний', 'поплавок аварийный'] const shur_status_bits = ['нужна вода', 'поплавок нижний', 'поплавок верхний', 'поплавок аварийный']
document.getElementById("tank-status").innerHTML = unpackBits(dataset['tank']['status_reg'], shur_status_bits) + " (" + dataset['tank']['status_reg'] + ")"; tmp = document.getElementById("tank-status")
tmp.innerHTML = unpackBits(dataset['tank']['status_reg'], shur_status_bits) + " (" + dataset['tank']['status_reg'] + ")"
// тут все хорошо, пока нет аварийного поплавка
_setIndicatorClass(tmp, (dataset['tank']['status_reg'] & 0x8) === 0)
//<p>Частота ПЧ: <span id="pump-vfd-freq"></span>Гц</p> //<p>Частота ПЧ: <span id="pump-vfd-freq"></span>Гц</p>
document.getElementById("pump-vfd-freq").innerHTML = dataset['pump']['vfd_freq'] document.getElementById("pump-vfd-freq").innerHTML = dataset['pump']['vfd_freq']
@ -122,39 +191,32 @@ async function updateStatus() {
document.getElementById("pump-vfd-curr").innerHTML = dataset['pump']['vfd_curr'] document.getElementById("pump-vfd-curr").innerHTML = dataset['pump']['vfd_curr']
//<p>Ошибка ПЧ: <span id="pump-vfd-error"></span></p> //<p>Ошибка ПЧ: <span id="pump-vfd-error"></span></p>
document.getElementById("pump-vfd-error").innerHTML = dataset['pump']['vfd_err'] tmp = document.getElementById("pump-vfd-error")
if (dataset['pump']['vfd_err'] in vfdErrorsDescription) {
tmp.innerHTML = dataset['pump']['vfd_err'] + " (" + vfdErrorsDescription[dataset['pump']['vfd_err']] + ")"
} else {
tmp.innerHTML = dataset['pump']['vfd_err']
}
_setIndicatorClass(tmp, dataset['pump']['vfd_err'] === 0)
//<p>Регистр аварий: <span id="pump-alarms"></span></p> //<p>Регистр аварий: <span id="pump-alarms"></span></p>
document.getElementById("pump-alarms").innerHTML = dataset['pump']['alarms'] tmp = document.getElementById("pump-alarms")
if (dataset['pump']['alarms'] === 0) {
tmp.innerHTML = "ok"
} else {
tmp.innerHTML = unpackBits(dataset['pump']['alarms'], pumpAlarmRegister) + " (" + dataset['pump']['alarms'] + ")"
}
_setIndicatorClass(tmp, dataset['pump']['alarms'] === 0)
//<p>Состояние КА: <span id="pump-stage"></span></p> //<p>Состояние КА: <span id="pump-stage"></span></p>
document.getElementById("pump-stage").innerHTML = dataset['pump']['pump_stage'] tmp = document.getElementById("pump-stage")
if (dataset['pump']['pump_stage'] in pumpStageDescription) {
tmp.innerHTML = dataset['pump']['pump_stage'] + " (" + pumpStageDescription[dataset['pump']['vfd_err']] + ")"
} else {
tmp.innerHTML = dataset['pump']['pump_stage']
}
//<p>Текущий расход: <span id="pump-flow-meter"></span>м³</p> //<p>Текущий расход: <span id="pump-flow-meter"></span>м³</p>
document.getElementById("pump-flow-meter").innerHTML = dataset['pump']['flow_meter'] document.getElementById("pump-flow-meter").innerHTML = dataset['pump']['flow_meter']
} }
// состояния конченого автомата
// $states = [
// 0 => "отключен",
// 2 => "инициализация: установка задвижек в начальное состояние",
// 21 => "инициализация: закрытие задвижек 23.5 и 23.6",
// 31 => "инициализация: открытие задвижек 23.5 и 23.6",
// 99 => "<span class=\"value-bad\">авария</span>",
//
// 100 => "ожидание сигнала на перекачку воды",
//
// 102 => "запуск: открытие задвижки 23.6",
// 110 => "запуск: ожидание сигнала от датчика уровня поз.36",
// 121 => "запуск: открытие задвижек насоса",
// 131 => "запуск: закрытие задвижки 23.6",
// 141 => "запуск: пуск ПЧ",
//
// 200 => "<span class=\"value-good\">работает</span>",
//
// 202 => "остановка: закрытие задвижек 23.3 и 23.4",
// 211 => "остановка: ожидание остановки ПЧ",
// 221 => "остановка: перевод запорной арматуры в исходное состояние",
// 231 => "остановка: работа компрессора",
// 235 => "остановка: сброс конденсата клапанами 25.*"
// ];

View File

@ -22,15 +22,15 @@
} }
#canvas-wrapper { #canvas-wrapper {
width: 100%; max-width: 1000px;
} }
.value-good { .value-good {
color: green; color: var(--text-good);
} }
.value-bad { .value-bad {
color: red; color: var(--text-bad);
} }
</style> </style>
{% endblock %} {% endblock %}
@ -49,9 +49,6 @@
<div class="content-block"> <div class="content-block">
<h2>Состояние насосной станции</h2> <h2>Состояние насосной станции</h2>
<p>Последнее обновление: <span id="pump-last-update"></span></p> <p>Последнее обновление: <span id="pump-last-update"></span></p>
<p>Частота ПЧ: <span id="pump-vfd-freq"></span>Гц</p>
<p>Ток ПЧ: <span id="pump-vfd-curr"></span>А</p>
<p>Ошибка ПЧ: <span id="pump-vfd-error"></span></p>
<p>Регистр аварий: <span id="pump-alarms"></span></p> <p>Регистр аварий: <span id="pump-alarms"></span></p>
<p>Состояние КА: <span id="pump-stage"></span></p> <p>Состояние КА: <span id="pump-stage"></span></p>
<p>Текущий расход: <span id="pump-flow-meter"></span>м³</p> <p>Текущий расход: <span id="pump-flow-meter"></span>м³</p>
@ -60,24 +57,36 @@
<h2>Состояние резервуара</h2> <h2>Состояние резервуара</h2>
<p>Последнее обновление: <span id="tank-last-update"></span></p> <p>Последнее обновление: <span id="tank-last-update"></span></p>
<p>Текущий уровень воды: <span id="tank-level-now"></span>%</p> <p>Текущий уровень воды: <span id="tank-level-now"></span>%</p>
<p>Текущее значение с радара: <span id="tank-raw-now"></span></p>
<p>Уровень воды <span id="tank-level-dir"></span></p> <p>Уровень воды <span id="tank-level-dir"></span></p>
<p>Статус: <span id="tank-status"></span></p> <p>Статус: <span id="tank-status"></span></p>
</div> </div>
</div> </div>
<div class="content-block chart"> <div class="content-block chart">
<h2> Уровень воды в резервуаре, %</h2>
<div id="canvas-wrapper"><canvas id="water_level"></canvas></div> <div id="canvas-wrapper"><canvas id="water_level"></canvas></div>
</div> </div>
<div class="content-block">
<h2>Прочие показатели</h2>
<p>Частота ПЧ: <span id="pump-vfd-freq"></span>Гц</p>
<p>Ток ПЧ: <span id="pump-vfd-curr"></span>А</p>
<p>Ошибка ПЧ: <span id="pump-vfd-error"></span></p>
<p>Текущее значение с радара: <span id="tank-raw-now"></span></p>
</div>
<div class="content-block">
<h2>Настройки</h2>
<div>
<label for="settings-chart-time">Период на графике (дней): </label>
<input id="settings-chart-time" type="number" min="1" max="31">
</div>
</div>
</div> </div>
<script> <script>
moment.locale('ru') moment.locale('ru')
let bodyStyles = window.getComputedStyle(document.body); const bodyStyles = window.getComputedStyle(document.body);
const chart_dataset = { const chart_dataset = {
label: "Резервуар", label: "Резервуар, уровень в %",
color: bodyStyles.getPropertyValue('--brand-text'), color: bodyStyles.getPropertyValue('--brand-text'),
data: [], data: [],
pointRadius: 0 pointRadius: 0
@ -94,7 +103,7 @@
responsive: true, responsive: true,
legend: { display: false }, legend: { display: false },
scales: { scales: {
y: { min: 0, max: 100 }, // y: { min: 0, max: 100 }, // раскомментировать, если хочется шкалу от 0 до 100, а так она будет автоматической
x: { x: {
type: 'time', type: 'time',
time: { time: {
@ -109,6 +118,19 @@
}); });
document.addEventListener("DOMContentLoaded", (event) => { document.addEventListener("DOMContentLoaded", (event) => {
// получаем элементы настроек
const settingsChartTime = document.getElementById('settings-chart-time')
// устанавливаем поле settings-chart-time
let chartTime = localStorage.getItem("settings-chart-time")
if (chartTime === null) {
// по умолчанию 7 дней
localStorage.setItem('settings-chart-time', '7')
settingsChartTime.value = 7
} else {
settingsChartTime.value = chartTime
}
// запуск обновления статистики // запуск обновления статистики
updateStatus().then(() => { updateStatus().then(() => {
setInterval(() => { setInterval(() => {
@ -123,15 +145,28 @@
chart.update(); chart.update();
} }
let chartUpdateTimer = null
loadChartData().then((data) => { loadChartData().then((data) => {
updateChart(data) updateChart(data)
setInterval(() => { // интервал обновления графика - 1% показываемого времени (2 недели)
const RELOAD_CHART_INTERVAL = (3600 * 1000 * 24 * parseInt(chartTime)) / 100
chartUpdateTimer = setInterval(() => {
loadChartData().then((d) => { loadChartData().then((d) => {
updateChart(d) updateChart(d)
}) })
}, RELOAD_CHART_INTERVAL) }, RELOAD_CHART_INTERVAL)
}) })
settingsChartTime.onchange = (event) => {
chartTime = settingsChartTime.value
localStorage.setItem('settings-chart-time', chartTime)
// TODO сделать динамическое обновление графика без перезагрузки страницы
window.location.reload()
};
}); });
</script> </script>
{% endblock %} {% endblock %}