eis/docs/superpowers/specs/2026-05-13-rng-parameterized-models-design.md
Huamonarch 302aa82e38 docs: add valve_pair jitter/over-delay fault simulation params
delay_jitter_ms for natural timing variation, delay_over_prob/delay_over_ms
for simulating occasional valve timeout faults (e.g. once per day).
2026-05-13 14:47:35 +08:00

262 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# RNG 参数化随机数生成 — 设计规格
## 概述
将 RNG 从硬编码的信号→数据源映射重构为配置驱动的参数化模型架构,使随机数/仿真数据的生成完全由外部配置控制,无需修改 C++ 代码即可定义新的信号行为。
## 核心设计决策
- **模型模板与信号绑定分离**:模型模板定义在 `rng_models.json`,每个信号通过 DB2 表 `T_LOV_FDAAITEM``tables[1]` 字段引用模型名
- **每个信号独立实例化**:即使 100 个信号用同一个模型模板,各自创建独立实例,状态不共享
- **基值参数取自 defaultValue**`TeleItem.defaultValue` 天然作为 `c`(constant)、`b`(linear)、`offset`(sine)、`base`(spike/drift)、`mean`(normal) 的默认值
- **纯 JSON 参数透传**:模型构造函数直接解析 JSON params key-value工厂不关心具体参数内容
## 模型模式
### 模拟量模式
| mode | 函数 | 从 defaultValue 取的基值 | 模板 params |
|------|------|------------------------|------------|
| `constant` | y = c | c | — |
| `linear` | y = k·t + b | b | k |
| `sine` | y = A·sin(ω·t + φ) + offset | offset | A, ω, φ |
| `normal` | y = μ + σ·N(0,1) | μ | σ |
| `uniform` | y = random(−Δ, +Δ) + center | center | Δ |
| `spike` | y = base + spike(t) | base | amplitude, probability |
| `drift` | y = base + rate·t | base | drift_rate |
| `csv` | y = csv(t, col) | — | file, column |
| `composite` | y = base(t) + noise(t) | — | base_model, noise_model |
t 为时间索引,从进程启动起每周期(~20ms递增。
### 布尔量模式
| mode | 行为 | 模板 params |
|------|------|------------|
| `bool_random` | 随机 0/1可配置为 1 的概率 | prob_true (default 0.5) |
| `bool_toggle` | 固定周期 0/1 翻转 | period_ms |
| `bool_csv` | CSV 数据回放int 列) | file, column |
| `valve_pair` | 跟随另一个信号,加延迟/闪断/抖动/超时 | on_delay_ms, off_delay_ms, delay_jitter_ms, flash_prob, delay_over_prob, delay_over_ms |
### spike 毛刺行为
- 每个周期以 `probability` 概率触发毛刺
- 触发时 y = base ± amplitude符号随机
- 毛刺持续 1 个周期后恢复
### drift 漂移行为
- y = base + drift_rate × t
- 漂移累加无上限,模拟传感器退化趋势
### valve_pair 时序模型
不模拟动作信号本身,而是通过模型名引用另一个信号(如 `bool_toggle`)的当前值,模拟传感器对动作的响应延迟。
参数说明:
| 参数 | 含义 | 默认值 |
|------|------|--------|
| `on_delay_ms` | 阀开到位延迟ms信号 0→1 后传感器多久变 1 | 必填 |
| `off_delay_ms` | 阀关到位延迟ms信号 1→0 后传感器多久变 0 | 必填 |
| `delay_jitter_ms` | 延迟抖动范围,实际延迟 = delay ± random(0, jitter) | 0 |
| `flash_prob` | 高电平期间每周期出现闪断的概率 | 0 |
| `delay_over_prob` | 每次阀动作时触发超时的概率on/off 独立判断) | 0 |
| `delay_over_ms` | 超时时的延迟值,替代正常延迟 | 0 |
正常时序(无超时,有抖动):
```
动作信号 A:
────────────┐ ┌────────────────
│ │
├── on_delay ──→ ├── off_delay ──→
传感器 B: │ ±jitter │ ±jitter
────────────┘ └────────────────
└─ 阀芯移动 ──→ └─ 阀芯复位 ──→
```
超时场景delay_over_prob 触发时):
```
动作信号 A:
────────────┐
├──────── on_delay_over (4000ms) ────────→ 正常 off_delay
传感器 B: │ ┌────── ...
────────────┘ │
└── 超时! 远超正常范围
```
- 每次 A 跳变时0→1 或 1→0`delay_over_prob` 概率独立判断是否超时
- 超时时延迟 = `delay_over_ms`,未超时时延迟 = `delay_ms` ± random(0, `delay_jitter_ms`)
- 闪断:高电平期间以 `flash_prob` 概率单周期归零后自动恢复
- `delay_over_prob = 0` = 永不超时;`delay_jitter_ms = 0` = 无抖动
- on 和 off 方向各自由各自概率独立触发超时
配对方式B 的 `tables[1]` = `valve_px_std:toggle_2s`,其中 `toggle_2s` 是 A 的模型名。运行时通过模型名查找动作信号的 IModel 实例。
一对多自然支持:同一个动作信号可被多个 valve_pair 传感器跟随,各自维护独立的延迟/闪断状态。
## JSON 配置格式
文件路径:`/users/dsc/code/TestProject/RNG/json/rng_models.json`Linux 运行环境固定路径)
```json
{
"models": {
"normal_tiny": { "mode": "normal", "params": { "sigma": 0.01 } },
"normal_med": { "mode": "normal", "params": { "sigma": 0.5 } },
"linear_slow": { "mode": "linear", "params": { "k": 0.001 } },
"linear_fast": { "mode": "linear", "params": { "k": 0.05 } },
"sine_ecc1": { "mode": "sine", "params": { "A": 5.0, "omega": 0.314, "phi": 0 } },
"sine_ecc2": { "mode": "sine", "params": { "A": 3.0, "omega": 0.628, "phi": 1.57 } },
"spike_sharp": { "mode": "spike", "params": { "amplitude": 50, "probability": 0.05 } },
"spike_mild": { "mode": "spike", "params": { "amplitude": 10, "probability": 0.15 } },
"drift_slow": { "mode": "drift", "params": { "drift_rate": 0.0001 } },
"drift_fast": { "mode": "drift", "params": { "drift_rate": 0.005 } },
"toggle_2s": { "mode": "bool_toggle", "params": { "period_ms": 2000 } },
"toggle_5s": { "mode": "bool_toggle", "params": { "period_ms": 5000 } },
"toggle_10s": { "mode": "bool_toggle", "params": { "period_ms": 10000 } },
"valve_px_std": { "mode": "valve_pair", "params": { "on_delay_ms": 200, "off_delay_ms": 150, "delay_jitter_ms": 20, "flash_prob": 0.02, "delay_over_prob": 0.0001, "delay_over_ms": 4000 } },
"valve_px_fast": { "mode": "valve_pair", "params": { "on_delay_ms": 80, "off_delay_ms": 60, "delay_jitter_ms": 10, "flash_prob": 0.01, "delay_over_prob": 0, "delay_over_ms": 0 } }
}
}
```
## DB2 信号绑定
每个信号在 `T_LOV_FDAAITEM` 表中通过 `tables[1]` 字段指定模型引用。
### tables[1] 语法
| 格式 | 含义 | 示例 |
|------|------|------|
| `model_name` | JSON 中定义的模型 | `normal_tiny` |
| `csv:file_name:col` | CSV 文件回放(内联简写) | `csv:C308速度毛刺仿真.csv:1` |
| `base_model+noise_model` | 组合模型 | `linear_slow+normal_tiny` |
| `pair_model:action_model` | 布尔配对模型(仅 valve_pair | `valve_px_std:toggle_2s` |
| 空 / `default` | 默认模型 `normal_tiny` | — |
### 示例数据行
| item | defaultValue | tables[1] | 说明 |
|------|-------------|-----------|------|
| BR1_1_V_act | 100.0 | `normal_tiny` | 均值100的正态噪声 |
| BR1_2_V_act | 120.0 | `normal_tiny` | 同上模板,独立实例 |
| BR2_2_I_act | 0.0 | `csv:spbdata:1` | CSV第1列回放 |
| TCM-4-82-BD-1-101 | 80.0 | `sine_ecc1` | 幅值5周期0.314的正弦 |
| TCM-72-150-BD-5-117 | 75.0 | `sine_ecc1` | 同上模板,独立实例 |
| RW_o_Drv_SpdErr | 0.0 | `linear_slow+spike_mild` | 线性爬升叠加毛刺 |
| 3G-DSA-B01-01B | 0 | `toggle_2s` | 2秒周期翻转动作阀 |
| 3G-PX-B01-01 | 0 | `valve_px_std:toggle_2s` | 跟随动作阀的到位传感器 |
## 软件架构
### 文件结构
```
TestProject/RNG/
├── model/
│ ├── IModel.h # 统一接口
│ ├── ModelRegistry.h/cc # 工厂 + tables[1] 解析
│ ├── NormalModel.h
│ ├── LinearModel.h
│ ├── SineModel.h
│ ├── UniformModel.h
│ ├── SpikeModel.h
│ ├── DriftModel.h
│ ├── CsvReplayModel.h
│ ├── BoolRandomModel.h
│ ├── BoolToggleModel.h
│ ├── BoolCsvModel.h
│ ├── ValvePairModel.h
│ └── CompositeModel.h
├── json/ # nlohmann_json (已有)
├── Generator.h/cc # 重构:去掉硬编码,走 ModelRegistry
├── RNG_icei.h/cc # 不变
├── RNG.h/cc # 启动时加载 rng_models.json
├── RandT.h # 保留,底层数学工具
├── read_csv.hpp # 保留CsvReplayModel 内部使用
└── CMakeLists.txt # 新增 model/ 源文件
```
### 关键接口
```cpp
// IModel.h — 所有模型的基类
class IModel {
public:
virtual ~IModel() = default;
virtual float evaluate(size_t t_index); // 模拟量默认返回0
virtual bool evaluateBool(size_t t_index); // 布尔量默认返回false
virtual void linkPeers(ModelRegistry& reg); // 配对信号关联,默认空
virtual void reset(); // 重置内部状态
};
// ModelRegistry — 单例
class ModelRegistry {
public:
using Ctor = std::function<std::unique_ptr<IModel>(const json& params, float defaultVal)>;
void loadModels(const std::string& jsonPath); // 加载 rng_models.json
IModel* getOrCreate(const std::string& tables1Spec, // 解析并创建实例
float defaultValue);
// 按模型名查找所有使用该模型的信号实例valve_pair 查询动作信号用)
std::vector<IModel*> findByModelName(const std::string& modelName);
void registerMode(const std::string& mode, Ctor ctor); // 注册新模型类型
};
```
### 数据流(每周期 TimeNotify
```
TimeNotify(eventNo)
→ BinaryTele.ReBuild(eventNo)
→ for i in 0..BinaryTele.size():
item = BinaryTele[i]
spec = item.tables[1]
model = ModelRegistry::getOrCreate(spec, atof(item.defaultValue))
if item.type[0] == 'b':
item = model->evaluateBool(timeIndex)
else:
item = model->evaluate(timeIndex) (float)
→ BinaryTele.GetTeleData() → m_mapfix[eventNo]->push(buff)
```
### 启动流程
```
RNG::start()
→ con_mag_->dbLogin()
→ ModelRegistry::instance().loadModels("/users/dsc/code/TestProject/RNG/json/rng_models.json")
→ RNG_server = new RNGICEI()
→ PACE 服务注册
```
## 扩展流程
新增一个模型模式(如指数衰减 `exponential`)只需要:
1. 新建 `model/ExponentialModel.h`,实现 `IModel::evaluate(t)`
2.`ModelRegistry` 构造函数中注册一行:`reg("exponential", ExponentialModel::create)`
3.`rng_models.json` 中添加模板:`"exp_decay": { "mode": "exponential", "params": { "lambda": 0.01 } }`
JSON params 以通用 map 形式透传给模型构造函数,工厂不需要理解新参数。
## 存量兼容
- `RandT.h` 保留,底层随机数工具被各 Model 内部使用
- `read_csv.hpp` 保留,`CsvReplayModel` 内部使用
- `Generator.cc` 中所有硬编码的 if-else 信号映射删除
- `BinaryTele` + `CMemFix<PLC_DATA>` 共享内存写入逻辑保持不变
- ICE 接口 `RNGICEI` 不变
- DB2 中已有的 `tables[1]` 为空的行走默认模型 `normal_tiny`,保持向后兼容
## 约束
- `tables[1]` 字段长度 40 字符(`name_type` = `char[40]`),模型名 + 引用语法需控制在此范围内
- 时间索引 `t_index` 从进程启动起单调递增,每周期(~20ms加 1不持久化