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

28 KiB
Raw Blame 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

{
  "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
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

#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
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

// 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; }
};
// 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
#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
#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
#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
#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
#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
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

#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
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

#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
#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
#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
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

#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 声明)
// 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
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

#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
#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
#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
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 的内容替换为:

#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 的内容替换为:

#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
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:

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);

替换为:

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

#include <TestProject/RNG/model/ModelRegistry.h>
  • Step 2: 提交
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 循环:

    // 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: 提交
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) 之后添加:

aux_source_directory(./model MODEL_DIR)

add_executable(RNG ${DIR_ROOT}) 中添加 ${MODEL_DIR}

add_executable(
  RNG
  ${DIR_ROOT}
  ${MODEL_DIR}
 )
  • Step 2: 验证 CMake 变更
cd TestProject/RNG/build && cmake .. -DCMAKE_BUILD_TYPE=Release
# 预期: Configuring done, 无错误
  • Step 3: Commit
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 无引用

grep -r "BaseData" TestProject/RNG/ --include="*.cc" --include="*.h"
# 预期: 无匹配(所有引用在 Generator.cc 重构中已移除)
  • Step 2: 删除 BaseData.h
git rm TestProject/RNG/BaseData.h
  • Step 3: Commit
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 环境进行(本机无编译环境)。