From 5b0ca302cc3ab715589cdca8061ce3ea68a4e11d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?LAPTOP-S9HJSOEB=5C=E6=98=8A=E5=A4=A9?= Date: Mon, 19 Oct 2026 09:30:10 +0800 Subject: [PATCH] =?UTF-8?q?=E8=8B=A5=E7=9B=B8=E6=9C=BA=E4=B8=8D=E5=AD=98?= =?UTF-8?q?=E5=9C=A8=EF=BC=8C=E5=88=99=E4=B8=8D=E5=8E=BB=E8=B0=83=E7=94=A8?= =?UTF-8?q?dll?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../camera/hik/ImageCaptureService.java | 125 +++++++++++++++--- .../camera/lx/LxCameraCapture.java | 6 +- .../camera/lx/LxCameraServiceImpl.java | 46 ++++++- 3 files changed, 154 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/example/lxcameraapi/service/IndustrialCamera/camera/hik/ImageCaptureService.java b/src/main/java/com/example/lxcameraapi/service/IndustrialCamera/camera/hik/ImageCaptureService.java index af4c087..f9dbd82 100644 --- a/src/main/java/com/example/lxcameraapi/service/IndustrialCamera/camera/hik/ImageCaptureService.java +++ b/src/main/java/com/example/lxcameraapi/service/IndustrialCamera/camera/hik/ImageCaptureService.java @@ -39,34 +39,83 @@ public class ImageCaptureService { @Resource private AppConfig appConfig; + + // SDK是否可用:DLL缺失/加载失败/版本不匹配时为false,此时所有相机操作直接返回,不影响主流程 + private volatile boolean sdkAvailable = true; + + // 是否已尝试过初始化(避免反复尝试加载不存在的DLL) + private volatile boolean sdkInitTried = false; + /** * 构造函数,初始化SDK + * 注意:未配置海康相机时不初始化SDK,也不会去加载任何DLL */ @PostConstruct public void init() { + List cameras = appConfig.getHikCamera(); + if (cameras == null || cameras.isEmpty()) { + log.info("未配置海康相机(hikCamera),跳过SDK初始化,不加载相机DLL"); + return; + } + initSdkAndCameras(cameras); + } + + /** + * 初始化SDK并打开配置的相机。 + * 任何异常或错误(UnsatisfiedLinkError/NoSuchFieldError/NoClassDefFoundError 等)都只记录日志, + * 绝不向上抛出,避免影响 Spring 容器启动。 + * + * @param cameras 相机配置,可为空 + */ + private synchronized void initSdkAndCameras(List cameras) { + if (sdkInitTried || !sdkAvailable) { + return; + } + sdkInitTried = true; + + int nRet; try { log.info("初始化海康相机SDK..."); - int nRet = MvCameraControl.MV_CC_Initialize(); - if (MV_OK != nRet) { - log.error("初始化SDK失败,errcode: [0x{}]", Integer.toHexString(nRet)); - } else { - isSdkInitialized = true; - log.info("SDK Version: {}", MV_CC_GetSDKVersion()); - log.info("SDK初始化成功"); - } - if (appConfig.getHikCamera() != null && !appConfig.getHikCamera().isEmpty()){ - initCamerasFromConfig(appConfig.getHikCamera()); + nRet = MvCameraControl.MV_CC_Initialize(); + } catch (Throwable t) { + log.error("海康相机SDK加载失败(DLL缺失或版本不匹配),相机功能不可用: {}", t.toString()); + sdkAvailable = false; + return; + } + + if (MV_OK != nRet) { + log.error("初始化SDK失败,errcode: [0x{}]", Integer.toHexString(nRet)); + return; + } + + isSdkInitialized = true; + try { + log.info("SDK Version: {}", MV_CC_GetSDKVersion()); + log.info("SDK初始化成功"); + if (cameras != null && !cameras.isEmpty()) { + initCamerasFromConfig(cameras); } - } catch (Exception e) { - log.error("初始化SDK异常", e); + } catch (Throwable t) { + log.error("SDK初始化后处理异常", t); } } + /** + * SDK是否可用(DLL已加载且初始化成功) + */ + public boolean isSdkAvailable() { + return sdkAvailable && isSdkInitialized; + } + /** * 销毁资源 */ @PreDestroy public void destroy() { + if (!sdkAvailable) { + handleMap.clear(); + return; + } log.info("开始清理相机资源..."); // 停止抓图并销毁所有句柄 @@ -90,6 +139,8 @@ public class ImageCaptureService { log.info("相机 {} 资源已清理", ip); } catch (Exception e) { log.error("清理相机 {} 资源时发生异常", ip, e); + } catch (Throwable t) { + log.warn("清理相机 {} 资源时失败(SDK不可用)", ip); } } @@ -102,6 +153,8 @@ public class ImageCaptureService { log.info("SDK反初始化成功"); } catch (Exception e) { log.error("SDK反初始化异常", e); + } catch (Throwable t) { + log.warn("SDK反初始化失败(SDK不可用)"); } } } @@ -112,6 +165,13 @@ public class ImageCaptureService { * @return 成功初始化的相机数量 */ public int initCameras(List ipList) { + if (!sdkAvailable) { + log.warn("海康相机SDK不可用(DLL未加载),跳过相机初始化"); + return 0; + } + if (!isSdkInitialized) { + initSdkAndCameras(appConfig.getHikCamera()); + } if (!isSdkInitialized) { log.error("SDK未初始化,无法初始化相机"); return 0; @@ -133,8 +193,10 @@ public class ImageCaptureService { return 0; } log.info("枚举到 {} 个设备", stDeviceList.size()); - } catch (CameraControlException e) { - log.error("枚举设备失败", e); + } catch (Throwable e) { + log.error("枚举设备失败,SDK不可用: {}", e.toString()); + sdkAvailable = false; + isSdkInitialized = false; return 0; } @@ -238,6 +300,9 @@ public class ImageCaptureService { } catch (CameraControlException e) { log.error("相机 {} 初始化异常", ip, e); return null; + } catch (Throwable t) { + log.error("相机 {} 初始化失败(SDK不可用)", ip, t); + return null; } } @@ -259,6 +324,13 @@ public class ImageCaptureService { * @return 是否成功 */ private boolean captureImage(String ip, String savePath, boolean isRetry) { + if (!sdkAvailable) { + log.warn("海康相机SDK不可用(DLL未加载),跳过拍照: {}", ip); + return false; + } + if (!isSdkInitialized) { + initSdkAndCameras(appConfig.getHikCamera()); + } if (!isSdkInitialized) { log.error("SDK未初始化"); return false; @@ -326,6 +398,9 @@ public class ImageCaptureService { } } return false; + } catch (Throwable t) { + log.error("拍照失败(SDK不可用)", t); + return false; } } @@ -455,17 +530,17 @@ public class ImageCaptureService { if (hCamera == null) return; try { MvCameraControl.MV_CC_StopGrabbing(hCamera); - } catch (Exception e) { + } catch (Throwable e) { log.warn("相机 {} 停止抓图异常", ip, e); } try { MvCameraControl.MV_CC_CloseDevice(hCamera); - } catch (Exception e) { + } catch (Throwable e) { log.warn("相机 {} 关闭设备异常", ip, e); } try { MvCameraControl.MV_CC_DestroyHandle(hCamera); - } catch (Exception e) { + } catch (Throwable e) { log.warn("相机 {} 销毁句柄异常", ip, e); } log.info("相机 {} 句柄已销毁", ip); @@ -476,6 +551,10 @@ public class ImageCaptureService { * @return 新句柄,失败返回null */ private Handle reconnectCamera(String ip) { + if (!sdkAvailable) { + log.warn("海康相机SDK不可用(DLL未加载),跳过重连: {}", ip); + return null; + } log.warn("相机 {} 开始重连...", ip); destroyCameraHandle(ip); @@ -495,6 +574,11 @@ public class ImageCaptureService { } } catch (Exception e) { log.warn("相机 {} 重连异常(第{}/{}次)", ip, i, RECONNECT_MAX_RETRIES, e); + } catch (Throwable t) { + log.warn("相机 {} 重连失败(SDK不可用)", ip, t); + sdkAvailable = false; + isSdkInitialized = false; + break; } if (i < RECONNECT_MAX_RETRIES) { @@ -520,6 +604,13 @@ public class ImageCaptureService { public List captureBatch(String ip, String basePath, int count) { List successPaths = new ArrayList<>(); + if (!sdkAvailable) { + log.warn("海康相机SDK不可用(DLL未加载),跳过拍照: {}", ip); + return successPaths; + } + if (!isSdkInitialized) { + initSdkAndCameras(appConfig.getHikCamera()); + } if (!isSdkInitialized) { log.error("SDK未初始化"); return successPaths; diff --git a/src/main/java/com/example/lxcameraapi/service/IndustrialCamera/camera/lx/LxCameraCapture.java b/src/main/java/com/example/lxcameraapi/service/IndustrialCamera/camera/lx/LxCameraCapture.java index 53c0d80..ead6861 100644 --- a/src/main/java/com/example/lxcameraapi/service/IndustrialCamera/camera/lx/LxCameraCapture.java +++ b/src/main/java/com/example/lxcameraapi/service/IndustrialCamera/camera/lx/LxCameraCapture.java @@ -50,9 +50,9 @@ public class LxCameraCapture { // 加载DLL库 this.dcLibrary = Native.loadLibrary(libraryPath, DcLibrary.class); log.info("LX相机SDK加载成功"); - } catch (Exception e) { - log.error("LX相机SDK加载失败", e); - throw new RuntimeException("加载LX相机SDK失败: " + e.getMessage()); + } catch (Throwable t) { + log.error("LX相机SDK加载失败", t); + throw new RuntimeException("加载LX相机SDK失败: " + t.toString()); } } diff --git a/src/main/java/com/example/lxcameraapi/service/IndustrialCamera/camera/lx/LxCameraServiceImpl.java b/src/main/java/com/example/lxcameraapi/service/IndustrialCamera/camera/lx/LxCameraServiceImpl.java index f41ddb2..bca2960 100644 --- a/src/main/java/com/example/lxcameraapi/service/IndustrialCamera/camera/lx/LxCameraServiceImpl.java +++ b/src/main/java/com/example/lxcameraapi/service/IndustrialCamera/camera/lx/LxCameraServiceImpl.java @@ -65,18 +65,36 @@ public class LxCameraServiceImpl implements LxCameraService { } } + // DLL是否可用:缺失/加载失败时为false,避免反复尝试加载不存在的DLL + private volatile boolean dllAvailable = true; + + // 是否已尝试过初始化(用于延迟初始化) + private volatile boolean initTried = false; + @PostConstruct public void init() { if (appConfig.getLxCamera() != null && !appConfig.getLxCamera().isEmpty()) { initCameras(); + } else { + log.info("未配置Lx相机(lxCamera),跳过SDK初始化,不加载相机DLL"); } } @Override public void initCameras() { - log.info("开始初始化所有Lx相机,数量: {}", appConfig.getLxCamera().size()); + List cameras = appConfig.getLxCamera(); + if (cameras == null || cameras.isEmpty()) { + log.info("未配置Lx相机(lxCamera),跳过初始化,不加载相机DLL"); + return; + } + if (!dllAvailable) { + log.warn("Lx相机DLL不可用(缺失或版本不匹配),跳过初始化"); + return; + } + + log.info("开始初始化所有Lx相机,数量: {}", cameras.size()); - for (AppConfig.Camera camera : appConfig.getLxCamera()) { + for (AppConfig.Camera camera : cameras) { try { // 复制DLL文件,一个相机对应一个独立的DLL String sourceDll = System.getProperty("user.dir") + "\\libs\\plc\\LxCameraApi.dll"; @@ -101,9 +119,16 @@ public class LxCameraServiceImpl implements LxCameraService { } catch (Exception e) { log.error("初始化相机 {} 时发生异常", camera.getIp(), e); + } catch (Error e) { + // DLL缺失/位数不匹配等会抛 UnsatisfiedLinkError(Error,不是Exception), + // 这里必须兜住,否则 @PostConstruct 失败会导致整个Spring容器启动失败 + log.error("初始化相机 {} 失败(DLL缺失或不可用),Lx相机功能不可用: {}", camera.getIp(), e.toString()); + dllAvailable = false; + break; } } + initTried = true; log.info("所有Lx相机初始化完成,成功: {}", instanceMap.size()); } @@ -214,8 +239,13 @@ public class LxCameraServiceImpl implements LxCameraService { log.info("相机 {} 开始拍照,配置: {}", cameraSn, config); DcLibrary instance = instanceMap.get(cameraSn); + if (instance == null && !initTried) { + // 延迟初始化:启动时尚未初始化过的情况下再试一次 + initCameras(); + instance = instanceMap.get(cameraSn); + } if (instance == null) { - return CaptureResult.failure(cameraSn, -3, "DcLibrary实例不存在"); + return CaptureResult.failure(cameraSn, -3, "DcLibrary实例不存在(Lx相机DLL未加载)"); } CaptureResult result = CaptureResult.success(cameraSn); @@ -414,6 +444,12 @@ public class LxCameraServiceImpl implements LxCameraService { return result; } } + } catch (Error e) { + log.error("相机 {} 第 {} 次拍照失败(SDK不可用)", cameraSn, retryCount, e); + stopStreamIfNeeded(cameraSn, instance); + result = CaptureResult.failure(cameraSn, -6, "拍照失败(SDK不可用): " + e.toString()); + result.withRetryCount(retryCount); + return result; } } @@ -487,6 +523,8 @@ public class LxCameraServiceImpl implements LxCameraService { } } catch (Exception e) { log.error("相机 {} 停止流时发生异常", cameraSn, e); + } catch (Error e) { + log.warn("相机 {} 停止流失败(SDK不可用): {}", cameraSn, e.toString()); } } } @@ -569,6 +607,8 @@ public class LxCameraServiceImpl implements LxCameraService { } } catch (Exception e) { log.error("相机 {} 关闭时发生异常", cameraSn, e); + } catch (Error e) { + log.warn("相机 {} 关闭失败(SDK不可用): {}", cameraSn, e.toString()); } handleMap.remove(cameraSn);