| | |
| | | |
| | | for (DeviceConfig device : devices) { |
| | | String deviceType = device.getDeviceType(); |
| | | log.info("处理设备: deviceId={}, deviceType={}, deviceName={}, WORKSTATION_SCANNER常量={}, equals={}", |
| | | log.debug("处理设备: deviceId={}, deviceType={}, deviceName={}, WORKSTATION_SCANNER常量={}, equals={}", |
| | | device.getId(), deviceType, device.getDeviceName(), |
| | | DeviceConfig.DeviceType.WORKSTATION_SCANNER, |
| | | DeviceConfig.DeviceType.WORKSTATION_SCANNER.equals(deviceType)); |
| | |
| | | || (deviceType != null && (deviceType.contains("扫码") || deviceType.contains("SCANNER"))); |
| | | boolean isLargeGlass = DeviceConfig.DeviceType.LARGE_GLASS.equals(deviceType); |
| | | boolean isTransfer = DeviceConfig.DeviceType.WORKSTATION_TRANSFER.equals(deviceType); |
| | | log.info("设备类型判断: deviceId={}, isLoadVehicle={}, isScanner={}, isLargeGlass={}, isTransfer={}", |
| | | log.debug("设备类型判断: deviceId={}, isLoadVehicle={}, isScanner={}, isLargeGlass={}, isTransfer={}", |
| | | device.getId(), isLoadVehicle, isScanner, isLargeGlass, isTransfer); |
| | | |
| | | // 1. 卧转立扫码设备:启动定时器扫描(每10秒处理一个玻璃ID) |
| | | if (isScanner) { |
| | | log.info("检测到扫码设备,准备启动定时器: deviceId={}, deviceType={}, deviceName={}", |
| | | log.debug("检测到扫码设备,准备启动定时器: deviceId={}, deviceType={}, deviceName={}", |
| | | device.getId(), device.getDeviceType(), device.getDeviceName()); |
| | | TaskStepDetail step = createStepRecord(task, device, currentOrder); |
| | | // 设置步骤为运行状态,并设置开始时间 |
| | | step.setStatus(TaskStepDetail.Status.RUNNING.name()); |
| | | step.setStartTime(new Date()); |
| | | taskStepDetailMapper.updateById(step); |
| | | notificationService.notifyStepUpdate(task.getTaskId(), step); |
| | | |
| | | ScheduledFuture<?> scannerTask = startScannerTimer(task, step, device, context); |
| | | if (scannerTask != null) { |
| | | registerScheduledTask(task.getTaskId(), scannerTask); |
| | | stepSummaries.add(createStepSummary(device.getDeviceName(), true, "定时器已启动,每10秒扫描一次")); |
| | | log.info("扫码设备定时器启动成功: deviceId={}, taskId={}", device.getId(), task.getTaskId()); |
| | | log.debug("扫码设备定时器启动成功: deviceId={}, taskId={}", device.getId(), task.getTaskId()); |
| | | } else { |
| | | log.warn("扫码设备定时器启动失败,glassIds可能为空: deviceId={}, taskId={}, contextParams={}", |
| | | device.getId(), task.getTaskId(), context.getParameters()); |
| | |
| | | |
| | | // 2. 卧转立设备:启动定时器定期检查并处理(中转设备) |
| | | if (isTransfer) { |
| | | log.info("检测到卧转立设备,准备启动定时器: deviceId={}, deviceType={}, deviceName={}", |
| | | log.debug("检测到卧转立设备,准备启动定时器: deviceId={}, deviceType={}, deviceName={}", |
| | | device.getId(), device.getDeviceType(), device.getDeviceName()); |
| | | TaskStepDetail step = createStepRecord(task, device, currentOrder); |
| | | // 设置步骤为运行状态,并设置开始时间 |
| | | step.setStatus(TaskStepDetail.Status.RUNNING.name()); |
| | | step.setStartTime(new Date()); |
| | | taskStepDetailMapper.updateById(step); |
| | | notificationService.notifyStepUpdate(task.getTaskId(), step); |
| | | |
| | | ScheduledFuture<?> transferTask = startTransferTimer(task, step, device, context); |
| | | if (transferTask != null) { |
| | | registerScheduledTask(task.getTaskId(), transferTask); |
| | | stepSummaries.add(createStepSummary(device.getDeviceName(), true, "定时器已启动,定期检查并处理玻璃批次")); |
| | | log.info("卧转立设备定时器启动成功: deviceId={}, taskId={}", device.getId(), task.getTaskId()); |
| | | log.debug("卧转立设备定时器启动成功: deviceId={}, taskId={}", device.getId(), task.getTaskId()); |
| | | } else { |
| | | log.warn("卧转立设备定时器启动失败: deviceId={}, taskId={}", device.getId(), task.getTaskId()); |
| | | stepSummaries.add(createStepSummary(device.getDeviceName(), false, "启动定时器失败")); |
| | |
| | | boolean isInboundVehicle = currentLoadVehicleIndex == 1; // 第一个大车是进片大车 |
| | | |
| | | TaskStepDetail step = createStepRecord(task, device, currentOrder); |
| | | // 设置步骤为运行状态,并设置开始时间 |
| | | step.setStatus(TaskStepDetail.Status.RUNNING.name()); |
| | | step.setStartTime(new Date()); |
| | | taskStepDetailMapper.updateById(step); |
| | | notificationService.notifyStepUpdate(task.getTaskId(), step); |
| | | |
| | | ScheduledFuture<?> vehicleTask; |
| | | if (isInboundVehicle) { |
| | |
| | | // 4. 大理片笼设备:启动定时器逻辑处理(不涉及PLC交互,只负责逻辑处理) |
| | | if (isLargeGlass) { |
| | | TaskStepDetail step = createStepRecord(task, device, currentOrder); |
| | | // 设置步骤为运行状态,并设置开始时间 |
| | | step.setStatus(TaskStepDetail.Status.RUNNING.name()); |
| | | step.setStartTime(new Date()); |
| | | taskStepDetailMapper.updateById(step); |
| | | notificationService.notifyStepUpdate(task.getTaskId(), step); |
| | | |
| | | ScheduledFuture<?> largeGlassTask = startLargeGlassTimer(task, step, device, context); |
| | | if (largeGlassTask != null) { |
| | |
| | | // 定时器会在后台持续运行,直到手动停止或超时 |
| | | boolean hasScheduledTasks = !CollectionUtils.isEmpty(taskScheduledTasks.get(task.getTaskId())); |
| | | if (hasScheduledTasks) { |
| | | log.info("任务已启动所有定时器,保持运行状态: taskId={}, scheduledTasksCount={}", |
| | | log.debug("任务已启动所有定时器,保持运行状态: taskId={}, scheduledTasksCount={}", |
| | | task.getTaskId(), taskScheduledTasks.get(task.getTaskId()).size()); |
| | | // 任务保持 RUNNING 状态,定时器在后台运行 |
| | | // 不更新任务状态为 COMPLETED,让任务持续运行 |
| | |
| | | try { |
| | | TaskParameters params = context.getParameters(); |
| | | List<String> glassIds = params.getGlassIds(); |
| | | log.info("卧转立扫码定时器初始化: taskId={}, deviceId={}, glassIds={}, glassIdsSize={}, isEmpty={}", |
| | | log.debug("卧转立扫码定时器初始化: taskId={}, deviceId={}, glassIds={}, glassIdsSize={}, isEmpty={}", |
| | | task.getTaskId(), device.getId(), glassIds, |
| | | glassIds != null ? glassIds.size() : 0, |
| | | CollectionUtils.isEmpty(glassIds)); |
| | |
| | | AtomicInteger successCount = new AtomicInteger(0); |
| | | AtomicInteger failCount = new AtomicInteger(0); |
| | | |
| | | final long CYCLE_INTERVAL_MS = 10_000; // 10秒间隔 |
| | | // 从设备配置中获取扫码间隔,默认10秒 |
| | | Map<String, Object> logicParams = parseLogicParams(device); |
| | | Integer scanIntervalMs = getLogicParam(logicParams, "scanIntervalMs", 10_000); |
| | | |
| | | log.info("启动卧转立扫码定时器: taskId={}, deviceId={}, glassCount={}, interval={}s, glassIds={}", |
| | | task.getTaskId(), device.getId(), glassIds.size(), CYCLE_INTERVAL_MS / 1000, glassIds); |
| | | log.debug("启动卧转立扫码定时器: taskId={}, deviceId={}, glassCount={}, interval={}ms, glassIds={}", |
| | | task.getTaskId(), device.getId(), glassIds.size(), scanIntervalMs, glassIds); |
| | | |
| | | // 启动定时任务 |
| | | ScheduledFuture<?> future = scheduledExecutor.scheduleWithFixedDelay(() -> { |
| | | try { |
| | | if (isTaskCancelled(context)) { |
| | | log.info("任务已取消,停止卧转立扫码定时器: taskId={}, deviceId={}", |
| | | log.debug("任务已取消,停止卧转立扫码定时器: taskId={}, deviceId={}", |
| | | task.getTaskId(), device.getId()); |
| | | return; |
| | | } |
| | | ensureStepRunning(step, task.getTaskId()); |
| | | // 检查是否需要暂停 |
| | | if (shouldPauseScanner(context)) { |
| | | log.debug("卧转立扫码定时器暂停: taskId={}, deviceId={}", task.getTaskId(), device.getId()); |
| | |
| | | // 检查是否还有待处理的玻璃ID |
| | | String glassId = glassIdQueue.poll(); |
| | | if (glassId == null) { |
| | | log.info("卧转立扫码定时器完成: taskId={}, deviceId={}, processed={}/{}, success={}, fail={}", |
| | | log.debug("卧转立扫码定时器完成: taskId={}, deviceId={}, processed={}/{}, success={}, fail={}", |
| | | task.getTaskId(), device.getId(), processedCount.get(), glassIds.size(), |
| | | successCount.get(), failCount.get()); |
| | | |
| | | // 清空plcRequest和plcGlassId(确保PLC状态清理) |
| | | try { |
| | | DeviceLogicHandler handler = handlerFactory.getHandler(device.getDeviceType()); |
| | | if (handler != null) { |
| | | Map<String, Object> clearParams = new HashMap<>(); |
| | | clearParams.put("_taskContext", context); |
| | | handler.execute(device, "clearPlc", clearParams); |
| | | log.debug("卧转立扫码定时器完成,已清空PLC请求字段: taskId={}, deviceId={}", |
| | | task.getTaskId(), device.getId()); |
| | | } |
| | | } catch (Exception e) { |
| | | log.warn("卧转立扫码定时器完成时清空PLC失败: taskId={}, deviceId={}, error={}", |
| | | task.getTaskId(), device.getId(), e.getMessage()); |
| | | } |
| | | |
| | | // 若之前未出现失败,再将状态置为完成 |
| | | boolean alreadyFailed = TaskStepDetail.Status.FAILED.name().equals(step.getStatus()); |
| | | if (!alreadyFailed) { |
| | |
| | | } |
| | | taskStepDetailMapper.updateById(step); |
| | | notificationService.notifyStepUpdate(task.getTaskId(), step); |
| | | // 扫码设备完成后尝试自动收尾整个任务 |
| | | checkAndCompleteTaskIfDone(step.getTaskId()); |
| | | } |
| | | deviceCoordinationService.syncDeviceStatus(device, |
| | | DeviceCoordinationService.DeviceStatus.COMPLETED, context); |
| | |
| | | } |
| | | |
| | | int currentIndex = processedCount.incrementAndGet(); |
| | | log.info("卧转立扫码定时器处理第{}/{}个玻璃: taskId={}, deviceId={}, glassId={}", |
| | | log.debug("卧转立扫码定时器处理第{}/{}个玻璃: taskId={}, deviceId={}, glassId={}", |
| | | currentIndex, glassIds.size(), task.getTaskId(), device.getId(), glassId); |
| | | |
| | | // 执行单次扫描 |
| | | Map<String, Object> scanParams = new HashMap<>(); |
| | | scanParams.put("glassId", glassId); |
| | | scanParams.put("_taskContext", context); |
| | | log.info("卧转立扫码定时器准备执行: taskId={}, deviceId={}, glassId={}, scanParams={}", |
| | | log.debug("卧转立扫码定时器准备执行: taskId={}, deviceId={}, glassId={}, scanParams={}", |
| | | task.getTaskId(), device.getId(), glassId, scanParams); |
| | | |
| | | DeviceLogicHandler handler = handlerFactory.getHandler(device.getDeviceType()); |
| | | if (handler != null) { |
| | | // 将logicParams合并到scanParams中 |
| | | Map<String, Object> logicParams = parseLogicParams(device); |
| | | // 将logicParams合并到scanParams中(使用已定义的logicParams变量) |
| | | if (logicParams != null && !logicParams.isEmpty()) { |
| | | scanParams.put("_logicParams", logicParams); |
| | | } |
| | | log.info("卧转立扫码定时器调用handler.execute: taskId={}, deviceId={}, glassId={}, operation=scanOnce, scanParamsKeys={}, scanParams={}", |
| | | log.debug("卧转立扫码定时器调用handler.execute: taskId={}, deviceId={}, glassId={}, operation=scanOnce, scanParamsKeys={}, scanParams={}", |
| | | task.getTaskId(), device.getId(), glassId, scanParams.keySet(), scanParams); |
| | | DevicePlcVO.OperationResult result = handler.execute(device, "scanOnce", scanParams); |
| | | log.info("卧转立扫码定时器handler.execute返回: taskId={}, deviceId={}, glassId={}, success={}", |
| | | log.debug("卧转立扫码定时器handler.execute返回: taskId={}, deviceId={}, glassId={}, success={}", |
| | | task.getTaskId(), device.getId(), glassId, result.getSuccess()); |
| | | |
| | | if (Boolean.TRUE.equals(result.getSuccess())) { |
| | | successCount.incrementAndGet(); |
| | | log.info("卧转立扫码定时器处理成功: taskId={}, deviceId={}, glassId={}", |
| | | log.debug("卧转立扫码定时器处理成功: taskId={}, deviceId={}, glassId={}", |
| | | task.getTaskId(), device.getId(), glassId); |
| | | } else { |
| | | failCount.incrementAndGet(); |
| | |
| | | task.getTaskId(), device.getId(), glassId, result.getMessage()); |
| | | } |
| | | |
| | | // 更新步骤状态 |
| | | updateStepStatus(step, result); |
| | | // 通知步骤更新(让前端实时看到步骤状态) |
| | | // 更新步骤状态(显示进度,保持RUNNING状态直到所有玻璃处理完成) |
| | | updateStepStatusForScanner(step, result, currentIndex, glassIds.size(), |
| | | successCount.get(), failCount.get()); |
| | | // 通知步骤更新(让前端实时看到步骤状态和进度) |
| | | notificationService.notifyStepUpdate(task.getTaskId(), step); |
| | | boolean opSuccess = Boolean.TRUE.equals(result.getSuccess()); |
| | | updateTaskProgress(task, step.getStepOrder(), opSuccess); |
| | |
| | | log.error("卧转立扫码定时器执行异常: taskId={}, deviceId={}", task.getTaskId(), device.getId(), e); |
| | | failCount.incrementAndGet(); |
| | | } |
| | | }, 0, CYCLE_INTERVAL_MS, TimeUnit.MILLISECONDS); |
| | | }, 0, scanIntervalMs, TimeUnit.MILLISECONDS); |
| | | |
| | | deviceCoordinationService.syncDeviceStatus(device, |
| | | DeviceCoordinationService.DeviceStatus.RUNNING, context); |
| | |
| | | Map<String, Object> logicParams = parseLogicParams(device); |
| | | Integer monitorIntervalMs = getLogicParam(logicParams, "monitorIntervalMs", 5_000); |
| | | |
| | | log.info("启动卧转立设备定时器: taskId={}, deviceId={}, interval={}ms", |
| | | log.debug("启动卧转立设备定时器: taskId={}, deviceId={}, interval={}ms", |
| | | task.getTaskId(), device.getId(), monitorIntervalMs); |
| | | |
| | | // 启动定时任务 |
| | | ScheduledFuture<?> future = scheduledExecutor.scheduleWithFixedDelay(() -> { |
| | | try { |
| | | if (isTaskCancelled(context)) { |
| | | log.info("任务已取消,停止卧转立设备定时器: taskId={}, deviceId={}", |
| | | log.debug("任务已取消,停止卧转立设备定时器: taskId={}, deviceId={}", |
| | | task.getTaskId(), device.getId()); |
| | | return; |
| | | } |
| | | ensureStepRunning(step, task.getTaskId()); |
| | | // 构建参数 |
| | | Map<String, Object> params = new HashMap<>(); |
| | | params.put("_taskContext", context); |
| | |
| | | if (opSuccess) { |
| | | String message = result.getMessage(); |
| | | if (message != null && message.contains("批次已写入PLC")) { |
| | | log.info("卧转立设备定时器执行成功(已写入PLC): taskId={}, deviceId={}, message={}", |
| | | log.debug("卧转立设备定时器执行成功(已写入PLC): taskId={}, deviceId={}, message={}", |
| | | task.getTaskId(), device.getId(), message); |
| | | } else { |
| | | log.debug("卧转立设备定时器等待中: taskId={}, deviceId={}, message={}", |
| | | log.debug("卧转立设备定时器等待中: taskId={}, deviceId={}, message={}", |
| | | task.getTaskId(), device.getId(), message); |
| | | } |
| | | } else { |
| | |
| | | final long MONITOR_INTERVAL_MS = 2_000; // 2秒监控一次 |
| | | final AtomicInteger lastProcessedCount = new AtomicInteger(0); |
| | | |
| | | log.info("启动进片大车设备定时器: taskId={}, deviceId={}, interval={}s", |
| | | log.debug("启动进片大车设备定时器: taskId={}, deviceId={}, interval={}s", |
| | | task.getTaskId(), device.getId(), MONITOR_INTERVAL_MS / 1000); |
| | | |
| | | // 启动定时任务 |
| | | ScheduledFuture<?> future = scheduledExecutor.scheduleWithFixedDelay(() -> { |
| | | try { |
| | | if (isTaskCancelled(context)) { |
| | | log.info("任务已取消,停止进片大车定时器: taskId={}, deviceId={}", |
| | | log.debug("任务已取消,停止进片大车定时器: taskId={}, deviceId={}", |
| | | task.getTaskId(), device.getId()); |
| | | return; |
| | | } |
| | | ensureStepRunning(step, task.getTaskId()); |
| | | // 检查是否有卧转立主体已输出、准备上大车的玻璃信息 |
| | | List<String> readyGlassIds = getTransferReadyGlassIds(context); |
| | | if (CollectionUtils.isEmpty(readyGlassIds)) { |
| | |
| | | return; |
| | | } |
| | | |
| | | log.info("进片大车设备定时器检测到卧转立输出的玻璃信息: taskId={}, deviceId={}, glassCount={}", |
| | | log.debug("进片大车设备定时器检测到卧转立输出的玻璃信息: taskId={}, deviceId={}, glassCount={}", |
| | | task.getTaskId(), device.getId(), currentCount); |
| | | |
| | | // 检查容量 |
| | |
| | | if (logicParams != null && !logicParams.isEmpty()) { |
| | | checkParams.put("_logicParams", logicParams); |
| | | } |
| | | DevicePlcVO.OperationResult result = handler.execute(device, "feedGlass", checkParams); |
| | | // 第一步:写入大车上料请求 |
| | | DevicePlcVO.OperationResult feedResult = handler.execute(device, "feedGlass", checkParams); |
| | | |
| | | if (Boolean.TRUE.equals(result.getSuccess())) { |
| | | log.info("进片大车设备定时器执行成功: taskId={}, deviceId={}, glassCount={}", |
| | | if (Boolean.TRUE.equals(feedResult.getSuccess())) { |
| | | log.debug("进片大车设备定时器执行成功: taskId={}, deviceId={}, glassCount={}", |
| | | task.getTaskId(), device.getId(), readyGlassIds.size()); |
| | | // 将已装载的玻璃ID保存到共享数据中(供大理片笼使用) |
| | | setLoadedGlassIds(context, new ArrayList<>(readyGlassIds)); |
| | |
| | | } else { |
| | | // 装不下,记录容量不足(是否需要影响扫码由工艺再决定) |
| | | log.warn("进片大车设备定时器容量不足: taskId={}, deviceId={}, message={}", |
| | | task.getTaskId(), device.getId(), result.getMessage()); |
| | | task.getTaskId(), device.getId(), feedResult.getMessage()); |
| | | lastProcessedCount.set(currentCount); // 记录当前数量,避免重复检查 |
| | | } |
| | | |
| | | // 更新步骤状态 |
| | | updateStepStatus(step, result); |
| | | boolean opSuccess = Boolean.TRUE.equals(result.getSuccess()); |
| | | updateTaskProgress(task, step.getStepOrder(), opSuccess); |
| | | if (!opSuccess) { |
| | | deviceCoordinationService.syncDeviceStatus(device, |
| | | DeviceCoordinationService.DeviceStatus.FAILED, context); |
| | | // 第二步:检查MES确认状态(如果大车处理器支持的话) |
| | | DevicePlcVO.OperationResult mesResult = null; |
| | | try { |
| | | mesResult = handler.execute(device, "checkMesConfirm", Collections.emptyMap()); |
| | | } catch (Exception e) { |
| | | log.warn("进片大车设备检查MES确认状态异常: taskId={}, deviceId={}, error={}", |
| | | task.getTaskId(), device.getId(), e.getMessage()); |
| | | } |
| | | |
| | | // 更新步骤状态(大车设备保持RUNNING,直到MES确认完成或任务取消) |
| | | if (mesResult != null) { |
| | | updateStepStatusForVehicle(step, mesResult); |
| | | boolean opSuccess = Boolean.TRUE.equals(mesResult.getSuccess()); |
| | | updateTaskProgress(task, step.getStepOrder(), opSuccess); |
| | | if (!opSuccess) { |
| | | deviceCoordinationService.syncDeviceStatus(device, |
| | | DeviceCoordinationService.DeviceStatus.FAILED, context); |
| | | } |
| | | } else { |
| | | updateStepStatusForVehicle(step, feedResult); |
| | | boolean opSuccess = Boolean.TRUE.equals(feedResult.getSuccess()); |
| | | updateTaskProgress(task, step.getStepOrder(), opSuccess); |
| | | if (!opSuccess) { |
| | | deviceCoordinationService.syncDeviceStatus(device, |
| | | DeviceCoordinationService.DeviceStatus.FAILED, context); |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | |
| | | try { |
| | | final long MONITOR_INTERVAL_MS = 2_000; // 2秒监控一次 |
| | | |
| | | log.info("启动出片大车设备定时器: taskId={}, deviceId={}, interval={}s", |
| | | log.debug("启动出片大车设备定时器: taskId={}, deviceId={}, interval={}s", |
| | | task.getTaskId(), device.getId(), MONITOR_INTERVAL_MS / 1000); |
| | | |
| | | // 启动定时任务 |
| | | ScheduledFuture<?> future = scheduledExecutor.scheduleWithFixedDelay(() -> { |
| | | try { |
| | | if (isTaskCancelled(context)) { |
| | | log.info("任务已取消,停止出片大车定时器: taskId={}, deviceId={}", |
| | | log.debug("任务已取消,停止出片大车定时器: taskId={}, deviceId={}", |
| | | task.getTaskId(), device.getId()); |
| | | return; |
| | | } |
| | | ensureStepRunning(step, task.getTaskId()); |
| | | // 检查是否有已处理的玻璃信息(从大理片笼来的) |
| | | List<String> processedGlassIds = getProcessedGlassIds(context); |
| | | if (CollectionUtils.isEmpty(processedGlassIds)) { |
| | |
| | | return; |
| | | } |
| | | |
| | | log.info("出片大车设备定时器检测到已处理的玻璃信息: taskId={}, deviceId={}, glassCount={}", |
| | | log.debug("出片大车设备定时器检测到已处理的玻璃信息: taskId={}, deviceId={}, glassCount={}", |
| | | task.getTaskId(), device.getId(), processedGlassIds.size()); |
| | | |
| | | // 执行出片操作 |
| | |
| | | if (logicParams != null && !logicParams.isEmpty()) { |
| | | checkParams.put("_logicParams", logicParams); |
| | | } |
| | | DevicePlcVO.OperationResult result = handler.execute(device, "feedGlass", checkParams); |
| | | // 第一步:写入大车出片请求 |
| | | DevicePlcVO.OperationResult feedResult = handler.execute(device, "feedGlass", checkParams); |
| | | |
| | | if (Boolean.TRUE.equals(result.getSuccess())) { |
| | | log.info("出片大车设备定时器执行成功: taskId={}, deviceId={}, glassCount={}", |
| | | if (Boolean.TRUE.equals(feedResult.getSuccess())) { |
| | | log.debug("出片大车设备定时器执行成功: taskId={}, deviceId={}, glassCount={}", |
| | | task.getTaskId(), device.getId(), processedGlassIds.size()); |
| | | // 清空已处理的玻璃ID列表(已处理) |
| | | clearProcessedGlassIds(context); |
| | | } else { |
| | | log.debug("出片大车设备定时器执行失败: taskId={}, deviceId={}, message={}", |
| | | task.getTaskId(), device.getId(), result.getMessage()); |
| | | task.getTaskId(), device.getId(), feedResult.getMessage()); |
| | | } |
| | | |
| | | // 更新步骤状态 |
| | | updateStepStatus(step, result); |
| | | boolean opSuccess = Boolean.TRUE.equals(result.getSuccess()); |
| | | updateTaskProgress(task, step.getStepOrder(), opSuccess); |
| | | if (!opSuccess) { |
| | | deviceCoordinationService.syncDeviceStatus(device, |
| | | DeviceCoordinationService.DeviceStatus.FAILED, context); |
| | | // 第二步:检查MES确认状态(如果大车处理器支持的话) |
| | | DevicePlcVO.OperationResult mesResult = null; |
| | | try { |
| | | mesResult = handler.execute(device, "checkMesConfirm", Collections.emptyMap()); |
| | | } catch (Exception e) { |
| | | log.warn("出片大车设备检查MES确认状态异常: taskId={}, deviceId={}, error={}", |
| | | task.getTaskId(), device.getId(), e.getMessage()); |
| | | } |
| | | |
| | | // 更新步骤状态(大车设备保持RUNNING,直到MES确认完成或任务取消) |
| | | if (mesResult != null) { |
| | | updateStepStatusForVehicle(step, mesResult); |
| | | boolean opSuccess = Boolean.TRUE.equals(mesResult.getSuccess()); |
| | | updateTaskProgress(task, step.getStepOrder(), opSuccess); |
| | | if (!opSuccess) { |
| | | deviceCoordinationService.syncDeviceStatus(device, |
| | | DeviceCoordinationService.DeviceStatus.FAILED, context); |
| | | } |
| | | } else { |
| | | updateStepStatusForVehicle(step, feedResult); |
| | | boolean opSuccess = Boolean.TRUE.equals(feedResult.getSuccess()); |
| | | updateTaskProgress(task, step.getStepOrder(), opSuccess); |
| | | if (!opSuccess) { |
| | | deviceCoordinationService.syncDeviceStatus(device, |
| | | DeviceCoordinationService.DeviceStatus.FAILED, context); |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | |
| | | Integer processTimeSeconds = getLogicParam(logicParams, "processTimeSeconds", 30); |
| | | final long PROCESS_TIME_MS = processTimeSeconds * 1000; |
| | | |
| | | log.info("启动大理片笼设备定时器: taskId={}, deviceId={}, processTime={}s", |
| | | log.debug("启动大理片笼设备定时器: taskId={}, deviceId={}, processTime={}s", |
| | | task.getTaskId(), device.getId(), processTimeSeconds); |
| | | |
| | | // 启动定时任务 |
| | | ScheduledFuture<?> future = scheduledExecutor.scheduleWithFixedDelay(() -> { |
| | | try { |
| | | if (isTaskCancelled(context)) { |
| | | log.info("任务已取消,停止大理片笼定时器: taskId={}, deviceId={}", |
| | | log.debug("任务已取消,停止大理片笼定时器: taskId={}, deviceId={}", |
| | | task.getTaskId(), device.getId()); |
| | | return; |
| | | } |
| | | ensureStepRunning(step, task.getTaskId()); |
| | | // 检查是否有已装载的玻璃信息(从进片大车来的) |
| | | List<String> loadedGlassIds = getLoadedGlassIds(context); |
| | | if (CollectionUtils.isEmpty(loadedGlassIds)) { |
| | |
| | | if (processStartTime == null) { |
| | | // 第一次检测到玻璃,记录开始处理时间 |
| | | setProcessStartTime(context, System.currentTimeMillis()); |
| | | log.info("大理片笼设备开始处理: taskId={}, deviceId={}, glassCount={}, processTime={}s", |
| | | log.debug("大理片笼设备开始处理: taskId={}, deviceId={}, glassCount={}, processTime={}s", |
| | | task.getTaskId(), device.getId(), loadedGlassIds.size(), processTimeSeconds); |
| | | return; |
| | | } |
| | |
| | | } |
| | | |
| | | // 处理时间已到,完成任务汇报 |
| | | log.info("大理片笼设备处理完成: taskId={}, deviceId={}, glassCount={}, processTime={}s", |
| | | log.debug("大理片笼设备处理完成: taskId={}, deviceId={}, glassCount={}, processTime={}s", |
| | | task.getTaskId(), device.getId(), loadedGlassIds.size(), processTimeSeconds); |
| | | |
| | | // 将已处理的玻璃ID转移到已处理列表(供出片大车使用) |
| | |
| | | step.setErrorMessage(null); |
| | | step.setOutputData(toJson(Collections.singletonMap("glassIds", loadedGlassIds))); |
| | | taskStepDetailMapper.updateById(step); |
| | | // 大理片笼完成后尝试自动收尾整个任务 |
| | | checkAndCompleteTaskIfDone(step.getTaskId()); |
| | | |
| | | } catch (Exception e) { |
| | | log.error("大理片笼设备定时器执行异常: taskId={}, deviceId={}", task.getTaskId(), device.getId(), e); |
| | |
| | | future.cancel(false); |
| | | } |
| | | } |
| | | log.info("已停止任务的所有定时器: taskId={}, count={}", taskId, futures.size()); |
| | | log.debug("已停止任务的所有定时器: taskId={}, count={}", taskId, futures.size()); |
| | | } |
| | | runningTaskContexts.remove(taskId); |
| | | } |
| | |
| | | long timeoutMs = timeoutMinutes * 60 * 1000; |
| | | long deadline = System.currentTimeMillis() + timeoutMs; |
| | | |
| | | log.info("等待定时器任务完成: taskId={}, timeout={}分钟", taskId, timeoutMinutes); |
| | | log.debug("等待定时器任务完成: taskId={}, timeout={}分钟", taskId, timeoutMinutes); |
| | | |
| | | while (System.currentTimeMillis() < deadline) { |
| | | List<ScheduledFuture<?>> futures = taskScheduledTasks.get(taskId); |
| | |
| | | } |
| | | } |
| | | |
| | | log.info("定时器任务等待完成: taskId={}", taskId); |
| | | log.debug("定时器任务等待完成: taskId={}", taskId); |
| | | } |
| | | |
| | | /** |
| | | * 当某个步骤可能完成时,检查任务是否所有步骤都已完成,如果是则自动将任务标记为已完成 |
| | | */ |
| | | private void checkAndCompleteTaskIfDone(String taskId) { |
| | | if (taskId == null) { |
| | | return; |
| | | } |
| | | try { |
| | | MultiDeviceTask task = multiDeviceTaskMapper.selectOne( |
| | | Wrappers.<MultiDeviceTask>lambdaQuery() |
| | | .eq(MultiDeviceTask::getTaskId, taskId) |
| | | ); |
| | | if (task == null) { |
| | | return; |
| | | } |
| | | // 仅在任务仍为RUNNING时才尝试自动收尾 |
| | | if (!MultiDeviceTask.Status.RUNNING.name().equals(task.getStatus())) { |
| | | return; |
| | | } |
| | | |
| | | int totalSteps = task.getTotalSteps() != null ? task.getTotalSteps() : 0; |
| | | if (totalSteps <= 0) { |
| | | return; |
| | | } |
| | | |
| | | int completedSteps = countCompletedSteps(taskId); |
| | | if (completedSteps < totalSteps) { |
| | | return; |
| | | } |
| | | |
| | | // 所有步骤都已完成,收尾任务 |
| | | task.setStatus(MultiDeviceTask.Status.COMPLETED.name()); |
| | | task.setEndTime(new Date()); |
| | | multiDeviceTaskMapper.updateById(task); |
| | | |
| | | // 停止所有定时器 |
| | | stopScheduledTasks(taskId); |
| | | |
| | | // 通知任务完成 |
| | | notificationService.notifyTaskStatus(task); |
| | | |
| | | log.info("所有步骤已完成,自动将任务标记为已完成: taskId={}, totalSteps={}", taskId, totalSteps); |
| | | } catch (Exception e) { |
| | | log.warn("检查并自动完成任务失败: taskId={}", taskId, e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | /** |
| | | * 确保步骤进入RUNNING状态(仅在第一次真正执行前调用) |
| | | */ |
| | | private void ensureStepRunning(TaskStepDetail step, String taskId) { |
| | | if (step == null) { |
| | | return; |
| | | } |
| | | if (!TaskStepDetail.Status.RUNNING.name().equals(step.getStatus())) { |
| | | step.setStatus(TaskStepDetail.Status.RUNNING.name()); |
| | | if (step.getStartTime() == null) { |
| | | step.setStartTime(new Date()); |
| | | } |
| | | taskStepDetailMapper.updateById(step); |
| | | notificationService.notifyStepUpdate(taskId, step); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 更新扫码设备步骤状态(显示进度,保持RUNNING状态直到所有玻璃处理完成) |
| | | */ |
| | | private void updateStepStatusForScanner(TaskStepDetail step, DevicePlcVO.OperationResult result, |
| | | int currentIndex, int totalCount, |
| | | int successCount, int failCount) { |
| | | if (step == null || result == null) { |
| | | return; |
| | | } |
| | | |
| | | boolean success = Boolean.TRUE.equals(result.getSuccess()); |
| | | |
| | | // 保持RUNNING状态,直到所有玻璃处理完成(在定时器完成时再设置为COMPLETED) |
| | | step.setStatus(TaskStepDetail.Status.RUNNING.name()); |
| | | |
| | | // 更新时间和耗时,前端可以实时看到执行耗时 |
| | | Date now = new Date(); |
| | | if (step.getStartTime() == null) { |
| | | step.setStartTime(now); |
| | | } |
| | | if (step.getStartTime() != null) { |
| | | step.setDurationMs(now.getTime() - step.getStartTime().getTime()); |
| | | } |
| | | |
| | | // 更新进度信息 |
| | | String progressMessage = String.format("正在处理 %d/%d (成功:%d, 失败:%d)", |
| | | currentIndex, totalCount, successCount, failCount); |
| | | |
| | | if (success) { |
| | | // 成功时显示进度和成功消息 |
| | | String resultMessage = result.getMessage(); |
| | | if (StringUtils.hasText(resultMessage)) { |
| | | step.setSuccessMessage(progressMessage + " - " + resultMessage); |
| | | } else { |
| | | step.setSuccessMessage(progressMessage); |
| | | } |
| | | step.setErrorMessage(null); |
| | | } else { |
| | | // 失败时显示进度和错误消息 |
| | | String errorMessage = result.getMessage(); |
| | | step.setErrorMessage(progressMessage + " - " + (StringUtils.hasText(errorMessage) ? errorMessage : "处理失败")); |
| | | step.setSuccessMessage(null); |
| | | } |
| | | |
| | | step.setOutputData(toJson(result)); |
| | | taskStepDetailMapper.updateById(step); |
| | | } |
| | | |
| | | /** |
| | | * 更新大车设备步骤状态(保持RUNNING,直到手动停止或任务取消;失败时标记为FAILED) |
| | | */ |
| | | private void updateStepStatusForVehicle(TaskStepDetail step, DevicePlcVO.OperationResult result) { |
| | | if (step == null || result == null) { |
| | | return; |
| | | } |
| | | boolean success = Boolean.TRUE.equals(result.getSuccess()); |
| | | boolean completed = false; |
| | | if (result.getData() != null && result.getData().get("completed") != null) { |
| | | Object flag = result.getData().get("completed"); |
| | | if (flag instanceof Boolean) { |
| | | completed = (Boolean) flag; |
| | | } else { |
| | | completed = "true".equalsIgnoreCase(String.valueOf(flag)); |
| | | } |
| | | } |
| | | Date now = new Date(); |
| | | |
| | | // 初始化开始时间 |
| | | if (step.getStartTime() == null) { |
| | | step.setStartTime(now); |
| | | } |
| | | |
| | | if (success && !completed) { |
| | | // 成功但未完成:保持RUNNING状态,仅更新提示信息和耗时 |
| | | step.setStatus(TaskStepDetail.Status.RUNNING.name()); |
| | | String message = result.getMessage(); |
| | | step.setSuccessMessage(StringUtils.hasText(message) ? message : "大车设备运行中"); |
| | | step.setErrorMessage(null); |
| | | if (step.getStartTime() != null) { |
| | | step.setDurationMs(now.getTime() - step.getStartTime().getTime()); |
| | | } |
| | | } else if (success && completed) { |
| | | // 成功且MES已确认完成:标记为COMPLETED并记录结束时间 |
| | | step.setStatus(TaskStepDetail.Status.COMPLETED.name()); |
| | | String message = result.getMessage(); |
| | | step.setSuccessMessage(StringUtils.hasText(message) ? message : "大车设备任务已完成"); |
| | | step.setErrorMessage(null); |
| | | if (step.getEndTime() == null) { |
| | | step.setEndTime(now); |
| | | } |
| | | if (step.getStartTime() != null && step.getEndTime() != null) { |
| | | step.setDurationMs(step.getEndTime().getTime() - step.getStartTime().getTime()); |
| | | } |
| | | // 尝试自动收尾整个任务 |
| | | checkAndCompleteTaskIfDone(step.getTaskId()); |
| | | } else { |
| | | // 失败:标记为FAILED并记录结束时间 |
| | | step.setStatus(TaskStepDetail.Status.FAILED.name()); |
| | | String message = result.getMessage(); |
| | | step.setErrorMessage(message); |
| | | if (step.getEndTime() == null) { |
| | | step.setEndTime(now); |
| | | } |
| | | if (step.getStartTime() != null && step.getEndTime() != null) { |
| | | step.setDurationMs(step.getEndTime().getTime() - step.getStartTime().getTime()); |
| | | } |
| | | } |
| | | |
| | | step.setOutputData(toJson(result)); |
| | | taskStepDetailMapper.updateById(step); |
| | | } |
| | | |
| | | /** |
| | | * 更新卧转立设备步骤状态(区分等待中和真正完成) |
| | | */ |
| | | private void updateStepStatusForTransfer(TaskStepDetail step, DevicePlcVO.OperationResult result) { |
| | |
| | | boolean success = Boolean.TRUE.equals(result.getSuccess()); |
| | | String message = result.getMessage(); |
| | | |
| | | // 判断是否真正完成(只有写入PLC才算完成) |
| | | boolean isRealCompleted = success && message != null && message.contains("批次已写入PLC"); |
| | | // 判断是否真正完成: |
| | | // 1. 写入PLC成功 |
| | | // 2. 且缓冲已清空(表示所有玻璃已处理完,无新玻璃) |
| | | boolean isRealCompleted = success && message != null |
| | | && message.contains("批次已写入PLC") |
| | | && message.contains("缓冲已清空,任务完成"); |
| | | |
| | | if (isRealCompleted) { |
| | | // 真正完成:设置为完成状态,并设置结束时间 |
| | |
| | | step.setSuccessMessage(message); |
| | | if (step.getEndTime() == null) { |
| | | step.setEndTime(new Date()); |
| | | } |
| | | // 计算耗时 |
| | | if (step.getStartTime() != null && step.getEndTime() != null) { |
| | | step.setDurationMs(step.getEndTime().getTime() - step.getStartTime().getTime()); |
| | | } |
| | | log.debug("卧转立设备步骤已完成: stepId={}, durationMs={}", step.getId(), step.getDurationMs()); |
| | | // 卧转立主体完成后尝试自动收尾整个任务 |
| | | checkAndCompleteTaskIfDone(step.getTaskId()); |
| | | } else if (success && message != null && message.contains("批次已写入PLC")) { |
| | | // 写入PLC成功但缓冲还有玻璃(车满情况),继续运行 |
| | | if (!TaskStepDetail.Status.RUNNING.name().equals(step.getStatus())) { |
| | | step.setStatus(TaskStepDetail.Status.RUNNING.name()); |
| | | } |
| | | step.setSuccessMessage(message); |
| | | // 确保开始时间已设置 |
| | | if (step.getStartTime() == null) { |
| | | step.setStartTime(new Date()); |
| | | } |
| | | } else if (success) { |
| | | // 等待中:保持运行状态,只更新消息 |
| | |
| | | step.setErrorMessage(message); |
| | | if (step.getEndTime() == null) { |
| | | step.setEndTime(new Date()); |
| | | } |
| | | // 计算耗时 |
| | | if (step.getStartTime() != null && step.getEndTime() != null) { |
| | | step.setDurationMs(step.getEndTime().getTime() - step.getStartTime().getTime()); |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | /** |
| | | * 分批执行大车设备玻璃上料(当玻璃ID数量超过6个且设置了单片间隔时) |
| | | * 分批执行大车设备玻璃上料(当玻璃ID数量超过6个时) |
| | | */ |
| | | private StepResult executeLoadVehicleWithBatches(MultiDeviceTask task, |
| | | DeviceConfig device, |
| | |
| | | TaskExecutionContext context, |
| | | List<Map<String, Object>> stepSummaries) { |
| | | List<String> allGlassIds = context.getParameters().getGlassIds(); |
| | | Integer glassIntervalMs = context.getParameters().getGlassIntervalMs(); |
| | | int batchSize = 6; // 每批最多6个玻璃ID |
| | | |
| | | // 分批处理 |
| | | int totalBatches = (allGlassIds.size() + batchSize - 1) / batchSize; |
| | | log.info("大车设备分批上料: deviceId={}, totalGlassIds={}, batchSize={}, totalBatches={}, glassIntervalMs={}", |
| | | device.getId(), allGlassIds.size(), batchSize, totalBatches, glassIntervalMs); |
| | | log.debug("大车设备分批上料: deviceId={}, totalGlassIds={}, batchSize={}, totalBatches={}", |
| | | device.getId(), allGlassIds.size(), batchSize, totalBatches); |
| | | |
| | | for (int batchIndex = 0; batchIndex < totalBatches; batchIndex++) { |
| | | int startIndex = batchIndex * batchSize; |
| | |
| | | // 创建临时参数,只包含当前批次的玻璃ID |
| | | TaskParameters batchParams = new TaskParameters(); |
| | | batchParams.setGlassIds(new ArrayList<>(batchGlassIds)); |
| | | batchParams.setGlassIntervalMs(glassIntervalMs); |
| | | batchParams.setPositionCode(context.getParameters().getPositionCode()); |
| | | batchParams.setPositionValue(context.getParameters().getPositionValue()); |
| | | |
| | |
| | | return stepResult; |
| | | } |
| | | |
| | | log.info("大车设备分批上料成功: deviceId={}, batchIndex={}/{}, glassIds={}", |
| | | log.debug("大车设备分批上料成功: deviceId={}, batchIndex={}/{}, glassIds={}", |
| | | device.getId(), batchIndex + 1, totalBatches, batchGlassIds); |
| | | |
| | | // 如果不是最后一批,等待间隔(模拟玻璃每片运动的时间) |
| | | // 这个等待让大车有时间处理当前批次的玻璃,然后再传递下一批 |
| | | if (batchIndex < totalBatches - 1 && glassIntervalMs != null && glassIntervalMs > 0) { |
| | | try { |
| | | log.info("等待单片间隔(模拟玻璃运动时间): glassIntervalMs={}ms, 大车可在此期间继续装玻璃", glassIntervalMs); |
| | | Thread.sleep(glassIntervalMs); |
| | | } catch (InterruptedException e) { |
| | | Thread.currentThread().interrupt(); |
| | | return StepResult.failure(device.getDeviceName(), "等待单片间隔时被中断"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 更新上下文中的已加载玻璃ID |
| | |
| | | Map<String, Object> params = buildOperationParams(device, context); |
| | | // 将context引用放入params,供设备处理器使用(用于设备协调) |
| | | params.put("_taskContext", context); |
| | | log.info("executeStepWithRetry构建参数: deviceId={}, deviceType={}, operation={}, paramsKeys={}, params={}", |
| | | log.debug("executeStepWithRetry构建参数: deviceId={}, deviceType={}, operation={}, paramsKeys={}, params={}", |
| | | device.getId(), device.getDeviceType(), determineOperation(device, params), params.keySet(), params); |
| | | step.setInputData(toJson(params)); |
| | | taskStepDetailMapper.updateById(step); |
| | |
| | | if (retryAttempt > 0) { |
| | | // 重试前等待 |
| | | long waitTime = retryPolicy.calculateRetryInterval(retryAttempt); |
| | | log.info("步骤执行重试: deviceId={}, operation={}, retryAttempt={}/{}, waitTime={}ms", |
| | | log.debug("步骤执行重试: deviceId={}, operation={}, retryAttempt={}/{}, waitTime={}ms", |
| | | device.getId(), operation, retryAttempt, retryPolicy.getMaxRetryCount(), waitTime); |
| | | Thread.sleep(waitTime); |
| | | |
| | |
| | | try { |
| | | if (retryAttempt > 0) { |
| | | long waitTime = retryPolicy.calculateRetryInterval(retryAttempt); |
| | | log.info("交互步骤执行重试: deviceId={}, retryAttempt={}/{}, waitTime={}ms", |
| | | log.debug("交互步骤执行重试: deviceId={}, retryAttempt={}/{}, waitTime={}ms", |
| | | device.getId(), retryAttempt, retryPolicy.getMaxRetryCount(), waitTime); |
| | | Thread.sleep(waitTime); |
| | | |
| | |
| | | if (taskParams.getPositionValue() != null) { |
| | | params.put("positionValue", taskParams.getPositionValue()); |
| | | } |
| | | // 传递单片间隔配置,如果任务参数中有设置,优先使用任务参数的,否则使用设备配置的 |
| | | if (taskParams.getGlassIntervalMs() != null) { |
| | | params.put("glassIntervalMs", taskParams.getGlassIntervalMs()); |
| | | } |
| | | params.put("triggerRequest", true); |
| | | break; |
| | | case DeviceConfig.DeviceType.LARGE_GLASS: |
| | |
| | | case DeviceConfig.DeviceType.WORKSTATION_SCANNER: |
| | | // 卧转立扫码设备:从任务参数中获取玻璃ID列表,取第一个作为当前要测试的玻璃ID |
| | | // 注意:扫码设备通常通过定时器执行,但如果通过executeStep执行,也需要传递glassId |
| | | log.info("buildOperationParams处理扫码设备: deviceId={}, taskParams.glassIds={}, isEmpty={}", |
| | | log.debug("buildOperationParams处理扫码设备: deviceId={}, taskParams.glassIds={}, isEmpty={}", |
| | | device.getId(), taskParams.getGlassIds(), |
| | | CollectionUtils.isEmpty(taskParams.getGlassIds())); |
| | | if (!CollectionUtils.isEmpty(taskParams.getGlassIds())) { |
| | | params.put("glassId", taskParams.getGlassIds().get(0)); |
| | | params.put("glassIds", new ArrayList<>(taskParams.getGlassIds())); |
| | | log.info("buildOperationParams为扫码设备添加glassId: deviceId={}, glassId={}, glassIdsSize={}", |
| | | log.debug("buildOperationParams为扫码设备添加glassId: deviceId={}, glassId={}, glassIdsSize={}", |
| | | device.getId(), taskParams.getGlassIds().get(0), taskParams.getGlassIds().size()); |
| | | } else { |
| | | log.warn("buildOperationParams扫码设备glassIds为空: deviceId={}, taskParams.glassIds={}, taskParams={}", |
| | |
| | | if (!CollectionUtils.isEmpty(scannerGlassIds)) { |
| | | context.getParameters().setGlassIds(new ArrayList<>(scannerGlassIds)); |
| | | context.setLoadedGlassIds(new ArrayList<>(scannerGlassIds)); |
| | | log.info("卧转立扫码获取到玻璃ID: {}", scannerGlassIds); |
| | | log.debug("卧转立扫码获取到玻璃ID: {}", scannerGlassIds); |
| | | } else { |
| | | log.warn("卧转立扫码未获取到玻璃ID,后续设备可能无法执行"); |
| | | } |