174 lines
5.8 KiB
Python
174 lines
5.8 KiB
Python
from .models import *
|
||
from ospaz_site.settings import DEBUG
|
||
from threading import Thread
|
||
import time
|
||
from datetime import datetime, timedelta
|
||
from pyModbusTCP.client import ModbusClient
|
||
|
||
|
||
class MbClearHistoryService(Thread):
|
||
def __init__(self, model, save_days):
|
||
super().__init__()
|
||
self.model = model
|
||
self._days = save_days
|
||
|
||
def run(self):
|
||
while True:
|
||
self.model.objects.filter(dt__lt=(timezone.now() - timezone.timedelta(days=60))).delete()
|
||
time.sleep(10)
|
||
|
||
|
||
class MbService(Thread):
|
||
def __init__(self, ip_addr, port, scan_rate, poll_time_ms=60000, save_days=60):
|
||
super().__init__()
|
||
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):
|
||
return {}
|
||
|
||
|
||
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):
|
||
_config = {
|
||
"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"]
|
||
]
|
||
}
|