情景状态闭环
This commit is contained in:
parent
50fc25abbc
commit
d102de849b
@ -0,0 +1,16 @@
|
||||
package com.yfd.business.css.common.exception;
|
||||
|
||||
public class ScenarioInferException extends RuntimeException {
|
||||
|
||||
private final String failDetailJson;
|
||||
|
||||
public ScenarioInferException(String message, String failDetailJson) {
|
||||
super(message);
|
||||
this.failDetailJson = failDetailJson;
|
||||
}
|
||||
|
||||
public String getFailDetailJson() {
|
||||
return failDetailJson;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,18 +2,23 @@ package com.yfd.business.css.controller;
|
||||
|
||||
import com.yfd.business.css.build.SimBuilder;
|
||||
import com.yfd.business.css.domain.Scenario;
|
||||
import com.yfd.business.css.domain.ScenarioResult;
|
||||
import com.yfd.business.css.facade.SimDataFacade;
|
||||
import com.yfd.business.css.model.*;
|
||||
import com.yfd.business.css.service.ScenarioResultService;
|
||||
import com.yfd.business.css.service.MaterialService;
|
||||
import com.yfd.business.css.service.ScenarioService;
|
||||
import com.yfd.business.css.service.SimService;
|
||||
import com.yfd.business.css.service.SimInferService;
|
||||
import com.yfd.platform.config.ResponseResult;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import com.yfd.platform.annotation.Log;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ -31,6 +36,8 @@ public class SimController {
|
||||
@Autowired private MaterialService materialService;
|
||||
@Autowired private SimInferService simInferService;
|
||||
@Autowired private ScenarioService scenarioService;
|
||||
@Autowired private ScenarioResultService scenarioResultService;
|
||||
@Autowired private ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
* 执行仿真计算
|
||||
@ -44,9 +51,24 @@ public class SimController {
|
||||
String projectId = (String) req.get("projectId");
|
||||
String scenarioId = (String) req.get("scenarioId");
|
||||
int steps = req.containsKey("steps") ? (int) req.get("steps") : 10;
|
||||
boolean clearResult = Boolean.TRUE.equals(req.get("clearResult"));
|
||||
|
||||
Scenario sc = scenarioService.getById(scenarioId);
|
||||
if (sc != null) {
|
||||
String st = sc.getStatus();
|
||||
if ("1".equals(st)) {
|
||||
return ResponseResult.error("情景正在运行中,请稍后重试");
|
||||
}
|
||||
if (("2".equals(st) || "3".equals(st)) && !clearResult) {
|
||||
return ResponseResult.error("请先清空历史结果后再重新模拟");
|
||||
}
|
||||
}
|
||||
if (clearResult) {
|
||||
scenarioResultService.remove(new QueryWrapper<ScenarioResult>().eq("scenario_id", scenarioId));
|
||||
}
|
||||
|
||||
// 0. Update Status: 更新情景状态为进行中
|
||||
updateScenarioStatus(scenarioId, "1"); // 1: 进行中
|
||||
updateScenarioStatus(scenarioId, "1", null); // 1: 进行中
|
||||
|
||||
try {
|
||||
// 1. Load Data: 获取项目、设备和事件数据
|
||||
@ -75,16 +97,30 @@ public class SimController {
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
// 仿真计算失败,更新状态为失败 (3: 失败)
|
||||
updateScenarioStatus(scenarioId, "3");
|
||||
String failDetail = null;
|
||||
try {
|
||||
Map<String, Object> fd = new HashMap<>();
|
||||
fd.put("code", "SIMULATION_FAILED");
|
||||
fd.put("message", "仿真计算失败");
|
||||
fd.put("detail", e.getMessage() == null ? "" : e.getMessage());
|
||||
fd.put("suggestions", List.of(
|
||||
"请检查情景配置、事件配置与拓扑数据是否完整",
|
||||
"请检查输入参数 steps 与相关数据是否符合预期"
|
||||
));
|
||||
failDetail = objectMapper.writeValueAsString(fd);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
updateScenarioStatus(scenarioId, "3", failDetail);
|
||||
return ResponseResult.error("Simulation failed: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateScenarioStatus(String scenarioId, String status) {
|
||||
private void updateScenarioStatus(String scenarioId, String status, String failDetail) {
|
||||
try {
|
||||
Scenario scenario = new Scenario();
|
||||
scenario.setScenarioId(scenarioId);
|
||||
scenario.setStatus(status);
|
||||
scenario.setFailDetail(failDetail);
|
||||
scenario.setUpdatedAt(LocalDateTime.now());
|
||||
scenarioService.updateById(scenario);
|
||||
} catch (Exception e) {
|
||||
|
||||
@ -54,4 +54,7 @@ public class Scenario implements Serializable {
|
||||
*/
|
||||
@TableField("device_algo_config")
|
||||
private String deviceAlgoConfig;
|
||||
|
||||
@TableField("fail_detail")
|
||||
private String failDetail;
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.yfd.business.css.domain.Scenario;
|
||||
import com.yfd.business.css.domain.ScenarioResult;
|
||||
import com.yfd.business.css.domain.AlgorithmModel;
|
||||
import com.yfd.business.css.common.exception.ScenarioInferException;
|
||||
import com.yfd.business.css.model.DeviceStepInfo;
|
||||
import com.yfd.business.css.model.InferRequest;
|
||||
import com.yfd.business.css.model.InferResponse;
|
||||
@ -52,6 +53,8 @@ public class DeviceInferService {
|
||||
// 增加标志位,记录是否至少成功执行了一次推理
|
||||
boolean hasAnySuccess = false;
|
||||
boolean hasAnyError = false;
|
||||
List<Map<String, Object>> missingModels = new ArrayList<>();
|
||||
List<String> errorMessages = new ArrayList<>();
|
||||
|
||||
// 1. 获取情景配置信息
|
||||
Scenario scenario = scenarioService.getById(scenarioId);
|
||||
@ -117,6 +120,11 @@ public class DeviceInferService {
|
||||
if (model == null || model.getModelPath() == null) {
|
||||
log.error("Model path not found for algorithmType: {}, deviceType: {}, materialType: {}", currentAlgoType, deviceType, currentMaterialType);
|
||||
hasAnyError = true;
|
||||
missingModels.add(Map.of(
|
||||
"algorithmType", currentAlgoType,
|
||||
"deviceType", deviceType,
|
||||
"materialType", currentMaterialType
|
||||
));
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -186,6 +194,9 @@ public class DeviceInferService {
|
||||
} catch (Exception e) {
|
||||
log.error("推理异常: {}", e.getMessage(), e);
|
||||
hasAnyError = true;
|
||||
if (e.getMessage() != null && !e.getMessage().isBlank()) {
|
||||
errorMessages.add(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -193,8 +204,42 @@ public class DeviceInferService {
|
||||
|
||||
// 5. 最终检查:如果没有任何成功的推理,且发生过错误,抛出异常以通知上层
|
||||
if (!hasAnySuccess && hasAnyError) {
|
||||
throw new RuntimeException("所有设备推理均失败或未找到模型");
|
||||
Map<String, Object> failDetail = new HashMap<>();
|
||||
if (!missingModels.isEmpty()) {
|
||||
failDetail.put("code", "MODEL_NOT_FOUND");
|
||||
failDetail.put("message", "缺少可用模型");
|
||||
failDetail.put("missingModels", dedupeMissingModels(missingModels));
|
||||
failDetail.put("suggestions", List.of(
|
||||
"请在模型管理中训练并激活缺失模型(algorithmType/deviceType/materialType 对应组合)",
|
||||
"检查材料类型来源是否正确,必要时启用材料类型回退/默认映射策略"
|
||||
));
|
||||
} else {
|
||||
failDetail.put("code", "INFER_FAILED");
|
||||
failDetail.put("message", "推理失败");
|
||||
if (!errorMessages.isEmpty()) {
|
||||
failDetail.put("errors", errorMessages.size() > 50 ? errorMessages.subList(0, 50) : errorMessages);
|
||||
}
|
||||
failDetail.put("suggestions", List.of(
|
||||
"请检查 Python 推理服务是否可用以及请求参数是否完整",
|
||||
"请检查模型是否已训练并激活,算法类型/设备类型/材料类型是否匹配"
|
||||
));
|
||||
}
|
||||
String json = null;
|
||||
try {
|
||||
json = objectMapper.writeValueAsString(failDetail);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
throw new ScenarioInferException("推理失败", json);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> dedupeMissingModels(List<Map<String, Object>> missingModels) {
|
||||
LinkedHashMap<String, Map<String, Object>> m = new LinkedHashMap<>();
|
||||
for (Map<String, Object> x : missingModels) {
|
||||
String k = String.valueOf(x.get("algorithmType")) + "|" + String.valueOf(x.get("deviceType")) + "|" + String.valueOf(x.get("materialType"));
|
||||
m.putIfAbsent(k, x);
|
||||
}
|
||||
return new ArrayList<>(m.values());
|
||||
}
|
||||
|
||||
private InferRequest buildInferenceRequest(String deviceType,List<DeviceStepInfo> devices,String modelPath, List<String> requiredFeatures) {
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
package com.yfd.business.css.service;
|
||||
|
||||
import com.yfd.business.css.domain.Scenario;
|
||||
import com.yfd.business.css.common.exception.ScenarioInferException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.yfd.business.css.model.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -25,6 +27,9 @@ public class SimInferService {
|
||||
@Autowired
|
||||
private ScenarioService scenarioService;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Async
|
||||
public void asyncInferAndSave(String projectId, String scenarioId, SimContext context, List<SimUnit> units) {
|
||||
try {
|
||||
@ -83,7 +88,7 @@ public class SimInferService {
|
||||
if (groupedDevices.isEmpty()) {
|
||||
log.warn("No device data found for inference. scenarioId: {}", scenarioId);
|
||||
// 即使没有数据,也应该更新状态为完成,或者视为正常结束
|
||||
updateScenarioStatus(scenarioId, "2");
|
||||
updateScenarioStatus(scenarioId, "2", null);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -98,21 +103,39 @@ public class SimInferService {
|
||||
// 但由于是 void 方法,无法直接得知。
|
||||
// 简单改进:如果 groupedDevices 非空,但所有组都因为找不到模型而跳过,应该视为失败吗?
|
||||
// 目前策略:只要没有抛出未捕获异常,就视为 Success。
|
||||
updateScenarioStatus(scenarioId, "2");
|
||||
updateScenarioStatus(scenarioId, "2", null);
|
||||
log.info("Async inference completed successfully. scenarioId: {}", scenarioId);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Async inference failed. scenarioId: {}, error: {}", scenarioId, e.getMessage(), e);
|
||||
// 5. 更新状态为失败 (假设 3 代表失败)
|
||||
updateScenarioStatus(scenarioId, "3");
|
||||
String failDetail = null;
|
||||
if (e instanceof ScenarioInferException) {
|
||||
failDetail = ((ScenarioInferException) e).getFailDetailJson();
|
||||
} else {
|
||||
try {
|
||||
Map<String, Object> fd = new HashMap<>();
|
||||
fd.put("code", "INFER_FAILED");
|
||||
fd.put("message", "推理失败");
|
||||
fd.put("detail", e.getMessage() == null ? "" : e.getMessage());
|
||||
fd.put("suggestions", List.of(
|
||||
"请检查模型是否已训练并激活,算法类型/设备类型/材料类型是否匹配",
|
||||
"请检查 Python 推理服务是否可用以及请求参数是否完整"
|
||||
));
|
||||
failDetail = objectMapper.writeValueAsString(fd);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
updateScenarioStatus(scenarioId, "3", failDetail);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateScenarioStatus(String scenarioId, String status) {
|
||||
private void updateScenarioStatus(String scenarioId, String status, String failDetail) {
|
||||
try {
|
||||
Scenario scenario = new Scenario();
|
||||
scenario.setScenarioId(scenarioId);
|
||||
scenario.setStatus(status);
|
||||
scenario.setFailDetail(failDetail);
|
||||
scenario.setUpdatedAt(LocalDateTime.now());
|
||||
scenarioService.updateById(scenario);
|
||||
} catch (Exception e) {
|
||||
|
||||
@ -24,49 +24,43 @@ public class AlgorithmModelServiceImpl extends ServiceImpl<AlgorithmModelMapper,
|
||||
@Override
|
||||
public String getCurrentModelPath(String algorithmType, String deviceType, String materialType) {
|
||||
log.debug("Querying current model path for algorithmType: {}, deviceType: {}, materialType: {}", algorithmType, deviceType, materialType);
|
||||
QueryWrapper<AlgorithmModel> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("algorithm_type", algorithmType)
|
||||
.eq("device_type", deviceType)
|
||||
.eq("is_current", 1); // 当前激活版本
|
||||
|
||||
if (materialType != null && !materialType.isEmpty()) {
|
||||
queryWrapper.eq("material_type", materialType);
|
||||
} else {
|
||||
// 如果未指定材料类型,优先匹配无材料类型的通用模型,或者返回任意匹配(兼容旧逻辑)
|
||||
// 这里为了稳健,如果 materialType 为 null,暂不添加 material_type 的过滤条件,或者显式匹配 null
|
||||
// 建议:如果 materialType 为空,尝试匹配 material_type is null or ''
|
||||
// queryWrapper.and(w -> w.isNull("material_type").or().eq("material_type", ""));
|
||||
}
|
||||
|
||||
List<AlgorithmModel> models = list(queryWrapper);
|
||||
if (models != null && !models.isEmpty()) {
|
||||
AlgorithmModel model = models.get(0);
|
||||
log.debug("Found model: {}", model.getModelPath());
|
||||
return model.getModelPath();
|
||||
} else {
|
||||
AlgorithmModel model = getCurrentModel(algorithmType, deviceType, materialType);
|
||||
if (model == null) {
|
||||
log.warn("Model not found in database for algorithmType: {}, deviceType: {}, materialType: {}", algorithmType, deviceType, materialType);
|
||||
return null;
|
||||
}
|
||||
log.debug("Found model: {}", model.getModelPath());
|
||||
return model.getModelPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlgorithmModel getCurrentModel(String algorithmType, String deviceType, String materialType) {
|
||||
log.debug("Querying current model for algorithmType: {}, deviceType: {}, materialType: {}", algorithmType, deviceType, materialType);
|
||||
QueryWrapper<AlgorithmModel> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("algorithm_type", algorithmType)
|
||||
String mt = materialType == null ? null : materialType.trim();
|
||||
if (mt != null && mt.isEmpty()) mt = null;
|
||||
|
||||
QueryWrapper<AlgorithmModel> qw = new QueryWrapper<>();
|
||||
qw.eq("algorithm_type", algorithmType)
|
||||
.eq("device_type", deviceType)
|
||||
.eq("is_current", 1); // 当前激活版本
|
||||
|
||||
if (materialType != null && !materialType.isEmpty()) {
|
||||
queryWrapper.eq("material_type", materialType);
|
||||
}
|
||||
|
||||
List<AlgorithmModel> models = list(queryWrapper);
|
||||
if (models != null && !models.isEmpty()) {
|
||||
return models.get(0);
|
||||
.eq("is_current", 1);
|
||||
if (mt != null) {
|
||||
qw.eq("material_type", mt);
|
||||
} else {
|
||||
return null;
|
||||
qw.and(w -> w.isNull("material_type").or().eq("material_type", ""));
|
||||
}
|
||||
List<AlgorithmModel> models = list(qw);
|
||||
if (models != null && !models.isEmpty()) return models.get(0);
|
||||
|
||||
if (mt != null) {
|
||||
QueryWrapper<AlgorithmModel> fallback = new QueryWrapper<>();
|
||||
fallback.eq("algorithm_type", algorithmType)
|
||||
.eq("device_type", deviceType)
|
||||
.eq("is_current", 1)
|
||||
.and(w -> w.isNull("material_type").or().eq("material_type", ""));
|
||||
List<AlgorithmModel> generic = list(fallback);
|
||||
if (generic != null && !generic.isEmpty()) return generic.get(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Loading…
Reference in New Issue
Block a user