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"] ] }