ospaz-site/static/js/index-main.js

233 lines
9.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// скрипт страницы index, на которой отображаются графики
// интервал обновления статуса в миллисекундах
const RELOAD_STATS_INTERVAL = 10000
const DATETIME_FORMAT = "DD MMM YYYY HH:mm:ss"
function unpackBits(num, desc) {
let out = ""
for (let i = 0; i < desc.length; i++) {
if (desc[i] !== null) {
if ((num & (1 << i)) !== 0) {
// бит установлен
if (out.length === 0) {
out = desc[i]
} else {
out += " + "
out += desc[i]
}
}
}
}
return out
}
/**
* Функция для линейной аппроксимации набора точек. Для заданных точек находит коэфицент a из уравнения y=ax+b.
* Уравнение подбирается при помощи метода наименьших квадратов.
* @param dataset набор точек в виде [[timestamp, val], [timestamp, val], ...], где timestamp - значение по X, val - значение по Y
* @returns {number}, коэфицент a
*/
function approximateWithTimestamps(dataset) {
// для точных расчетов нужно сместить timestamp
const timestamp_offset = dataset[0][0]
// сумма (x[i] * y[i])
let sum_x_mul_y = 0
for (let i = 0; i < dataset.length; i++) {
sum_x_mul_y += (dataset[i][0] - timestamp_offset) * dataset[i][1]
}
// сумма всех x[i]
let sum_x = 0;
for (let i = 0; i < dataset.length; i++) {
sum_x += dataset[i][0] - timestamp_offset
}
// сумма всех y[i]
let sum_y = 0;
for (let i = 0; i < dataset.length; i++) {
sum_y += dataset[i][1]
}
// сумма всех x[i]^2
let sum_x_mul_x = 0;
for (let i = 0; i < dataset.length; i++) {
sum_x_mul_x += Math.pow(dataset[i][0] - timestamp_offset, 2)
}
// вычисление коэфицента a из формулы y=ax+b
// нам нужен только он
return (dataset.length * sum_x_mul_y - sum_x * sum_y) / (sum_x_mul_x - Math.pow(sum_x, 2))
}
async function makeRequest(url) {
let response = await fetch(url)
if (response.status === 403) {
// http Forbidden, исправляется перезагрузкой страницы и просмотром окошка "Требуется авторизация"
window.location.reload()
return {}
} else if (response.status !== 200) {
console.log('fetch(' + url + ') failed. Status Code: ' + response.status);
return null;
}
return await response.json()
}
async function loadChartData() {
let chartTime = localStorage.getItem("settings-chart-time")
return (await makeRequest('/fetch/tank-chart?days=' + chartTime))['tank_chart']
}
async function loadLastUpdates() {
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() {
let dataset = await loadLastUpdates()
const now = Date.now() / 1000
// последнее обновление
// сначала резервуар
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>
const last_radar_values = dataset['tank']['last_radar_values']
if (last_radar_values.length === 0) {
document.getElementById("tank-level-dir").innerHTML = "(?)"
} else {
let ap = approximateWithTimestamps(last_radar_values)
if (Math.abs(ap) < 0.02) {
document.getElementById("tank-level-dir").innerHTML = '→'
} else {
document.getElementById("tank-level-dir").innerHTML = ap < 0 ? '↘' : '↗'
}
}
//<p>Текущий уровень воды: <span id="tank-level-now"></span>%</p>
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>
document.getElementById("tank-raw-now").innerHTML = dataset['tank']['last_radar']
//<p>Статус: <span id="tank-status"></span></p>
const shur_status_bits = ['нужна вода', 'поплавок нижний', 'поплавок верхний', 'поплавок аварийный']
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>
document.getElementById("pump-vfd-freq").innerHTML = dataset['pump']['vfd_freq']
//<p>Ток ПЧ: <span id="pump-vfd-curr"></span>А</p>
document.getElementById("pump-vfd-curr").innerHTML = dataset['pump']['vfd_curr']
//<p>Ошибка ПЧ: <span id="pump-vfd-error"></span></p>
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>
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>
tmp = document.getElementById("pump-stage")
if (dataset['pump']['pump_stage'] in pumpStageDescription) {
tmp.innerHTML = dataset['pump']['pump_stage'] + " (" + pumpStageDescription[dataset['pump']['pump_stage']] + ")"
} else {
tmp.innerHTML = dataset['pump']['pump_stage']
}
//<p>Текущий расход: <span id="pump-flow-meter"></span>м³</p>
document.getElementById("pump-flow-meter").innerHTML = dataset['pump']['flow_meter']
}