JavaProjectRepo/business-css/SimController 及相关接口实现建议v4-2.md
2026-03-19 11:18:15 +08:00

13 KiB
Raw Blame History

SimController 及相关接口实现建议 v4-2结合当前工程现状

本文档基于 情景模拟分析结果v3.md 第 3 条,并结合当前可运行代码的真实调用链,给出可落地的实现建议。


0. 现状盘点(必须先对齐)

0.1 当前“线上可用”的仿真接口在哪里

目前仿真入口实际在 ProjectController,而不是 SimController

0.2 SimController 为什么不可用

SimController.java 目前是“原型草稿”,在本仓库中无法落地,原因是:

  • 引用的 ProjectRepository/EventRepository/InfluenceRepository/InferenceConverter 仅在该文件出现,仓库中不存在真实实现。
  • SimBuilder.buildUnits/buildEvents/buildInfluenceNodes 也不存在(SimBuilder.java 只有注释草稿)。
  • SimService 当前逻辑是 KV 级简化引擎,并且事件优先级与现有 initSimulation 的行为不一致(SimService.java)。

0.3 当前系统的“真实数据格式”

runSimulation 解析的是一种“frames → devices”结构并按 deviceType 分组:

因此,“让 SimController 可用”的关键,不是从 0 造一套新格式,而是:复用现有 frames 格式(或在 SimController 内把 SimContext 转为该格式),以便直接复用 DeviceDataParser + DeviceInferService 的整条推理/落库链路。


1. 建议的目标形态SimController 成为“仿真编排入口”

建议把 SimController 从“原型”升级为生产入口,但要遵循当前工程已存在的服务边界:

1.1 目标职责划分(贴合当前代码)

  • SimController:只处理 HTTP + 参数校验 + 返回值结构统一(保持与 ProjectController 相同的 {code,msg,data} 或复用统一响应体)。
  • SimulationFacade新增:负责把“现有 services + topology JSON + event 表”组装成可计算输入;并在需要时调用推理、落库、更新情景状态。
  • SimulationEngine新增或重构现有 SimService:只做“时序帧生成/影响计算/事件注入”,纯内存计算,不碰 DB。
  • Converter新增:把 Engine 输出转成 DeviceDataParser 能吃的 frames JSON或直接输出 Map 结构)。

这样做的好处:既保留 SimController 分层方向,又不引入仓库里不存在的 Repository/Converter 类型。


2. 具体落地建议(按当前文件/类名对齐)

2.1 先做最小可用:让 /sim 接口复用现有 ProjectServiceImpl 的能力

