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.
This commit is contained in:
@@ -30,13 +30,13 @@ sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
||||
from usda_vision_system.core.config import Config
|
||||
from usda_vision_system.camera.recorder import CameraRecorder
|
||||
from usda_vision_system.core.state_manager import StateManager
|
||||
from usda_vision_system.core.events import EventSystem
|
||||
from usda_vision_system.core.events import EventSystem, EventType, Event
|
||||
|
||||
|
||||
class StandaloneAutoRecorder:
|
||||
"""Standalone auto-recording system that monitors MQTT and controls cameras directly"""
|
||||
|
||||
def __init__(self, config_path: str = "config.json", config: Optional[Config] = None):
|
||||
def __init__(self, config_path: str = "config.json", config: Optional[Config] = None, camera_manager=None, state_manager=None, event_system=None):
|
||||
# Load configuration
|
||||
if config:
|
||||
self.config = config
|
||||
@@ -45,9 +45,9 @@ class StandaloneAutoRecorder:
|
||||
|
||||
# Setup logging (only if not already configured)
|
||||
if not logging.getLogger().handlers:
|
||||
# Use WARNING level by default to reduce INFO log noise
|
||||
log_level = getattr(self.config.system, 'log_level', 'WARNING')
|
||||
log_level_num = getattr(logging, log_level.upper(), logging.WARNING)
|
||||
# Use configured log level
|
||||
log_level = getattr(self.config.system, 'log_level', 'INFO')
|
||||
log_level_num = getattr(logging, log_level.upper(), logging.INFO)
|
||||
logging.basicConfig(
|
||||
level=log_level_num,
|
||||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||
@@ -59,16 +59,17 @@ class StandaloneAutoRecorder:
|
||||
self.logger = logging.getLogger(__name__)
|
||||
# Ensure this logger respects the configured log level
|
||||
if hasattr(self.config, 'system') and hasattr(self.config.system, 'log_level'):
|
||||
self.logger.setLevel(getattr(logging, self.config.system.log_level.upper(), logging.WARNING))
|
||||
self.logger.setLevel(getattr(logging, self.config.system.log_level.upper(), logging.INFO))
|
||||
|
||||
# Initialize components
|
||||
self.state_manager = StateManager()
|
||||
self.event_system = EventSystem()
|
||||
# Use provided components or create new ones
|
||||
self.state_manager = state_manager if state_manager else StateManager()
|
||||
self.event_system = event_system if event_system else EventSystem()
|
||||
self.camera_manager = camera_manager
|
||||
|
||||
# MQTT client
|
||||
# MQTT client (only if not using event system)
|
||||
self.mqtt_client: Optional[mqtt.Client] = None
|
||||
|
||||
# Camera recorders
|
||||
# Camera recorders (only if not using camera_manager)
|
||||
self.camera_recorders: Dict[str, CameraRecorder] = {}
|
||||
self.active_recordings: Dict[str, str] = {} # camera_name -> filename
|
||||
|
||||
@@ -82,8 +83,17 @@ class StandaloneAutoRecorder:
|
||||
self.running = False
|
||||
self._stop_event = threading.Event()
|
||||
|
||||
# Subscribe to machine state change events if using event system
|
||||
if self.event_system and self.camera_manager:
|
||||
self.event_system.subscribe(EventType.MACHINE_STATE_CHANGED, self._on_machine_state_changed)
|
||||
self.logger.info("Subscribed to MACHINE_STATE_CHANGED events")
|
||||
|
||||
self.logger.info("Standalone Auto-Recorder initialized")
|
||||
self.logger.info(f"Machine-Camera mapping: {self.machine_camera_map}")
|
||||
if self.camera_manager:
|
||||
self.logger.info("Using provided camera_manager for recording")
|
||||
else:
|
||||
self.logger.info("Will create own camera recorders (standalone mode)")
|
||||
|
||||
def _build_machine_camera_map(self) -> Dict[str, str]:
|
||||
"""Build mapping from machine topics to camera names"""
|
||||
@@ -162,80 +172,137 @@ class StandaloneAutoRecorder:
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error processing MQTT message: {e}")
|
||||
|
||||
def _on_machine_state_changed(self, event: Event):
|
||||
"""Handle machine state change event from event system"""
|
||||
try:
|
||||
machine_name = event.data.get("machine_name")
|
||||
state = event.data.get("state", "").lower()
|
||||
source = event.source
|
||||
|
||||
self.logger.info(f"📡 AUTO-RECORDER: Received MACHINE_STATE_CHANGED event from {source}")
|
||||
self.logger.info(f"📡 AUTO-RECORDER: Event data - machine_name: {machine_name}, state: {state}")
|
||||
|
||||
if not machine_name or not state:
|
||||
self.logger.warning(f"❌ AUTO-RECORDER: Invalid event data - machine_name: {machine_name}, state: {state}")
|
||||
return
|
||||
|
||||
self._handle_machine_state_change(machine_name, state)
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"❌ AUTO-RECORDER: Error handling machine state change event: {e}", exc_info=True)
|
||||
|
||||
def _handle_machine_state_change(self, machine_name: str, state: str):
|
||||
"""Handle machine state change"""
|
||||
try:
|
||||
# Check if we have a camera for this machine
|
||||
camera_name = self.machine_camera_map.get(machine_name)
|
||||
if not camera_name:
|
||||
self.logger.debug(f"No camera mapped to machine: {machine_name}")
|
||||
return
|
||||
|
||||
self.logger.info(f"Handling state change: {machine_name} ({camera_name}) -> {state}")
|
||||
self.logger.info(f"📡 MQTT: Machine {machine_name} ({camera_name}) -> {state}")
|
||||
|
||||
if state == "on":
|
||||
self._start_recording(camera_name, machine_name)
|
||||
elif state == "off":
|
||||
self._stop_recording(camera_name, machine_name)
|
||||
else:
|
||||
self.logger.debug(f"Ignoring state '{state}' for machine {machine_name}")
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error handling machine state change: {e}")
|
||||
self.logger.error(f"Error handling machine state change: {e}", exc_info=True)
|
||||
|
||||
def _start_recording(self, camera_name: str, machine_name: str):
|
||||
"""Start recording for a camera"""
|
||||
try:
|
||||
# Check if already recording
|
||||
if camera_name in self.active_recordings:
|
||||
self.logger.warning(f"Camera {camera_name} is already recording")
|
||||
camera_info = self.state_manager.get_camera_status(camera_name) if self.state_manager else None
|
||||
if camera_info and camera_info.is_recording:
|
||||
self.logger.info(f"Camera {camera_name} is already recording, skipping")
|
||||
return
|
||||
|
||||
# Get or create camera recorder
|
||||
recorder = self._get_camera_recorder(camera_name)
|
||||
if not recorder:
|
||||
self.logger.error(f"Failed to get recorder for camera {camera_name}")
|
||||
return
|
||||
# Use camera_manager if available, otherwise use standalone recorder
|
||||
if self.camera_manager:
|
||||
# Generate filename with timestamp and machine info
|
||||
from ..core.timezone_utils import format_filename_timestamp
|
||||
timestamp = format_filename_timestamp()
|
||||
camera_config = self.config.get_camera_by_name(camera_name)
|
||||
video_format = camera_config.video_format if camera_config else "mp4"
|
||||
filename = f"{camera_name}_auto_{machine_name}_{timestamp}.{video_format}"
|
||||
|
||||
# Generate filename with timestamp and machine info
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
camera_config = self.config.get_camera_by_name(camera_name)
|
||||
video_format = camera_config.video_format if camera_config else "mp4"
|
||||
filename = f"{camera_name}_auto_{machine_name}_{timestamp}.{video_format}"
|
||||
# Use camera manager to start recording with camera's default settings
|
||||
success = self.camera_manager.manual_start_recording(
|
||||
camera_name=camera_name,
|
||||
filename=filename,
|
||||
exposure_ms=camera_config.exposure_ms if camera_config else None,
|
||||
gain=camera_config.gain if camera_config else None,
|
||||
fps=camera_config.target_fps if camera_config else None
|
||||
)
|
||||
|
||||
# Start recording
|
||||
success = recorder.start_recording(filename)
|
||||
if success:
|
||||
self.active_recordings[camera_name] = filename
|
||||
self.logger.info(f"✅ Started recording: {camera_name} -> {filename}")
|
||||
if success:
|
||||
self.logger.info(f"✅ Started auto-recording: {camera_name} -> {filename}")
|
||||
self.active_recordings[camera_name] = filename
|
||||
else:
|
||||
self.logger.error(f"❌ Failed to start auto-recording for camera {camera_name}")
|
||||
else:
|
||||
self.logger.error(f"❌ Failed to start recording for camera {camera_name}")
|
||||
# Standalone mode - use own recorder
|
||||
recorder = self._get_camera_recorder(camera_name)
|
||||
if not recorder:
|
||||
self.logger.error(f"Failed to get recorder for camera {camera_name}")
|
||||
return
|
||||
|
||||
# Generate filename with timestamp and machine info
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
camera_config = self.config.get_camera_by_name(camera_name)
|
||||
video_format = camera_config.video_format if camera_config else "mp4"
|
||||
filename = f"{camera_name}_auto_{machine_name}_{timestamp}.{video_format}"
|
||||
|
||||
# Start recording
|
||||
success = recorder.start_recording(filename)
|
||||
if success:
|
||||
self.active_recordings[camera_name] = filename
|
||||
self.logger.info(f"✅ Started recording: {camera_name} -> {filename}")
|
||||
else:
|
||||
self.logger.error(f"❌ Failed to start recording for camera {camera_name}")
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error starting recording for {camera_name}: {e}")
|
||||
self.logger.error(f"Error starting recording for {camera_name}: {e}", exc_info=True)
|
||||
|
||||
def _stop_recording(self, camera_name: str, machine_name: str):
|
||||
"""Stop recording for a camera"""
|
||||
try:
|
||||
# Check if recording
|
||||
if camera_name not in self.active_recordings:
|
||||
self.logger.warning(f"Camera {camera_name} is not recording")
|
||||
return
|
||||
|
||||
# Get recorder
|
||||
recorder = self._get_camera_recorder(camera_name)
|
||||
if not recorder:
|
||||
self.logger.error(f"Failed to get recorder for camera {camera_name}")
|
||||
return
|
||||
|
||||
# Stop recording
|
||||
filename = self.active_recordings.pop(camera_name)
|
||||
success = recorder.stop_recording()
|
||||
|
||||
if success:
|
||||
self.logger.info(f"✅ Stopped recording: {camera_name} -> {filename}")
|
||||
# Use camera_manager if available
|
||||
if self.camera_manager:
|
||||
success = self.camera_manager.manual_stop_recording(camera_name)
|
||||
if success:
|
||||
self.logger.info(f"✅ Stopped auto-recording: {camera_name}")
|
||||
if camera_name in self.active_recordings:
|
||||
filename = self.active_recordings.pop(camera_name)
|
||||
self.logger.debug(f"Recording filename was: {filename}")
|
||||
else:
|
||||
self.logger.warning(f"Camera {camera_name} may not have been recording")
|
||||
else:
|
||||
self.logger.error(f"❌ Failed to stop recording for camera {camera_name}")
|
||||
# Standalone mode - use own recorder
|
||||
if camera_name not in self.active_recordings:
|
||||
self.logger.warning(f"Camera {camera_name} is not recording")
|
||||
return
|
||||
|
||||
recorder = self._get_camera_recorder(camera_name)
|
||||
if not recorder:
|
||||
self.logger.error(f"Failed to get recorder for camera {camera_name}")
|
||||
return
|
||||
|
||||
# Stop recording
|
||||
filename = self.active_recordings.pop(camera_name)
|
||||
success = recorder.stop_recording()
|
||||
|
||||
if success:
|
||||
self.logger.info(f"✅ Stopped recording: {camera_name} -> {filename}")
|
||||
else:
|
||||
self.logger.error(f"❌ Failed to stop recording for camera {camera_name}")
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error stopping recording for {camera_name}: {e}")
|
||||
self.logger.error(f"Error stopping recording for {camera_name}: {e}", exc_info=True)
|
||||
|
||||
def _get_camera_recorder(self, camera_name: str) -> Optional[CameraRecorder]:
|
||||
"""Get or create camera recorder"""
|
||||
@@ -356,19 +423,27 @@ class StandaloneAutoRecorder:
|
||||
try:
|
||||
self.logger.info("Starting Standalone Auto-Recorder...")
|
||||
|
||||
# Setup MQTT
|
||||
# If using event system and camera_manager, we don't need our own MQTT client
|
||||
if self.event_system and self.camera_manager:
|
||||
self.logger.info("Using event system - no need for separate MQTT client")
|
||||
self.running = True
|
||||
self.logger.info("✅ Standalone Auto-Recorder started successfully (event-based mode)")
|
||||
return True
|
||||
|
||||
# Otherwise, setup MQTT client for standalone mode
|
||||
if not self._setup_mqtt():
|
||||
self.logger.error("Failed to setup MQTT client")
|
||||
return False
|
||||
|
||||
# Wait for MQTT connection
|
||||
time.sleep(2)
|
||||
|
||||
self.running = True
|
||||
self.logger.info("✅ Standalone Auto-Recorder started successfully")
|
||||
self.logger.info("✅ Standalone Auto-Recorder started successfully (standalone MQTT mode)")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Failed to start auto-recorder: {e}")
|
||||
self.logger.error(f"Failed to start auto-recorder: {e}", exc_info=True)
|
||||
return False
|
||||
|
||||
def stop(self) -> bool:
|
||||
|
||||
Reference in New Issue
Block a user