Files
usda-vision/camera-management-api/usda_vision_system/main.py
salirezav 4acad772f9 Update camera management and MQTT logging for improved functionality
- Changed log level in configuration from WARNING to INFO for better visibility of system operations.
- Enhanced StandaloneAutoRecorder initialization to accept camera manager, state manager, and event system for improved modularity.
- Updated recording routes to handle optional request bodies and improved error logging for better debugging.
- Added checks in CameraMonitor to determine if a camera is already in use before initialization, enhancing resource management.
- Improved MQTT client logging to provide more detailed connection and message handling information.
- Added new MQTT event handling capabilities to the VisionApiClient for better tracking of machine states.
2025-11-03 16:56:53 -05:00

280 lines
11 KiB
Python

"""
Main Application Coordinator for the USDA Vision Camera System.
This module coordinates all system components and provides graceful startup/shutdown.
"""
import signal
import time
import logging
import sys
from typing import Optional
from datetime import datetime
from .core.config import Config
from .core.state_manager import StateManager
from .core.events import EventSystem, EventType
from .core.logging_config import setup_logging, get_error_tracker, get_performance_logger
from .core.timezone_utils import log_time_info, check_time_sync
from .mqtt.client import MQTTClient
from .camera.manager import CameraManager
from .storage.manager import StorageManager
from .recording.standalone_auto_recorder import StandaloneAutoRecorder
from .api.server import APIServer
class USDAVisionSystem:
"""Main application coordinator for the USDA Vision Camera System"""
def __init__(self, config_file: Optional[str] = None):
# Load configuration first (basic logging will be used initially)
self.config = Config(config_file)
# Setup comprehensive logging
self.logger_setup = setup_logging(log_level=self.config.system.log_level, log_file=self.config.system.log_file)
self.logger = logging.getLogger(__name__)
# Setup error tracking and performance monitoring
self.error_tracker = get_error_tracker("main_system")
self.performance_logger = get_performance_logger("main_system")
# Initialize core components
self.state_manager = StateManager()
self.event_system = EventSystem()
# Initialize system components
self.storage_manager = StorageManager(self.config, self.state_manager, self.event_system)
self.mqtt_client = MQTTClient(self.config, self.state_manager, self.event_system)
self.camera_manager = CameraManager(self.config, self.state_manager, self.event_system)
self.auto_recording_manager = StandaloneAutoRecorder(
config=self.config,
camera_manager=self.camera_manager,
state_manager=self.state_manager,
event_system=self.event_system
)
self.api_server = APIServer(self.config, self.state_manager, self.event_system, self.camera_manager, self.mqtt_client, self.storage_manager, self.auto_recording_manager)
# System state
self.running = False
self.start_time: Optional[datetime] = None
# Setup signal handlers for graceful shutdown
self._setup_signal_handlers()
self.logger.info("USDA Vision Camera System initialized")
def _setup_signal_handlers(self) -> None:
"""Setup signal handlers for graceful shutdown"""
def signal_handler(signum, frame):
self.logger.info(f"Received signal {signum}, initiating graceful shutdown...")
self.stop()
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
def start(self) -> bool:
"""Start the entire system"""
if self.running:
self.logger.warning("System is already running")
return True
self.logger.info("Starting USDA Vision Camera System...")
self.performance_logger.start_timer("system_startup")
self.start_time = datetime.now()
# Check time synchronization
self.logger.info("Checking time synchronization...")
log_time_info(self.logger)
sync_info = check_time_sync()
if sync_info["sync_status"] == "out_of_sync":
self.error_tracker.log_warning(f"System time may be out of sync (difference: {sync_info.get('time_diff_seconds', 'unknown')}s)", "time_sync_check")
elif sync_info["sync_status"] == "synchronized":
self.logger.info("✅ System time is synchronized")
try:
# Start storage manager (no background tasks)
self.logger.info("Initializing storage manager...")
try:
# Verify storage integrity
integrity_report = self.storage_manager.verify_storage_integrity()
if integrity_report.get("fixed_issues", 0) > 0:
self.logger.info(f"Fixed {integrity_report['fixed_issues']} storage integrity issues")
self.logger.info("Storage manager ready")
except Exception as e:
self.error_tracker.log_error(e, "storage_manager_init")
self.logger.error("Failed to initialize storage manager")
return False
# Start MQTT client
self.logger.info("Starting MQTT client...")
try:
if not self.mqtt_client.start():
self.error_tracker.log_error(Exception("MQTT client failed to start"), "mqtt_startup")
return False
self.logger.info("MQTT client started successfully")
except Exception as e:
self.error_tracker.log_error(e, "mqtt_startup")
return False
# Start camera manager
self.logger.info("Starting camera manager...")
try:
if not self.camera_manager.start():
self.error_tracker.log_error(Exception("Camera manager failed to start"), "camera_startup")
self.mqtt_client.stop()
return False
self.logger.info("Camera manager started successfully")
except Exception as e:
self.error_tracker.log_error(e, "camera_startup")
self.mqtt_client.stop()
return False
# Start auto-recording manager
self.logger.info("Starting auto-recording manager...")
try:
if not self.auto_recording_manager.start():
self.error_tracker.log_warning("Failed to start auto-recording manager", "auto_recording_startup")
else:
self.logger.info("Auto-recording manager started successfully")
except Exception as e:
self.error_tracker.log_error(e, "auto_recording_startup")
self.logger.warning("Auto-recording manager failed to start (continuing without auto-recording)")
# Start API server
self.logger.info("Starting API server...")
try:
if not self.api_server.start():
self.error_tracker.log_warning("Failed to start API server", "api_startup")
else:
self.logger.info("API server started successfully")
except Exception as e:
self.error_tracker.log_error(e, "api_startup")
self.logger.warning("API server failed to start (continuing without API)")
# Update system state
self.running = True
self.state_manager.set_system_started(True)
# Publish system started event
self.event_system.publish(EventType.SYSTEM_SHUTDOWN, "main_system", {"action": "started", "timestamp": self.start_time.isoformat()}) # We don't have SYSTEM_STARTED, using closest
startup_time = self.performance_logger.end_timer("system_startup")
self.logger.info(f"USDA Vision Camera System started successfully in {startup_time:.2f}s")
return True
except Exception as e:
self.error_tracker.log_error(e, "system_startup")
self.stop()
return False
def stop(self) -> None:
"""Stop the entire system gracefully"""
if not self.running:
return
self.logger.info("Stopping USDA Vision Camera System...")
self.running = False
try:
# Update system state
self.state_manager.set_system_started(False)
# Publish system shutdown event
self.event_system.publish(EventType.SYSTEM_SHUTDOWN, "main_system", {"action": "stopping", "timestamp": datetime.now().isoformat()})
# Stop API server
self.api_server.stop()
# Stop auto-recording manager
self.auto_recording_manager.stop()
# Stop camera manager (this will stop all recordings)
self.camera_manager.stop()
# Stop MQTT client
self.mqtt_client.stop()
# Final cleanup
if self.start_time:
uptime = (datetime.now() - self.start_time).total_seconds()
self.logger.info(f"System uptime: {uptime:.1f} seconds")
self.logger.info("USDA Vision Camera System stopped")
except Exception as e:
self.logger.error(f"Error during system shutdown: {e}")
def run(self) -> None:
"""Run the system (blocking call)"""
if not self.start():
self.logger.error("Failed to start system")
return
try:
self.logger.info("System running... Press Ctrl+C to stop")
# Main loop - just keep the system alive
while self.running:
time.sleep(1)
# Periodic maintenance tasks could go here
# For example: cleanup old recordings, health checks, etc.
except KeyboardInterrupt:
self.logger.info("Keyboard interrupt received")
except Exception as e:
self.logger.error(f"Unexpected error in main loop: {e}")
finally:
self.stop()
def get_system_status(self) -> dict:
"""Get comprehensive system status"""
return {
"running": self.running,
"start_time": self.start_time.isoformat() if self.start_time else None,
"uptime_seconds": (datetime.now() - self.start_time).total_seconds() if self.start_time else 0,
"components": {"mqtt_client": {"running": self.mqtt_client.is_running(), "connected": self.mqtt_client.is_connected()}, "camera_manager": {"running": self.camera_manager.is_running()}, "api_server": {"running": self.api_server.is_running()}},
"state_summary": self.state_manager.get_system_summary(),
}
def is_running(self) -> bool:
"""Check if system is running"""
return self.running
def main():
"""Main entry point for the application"""
import argparse
parser = argparse.ArgumentParser(description="USDA Vision Camera System")
parser.add_argument("--config", type=str, help="Path to configuration file", default="config.json")
parser.add_argument("--log-level", type=str, choices=["DEBUG", "INFO", "WARNING", "ERROR"], help="Override log level", default=None)
parser.add_argument("--debug", action="store_true", help="Enable debug mode (sets log level to DEBUG)")
parser.add_argument("--verbose", action="store_true", help="Enable verbose logging (sets log level to DEBUG and enables additional debug output)")
args = parser.parse_args()
# Create and run system
system = USDAVisionSystem(args.config)
# Override log level if specified
if args.debug or args.verbose:
logging.getLogger().setLevel(logging.DEBUG)
# Enable debug mode in the system
if hasattr(system.config.system, 'debug_mode'):
system.config.system.debug_mode = True
print("🐛 Debug mode enabled - verbose logging active")
elif args.log_level:
logging.getLogger().setLevel(getattr(logging, args.log_level))
try:
system.run()
except Exception as e:
logging.error(f"Fatal error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()