为了快速验证链路,第一阶段建议不要立刻重写引擎,而是“搬运+封装”:

  1. 新增 SimulationFacade(建议放在 com.yfd.business.css.service.sim 包)
    内部依赖现有 serviceProjectService/ScenarioService/EventService/DeviceInferService(以及 MaterialService 若需要 DB 补全静态物料属性)。
    它暴露两类能力:
  1. SimController 直接调用 facade并提供与 ProjectController 一致的返回结构
    这样能做到:新增 /sim/* 不影响现有 /projects/simulation/*,并且复用现有稳定链路。

这一阶段的目标是:先让 SimController 可用、可回归测试、可逐步迁移

2.2 第二阶段:把 initSimulation 拆成“解析/计算/输出”三个可替换模块

当前 initSimulation 里混杂了:拓扑解析、设备顺序、静态注入、影响计算、事件解析、帧输出。建议拆成 3 个模块,便于未来替换而不改 API

A. Topology & Static 解析模块建议TopologyParser

直接复用现有实现(迁移或抽取):

建议输出一个结构化的 DTO例如

  • List<Device> orderedDevices
  • Map<deviceId, deviceType>
  • Map<deviceId, materialId>
  • devStatic/devInfluence/matStatic/matInfluence/matStaticDb

B. Event 解析模块建议EventScheduleBuilder

直接复用现有 attr_changes 解析逻辑:

补强建议(贴合现状的缺口):

  • timePoints 为空时不要直接失败:允许用 params 提供 start/end/interval 生成时间网格(否则“无事件仿真”无法跑)。
  • ramp 事件目前仅把起止时刻作为输出点;若前端想看平滑曲线,需要补齐采样点(例如每 1s

C. Frame 生成模块建议FrameGenerator

复用 initSimulation 的生成循环,但把“覆盖策略”参数化:

  • 现在的覆盖策略是:Static → Influence → overrideWithEvents最终覆盖
    代码:overrideWithEvents
  • 建议支持两类事件Input 与 Override可先用 params 开关模拟)
    • Input计算前注入
    • Override计算后强制覆盖

这样第三阶段才需要动到“事件类型”定义;第二阶段只要把钩子留好即可。


3. SimService/SimModel 如何与现有链路对接(不要重新造轮子)

3.1 现有 SimModel 的适配建议(只做必要改造)

当前 SimModel 过于抽象(纯 KV无法表达“设备/物料静态属性注入、deviceType、material 绑定”等现有业务关键点:

  • SimUnit 只有 unitId/deviceId/materialId/deviceTypeSimUnit.java
  • SimContext 只有 Map<SimPropertyKey, Double> 的 currentValuesSimContext.java

建议的最小增强(为了能生成当前 runSimulation 可消费的 frames

  • SimUnit 增加 Map<String, Double> staticProps(至少承载 diameter/height 与物料关键属性)。
  • SimEvent 增加 boolean override 或扩展 EventType区分 Input/Override
  • SimInfluenceSource.delay 目前未在 SimService 使用;若要支持 delay必须用 ctx.timeline 回看历史值(当前 SimContext 已能保留 step 的快照)。

3.2 Converter把 SimContext 输出转换成现有 frames 格式

建议增加一个 Converter替代原型里的 InferenceConverter),输出结构与 DeviceDataParser 一致:

{
  \"data\": {
    \"frames\": [
      {
        \"step\": 0,
        \"time\": 0,
        \"devices\": {
          \"dev-001\": {\"deviceType\": \"CylindricalTank\", \"diameter\": 20, \"height\": 20, \"u_concentration\": 20}
        }
      }
    ]
  }
}

理由:这样 SimController 可以直接调用 DeviceDataParser.parseAndGroupDeviceData + DeviceInferService.processDeviceInference,与现有落库路径完全一致。


4. SimController 的接口形态建议(与现有系统兼容)

建议同时支持“两段式”和“一段式”,避免推倒重来:

4.1 两段式(兼容现有前端/流程)

  • POST /sim/init:返回 frames/projects/simulation/init 对齐)
  • POST /sim/run:接收 frames调用推理并落库/projects/simulation/run 对齐)

4.2 一段式(面向后端批处理/自动化)

  • POST /sim/run-all:内部调用 init 生成 frames再立即 run 推理落库,返回摘要(如 snapshots、结果条数、耗时

5. 迁移与风险控制(建议强制执行)

  1. 先引入新接口,不删旧接口:让 /sim/*/projects/simulation/* 并行一段时间。\n\n2) 帧格式不变:任何新实现必须输出 DeviceDataParser 可解析的结构,否则推理链路与前端都要一起改,风险最大。\n\n3) 行为一致性测试:对比新旧 init 输出(同 projectId/scenarioId是否一致对比 run 后写入 scenario_result 条数与 key 字段是否一致。\n\n4) 清理 System.out.printlnProjectServiceImpl 的 init/run 里有大量 System.out.println(例如 ProjectServiceImpl.java:L644-L661runSimulation debug),建议迁移到统一日志体系后再移除,以免污染生产日志与性能。\n\n---\n\n## 6. 最小实现清单(按优先级排序)\n\n- 建议 1SimController 先“可用”——删除/替换不存在的 Repository/Converter 依赖,改用现有 Service。\n- 建议 2新增 SimulationFacade,把现有 ProjectServiceImpl.initSimulation/runSimulation 先封装起来。\n- 建议 3逐步抽取 TopologyParser/EventScheduleBuilder/FrameGenerator,把 ProjectServiceImpl 中的计算逻辑迁移出来。\n- 建议 4最后再考虑把增强后的 SimService/SimModel 正式替换成唯一引擎实现。\n+