JavaProjectRepo/business-css/情景模拟分析结果v3.md

78 lines
7.1 KiB
Markdown
Raw Permalink Normal View History

2026-03-19 11:18:15 +08:00
# 情景模拟分析结果 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/*` 作为唯一入口。