174 lines
6.8 KiB
Python
174 lines
6.8 KiB
Python
"""
|
|
Communicator manager.
|
|
Cosa fa: crea e gestisce i communicator (SFP, Serial, TFTP), connessioni e callback.
|
|
Principali: CommunicatorManager
|
|
Ingressi/Uscite: configurazione via set_config(); operazioni di connect/disconnect come side-effect.
|
|
"""
|
|
|
|
from typing import Optional, Dict, Any, Callable, List, Tuple
|
|
from target_simulator.core.sfp_communicator import SFPCommunicator
|
|
from target_simulator.core.serial_communicator import SerialCommunicator
|
|
from target_simulator.core.tftp_communicator import TFTPCommunicator
|
|
from target_simulator.core.communicator_interface import CommunicatorInterface
|
|
|
|
|
|
class CommunicatorManager:
|
|
"""Encapsulates creation, initialization and basic lifecycle of communicators.
|
|
code
|
|
Code
|
|
This is a small, testable façade that mirrors the behavior previously
|
|
implemented directly inside MainView. It intentionally keeps a thin API
|
|
so MainView can migrate to it without changing external behavior.
|
|
"""
|
|
|
|
def __init__(self, simulation_hub, logger=None, defer_sfp_connection: bool = True):
|
|
self.simulation_hub = simulation_hub
|
|
self.logger = logger
|
|
self.defer_sfp_connection = defer_sfp_connection
|
|
|
|
self.config: Dict[str, Any] = {}
|
|
|
|
self.target_communicator: Optional[CommunicatorInterface] = None
|
|
self.lru_communicator: Optional[CommunicatorInterface] = None
|
|
|
|
# Callbacks that want to be notified about connection state changes
|
|
self._connection_state_callbacks: List[Callable[[bool], None]] = []
|
|
|
|
def set_config(self, config: Dict[str, Any]):
|
|
"""Sets the configuration for the communicators."""
|
|
self.config = config or {}
|
|
|
|
def initialize_communicators(
|
|
self,
|
|
) -> Tuple[
|
|
Optional[CommunicatorInterface], bool, Optional[CommunicatorInterface], bool
|
|
]:
|
|
"""Create and (optionally) connect communicator instances.
|
|
|
|
Returns: (target_comm, target_connected, lru_comm, lru_connected)
|
|
"""
|
|
# Disconnect any existing connections before re-initializing
|
|
self._disconnect_communicator(self.target_communicator, "target")
|
|
self._disconnect_communicator(self.lru_communicator, "LRU")
|
|
|
|
target_cfg = (self.config or {}).get("target", {})
|
|
lru_cfg = (self.config or {}).get("lru", {})
|
|
|
|
# Initialize Target Communicator
|
|
self.target_communicator, target_connected = self._setup_communicator(
|
|
target_cfg, "Target"
|
|
)
|
|
|
|
# Initialize LRU Communicator
|
|
self.lru_communicator, lru_connected = self._setup_communicator(lru_cfg, "LRU")
|
|
|
|
return (
|
|
self.target_communicator,
|
|
bool(target_connected),
|
|
self.lru_communicator,
|
|
bool(lru_connected),
|
|
)
|
|
|
|
def _setup_communicator(self, config: Dict[str, Any], name: str):
|
|
"""Helper to create and connect a single communicator."""
|
|
comm_type = (config or {}).get("type")
|
|
if self.logger:
|
|
self.logger.info(f"Initializing {name} communicator of type: {comm_type}")
|
|
|
|
communicator = None
|
|
config_data = None
|
|
|
|
try:
|
|
if comm_type == "serial":
|
|
communicator = SerialCommunicator()
|
|
config_data = config.get("serial", {})
|
|
elif comm_type == "tftp":
|
|
communicator = TFTPCommunicator()
|
|
config_data = config.get("tftp", {})
|
|
elif comm_type == "sfp":
|
|
communicator = SFPCommunicator(simulation_hub=self.simulation_hub)
|
|
if hasattr(communicator, "add_connection_state_callback"):
|
|
communicator.add_connection_state_callback(
|
|
self._notify_connection_state
|
|
)
|
|
config_data = config.get("sfp", {})
|
|
if self.defer_sfp_connection:
|
|
return communicator, False
|
|
|
|
if communicator and config_data:
|
|
if communicator.connect(config_data):
|
|
return communicator, True
|
|
except Exception:
|
|
if self.logger:
|
|
self.logger.exception(
|
|
f"Failed to initialize or connect {name} communicator."
|
|
)
|
|
|
|
if self.logger:
|
|
self.logger.warning(f"Failed to initialize or connect {name} communicator.")
|
|
return None, False
|
|
|
|
def connect_target(self) -> bool:
|
|
"""Attempt to connect the target communicator using current config."""
|
|
try:
|
|
if not self.target_communicator:
|
|
self.initialize_communicators()
|
|
|
|
cfg = (self.config or {}).get("target", {})
|
|
sfp_cfg = cfg.get("sfp") if cfg else None
|
|
if cfg.get("type") == "sfp" and sfp_cfg and self.target_communicator:
|
|
return bool(self.target_communicator.connect(sfp_cfg))
|
|
|
|
self.initialize_communicators()
|
|
return bool(
|
|
self.target_communicator
|
|
and getattr(self.target_communicator, "is_open", False)
|
|
)
|
|
except Exception:
|
|
if self.logger:
|
|
self.logger.exception("Unhandled exception in connect_target")
|
|
return False
|
|
|
|
def disconnect_target(self):
|
|
"""Disconnects only the target communicator."""
|
|
self._disconnect_communicator(self.target_communicator, "target")
|
|
|
|
def _disconnect_communicator(
|
|
self, comm: Optional[CommunicatorInterface], name: str
|
|
):
|
|
"""Safely disconnects a single communicator instance if it's open."""
|
|
if not comm or not getattr(comm, "is_open", False):
|
|
return
|
|
try:
|
|
comm.disconnect()
|
|
except Exception:
|
|
if self.logger:
|
|
self.logger.exception(f"Error disconnecting {name} communicator")
|
|
|
|
def shutdown(self):
|
|
"""Disconnects all managed communicators cleanly."""
|
|
self.logger.info("Shutting down all communicators.")
|
|
self._disconnect_communicator(self.target_communicator, "target")
|
|
self._disconnect_communicator(self.lru_communicator, "LRU")
|
|
|
|
def add_connection_state_callback(self, cb: Callable[[bool], None]):
|
|
"""Registers a callback for connection state changes."""
|
|
if cb not in self._connection_state_callbacks:
|
|
self._connection_state_callbacks.append(cb)
|
|
|
|
def remove_connection_state_callback(self, cb: Callable[[bool], None]):
|
|
"""Unregisters a connection state callback."""
|
|
try:
|
|
self._connection_state_callbacks.remove(cb)
|
|
except ValueError:
|
|
pass
|
|
|
|
def _notify_connection_state(self, is_connected: bool):
|
|
"""Notifies all registered callbacks about a connection state change."""
|
|
for cb in list(self._connection_state_callbacks):
|
|
try:
|
|
cb(is_connected)
|
|
except Exception:
|
|
if self.logger:
|
|
self.logger.exception("Connection state callback failed")
|