This commit is contained in:
edo-neo 2025-08-21 14:02:23 +02:00
parent f2aa761ad9
commit e93d25c33e
2 changed files with 221 additions and 108 deletions

View File

@ -226,114 +226,193 @@ mv_lib.MV_VS_SetStringValue.restype = c_int
def MV_VS_GetFrame(handle, name=""):
"""
Get a frame from the camera with enhanced error handling to prevent access violations.
Args:
handle: Camera handle
name: Optional name for debugging
Returns:
dict: Dictionary containing frame data and results, or None if an error occurred
"""
# Create a logger for this function
import logging
logger = logging.getLogger("MV_VS_GetFrame")
try:
stFrameData = MV_VS_DATA()
stFrameData = MV_VS_DATA()
nRet = mv_lib.MV_VS_GetResultData(handle, byref(stFrameData), 500)
if nRet == MV_VS_OK:
# Print frame information
# print(f"Frame Width: [{stFrameData.nImageWidth}], Height: [{stFrameData.nImageHeight}], ImageLen: [{stFrameData.nImageLen}]")
# Get result data with timeout
nRet = mv_lib.MV_VS_GetResultData(handle, byref(stFrameData), 500)
if nRet != MV_VS_OK:
error_code = nRet & 0xFFFFFFFF
logger.error(f"MV_VS_GetResultData failed! Error code: 0x{error_code:x}")
# Special handling for 0x80030100 (MV_VS_E_GC_GENERIC)
if error_code == 0x80030100:
logger.warning("Detected MV_VS_E_GC_GENERIC error (0x80030100) during frame acquisition")
logger.info("This is a non-critical error, will return None")
return None
# Validate frame data
if stFrameData.pImage is None:
logger.error("Frame data contains null image pointer")
return None
if stFrameData.nImageLen <= 0:
logger.error(f"Invalid image length: {stFrameData.nImageLen}")
return None
# Process chunk data
currentDataTag = 0
chResultInfo = {}
while stFrameData.nChunkDataLen > currentDataTag:
chunkLength = ctypes.c_uint32()
chunkID = ctypes.c_uint32()
# Calculate source addresses for chunkLength and chunkID
source_address_length = ctypes.addressof(
stFrameData.pChunkData.contents) + stFrameData.nChunkDataLen - 4 - currentDataTag
source_address_id = ctypes.addressof(
stFrameData.pChunkData.contents) + stFrameData.nChunkDataLen - 8 - currentDataTag
# Copy data from source to chunkLength and chunkID
ctypes.memmove(ctypes.byref(chunkLength), source_address_length, 4)
ctypes.memmove(ctypes.byref(chunkID), source_address_id, 4)
# Convert network byte order to host byte order
chunkLength.value = socket.ntohl(chunkLength.value)
chunkID.value = socket.ntohl(chunkID.value)
# Print the values
#print(f"chunkLength [{chunkLength.value}], chunkID: [{chunkID.value}]")
if chunkLength.value <= 0 or chunkLength.value > stFrameData.nChunkDataLen - 8 - currentDataTag:
break
if chunkID.value == CHUNK_RESULT_PORT: # Result data in JSON format
chRawInfo = ctypes.cast(stFrameData.pChunkData, ctypes.POINTER(ctypes.c_char))
chRawInfo = ctypes.cast(ctypes.addressof(
chRawInfo.contents) + stFrameData.nChunkDataLen - 8 - chunkLength.value - currentDataTag,
ctypes.POINTER(ctypes.c_char))
chResultInfo = ctypes.string_at(chRawInfo, chunkLength.value)
chResultInfo = json.loads(chResultInfo.decode())
# if chResultInfo:
# print(f"chResultInfo OK")
elif chunkID.value == CHUNK_MASK_IMAGE_PORT: # Mask image data
maskModID = ctypes.c_uint32()
maskModFormat = ctypes.c_uint32()
maskModWidth = ctypes.c_uint32()
maskModHeight = ctypes.c_uint32()
# Calculate source addresses
source_address_id = ctypes.addressof(
stFrameData.pChunkData.contents) + stFrameData.nChunkDataLen - 8 - chunkLength.value - currentDataTag
source_address_format = source_address_id + 4
source_address_width = source_address_id + 8
source_address_height = source_address_id + 12
# Copy data from source addresses to corresponding variables
ctypes.memmove(ctypes.byref(maskModID), source_address_id, 4)
ctypes.memmove(ctypes.byref(maskModFormat), source_address_format, 4)
ctypes.memmove(ctypes.byref(maskModWidth), source_address_width, 4)
ctypes.memmove(ctypes.byref(maskModHeight), source_address_height, 4)
# print(f"Mask ModID[{maskModID.value}], ModFormat[{maskModFormat.value}], ModWidth[{maskModWidth.value}], ModHeight[{maskModHeight.value}], ")
try:
chMaskModImageData = ctypes.cast(ctypes.create_string_buffer(chunkLength.value - 16 + 1),
ctypes.POINTER(ctypes.c_char))
if chMaskModImageData:
ctypes.memset(chMaskModImageData, 0, chunkLength.value - 16 + 1)
chImageData = ctypes.cast(ctypes.addressof(
stFrameData.pChunkData.contents) + stFrameData.nChunkDataLen - 8 - chunkLength.value - currentDataTag + 16,
ctypes.POINTER(ctypes.c_char))
ctypes.memmove(chMaskModImageData, chImageData, chunkLength.value - 16)
# print(f"Mask data length [{chunkLength.value - 16}]")
except (MemoryError, TypeError) as e:
print(f"Error allocating or copying memory: {e}")
currentDataTag = currentDataTag + 8 + chunkLength.value
# end process chunk data
m_stJpgParam = MV_VS_JPG_PARAM()
m_stJpgParam.pBufInput = ctypes.cast(stFrameData.pImage,ctypes.POINTER(ctypes.c_ubyte))
m_stJpgParam.nBufInputLen = stFrameData.nImageLen
jpegContents = ctypes.string_at(m_stJpgParam.pBufInput, m_stJpgParam.nBufInputLen)
# with open(f"tmp/test_{name}.jpg", "wb") as f:
# f.write(jpegContents)
jpg_as_np = np.frombuffer(jpegContents, dtype=np.uint8)
# Convert the NumPy array to an OpenCV image.
img = cv2.imdecode(jpg_as_np, flags=1)
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
# Release frame buffer
nRet = mv_lib.MV_VS_ReleaseResultData(handle, ctypes.byref(stFrameData))
if nRet != MV_VS_OK:
print(f"Release frame buffer fail! nRet [0x{nRet:x}]")
# Check if chunk data is valid
if stFrameData.pChunkData is None:
logger.warning("Chunk data pointer is null, skipping chunk processing")
elif stFrameData.nChunkDataLen <= 0:
logger.warning(f"Invalid chunk data length: {stFrameData.nChunkDataLen}")
else:
# print(f"Release frame buffer OK")
pass
# Process chunk data safely
try:
while stFrameData.nChunkDataLen > currentDataTag:
chunkLength = ctypes.c_uint32()
chunkID = ctypes.c_uint32()
# Calculate source addresses for chunkLength and chunkID
try:
source_address_length = ctypes.addressof(
stFrameData.pChunkData.contents) + stFrameData.nChunkDataLen - 4 - currentDataTag
source_address_id = ctypes.addressof(
stFrameData.pChunkData.contents) + stFrameData.nChunkDataLen - 8 - currentDataTag
# Copy data from source to chunkLength and chunkID
ctypes.memmove(ctypes.byref(chunkLength), source_address_length, 4)
ctypes.memmove(ctypes.byref(chunkID), source_address_id, 4)
# Convert network byte order to host byte order
chunkLength.value = socket.ntohl(chunkLength.value)
chunkID.value = socket.ntohl(chunkID.value)
except (AttributeError, TypeError) as e:
logger.error(f"Error accessing chunk data memory: {e}")
break
# Validate chunk length
if chunkLength.value <= 0 or chunkLength.value > stFrameData.nChunkDataLen - 8 - currentDataTag:
logger.warning(f"Invalid chunk length: {chunkLength.value}, breaking chunk processing")
break
# Process different chunk types
try:
if chunkID.value == CHUNK_RESULT_PORT: # Result data in JSON format
chRawInfo = ctypes.cast(stFrameData.pChunkData, ctypes.POINTER(ctypes.c_char))
chRawInfo = ctypes.cast(ctypes.addressof(
chRawInfo.contents) + stFrameData.nChunkDataLen - 8 - chunkLength.value - currentDataTag,
ctypes.POINTER(ctypes.c_char))
chResultInfo_bytes = ctypes.string_at(chRawInfo, chunkLength.value)
try:
chResultInfo = json.loads(chResultInfo_bytes.decode())
except json.JSONDecodeError as e:
logger.error(f"Error decoding JSON result data: {e}")
chResultInfo = {}
elif chunkID.value == CHUNK_MASK_IMAGE_PORT: # Mask image data
maskModID = ctypes.c_uint32()
maskModFormat = ctypes.c_uint32()
maskModWidth = ctypes.c_uint32()
maskModHeight = ctypes.c_uint32()
# Calculate source addresses
source_address_id = ctypes.addressof(
stFrameData.pChunkData.contents) + stFrameData.nChunkDataLen - 8 - chunkLength.value - currentDataTag
source_address_format = source_address_id + 4
source_address_width = source_address_id + 8
source_address_height = source_address_id + 12
# Copy data from source addresses to corresponding variables
ctypes.memmove(ctypes.byref(maskModID), source_address_id, 4)
ctypes.memmove(ctypes.byref(maskModFormat), source_address_format, 4)
ctypes.memmove(ctypes.byref(maskModWidth), source_address_width, 4)
ctypes.memmove(ctypes.byref(maskModHeight), source_address_height, 4)
try:
chMaskModImageData = ctypes.cast(ctypes.create_string_buffer(chunkLength.value - 16 + 1),
ctypes.POINTER(ctypes.c_char))
if chMaskModImageData:
ctypes.memset(chMaskModImageData, 0, chunkLength.value - 16 + 1)
chImageData = ctypes.cast(ctypes.addressof(
stFrameData.pChunkData.contents) + stFrameData.nChunkDataLen - 8 - chunkLength.value - currentDataTag + 16,
ctypes.POINTER(ctypes.c_char))
ctypes.memmove(chMaskModImageData, chImageData, chunkLength.value - 16)
except (MemoryError, TypeError, AttributeError) as e:
logger.error(f"Error allocating or copying memory for mask data: {e}")
except Exception as e:
logger.error(f"Error processing chunk ID {chunkID.value}: {e}")
# Continue with next chunk
# Move to next chunk
currentDataTag = currentDataTag + 8 + chunkLength.value
except Exception as e:
logger.error(f"Error during chunk data processing: {e}")
# Continue with image processing despite chunk errors
# Process image data
try:
m_stJpgParam = MV_VS_JPG_PARAM()
m_stJpgParam.pBufInput = ctypes.cast(stFrameData.pImage, ctypes.POINTER(ctypes.c_ubyte))
m_stJpgParam.nBufInputLen = stFrameData.nImageLen
# Safely get JPEG contents
try:
jpegContents = ctypes.string_at(m_stJpgParam.pBufInput, m_stJpgParam.nBufInputLen)
except (TypeError, ValueError) as e:
logger.error(f"Error accessing JPEG data: {e}")
# Release frame buffer before returning
mv_lib.MV_VS_ReleaseResultData(handle, ctypes.byref(stFrameData))
return None
# Convert to numpy array and then to OpenCV image
try:
jpg_as_np = np.frombuffer(jpegContents, dtype=np.uint8)
img = cv2.imdecode(jpg_as_np, flags=1)
if img is None:
logger.error("Failed to decode JPEG data")
# Release frame buffer before returning
mv_lib.MV_VS_ReleaseResultData(handle, ctypes.byref(stFrameData))
return None
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
except Exception as e:
logger.error(f"Error converting JPEG to image: {e}")
# Release frame buffer before returning
mv_lib.MV_VS_ReleaseResultData(handle, ctypes.byref(stFrameData))
return None
except Exception as e:
logger.error(f"Error processing image data: {e}")
# Release frame buffer before returning
mv_lib.MV_VS_ReleaseResultData(handle, ctypes.byref(stFrameData))
return None
# Release frame buffer
try:
nRet = mv_lib.MV_VS_ReleaseResultData(handle, ctypes.byref(stFrameData))
if nRet != MV_VS_OK:
error_code = nRet & 0xFFFFFFFF
logger.warning(f"Release frame buffer failed! Error code: 0x{error_code:x}")
except Exception as e:
logger.error(f"Exception during frame buffer release: {e}")
# Return the processed frame and results
return {"frame": img, "res": chResultInfo}
else:
print(f"MV_VS_GetResultData fail! nRet [0x{nRet&0xFFFFFFFF:x}]")
except Exception as e:
logger.error(f"Unhandled exception in MV_VS_GetFrame: {e}")
import traceback
logger.error(f"Exception traceback: {traceback.format_exc()}")
return None
def print_device_info(device_info_list):

View File

@ -241,23 +241,47 @@ class HikrobotSmartCamera(Component):
def refresh_module_list(self):
"""
Refresh the module list after switching schemes.
Refresh the module list after switching schemes with enhanced error handling.
Returns:
bool: True if successful, False otherwise
"""
if not self.connected:
self.log.error("Cannot refresh module list: Camera not connected")
return False
# Get the first camera handle
# Check connection status with retry mechanism
retry_count = 0
max_retries = 3
while retry_count < max_retries:
if not self.connected:
self.log.warning(f"Camera not connected during module list refresh (attempt {retry_count+1}/{max_retries})")
if retry_count == max_retries - 1:
self.log.error("Cannot refresh module list: Camera not connected after retries")
return False
# Wait and retry
self.log.info("Waiting 1 second before retrying connection check")
time.sleep(1)
retry_count += 1
continue
else:
break
# Get the first camera handle with validation
if len(self.cam_list) == 0:
self.log.error("Cannot refresh module list: No camera handles available")
return False
handle = self.cam_list[0]["handle"]
# Validate handle before using it
handle = self.cam_list[0].get("handle")
if handle is None:
self.log.error("Cannot refresh module list: Camera handle is None")
return False
# Add a delay before refreshing to allow camera to stabilize
time.sleep(0.5)
try:
self.log.info("Attempting to refresh module list")
# Command to refresh module list
nRet = mv_lib.MV_VS_SetCommandValue(handle, b"CommandRefreshModuleList")
if nRet != MV_VS_OK:
@ -271,6 +295,13 @@ class HikrobotSmartCamera(Component):
# We'll return True here to avoid treating this as a fatal error
return True
# For other error codes, check if we should retry
if retry_count < max_retries - 1:
retry_count += 1
self.log.warning(f"Retrying module list refresh after error (attempt {retry_count}/{max_retries})")
time.sleep(1) # Wait before retrying
return self.refresh_module_list() # Recursive retry
return False
self.log.info("Module list refreshed successfully")
@ -280,7 +311,10 @@ class HikrobotSmartCamera(Component):
# Log the full exception traceback for debugging
import traceback
self.log.error(f"Exception traceback: {traceback.format_exc()}")
return False
# Return True for non-critical exceptions to avoid blocking the workflow
self.log.info("Treating exception as non-critical, continuing operation")
return True
def switch_scheme(self, solution_name, retry_count=0, max_retries=2):
"""