This commit is contained in:
edo-neo 2025-08-21 14:19:46 +02:00
parent 6cfcbbeb65
commit 7989f6c166
2 changed files with 141 additions and 41 deletions

View File

@ -238,6 +238,99 @@ class HikrobotSmartCamera(Component):
except Exception as e:
self.log.error(f"Error getting debug camera state: {e}")
def is_camera_busy(self, handle=None):
"""
Check if the camera is currently busy.
This method checks the camera's device status and acquisition status to determine
if it's currently busy with operations that would prevent scheme switching.
Args:
handle: Camera handle. If None, uses the first camera handle.
Returns:
bool: True if the camera is busy, False otherwise
"""
# Check if camera is connected
if not self.connected:
self.log.warning("Cannot check if camera is busy: Camera not connected")
return True # Assume busy if not connected
# Get camera handle if not provided
if handle is None:
if len(self.cam_list) == 0:
self.log.warning("Cannot check if camera is busy: No camera handles available")
return True # Assume busy if no handles
handle = self.cam_list[0]["handle"]
# Check if an operation is already in progress
if self.current_operation is not None:
self.log.info(f"Camera is busy with operation: {self.current_operation}")
return True
try:
self.log.info("Checking if camera is busy...")
# Try to get the device status
device_status = c_uint()
nRet = mv_lib.MV_VS_GetEnumValue(handle, b"DeviceStatus", byref(device_status))
# If we get the 0x80030100 error, the camera is busy
if nRet != MV_VS_OK:
error_code = nRet & 0xFFFFFFFF
if error_code == 0x80030100: # MV_VS_E_GC_GENERIC
self.log.warning("Camera is busy: Detected MV_VS_E_GC_GENERIC error (0x80030100) when checking device status")
return True
else:
self.log.warning(f"Error checking device status, error code: 0x{error_code:x}")
else:
# Log device status value
self.log.info(f"Device status: {device_status.value}")
# Device status values (based on documentation and testing):
# 0: Device is idle
# 1: Device is busy with acquisition
# 2: Device is busy with processing
# 3: Device is busy with transfer
if device_status.value > 0:
self.log.warning(f"Camera appears busy: Device status is {device_status.value}")
return True
# Try to get the acquisition status
acquisition_status = c_uint()
nRet = mv_lib.MV_VS_GetEnumValue(handle, b"AcquisitionStatus", byref(acquisition_status))
# If we get the 0x80030100 error, the camera is busy
if nRet != MV_VS_OK:
error_code = nRet & 0xFFFFFFFF
if error_code == 0x80030100: # MV_VS_E_GC_GENERIC
self.log.warning("Camera is busy: Detected MV_VS_E_GC_GENERIC error (0x80030100) when checking acquisition status")
return True
else:
self.log.warning(f"Error checking acquisition status, error code: 0x{error_code:x}")
else:
# Log acquisition status value
self.log.info(f"Acquisition status: {acquisition_status.value}")
# Acquisition status values (based on documentation and testing):
# 0: Acquisition is idle
# 1: Acquisition is active
if acquisition_status.value > 0:
self.log.warning(f"Camera appears busy: Acquisition status is {acquisition_status.value}")
return True
# If we've reached this point, the camera is not busy
self.log.info("Camera is not busy, safe to proceed with operations")
return False
except Exception as e:
self.log.error(f"Error checking if camera is busy: {e}")
# Log the full exception traceback for debugging
import traceback
self.log.error(f"Exception traceback: {traceback.format_exc()}")
return True # Assume busy if there's an error
def refresh_module_list(self):
"""
@ -282,7 +375,7 @@ class HikrobotSmartCamera(Component):
self.log.error(f"Exception traceback: {traceback.format_exc()}")
return False
def switch_scheme(self, solution_name, retry_count=0, max_retries=2):
def switch_scheme(self, solution_name, retry_count=0, max_retries=2, force=False, wait_timeout=30):
"""
Switch to a different scheme/solution using the API as described in the documentation.
@ -290,6 +383,8 @@ class HikrobotSmartCamera(Component):
solution_name: The name of the solution to switch to
retry_count: Current retry attempt (used internally for recursion)
max_retries: Maximum number of retry attempts for error recovery
force: If True, attempt to switch scheme even if camera appears busy
wait_timeout: Maximum time in seconds to wait for camera to become available
Returns:
bool: True if the scheme switching process was successfully initiated, False otherwise
@ -314,6 +409,43 @@ class HikrobotSmartCamera(Component):
handle = self.cam_list[0]["handle"]
# Check if camera is busy before proceeding
if not force:
is_busy = self.is_camera_busy(handle)
if is_busy:
self.log.warning(f"Camera is busy. Waiting for it to become available before switching to scheme: {solution_name}")
# Wait for the camera to become available with timeout
start_time = time.time()
wait_interval = 1.0 # Start with 1 second interval
while is_busy and (time.time() - start_time) < wait_timeout:
# Wait with increasing interval (up to 3 seconds)
self.log.info(f"Waiting {wait_interval:.1f} seconds before checking camera status again...")
time.sleep(wait_interval)
# Increase wait interval for next iteration (up to 3 seconds)
wait_interval = min(wait_interval * 1.5, 3.0)
# Check if camera is still busy
is_busy = self.is_camera_busy(handle)
if not is_busy:
self.log.info("Camera is now available, proceeding with scheme switch")
break
# If still busy after timeout, either retry or fail
if is_busy:
elapsed = time.time() - start_time
self.log.warning(f"Camera still busy after waiting {elapsed:.1f} seconds")
if retry_count < max_retries:
self.log.info(f"Retrying scheme switch (attempt {retry_count+1}/{max_retries})")
return self.switch_scheme(solution_name, retry_count + 1, max_retries, force, wait_timeout)
else:
self.log.warning("Maximum retries reached, forcing scheme switch")
# Continue with force=True on the last attempt
force = True
# Get current camera state for debugging
self.log.info("Getting camera state before scheme switch:")
self._debug_camera_state(handle)

