TCPDashboard/data/collector/collector_connection_manager.py
Vasily.onl dbe58e5cef Remove deprecated app_new.py and consolidate main application logic into main.py
- Deleted `app_new.py`, which was previously the main entry point for the dashboard application, to streamline the codebase.
- Consolidated the application initialization and callback registration logic into `main.py`, enhancing modularity and maintainability.
- Updated the logging and error handling practices in `main.py` to ensure consistent application behavior and improved debugging capabilities.

These changes simplify the application structure, aligning with project standards for modularity and maintainability.
2025-06-11 18:36:34 +08:00

149 lines
6.3 KiB
Python

"""
Module for managing network connection and reconnection logic for data collectors.
This module encapsulates the complexities of connecting, disconnecting,
and handling reconnection attempts to a data source, promoting a clean
separation of concerns within the data collector architecture.
"""
import asyncio
from typing import List, Any
from datetime import datetime
# from ..base_collector import DataType # Import from base_collector for now, will refactor later
from .collector_state_telemetry import CollectorStatus, CollectorStateAndTelemetry
from data.common.data_types import DataType
class ConnectionManager:
"""
Manages the connection, disconnection, and reconnection logic for a data collector.
"""
def __init__(self,
exchange_name: str,
component_name: str,
max_reconnect_attempts: int = 5,
reconnect_delay: float = 5.0,
logger=None,
state_telemetry: CollectorStateAndTelemetry = None):
self.exchange_name = exchange_name
self.component_name = component_name
self._max_reconnect_attempts = max_reconnect_attempts
self._reconnect_delay = reconnect_delay
self.logger = logger
self._state_telemetry = state_telemetry
self._connection = None # Placeholder for the actual connection object
self._reconnect_attempts = 0
def _log_debug(self, message: str) -> None:
if self._state_telemetry:
self._state_telemetry._log_debug(f"{self.component_name}: {message}")
elif self.logger:
self.logger.debug(f"{self.component_name}: {message}")
def _log_info(self, message: str) -> None:
if self._state_telemetry:
self._state_telemetry._log_info(f"{self.component_name}: {message}")
elif self.logger:
self.logger.info(f"{self.component_name}: {message}")
def _log_warning(self, message: str) -> None:
if self._state_telemetry:
self._state_telemetry._log_warning(f"{self.component_name}: {message}")
elif self.logger:
self.logger.warning(f"{self.component_name}: {message}")
def _log_error(self, message: str, exc_info: bool = False) -> None:
if self._state_telemetry:
self._state_telemetry._log_error(f"{self.component_name}: {message}", exc_info=exc_info)
elif self.logger:
self.logger.error(f"{self.component_name}: {message}", exc_info=exc_info)
async def connect(self, connect_logic: callable) -> bool:
"""
Establish connection to the data source using provided logic.
Args:
connect_logic: A callable (async function) that performs the actual connection.
Returns:
True if connection successful, False otherwise
"""
self._log_info(f"Connecting to {self.exchange_name} data source")
try:
success = await connect_logic()
if success:
self._connection = True # Indicate connection is established
self._state_telemetry.set_connection_uptime_start()
self._log_info(f"Successfully connected to {self.exchange_name}")
return True
else:
self._log_error(f"Failed to connect to {self.exchange_name}")
return False
except Exception as e:
self._log_error(f"Error during connection to {self.exchange_name}: {e}", exc_info=True)
return False
async def disconnect(self, disconnect_logic: callable) -> None:
"""
Disconnect from the data source using provided logic.
Args:
disconnect_logic: A callable (async function) that performs the actual disconnection.
"""
self._log_info(f"Disconnecting from {self.exchange_name} data source")
try:
if self._connection:
await disconnect_logic()
self._connection = None
self._log_info(f"Disconnected from {self.exchange_name}")
except Exception as e:
self._log_error(f"Error during disconnection from {self.exchange_name}: {e}", exc_info=True)
async def handle_connection_error(self, connect_logic: callable, subscribe_logic: callable, symbols: List[str], data_types: List[DataType]) -> bool:
"""
Handle connection errors and attempt reconnection.
Args:
connect_logic: Callable for connecting.
subscribe_logic: Callable for subscribing.
symbols: List of symbols to re-subscribe to.
data_types: List of data types to re-subscribe to.
Returns:
True if reconnection successful, False if max attempts exceeded
"""
self._reconnect_attempts += 1
if self._reconnect_attempts > self._max_reconnect_attempts:
self._log_error(f"Max reconnection attempts ({self._max_reconnect_attempts}) exceeded for {self.exchange_name}")
if self._state_telemetry:
self._state_telemetry.update_status(CollectorStatus.ERROR)
self._state_telemetry.set_should_be_running(False)
return False
if self._state_telemetry:
self._state_telemetry.update_status(CollectorStatus.RECONNECTING)
self._log_warning(f"Connection lost. Attempting reconnection {self._reconnect_attempts}/{self._max_reconnect_attempts} for {self.exchange_name}")
# Disconnect and wait before retrying
await self.disconnect(lambda: None) # Pass a no-op disconnect for internal use, actual disconnect handled by caller
await asyncio.sleep(self._reconnect_delay)
# Attempt to reconnect
try:
if await self.connect(connect_logic):
if await subscribe_logic(symbols, data_types):
self._log_info(f"Reconnection successful for {self.exchange_name}")
if self._state_telemetry:
self._state_telemetry.update_status(CollectorStatus.RUNNING)
self._reconnect_attempts = 0
return True
except Exception as e:
self._log_error(f"Reconnection attempt failed for {self.exchange_name}: {e}", exc_info=True)
return False