# 情景模拟分析结果 v3(针对“几个疑问”) 对应问题来源:[几个疑问.md](file:///e:/projectJava/JavaProjectRepo/business-css/%E5%87%A0%E4%B8%AA%E7%96%91%E9%97%AE.md) ## 1. 初始化顺序与优先级:静态值、事件值、影响计算如何排布? ### 1.1 三类数据的“角色定义” - **静态值(设备表/物料表/拓扑 static)**:系统的“默认底色/初值基线”。没有事件、没有影响计算时,也必须能给出一个自洽的初始状态。 - **事件值(始发事件/attr_changes)**:系统的“外部输入”。既可能是一次性的设定(某一步将某属性改成某值),也可能代表持续的干预(在一段时间内强制维持某值)。 - **影响计算(influence 公式)**:系统的“内部传播/派生规则”。它根据来源属性计算得到目标属性,是一个“生成值”的过程。 ### 1.2 当前代码实现的顺序(实际行为) 在 `ProjectServiceImpl.initSimulation` 里,单帧生成顺序是: 1) 注入尺寸(Device.size) 2) 写入设备静态值 3) 执行设备 influence 计算并写入 state 4) 写入物料静态值(DB + topology) 5) 执行物料 influence 计算并写入 state 6) **最后对 device/material 应用事件覆盖(overrideWithEvents)** 这相当于:**Static → Influence → Event(最终覆盖)**。代码位置见:[ProjectServiceImpl.initSimulation](file:///e:/projectJava/JavaProjectRepo/business-css/src/main/java/com/yfd/business/css/service/impl/ProjectServiceImpl.java#L614-L767) 与 [overrideWithEvents/readValue](file:///e:/projectJava/JavaProjectRepo/business-css/src/main/java/com/yfd/business/css/service/impl/ProjectServiceImpl.java#L998-L1114)。 另外,`readValue()` 在计算 influence 时会“优先读事件、再读静态”,但最终仍会被 `overrideWithEvents()` 再覆盖一次,这意味着事件在该实现里拥有“最终裁决权”。见:[readValue](file:///e:/projectJava/JavaProjectRepo/business-css/src/main/java/com/yfd/business/css/service/impl/ProjectServiceImpl.java#L998-L1035)。 ### 1.3 推荐的合理顺序(建议落地的“标准管线”) 建议将初始化/每步演进统一成一个明确的“管线”,并对事件类型做区分: **推荐管线:** 1) **Static 基线**:加载设备/物料静态值(含 DB 补全)作为默认状态。 2) **Event(输入/初始条件)**:应用在当前时刻生效的事件(尤其是 t=0 或 step=1 的“初始条件事件”)。 3) **Influence(派生计算)**:基于当前状态执行影响关系计算,得到派生值。 4) **Event(强制覆盖/锁定)**:仅对“强制锁定类事件”再次覆盖(可选,但强烈建议引入),确保“外部强制干预”不会被公式计算反覆盖。 这样做的好处: - 既能让事件作为“输入”影响传播(步骤 2 发生在计算前),又能让“强制事件”在输出阶段生效(步骤 4)。 - 能解释并覆盖两类业务直觉: - “我设了初始浓度,后续按公式变化”(步骤 2 + 3) - “我强制把某槽位液位锁死,不管公式怎么算”(步骤 4) ### 1.4 关键补充:时间轴/采样点也属于优先级的一部分 当前 `initSimulation` 的帧生成时间点来自事件时间集合:`collectTimePoints(valueProviders)`,若没有事件时间点会直接返回“无法生成帧”。见:[collectTimePoints](file:///e:/projectJava/JavaProjectRepo/business-css/src/main/java/com/yfd/business/css/service/impl/ProjectServiceImpl.java#L1037-L1061) 与 [initSimulation timePoints 为空处理](file:///e:/projectJava/JavaProjectRepo/business-css/src/main/java/com/yfd/business/css/service/impl/ProjectServiceImpl.java#L667-L675)。 建议: - 当没有事件时,允许通过参数指定 `start/end/stepInterval`,生成基础时间网格(否则“纯静态模拟”永远跑不起来)。 - 对于 ramp/linear 事件,当前只把 start/end 加入时间点(中间不采样),但 `readValue` 支持任意 t 的线性插值。若希望输出更“连续”的曲线,必须补齐采样点(例如每 1s/每步都采样)。 ## 2. 合理性分析:为什么推荐上述顺序? ### 2.1 从“因果关系”角度 - 静态值是**先验事实**(设备尺寸、物料基础参数),应先进入状态。 - 事件是**外部驱动/输入**,会改变系统边界条件,应在影响传播前进入状态,否则传播用的仍是旧值。 - influence 是**系统内部响应**,应在输入就绪后计算。 - 强制覆盖事件是**高优先级干预**,必须在计算后仍能维持,否则“强制”二字失去意义。 ### 2.2 从“可维护性/可解释性”角度 把事件分成两类最关键: - **Event-Input(输入型)**:参与计算的输入(初值设定、控制变量变化)。 - **Event-Override(强制型)**:输出阶段的锁定/强制(故障注入、人工接管)。 如果不区分,只能在“事件最高”与“公式最高”之间二选一,最终会在不同业务场景下反复打补丁。 ## 3. SimController 及相关接口分析(并给出建议) ### 3.1 现状:SimController 是“半成品/原型”,当前不可用 [SimController.java](file:///e:/projectJava/JavaProjectRepo/business-css/src/main/java/com/yfd/business/css/controller/SimController.java#L1-L43) 体现了一套更清晰的分层意图:`Controller -> Repo 装配 -> Builder -> SimService -> 推理输入`,但存在几处硬性问题: - 依赖的 `ProjectRepository / EventRepository / InfluenceRepository / InferenceConverter` 在项目代码中找不到定义,无法编译/运行(当前仓库中仅此文件引用这些类型)。 - `SimBuilder` 中对应的 `buildUnits/buildEvents/buildInfluenceNodes` 方法是注释掉的,占位未实现。见:[SimBuilder.java](file:///e:/projectJava/JavaProjectRepo/business-css/src/main/java/com/yfd/business/css/build/SimBuilder.java#L1-L44)。 - `SimService` 当前实现仅是 KV 级别的简化引擎,且采用 **事件先写、后计算覆盖** 的顺序(会导致“事件设定失效”)。见:[SimService.runSimulation](file:///e:/projectJava/JavaProjectRepo/business-css/src/main/java/com/yfd/business/css/service/SimService.java#L9-L51)。 ### 3.2 与当前线上接口的关系 当前“初始化/运行模拟”入口实际在 `ProjectController`(/simulation/init、/simulation/run)并调用 `ProjectServiceImpl`。例如 `runSimulation`:见 [ProjectController.java:L289-L300](file:///e:/projectJava/JavaProjectRepo/business-css/src/main/java/com/yfd/business/css/controller/ProjectController.java#L289-L300)。 ### 3.3 建议:保留 SimController 的分层方向,但必须补齐三件事 1) **补齐依赖与 Builder**:明确 Repo 层要读什么(拓扑、事件、影响关系)并落地成可编译的类/接口;实现 `SimBuilder` 的构建方法或直接复用 `ProjectServiceImpl` 的解析器。 2) **统一优先级管线**:让 `SimService` 采用“Static → Event(Input) → Influence → Event(Override)”(见第 1 章建议),避免出现“事件被公式反覆盖”。 3) **统一对外 API**:建议最终以 `/sim/*` 作为唯一入口。