View File

@ -4,7 +4,7 @@ import time
from PyQt5.QtCore import pyqtSignal, QTimer
from PyQt5.QtGui import QColor, QImage, QPalette, QPixmap
from PyQt5.QtWidgets import QHeaderView, QProgressBar, QTableWidgetItem, QDialog, QVBoxLayout, QLabel, QApplication
from PyQt5.QtWidgets import QHeaderView, QProgressBar, QTableWidgetItem, QDialog, QVBoxLayout, QLabel
from src.lib.helpers.blocking_dialog import BlockingDialog
from src.ui.helpers import calc_foreground_color
@ -172,46 +172,14 @@ class Test_Vision(Test_Test):
if self.components[cam_type].current_operation != "switch_scheme":
self.log.info(f"Initiating scheme switch to: {target_scheme}")
# Check if camera is busy before attempting to switch scheme
if self.components[cam_type].is_camera_busy():
self.log.warning(f"Camera is busy. Waiting for it to become available before switching to scheme: {target_scheme}")
# Update the progress dialog to show waiting status
progress_dialog.status_label.setText("Status: Waiting for camera to become available...")
progress_dialog.debug_label.setText("Debug info: Camera is busy with another operation")
# Try up to 3 times with increasing delays
max_busy_retries = 3
for retry in range(max_busy_retries):
# Process events to keep UI responsive
QApplication.processEvents()
# Wait with increasing delay
wait_time = (retry + 1) * 2 # 2, 4, 6 seconds
self.log.info(f"Waiting {wait_time} seconds before retry {retry+1}/{max_busy_retries}")
# Update progress dialog
progress_dialog.debug_label.setText(f"Debug info: Waiting {wait_time} seconds before retry {retry+1}/{max_busy_retries}")
# Wait
time.sleep(wait_time)
# Check again if camera is still busy
if not self.components[cam_type].is_camera_busy():
self.log.info("Camera is now available")
break
if retry == max_busy_retries - 1:
self.log.warning("Camera still busy after maximum retries, attempting to force scheme switch")
progress_dialog.debug_label.setText("Debug info: Forcing scheme switch after maximum retries")
# Update the progress dialog to show we're checking camera status
progress_dialog.status_label.setText("Status: Checking camera availability...")
progress_dialog.debug_label.setText("Debug info: Checking if camera is busy before switching scheme")
# Attempt to switch scheme, with force=True on the last retry if still busy
force_switch = self.components[cam_type].is_camera_busy()
if force_switch:
self.log.warning("Forcing scheme switch even though camera appears busy")
self.components[cam_type].switch_scheme(target_scheme, force=True)
else:
self.components[cam_type].switch_scheme(target_scheme)
# Start the scheme switching process with wait_timeout parameter
# This will automatically wait if the camera is busy
self.log.info("Starting scheme switch with automatic busy detection and waiting")
self.components[cam_type].switch_scheme(target_scheme, wait_timeout=25) # 25 seconds timeout to leave 5 seconds for the dialog timeout
# Show the dialog and wait for it to complete
result = progress_dialog.exec_()