delay_jitter_ms for natural timing variation, delay_over_prob/delay_over_ms for simulating occasional valve timeout faults (e.g. once per day).
11 KiB
11 KiB
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 运行环境固定路径)
{
"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/ 源文件
关键接口
// 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)只需要:
- 新建
model/ExponentialModel.h,实现IModel::evaluate(t) - 在
ModelRegistry构造函数中注册一行:reg("exponential", ExponentialModel::create) - 在
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,不持久化