eis/docs/superpowers/plans/2026-05-13-rng-parameterized-models-plan.md
Huamonarch 7339801f2f docs: add RNG parameterized models implementation plan
11 tasks covering JSON config, 14 model classes, ModelRegistry factory,
Generator refactoring, valve_pair linking, and CMake updates.
2026-05-13 15:02:58 +08:00

957 lines
28 KiB
Markdown
Raw 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 参数化随机数生成 — 实现计划
> **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 <cstddef>
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 <TestProject/RNG/model/IModel.h>
#include <nlohmann/json.hpp>
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 <TestProject/RNG/model/IModel.h>
#include <TestProject/RNG/RandT.h>
#include <nlohmann/json.hpp>
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 <TestProject/RNG/model/IModel.h>
#include <nlohmann/json.hpp>
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 <TestProject/RNG/model/IModel.h>
#include <nlohmann/json.hpp>
#include <cmath>
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 <TestProject/RNG/model/IModel.h>
#include <TestProject/RNG/RandT.h>
#include <nlohmann/json.hpp>
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 <TestProject/RNG/model/IModel.h>
#include <nlohmann/json.hpp>
#include <cstdlib>
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 <TestProject/RNG/model/IModel.h>
#include <nlohmann/json.hpp>
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 <TestProject/RNG/model/IModel.h>
#include <TestProject/RNG/read_csv.hpp>
#include <nlohmann/json.hpp>
#include <string>
using json = nlohmann::json;
struct CsvReplayModel : IModel {
ReadCSV::DoubleData data;
int column;
CsvReplayModel(const json& params, float)
: data(params["file"].get<std::string>())
, column(params["column"].get<int>()) {}
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 <TestProject/RNG/model/IModel.h>
#include <nlohmann/json.hpp>
#include <cstdlib>
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 <TestProject/RNG/model/IModel.h>
#include <nlohmann/json.hpp>
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 <TestProject/RNG/model/IModel.h>
#include <TestProject/RNG/read_csv.hpp>
#include <nlohmann/json.hpp>
#include <string>
using json = nlohmann::json;
struct BoolCsvModel : IModel {
ReadCSV::IntData data;
int column;
BoolCsvModel(const json& params, float)
: data(params["file"].get<std::string>())
, column(params["column"].get<int>()) {}
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 <TestProject/RNG/model/IModel.h>
#include <nlohmann/json.hpp>
#include <cstdlib>
#include <string>
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 <vector>
// ... 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 <TestProject/RNG/model/IModel.h>
#include <memory>
struct CompositeModel : IModel {
std::unique_ptr<IModel> base;
std::unique_ptr<IModel> noise;
CompositeModel(std::unique_ptr<IModel> b, std::unique_ptr<IModel> 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 <TestProject/RNG/model/IModel.h>
#include <nlohmann/json.hpp>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <vector>
using json = nlohmann::json;
class ModelRegistry {
public:
using Ctor = std::function<std::unique_ptr<IModel>(const json& params, float defaultVal)>;
static ModelRegistry& instance();
void loadModels(const std::string& jsonPath);
IModel* getOrCreate(const std::string& tables1Spec, float defaultVal);
std::vector<IModel*> findByModelName(const std::string& modelName);
void registerMode(const std::string& mode, Ctor ctor);
private:
ModelRegistry();
std::unique_ptr<IModel> createModel(const std::string& modelName, float defaultVal);
struct ModelDef {
std::string mode;
json params;
};
std::map<std::string, ModelDef> modelTemplates; // JSON loaded templates
std::map<std::string, std::unique_ptr<IModel>> instances; // created instances, keyed by spec
std::map<std::string, std::vector<IModel*>> byModelName; // model name → instances
std::map<std::string, Ctor> factory; // mode → constructor
};
```
- [ ] **Step 3: 编写 ModelRegistry.cc**
```cpp
#include <TestProject/RNG/model/ModelRegistry.h>
#include <TestProject/RNG/model/ConstantModel.h>
#include <TestProject/RNG/model/NormalModel.h>
#include <TestProject/RNG/model/LinearModel.h>
#include <TestProject/RNG/model/SineModel.h>
#include <TestProject/RNG/model/UniformModel.h>
#include <TestProject/RNG/model/SpikeModel.h>
#include <TestProject/RNG/model/DriftModel.h>
#include <TestProject/RNG/model/CsvReplayModel.h>
#include <TestProject/RNG/model/BoolRandomModel.h>
#include <TestProject/RNG/model/BoolToggleModel.h>
#include <TestProject/RNG/model/BoolCsvModel.h>
#include <TestProject/RNG/model/ValvePairModel.h>
#include <TestProject/RNG/model/CompositeModel.h>
#include <fstream>
#include <sstream>
#include <stdexcept>
ModelRegistry& ModelRegistry::instance() {
static ModelRegistry reg;
return reg;
}
ModelRegistry::ModelRegistry() {
registerMode("constant", [](const json& p, float d) { return std::make_unique<ConstantModel>(p, d); });
registerMode("normal", [](const json& p, float d) { return std::make_unique<NormalModel>(p, d); });
registerMode("linear", [](const json& p, float d) { return std::make_unique<LinearModel>(p, d); });
registerMode("sine", [](const json& p, float d) { return std::make_unique<SineModel>(p, d); });
registerMode("uniform", [](const json& p, float d) { return std::make_unique<UniformModel>(p, d); });
registerMode("spike", [](const json& p, float d) { return std::make_unique<SpikeModel>(p, d); });
registerMode("drift", [](const json& p, float d) { return std::make_unique<DriftModel>(p, d); });
registerMode("csv", [](const json& p, float d) { return std::make_unique<CsvReplayModel>(p, d); });
registerMode("bool_random",[](const json& p, float d) { return std::make_unique<BoolRandomModel>(p, d); });
registerMode("bool_toggle",[](const json& p, float d) { return std::make_unique<BoolToggleModel>(p, d); });
registerMode("bool_csv", [](const json& p, float d) { return std::make_unique<BoolCsvModel>(p, d); });
registerMode("valve_pair", [](const json& p, float d) { return std::make_unique<ValvePairModel>(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<std::string>(), def.value("params", json::object()) };
}
}
std::unique_ptr<IModel> 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<CompositeModel>(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<ValvePairModel*>(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<IModel*> 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 <glob/BinaryTele.h>
#include <log4cplus/LOG.h>
#include <zlib/MemVar.h>
#include <zlib/MemFix.hpp>
#include <zlib/zoneDef.h>
#include <TestProject/RNG/model/ModelRegistry.h>
#include <array>
#include <chrono>
#include <map>
#include <numeric>
#include <string>
using namespace std;
class Generator {
public:
Generator();
~Generator();
bool wtite_in_shm(int event_no);
private:
std::unique_ptr<LOG> logger_;
BinaryTele binary_tele{CMemVar::Const()->event_eis_start, "T_LOV_FDAAITEM"};
map<int, CMemFix<PLC_DATA>*> m_mapfix;
chrono::system_clock::time_point stime;
ModelRegistry& registry;
};
```
- [ ] **Step 2: 重写 Generator.cc — 去掉全局 CSV 变量和硬编码 if-else**
将现有 `Generator.cc` 的内容替换为:
```cpp
#include <TestProject/RNG/Generator.h>
#include <TestProject/RNG/model/ModelRegistry.h>
#include <glob/BinaryTele.h>
#include <zlib/MemFix.hpp>
#include <zlib/zoneDef.h>
#include <string>
#include <cmath>
#include "mix_cc/debug/pre_define.h"
using namespace std;
using namespace chrono;
Generator::Generator() {
stime = chrono::system_clock::now();
logger_ = make_unique<LOG>("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<PLC_DATA>(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<ConnectionMag>();
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<ConnectionMag>();
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 <TestProject/RNG/model/ModelRegistry.h>
```
- [ ] **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 环境进行(本机无编译环境)。