eis/docs/superpowers/plans/2026-05-13-rng-parameterized-models-plan.md

957 lines
28 KiB
Markdown
Raw Normal View History

# 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 环境进行(本机无编译环境)。