"""Service Container for Dependency Injection. This module provides a singleton container that manages service instances and their dependencies, ensuring consistent state across all interfaces (REST API, BLE GATT, plugins, etc.). """ from typing import Optional from ..workers.pm3_worker import PM3Worker, SubprocessPM3Worker from ..managers.session_manager import SessionManager from ..managers.wifi_manager import WiFiManager from ..managers.update_manager import get_update_manager from ..managers.pm3_device_manager import PM3DeviceManager from .pm3_service import PM3Service from .system_service import SystemService from .wifi_service import WiFiService from .update_service import UpdateService class ServiceContainer: """Dependency injection container for services. This container: - Creates and manages singleton instances of services - Injects shared dependencies (workers, managers) - Ensures consistent state across all interfaces - Provides centralized access to services Usage: # In REST API from app.backend.services.container import container result = await container.pm3_service.execute_command(...) # In BLE GATT from app.backend.services.container import container result = await container.pm3_service.execute_command(...) # Both use the same service instance! """ _instance: Optional['ServiceContainer'] = None def __init__(self): """Initialize service container with shared dependencies.""" if ServiceContainer._instance is not None: raise RuntimeError( "ServiceContainer is a singleton. Use container.get_instance() " "or import the global 'container' instance." ) # Create shared worker/manager instances # These are the low-level components that services will use # Use PM3Worker with SWIG bindings for better performance # Falls back to SubprocessPM3Worker if SWIG import fails try: self._pm3_worker = PM3Worker() # Try SWIG bindings first except Exception: self._pm3_worker = SubprocessPM3Worker() # Fallback to subprocess self._pm3_device_manager = PM3DeviceManager() # NEW: Multi-device manager self._session_manager = SessionManager() self._wifi_manager = WiFiManager() self._update_manager = get_update_manager() # Create service instances with injected dependencies self._pm3_service = PM3Service( pm3_worker=self._pm3_worker, session_manager=self._session_manager, device_manager=self._pm3_device_manager # NEW: Multi-device support ) self._system_service = SystemService() self._wifi_service = WiFiService( wifi_manager=self._wifi_manager ) self._update_service = UpdateService( update_manager=self._update_manager ) # Note: WorkflowService will be added in Phase 5 # self._workflow_service = WorkflowService( # pm3_service=self._pm3_service # ) @classmethod def get_instance(cls) -> 'ServiceContainer': """Get the singleton service container instance. Returns: ServiceContainer instance """ if cls._instance is None: cls._instance = ServiceContainer() return cls._instance @classmethod def reset_instance(cls): """Reset the singleton instance. This is primarily used for testing purposes. """ cls._instance = None # Service properties (read-only access) @property def pm3_service(self) -> PM3Service: """Get PM3 service instance. Returns: Shared PM3Service instance """ return self._pm3_service @property def system_service(self) -> SystemService: """Get system service instance. Returns: Shared SystemService instance """ return self._system_service @property def wifi_service(self) -> WiFiService: """Get WiFi service instance. Returns: Shared WiFiService instance """ return self._wifi_service @property def update_service(self) -> UpdateService: """Get update service instance. Returns: Shared UpdateService instance """ return self._update_service # Manager/Worker access (for cases where direct access is needed) @property def pm3_worker(self) -> PM3Worker: """Get PM3 worker instance. Returns: Shared PM3Worker instance """ return self._pm3_worker @property def session_manager(self) -> SessionManager: """Get session manager instance. Returns: Shared SessionManager instance """ return self._session_manager @property def wifi_manager(self) -> WiFiManager: """Get WiFi manager instance. Returns: Shared WiFiManager instance """ return self._wifi_manager @property def pm3_device_manager(self) -> PM3DeviceManager: """Get PM3 device manager instance. Returns: Shared PM3DeviceManager instance for multi-device support """ return self._pm3_device_manager # Global singleton instance # Import this throughout the codebase for consistent service access container = ServiceContainer.get_instance() # Convenience exports for common services __all__ = [ 'ServiceContainer', 'container', ]