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<String, String> 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<DeviceConfig> 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<Map<String, Object>> 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<String, Object> 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<String, Object> 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<MultiDeviceTask> update = Wrappers.<MultiDeviceTask>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<String, Object> 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<String, Object> buildOperationParams(DeviceConfig device, TaskExecutionContext context) {
|
Map<String, Object> 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<String> 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<String> 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<String, Object> params) {
|
if (CollectionUtils.isEmpty(taskParameters.getDeviceOverrides())) {
|
return;
|
}
|
Map<String, Object> 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<String, Object> 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<String> extractGlassIds(Map<String, Object> params) {
|
if (params == null) {
|
return Collections.emptyList();
|
}
|
Object glassIds = params.get("glassIds");
|
if (glassIds instanceof List) {
|
@SuppressWarnings("unchecked")
|
List<String> cast = (List<String>) 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<String, Object> toSummary() {
|
Map<String, Object> summary = new HashMap<>();
|
summary.put("deviceName", deviceName);
|
summary.put("success", success);
|
summary.put("message", message);
|
return summary;
|
}
|
}
|
}
|