diff --git a/docs/superpowers/plans/2026-05-13-rng-parameterized-models-plan.md b/docs/superpowers/plans/2026-05-13-rng-parameterized-models-plan.md new file mode 100644 index 0000000..1876f35 --- /dev/null +++ b/docs/superpowers/plans/2026-05-13-rng-parameterized-models-plan.md @@ -0,0 +1,956 @@ +# RNG 参数化随机数生成 — 实现计划 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 将 RNG 从硬编码信号映射重构为 JSON 配置驱动的参数化模型架构,支持 9 种模拟量模式 + 4 种布尔量模式。 + +**Architecture:** 每个模型实现 `IModel` 接口,`ModelRegistry` 负责加载 `rng_models.json` 并根据 `tables[1]` 创建实例。`Generator::wtite_in_shm` 中去掉所有硬编码 if-else,改为遍历 signals 调用 `model->evaluate()`。 + +**Tech Stack:** C++20, nlohmann_json (vcpkg), 现有 RandT.h / read_csv.hpp + +**Build note:** 该项目在 Linux (CentOS 7, GCC 10/devtoolset-10) 编译,当前 Windows 环境仅用于代码编辑。 + +--- + +### Task 1: 创建 JSON 配置文件 + +**Files:** +- Create: `TestProject/RNG/json/rng_models.json` + +- [ ] **Step 1: 创建 rng_models.json** + +```json +{ + "models": { + "constant_zero": { "mode": "constant", "params": {} }, + "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 } } + } +} +``` + +- [ ] **Step 2: Commit** + +```bash +git add TestProject/RNG/json/rng_models.json +git commit -m "feat: add rng_models.json with model templates" +``` + +--- + +### Task 2: 创建 IModel 基类接口 + +**Files:** +- Create: `TestProject/RNG/model/IModel.h` + +- [ ] **Step 1: 编写 IModel.h** + +```cpp +#pragma once +#include + +class ModelRegistry; + +class IModel { +public: + virtual ~IModel() = default; + + virtual float evaluate(size_t t_index) { return 0.0f; } + virtual bool evaluateBool(size_t t_index) { return false; } + virtual void linkPeers(ModelRegistry& reg) {} + virtual void reset() {} +}; +``` + +- [ ] **Step 2: Commit** + +```bash +git add TestProject/RNG/model/IModel.h +git commit -m "feat: add IModel base interface" +``` + +--- + +### Task 3: 实现模拟量模型(Constant, Normal, Linear, Sine, Uniform, Spike, Drift) + +**Files:** +- Create: `TestProject/RNG/model/ConstantModel.h` +- Create: `TestProject/RNG/model/NormalModel.h` +- Create: `TestProject/RNG/model/LinearModel.h` +- Create: `TestProject/RNG/model/SineModel.h` +- Create: `TestProject/RNG/model/UniformModel.h` +- Create: `TestProject/RNG/model/SpikeModel.h` +- Create: `TestProject/RNG/model/DriftModel.h` + +- [ ] **Step 1: 编写 ConstantModel.h 和 NormalModel.h** + +```cpp +// ConstantModel.h +#pragma once +#include +#include +using json = nlohmann::json; + +struct ConstantModel : IModel { + float c; + ConstantModel(const json& params, float defaultVal) : c(defaultVal) {} + float evaluate(size_t) override { return c; } +}; +``` + +```cpp +// NormalModel.h +#pragma once +#include +#include +#include +using json = nlohmann::json; + +struct NormalModel : IModel { + float mean, sigma; + NormalModel(const json& params, float defaultVal) + : mean(defaultVal), sigma(params.value("sigma", 0.01f)) {} + float evaluate(size_t) override { return RandT::GuassRand(mean, sigma); } +}; +``` + +- [ ] **Step 2: 编写 LinearModel.h** + +```cpp +#pragma once +#include +#include +using json = nlohmann::json; + +struct LinearModel : IModel { + float k, b; + LinearModel(const json& params, float defaultVal) + : k(params.value("k", 0.0f)), b(defaultVal) {} + float evaluate(size_t t) override { return k * t + b; } +}; +``` + +- [ ] **Step 3: 编写 SineModel.h** + +```cpp +#pragma once +#include +#include +#include +using json = nlohmann::json; + +struct SineModel : IModel { + float A, omega, phi, offset; + SineModel(const json& params, float defaultVal) + : A(params.value("A", 1.0f)), omega(params.value("omega", 1.0f)), + phi(params.value("phi", 0.0f)), offset(defaultVal) {} + float evaluate(size_t t) override { return A * sinf(omega * t + phi) + offset; } +}; +``` + +- [ ] **Step 4: 编写 UniformModel.h** + +```cpp +#pragma once +#include +#include +#include +using json = nlohmann::json; + +struct UniformModel : IModel { + float center, delta; + UniformModel(const json& params, float defaultVal) + : center(defaultVal), delta(params.value("delta", 0.01f)) {} + float evaluate(size_t) override { return RandT::RandT(center - delta, center + delta); } +}; +``` + +- [ ] **Step 5: 编写 SpikeModel.h** + +```cpp +#pragma once +#include +#include +#include +using json = nlohmann::json; + +struct SpikeModel : IModel { + float base, amplitude, probability; + SpikeModel(const json& params, float defaultVal) + : base(defaultVal) + , amplitude(params.value("amplitude", 1.0f)) + , probability(params.value("probability", 0.01f)) {} + float evaluate(size_t) override { + if ((double)rand() / RAND_MAX < probability) { + return base + ((rand() % 2) ? amplitude : -amplitude); + } + return base; + } +}; +``` + +- [ ] **Step 6: 编写 DriftModel.h** + +```cpp +#pragma once +#include +#include +using json = nlohmann::json; + +struct DriftModel : IModel { + float base, drift_rate; + DriftModel(const json& params, float defaultVal) + : base(defaultVal), drift_rate(params.value("drift_rate", 0.0f)) {} + float evaluate(size_t t) override { return base + drift_rate * t; } +}; +``` + +- [ ] **Step 7: Commit** + +```bash +git add TestProject/RNG/model/NormalModel.h TestProject/RNG/model/LinearModel.h \ + TestProject/RNG/model/SineModel.h TestProject/RNG/model/UniformModel.h \ + TestProject/RNG/model/SpikeModel.h TestProject/RNG/model/DriftModel.h +git commit -m "feat: add 7 analog signal models (constant, normal, linear, sine, uniform, spike, drift)" +``` + +--- + +### Task 4: 实现 CsvReplayModel + +**Files:** +- Create: `TestProject/RNG/model/CsvReplayModel.h` + +- [ ] **Step 1: 编写 CsvReplayModel.h** + +```cpp +#pragma once +#include +#include +#include +#include +using json = nlohmann::json; + +struct CsvReplayModel : IModel { + ReadCSV::DoubleData data; + int column; + + CsvReplayModel(const json& params, float) + : data(params["file"].get()) + , column(params["column"].get()) {} + + float evaluate(size_t t) override { return data(t, column); } +}; +``` + +- [ ] **Step 2: Commit** + +```bash +git add TestProject/RNG/model/CsvReplayModel.h +git commit -m "feat: add CsvReplayModel for CSV data replay" +``` + +--- + +### Task 5: 实现布尔量模型(BoolRandom, BoolToggle, BoolCsv) + +**Files:** +- Create: `TestProject/RNG/model/BoolRandomModel.h` +- Create: `TestProject/RNG/model/BoolToggleModel.h` +- Create: `TestProject/RNG/model/BoolCsvModel.h` + +- [ ] **Step 1: 编写 BoolRandomModel.h** + +```cpp +#pragma once +#include +#include +#include +using json = nlohmann::json; + +struct BoolRandomModel : IModel { + float prob_true; + BoolRandomModel(const json& params, float defaultVal) + : prob_true(params.value("prob_true", 0.5f)) {} + bool evaluateBool(size_t) override { + return (double)rand() / RAND_MAX < prob_true; + } +}; +``` + +- [ ] **Step 2: 编写 BoolToggleModel.h** + +```cpp +#pragma once +#include +#include +using json = nlohmann::json; + +struct BoolToggleModel : IModel { + int period_ticks; // ~20ms per tick + BoolToggleModel(const json& params, float) + : period_ticks(params.value("period_ms", 2000) / 20) {} + bool evaluateBool(size_t t) override { + return (t / period_ticks) % 2 == 0; + } +}; +``` + +- [ ] **Step 3: 编写 BoolCsvModel.h** + +```cpp +#pragma once +#include +#include +#include +#include +using json = nlohmann::json; + +struct BoolCsvModel : IModel { + ReadCSV::IntData data; + int column; + + BoolCsvModel(const json& params, float) + : data(params["file"].get()) + , column(params["column"].get()) {} + + bool evaluateBool(size_t t) override { return (bool)data(t, column); } +}; +``` + +- [ ] **Step 4: Commit** + +```bash +git add TestProject/RNG/model/BoolRandomModel.h TestProject/RNG/model/BoolToggleModel.h \ + TestProject/RNG/model/BoolCsvModel.h +git commit -m "feat: add 3 boolean signal models" +``` + +--- + +### Task 6: 实现 ValvePairModel + +**Files:** +- Create: `TestProject/RNG/model/ValvePairModel.h` + +- [ ] **Step 1: 编写 ValvePairModel.h** + +```cpp +#pragma once +#include +#include +#include +#include +using json = nlohmann::json; + +struct ValvePairModel : IModel { + IModel* action = nullptr; + std::string actionModelName; + + int on_delay_ticks; + int off_delay_ticks; + int delay_jitter_ticks; + float flash_prob; + float delay_over_prob; + int delay_over_ticks; + + enum State { IDLE_LOW, WAITING_RISE, HIGH, WAITING_FALL }; + State state = IDLE_LOW; + int delay_counter = 0; + bool prev_action = false; + + ValvePairModel(const json& params, float) + : on_delay_ticks(params.value("on_delay_ms", 200) / 20) + , off_delay_ticks(params.value("off_delay_ms", 150) / 20) + , delay_jitter_ticks(params.value("delay_jitter_ms", 0) / 20) + , flash_prob(params.value("flash_prob", 0.0f)) + , delay_over_prob(params.value("delay_over_prob", 0.0f)) + , delay_over_ticks(params.value("delay_over_ms", 0) / 20) {} + + void linkPeers(ModelRegistry& reg) override; + + bool evaluateBool(size_t t) override { + if (!action) return false; + bool action_now = action->evaluateBool(t); + + // edge detection + bool rising_edge = !prev_action && action_now; + bool falling_edge = prev_action && !action_now; + prev_action = action_now; + + switch (state) { + case IDLE_LOW: + if (rising_edge) { + bool over = ((double)rand() / RAND_MAX) < delay_over_prob; + int base = over ? delay_over_ticks : on_delay_ticks; + delay_counter = base + (delay_jitter_ticks ? rand() % (delay_jitter_ticks + 1) : 0); + state = WAITING_RISE; + } + break; + case WAITING_RISE: + if (delay_counter > 0) { delay_counter--; } + else { state = HIGH; } + break; + case HIGH: + if (falling_edge) { + bool over = ((double)rand() / RAND_MAX) < delay_over_prob; + int base = over ? delay_over_ticks : off_delay_ticks; + delay_counter = base + (delay_jitter_ticks ? rand() % (delay_jitter_ticks + 1) : 0); + state = WAITING_FALL; + } else if (((double)rand() / RAND_MAX) < flash_prob) { + return false; // flash: momentary drop, auto-recover next cycle + } + break; + case WAITING_FALL: + if (delay_counter > 0) { delay_counter--; } + else { state = IDLE_LOW; } + break; + } + return state == HIGH || state == WAITING_FALL; + } + + void reset() override { state = IDLE_LOW; prev_action = false; } +}; +``` + +`linkPeers` 实现在 Task 7 ModelRegistry 之后,详见 Step 2。 + +- [ ] **Step 2: 在 ValvePairModel 中实现 linkPeers(需要 ModelRegistry 声明)** + +```cpp +// ValvePairModel::linkPeers — 在 model/ValvePairModel.h 末尾,需要先 include ModelRegistry.h +// (或者将 linkPeers 实现放在 ModelRegistry.cc 中) + +// 由于循环依赖(ValvePairModel::linkPeers 需要 ModelRegistry),使用前向声明 + 延迟实现: +// model/ValvePairModel.h 中声明 linkPeers,实现在 ModelRegistry.cc 尾部。 + +#include +// ... in ModelRegistry.cc or a separate file: + +void ValvePairModel::linkPeers(ModelRegistry& reg) { + auto models = reg.findByModelName(actionModelName); + if (!models.empty()) action = models[0]; +} +``` + +- [ ] **Step 3: Commit** + +```bash +git add TestProject/RNG/model/ValvePairModel.h +git commit -m "feat: add ValvePairModel with jitter/flash/over-delay" +``` + +--- + +### Task 7: 实现 CompositeModel + ModelRegistry + +**Files:** +- Create: `TestProject/RNG/model/CompositeModel.h` +- Create: `TestProject/RNG/model/ModelRegistry.h` +- Create: `TestProject/RNG/model/ModelRegistry.cc` + +- [ ] **Step 1: 编写 CompositeModel.h** + +```cpp +#pragma once +#include +#include + +struct CompositeModel : IModel { + std::unique_ptr base; + std::unique_ptr noise; + + CompositeModel(std::unique_ptr b, std::unique_ptr n) + : base(std::move(b)), noise(std::move(n)) {} + + float evaluate(size_t t) override { + return base->evaluate(t) + noise->evaluate(t); + } +}; +``` + +- [ ] **Step 2: 编写 ModelRegistry.h** + +```cpp +#pragma once +#include +#include +#include +#include +#include +#include +#include +using json = nlohmann::json; + +class ModelRegistry { +public: + using Ctor = std::function(const json& params, float defaultVal)>; + + static ModelRegistry& instance(); + + void loadModels(const std::string& jsonPath); + + IModel* getOrCreate(const std::string& tables1Spec, float defaultVal); + + std::vector findByModelName(const std::string& modelName); + + void registerMode(const std::string& mode, Ctor ctor); + +private: + ModelRegistry(); + std::unique_ptr createModel(const std::string& modelName, float defaultVal); + + struct ModelDef { + std::string mode; + json params; + }; + std::map modelTemplates; // JSON loaded templates + std::map> instances; // created instances, keyed by spec + std::map> byModelName; // model name → instances + std::map factory; // mode → constructor +}; +``` + +- [ ] **Step 3: 编写 ModelRegistry.cc** + +```cpp +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +ModelRegistry& ModelRegistry::instance() { + static ModelRegistry reg; + return reg; +} + +ModelRegistry::ModelRegistry() { + registerMode("constant", [](const json& p, float d) { return std::make_unique(p, d); }); + registerMode("normal", [](const json& p, float d) { return std::make_unique(p, d); }); + registerMode("linear", [](const json& p, float d) { return std::make_unique(p, d); }); + registerMode("sine", [](const json& p, float d) { return std::make_unique(p, d); }); + registerMode("uniform", [](const json& p, float d) { return std::make_unique(p, d); }); + registerMode("spike", [](const json& p, float d) { return std::make_unique(p, d); }); + registerMode("drift", [](const json& p, float d) { return std::make_unique(p, d); }); + registerMode("csv", [](const json& p, float d) { return std::make_unique(p, d); }); + registerMode("bool_random",[](const json& p, float d) { return std::make_unique(p, d); }); + registerMode("bool_toggle",[](const json& p, float d) { return std::make_unique(p, d); }); + registerMode("bool_csv", [](const json& p, float d) { return std::make_unique(p, d); }); + registerMode("valve_pair", [](const json& p, float d) { return std::make_unique(p, d); }); +} + +void ModelRegistry::registerMode(const std::string& mode, Ctor ctor) { + factory[mode] = std::move(ctor); +} + +void ModelRegistry::loadModels(const std::string& jsonPath) { + std::ifstream f(jsonPath); + if (!f.is_open()) throw std::runtime_error("Cannot open " + jsonPath); + json j; + f >> j; + for (auto& [name, def] : j["models"].items()) { + modelTemplates[name] = { def["mode"].get(), def.value("params", json::object()) }; + } +} + +std::unique_ptr ModelRegistry::createModel(const std::string& modelName, float defaultVal) { + // Check if it's a composite: base+noise + auto plusPos = modelName.find('+'); + if (plusPos != std::string::npos) { + auto base = createModel(modelName.substr(0, plusPos), defaultVal); + auto noise = createModel(modelName.substr(plusPos + 1), defaultVal); + return std::make_unique(std::move(base), std::move(noise)); + } + + // Check if it's inline CSV: csv:file:col + if (modelName.rfind("csv:", 0) == 0) { + auto first = modelName.find(':', 4); + auto second = modelName.find(':', first + 1); + json p; + p["file"] = modelName.substr(4, first - 4); + p["column"] = std::stoi(modelName.substr(first + 1, second - first - 1)); + return factory["csv"](p, 0.0f); + } + + // Check if it's a valve_pair:action_model pair reference + auto colonPos = modelName.find(':'); + if (colonPos != std::string::npos) { + std::string pairModel = modelName.substr(0, colonPos); + std::string actionModel = modelName.substr(colonPos + 1); + auto it = modelTemplates.find(pairModel); + if (it != modelTemplates.end() && it->second.mode == "valve_pair") { + auto model = factory["valve_pair"](it->second.params, defaultVal); + static_cast(model.get())->actionModelName = actionModel; + return model; + } + } + + // Simple model name lookup + auto it = modelTemplates.find(modelName); + if (it == modelTemplates.end()) { + // Fallback to normal_tiny if model not found + it = modelTemplates.find("normal_tiny"); + } + auto fit = factory.find(it->second.mode); + if (fit == factory.end()) { + throw std::runtime_error("Unknown mode: " + it->second.mode); + } + return fit->second(it->second.params, defaultVal); +} + +IModel* ModelRegistry::getOrCreate(const std::string& spec, float defaultVal) { + std::string key = spec.empty() ? "default" : spec; + auto it = instances.find(key); + if (it != instances.end()) return it->second.get(); + + auto model = createModel(spec.empty() ? "normal_tiny" : spec, defaultVal); + + // Track by model name (parse base model name for composite/pair) + std::string modelName = spec; + auto plusPos = modelName.find('+'); + if (plusPos != std::string::npos) modelName = modelName.substr(0, plusPos); + auto colonPos = modelName.find(':'); + if (colonPos != std::string::npos) modelName = modelName.substr(0, colonPos); + + byModelName[modelName].push_back(model.get()); + instances[key] = std::move(model); + return instances[key].get(); +} + +std::vector ModelRegistry::findByModelName(const std::string& modelName) { + auto it = byModelName.find(modelName); + if (it != byModelName.end()) return it->second; + return {}; +} + +// ValvePairModel::linkPeers — defined here to avoid circular include +void ValvePairModel::linkPeers(ModelRegistry& reg) { + auto models = reg.findByModelName(actionModelName); + if (!models.empty()) action = models[0]; +} +``` + +- [ ] **Step 4: Commit** + +```bash +git add TestProject/RNG/model/CompositeModel.h TestProject/RNG/model/ModelRegistry.h \ + TestProject/RNG/model/ModelRegistry.cc +git commit -m "feat: add ModelRegistry with JSON loading and composite model" +``` + +--- + +### Task 8: 重构 Generator 去除硬编码 + +**Files:** +- Modify: `TestProject/RNG/Generator.h` +- Modify: `TestProject/RNG/Generator.cc` + +- [ ] **Step 1: 更新 Generator.h — 添加 ModelRegistry 成员** + +将现有 `Generator.h` 的内容替换为: + +```cpp +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +class Generator { +public: + Generator(); + ~Generator(); + bool wtite_in_shm(int event_no); + +private: + std::unique_ptr logger_; + BinaryTele binary_tele{CMemVar::Const()->event_eis_start, "T_LOV_FDAAITEM"}; + map*> m_mapfix; + chrono::system_clock::time_point stime; + ModelRegistry& registry; +}; +``` + +- [ ] **Step 2: 重写 Generator.cc — 去掉全局 CSV 变量和硬编码 if-else** + +将现有 `Generator.cc` 的内容替换为: + +```cpp +#include +#include +#include +#include +#include +#include +#include +#include "mix_cc/debug/pre_define.h" + +using namespace std; +using namespace chrono; + +Generator::Generator() { + stime = chrono::system_clock::now(); + logger_ = make_unique("Generator"); + registry = ModelRegistry::instance(); +} + +Generator::~Generator() { + for (auto& it : m_mapfix) { + if (it.second != nullptr) { + delete it.second; + } + } +} + +bool Generator::wtite_in_shm(int event_no) { + try { + if (m_mapfix.find(event_no) == m_mapfix.end()) { + m_mapfix.insert( + make_pair(event_no, new CMemFix(to_string(event_no), + TEL_CACHE_SIZE))); + } + + binary_tele.ReBuild(event_no); + int size = binary_tele.size(); + + size_t data_index = + ((chrono::system_clock::now() - stime).count() / (size_t)pow(10, 6)) / + 20; + + for (int i = 0; i < size; i++) { + const char* spec = binary_tele[i].tables[1]; + string specStr = (spec != nullptr && strlen(spec) > 0) ? string(spec) : ""; + float defaultVal = atof(binary_tele[i].defaultValue); + IModel* model = registry.getOrCreate(specStr, defaultVal); + + if (binary_tele[i].type[0] == 'b') { + binary_tele[i] = model->evaluateBool(data_index) ? 1.0f : 0.0f; + } else { + binary_tele[i] = model->evaluate(data_index); + } + } + + char* buff = binary_tele.GetTeleData(); + m_mapfix[event_no]->push((PLC_DATA*)buff); + return true; + } catch (const std::exception& e) { + logger_->Error() << "wtite_in_shm Error!" << e.what() << std::endl; + return false; + } +} +``` + +- [ ] **Step 3: Commit** + +```bash +git add TestProject/RNG/Generator.h TestProject/RNG/Generator.cc +git commit -m "refactor: replace hardcoded signal mappings with ModelRegistry" +``` + +--- + +### Task 9: 更新 RNG 启动流程加载配置 + 链接 valve_pair + +**Files:** +- Modify: `TestProject/RNG/RNG.h` +- Modify: `TestProject/RNG/RNG.cc` + +- [ ] **Step 1: 更新 RNG.cc — 在 start() 中加载 rng_models.json** + +修改 `RNG::start()`(文件 `TestProject/RNG/RNG.cc`),在 `con_mag_->dbLogin()` 之后添加一行: + +原始 code: +```cpp +int RNG::start() { + logger_->Info() << "-------RNG::start-------" << std::endl; + con_mag_ = std::make_shared(); + con_mag_->dbLogin(); + try { + auto module_name = name(); + RNG_server = new RNGICEI(); + this->add(string("baosight/") + name(), RNG_server); +``` + +替换为: +```cpp +int RNG::start() { + logger_->Info() << "-------RNG::start-------" << std::endl; + con_mag_ = std::make_shared(); + con_mag_->dbLogin(); + ModelRegistry::instance().loadModels("/users/dsc/code/TestProject/RNG/json/rng_models.json"); + try { + auto module_name = name(); + RNG_server = new RNGICEI(); + this->add(string("baosight/") + name(), RNG_server); +``` + +同时在 RNG.cc 顶部添加 include: +```cpp +#include +``` + +- [ ] **Step 2: 提交** + +```bash +git add TestProject/RNG/RNG.cc +git commit -m "feat: load rng_models.json on startup" +``` + +- [ ] **Step 3: 在 Generator::wtite_in_shm 中添加 valve_pair linkPeers 调用** + +Generator 首次为 event_no 创建所有 model 实例后需要 link。修改 `Generator.cc`: + +在 `for (int i = 0; i < size; i++)` 循环**之前**添加一次 link 循环: + +```cpp + // First pass: create all models, then link valve_pair models + for (int i = 0; i < size; i++) { + const char* spec = binary_tele[i].tables[1]; + string specStr = (spec != nullptr && strlen(spec) > 0) ? string(spec) : ""; + float defaultVal = atof(binary_tele[i].defaultValue); + IModel* model = registry.getOrCreate(specStr, defaultVal); + model->linkPeers(registry); + } + + // Second pass: evaluate + for (int i = 0; i < size; i++) { + const char* spec = binary_tele[i].tables[1]; + string specStr = (spec != nullptr && strlen(spec) > 0) ? string(spec) : ""; + float defaultVal = atof(binary_tele[i].defaultValue); + IModel* model = registry.getOrCreate(specStr, defaultVal); + + if (binary_tele[i].type[0] == 'b') { + binary_tele[i] = model->evaluateBool(data_index) ? 1.0f : 0.0f; + } else { + binary_tele[i] = model->evaluate(data_index); + } + } +``` + +- [ ] **Step 4: 提交** + +```bash +git add TestProject/RNG/Generator.cc +git commit -m "feat: link valve_pair action models before evaluation" +``` + +--- + +### Task 10: 更新 CMakeLists.txt + +**Files:** +- Modify: `TestProject/RNG/CMakeLists.txt` + +- [ ] **Step 1: 添加 model/ 目录的源文件** + +在 `aux_source_directory(./ DIR_ROOT)` 之后添加: + +```cmake +aux_source_directory(./model MODEL_DIR) +``` + +在 `add_executable(RNG ${DIR_ROOT})` 中添加 `${MODEL_DIR}`: + +```cmake +add_executable( + RNG + ${DIR_ROOT} + ${MODEL_DIR} + ) +``` + +- [ ] **Step 2: 验证 CMake 变更** + +```bash +cd TestProject/RNG/build && cmake .. -DCMAKE_BUILD_TYPE=Release +# 预期: Configuring done, 无错误 +``` + +- [ ] **Step 3: Commit** + +```bash +git add TestProject/RNG/CMakeLists.txt +git commit -m "build: add model/ source directory to CMake" +``` + +--- + +### Task 11: 清理旧的未使用文件 + +**Files:** +- Remove references: `TestProject/RNG/BaseData.h` (不再被任何文件引用) + +- [ ] **Step 1: 验证 BaseData.h 无引用** + +```bash +grep -r "BaseData" TestProject/RNG/ --include="*.cc" --include="*.h" +# 预期: 无匹配(所有引用在 Generator.cc 重构中已移除) +``` + +- [ ] **Step 2: 删除 BaseData.h** + +```bash +git rm TestProject/RNG/BaseData.h +``` + +- [ ] **Step 3: Commit** + +```bash +git commit -m "chore: remove unused BaseData.h" +``` + +--- + +## 实现顺序总结 + +``` +Task 1 (json config) ──┐ +Task 2 (IModel interface) ──┤ 基础设施,可并行 +Task 3 (6 analog models) ──┤ +Task 4 (CsvReplayModel) ──┤ +Task 5 (3 boolean models) ──┤ +Task 6 (ValvePairModel) ──┤ +Task 7 (ModelRegistry) ──┘ ← 依赖 Task 2-6 +Task 8 (refactor Generator) ──── ← 依赖 Task 7 +Task 9 (RNG startup + link) ──── ← 依赖 Task 8 +Task 10 (CMakeLists) ──── ← 依赖 Task 7 (需要 model/ModelRegistry.cc 存在) +Task 11 (cleanup) ──── ← 最后 +``` + +每个 Task 提交一次,编译验证在 Linux 环境进行(本机无编译环境)。