| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576 |
- import logging
- from pymodbus.client import ModbusTcpClient
- import threading
- import time
- from typing import Callable, List
- logger = logging.getLogger(__name__)
- _MIN_BACKOFF = 5
- _MAX_BACKOFF = 60
- class ModbusClient:
- def __init__(self, config, on_data_callback: Callable[[List[bool]], None]):
- self.config = config
- self.on_data_callback = on_data_callback
- self.client: ModbusTcpClient = None
- self.running = False
- self.thread = None
- self._coil_count = 100
- self._backoff = _MIN_BACKOFF
- self._last_connect_attempt = 0.0
- def connect(self) -> bool:
- self._last_connect_attempt = time.time()
- try:
- self.client = ModbusTcpClient(self.config.ip, port=self.config.port)
- self.client.connect()
- self._backoff = _MIN_BACKOFF
- self.running = True
- self.thread = threading.Thread(target=self._poll_loop, daemon=True)
- self.thread.start()
- logger.info(f"Modbus连接成功 {self.config.ip}:{self.config.port}")
- return True
- except Exception as e:
- self._backoff = min(self._backoff * 2, _MAX_BACKOFF)
- logger.error(f"Modbus连接失败,下次{self._backoff}s后重试: {e}")
- return False
- def disconnect(self):
- self.running = False
- if self.client:
- self.client.close()
- self.client = None
- logger.info("Modbus已断开")
- def _poll_loop(self):
- while self.running:
- try:
- if self.client is None or not self.client.is_socket_open():
- logger.warning("Modbus连接断开,尝试重连")
- self._try_reconnect()
- if self.client is None:
- time.sleep(min(self._backoff, self.config.frequency / 1000.0))
- continue
- result = self.client.read_coils(0, self._coil_count)
- if result and not result.isError():
- bools = [result.bits[i] for i in range(len(result.bits))]
- self.on_data_callback(bools)
- except Exception as e:
- logger.error(f"Modbus读取错误: {e}")
- self.client = None
- time.sleep(self.config.frequency / 1000.0)
- def _try_reconnect(self):
- if time.time() - self._last_connect_attempt < self._backoff:
- return
- self._last_connect_attempt = time.time()
- try:
- self.client = ModbusTcpClient(self.config.ip, port=self.config.port)
- self.client.connect()
- self._backoff = _MIN_BACKOFF
- logger.info("Modbus重连成功")
- except Exception as e:
- self._backoff = min(self._backoff * 2, _MAX_BACKOFF)
- logger.error(f"Modbus重连失败,下次{self._backoff}s后重试: {e}")
- self.client = None
|