feat: Integrate auto-recording feature into USDA Vision Camera System
- Added instructions for implementing auto-recording functionality in the React app. - Updated TypeScript interfaces to include new fields for auto-recording status and configuration. - Created new API endpoints for enabling/disabling auto-recording and retrieving system status. - Enhanced UI components to display auto-recording status, controls, and error handling. - Developed a comprehensive Auto-Recording Feature Implementation Guide. - Implemented a test script for validating auto-recording functionality, including configuration checks and API connectivity. - Introduced AutoRecordingManager to manage automatic recording based on machine state changes with retry logic. - Established a retry mechanism for failed recording attempts and integrated status tracking for auto-recording.
This commit is contained in:
@@ -19,58 +19,55 @@ 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.auto_manager import AutoRecordingManager
|
||||
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_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.api_server = APIServer(
|
||||
self.config, self.state_manager, self.event_system,
|
||||
self.camera_manager, self.mqtt_client, self.storage_manager
|
||||
)
|
||||
|
||||
self.auto_recording_manager = AutoRecordingManager(self.config, self.state_manager, self.event_system, self.camera_manager)
|
||||
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:
|
||||
@@ -86,10 +83,7 @@ class USDAVisionSystem:
|
||||
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"
|
||||
)
|
||||
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")
|
||||
|
||||
@@ -131,6 +125,17 @@ class USDAVisionSystem:
|
||||
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:
|
||||
@@ -147,11 +152,7 @@ class USDAVisionSystem:
|
||||
self.state_manager.set_system_started(True)
|
||||
|
||||
# Publish system started event
|
||||
self.event_system.publish(
|
||||
EventType.SYSTEM_SHUTDOWN, # We don't have SYSTEM_STARTED, using closest
|
||||
"main_system",
|
||||
{"action": "started", "timestamp": self.start_time.isoformat()}
|
||||
)
|
||||
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")
|
||||
@@ -161,89 +162,77 @@ class USDAVisionSystem:
|
||||
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()}
|
||||
)
|
||||
|
||||
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()
|
||||
"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
|
||||
@@ -252,31 +241,20 @@ class USDAVisionSystem:
|
||||
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("--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)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
# Create and run system
|
||||
system = USDAVisionSystem(args.config)
|
||||
|
||||
|
||||
# Override log level if specified
|
||||
if args.log_level:
|
||||
logging.getLogger().setLevel(getattr(logging, args.log_level))
|
||||
|
||||
|
||||
try:
|
||||
system.run()
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user