Refactor: PHASE 6: Decoupling & Integration
This commit is contained in:
parent
6c7c4c5d9c
commit
accefde8a1
8 changed files with 2344 additions and 86 deletions
220
app_refactored.py
Normal file
220
app_refactored.py
Normal file
|
@ -0,0 +1,220 @@
|
|||
"""
|
||||
Refactored FastAPI application using the new modular architecture.
|
||||
|
||||
This replaces the monolithic app.py with a clean, maintainable structure
|
||||
using dependency injection and singleton managers.
|
||||
"""
|
||||
import logging
|
||||
import asyncio
|
||||
from fastapi import FastAPI, WebSocket, HTTPException
|
||||
from fastapi.responses import Response
|
||||
|
||||
from detector_worker.core.config import get_config_manager, validate_config
|
||||
from detector_worker.core.dependency_injection import get_container
|
||||
from detector_worker.core.singleton_managers import (
|
||||
ModelStateManager, StreamStateManager, SessionStateManager,
|
||||
CacheStateManager, CameraStateManager, PipelineStateManager
|
||||
)
|
||||
from detector_worker.communication.websocket_handler import WebSocketHandler
|
||||
from detector_worker.utils.system_monitor import get_system_metrics
|
||||
from detector_worker.utils.error_handler import ErrorHandler, create_logger
|
||||
|
||||
# Setup logging
|
||||
logger = create_logger("detector_worker.main", logging.INFO)
|
||||
|
||||
# Create FastAPI app
|
||||
app = FastAPI(title="Detector Worker", version="2.0.0")
|
||||
|
||||
# Global state managers (singleton instances)
|
||||
model_manager = ModelStateManager()
|
||||
stream_manager = StreamStateManager()
|
||||
session_manager = SessionStateManager()
|
||||
cache_manager = CacheStateManager()
|
||||
camera_manager = CameraStateManager()
|
||||
pipeline_manager = PipelineStateManager()
|
||||
|
||||
# Dependency injection container
|
||||
container = get_container()
|
||||
|
||||
# System monitoring function available
|
||||
|
||||
# Error handler
|
||||
error_handler = ErrorHandler("main_app")
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup_event():
|
||||
"""Initialize application on startup."""
|
||||
try:
|
||||
# Validate configuration
|
||||
config_manager = get_config_manager()
|
||||
errors = validate_config()
|
||||
|
||||
if errors:
|
||||
logger.error(f"Configuration validation failed: {errors}")
|
||||
raise RuntimeError(f"Invalid configuration: {', '.join(errors)}")
|
||||
|
||||
logger.info("Configuration validation passed")
|
||||
|
||||
# Log startup information
|
||||
config = config_manager.get_all()
|
||||
logger.info(f"Starting Detector Worker v2.0.0")
|
||||
logger.info(f"Max streams: {config.get('max_streams', 5)}")
|
||||
logger.info(f"Target FPS: {config.get('target_fps', 10)}")
|
||||
|
||||
# Initialize dependency injection container
|
||||
container_stats = container.get_container().get_stats()
|
||||
logger.info(f"Dependency container initialized: {container_stats}")
|
||||
|
||||
logger.info("Detector Worker startup complete")
|
||||
|
||||
except Exception as e:
|
||||
logger.critical(f"Startup failed: {e}")
|
||||
raise
|
||||
|
||||
|
||||
@app.on_event("shutdown")
|
||||
async def shutdown_event():
|
||||
"""Clean up resources on shutdown."""
|
||||
try:
|
||||
logger.info("Shutting down Detector Worker...")
|
||||
|
||||
# Clear all state managers
|
||||
model_manager.clear_all()
|
||||
stream_manager.clear_all()
|
||||
session_manager.clear_all()
|
||||
cache_manager.clear_all()
|
||||
camera_manager.clear_all()
|
||||
pipeline_manager.clear_all()
|
||||
|
||||
# Clear dependency container singletons
|
||||
container.get_container().clear_singletons()
|
||||
|
||||
logger.info("Detector Worker shutdown complete")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error during shutdown: {e}")
|
||||
|
||||
|
||||
@app.websocket("/ws")
|
||||
async def websocket_endpoint(websocket: WebSocket):
|
||||
"""Main WebSocket endpoint for real-time communication."""
|
||||
try:
|
||||
# Create WebSocket handler using dependency injection
|
||||
ws_handler = container.resolve(WebSocketHandler)
|
||||
|
||||
await ws_handler.handle_connection(websocket)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"WebSocket error: {e}")
|
||||
if not websocket.client_state.DISCONNECTED:
|
||||
await websocket.close()
|
||||
|
||||
|
||||
@app.get("/camera/{camera_id}/image")
|
||||
async def get_camera_image(camera_id: str):
|
||||
"""REST endpoint to get latest frame from camera."""
|
||||
try:
|
||||
# Get latest frame from cache manager
|
||||
frame_data = cache_manager.get_latest_frame(camera_id)
|
||||
|
||||
if frame_data is None:
|
||||
raise HTTPException(status_code=404, detail=f"No frame available for camera {camera_id}")
|
||||
|
||||
# Return frame as image response
|
||||
return Response(
|
||||
content=frame_data,
|
||||
media_type="image/jpeg",
|
||||
headers={"Cache-Control": "no-cache, no-store, must-revalidate"}
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting camera image: {e}")
|
||||
raise HTTPException(status_code=500, detail="Internal server error")
|
||||
|
||||
|
||||
@app.get("/health")
|
||||
async def health_check():
|
||||
"""Health check endpoint with system metrics."""
|
||||
try:
|
||||
# Get system metrics
|
||||
system_stats = get_system_metrics()
|
||||
|
||||
# Get state manager statistics
|
||||
stats = {
|
||||
"status": "healthy",
|
||||
"version": "2.0.0",
|
||||
"system": system_stats,
|
||||
"managers": {
|
||||
"models": model_manager.get_stats(),
|
||||
"streams": stream_manager.get_stats(),
|
||||
"sessions": session_manager.get_stats(),
|
||||
"cache": cache_manager.get_stats(),
|
||||
"cameras": camera_manager.get_stats(),
|
||||
"pipeline": pipeline_manager.get_stats()
|
||||
},
|
||||
"container": container.get_container().get_stats(),
|
||||
"errors": error_handler.get_error_stats()
|
||||
}
|
||||
|
||||
return stats
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Health check failed: {e}")
|
||||
return {
|
||||
"status": "unhealthy",
|
||||
"error": str(e),
|
||||
"version": "2.0.0"
|
||||
}
|
||||
|
||||
|
||||
@app.get("/config")
|
||||
async def get_configuration():
|
||||
"""Get current configuration."""
|
||||
try:
|
||||
config_manager = get_config_manager()
|
||||
return {
|
||||
"config": config_manager.get_all(),
|
||||
"validation_errors": validate_config()
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting configuration: {e}")
|
||||
raise HTTPException(status_code=500, detail="Internal server error")
|
||||
|
||||
|
||||
@app.post("/config/reload")
|
||||
async def reload_configuration():
|
||||
"""Reload configuration from all sources."""
|
||||
try:
|
||||
config_manager = get_config_manager()
|
||||
success = config_manager.reload()
|
||||
|
||||
if not success:
|
||||
raise HTTPException(status_code=500, detail="Failed to reload configuration")
|
||||
|
||||
errors = validate_config()
|
||||
return {
|
||||
"success": True,
|
||||
"validation_errors": errors,
|
||||
"config": config_manager.get_all()
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Error reloading configuration: {e}")
|
||||
raise HTTPException(status_code=500, detail="Internal server error")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
|
||||
# Get configuration
|
||||
config_manager = get_config_manager()
|
||||
|
||||
# Run the application
|
||||
uvicorn.run(
|
||||
app,
|
||||
host="0.0.0.0",
|
||||
port=8000,
|
||||
log_level="info"
|
||||
)
|
Loading…
Add table
Add a link
Reference in a new issue