# SimController 及相关接口详细实现文档 v4-2 本文档基于 [SimController 及相关接口实现建议v4.md](file:///e:/projectJava/JavaProjectRepo/business-css/SimController%20%E5%8F%8A%E7%9B%B8%E5%85%B3%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E5%BB%BA%E8%AE%AEv4.md) 进一步细化,提供了更贴合当前代码库(ProjectServiceImpl)的详细实现方案。 ## 1. 核心目标 * **激活 `SimController`**:使其成为仿真服务的唯一入口,替代分散在 `ProjectController` 中的逻辑。 * **标准化计算管线**:实现 `Static -> Event(Input) -> Influence -> Event(Override)` 的标准计算流程。 * **复用现有逻辑**:最大程度复用 `ProjectServiceImpl` 中已有的 JSON 解析和数据组装代码,避免重复造轮子。 --- ## 2. 模块详细设计与实现 ### 2.1 数据获取层 (SimDataFacade) 负责从 DB 获取原始数据,并进行初步的组装。 **File:** `src/main/java/com/yfd/business/css/facade/SimDataFacade.java` (新建) ```java @Component public class SimDataFacade { @Autowired private ProjectService projectService; @Autowired private EventService eventService; @Autowired private MaterialService materialService; // 用于补全物料静态属性 public SimDataPackage loadSimulationData(String projectId, String scenarioId) { // 1. 获取项目与拓扑 Project project = projectService.getById(projectId); if (project == null) throw new IllegalArgumentException("Project not found: " + projectId); // 2. 获取事件 List events = eventService.list(new QueryWrapper().eq("scenario_id", scenarioId)); // 3. 预加载物料库 (用于后续补全静态属性) // 逻辑复用 ProjectServiceImpl.buildMaterialStaticFromDb // 这里先返回原始数据,由 Builder 处理具体的补全逻辑 return new SimDataPackage(project, events); } } ``` ### 2.2 模型构建层 (SimBuilder) 负责将 `Project` (JSON) 和 `Event` (JSON) 转换为 `SimUnit`, `SimEvent`, `SimInfluenceNode`。 **关键策略**:直接复用 `ProjectServiceImpl` 中的解析方法(`parseDeviceStaticsAndInfluences`, `buildValueProviders` 等),将其重构为静态工具方法或独立组件。 **File:** `src/main/java/com/yfd/business/css/build/SimBuilder.java` (激活并重构) ```java public class SimBuilder { private final ObjectMapper objectMapper = new ObjectMapper(); // 1. 构建单元 (SimUnit) - 包含静态属性 public List buildUnits(Project project, MaterialService materialService) { List units = new ArrayList<>(); JsonNode root = objectMapper.readTree(project.getTopology()); // 解析设备静态属性 (复用 ProjectServiceImpl.parseDeviceStaticsAndInfluences 的部分逻辑) // 解析物料静态属性 (复用 ProjectServiceImpl.buildMaterialStaticFromDb) // 伪代码示例 for (JsonNode deviceNode : root.path("devices")) { String deviceId = deviceNode.path("deviceId").asText(); Map staticProps = new HashMap<>(); // 1.1 解析 topology 中的 static 节点 // 1.2 解析 Device.size 并注入 // 1.3 如果有绑定的 material,查询 DB 并注入物料属性 units.add(new SimUnit(deviceId, ..., staticProps)); } return units; } // 2. 构建事件 (SimEvent) public List buildEvents(List events) { // 复用 ProjectServiceImpl.buildValueProviders 解析 attr_changes // 将解析出的 Schedule 转换为 List // 注意区分 isOverride (强制覆盖) } // 3. 构建影响关系 (SimInfluenceNode) public List buildInfluenceNodes(Project project) { // 复用 ProjectServiceImpl.parseDeviceStaticsAndInfluences 中的 influence 解析逻辑 } } ``` ### 2.3 核心计算引擎 (SimService) 实现标准计算管线。 **File:** `src/main/java/com/yfd/business/css/service/SimService.java` (重构) ```java public class SimService { public SimContext runSimulation(List units, List events, List nodes, int steps) { SimContext ctx = new SimContext(); // Step 1: 初始化静态基线 (t=0) for (SimUnit unit : units) { unit.getStaticProperties().forEach((k, v) -> ctx.setValue(SimPropertyKey.of(unit.unitId(), k), v) ); } // Step 2: 循环推进 (t=1 to steps) for (int step = 1; step <= steps; step++) { // 2.1 Event (Input): 应用普通事件 applyEvents(ctx, events, step, false); // 2.2 Influence: 计算影响关系 // 基于当前 ctx (包含 static + input event) 计算 applyInfluences(ctx, nodes); // 2.3 Event (Override): 应用强制覆盖事件 // 再次覆盖,确保强制逻辑生效 applyEvents(ctx, events, step, true); // 2.4 Snapshot ctx.snapshot(step); } return ctx; } // 辅助方法:applyEvents, applyInfluences } ``` ### 2.4 统一控制器 (SimController) **File:** `src/main/java/com/yfd/business/css/controller/SimController.java` (激活) ```java @RestController @RequestMapping("/sim") public class SimController { @Autowired private SimDataFacade simDataFacade; @Autowired private SimBuilder simBuilder; // 如果是 Bean @Autowired private SimService simService; // 标准运行接口 @PostMapping("/run") public Result> run(@RequestBody SimulationRequest req) { // 1. Load Data SimDataPackage data = simDataFacade.loadSimulationData(req.getProjectId(), req.getScenarioId()); // 2. Build Model List units = simBuilder.buildUnits(data.getProject(), ...); List events = simBuilder.buildEvents(data.getEvents()); List nodes = simBuilder.buildInfluenceNodes(data.getProject()); // 3. Run Engine SimContext ctx = simService.runSimulation(units, events, nodes, req.getSteps()); // 4. Convert Result (复用 DeviceDataParser 或 InferenceConverter) // 保持与前端/推理接口的数据格式兼容 return Result.success(SimResultConverter.toFrames(ctx)); } } ``` --- ## 3. 具体复用点清单 (ProjectServiceImpl -> SimBuilder) 为了加快开发,以下方法建议直接从 `ProjectServiceImpl` 提取到 `SimBuilder` 或工具类 `SimParserUtils` 中: 1. **`injectDeviceSize`**: 解析设备尺寸。 2. **`buildMaterialStaticFromDb`**: 补全物料静态属性。 3. **`parseDeviceStaticsAndInfluences`**: 解析设备静态值和影响关系。 4. **`parseMaterialStaticsAndInfluences`**: 解析物料静态值和影响关系。 5. **`buildValueProviders`**: 解析事件 JSON (attr_changes)。 **注意**:在提取时,需要将原本直接操作 `Map state` 的逻辑,改为构建 `SimUnit` 或 `SimInfluenceNode` 对象。 --- ## 4. 兼容性与迁移 * **API 兼容**:新接口 `/sim/run` 的返回结构应尽量与原 `/projects/simulation/run` 中的 `frames` 结构保持一致,以便前端无缝切换。 * **分步上线**: 1. 先上线 `/sim/run` 供测试使用。 2. 验证无误后,将前端调用切到新接口。 3. 废弃 `ProjectController` 中的相关接口。