195 lines
7.6 KiB
Markdown
195 lines
7.6 KiB
Markdown
|
|
# 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<Event> events = eventService.list(new QueryWrapper<Event>().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<SimUnit> buildUnits(Project project, MaterialService materialService) {
|
|||
|
|
List<SimUnit> 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<String, Double> 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<SimEvent> buildEvents(List<Event> events) {
|
|||
|
|
// 复用 ProjectServiceImpl.buildValueProviders 解析 attr_changes
|
|||
|
|
// 将解析出的 Schedule 转换为 List<SimEvent>
|
|||
|
|
// 注意区分 isOverride (强制覆盖)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 3. 构建影响关系 (SimInfluenceNode)
|
|||
|
|
public List<SimInfluenceNode> 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<SimUnit> units,
|
|||
|
|
List<SimEvent> events,
|
|||
|
|
List<SimInfluenceNode> 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<Map<String, Object>> run(@RequestBody SimulationRequest req) {
|
|||
|
|
// 1. Load Data
|
|||
|
|
SimDataPackage data = simDataFacade.loadSimulationData(req.getProjectId(), req.getScenarioId());
|
|||
|
|
|
|||
|
|
// 2. Build Model
|
|||
|
|
List<SimUnit> units = simBuilder.buildUnits(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, 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<String, Object> state` 的逻辑,改为构建 `SimUnit` 或 `SimInfluenceNode` 对象。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. 兼容性与迁移
|
|||
|
|
|
|||
|
|
* **API 兼容**:新接口 `/sim/run` 的返回结构应尽量与原 `/projects/simulation/run` 中的 `frames` 结构保持一致,以便前端无缝切换。
|
|||
|
|
* **分步上线**:
|
|||
|
|
1. 先上线 `/sim/run` 供测试使用。
|
|||
|
|
2. 验证无误后,将前端调用切到新接口。
|
|||
|
|
3. 废弃 `ProjectController` 中的相关接口。
|