🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
193 lines
5.5 KiB
Python
193 lines
5.5 KiB
Python
"""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',
|
|
]
|