Merge branch 'develop-business-css' of http://121.37.111.42:3000/ThbTech/JavaProjectRepo into develop-business-css

This commit is contained in:
limengnan 2026-03-19 08:54:35 +08:00
commit be188c9c05
12 changed files with 302 additions and 94 deletions

View File

@ -40,6 +40,8 @@ public class SimBuilder {
JsonNode root = objectMapper.readTree(project.getTopology()); JsonNode root = objectMapper.readTree(project.getTopology());
Map<String, String> devToMat = buildDeviceMaterialMap(root); Map<String, String> devToMat = buildDeviceMaterialMap(root);
Map<String, Map<String, Double>> matStaticDb = buildMaterialStaticFromDb(devToMat, materialService); Map<String, Map<String, Double>> matStaticDb = buildMaterialStaticFromDb(devToMat, materialService);
// 补充建立 Material ID -> Material Type 的映射
Map<String, String> matTypeMap = buildMaterialTypeMap(devToMat, materialService);
JsonNode devicesNode = root.path("devices"); JsonNode devicesNode = root.path("devices");
if (devicesNode.isArray()) { if (devicesNode.isArray()) {
@ -49,6 +51,7 @@ public class SimBuilder {
String type = deviceNode.path("type").asText(); String type = deviceNode.path("type").asText();
String materialId = devToMat.get(deviceId); String materialId = devToMat.get(deviceId);
String materialType = materialId != null ? matTypeMap.get(materialId) : null;
Map<String, Double> staticProps = new HashMap<>(); Map<String, Double> staticProps = new HashMap<>();
@ -92,7 +95,7 @@ public class SimBuilder {
} }
} }
units.add(new SimUnit(deviceId, deviceId, materialId, type, staticProps)); units.add(new SimUnit(deviceId, deviceId, materialId, type, materialType, staticProps));
} }
} }
} catch (Exception e) { } catch (Exception e) {
@ -101,6 +104,24 @@ public class SimBuilder {
return units; return units;
} }
private Map<String, String> buildMaterialTypeMap(Map<String, String> devToMat, MaterialService materialService) {
Map<String, String> out = new HashMap<>();
if (devToMat.isEmpty()) return out;
Set<String> mids = new HashSet<>(devToMat.values());
List<Material> mats = materialService.list(new QueryWrapper<Material>().in("material_id", mids));
for (Material m : mats) {
String type = "unknown";
// 简单推导规则: 优先判定 Pu其次 U
if (m.getPuConcentration() != null && m.getPuConcentration().doubleValue() > 0) {
type = "Pu";
} else if (m.getUConcentration() != null && m.getUConcentration().doubleValue() > 0) {
type = "U";
}
out.put(m.getMaterialId(), type);
}
return out;
}
private void injectDeviceSize(Device device, Map<String, Double> staticProps) { private void injectDeviceSize(Device device, Map<String, Double> staticProps) {
try { try {
String sizeJson = device.getSize(); String sizeJson = device.getSize();
@ -124,7 +145,7 @@ public class SimBuilder {
case "CylindricalTank": case "CylindricalTank":
case "AnnularTank": case "AnnularTank":
case "TubeBundleTank": case "TubeBundleTank"://管束槽
parseCommonSize(sizeNode, staticProps); parseCommonSize(sizeNode, staticProps);
break; break;

View File

@ -80,11 +80,12 @@ public class AlgorithmModelController {
return algorithmModelService.deleteBatchWithCheck(ids); return algorithmModelService.deleteBatchWithCheck(ids);
} }
//返回该算法+设备类型的版本列表 //返回该算法+设备类型+材料类型的版本列表
@GetMapping("/search") @GetMapping("/search")
@Operation(summary = "查询模型版本列表", description = "按算法类型与设备类型过滤并分页返回模型版本") @Operation(summary = "查询模型版本列表", description = "按算法类型、设备类型与材料类型过滤并分页返回模型版本")
public Page<AlgorithmModel> search(@RequestParam(required = false) String algorithmType, public Page<AlgorithmModel> search(@RequestParam(required = false) String algorithmType,
@RequestParam(required = false) String deviceType, @RequestParam(required = false) String deviceType,
@RequestParam(required = false) String materialType,
@RequestParam(required = false) String versionTag, @RequestParam(required = false) String versionTag,
@RequestParam(required = false) String isCurrent, @RequestParam(required = false) String isCurrent,
@RequestParam(defaultValue = "1") long pageNum, @RequestParam(defaultValue = "1") long pageNum,
@ -92,6 +93,7 @@ public class AlgorithmModelController {
QueryWrapper<AlgorithmModel> qw = new QueryWrapper<>(); QueryWrapper<AlgorithmModel> qw = new QueryWrapper<>();
if (algorithmType != null && !algorithmType.isEmpty()) qw.eq("algorithm_type", algorithmType); if (algorithmType != null && !algorithmType.isEmpty()) qw.eq("algorithm_type", algorithmType);
if (deviceType != null && !deviceType.isEmpty()) qw.eq("device_type", deviceType); if (deviceType != null && !deviceType.isEmpty()) qw.eq("device_type", deviceType);
if (materialType != null && !materialType.isEmpty()) qw.eq("material_type", materialType);
if (versionTag != null && !versionTag.isEmpty()) qw.eq("version_tag", versionTag); if (versionTag != null && !versionTag.isEmpty()) qw.eq("version_tag", versionTag);
if (isCurrent != null && !isCurrent.isEmpty()) qw.eq("is_current", isCurrent); if (isCurrent != null && !isCurrent.isEmpty()) qw.eq("is_current", isCurrent);
qw.orderByDesc("updated_at"); qw.orderByDesc("updated_at");
@ -99,29 +101,38 @@ public class AlgorithmModelController {
return algorithmModelService.page(page, qw); return algorithmModelService.page(page, qw);
} }
//返回该算法+设备类型的当前激活版本 //返回该算法+设备类型+材料类型的当前激活版本
@GetMapping("/current") @GetMapping("/current")
@Operation(summary = "获取当前激活版本", description = "根据算法类型与设备类型,返回 is_current=1 的模型版本") @Operation(summary = "获取当前激活版本", description = "根据算法类型、设备类型与材料类型,返回 is_current=1 的模型版本")
public AlgorithmModel getCurrent(@RequestParam String algorithmType, public AlgorithmModel getCurrent(@RequestParam String algorithmType,
@RequestParam String deviceType) { @RequestParam String deviceType,
@RequestParam(required = false) String materialType) {
QueryWrapper<AlgorithmModel> qw = new QueryWrapper<>(); QueryWrapper<AlgorithmModel> qw = new QueryWrapper<>();
qw.eq("algorithm_type", algorithmType); qw.eq("algorithm_type", algorithmType);
qw.eq("device_type", deviceType); qw.eq("device_type", deviceType);
if (materialType != null && !materialType.isEmpty()) {
qw.eq("material_type", materialType);
}
qw.eq("is_current", 1); qw.eq("is_current", 1);
qw.orderByDesc("updated_at"); qw.orderByDesc("updated_at");
return algorithmModelService.getOne(qw); return algorithmModelService.getOne(qw, false); // 使用 false 避免多条结果时抛出异常虽然正常不应该有多条
} }
//版本激活 //版本激活
@PostMapping("/activate") @PostMapping("/activate")
@Operation(summary = "激活模型版本", description = "将目标模型版本设为当前,并将同组其他版本设为非当前") @Operation(summary = "激活模型版本", description = "将目标模型版本设为当前,并将同组(算法+设备+材料)其他版本设为非当前")
public boolean activate(@RequestParam String algorithmModelId) { public boolean activate(@RequestParam String algorithmModelId) {
AlgorithmModel model = algorithmModelService.getById(algorithmModelId); AlgorithmModel model = algorithmModelService.getById(algorithmModelId);
if (model == null) return false; if (model == null) return false;
// 先将所有版本设为非当前 // 先将所有同组版本设为非当前
QueryWrapper<AlgorithmModel> qw = new QueryWrapper<>(); QueryWrapper<AlgorithmModel> qw = new QueryWrapper<>();
qw.eq("algorithm_type", model.getAlgorithmType()); qw.eq("algorithm_type", model.getAlgorithmType());
qw.eq("device_type", model.getDeviceType()); qw.eq("device_type", model.getDeviceType());
if (model.getMaterialType() != null && !model.getMaterialType().isEmpty()) {
qw.eq("material_type", model.getMaterialType());
} else {
qw.and(wrapper -> wrapper.isNull("material_type").or().eq("material_type", ""));
}
AlgorithmModel upd = new AlgorithmModel(); AlgorithmModel upd = new AlgorithmModel();
upd.setIsCurrent(0); upd.setIsCurrent(0);
algorithmModelService.update(upd, qw); algorithmModelService.update(upd, qw);
@ -138,6 +149,7 @@ public class AlgorithmModelController {
public Map<String, Object> trainExcel(@RequestBody Map<String, Object> body) { public Map<String, Object> trainExcel(@RequestBody Map<String, Object> body) {
String algorithmType = str(body.get("algorithm_type")); String algorithmType = str(body.get("algorithm_type"));
String deviceType = str(body.get("device_type")); String deviceType = str(body.get("device_type"));
String materialType = str(body.getOrDefault("material_type", ""));
String datasetPath = str(body.get("dataset_path")); String datasetPath = str(body.get("dataset_path"));
String modelDir = str(body.getOrDefault("model_dir", "")); String modelDir = str(body.getOrDefault("model_dir", ""));
boolean activate = bool(body.getOrDefault("activate", false)); boolean activate = bool(body.getOrDefault("activate", false));
@ -178,6 +190,7 @@ public class AlgorithmModelController {
model.setAlgorithmModelId(UUID.randomUUID().toString()); model.setAlgorithmModelId(UUID.randomUUID().toString());
model.setAlgorithmType(algorithmType); model.setAlgorithmType(algorithmType);
model.setDeviceType(deviceType); model.setDeviceType(deviceType);
model.setMaterialType(materialType);
model.setVersionTag(genVersionTag()); model.setVersionTag(genVersionTag());
model.setModelPath(modelPath); model.setModelPath(modelPath);
model.setFeatureMapSnapshot(isBlank(featureMapSnapshot) ? "{}" : featureMapSnapshot); model.setFeatureMapSnapshot(isBlank(featureMapSnapshot) ? "{}" : featureMapSnapshot);
@ -190,6 +203,11 @@ public class AlgorithmModelController {
if (activate) { if (activate) {
QueryWrapper<AlgorithmModel> qw = new QueryWrapper<>(); QueryWrapper<AlgorithmModel> qw = new QueryWrapper<>();
qw.eq("algorithm_type", algorithmType).eq("device_type", deviceType); qw.eq("algorithm_type", algorithmType).eq("device_type", deviceType);
if (!isBlank(materialType)) {
qw.eq("material_type", materialType);
} else {
qw.and(wrapper -> wrapper.isNull("material_type").or().eq("material_type", ""));
}
AlgorithmModel upd = new AlgorithmModel(); AlgorithmModel upd = new AlgorithmModel();
upd.setIsCurrent(0); upd.setIsCurrent(0);
algorithmModelService.update(upd, qw); algorithmModelService.update(upd, qw);
@ -204,6 +222,7 @@ public class AlgorithmModelController {
public Map<String, Object> trainSamples(@RequestBody Map<String, Object> body) { public Map<String, Object> trainSamples(@RequestBody Map<String, Object> body) {
String algorithmType = str(body.get("algorithm_type")); String algorithmType = str(body.get("algorithm_type"));
String deviceType = str(body.get("device_type")); String deviceType = str(body.get("device_type"));
String materialType = str(body.getOrDefault("material_type", ""));
Object samples = body.get("samples"); // 期望为 List<Map>由前端提供 Object samples = body.get("samples"); // 期望为 List<Map>由前端提供
String modelDir = str(body.getOrDefault("model_dir", "")); String modelDir = str(body.getOrDefault("model_dir", ""));
boolean activate = bool(body.getOrDefault("activate", false)); boolean activate = bool(body.getOrDefault("activate", false));
@ -230,6 +249,7 @@ public class AlgorithmModelController {
model.setAlgorithmModelId(UUID.randomUUID().toString()); model.setAlgorithmModelId(UUID.randomUUID().toString());
model.setAlgorithmType(algorithmType); model.setAlgorithmType(algorithmType);
model.setDeviceType(deviceType); model.setDeviceType(deviceType);
model.setMaterialType(materialType);
model.setVersionTag(genVersionTag()); model.setVersionTag(genVersionTag());
model.setModelPath(modelPath); model.setModelPath(modelPath);
model.setFeatureMapSnapshot(isBlank(featureMapSnapshot) ? "{}" : featureMapSnapshot); model.setFeatureMapSnapshot(isBlank(featureMapSnapshot) ? "{}" : featureMapSnapshot);
@ -242,6 +262,11 @@ public class AlgorithmModelController {
if (activate) { if (activate) {
QueryWrapper<AlgorithmModel> qw = new QueryWrapper<>(); QueryWrapper<AlgorithmModel> qw = new QueryWrapper<>();
qw.eq("algorithm_type", algorithmType).eq("device_type", deviceType); qw.eq("algorithm_type", algorithmType).eq("device_type", deviceType);
if (!isBlank(materialType)) {
qw.eq("material_type", materialType);
} else {
qw.and(wrapper -> wrapper.isNull("material_type").or().eq("material_type", ""));
}
AlgorithmModel upd = new AlgorithmModel(); AlgorithmModel upd = new AlgorithmModel();
upd.setIsCurrent(0); upd.setIsCurrent(0);
algorithmModelService.update(upd, qw); algorithmModelService.update(upd, qw);

View File

@ -44,29 +44,49 @@ public class SimController {
int steps = req.containsKey("steps") ? (int) req.get("steps") : 10; int steps = req.containsKey("steps") ? (int) req.get("steps") : 10;
// 0. Update Status: 更新情景状态为进行中 // 0. Update Status: 更新情景状态为进行中
Scenario startScenario = new Scenario(); updateScenarioStatus(scenarioId, "1"); // 1: 进行中
startScenario.setScenarioId(scenarioId);
startScenario.setStatus("1"); // 1: 进行中
startScenario.setUpdatedAt(LocalDateTime.now());
scenarioService.updateById(startScenario);
// 1. Load Data: 获取项目设备和事件数据 try {
SimDataPackage data = simDataFacade.loadSimulationData(projectId, scenarioId); // 1. Load Data: 获取项目设备和事件数据
SimDataPackage data = simDataFacade.loadSimulationData(projectId, scenarioId);
// 2. Build Model: 将原始数据转换为仿真模型 (Units, Events, Nodes)
List<SimUnit> units = simBuilder.buildUnits(data, materialService); // 2. Build Model: 将原始数据转换为仿真模型 (Units, Events, Nodes)
List<SimEvent> events = simBuilder.buildEvents(data.getEvents()); List<SimUnit> units = simBuilder.buildUnits(data, materialService);
List<SimInfluenceNode> nodes = simBuilder.buildInfluenceNodes(data.getProject()); List<SimEvent> events = simBuilder.buildEvents(data.getEvents());
List<SimInfluenceNode> nodes = simBuilder.buildInfluenceNodes(data.getProject());
// 3. Run Engine: 执行核心仿真计算返回上下文结果
SimContext ctx = simService.runSimulation(units, events, nodes, steps); // 3. Run Engine: 执行核心仿真计算返回上下文结果
SimContext ctx = simService.runSimulation(units, events, nodes, steps);
// 4. Async Infer: 异步执行推理并保存结果完成后更新情景状态
simInferService.asyncInferAndSave(projectId, scenarioId, ctx, units); // 4. Async Infer: 异步执行推理并保存结果完成后更新情景状态
simInferService.asyncInferAndSave(projectId, scenarioId, ctx, units);
// 6. Convert Result: 将仿真结果转换为前端友好的格式包含静态属性补全和元数据 // 6. Convert Result: 将仿真结果转换为前端友好的格式包含静态属性补全和元数据
Map<String, Object> resultData = SimResultConverter.toFrames(ctx, units, projectId, scenarioId); Map<String, Object> resultData = SimResultConverter.toFrames(ctx, units, projectId, scenarioId);
return ResponseResult.successData(resultData); // 为了让前端明确感知推理是否启动成功这里增加一个状态提示
// 但因为是异步的我们只能保证已提交真正的成败由 asyncInferAndSave 决定
resultData.put("inferenceStatus", "submitted");
return ResponseResult.successData(resultData);
} catch (Exception e) {
e.printStackTrace();
// 仿真计算失败更新状态为失败 (3: 失败)
updateScenarioStatus(scenarioId, "3");
return ResponseResult.error("Simulation failed: " + e.getMessage());
}
}
private void updateScenarioStatus(String scenarioId, String status) {
try {
Scenario scenario = new Scenario();
scenario.setScenarioId(scenarioId);
scenario.setStatus(status);
scenario.setUpdatedAt(LocalDateTime.now());
scenarioService.updateById(scenario);
} catch (Exception e) {
System.err.println("Failed to update scenario status to " + status + ": " + e.getMessage());
}
} }
} }

View File

@ -24,6 +24,9 @@ public class AlgorithmModel implements Serializable {
@TableField("device_type") @TableField("device_type")
private String deviceType; private String deviceType;
@TableField("material_type")
private String materialType;
@TableField("version_tag") @TableField("version_tag")
private String versionTag; private String versionTag;

View File

@ -5,15 +5,21 @@ import java.util.Map;
public class DeviceStepInfo { public class DeviceStepInfo {
private String deviceId; private String deviceId;
private String deviceType; private String deviceType;
private String materialType; // 新增
private Map<String, Object> properties; private Map<String, Object> properties;
private int step; private int step;
private int time; private int time;
public DeviceStepInfo() {} public DeviceStepInfo() {}
public DeviceStepInfo(String deviceId, String deviceType, Map<String, Object> properties,int step,int time) { public DeviceStepInfo(String deviceId, String deviceType, Map<String, Object> properties, int step, int time) {
this(deviceId, deviceType, null, properties, step, time);
}
public DeviceStepInfo(String deviceId, String deviceType, String materialType, Map<String, Object> properties, int step, int time) {
this.deviceId = deviceId; this.deviceId = deviceId;
this.deviceType = deviceType; this.deviceType = deviceType;
this.materialType = materialType;
this.properties = properties; this.properties = properties;
this.step = step; this.step = step;
this.time = time; this.time = time;
@ -28,7 +34,6 @@ public class DeviceStepInfo {
this.deviceId = deviceId; this.deviceId = deviceId;
} }
public String getDeviceType() { public String getDeviceType() {
return deviceType; return deviceType;
} }
@ -37,6 +42,14 @@ public class DeviceStepInfo {
this.deviceType = deviceType; this.deviceType = deviceType;
} }
public String getMaterialType() {
return materialType;
}
public void setMaterialType(String materialType) {
this.materialType = materialType;
}
public Map<String, Object> getProperties() { public Map<String, Object> getProperties() {
return properties; return properties;
} }
@ -66,9 +79,10 @@ public class DeviceStepInfo {
return "DeviceStepInfo{" + return "DeviceStepInfo{" +
"deviceId='" + deviceId + '\'' + "deviceId='" + deviceId + '\'' +
", deviceType='" + deviceType + '\'' + ", deviceType='" + deviceType + '\'' +
", materialType='" + materialType + '\'' +
", properties=" + properties + ", properties=" + properties +
", step=" + step + ", step=" + step +
", time=" + time + ", time=" + time +
'}'; '}';
} }
} }

View File

@ -2,8 +2,17 @@ package com.yfd.business.css.model;
import java.util.Map; import java.util.Map;
public record SimUnit(String unitId, String deviceId, String materialId, String deviceType, Map<String, Double> staticProperties) { public record SimUnit(String unitId, String deviceId, String materialId, String deviceType, String materialType, Map<String, Double> staticProperties) {
public SimUnit(String unitId, String deviceId, String materialId, String deviceType, String materialType) {
this(unitId, deviceId, materialId, deviceType, materialType, Map.of());
}
// 兼容旧构造函数
public SimUnit(String unitId, String deviceId, String materialId, String deviceType, Map<String, Double> staticProperties) {
this(unitId, deviceId, materialId, deviceType, null, staticProperties);
}
public SimUnit(String unitId, String deviceId, String materialId, String deviceType) { public SimUnit(String unitId, String deviceId, String materialId, String deviceType) {
this(unitId, deviceId, materialId, deviceType, Map.of()); this(unitId, deviceId, materialId, deviceType, null, Map.of());
} }
} }

View File

@ -14,7 +14,17 @@ public interface AlgorithmModelService extends IService<AlgorithmModel> {
* @param deviceType 设备类型如CylindricalTank/AnnularTank * @param deviceType 设备类型如CylindricalTank/AnnularTank
* @return 激活版本的模型文件路径如果不存在则返回null * @return 激活版本的模型文件路径如果不存在则返回null
*/ */
String getCurrentModelPath(String algorithmType, String deviceType) ; String getCurrentModelPath(String algorithmType, String deviceType);
/**
* 根据算法类型设备类型材料类型获取当前激活的模型路径
*
* @param algorithmType 算法类型
* @param deviceType 设备类型
* @param materialType 材料类型如Pu/U
* @return 激活版本的模型文件路径
*/
String getCurrentModelPath(String algorithmType, String deviceType, String materialType);
boolean deleteBatchWithCheck(List<String> ids); boolean deleteBatchWithCheck(List<String> ids);

View File

@ -19,6 +19,8 @@ import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*; import java.util.*;
@Service @Service
@ -26,6 +28,11 @@ public class DeviceInferService {
@Value("${python.api.url:http://localhost:8000}") @Value("${python.api.url:http://localhost:8000}")
private String pythonInferUrl; private String pythonInferUrl;
@Value("${file-space.model-path}")
private String modelRootPath;
@Resource @Resource
private ScenarioService scenarioService; private ScenarioService scenarioService;
@Resource @Resource
@ -38,6 +45,10 @@ public class DeviceInferService {
public void processDeviceInference(String projectId, String scenarioId, public void processDeviceInference(String projectId, String scenarioId,
Map<String, List<DeviceStepInfo>> groupedDevices) { Map<String, List<DeviceStepInfo>> groupedDevices) {
// 增加标志位记录是否至少成功执行了一次推理
boolean hasAnySuccess = false;
boolean hasAnyError = false;
// 1. 获取情景配置信息 // 1. 获取情景配置信息
Scenario scenario = scenarioService.getById(scenarioId); Scenario scenario = scenarioService.getById(scenarioId);
if (scenario == null) { if (scenario == null) {
@ -81,53 +92,79 @@ public class DeviceInferService {
String currentAlgoType = algoEntry.getKey(); String currentAlgoType = algoEntry.getKey();
List<DeviceStepInfo> currentDevices = algoEntry.getValue(); List<DeviceStepInfo> currentDevices = algoEntry.getValue();
// 获取模型路径根据算法类型和设备类型 // 4.1 按材料类型进行三级分组 (AlgoType -> MaterialType)
System.out.println("Processing inference for algorithmType: " + currentAlgoType + ", deviceType: " + deviceType); Map<String, List<DeviceStepInfo>> matGroup = new HashMap<>();
String modelPath = algorithmModelService.getCurrentModelPath(currentAlgoType, deviceType); for (DeviceStepInfo d : currentDevices) {
System.out.println("modelPath=" + modelPath); String mType = d.getMaterialType();
// 如果 materialType 为空归类为 "unknown" null视业务而定这里保留 null
if (modelPath == null) { matGroup.computeIfAbsent(mType, k -> new ArrayList<>()).add(d);
System.err.println("Model path not found for algorithmType: " + currentAlgoType + ", deviceType: " + deviceType);
// 这里可以选择抛异常中断或者跳过该组继续处理其他组
// 为了保证健壮性这里选择记录错误并跳过或者根据业务需求抛出异常
// throw new IllegalArgumentException("未配置 " + currentAlgoType + " 模型路径 (deviceType: " + deviceType + ")");
continue;
} }
// 封装推理请求 // 4.2 遍历材料分组分别获取模型并推理
InferRequest request = buildInferenceRequest(deviceType, currentDevices, modelPath); for (Map.Entry<String, List<DeviceStepInfo>> matEntry : matGroup.entrySet()) {
// System.out.println("request=" + request); String currentMaterialType = matEntry.getKey();
List<DeviceStepInfo> batchDevices = matEntry.getValue();
try { // 获取模型路径根据算法类型设备类型材料类型
// 调用Python推理服务 System.out.println("Processing inference for algorithmType: " + currentAlgoType +
InferResponse response = infer(request); ", deviceType: " + deviceType + ", materialType: " + currentMaterialType);
System.out.println("推理服务返回结果: code=" + (response != null ? response.getCode() : "null")); String modelRelPath = algorithmModelService.getCurrentModelPath(currentAlgoType, deviceType, currentMaterialType);
System.out.println("modelRelPath=" + modelRelPath);
// 处理推理结果 if (modelRelPath == null) {
if (response != null && response.getCode() == 0) { System.err.println("Model path not found for algorithmType: " + currentAlgoType +
// 重新构建InferResponse对象示例 (为了兼容性保留原有逻辑) ", deviceType: " + deviceType + ", materialType: " + currentMaterialType);
InferResponse.InferData originalData = response.getData(); hasAnyError = true;
InferResponse.InferData newData = new InferResponse.InferData(); continue;
newData.setItems(originalData.getItems()); }
newData.setMeta(originalData.getMeta());
newData.setFeatures(originalData.getFeatures()); // 将相对路径转换为绝对路径
newData.setKeff(originalData.getKeff()); String absoluteModelPath = Paths.get(modelRootPath).resolve(modelRelPath).toAbsolutePath().normalize().toString();
System.out.println("Absolute modelPath=" + absoluteModelPath);
InferResponse reconstructedResponse = new InferResponse();
reconstructedResponse.setCode(response.getCode()); // 封装推理请求
reconstructedResponse.setMsg(response.getMsg()); InferRequest request = buildInferenceRequest(deviceType, batchDevices, absoluteModelPath);
reconstructedResponse.setData(newData); System.out.println("request=" + request);
processInferenceResults(projectId, scenarioId, deviceType, currentDevices, reconstructedResponse); try {
} else { // 调用Python推理服务
System.err.println("推理服务调用失败: " + (response != null ? response.getMsg() : "未知错误")); InferResponse response = infer(request);
System.out.println("推理服务返回结果: code=" + (response != null ? response.getCode() : "null"));
// 处理推理结果
if (response != null && response.getCode() == 0) {
// 重新构建InferResponse对象示例 (为了兼容性保留原有逻辑)
InferResponse.InferData originalData = response.getData();
InferResponse.InferData newData = new InferResponse.InferData();
newData.setItems(originalData.getItems());
newData.setMeta(originalData.getMeta());
newData.setFeatures(originalData.getFeatures());
newData.setKeff(originalData.getKeff());
InferResponse reconstructedResponse = new InferResponse();
reconstructedResponse.setCode(response.getCode());
reconstructedResponse.setMsg(response.getMsg());
reconstructedResponse.setData(newData);
processInferenceResults(projectId, scenarioId, deviceType, batchDevices, reconstructedResponse);
hasAnySuccess = true;
} else {
System.err.println("推理服务调用失败: " + (response != null ? response.getMsg() : "未知错误"));
hasAnyError = true;
}
} catch (Exception e) {
System.err.println("推理异常: " + e.getMessage());
e.printStackTrace();
hasAnyError = true;
} }
} catch (Exception e) {
System.err.println("推理异常: " + e.getMessage());
e.printStackTrace();
} }
} }
} }
// 5. 最终检查如果没有任何成功的推理且发生过错误抛出异常以通知上层
if (!hasAnySuccess && hasAnyError) {
throw new RuntimeException("所有设备推理均失败或未找到模型");
}
} }
private InferRequest buildInferenceRequest(String deviceType,List<DeviceStepInfo> devices,String modelPath) { private InferRequest buildInferenceRequest(String deviceType,List<DeviceStepInfo> devices,String modelPath) {

View File

@ -69,6 +69,7 @@ public class SimInferService {
DeviceStepInfo info = new DeviceStepInfo(); DeviceStepInfo info = new DeviceStepInfo();
info.setDeviceId(unit.deviceId()); // unitId 通常等于 deviceId info.setDeviceId(unit.deviceId()); // unitId 通常等于 deviceId
info.setDeviceType(deviceType); info.setDeviceType(deviceType);
info.setMaterialType(unit.materialType()); // 注入材料类型
info.setProperties(properties); info.setProperties(properties);
info.setStep(step); info.setStep(step);
info.setTime(step); // 假设 time = step或者根据步长计算 info.setTime(step); // 假设 time = step或者根据步长计算
@ -89,6 +90,12 @@ public class SimInferService {
deviceInferService.processDeviceInference(projectId, scenarioId, groupedDevices); deviceInferService.processDeviceInference(projectId, scenarioId, groupedDevices);
// 4. 更新状态为已完成 // 4. 更新状态为已完成
// 注意如果前面的 deviceInferService.processDeviceInference 内部发生部分异常但未抛出例如 continue
// 这里依然会设置为 2 (Success)
// 建议检查 deviceInferService 是否真的执行了推理
// 但由于是 void 方法无法直接得知
// 简单改进如果 groupedDevices 非空但所有组都因为找不到模型而跳过应该视为失败吗
// 目前策略只要没有抛出未捕获异常就视为 Success
updateScenarioStatus(scenarioId, "2"); updateScenarioStatus(scenarioId, "2");
} catch (Exception e) { } catch (Exception e) {

View File

@ -16,13 +16,29 @@ public class AlgorithmModelServiceImpl extends ServiceImpl<AlgorithmModelMapper,
@Override @Override
public String getCurrentModelPath(String algorithmType, String deviceType) { public String getCurrentModelPath(String algorithmType, String deviceType) {
System.out.println("Querying current model path for algorithmType: " + algorithmType + ", deviceType: " + deviceType); return getCurrentModelPath(algorithmType, deviceType, null);
}
@Override
public String getCurrentModelPath(String algorithmType, String deviceType, String materialType) {
System.out.println("Querying current model path for algorithmType: " + algorithmType + ", deviceType: " + deviceType + ", materialType: " + materialType);
QueryWrapper<AlgorithmModel> queryWrapper = new QueryWrapper<>(); QueryWrapper<AlgorithmModel> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("algorithm_type", algorithmType) queryWrapper.eq("algorithm_type", algorithmType)
.eq("device_type", deviceType) .eq("device_type", deviceType)
.eq("is_current", 1); // 当前激活版本 .eq("is_current", 1); // 当前激活版本
AlgorithmModel model = getOne(queryWrapper);
if (model != null) { 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);
System.out.println("Found model: " + model.getModelPath()); System.out.println("Found model: " + model.getModelPath());
return model.getModelPath(); return model.getModelPath();
} else { } else {
@ -42,9 +58,9 @@ public class AlgorithmModelServiceImpl extends ServiceImpl<AlgorithmModelMapper,
// 1. 查询是否存在当前激活版本 // 1. 查询是否存在当前激活版本
List<AlgorithmModel> currentModels = this.list( List<AlgorithmModel> currentModels = this.list(
new QueryWrapper<AlgorithmModel>() new QueryWrapper<AlgorithmModel>()
.in("id", ids) .in("algorithm_model_id", ids)
.eq("is_current", 1) .eq("is_current", 1)
.select("id", "model_name") .select("algorithm_model_id", "algorithm_type", "device_type")
); );
// 2. 若存在激活版本拒绝删除 // 2. 若存在激活版本拒绝删除
if (!currentModels.isEmpty()) { if (!currentModels.isEmpty()) {

View File

@ -308,23 +308,35 @@ public class ModelTrainServiceImpl extends ServiceImpl<ModelTrainTaskMapper, Mod
throw new BizException("versionTag 格式不合法"); throw new BizException("versionTag 格式不合法");
} }
// 检查版本号唯一性
long count = algorithmModelService.count(new QueryWrapper<AlgorithmModel>()
.eq("algorithm_type", task.getAlgorithmType())
.eq("device_type", task.getDeviceType())
.eq("version_tag", versionTag));
if (count > 0) {
throw new BizException("版本号已存在");
}
String algorithmType = task.getAlgorithmType(); String algorithmType = task.getAlgorithmType();
String deviceType = task.getDeviceType(); String deviceType = task.getDeviceType();
if (algorithmType == null || algorithmType.isBlank() || deviceType == null || deviceType.isBlank()) { if (algorithmType == null || algorithmType.isBlank() || deviceType == null || deviceType.isBlank()) {
throw new BizException("算法类型或设备类型不能为空"); throw new BizException("算法类型或设备类型不能为空");
} }
// 解析材料类型
String materialType = "unknown";
String outputPath = task.getModelOutputPath();
if (outputPath != null && !outputPath.isBlank()) {
String[] parts = outputPath.replace("\\", "/").split("/");
// 预期结构: runs/{algorithmType}/{deviceType}/{materialType}/{taskId}/...
if (parts.length > 4 && "runs".equals(parts[0])) {
materialType = parts[3];
}
}
// 检查版本号唯一性
long count = algorithmModelService.count(new QueryWrapper<AlgorithmModel>()
.eq("algorithm_type", algorithmType)
.eq("device_type", deviceType)
.eq("material_type", materialType)
.eq("version_tag", versionTag));
if (count > 0) {
throw new BizException("版本号已存在");
}
Path root = Paths.get(modelPath).toAbsolutePath().normalize(); Path root = Paths.get(modelPath).toAbsolutePath().normalize();
Path versionDir = root.resolve(Paths.get(algorithmType, deviceType, versionTag)).normalize(); Path versionDir = root.resolve(Paths.get(algorithmType, deviceType, materialType, versionTag)).normalize();
if (!versionDir.startsWith(root)) { if (!versionDir.startsWith(root)) {
throw new BizException("发布目录非法"); throw new BizException("发布目录非法");
} }
@ -339,11 +351,43 @@ public class ModelTrainServiceImpl extends ServiceImpl<ModelTrainTaskMapper, Mod
if (task.getMetricsImagePath() != null && !task.getMetricsImagePath().isBlank()) { if (task.getMetricsImagePath() != null && !task.getMetricsImagePath().isBlank()) {
publishedMetricsRelPath = copyToVersionDir(root, versionDir, task.getMetricsImagePath()); publishedMetricsRelPath = copyToVersionDir(root, versionDir, task.getMetricsImagePath());
} }
// 尝试一并复制训练过程生成的其他配置/特征文件
// 根据 python 端的生成规则这些文件通常与模型文件 (pipeline.pkl) 在同一个目录下
if (task.getModelOutputPath() != null && !task.getModelOutputPath().isBlank()) {
try {
// 推导出原始模型文件所在的目录
String normalized = task.getModelOutputPath().replace("\\", "/");
if (normalized.startsWith("models/")) {
normalized = normalized.substring("models/".length());
}
Path srcPath = Paths.get(normalized);
Path srcDir = srcPath.isAbsolute() ? srcPath.normalize().getParent() : root.resolve(normalized).normalize().getParent();
if (srcDir != null && Files.exists(srcDir)) {
// 需要复制的文件名列表
String[] extraFiles = {"train_params.json", "feature_map.json"};
for (String fileName : extraFiles) {
Path extraFileSrc = srcDir.resolve(fileName);
if (Files.exists(extraFileSrc) && Files.isRegularFile(extraFileSrc)) {
Path dest = versionDir.resolve(fileName).normalize();
if (!Files.exists(dest)) {
Files.copy(extraFileSrc, dest, StandardCopyOption.COPY_ATTRIBUTES);
}
}
}
}
} catch (Exception e) {
// 附加文件复制失败不应阻断发布主流程仅记录日志
System.err.println("复制附加训练文件失败: " + e.getMessage());
}
}
// 创建正式模型记录 // 创建正式模型记录
AlgorithmModel model = new AlgorithmModel(); AlgorithmModel model = new AlgorithmModel();
model.setAlgorithmType(task.getAlgorithmType()); model.setAlgorithmType(task.getAlgorithmType());
model.setDeviceType(task.getDeviceType()); model.setDeviceType(task.getDeviceType());
model.setMaterialType(materialType);
model.setVersionTag(versionTag); model.setVersionTag(versionTag);
model.setModelPath(publishedModelRelPath); model.setModelPath(publishedModelRelPath);
model.setMetricsImagePath(publishedMetricsRelPath); model.setMetricsImagePath(publishedMetricsRelPath);

View File

@ -11,9 +11,11 @@ spring:
password: q3eef875%&4@44%*3 password: q3eef875%&4@44%*3
slave: slave:
driverClassName: com.mysql.cj.jdbc.Driver driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://43.138.168.68:3306/businessdb_css?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true #url: jdbc:mysql://43.138.168.68:3306/businessdb_css?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true
url: jdbc:mysql://127.0.0.1:3306/businessdb_css?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true
username: root username: root
password: q3eef875%&4@44%*3 #password: q3eef875%&4@44%*3
password: 123456
file-space: file-space:
files: D:\css\files\ files: D:\css\files\