diff --git a/logs_service/apps.py b/logs_service/apps.py index d1829b5..fda7dbe 100644 --- a/logs_service/apps.py +++ b/logs_service/apps.py @@ -19,14 +19,15 @@ class LogsServiceConfig(AppConfig): # защита от двойного запуска if os.environ.get('RUN_MAIN', None) == 'true': return + print(f"Running logger service in process with pid={os.getpid()}") from .services import MbTankService, MbPumpService # включение/выключение сервисов mb_tank_service_enable = os.getenv('ENABLE_MB_TANK_SERVICE', '1') - LogsServiceConfig.mb_tank_service_enable = int(mb_tank_service_enable) + LogsServiceConfig.mb_tank_service_enable = int(mb_tank_service_enable) != 0 mb_pump_service_enable = os.getenv('ENABLE_MB_PUMP_SERVICE', '1') - LogsServiceConfig.mb_pump_service_enable = int(mb_pump_service_enable) + LogsServiceConfig.mb_pump_service_enable = int(mb_pump_service_enable) != 0 save_days = int(os.getenv('LOGGER_SAVE_DAYS')) diff --git a/logs_service/services.py b/logs_service/services.py index 9dc04aa..ea3713b 100644 --- a/logs_service/services.py +++ b/logs_service/services.py @@ -21,7 +21,7 @@ class MbClearHistoryService(Thread): class MbService(Thread): def __init__(self, ip_addr, port, scan_rate, poll_time_ms=60000, save_days=60): - super().__init__() + super().__init__(daemon=True) self._poll_time_ms = poll_time_ms self._scan_rate = scan_rate @@ -107,6 +107,8 @@ class MbTankService(MbService): # D1: radar_high_reg # D2: radar_low_reg values = self.mb.read_holding_registers(0, 6) + if values is None: + return self._curr_state = { "level": values[0], "status": values[5], @@ -148,10 +150,12 @@ class MbPumpService(MbService): # D26: vfd_current # D27: vfd_error off = 16 - values = self.mb.read_holding_registers(16, 35 - off) + values = self.mb.read_holding_registers(16, 36 - off) + if values is None: + return with self._lock: # определение запущенного насоса - pr = None + pr = -1 if values[33 - off] & (1 << 14): pr = 1 elif values[33 - off] & (1 << 15): @@ -159,15 +163,15 @@ class MbPumpService(MbService): self._curr_state = { 'alarms': values[35 - off], - 'flow_meter': values[16 - off], + 'flow_meter': values[16 - off] / 100, 'last_update': int(datetime.now().timestamp()), 'pump_stage': values[28 - off], - 'vfd_curr': values[26 - off], + 'vfd_curr': values[26 - off] / 100, 'vfd_err': values[27 - off], - 'vfd_freq': values[25 - off], + 'vfd_freq': values[25 - off] / 100, 'pump_running': pr, - 'pump_moto_watch_1': values[31 - off], - 'pump_moto_watch_2': values[32 - off], + 'moto_watch_1': values[31 - off], + 'moto_watch_2': values[32 - off], 'half_auto_control': values[30 - off] } @@ -186,8 +190,8 @@ class MbPumpService(MbService): 'vfd_err': None, 'vfd_freq': None, 'pump_running': None, - 'pump_moto_watch_1': None, - 'pump_moto_watch_2': None, + 'moto_watch_1': None, + 'moto_watch_2': None, 'half_auto_control': None } diff --git a/logs_service/views.py b/logs_service/views.py index c6bd6b3..cf8a931 100644 --- a/logs_service/views.py +++ b/logs_service/views.py @@ -1,4 +1,5 @@ import json +import os from datetime import datetime, timedelta from django.http import HttpResponse, HttpResponseBadRequest from django.contrib.auth.decorators import permission_required @@ -8,11 +9,12 @@ from .apps import LogsServiceConfig @permission_required(perm='logs_service.view_mbtankrecord', raise_exception=True) def view_stats(request): + print(f"Executing view_stats in process with pid={os.getpid()}") tank = MbTankRecord.load_stats() - pump = None + pump = {} if request.user.has_perm('logs_service.view_pump_stats'): - if LogsServiceConfig.mb_tank_service is not None and LogsServiceConfig.mb_tank_service_enable: - pump = LogsServiceConfig.mb_tank_service.get_stats() + if LogsServiceConfig.mb_pump_service is not None and LogsServiceConfig.mb_pump_service_enable: + pump = LogsServiceConfig.mb_pump_service.get_stats() out = { 'stats': { diff --git a/static/js/index-main.js b/static/js/index-main.js index 35f3868..d3f7ffd 100644 --- a/static/js/index-main.js +++ b/static/js/index-main.js @@ -5,6 +5,8 @@ const RELOAD_STATS_INTERVAL = 10000 const DATETIME_FORMAT = "DD MMM YYYY HH:mm:ss" +const UNDEFINED_FIELD_TEXT = '(?)' + function unpackBits(num, desc) { let out = "" for (let i = 0; i < desc.length; i++) { @@ -90,154 +92,257 @@ async function loadLastUpdates() { * @private */ function _setIndicatorClass(element, good) { - if (good) { - element.classList.remove("value-bad") - element.classList.add("value-good") - } else { + if (good === null) { element.classList.remove("value-good") - element.classList.add("value-bad") + element.classList.remove("value-bad") + } else if (good !== undefined) { + if (good) { + element.classList.remove("value-bad") + element.classList.add("value-good") + } else { + element.classList.remove("value-good") + element.classList.add("value-bad") + } } } const updateFunctions = { // последнее обновление резервуара - 'tank-last-update': (element, dataset) => { - const now = Date.now() / 1000 - element.innerHTML = moment(new Date(dataset['tank']['last_update'] * 1000)).format(DATETIME_FORMAT) - // для резервуара нормально, если обновление было меньше получаса назад - _setIndicatorClass(element, now - dataset['tank']['last_update'] < 1800) + 'tank-last-update': { + 'get_val': (dataset) => { return dataset['tank']['last_update'] }, + 'handler': (element, value) => { + const now = Date.now() / 1000 + element.innerHTML = moment(new Date(value * 1000)).format(DATETIME_FORMAT) + // для резервуара нормально, если обновление было меньше получаса назад + return now - value < 1800 + } }, // последнее обновление насосной - 'pump-last-update': (element, dataset) => { - const now = Date.now() / 1000 - element.innerHTML = moment(new Date(dataset['pump']['last_update'] * 1000)).format(DATETIME_FORMAT) - // для насосной можно допустить последнее обновление 10 минут назад - _setIndicatorClass(element, now - dataset['pump']['last_update'] < 600) + 'pump-last-update': { + 'get_val': (dataset) => { return dataset['pump']['last_update'] }, + 'handler': (element, value) => { + const now = Date.now() / 1000 + element.innerHTML = moment(new Date(value * 1000)).format(DATETIME_FORMAT) + // для насосной можно допустить последнее обновление 10 минут назад + return now - value < 600 + } }, // Уровень воды - 'tank-level-dir': (element, dataset) => { - const last_radar_values = dataset['tank']['last_radar_values'] - if (last_radar_values.length < 3) { - element.innerHTML = "(?)" - } else { - let ap = approximateWithTimestamps(last_radar_values) - if (Math.abs(ap) < 0.02) { - element.innerHTML = '→' + 'tank-level-dir': { + 'get_val': (dataset) => { return dataset['tank']['last_radar_values'] }, + 'handler': (element, value) => { + if (value.length < 3) { + element.innerHTML = UNDEFINED_FIELD_TEXT } else { - element.innerHTML = ap < 0 ? '↘' : '↗' + let ap = approximateWithTimestamps(value) + if (Math.abs(ap) < 0.02) { + element.innerHTML = '→' + } else { + element.innerHTML = ap < 0 ? '↘' : '↗' + } } + return null } }, // Текущий уровень воды: % - 'tank-level-now': (element, dataset) => { - element.innerHTML = dataset['tank']['level'] - // тут все хорошо если влезаем в установленные рамки +-2% (69-80%) - _setIndicatorClass(element, 67 <= dataset['tank']['last_percentage'] <= 82) + 'tank-level-now': { + 'get_val': (dataset) => { return dataset['tank']['level'] }, + 'handler': (element, value) => { + element.innerHTML = value + // тут все хорошо если влезаем в установленные рамки +-2% (69-80%) + return 67 <= value <= 82 + } }, // Текущее значение с радара - 'tank-raw-now': (element, dataset) => { - element.innerHTML = dataset['tank']['radar'] + 'tank-raw-now': { + 'get_val': (dataset) => { return dataset['tank']['radar'] }, + 'handler': (element, value) => { + element.innerHTML = value + return null + } }, // Статус: - 'tank-status': (element, dataset) => { - const shur_status_bits = ['нужна вода', 'поплавок нижний', 'поплавок верхний', 'поплавок аварийный'] - element.innerHTML = unpackBits(dataset['tank']['status'], shur_status_bits) + " (" + dataset['tank']['status'] + ")" - // тут все хорошо, пока нет аварийного поплавка - _setIndicatorClass(element, (dataset['tank']['status'] & 0x8) === 0) + 'tank-status': { + 'get_val': (dataset) => { return dataset['tank']['status'] }, + 'handler': (element, value) => { + const shur_status_bits = ['нужна вода', 'поплавок нижний', 'поплавок верхний', 'поплавок аварийный'] + element.innerHTML = unpackBits(value, shur_status_bits) + " (" + value + ")" + // тут все хорошо, пока нет аварийного поплавка + return (value & 0x8) === 0 + } }, // Частота ПЧ: Гц - 'pump-vfd-freq': (element, dataset) => { - element.innerHTML = dataset['pump']['vfd_freq'] + 'pump-vfd-freq': { + 'get_val': (dataset) => { return dataset['pump']['vfd_freq'] }, + 'handler': (element, value) => { + element.innerHTML = value + return null + } }, // Ток ПЧ: А - 'pump-vfd-curr': (element, dataset) => { - document.getElementById("pump-vfd-curr").innerHTML = dataset['pump']['vfd_curr'] + 'pump-vfd-curr': { + 'get_val': (dataset) => { return dataset['pump']['vfd_curr'] }, + 'handler': (element, value) => { + element.innerHTML = value + return null + } }, // Ошибка ПЧ: - 'pump-vfd-error': (element, dataset) => { - const vfdErrorsDescription = { - 5: "превышение напряжения", - 8: "пониженное напряжение", - 11: "обрыв фазы питания", - 12: "отказ выходной цепи", - 15: "перегрев ПЧ", - 17: "замыкание двигателя на землю", - 19: "двигатель без нагрузки", - 34: "перегрузка по току" - } + 'pump-vfd-error': { + 'get_val': (dataset) => { return dataset['pump']['vfd_err'] }, + 'handler': (element, value) => { + const vfdErrorsDescription = { + 5: "превышение напряжения", + 8: "пониженное напряжение", + 11: "обрыв фазы питания", + 12: "отказ выходной цепи", + 15: "перегрев ПЧ", + 17: "замыкание двигателя на землю", + 19: "двигатель без нагрузки", + 34: "перегрузка по току" + } - if (dataset['pump']['vfd_err'] in vfdErrorsDescription) { - element.innerHTML = dataset['pump']['vfd_err'] + " (" + vfdErrorsDescription[dataset['pump']['vfd_err']] + ")" - } else { - element.innerHTML = dataset['pump']['vfd_err'] + if (value in vfdErrorsDescription) { + element.innerHTML = value + " (" + vfdErrorsDescription[value] + ")" + } else { + element.innerHTML = value + } + return value === 0 } - _setIndicatorClass(element, dataset['pump']['vfd_err'] === 0) }, // Регистр аварий: - 'pump-alarms': (element, dataset) => { - const pumpAlarmRegister = [ - "общая авария", - "реле контроля фаз", - "насос", - "ошибка ПЧ", - "датчик потока", - "датчик уровня воды", - "датчик наличия воды", - "задвижки" - ] + 'pump-alarms': { + 'get_val': (dataset) => { return dataset['pump']['alarms'] }, + 'handler': (element, value) => { + const pumpAlarmRegister = [ + "общая авария", + "реле контроля фаз", + "насос", + "ошибка ПЧ", + "датчик потока", + "датчик уровня воды", + "датчик наличия воды", + "задвижки" + ] - if (dataset['pump']['alarms'] === 0) { - element.innerHTML = "ok" - } else { - element.innerHTML = unpackBits(dataset['pump']['alarms'], pumpAlarmRegister) + " (" + dataset['pump']['alarms'] + ")" + if (value === 0) { + element.innerHTML = "ok" + } else { + element.innerHTML = unpackBits(value, pumpAlarmRegister) + " (" + value + ")" + } + return value === 0 } - _setIndicatorClass(element, dataset['pump']['alarms'] === 0) }, // Состояние КА: - 'pump-stage': (element, dataset) => { - const pumpStageDescription = { - 0: "отключен", - 2: "инициализация: установка задвижек в начальное состояние", - 21: "инициализация: закрытие задвижек 23.5 и 23.6", - 31: "инициализация: открытие задвижек 23.5 и 23.6", - 99: "авария", - 100: "ожидание сигнала на перекачку воды", - 102: "запуск: открытие задвижки 23.6", - 110: "запуск: ожидание сигнала от датчика уровня поз.36", - 121: "запуск: открытие задвижек насоса", - 131: "запуск: закрытие задвижки 23.6", - 141: "запуск: пуск ПЧ", - 200: "работает", - 202: "остановка: закрытие задвижек 23.3 и 23.4", - 211: "остановка: ожидание остановки ПЧ", - 221: "остановка: перевод запорной арматуры в исходное состояние", - 231: "остановка: работа компрессора", - 235: "остановка: сброс конденсата клапанами 25.*" - } + 'pump-stage': { + 'get_val': (dataset) => { return dataset['pump']['pump_stage'] }, + 'handler': (element, value) => { + const pumpStageDescription = { + 0: "отключен", + 2: "инициализация: установка задвижек в начальное состояние", + 21: "инициализация: закрытие задвижек 23.5 и 23.6", + 31: "инициализация: открытие задвижек 23.5 и 23.6", + 99: "авария", + 100: "ожидание сигнала на перекачку воды", + 102: "запуск: открытие задвижки 23.6", + 110: "запуск: ожидание сигнала от датчика уровня поз.36", + 121: "запуск: открытие задвижек насоса", + 131: "запуск: закрытие задвижки 23.6", + 141: "запуск: пуск ПЧ", + 200: "работает", + 202: "остановка: закрытие задвижек 23.3 и 23.4", + 211: "остановка: ожидание остановки ПЧ", + 221: "остановка: перевод запорной арматуры в исходное состояние", + 231: "остановка: работа компрессора", + 235: "остановка: сброс конденсата клапанами 25.*" + } - if (dataset['pump']['pump_stage'] in pumpStageDescription) { - element.innerHTML = dataset['pump']['pump_stage'] + " (" + pumpStageDescription[dataset['pump']['pump_stage']] + ")" - } else { - element.innerHTML = dataset['pump']['pump_stage'] + if (value in pumpStageDescription) { + element.innerHTML = value + " (" + pumpStageDescription[value] + ")" + } else { + element.innerHTML = value + } + + if (value === 99) { + return false + } else if (value === 200) { + return true + } else { + return null + } } }, // Текущий расход: м³ - 'pump-flow-meter': (element, dataset) => { - element.innerHTML = dataset['pump']['flow_meter'] + 'pump-flow-meter': { + 'get_val': (dataset) => { return dataset['pump']['flow_meter'] }, + 'handler': (element, value) => { + element.innerHTML = value + return null + } }, - // 'id': (element, dataset) => {}, + // Запущенный насос: + 'pump-running': { + 'get_val': (dataset) => { return dataset['pump']['pump_running'] }, + 'handler': (element, value) => { + if (value <= -1) { + element.innerHTML = '-' + } else { + element.innerHTML = value + } + return null + } + }, + + // Моточасы насоса 1: + 'pump-moto-watch-1': { + 'get_val': (dataset) => { return dataset['pump']['moto_watch_1'] }, + 'handler': (element, value) => { + element.innerHTML = value + return null + } + }, + + // Моточасы насоса 2: + 'pump-moto-watch-2': { + 'get_val': (dataset) => { return dataset['pump']['moto_watch_2'] }, + 'handler': (element, value) => { + element.innerHTML = value + return null + } + }, + + // Дистанционный режим: + 'pump-remote-mode': { + 'get_val': (dataset) => { return dataset['pump']['half_auto_control'] }, + 'handler': (element, value) => { + if ((value & (1 << 2)) === 0) { + element.innerHTML = 'не используется' + } else { + element.innerHTML = 'насос ' + (((value & 1) === 0) ? '1 ' : '2 ') + (((value & (1 << 1)) !== 0) ? 'включен' : 'выключен') + } + return null + } + }, + + // 'id': { + // 'get_val': (dataset) => { return dataset['...']['...'] }, + // 'handler': (element, value) => { + // element.innerHTML = value + // return null + // } + // }, } async function updateStatus() { @@ -246,7 +351,17 @@ async function updateStatus() { for (let id in updateFunctions) { let element = document.getElementById(id) if (element !== null) { - updateFunctions[id](element, dataset) + try { + const value = updateFunctions[id]['get_val'](dataset) + if (value === undefined || value === null) { + element.innerHTML = UNDEFINED_FIELD_TEXT + _setIndicatorClass(element, false) + } else { + _setIndicatorClass(element, updateFunctions[id]['handler'](element, value)) + } + } catch (e) { + console.log(e) + } } } } diff --git a/templates/index.html b/templates/index.html index 4d10d91..43b0eb8 100644 --- a/templates/index.html +++ b/templates/index.html @@ -79,9 +79,10 @@

Текущее значение с радара:

-

Запущенный насос: NI

-

Моточасы насоса 1: NI

-

Моточасы насоса 2: NI

+

Запущенный насос:

+

Моточасы насоса 1:

+

Моточасы насоса 2:

+

Дистанционный режим: