diff --git a/src/components/hikrobot_sc/hikrobot_dll.py b/src/components/hikrobot_sc/hikrobot_dll.py index 280b6b3..10f4f66 100644 --- a/src/components/hikrobot_sc/hikrobot_dll.py +++ b/src/components/hikrobot_sc/hikrobot_dll.py @@ -226,193 +226,114 @@ 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() - # 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 + 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}]") # Process chunk data currentDataTag = 0 chResultInfo = {} - - # 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: - # Process chunk data safely - try: - while stFrameData.nChunkDataLen > currentDataTag: - chunkLength = ctypes.c_uint32() - chunkID = ctypes.c_uint32() + 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 + # 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) + # 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 + # Convert network byte order to host byte order + chunkLength.value = socket.ntohl(chunkLength.value) + chunkID.value = socket.ntohl(chunkID.value) - # 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 + # Print the values + #print(f"chunkLength [{chunkLength.value}], chunkID: [{chunkID.value}]") - # 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)) + if chunkLength.value <= 0 or chunkLength.value > stFrameData.nChunkDataLen - 8 - currentDataTag: + break - 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 = {} + 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)) - 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() + chResultInfo = ctypes.string_at(chRawInfo, chunkLength.value) + chResultInfo = json.loads(chResultInfo.decode()) - # 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 + # if chResultInfo: + # print(f"chResultInfo OK") - # 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) + 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() - 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 + # 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 - # 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 - + # 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 - 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}") + 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}]") + else: + # print(f"Release frame buffer OK") + pass - # Return the processed frame and results return {"frame": img, "res": chResultInfo} - - except Exception as e: - logger.error(f"Unhandled exception in MV_VS_GetFrame: {e}") - import traceback - logger.error(f"Exception traceback: {traceback.format_exc()}") + else: + print(f"MV_VS_GetResultData fail! nRet [0x{nRet&0xFFFFFFFF:x}]") return None def print_device_info(device_info_list): diff --git a/src/components/hikrobot_sc/hikrobot_sc.py b/src/components/hikrobot_sc/hikrobot_sc.py index 8f7b0e0..4e66b61 100644 --- a/src/components/hikrobot_sc/hikrobot_sc.py +++ b/src/components/hikrobot_sc/hikrobot_sc.py @@ -241,47 +241,23 @@ class HikrobotSmartCamera(Component): def refresh_module_list(self): """ - Refresh the module list after switching schemes with enhanced error handling. + Refresh the module list after switching schemes. Returns: bool: True if successful, False otherwise """ - # 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 not self.connected: + self.log.error("Cannot refresh module list: Camera not connected") + return False + + # Get the first camera handle if len(self.cam_list) == 0: self.log.error("Cannot refresh module list: No camera handles available") return False - # 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) + handle = self.cam_list[0]["handle"] 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: @@ -295,13 +271,6 @@ 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") @@ -311,10 +280,7 @@ class HikrobotSmartCamera(Component): # Log the full exception traceback for debugging import traceback self.log.error(f"Exception traceback: {traceback.format_exc()}") - - # Return True for non-critical exceptions to avoid blocking the workflow - self.log.info("Treating exception as non-critical, continuing operation") - return True + return False def switch_scheme(self, solution_name, retry_count=0, max_retries=2): """