Skip to content

Commit

Permalink
improve: remove locks to improve performance, as this is not needed f…
Browse files Browse the repository at this point in the history
…or TCP
  • Loading branch information
Pho3niX90 committed Feb 14, 2025
1 parent 48ca769 commit d31ae09
Showing 1 changed file with 44 additions and 50 deletions.
94 changes: 44 additions & 50 deletions custom_components/solis_modbus/modbus_controller.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import asyncio
import logging
import time
from pymodbus.client import AsyncModbusTcpClient
Expand All @@ -11,9 +10,8 @@ class ModbusController:
def __init__(self, host, port=502, poll_interval=15):
self.host = host
self.port = port
self.client: AsyncModbusTcpClient = AsyncModbusTcpClient(self.host, port=self.port, retries=10, timeout=10, reconnect_delay=10)
self.client: AsyncModbusTcpClient = AsyncModbusTcpClient(host=self.host, port=self.port, retries=10, timeout=10, reconnect_delay=10)
self.connect_failures = 0
self._lock = asyncio.Lock()
self._data_received = False
self._poll_interval = poll_interval
self._model = MODEL
Expand All @@ -22,85 +20,81 @@ def __init__(self, host, port=502, poll_interval=15):
self._last_attempt = 0 # Track last connection attempt time

async def connect(self):
"""Ensure the Modbus TCP connection is active."""
if self.client.connected:
return True

async with self._lock:
now = time.monotonic()
if now - self._last_attempt < 1:
return False # Skip execution if called too soon
return True # Already connected

self._last_attempt = now # Update last attempt time
now = time.monotonic()
if now - self._last_attempt < 1:
return False # Prevent excessive reconnections

try:
_LOGGER.debug('connecting')
self._last_attempt = now # Update last attempt time

if not await self.client.connect():
self.connect_failures += 1
fail_msg = f"Failed to connect to Modbus device. Will retry, failures = {self.connect_failures}"
if self.connect_failures > 50:
_LOGGER.warning(fail_msg)
elif self.connect_failures > 30:
_LOGGER.info(fail_msg)
else:
_LOGGER.debug(fail_msg)
return False
try:
_LOGGER.debug('Connecting to Modbus TCP...')

else:
self.connect_failures = 0
return True
if not await self.client.connect():
self.connect_failures += 1
_LOGGER.warning(f"Failed to connect (Attempt {self.connect_failures})")
return False

except ConnectionError as e:
return False # Return False if an exception occurs
self.connect_failures = 0 # Reset failure counter
return True

async def disconnect(self):
if self.client.connected:
self.client.close()
except Exception as e:
_LOGGER.error(f"Connection error: {e}")
return False

async def async_read_input_register(self, register, count=1):
"""Reads an input register asynchronously without locking."""
try:
await self.connect()
async with self._lock:
result = await self.client.read_input_registers(address=register, count=count, slave=1)
_LOGGER.debug(f'register value, register = {register}, result = {result.registers}')
result = await self.client.read_input_registers(address=register, count=count, slave=1)
_LOGGER.debug(f"Register {register}: {result.registers}")
return result.registers
except Exception as e:
_LOGGER.debug(f"Failed to read Modbus holding register: {str(e)}")
_LOGGER.error(f"Failed to read input register {register}: {str(e)}")
return None

async def async_read_holding_register(self, register: int, count=1):
async def async_read_holding_register(self, register, count=1):
"""Reads a holding register asynchronously."""
try:
await self.connect()
async with self._lock:
result = await self.client.read_holding_registers(address=register, count=count, slave=1)
_LOGGER.debug(f'holding register value, register = {register}, result = {result.registers}')
result = await self.client.read_holding_registers(address=register, count=count, slave=1)
_LOGGER.debug(f"Holding Register {register}: {result.registers}")
return result.registers
except Exception as e:
_LOGGER.debug(f"Failed to read Modbus holding register: {str(e)}")
_LOGGER.error(f"Failed to read holding register {register}: {str(e)}")
return None

async def async_write_holding_register(self, register: int, value):
async def async_write_holding_register(self, register, value):
"""Writes a single holding register asynchronously."""
try:
await self.connect()
async with self._lock:
result = await self.client.write_register(address=register, value=value, slave=1)
return result
return await self.client.write_register(address=register, value=value, slave=1)
except Exception as e:
_LOGGER.debug(f"Failed to write Modbus holding register ({register}): {str(e)}")
_LOGGER.error(f"Failed to write holding register {register}: {str(e)}")
return None

async def async_write_holding_registers(self, start_register: int, values: list[int]):
async def async_write_holding_registers(self, start_register, values):
"""Writes multiple holding registers asynchronously."""
try:
await self.connect()
async with self._lock:
result = await self.client.write_registers(address=start_register, values=values, slave=1)
return result
return await self.client.write_registers(address=start_register, values=values, slave=1)
except Exception as e:
_LOGGER.debug(
f"Failed to write Modbus holding registers ({start_register}), values = {values}: {str(e)}")
_LOGGER.error(f"Failed to write holding registers {start_register}: {str(e)}")
return None

def disable_connection(self):
self.enabled = False
self.close_connection()

async def enable_connection(self):
self.enabled = True
await self.connect()

def close_connection(self):
"""Closes the Modbus connection."""
self.client.close()

def connected(self):
Expand Down

0 comments on commit d31ae09

Please sign in to comment.