условно работающий логгинг резервуара
This commit is contained in:
parent
a5a77b864c
commit
8a7b15f093
@ -13,4 +13,14 @@ DB_PORT=5432
|
|||||||
DJANGO_SECRET="django-secure"
|
DJANGO_SECRET="django-secure"
|
||||||
|
|
||||||
# включение/отключение сервисов ModBus
|
# включение/отключение сервисов ModBus
|
||||||
ENABLE_MB_SERVICES=1
|
ENABLE_MB_TANK_SERVICE=1
|
||||||
|
MB_TANK_ADDRESS="10.8.105.3"
|
||||||
|
MB_TANK_PORT=504
|
||||||
|
MB_TANK_SCAN_RATE_MS=60000
|
||||||
|
|
||||||
|
ENABLE_MB_PUMP_SERVICE=1
|
||||||
|
MB_PUMP_ADDRESS="10.8.105.2"
|
||||||
|
MB_PUMP_PORT=503
|
||||||
|
MB_PUMP_SCAN_RATE_MS=5000
|
||||||
|
|
||||||
|
LOGGER_SAVE_DAYS=60
|
||||||
|
5
logs_service/admin.py
Normal file
5
logs_service/admin.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
from .models import MbPumpRecord, MbTankRecord
|
||||||
|
|
||||||
|
admin.site.register(MbPumpRecord)
|
||||||
|
admin.site.register(MbTankRecord)
|
@ -1,14 +1,58 @@
|
|||||||
|
import os
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
from .services import MbTankService, MbPumpService
|
from ospaz_site.settings import DEBUG
|
||||||
|
|
||||||
|
_is_ready = False
|
||||||
|
|
||||||
class LogsServiceConfig(AppConfig):
|
class LogsServiceConfig(AppConfig):
|
||||||
default_auto_field = 'django.db.models.BigAutoField'
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
name = 'logs_service'
|
name = 'logs_service'
|
||||||
|
|
||||||
mb_tank_service = MbTankService('10.105.0.3')
|
mb_tank_service = None
|
||||||
mb_pump_service = MbPumpService('10.105.0.2')
|
mb_pump_service = None
|
||||||
|
|
||||||
|
mb_tank_service_enable = True
|
||||||
|
mb_pump_service_enable = True
|
||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
LogsServiceConfig.mb_tank_service.start()
|
# защита от двойного запуска
|
||||||
LogsServiceConfig.mb_pump_service.start()
|
if os.environ.get('RUN_MAIN', None) == 'true':
|
||||||
|
return
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
mb_pump_service_enable = os.getenv('ENABLE_MB_PUMP_SERVICE', '1')
|
||||||
|
LogsServiceConfig.mb_pump_service_enable = int(mb_pump_service_enable)
|
||||||
|
|
||||||
|
save_days = int(os.getenv('LOGGER_SAVE_DAYS'))
|
||||||
|
|
||||||
|
# создание сервисов
|
||||||
|
if LogsServiceConfig.mb_tank_service_enable:
|
||||||
|
LogsServiceConfig.mb_tank_service = MbTankService(ip_addr=os.getenv('MB_TANK_ADDRESS'),
|
||||||
|
port=int(os.getenv('MB_TANK_PORT')),
|
||||||
|
scan_rate=int(os.getenv('MB_TANK_SCAN_RATE_MS')),
|
||||||
|
save_days=save_days)
|
||||||
|
|
||||||
|
if LogsServiceConfig.mb_pump_service_enable:
|
||||||
|
LogsServiceConfig.mb_pump_service = MbPumpService(ip_addr=os.getenv('MB_PUMP_ADDRESS'),
|
||||||
|
port=int(os.getenv('MB_PUMP_PORT')),
|
||||||
|
scan_rate=int(os.getenv('MB_PUMP_SCAN_RATE_MS')),
|
||||||
|
save_days=save_days)
|
||||||
|
|
||||||
|
# запуск сервисов
|
||||||
|
if LogsServiceConfig.mb_tank_service_enable:
|
||||||
|
if DEBUG:
|
||||||
|
print('Creating service MbTankService...')
|
||||||
|
LogsServiceConfig.mb_tank_service.start()
|
||||||
|
|
||||||
|
if LogsServiceConfig.mb_pump_service_enable:
|
||||||
|
if DEBUG:
|
||||||
|
print('Creating service MbPumpService...')
|
||||||
|
LogsServiceConfig.mb_pump_service.start()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ class MbTankRecord(models.Model):
|
|||||||
dt = models.DateTimeField(verbose_name="Время записи", default=timezone.now)
|
dt = models.DateTimeField(verbose_name="Время записи", default=timezone.now)
|
||||||
radar_raw = models.PositiveIntegerField(verbose_name="Уровень воды по радару радара")
|
radar_raw = models.PositiveIntegerField(verbose_name="Уровень воды по радару радара")
|
||||||
level = models.PositiveSmallIntegerField(verbose_name="Уровень воды в % (пересчитан ПЛК)")
|
level = models.PositiveSmallIntegerField(verbose_name="Уровень воды в % (пересчитан ПЛК)")
|
||||||
|
status = models.PositiveSmallIntegerField(verbose_name="Регистр статуса")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"({self.id}) {self.dt}: radar={self.radar_raw}, level={self.level}"
|
return f"({self.id}) {self.dt}: radar={self.radar_raw}, level={self.level}"
|
||||||
|
@ -1,21 +1,173 @@
|
|||||||
from .models import *
|
from .models import *
|
||||||
|
from ospaz_site.settings import DEBUG
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
import time
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from pyModbusTCP.client import ModbusClient
|
||||||
|
|
||||||
|
|
||||||
class MbService(Thread):
|
class MbService(Thread):
|
||||||
def __init__(self, ip_addr):
|
def __init__(self, ip_addr, port, scan_rate, poll_time_ms=60000, save_days=60):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._ip_addr = ip_addr
|
self._poll_time_ms = poll_time_ms
|
||||||
|
self._scan_rate = scan_rate
|
||||||
|
|
||||||
|
self.mb = ModbusClient(host=ip_addr, port=port, debug=DEBUG, auto_open=True)
|
||||||
|
|
||||||
|
def _init_state(self):
|
||||||
|
"""
|
||||||
|
Функция вызывается один раз при создании сервиса, в ней нужно загрузить содержимое регистров из базы данных.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _push_current_state(self):
|
||||||
|
"""
|
||||||
|
Функция вызывается в момент, когда требуется сохранить состояние в базу данных
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _load_current_state(self):
|
||||||
|
"""
|
||||||
|
Функция должна совершить операцию чтения регистров modbus
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _check_need_save(self):
|
||||||
|
"""
|
||||||
|
Функция должна вернуть результат: нужно ли сохранять текущее состояние в базу данных
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
# if self.log_type == 'on-change':
|
||||||
|
# pass
|
||||||
|
# else:
|
||||||
|
print(f"[MbService:{self.__class__.name}] service started!")
|
||||||
|
self._init_state()
|
||||||
|
|
||||||
|
last_query = datetime.now()
|
||||||
|
scan_rate = timedelta(milliseconds=self._scan_rate)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
self._load_current_state()
|
||||||
|
if self._check_need_save():
|
||||||
|
self._push_current_state()
|
||||||
|
except Exception as ex:
|
||||||
|
print(f"[MbService:{self.__class__.name}] Exception: {ex}")
|
||||||
|
|
||||||
|
# вычислим время до следующего опроса и подождем
|
||||||
|
# время следующего опроса
|
||||||
|
need_time = last_query + scan_rate
|
||||||
|
curr_time = datetime.now()
|
||||||
|
if need_time > curr_time:
|
||||||
|
delta = need_time - curr_time
|
||||||
|
time.sleep(delta.seconds + (delta.microseconds / 1000000))
|
||||||
|
last_query = need_time
|
||||||
|
else:
|
||||||
|
last_query = datetime.now()
|
||||||
|
|
||||||
def to_json(self):
|
def to_json(self):
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
class MbTankService(MbService):
|
class MbClearHistoryService(Thread):
|
||||||
|
def __init__(self, model, save_days):
|
||||||
|
super().__init__()
|
||||||
|
self.model = model
|
||||||
|
self._days = save_days
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
pass
|
while True:
|
||||||
|
self.model.objects.filter(dt__lt=(timezone.now() - timezone.timedelta(days=60))).delete()
|
||||||
|
time.sleep(10)
|
||||||
|
|
||||||
|
|
||||||
|
class MbTankService(MbService):
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.registers = {}
|
||||||
|
# {
|
||||||
|
# 'last_update': None, # последнее обновление, unix time
|
||||||
|
# 'level': None, # текущее показание, пересчитанное в %
|
||||||
|
# 'radar': None, # текущее показание с радара
|
||||||
|
# 'status': None, # статусный регистр
|
||||||
|
# 'last_radar_values': [] # последние показания за 15 минут
|
||||||
|
# }
|
||||||
|
|
||||||
|
self._curr_state = None
|
||||||
|
# {
|
||||||
|
# "level": int,
|
||||||
|
# "status": int,
|
||||||
|
# "radar": int
|
||||||
|
# }
|
||||||
|
|
||||||
|
# для фильтрации уровня, чтобы его в бд писать
|
||||||
|
self.level_filter = []
|
||||||
|
|
||||||
|
def _update_last_radar_values(self):
|
||||||
|
res = [[item.dt.timestamp(), item.radar_raw]
|
||||||
|
for item in MbTankRecord.objects.filter(dt__gt=(datetime.now() - timedelta(minutes=15))).order_by('-dt')]
|
||||||
|
self.registers['last_radar_values'] = res
|
||||||
|
|
||||||
|
def _init_state(self):
|
||||||
|
self.last_save = MbTankRecord.objects.order_by('-dt').first()
|
||||||
|
print(f"Initializing MbTankService: last_save={self.last_save}")
|
||||||
|
if self.last_save is not None:
|
||||||
|
self.registers['last_update'] = self.last_save.dt
|
||||||
|
self.registers['level'] = self.last_save.level
|
||||||
|
self.registers['radar'] = self.last_save.radar_raw
|
||||||
|
self.registers['status'] = self.last_save.status
|
||||||
|
self._update_last_radar_values()
|
||||||
|
|
||||||
|
def _push_current_state(self):
|
||||||
|
record = MbTankRecord(radar_raw=self._curr_state['radar'],
|
||||||
|
level=self._curr_state['level'],
|
||||||
|
status=self._curr_state['status'])
|
||||||
|
record.save()
|
||||||
|
|
||||||
|
def _load_current_state(self):
|
||||||
|
# D0: level_percentage
|
||||||
|
# D5: status_reg
|
||||||
|
# D1: radar_high_reg
|
||||||
|
# D2: radar_low_reg
|
||||||
|
values = self.mb.read_holding_registers(0, 6)
|
||||||
|
self._curr_state = {
|
||||||
|
"level": values[0],
|
||||||
|
"status": values[5],
|
||||||
|
"radar": (values[1] << 16) | values[2]
|
||||||
|
}
|
||||||
|
self.registers['last_update'] = datetime.now()
|
||||||
|
|
||||||
|
def _check_need_save(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def to_json(self):
|
||||||
|
return self.registers
|
||||||
|
|
||||||
|
|
||||||
class MbPumpService(MbService):
|
class MbPumpService(MbService):
|
||||||
def run(self):
|
_config = {
|
||||||
pass
|
"modbus": {
|
||||||
|
"host": "10.8.105.2",
|
||||||
|
"port": 503,
|
||||||
|
"log-type": "on-change"
|
||||||
|
},
|
||||||
|
"registers": [
|
||||||
|
["D0", "v231_v236"],
|
||||||
|
["D1", "v236_v29"],
|
||||||
|
["D15", "ps_39"],
|
||||||
|
["D16", "flow_meter"],
|
||||||
|
["D28", "pump_stage"],
|
||||||
|
["D30", "half_auto_control"],
|
||||||
|
["D31", "watch_1"],
|
||||||
|
["D32", "watch_2"],
|
||||||
|
|
||||||
|
["D33", "st_valves"],
|
||||||
|
["D35", "alarms"],
|
||||||
|
|
||||||
|
["D25", "vfd_freq"],
|
||||||
|
["D26", "vfd_current"],
|
||||||
|
["D27", "vfd_error"]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
@ -51,7 +51,8 @@ INSTALLED_APPS = [
|
|||||||
|
|
||||||
# custom apps
|
# custom apps
|
||||||
'users.apps.UsersConfig',
|
'users.apps.UsersConfig',
|
||||||
'index.apps.IndexConfig'
|
'index.apps.IndexConfig',
|
||||||
|
'logs_service'
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
@ -113,7 +113,7 @@ const updateFunctions = {
|
|||||||
const now = Date.now() / 1000
|
const now = Date.now() / 1000
|
||||||
element.innerHTML = moment(new Date(dataset['pump']['last_update'] * 1000)).format(DATETIME_FORMAT)
|
element.innerHTML = moment(new Date(dataset['pump']['last_update'] * 1000)).format(DATETIME_FORMAT)
|
||||||
// для насосной можно допустить последнее обновление 10 минут назад
|
// для насосной можно допустить последнее обновление 10 минут назад
|
||||||
_setIndicatorClass(element, now - dataset['tank']['last_update'] < 600)
|
_setIndicatorClass(element, now - dataset['pump']['last_update'] < 600)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Уровень воды <span id="tank-level-dir"></span>
|
// Уровень воды <span id="tank-level-dir"></span>
|
||||||
|
@ -49,7 +49,17 @@ class User(AbstractBaseUser):
|
|||||||
'users.add_user': 1,
|
'users.add_user': 1,
|
||||||
'users.change_user': 1,
|
'users.change_user': 1,
|
||||||
'users.delete_user': 1,
|
'users.delete_user': 1,
|
||||||
'users.view_user': 1
|
'users.view_user': 1,
|
||||||
|
|
||||||
|
'logs_service.add_mbpumprecord': 1,
|
||||||
|
'logs_service.change_mbpumprecord': 1,
|
||||||
|
'logs_service.delete_mbpumprecord': 1,
|
||||||
|
'logs_service.view_mbpumprecord': 1,
|
||||||
|
|
||||||
|
'logs_service.add_mbtankrecord': 1,
|
||||||
|
'logs_service.change_mbtankrecord': 1,
|
||||||
|
'logs_service.delete_mbtankrecord': 1,
|
||||||
|
'logs_service.view_mbtankrecord': 0,
|
||||||
}
|
}
|
||||||
if perm in permissions:
|
if perm in permissions:
|
||||||
if permissions[perm] <= secure_level:
|
if permissions[perm] <= secure_level:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user