package com.mes.task.service; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.mes.device.entity.DeviceConfig; import com.mes.device.entity.DeviceGroupConfig; import com.mes.interaction.DeviceInteraction; import com.mes.interaction.DeviceInteractionRegistry; import com.mes.interaction.DeviceLogicHandler; import com.mes.interaction.DeviceLogicHandlerFactory; import com.mes.interaction.base.InteractionContext; import com.mes.interaction.base.InteractionResult; import com.mes.device.service.DeviceInteractionService; import com.mes.task.dto.TaskParameters; import com.mes.task.entity.MultiDeviceTask; import com.mes.task.entity.TaskStepDetail; import com.mes.task.mapper.MultiDeviceTaskMapper; import com.mes.task.mapper.TaskStepDetailMapper; import com.mes.task.model.TaskExecutionContext; import com.mes.task.model.TaskExecutionResult; import com.mes.device.vo.DevicePlcVO; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import java.util.*; /** * 多设备任务执行引擎 */ @Slf4j @Component @RequiredArgsConstructor public class TaskExecutionEngine { private static final Map DEFAULT_OPERATIONS = new HashMap<>(); static { DEFAULT_OPERATIONS.put(DeviceConfig.DeviceType.LOAD_VEHICLE, "feedGlass"); DEFAULT_OPERATIONS.put(DeviceConfig.DeviceType.LARGE_GLASS, "processGlass"); DEFAULT_OPERATIONS.put(DeviceConfig.DeviceType.GLASS_STORAGE, "storeGlass"); } private final TaskStepDetailMapper taskStepDetailMapper; private final MultiDeviceTaskMapper multiDeviceTaskMapper; private final DeviceInteractionService deviceInteractionService; private final DeviceInteractionRegistry interactionRegistry; private final DeviceLogicHandlerFactory handlerFactory; private final ObjectMapper objectMapper; public TaskExecutionResult execute(MultiDeviceTask task, DeviceGroupConfig groupConfig, List devices, TaskParameters parameters) { if (CollectionUtils.isEmpty(devices)) { return TaskExecutionResult.failure("设备组未配置设备,无法执行任务", Collections.emptyMap()); } TaskExecutionContext context = new TaskExecutionContext(parameters); task.setTotalSteps(devices.size()); task.setStatus(MultiDeviceTask.Status.RUNNING.name()); multiDeviceTaskMapper.updateById(task); List> stepSummaries = new ArrayList<>(); boolean success = true; String failureMessage = null; for (int i = 0; i < devices.size(); i++) { DeviceConfig device = devices.get(i); int order = i + 1; TaskStepDetail step = createStepRecord(task, device, order); StepResult stepResult = executeStep(task, step, device, context); stepSummaries.add(stepResult.toSummary()); if (!stepResult.isSuccess()) { success = false; failureMessage = stepResult.getMessage(); break; } } Map payload = new HashMap<>(); payload.put("steps", stepSummaries); payload.put("groupId", groupConfig.getId()); payload.put("deviceCount", devices.size()); if (success) { return TaskExecutionResult.success(payload); } return TaskExecutionResult.failure(failureMessage != null ? failureMessage : "任务执行失败", payload); } private TaskStepDetail createStepRecord(MultiDeviceTask task, DeviceConfig device, int order) { TaskStepDetail step = new TaskStepDetail(); step.setTaskId(task.getTaskId()); step.setStepOrder(order); step.setDeviceId(String.valueOf(device.getId())); step.setStepName(device.getDeviceName()); step.setStatus(TaskStepDetail.Status.PENDING.name()); step.setRetryCount(0); taskStepDetailMapper.insert(step); return step; } private StepResult executeStep(MultiDeviceTask task, TaskStepDetail step, DeviceConfig device, TaskExecutionContext context) { Date startTime = new Date(); step.setStartTime(startTime); step.setStatus(TaskStepDetail.Status.RUNNING.name()); DeviceInteraction deviceInteraction = interactionRegistry.getInteraction(device.getDeviceType()); if (deviceInteraction != null) { return executeInteractionStep(task, step, device, context, deviceInteraction); } Map params = buildOperationParams(device, context); step.setInputData(toJson(params)); taskStepDetailMapper.updateById(step); String operation = determineOperation(device, params); DeviceLogicHandler handler = handlerFactory.getHandler(device.getDeviceType()); DevicePlcVO.OperationResult result; try { if (handler == null) { result = deviceInteractionService.executeOperation(device.getId(), operation, params); } else { result = handler.execute(device, operation, params); } boolean opSuccess = Boolean.TRUE.equals(result.getSuccess()); updateStepAfterOperation(step, result, opSuccess); updateTaskProgress(task, step.getStepOrder(), opSuccess); if (opSuccess) { updateContextAfterSuccess(device, context, params); return StepResult.success(device.getDeviceName(), result.getMessage()); } return StepResult.failure(device.getDeviceName(), result.getMessage()); } catch (Exception e) { log.error("设备操作异常, deviceId={}, operation={}", device.getId(), operation, e); step.setStatus(TaskStepDetail.Status.FAILED.name()); step.setErrorMessage(e.getMessage()); step.setEndTime(new Date()); step.setDurationMs(step.getEndTime().getTime() - step.getStartTime().getTime()); taskStepDetailMapper.updateById(step); updateTaskProgress(task, step.getStepOrder(), false); return StepResult.failure(device.getDeviceName(), e.getMessage()); } } private StepResult executeInteractionStep(MultiDeviceTask task, TaskStepDetail step, DeviceConfig device, TaskExecutionContext context, DeviceInteraction deviceInteraction) { try { InteractionContext interactionContext = new InteractionContext(device, context); step.setInputData(toJson(context.getParameters())); InteractionResult interactionResult = deviceInteraction.execute(interactionContext); boolean success = interactionResult != null && interactionResult.isSuccess(); updateStepAfterInteraction(step, interactionResult); updateTaskProgress(task, step.getStepOrder(), success); if (success) { return StepResult.success(device.getDeviceName(), interactionResult.getMessage()); } String message = interactionResult != null ? interactionResult.getMessage() : "交互执行失败"; return StepResult.failure(device.getDeviceName(), message); } catch (Exception e) { log.error("交互执行异常, deviceId={}", device.getId(), e); step.setStatus(TaskStepDetail.Status.FAILED.name()); step.setErrorMessage(e.getMessage()); step.setEndTime(new Date()); step.setDurationMs(step.getEndTime().getTime() - step.getStartTime().getTime()); taskStepDetailMapper.updateById(step); updateTaskProgress(task, step.getStepOrder(), false); return StepResult.failure(device.getDeviceName(), e.getMessage()); } } private void updateStepAfterOperation(TaskStepDetail step, DevicePlcVO.OperationResult result, boolean success) { step.setEndTime(new Date()); if (step.getStartTime() != null) { step.setDurationMs(step.getEndTime().getTime() - step.getStartTime().getTime()); } step.setStatus(success ? TaskStepDetail.Status.COMPLETED.name() : TaskStepDetail.Status.FAILED.name()); step.setErrorMessage(success ? null : result.getMessage()); step.setOutputData(toJson(result)); taskStepDetailMapper.updateById(step); } private void updateStepAfterInteraction(TaskStepDetail step, InteractionResult result) { step.setEndTime(new Date()); if (step.getStartTime() != null) { step.setDurationMs(step.getEndTime().getTime() - step.getStartTime().getTime()); } boolean success = result != null && result.isSuccess(); step.setStatus(success ? TaskStepDetail.Status.COMPLETED.name() : TaskStepDetail.Status.FAILED.name()); step.setErrorMessage(success ? null : (result != null ? result.getMessage() : "交互执行失败")); step.setOutputData(result != null ? toJson(result.getData()) : "{}"); taskStepDetailMapper.updateById(step); } private void updateTaskProgress(MultiDeviceTask task, int currentStep, boolean success) { task.setCurrentStep(currentStep); if (!success) { task.setStatus(MultiDeviceTask.Status.FAILED.name()); } LambdaUpdateWrapper update = Wrappers.lambdaUpdate() .eq(MultiDeviceTask::getId, task.getId()) .set(MultiDeviceTask::getCurrentStep, currentStep); if (!success) { update.set(MultiDeviceTask::getStatus, MultiDeviceTask.Status.FAILED.name()); } multiDeviceTaskMapper.update(null, update); } private String determineOperation(DeviceConfig device, Map params) { if (params != null && params.containsKey("operation")) { Object op = params.get("operation"); if (op != null) { return String.valueOf(op); } } return DEFAULT_OPERATIONS.getOrDefault(device.getDeviceType(), "feedGlass"); } private Map buildOperationParams(DeviceConfig device, TaskExecutionContext context) { Map params = new HashMap<>(); TaskParameters taskParams = context.getParameters(); switch (device.getDeviceType()) { case DeviceConfig.DeviceType.LOAD_VEHICLE: params.put("glassIds", new ArrayList<>(taskParams.getGlassIds())); if (StringUtils.hasText(taskParams.getPositionCode())) { params.put("positionCode", taskParams.getPositionCode()); } if (taskParams.getPositionValue() != null) { params.put("positionValue", taskParams.getPositionValue()); } params.put("triggerRequest", true); break; case DeviceConfig.DeviceType.LARGE_GLASS: List source = context.getSafeLoadedGlassIds(); if (CollectionUtils.isEmpty(source)) { source = taskParams.getGlassIds(); } if (!CollectionUtils.isEmpty(source)) { params.put("glassId", source.get(0)); params.put("glassIds", new ArrayList<>(source)); } params.put("processType", taskParams.getProcessType() != null ? taskParams.getProcessType() : 1); params.put("triggerRequest", true); break; case DeviceConfig.DeviceType.GLASS_STORAGE: List processed = context.getSafeProcessedGlassIds(); if (CollectionUtils.isEmpty(processed)) { processed = context.getSafeLoadedGlassIds(); } if (!CollectionUtils.isEmpty(processed)) { params.put("glassId", processed.get(0)); params.put("glassIds", new ArrayList<>(processed)); } if (taskParams.getStoragePosition() != null) { params.put("storagePosition", taskParams.getStoragePosition()); } params.put("triggerRequest", true); break; default: if (!CollectionUtils.isEmpty(taskParams.getExtra())) { params.putAll(taskParams.getExtra()); } } mergeOverrides(device, taskParams, params); return params; } private void mergeOverrides(DeviceConfig device, TaskParameters taskParameters, Map params) { if (CollectionUtils.isEmpty(taskParameters.getDeviceOverrides())) { return; } Map override = taskParameters.getDeviceOverrides().get(device.getDeviceType()); if (override == null && StringUtils.hasText(device.getDeviceCode())) { override = taskParameters.getDeviceOverrides().get(device.getDeviceCode()); } if (override != null) { params.putAll(override); } } private void updateContextAfterSuccess(DeviceConfig device, TaskExecutionContext context, Map params) { switch (device.getDeviceType()) { case DeviceConfig.DeviceType.LOAD_VEHICLE: context.setLoadedGlassIds(extractGlassIds(params)); break; case DeviceConfig.DeviceType.LARGE_GLASS: context.setProcessedGlassIds(extractGlassIds(params)); break; default: break; } } private List extractGlassIds(Map params) { if (params == null) { return Collections.emptyList(); } Object glassIds = params.get("glassIds"); if (glassIds instanceof List) { @SuppressWarnings("unchecked") List cast = (List) glassIds; return new ArrayList<>(cast); } Object glassId = params.get("glassId"); if (glassId != null) { return Collections.singletonList(String.valueOf(glassId)); } return Collections.emptyList(); } private String toJson(Object value) { try { return objectMapper.writeValueAsString(value); } catch (JsonProcessingException e) { return "{}"; } } private static class StepResult { private final boolean success; private final String message; private final String deviceName; private StepResult(boolean success, String message, String deviceName) { this.success = success; this.message = message; this.deviceName = deviceName; } public static StepResult success(String deviceName, String message) { return new StepResult(true, message, deviceName); } public static StepResult failure(String deviceName, String message) { return new StepResult(false, message, deviceName); } public boolean isSuccess() { return success; } public String getMessage() { return message; } public Map toSummary() { Map summary = new HashMap<>(); summary.put("deviceName", deviceName); summary.put("success", success); summary.put("message", message); return summary; } } }