Fixing reconnect issues after outage
This commit is contained in:
@@ -221,10 +221,17 @@ class ModbusRTUMonitorHub:
|
|||||||
|
|
||||||
async def _reconnect(self) -> None:
|
async def _reconnect(self) -> None:
|
||||||
"""Reconnect to Modbus gateway after connection loss."""
|
"""Reconnect to Modbus gateway after connection loss."""
|
||||||
# Close existing connection
|
# Close existing connection and null out references
|
||||||
if self._writer:
|
if self._writer:
|
||||||
|
try:
|
||||||
self._writer.close()
|
self._writer.close()
|
||||||
await self._writer.wait_closed()
|
await self._writer.wait_closed()
|
||||||
|
except Exception as ex:
|
||||||
|
_LOGGER.debug("Error closing writer during reconnect: %s", ex)
|
||||||
|
|
||||||
|
# Always null out reader/writer to ensure clean state
|
||||||
|
self._reader = None
|
||||||
|
self._writer = None
|
||||||
|
|
||||||
# Wait before reconnecting (30 seconds to prevent DDOS during server outages)
|
# Wait before reconnecting (30 seconds to prevent DDOS during server outages)
|
||||||
await asyncio.sleep(30)
|
await asyncio.sleep(30)
|
||||||
@@ -232,10 +239,15 @@ class ModbusRTUMonitorHub:
|
|||||||
# Attempt reconnection
|
# Attempt reconnection
|
||||||
try:
|
try:
|
||||||
await self._connect()
|
await self._connect()
|
||||||
_LOGGER.info("Reconnected to Modbus gateway")
|
_LOGGER.info("Reconnected to Modbus gateway at %s:%s", self.host, self.port)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
_LOGGER.error("Reconnection failed: %s", ex)
|
_LOGGER.error(
|
||||||
# Will retry on next loop iteration
|
"Reconnection failed for %s:%s - %s (will retry in 30 seconds)",
|
||||||
|
self.host,
|
||||||
|
self.port,
|
||||||
|
ex,
|
||||||
|
)
|
||||||
|
# Reader/writer are already None, loop will detect this and retry
|
||||||
|
|
||||||
async def _monitor_loop(self) -> None:
|
async def _monitor_loop(self) -> None:
|
||||||
"""Main monitoring loop for passive frame reception."""
|
"""Main monitoring loop for passive frame reception."""
|
||||||
@@ -243,13 +255,24 @@ class ModbusRTUMonitorHub:
|
|||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
# Check if reader is valid before attempting to read
|
||||||
|
if self._reader is None:
|
||||||
|
# No active connection, attempt reconnect
|
||||||
|
_LOGGER.info("No active connection to %s:%s, attempting reconnect", self.host, self.port)
|
||||||
|
await self._reconnect()
|
||||||
|
# Clear buffer for fresh start after reconnection
|
||||||
|
buffer.clear()
|
||||||
|
continue
|
||||||
|
|
||||||
# Read with timeout to allow graceful shutdown
|
# Read with timeout to allow graceful shutdown
|
||||||
data = await asyncio.wait_for(self._reader.read(1024), timeout=1.0)
|
data = await asyncio.wait_for(self._reader.read(1024), timeout=1.0)
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
# Connection closed
|
# Connection closed
|
||||||
_LOGGER.warning("Connection closed, attempting reconnect")
|
_LOGGER.warning("Connection closed to %s:%s, attempting reconnect", self.host, self.port)
|
||||||
await self._reconnect()
|
await self._reconnect()
|
||||||
|
# Clear buffer for fresh start after reconnection
|
||||||
|
buffer.clear()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
buffer.extend(data)
|
buffer.extend(data)
|
||||||
@@ -290,8 +313,23 @@ class ModbusRTUMonitorHub:
|
|||||||
continue
|
continue
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
break
|
break
|
||||||
|
except (AttributeError, OSError, ConnectionError) as ex:
|
||||||
|
# Connection-related errors - trigger reconnection
|
||||||
|
_LOGGER.warning(
|
||||||
|
"Connection error in monitor loop for %s:%s - %s (will reconnect)",
|
||||||
|
self.host,
|
||||||
|
self.port,
|
||||||
|
ex,
|
||||||
|
)
|
||||||
|
# Null out connection and trigger reconnect
|
||||||
|
self._reader = None
|
||||||
|
self._writer = None
|
||||||
|
await self._reconnect()
|
||||||
|
buffer.clear()
|
||||||
|
continue
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
_LOGGER.error("Error in monitor loop: %s", ex)
|
# Other unexpected errors
|
||||||
|
_LOGGER.error("Unexpected error in monitor loop for %s:%s - %s", self.host, self.port, ex)
|
||||||
await asyncio.sleep(5)
|
await asyncio.sleep(5)
|
||||||
|
|
||||||
async def _handle_frame(self, frame: ModbusRTUFrame) -> None:
|
async def _handle_frame(self, frame: ModbusRTUFrame) -> None:
|
||||||
@@ -632,10 +670,10 @@ class ModbusRTUMonitorHub:
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Write temperature setpoint to slave using RTU protocol over existing connection."""
|
"""Write temperature setpoint to slave using RTU protocol over existing connection."""
|
||||||
async with self._write_lock:
|
async with self._write_lock:
|
||||||
# Ensure writer is available
|
# Ensure writer is available and not closed
|
||||||
if not self._writer:
|
if not self._writer or self._writer.is_closing():
|
||||||
raise HomeAssistantError(
|
raise HomeAssistantError(
|
||||||
"No active connection to Modbus gateway - cannot write setpoint"
|
f"No active connection to Modbus gateway at {self.host}:{self.port} - cannot write setpoint"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Scale temperature value (multiply by 10)
|
# Scale temperature value (multiply by 10)
|
||||||
|
|||||||
Reference in New Issue
Block a user