From d9210a08f429e8b87141c988472483477211ebba Mon Sep 17 00:00:00 2001 From: gengby <858962040@qq.com> Date: Sat, 6 Dec 2025 15:33:06 +0800 Subject: [PATCH] =?UTF-8?q?rev:=E6=8B=8D=E7=85=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/constant/DictConstantPool.java | 4 +- .../hikvision/HikvisionSnapshotUtil.java | 317 +++++++++++++++--- .../nl/common/hikvision/JnaCrashMonitor.java | 170 ++++++++++ .../ext/service/impl/AcsToWmsServiceImpl.java | 18 +- wms/nladmin-ui/package.json | 2 +- .../wms/basedata/storagevehicleinfo/index.vue | 162 ++++++--- 6 files changed, 562 insertions(+), 111 deletions(-) create mode 100644 wms/nladmin-system/nlsso-server/src/main/java/org/nl/common/hikvision/JnaCrashMonitor.java diff --git a/wms/nladmin-system/nlsso-server/src/main/java/org/nl/common/domain/constant/DictConstantPool.java b/wms/nladmin-system/nlsso-server/src/main/java/org/nl/common/domain/constant/DictConstantPool.java index 8ec78a4..6a0c0f7 100644 --- a/wms/nladmin-system/nlsso-server/src/main/java/org/nl/common/domain/constant/DictConstantPool.java +++ b/wms/nladmin-system/nlsso-server/src/main/java/org/nl/common/domain/constant/DictConstantPool.java @@ -42,8 +42,8 @@ public class DictConstantPool { @PostConstruct public void initHikvisonConfig(){ System.out.println("初始化海康威视配置:"+ this); - HikvisionSnapshotUtil.ins = this.ins; - HikvisionSnapshotUtil.path = this.path; +// HikvisionSnapshotUtil.ins = this.ins; +// HikvisionSnapshotUtil.path = this.path; HikvisionSnapshotUtil.ip = this.ip; HikvisionSnapshotUtil.port = this.port; HikvisionSnapshotUtil.username = this.username; diff --git a/wms/nladmin-system/nlsso-server/src/main/java/org/nl/common/hikvision/HikvisionSnapshotUtil.java b/wms/nladmin-system/nlsso-server/src/main/java/org/nl/common/hikvision/HikvisionSnapshotUtil.java index 96ae4a0..8c2fa73 100644 --- a/wms/nladmin-system/nlsso-server/src/main/java/org/nl/common/hikvision/HikvisionSnapshotUtil.java +++ b/wms/nladmin-system/nlsso-server/src/main/java/org/nl/common/hikvision/HikvisionSnapshotUtil.java @@ -1,6 +1,5 @@ package org.nl.common.hikvision; -import cn.hutool.core.date.DateUtil; import com.sun.jna.Library; import com.sun.jna.Native; import com.sun.jna.Structure; @@ -11,14 +10,21 @@ import org.springframework.stereotype.Service; import java.io.File; import java.time.LocalDate; import java.time.format.DateTimeFormatter; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantLock; @Service @Slf4j public class HikvisionSnapshotUtil { - public static String ins; //HCNetSDK.dll文件路径 - public static String path; //文件存储位置 + public static String ins = "C:\\Users\\Administrator\\Desktop\\hik\\HCNetSDK.dll"; //HCNetSDK.dll文件路径 + public static String path = "C:\\software\\wms\\images"; //文件存储位置 public static String ip; //海康威视设备网络搜索应用中摄像头ip public static Short port;//海康威视设备网络搜索应用中摄像头port public static String username;//海康威视设备网络搜索应用中摄像头管理账号admin @@ -26,27 +32,19 @@ public class HikvisionSnapshotUtil { public static String channelHttp;//默认 public static Integer channel;//默认1 + // SDK 初始化状态标记,确保全局只初始化一次 + private static final AtomicBoolean sdkInitialized = new AtomicBoolean(false); + // 用于同步 SDK 初始化和清理操作的锁 + private static final ReentrantLock sdkLock = new ReentrantLock(); + // DLL 加载状态标记 + private static final AtomicBoolean dllLoaded = new AtomicBoolean(false); + // DLL 实例,延迟加载 + private static volatile HCNetSDK sdkInstance = null; + // 用于同步 DLL 加载的锁 + private static final ReentrantLock dllLoadLock = new ReentrantLock(); + // HCNetSDK 接口映射 public interface HCNetSDK extends Library { - HCNetSDK INSTANCE = Holder.INSTANCE; - - class Holder { - private static final HCNetSDK INSTANCE; - - static { - if (StringUtils.isBlank(HikvisionSnapshotUtil.ins)) { - throw new RuntimeException("HCNetSDK.dll 路径未设置,请检查 hikvison.ins 配置"); - } - try { - System.out.println("加载 HCNetSDK.dll => " + HikvisionSnapshotUtil.ins); - INSTANCE = Native.load("C:\\Users\\Administrator\\Desktop\\hik\\HCNetSDK.dll", HCNetSDK.class); - System.out.println("加载 HCNetSDK.dll 成功"); - } catch (Throwable e) { - System.err.println("加载 HCNetSDK.dll 失败,路径:" + HikvisionSnapshotUtil.ins + "异常信息:" + e.getMessage()); - throw e; - } - } - } //初始化 SDK 库 boolean NET_DVR_Init(); @@ -95,57 +93,272 @@ public class HikvisionSnapshotUtil { public short wPicQuality; } - public static void sync截图(String imageName, String taskCode) { + /** + * 超时保护 防止 JNA 调用永久阻塞 + */ + private static boolean executeCaptureWithTimeout(HCNetSDK sdk, int userId, int channel, + NET_DVR_JPEGPARA jpegPara, String sFileUrl, + long timeoutSeconds) throws Throwable { + Future future = Executors.newSingleThreadExecutor().submit(() -> { + try { + return sdk.NET_DVR_CaptureJPEGPicture(userId, channel, jpegPara, sFileUrl); + } catch (Throwable e) { + log.info("[SDK] 抓图执行时发生异常", e); + throw new RuntimeException("抓图执行异常", e); + } + }); + + try { + return future.get(timeoutSeconds, TimeUnit.SECONDS); + } catch (TimeoutException e) { + future.cancel(true); + log.info("[SDK] 抓图操作超时({}秒),已取消。userId={}, channel={}, file={}", + timeoutSeconds, userId, channel, sFileUrl); + throw new TimeoutException("抓图操作超时: " + timeoutSeconds + "秒"); + } catch (ExecutionException e) { + log.info(e.getMessage()); + Throwable cause = e.getCause(); + if (cause != null) { + throw cause; + } + throw e; + } + } + + /** + * 获取 HCNetSDK 实例 + */ + private static HCNetSDK getSdkInstance() { + if (sdkInstance == null) { + dllLoadLock.lock(); + try { + // 双重检查 + if (sdkInstance == null) { + if (StringUtils.isBlank(ins)) { + throw new RuntimeException("HCNetSDK.dll 路径未设置,请检查 hikvison.ins 配置。可能原因:配置未加载或配置项缺失"); + } + try { + log.info("[SDK] 开始加载 HCNetSDK.dll => {}", ins); + sdkInstance = Native.load(ins, HCNetSDK.class); + dllLoaded.set(true); + log.info("[SDK] 加载 HCNetSDK.dll 成功"); + } catch (Throwable e) { + log.info("[SDK] 加载 HCNetSDK.dll 失败,路径:{},异常信息: {}", ins, e.getMessage(), e); + throw new RuntimeException("加载 HCNetSDK.dll 失败: " + e.getMessage() + ",路径: " + ins, e); + } + } + } finally { + dllLoadLock.unlock(); + } + } + return sdkInstance; + } + + /** + * 截图 + * + * @param imageName 图片名称 + * @param taskCode 任务编码 + */ + public static void syncSnap(String imageName, String taskCode) { if (StringUtils.isEmpty(imageName)) { - log.warn("图片名称未定义"); + log.info("[SDK] 图片名称未定义,跳过截图"); + return; + } + + // 参数校验 + if (StringUtils.isBlank(ip) || port == null || StringUtils.isBlank(username) + || StringUtils.isBlank(password) || channel == null) { + log.info("[SDK] 海康威视配置参数不完整,无法执行截图。ip={}, port={}, username={}, channel={}", + ip, port, username, channel); + return; + } + + // 获取 SDK 实例 + HCNetSDK sdk; + try { + sdk = getSdkInstance(); + } catch (RuntimeException e) { + log.info("[SDK] 获取 SDK 实例失败,无法执行截图", e); return; } LocalDate now = LocalDate.now(); String datePath = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); - String imagePath = "C:" + File.separator + "software" + File.separator + "wms" + File.separator + "images" + File.separator + datePath; + // 使用配置的路径,如果没有配置则使用默认路径 +// String basePath = StringUtils.isNotBlank(path) ? path : +// "C:" + File.separator + "software" + File.separator + "wms" + File.separator + "images"; + String imagePath = path + File.separator + datePath; File dir = new File(imagePath); if (!dir.exists()) { - dir.mkdirs(); + boolean created = dir.mkdirs(); + if (!created) { + log.info("[SDK] 创建图片目录失败: {}", imagePath); + return; + } } String sFileUrl = imagePath + File.separator + imageName + "_" + taskCode + ".jpg"; + log.info("[SDK] 开始截图,保存路径: {}", sFileUrl); - if (!HCNetSDK.INSTANCE.NET_DVR_Init()) { - log.warn("[SDK] 初始化失败"); - return; - } - - NET_DVR_DEVICEINFO_V30 deviceInfo = new NET_DVR_DEVICEINFO_V30(); - int userId = HCNetSDK.INSTANCE.NET_DVR_Login_V30(ip, port, username, password, deviceInfo); - if (userId < 0) { - log.warn("[SDK] 登录失败"); - HCNetSDK.INSTANCE.NET_DVR_Cleanup(); - return; - } - - NET_DVR_JPEGPARA jpegPara = new NET_DVR_JPEGPARA(); - jpegPara.wPicSize = 2; - jpegPara.wPicQuality = 0; - + // 使用锁确保 SDK 操作的线程安全 + boolean lockAcquired = false; try { - boolean result = HCNetSDK.INSTANCE.NET_DVR_CaptureJPEGPicture(userId, channel, jpegPara, sFileUrl); - if (result) { - log.info("[SDK] 抓图成功: " + sFileUrl); + // 尝试获取锁,最多等待 5 秒,避免死锁 + lockAcquired = sdkLock.tryLock(5, TimeUnit.SECONDS); + if (!lockAcquired) { + log.info("[SDK] 获取 SDK 操作锁超时,跳过本次截图操作。可能存在死锁或长时间占用锁的情况。"); + return; + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + log.info("[SDK] 获取锁时线程被中断", e); + return; + } + + int userId = -1; + boolean initSuccess = false; + try { + log.info("[SDK] 已获取锁,开始执行 SDK 操作"); + + // 初始化 SDK只初始化一次 + if (!sdkInitialized.get()) { + try { + initSuccess = sdk.NET_DVR_Init(); + if (!initSuccess) { + log.info("[SDK] SDK 初始化失败,可能原因:SDK 已被初始化或初始化参数错误"); + return; + } + sdkInitialized.set(true); + log.info("[SDK] SDK 初始化成功"); + } catch (Throwable e) { + log.info("[SDK] SDK 初始化时发生异常", e); + return; + } } else { - // 失败重试一次 - result = HCNetSDK.INSTANCE.NET_DVR_CaptureJPEGPicture(userId, channel, jpegPara, sFileUrl); + initSuccess = true; + log.info("[SDK] SDK 已初始化,跳过初始化步骤"); + } + + // 登录设备 + NET_DVR_DEVICEINFO_V30 deviceInfo = new NET_DVR_DEVICEINFO_V30(); + try { + userId = sdk.NET_DVR_Login_V30(ip, port, username, password, deviceInfo); + if (userId < 0) { + log.info("[SDK] 登录设备失败,userId={},请检查设备连接和账号密码。ip={}, port={}, username={}", + userId, ip, port, username); + return; + } + log.info("[SDK] 设备登录成功,userId={}", userId); + } catch (Throwable e) { + log.info("[SDK] 登录设备时发生异常,ip={}, port={}, username={}", ip, port, username, e); + return; + } + + // 配置抓图参数 + NET_DVR_JPEGPARA jpegPara = new NET_DVR_JPEGPARA(); + jpegPara.wPicSize = 2; + jpegPara.wPicQuality = 0; + + // 执行抓图 + boolean result = false; + try { + log.info("[SDK] 开始执行抓图操作,userId={}, channel={}", userId, channel); + + // 记录 JNA 调用前的状态 + try { + JnaCrashMonitor.logBeforeJnaCall("NET_DVR_CaptureJPEGPicture", userId, channel, sFileUrl); + } catch (Throwable e) { + // 忽略监控日志异常 + } + + // 执行抓图 设置超时保护,避免永久阻塞 + result = executeCaptureWithTimeout(sdk, userId, channel, jpegPara, sFileUrl, 10); + + // 记录 JNA 调用后的状态 + try { + JnaCrashMonitor.logAfterJnaCall("NET_DVR_CaptureJPEGPicture", result); + } catch (Throwable e) { + } + if (result) { - log.info("[SDK] 抓图成功(第二次尝试): " + sFileUrl); + log.info("[SDK] 抓图成功: {}", sFileUrl); } else { - log.warn("[SDK] 抓图失败: " + sFileUrl); + // 失败重试一次 + log.info("[SDK] 第一次抓图失败,进行重试..."); + try { + Thread.sleep(500); // 等待500ms后重试 + result = executeCaptureWithTimeout(sdk, userId, channel, jpegPara, sFileUrl, 10); + if (result) { + log.info("[SDK] 抓图成功(第二次尝试): {}", sFileUrl); + } else { + log.info("[SDK] 抓图失败(重试后仍失败): {},请检查设备连接和通道配置。userId={}, channel={}", + sFileUrl, userId, channel); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + log.info("[SDK] 重试抓图时线程被中断", e); + } catch (Throwable e) { + log.info("[SDK] 重试抓图时发生异常: {}", sFileUrl, e); + try { + JnaCrashMonitor.logJnaCallError("NET_DVR_CaptureJPEGPicture(重试)", e, userId, channel, sFileUrl); + } catch (Throwable ignored) { + } + } + } + } catch (Throwable e) { + log.info("[SDK] 抓图过程中发生异常: {}, userId={}, channel={}", sFileUrl, userId, channel, e); + if (e instanceof Error) { + log.info("[SDK] 发生严重错误(Error),可能导致 JVM 不稳定", e); + } + try { + JnaCrashMonitor.logJnaCallError("NET_DVR_CaptureJPEGPicture", e, userId, channel, sFileUrl); + } catch (Throwable ignored) { } } + + } catch (Throwable e) { + log.info("[SDK] 截图过程中发生未预期的异常", e); + if (e instanceof Error) { + log.info("[SDK] 发生严重错误(Error),可能导致 JVM 崩溃", e); + } } finally { - HCNetSDK.INSTANCE.NET_DVR_Logout(userId); - HCNetSDK.INSTANCE.NET_DVR_Cleanup(); + log.info("[SDK] 进入finally"); + try { + if (userId >= 0 && sdk != null) { + try { + boolean logoutResult = sdk.NET_DVR_Logout(userId); + if (!logoutResult) { + log.info("[SDK] 注销登录失败,userId={}", userId); + } else { + log.info("[SDK] 注销登录成功,userId={}", userId); + } + } catch (Throwable e) { + log.info("[SDK] 注销登录时发生异常,userId={}", userId, e); + } + } else { + log.info("[SDK] 无需注销登录,userId={}", userId); + } + } catch (Throwable e) { + log.info("[SDK] 清理资源时发生异常", e); + } + + // 确保锁被释放 + if (lockAcquired) { + log.info("[SDK] 开始释放锁"); + try { + sdkLock.unlock(); + log.info("[SDK] 截图操作完成,已释放锁"); + } catch (IllegalMonitorStateException e) { + log.info("[SDK] 释放锁时发生异常,可能锁状态异常", e); + } catch (Throwable e) { + log.info("[SDK] 释放锁时发生未知异常", e); + } + log.info("[SDK] 释放锁完成"); + } + log.info("[SDK] 结束finally"); } } diff --git a/wms/nladmin-system/nlsso-server/src/main/java/org/nl/common/hikvision/JnaCrashMonitor.java b/wms/nladmin-system/nlsso-server/src/main/java/org/nl/common/hikvision/JnaCrashMonitor.java new file mode 100644 index 0000000..006d2e6 --- /dev/null +++ b/wms/nladmin-system/nlsso-server/src/main/java/org/nl/common/hikvision/JnaCrashMonitor.java @@ -0,0 +1,170 @@ +package org.nl.common.hikvision; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.ThreadMXBean; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * JNA/DLL 崩溃监控和诊断工具 + * 用于监控 JNA 调用本地 DLL 时的异常情况 + * + * @author System + */ +@Component +@Slf4j +public class JnaCrashMonitor { + + private static final String CRASH_LOG_DIR = "C:\\software\\wms\\logs\\crash"; + private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + @PostConstruct + public void init() { + // 创建崩溃日志目录 + File logDir = new File(CRASH_LOG_DIR); + if (!logDir.exists()) { + logDir.mkdirs(); + } + + // 设置未捕获异常处理器 + Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> { + logCrashInfo("未捕获异常", thread, throwable); + }); + + // 添加 JVM 关闭钩子,用于记录崩溃信息 + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + log.info("[JNA监控] JVM 正在关闭,记录系统状态..."); + logSystemState("JVM关闭"); + })); + + log.info("[JNA监控] JNA 崩溃监控已初始化,日志目录: {}", CRASH_LOG_DIR); + } + + /** + * 记录崩溃信息 + */ + public static void logCrashInfo(String event, Thread thread, Throwable throwable) { + String timestamp = LocalDateTime.now().format(DATE_FORMATTER); + String logFile = CRASH_LOG_DIR + File.separator + "crash_" + + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + ".log"; + + try (FileWriter fw = new FileWriter(logFile, true); + PrintWriter pw = new PrintWriter(fw)) { + + pw.println(repeatString("=", 80)); + pw.println("时间: " + timestamp); + pw.println("事件: " + event); + pw.println("线程: " + (thread != null ? thread.getName() : "未知")); + pw.println("异常类型: " + (throwable != null ? throwable.getClass().getName() : "未知")); + + if (throwable != null) { + pw.println("异常消息: " + throwable.getMessage()); + pw.println("堆栈跟踪:"); + throwable.printStackTrace(pw); + } + + // 记录系统状态 + logSystemStateToFile(pw); + + pw.println(repeatString("=", 80)); + pw.println(); + + } catch (IOException e) { + log.info("[JNA监控] 写入崩溃日志失败", e); + } + + // 同时记录到标准日志 + log.info("[JNA监控] {} - 线程: {}, 异常: {}", + event, + thread != null ? thread.getName() : "未知", + throwable != null ? throwable.getMessage() : "未知", + throwable); + } + + /** + * 记录系统状态 + */ + public static void logSystemState(String event) { + String logFile = CRASH_LOG_DIR + File.separator + "system_state_" + + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + ".log"; + + try (FileWriter fw = new FileWriter(logFile, true); + PrintWriter pw = new PrintWriter(fw)) { + + pw.println(repeatString("=", 80)); + pw.println("时间: " + LocalDateTime.now().format(DATE_FORMATTER)); + pw.println("事件: " + event); + logSystemStateToFile(pw); + pw.println(repeatString("=", 80)); + pw.println(); + + } catch (IOException e) { + log.info("[JNA监控] 写入系统状态日志失败", e); + } + } + + /** + * 将系统状态写入文件 + */ + private static void logSystemStateToFile(PrintWriter pw) { + // 内存信息 + MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean(); + pw.println("堆内存使用: " + (memoryBean.getHeapMemoryUsage().getUsed() / 1024 / 1024) + " MB / " + + (memoryBean.getHeapMemoryUsage().getMax() / 1024 / 1024) + " MB"); + pw.println("非堆内存使用: " + (memoryBean.getNonHeapMemoryUsage().getUsed() / 1024 / 1024) + " MB / " + + (memoryBean.getNonHeapMemoryUsage().getMax() / 1024 / 1024) + " MB"); + + // 线程信息 + ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); + pw.println("活动线程数: " + threadBean.getThreadCount()); + pw.println("峰值线程数: " + threadBean.getPeakThreadCount()); + + // JVM 信息 + pw.println("JVM 名称: " + ManagementFactory.getRuntimeMXBean().getVmName()); + pw.println("JVM 版本: " + ManagementFactory.getRuntimeMXBean().getVmVersion()); + pw.println("运行时间: " + (ManagementFactory.getRuntimeMXBean().getUptime() / 1000) + " 秒"); + } + + /** + * 重复字符串(兼容 Java 8) + */ + private static String repeatString(String str, int count) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < count; i++) { + sb.append(str); + } + return sb.toString(); + } + + /** + * 记录 JNA 调用前的状态 + */ + public static void logBeforeJnaCall(String methodName, Object... params) { + log.info("[JNA监控] 准备调用 JNA 方法: {}, 参数: {}", methodName, params); + } + + /** + * 记录 JNA 调用后的状态 + */ + public static void logAfterJnaCall(String methodName, Object result) { + log.info("[JNA监控] JNA 方法调用完成: {}, 结果: {}", methodName, result); + } + + /** + * 记录 JNA 调用异常 + */ + public static void logJnaCallError(String methodName, Throwable error, Object... params) { + log.info("[JNA监控] JNA 方法调用异常: {}, 参数: {}", methodName, params, error); + logCrashInfo("JNA调用异常", Thread.currentThread(), error); + } +} + diff --git a/wms/nladmin-system/nlsso-server/src/main/java/org/nl/wms/ext/service/impl/AcsToWmsServiceImpl.java b/wms/nladmin-system/nlsso-server/src/main/java/org/nl/wms/ext/service/impl/AcsToWmsServiceImpl.java index 0857aa1..ddd837e 100644 --- a/wms/nladmin-system/nlsso-server/src/main/java/org/nl/wms/ext/service/impl/AcsToWmsServiceImpl.java +++ b/wms/nladmin-system/nlsso-server/src/main/java/org/nl/wms/ext/service/impl/AcsToWmsServiceImpl.java @@ -194,12 +194,22 @@ public class AcsToWmsServiceImpl implements AcsToWmsService { Param ParamDao = sysParamService.findByCode("is_pat"); if (ParamDao.getValue().equals(IOSConstant.IS_DELETE_YES)) { CompletableFuture.runAsync(() -> { + String vehicleCode = vehicleDao.getStoragevehicle_code(); + String taskCode = taskDao.getTask_code(); + log.info("[拍照] 开始异步调用海康拍照,vehicleCode={}, taskCode={}", vehicleCode, taskCode); try { - HikvisionSnapshotUtil.sync截图(vehicleDao.getStoragevehicle_code(), taskDao.getTask_code()); - } catch (Exception e) { - log.info("调用海康拍照失败:" + e); + HikvisionSnapshotUtil.syncSnap(vehicleCode, taskCode); + log.info("[拍照] 海康拍照调用完成,vehicleCode={}, taskCode={}", vehicleCode, taskCode); + } catch (Throwable e) { + log.info("[拍照] 调用海康拍照失败,vehicleCode={}, taskCode={}", vehicleCode, taskCode, e); + if (e instanceof Error) { + log.info("[拍照] 发生严重错误(Error),可能导致线程或 JVM 不稳定", e); + } } - }, pool); + }, pool).exceptionally(throwable -> { + log.info("[拍照] CompletableFuture 执行异常", throwable); + return null; + }); } log.info("ACS向WMS反馈重量,返回参数:--------------------------------------" + BaseResponse.responseOk(resultWeigh).toString()); diff --git a/wms/nladmin-ui/package.json b/wms/nladmin-ui/package.json index 1912240..3a886b3 100644 --- a/wms/nladmin-ui/package.json +++ b/wms/nladmin-ui/package.json @@ -52,7 +52,7 @@ "jquery": "^3.6.0", "js-beautify": "^1.10.2", "js-cookie": "2.2.0", - "jsbarcode": "^3.11.5", + "jsbarcode": "^3.12.1", "jsencrypt": "^3.0.0-rc.1", "json-bigint": "^1.0.0", "jszip": "3.1.5", diff --git a/wms/nladmin-ui/src/views/wms/basedata/storagevehicleinfo/index.vue b/wms/nladmin-ui/src/views/wms/basedata/storagevehicleinfo/index.vue index cfb3392..ea5999e 100644 --- a/wms/nladmin-ui/src/views/wms/basedata/storagevehicleinfo/index.vue +++ b/wms/nladmin-ui/src/views/wms/basedata/storagevehicleinfo/index.vue @@ -53,6 +53,16 @@ > 打印 + + 打印预览 + - + - + - + @@ -106,13 +116,13 @@ style="width: 100%;" @selection-change="crud.selectionChangeHandler" > - - - - - - - + + + + + + +