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:
@@ -4,6 +4,7 @@ Recording-related API routes.
|
||||
|
||||
import logging
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from typing import Optional
|
||||
from ...camera.manager import CameraManager
|
||||
from ..models import StartRecordingResponse, StopRecordingResponse, StartRecordingRequest
|
||||
from ...core.timezone_utils import format_filename_timestamp
|
||||
@@ -17,12 +18,19 @@ def register_recording_routes(
|
||||
"""Register recording-related routes"""
|
||||
|
||||
@app.post("/cameras/{camera_name}/start-recording", response_model=StartRecordingResponse)
|
||||
async def start_recording(camera_name: str, request: StartRecordingRequest):
|
||||
async def start_recording(camera_name: str, request: Optional[StartRecordingRequest] = None):
|
||||
"""Manually start recording for a camera"""
|
||||
try:
|
||||
if not camera_manager:
|
||||
logger.error("Camera manager not available")
|
||||
raise HTTPException(status_code=503, detail="Camera manager not available")
|
||||
|
||||
# Handle case where request body might be None or empty
|
||||
if request is None:
|
||||
request = StartRecordingRequest()
|
||||
|
||||
logger.info(f"📹 Starting recording for {camera_name} - filename: {request.filename}, exposure_ms: {request.exposure_ms}, gain: {request.gain}, fps: {request.fps}")
|
||||
|
||||
success = camera_manager.manual_start_recording(
|
||||
camera_name=camera_name,
|
||||
filename=request.filename,
|
||||
@@ -37,19 +45,28 @@ def register_recording_routes(
|
||||
if request.filename:
|
||||
timestamp = format_filename_timestamp()
|
||||
actual_filename = f"{timestamp}_{request.filename}"
|
||||
else:
|
||||
timestamp = format_filename_timestamp()
|
||||
camera_config = camera_manager.get_camera_config(camera_name)
|
||||
video_format = camera_config.video_format if camera_config else "mp4"
|
||||
actual_filename = f"{camera_name}_manual_{timestamp}.{video_format}"
|
||||
|
||||
logger.info(f"✅ Recording started successfully for {camera_name}: {actual_filename}")
|
||||
return StartRecordingResponse(
|
||||
success=True,
|
||||
message=f"Recording started for {camera_name}",
|
||||
filename=actual_filename
|
||||
)
|
||||
else:
|
||||
logger.error(f"❌ Failed to start recording for {camera_name} - manual_start_recording returned False")
|
||||
return StartRecordingResponse(
|
||||
success=False,
|
||||
message=f"Failed to start recording for {camera_name}"
|
||||
message=f"Failed to start recording for {camera_name}. Check camera status and logs."
|
||||
)
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error starting recording: {e}")
|
||||
logger.error(f"❌ Error starting recording for {camera_name}: {e}", exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@app.post("/cameras/{camera_name}/stop-recording", response_model=StopRecordingResponse)
|
||||
|
||||
@@ -188,6 +188,34 @@ class CameraMonitor:
|
||||
|
||||
self.logger.info(f"Attempting to initialize camera {camera_name} for availability test...")
|
||||
|
||||
# Check if camera is already in use by recorder or streamer before trying to initialize
|
||||
recorder = self.camera_manager.camera_recorders.get(camera_name) if self.camera_manager else None
|
||||
streamer = self.camera_manager.camera_streamers.get(camera_name) if self.camera_manager else None
|
||||
|
||||
camera_in_use = False
|
||||
if recorder and recorder.hCamera:
|
||||
try:
|
||||
# Check if recorder has camera open
|
||||
if mvsdk.CameraIsOpened(recorder.hCamera):
|
||||
camera_in_use = True
|
||||
self.logger.info(f"Camera {camera_name} is already in use by recorder (handle: {recorder.hCamera})")
|
||||
except:
|
||||
pass
|
||||
|
||||
if not camera_in_use and streamer and streamer.hCamera:
|
||||
try:
|
||||
# Check if streamer has camera open
|
||||
if mvsdk.CameraIsOpened(streamer.hCamera):
|
||||
camera_in_use = True
|
||||
self.logger.info(f"Camera {camera_name} is already in use by streamer (handle: {streamer.hCamera})")
|
||||
except:
|
||||
pass
|
||||
|
||||
# If camera is already in use, mark as available (since it's working, just occupied)
|
||||
if camera_in_use:
|
||||
self.logger.info(f"Camera {camera_name} is in use by system components - marking as available")
|
||||
return "available", "Camera is in use by system", self._get_device_info_dict(device_info)
|
||||
|
||||
# Suppress output to avoid MVCAMAPI error messages during camera testing
|
||||
hCamera = None
|
||||
try:
|
||||
@@ -195,7 +223,26 @@ class CameraMonitor:
|
||||
hCamera = mvsdk.CameraInit(device_info, -1, -1)
|
||||
self.logger.info(f"Camera {camera_name} initialized successfully, starting test capture...")
|
||||
except mvsdk.CameraException as init_e:
|
||||
self.logger.warning(f"CameraInit failed for {camera_name}: {init_e.message} (error_code: {init_e.error_code})")
|
||||
error_msg = f"CameraInit failed for {camera_name}: {init_e.message} (error_code: {init_e.error_code})"
|
||||
|
||||
# Special handling for error code 32774 (camera already in use)
|
||||
if init_e.error_code == 32774:
|
||||
error_msg += " - Camera may be in use by another process or resource conflict. "
|
||||
error_msg += "This camera may still be functional if accessed through existing recorder/streamer."
|
||||
self.logger.warning(error_msg)
|
||||
# Mark as "available" but with warning, since it might be usable through existing connections
|
||||
# The UI can show a warning but camera operations might still work
|
||||
try:
|
||||
device_info_dict = self._get_device_info_dict(device_info)
|
||||
device_info_dict["init_error"] = "Camera appears in use (error 32774) but may be accessible"
|
||||
device_info_dict["init_error_code"] = 32774
|
||||
except Exception as dev_info_e:
|
||||
self.logger.warning(f"Failed to get device info dict after CameraInit failure: {dev_info_e}")
|
||||
device_info_dict = None
|
||||
return "available", "Camera may be in use (error 32774) - check if recorder/streamer is active", device_info_dict
|
||||
else:
|
||||
self.logger.warning(error_msg)
|
||||
|
||||
# Get device info dict before returning - wrap in try/except in case device_info is corrupted
|
||||
try:
|
||||
device_info_dict = self._get_device_info_dict(device_info)
|
||||
|
||||
@@ -26,6 +26,13 @@ from ..core.events import EventSystem, publish_recording_started, publish_record
|
||||
from ..core.timezone_utils import now_atlanta, format_filename_timestamp
|
||||
from .sdk_config import ensure_sdk_initialized
|
||||
from .utils import suppress_camera_errors
|
||||
from .constants import (
|
||||
CAMERA_GET_BUFFER_TIMEOUT,
|
||||
CAMERA_INIT_TIMEOUT,
|
||||
CAMERA_TEST_CAPTURE_TIMEOUT,
|
||||
DEFAULT_VIDEO_FPS,
|
||||
BRIEF_PAUSE_SLEEP,
|
||||
)
|
||||
|
||||
|
||||
class CameraRecorder:
|
||||
|
||||
@@ -46,7 +46,12 @@ class USDAVisionSystem:
|
||||
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)
|
||||
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
|
||||
|
||||
@@ -172,14 +172,15 @@ class MQTTClient:
|
||||
self.connected = True
|
||||
self.state_manager.set_mqtt_connected(True)
|
||||
self.event_system.publish(EventType.MQTT_CONNECTED, "mqtt_client")
|
||||
self.logger.info("🔗 MQTT CONNECTED to broker successfully")
|
||||
self.logger.info(f"🔗 MQTT CONNECTED to broker successfully at {self.mqtt_config.broker_host}:{self.mqtt_config.broker_port}")
|
||||
print(f"🔗 MQTT CONNECTED: {self.mqtt_config.broker_host}:{self.mqtt_config.broker_port}")
|
||||
|
||||
# Subscribe to topics immediately after connection
|
||||
self._subscribe_to_topics()
|
||||
self.logger.info(f"📋 MQTT subscribed to {len(self.mqtt_config.topics)} topics")
|
||||
else:
|
||||
self.connected = False
|
||||
self.logger.error(f"❌ MQTT CONNECTION FAILED with return code {rc}")
|
||||
self.logger.error(f"❌ MQTT CONNECTION FAILED with return code {rc} to {self.mqtt_config.broker_host}:{self.mqtt_config.broker_port}")
|
||||
print(f"❌ MQTT CONNECTION FAILED: {self.mqtt_config.broker_host}:{self.mqtt_config.broker_port} (code: {rc})")
|
||||
|
||||
def _on_disconnect(self, client, userdata, rc) -> None:
|
||||
@@ -201,7 +202,8 @@ class MQTTClient:
|
||||
topic = msg.topic
|
||||
payload = msg.payload.decode("utf-8").strip()
|
||||
|
||||
self.logger.debug(f"MQTT message received - Topic: {topic}, Payload: {payload}")
|
||||
# Log at INFO level so we can see messages in production
|
||||
self.logger.info(f"📡 MQTT MESSAGE RECEIVED - Topic: {topic}, Payload: '{payload}'")
|
||||
|
||||
# Update MQTT activity and tracking
|
||||
self.state_manager.update_mqtt_activity()
|
||||
@@ -211,19 +213,20 @@ class MQTTClient:
|
||||
# Get machine name from topic
|
||||
machine_name = self.topic_to_machine.get(topic)
|
||||
if not machine_name:
|
||||
self.logger.warning(f"❓ MQTT UNKNOWN TOPIC: {topic}")
|
||||
print(f"❓ MQTT UNKNOWN TOPIC: {topic}")
|
||||
self.logger.warning(f"❓ MQTT UNKNOWN TOPIC: {topic} (payload: '{payload}')")
|
||||
print(f"❓ MQTT UNKNOWN TOPIC: {topic} (payload: '{payload}')")
|
||||
return
|
||||
|
||||
# Show MQTT message on console
|
||||
# Show MQTT message on console with machine name
|
||||
print(f"📡 MQTT MESSAGE: {machine_name} → {payload}")
|
||||
self.logger.info(f"📡 Processing MQTT message for machine '{machine_name}': '{payload}'")
|
||||
|
||||
# Handle the message
|
||||
self.message_handler.handle_message(machine_name, topic, payload)
|
||||
|
||||
except Exception as e:
|
||||
self.error_count += 1
|
||||
self.logger.error(f"Error processing MQTT message: {e}")
|
||||
self.logger.error(f"❌ Error processing MQTT message: {e}", exc_info=True)
|
||||
|
||||
def publish_message(self, topic: str, payload: str, qos: int = 0, retain: bool = False) -> bool:
|
||||
"""Publish a message to MQTT broker"""
|
||||
|
||||
@@ -31,10 +31,11 @@ class MQTTMessageHandler:
|
||||
self.message_count += 1
|
||||
self.last_message_time = datetime.now()
|
||||
|
||||
self.logger.info(f"Processing MQTT message - Machine: {machine_name}, Topic: {topic}, Payload: {payload}")
|
||||
self.logger.info(f"📡 Processing MQTT message - Machine: {machine_name}, Topic: {topic}, Payload: '{payload}'")
|
||||
|
||||
# Normalize payload
|
||||
normalized_payload = self._normalize_payload(payload)
|
||||
self.logger.info(f"📡 Normalized payload '{payload}' -> '{normalized_payload}' for machine {machine_name}")
|
||||
|
||||
# Update machine state
|
||||
state_changed = self.state_manager.update_machine_state(name=machine_name, state=normalized_payload, message=payload, topic=topic)
|
||||
@@ -44,9 +45,12 @@ class MQTTMessageHandler:
|
||||
|
||||
# Publish state change event if state actually changed
|
||||
if state_changed:
|
||||
self.logger.info(f"📡 MQTT: Machine {machine_name} state changed to: {normalized_payload}")
|
||||
self.logger.info(f"📡 Publishing MACHINE_STATE_CHANGED event for {machine_name} -> {normalized_payload}")
|
||||
publish_machine_state_changed(machine_name=machine_name, state=normalized_payload, source="mqtt_handler")
|
||||
|
||||
self.logger.info(f"Machine {machine_name} state changed to: {normalized_payload}")
|
||||
self.logger.info(f"✅ Published MACHINE_STATE_CHANGED event for {machine_name} -> {normalized_payload}")
|
||||
else:
|
||||
self.logger.info(f"📡 Machine {machine_name} state unchanged (still {normalized_payload}) - no event published")
|
||||
|
||||
# Log the message for debugging
|
||||
self._log_message_details(machine_name, topic, payload, normalized_payload)
|
||||
|
||||
@@ -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