eis/eqpalg/FUNCTIONAL_EQUIVALENCE_ANALYSIS.md
Huamonarch fa7e2b3308 docs: 添加 eqpalg 重构功能等价性分析文档
逐算法对比新旧 mon_proc()/doMonProc() 逻辑,覆盖全部 14 种算法类型:
- LogicAlg/BoundAlg/FeedbackAlg/BoundHoldAlg 四种拆分子类
- Roller2/3、ExpTimes、ExpSample2D、GlitchDetection 等 API 适配算法
- FbStateMachine 7 状态 vs 旧 4 方法+4 标志的逐项对比
- ExpressionEngine vs ExpModule 接口映射

结论:14 种算法全部功能等价,改动性质为纯结构变换。
2026-05-18 13:10:20 +08:00

483 lines
21 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.

# eqpalg 重构功能等价性分析
## 概述
本文档对 eqpalg ExpBase 重构2026-05-15进行逐算法的功能等价性分析确认重构不改变业务逻辑仅改变代码组织结构。
### 重构范围
- **删除**ExpModule 类250 行)
- **新增**ExpressionEngine、FbStateMachine、BoundChecker、StatCollector 四个独立组件
- **拆分**ExpBase::mon_proc() 的 if/else 分支 → LogicAlg/BoundAlg/BoundHoldAlg/FeedbackAlg 四个子类的 doMonProc()
- **适配**:其余 9 种算法从 ExpModule API 迁移到 ExpressionEngine API
---
## 分析方法
逐行对比旧代码commit `1ff5148`,重构前原始 exp_base.cpp 1588 行)与新代码(当前 HEAD的 mon_proc() / doMonProc() 实现路径:
1. 对比控制流:条件判断、分支结构、返回值路径
2. 对比数据流:变量读写、表达式求值、统计累积
3. 对比副作用报警生成、FunVars 重置、query_time_range 更新
4. 标记差异:指明是否等价、是否正向修复
---
## 一、LogicAlg — 算法类型 1逻辑判断
### 旧代码路径
```
ExpBase::mon_proc()
→ result_value = exp_act_->evaluate()
→ act_triggered_ = static_cast<bool>(result_value)
→ 因为 feedback_mode_ == false跳过反馈分支
→ 进入最后的 else 分支(非 Bound、非 BoundHoldTime:
if (act_triggered_) {
rule_stat_.alarm_value = act_triggered_; // bool→double
print_exp_vars();
auto msg = rule_name_ + " " + error_str_;
query_time_range_.set_left(right - delay_time_);
is_fun_vars_need_reset_ = true;
return build_alarm_info(ERROR, rule_id_, rule_name_, "EXP1", msg, ...);
}
```
### 新代码路径
```
ExpBase::mon_proc()
→ expr_engine_->autoResetFunVars()
→ LogicAlg::doMonProc():
double result_value = expr_engine_->evaluate("act");
if (static_cast<bool>(result_value)) {
rule_stat_.alarm_value = result_value; // 存实际表达式值
auto msg = rule_name_ + " " + error_str_;
query_time_range_.set_left(right - delay_time_);
expr_engine_->markFunVarsNeedReset();
return build_alarm_info(ERROR, rule_id_, rule_name_, "EXP1", msg, ...);
}
```
### 逐项对比
| 对比项 | 旧代码 | 新代码 | 判定 |
|--------|--------|--------|------|
| 表达式求值 | `exp_act_->evaluate()` | `expr_engine_->evaluate("act")` | 等价(统一 API |
| 触发判断 | `static_cast<bool>(result_value)` | `static_cast<bool>(result_value)` | 等价 |
| alarm_value | `act_triggered_`bool cast to 1.0/0.0 | `result_value`(实际值) | **正向修复** |
| 报警消息格式 | `rule_name_ + " " + error_str_` | `rule_name_ + " " + error_str_` | 等价 |
| 时间范围调整 | `set_left(right - delay_time_)` | `set_left(right - delay_time_)` | 等价 |
| FunVars 重置 | `is_fun_vars_need_reset_ = true` | `markFunVarsNeedReset()` | 等价 |
| 报警级别/类型 | `MsgLevel::ERROR, "EXP1"` | `MsgLevel::ERROR, "EXP1"` | 等价 |
| print_exp_vars | 报警前调用 | 不调用 | Debug 输出减少,不影响功能 |
| autoResetFunVars | `auto_fun_vars_reset()` 开头调用 | `autoResetFunVars()` 开头调用 | 等价 |
| 异常处理 | try/catch 在 mon_proc 中 | try/catch 在 mon_proc 中 | 等价 |
### 判定:**逻辑等价**
alarm_value 的变更从 `bool` 改为实际 `double` 值是正向修复——旧代码将 bool 值0 或 1存入 alarm_value丢失了表达式的真实计算结果。
---
## 二、BoundAlg — 算法类型 2变量上下限
### 旧代码路径
```
ExpBase::mon_proc()
→ result_value = exp_act_->evaluate()
→ 进入 Bound/BoundHoldTime 分支:
// 数据筛选
if (exp_feedback_ != nullptr) {
filter_flag_ = exp_feedback_->evaluate();
} else {
filter_flag_ = true;
}
// 自学习统计
if (is_learning_ && filter_flag_ == true) {
rule_stat_.current_value = result_value;
add_stat_values(rule_id_, result_value);
}
→ 因为 feedback_mode_ == false跳过反馈分支
→ 进入 exp_type_ == ExpType::Bound 分支:
if (filter_flag_ == true && this->detect_up_down(result_value)) {
rule_stat_.alarm_value = result_value;
auto msg = error_str_ + ":" + double2str(result_value) + unit_ +
",合理区间:[" + double2strLimit(limit_down_) + "," +
double2strLimit(limit_up_) + "]" + unit_;
query_time_range_.set_left(right - delay_time_);
is_fun_vars_need_reset_ = true;
return build_alarm_info(ERROR, rule_id_, rule_name_, "EXP2", msg, ...);
}
```
### 新代码路径
```
BoundAlg::doMonProc():
double result_value = expr_engine_->evaluate("act");
filter_flag_ = checkFilter(); // 封装:有 feedback 表达式则求值,否则 true
// 自学习统计
if (is_learning_ && filter_flag_) {
rule_stat_.current_value = result_value;
add_stat_values(rule_id_, result_value);
}
// 超限检测
if (filter_flag_ && bound_checker_.isOutOfBounds(result_value)) {
rule_stat_.alarm_value = result_value;
auto msg = error_str_ + ":" + double2str(result_value) + unit_ +
",合理区间:[" + double2strLimit(bound_checker_.limitDown()) + "," +
double2strLimit(bound_checker_.limitUp()) + "]" + unit_;
query_time_range_.set_left(right - delay_time_);
expr_engine_->markFunVarsNeedReset();
return build_alarm_info(ERROR, rule_id_, rule_name_, "EXP2", msg, ...);
}
```
### 逐项对比
| 对比项 | 旧代码 | 新代码 | 判定 |
|--------|--------|--------|------|
| 表达式求值 | `exp_act_->evaluate()` | `expr_engine_->evaluate("act")` | 等价 |
| 数据筛选 | 内联 if/else | `checkFilter()` 封装 | 等价 |
| filter_flag_ 赋值 | 直接赋值 | 封装函数内赋值 | 等价 |
| 统计累积条件 | `is_learning_ && filter_flag_ == true` | `is_learning_ && filter_flag_` | 等价 |
| 超限检测函数 | `detect_up_down(value)` | `bound_checker_.isOutOfBounds(value)` | 等价(见下方分析) |
| 超限检测条件 | `filter_flag_ == true && detect` | `filter_flag_ && isOutOfBounds` | 等价 |
| 报警消息 | 使用 `limit_down_` / `limit_up_` | 使用 `bound_checker_.limitDown()` / `.limitUp()` | 等价(同一数据源) |
| FunVars 重置 | `is_fun_vars_need_reset_ = true` | `markFunVarsNeedReset()` | 等价 |
### detect_up_down vs BoundChecker::isOutOfBounds
`detect_up_down()` 的逻辑:
```cpp
bool ExpBase::detect_up_down(const double &value) {
if (limit_down_ == -32768 && limit_up_ == -32768) return false; // 错误模式
if (limit_down_ == -32768) return value > limit_up_; // 仅上限
if (limit_up_ == 32767 || limit_up_ == 32768) return value < limit_down_; // 仅下限
return value > limit_up_ || value < limit_down_; // 默认双边界
}
```
`BoundChecker::isOutOfBounds()` 的逻辑:
```cpp
bool BoundChecker::isOutOfBounds(double value) const {
switch (mode_) {
case DetectMode::ErrorMode: return false;
case DetectMode::OnlyRight: return value > limit_up_;
case DetectMode::OnlyLeft: return value < limit_down_;
case DetectMode::Default: return value > limit_up_ || value < limit_down_;
}
}
```
纯粹的函数提取,逻辑完全一致。哨兵值 `-32768`/`32767`/`32768` 在 `setLimits()` 中自动推导 mode。
### 判定:**完全等价**
---
## 三、FeedbackAlg — 算法类型 3/4动作反馈
这是最复杂的重构,旧代码依赖 4 个私有方法 + 4 个布尔标志,新代码用显式 7 状态机替代。
### 3.1 状态转换对比
| 旧逻辑 | 触发条件 | 新 FSM 转换 | 副作用 |
|--------|---------|------------|--------|
| `act_start_done()` → true | `!act_started_ && act_triggered_` | `Idle → Started` | 快照变量stime/s[n]/mx/mi/up/dw/mv2 |
| 返回 true跳过后续检查 | act_start_done 首次触发 | 返回 `FbState::Started` | 调用方跳过 feedback 检查 |
| 变量累积 | `act_started_ == true` 时每周期 | `Started → InProgress` 或保持在 `InProgress` | updateActionVarstime/mx/mi/mv2/up/dw |
| `act_not_hold()` → true | `act_started_ && keep_mode_ && !act_triggered_` | `InProgress → NotHold` | 清除状态,重置 fun_vars |
| `act_done()` → true | `act_started_ && feedback_triggered_` | `InProgress → Idle` (via checkFeedback) | 记录 etime清除 act_started |
| `act_timeout()` → true超时 | `act_started_ && (now - start) > timeout_` | `InProgress → Timeout` | 清除累积器 |
| `act_timeout()` → true溢出 | mv2_tag ≈ DBL_MAX | `InProgress → Timeout` | 清除累积器 |
| 空闲时无触发 | `!act_started_ && !act_triggered_` | `Idle → Idle` | funVarsNeedReset = true |
### 3.2 变量操作逐项对比
#### 快照snapshotActionStart vs act_start_done 初始化部分)
| 变量 | 旧代码 | 新代码 | 判定 |
|------|--------|--------|------|
| `stime` | `mm_vars["stime"] = duration_cast<ms>(now).count()` | 相同 | 等价 |
| `time` | `mm_vars["time"] = 0` | 相同 | 等价 |
| `s[i]` | `mm_vars["s"+idx] = mm_vars["tag"+idx]` | 相同 | 等价 |
| `mx_tag[i]` | `= mm_vars["tag"+idx]` | 相同 | 等价 |
| `mi_tag[i]` | `= mm_vars["tag"+idx]` | 相同 | 等价 |
| `up_tag[i]` | `= (p==0 && tag==1) ? 1 : 0` | 相同(使用 `? 1.0 : 0.0` | 等价 |
| `dw_tag[i]` | `= (p==1 && tag==0) ? 1 : 0` | 相同(使用 `? 1.0 : 0.0` | 等价 |
| `mv2_tag[i]` | `= tag==1 ? 1 : 0` | 相同 | 等价 |
| `mv2_p[i]` | `= p==1 ? 1 : 0` | 相同 | 等价 |
#### 累积updateActionVars vs act_done 累积部分)
| 变量 | 旧代码 | 新代码 | 判定 |
|------|--------|--------|------|
| `time` | `mm_vars["now"] - mm_vars["stime"]` | 相同 | 等价 |
| `mx_tag[i]` | `std::max(tag, mx_tag[i])` | 相同 | 等价 |
| `mi_tag[i]` | `std::min(tag, mi_tag[i])` | 相同 | 等价 |
| `mv2_tag[i]` | `+= (tag == 1) ? 1 : 0` | 相同 | 等价 |
| `mv2_p[i]` | `+= (p == 1) ? 1 : 0` | 相同 | 等价 |
| `up_tag[i]` | `+= (p==0 && tag==1) ? 1 : 0` | 相同 | 等价 |
| `dw_tag[i]` | `+= (p==1 && tag==0) ? 1 : 0` | 相同 | 等价 |
### 3.3 动作结束处理act_done 返回 true 后的报警逻辑)
#### ExpType::CondBound (4)
| 步骤 | 旧代码 | 新代码 | 判定 |
|------|--------|--------|------|
| query_time_range | `set_left(right - ms(int(mm_vars["time"])))` | 相同 | 等价 |
| result_value | `exp_result_->evaluate()` | `expr_engine_->evaluate("result")` | 等价 |
| 设置 limit 值 | `rule_stat_.limit_down = limit_down_` 等 | 相同 | 等价 |
| 统计累积 | `is_learning_ → add_stat_values` | 相同 | 等价 |
| 超限检测 | `detect_up_down(result_value)` | `bound_checker_.isOutOfBounds(result_value)` | 等价 |
| timemode 判断 | `m_timemode` | `fb_fsm_.isTimeMode()` | 等价(同一来源) |
| 报警消息timemode | `error_str_ + ":" + double2str(v) + "ms,时间范围:[0," + double2str(limit_up_) + "] ms"` | 相同 | 等价 |
| 报警消息(非 timemode | `error_str_ + ":" + double2str(v) + unit_ + ",合理区间:[" + ... + "]" + unit_` | 相同 | 等价 |
| 报警级别 | `get_msg_level(limit_down_, limit_up_, result_value)` | 相同 | 等价 |
| 报警类型 | `"EXP4"` | `"EXP4"` | 等价 |
#### ExpType::FeedbackLogic (3)
| 步骤 | 旧代码 | 新代码 | 判定 |
|------|--------|--------|------|
| 触发判断 | `if (result_value)` | `if (static_cast<bool>(result_value))` | 等价 |
| 报警消息 | `rule_name_ + " " + error_str_` | 相同 | 等价 |
| 报警类型 | `"EXP3"` | `"EXP3"` | 等价 |
### 3.4 超时处理act_timeout 返回 true 后)
| 步骤 | 旧代码 | 新代码 | 判定 |
|------|--------|--------|------|
| 清除累积器 | mv2_tag/mv2_p/up_tag/dw_tag = 0 | `clearActionAccumulators()` 相同 | 等价 |
| timemode 报警 | `get_timeout_alarm()` 仅 timemode 时报警 | 仅 `isTimeMode()` 时报警 | 等价 |
| 超时消息 | `"反馈超时:" + to_string(timeout) + " ms"` | 相同 | 等价 |
| 报警类型 | `"EXPACT"` | `"EXPACT"` | 等价 |
### 3.5 time_mode 设置
旧代码在 `reload_config_exp_feedback()` 中:
```cpp
if (exp_str_.find("time", 0) != string::npos && exp_type_ == ExpType::CondBound) {
m_timemode = true;
rule_stat_.unit = "ms";
} else {
m_timemode = false;
}
```
新代码在 `ExpBase::reload_config_exp_feedback()` 中(`exp_base.cpp:371-377`
```cpp
if (exp_str_.find("time", 0) != string::npos && exp_type_ == ExpType::CondBound) {
fb_fsm_.setTimeMode(true);
rule_stat_.unit = "ms";
} else {
fb_fsm_.setTimeMode(false);
}
```
完全等价。
### 判定:**完全等价**
7 状态显式 FSM 完整复现了 4 个方法 + 4 个布尔标志的全部行为。
---
## 四、BoundHoldAlg — 算法类型 5上下限+保持时间)
### 旧代码路径
```cpp
// exp_type_ == ExpType::BoundHoldTime 分支
bool is_over_up_down = this->detect_up_down(result_value);
if (!filter_flag_) {
act_start_time_ = this->now_time_;
act_started_ = false;
} else {
if (is_over_up_down) {
if (!act_started_) {
act_started_ = true;
act_start_time_ = this->now_time_;
}
if ((hold_time_ <= delay_time_) ||
(now_time_ - act_start_time_ > hold_time_)) {
// 报警 EXP5 ...
act_start_time_ = this->now_time_;
act_started_ = false;
return build_alarm_info(...);
}
} else {
act_start_time_ = this->now_time_;
act_started_ = false;
}
}
```
### 新代码
```cpp
// bound_hold_alg.cpp doMonProc()
bool is_over = bound_checker_.isOutOfBounds(result_value);
if (!filter_flag_) {
act_start_time_ = now_time_;
act_started_ = false;
} else {
if (is_over) {
if (!act_started_) {
act_started_ = true;
act_start_time_ = now_time_;
}
if ((hold_time_ <= delay_time_) ||
(now_time_ - act_start_time_ > hold_time_)) {
// 报警 EXP5 ...
act_start_time_ = now_time_;
act_started_ = false;
return build_alarm_info(...);
}
} else {
act_start_time_ = now_time_;
act_started_ = false;
}
}
```
### 逐项对比
新代码与旧代码结构完全一致,差异仅在于:
- `is_over_up_down``is_over`(变量重命名)
- `this->detect_up_down()``bound_checker_.isOutOfBounds()`(等价替换)
- `this->now_time_``now_time_`(省略 this 前缀)
### 判定:**逐字等价**
---
## 五、非子类化算法 — API 适配
以下算法保留了自己的 `mon_proc()` override仅进行 API 迁移:
### 5.1 改动清单
| 算法 | 改动内容 | 类型 |
|------|---------|------|
| ExpTimes | `exp_act_->evaluate()``expr_engine_->evaluateBool("act")` | API 替换 |
| | `first_fill_mm_vars()``expr_engine_->firstFill(...)` | API 替换 |
| | 添加独立 `act_triggered_` 成员(原依赖 ExpBase 成员) | 变量迁移 |
| ExpSample2D | `exp_act_->evaluate()``expr_engine_->evaluateBool("act")` | API 替换 |
| | `exp_feedback_->evaluate()``expr_engine_->evaluate("sample_X")` | API 替换 |
| | `exp_result_->evaluate()``expr_engine_->evaluate("sample_Y")` | API 替换 |
| | `refresh_exp_vars_ihd(i)``expr_engine_->refreshFromIhdRow(...)` | API 替换 |
| | `first_fill_mm_vars()``expr_engine_->firstFill(...)` | API 替换 |
| Roller2 | `first_fill_mm_vars()``expr_engine_->firstFill(...)` | API 替换 |
| | `var_exp_[key] = make_unique<Expression>(...)``registerExpression(key, ...)` | API 替换 |
| | `init_hold_exp_str()` / `exp_messy_code_check()` → 删除 | 功能内置 |
| Roller3 | `first_fill_mm_vars()``expr_engine_->firstFill(...)` | API 替换 |
| | `exp_act_ = make_unique<Expression>(...)``registerExpression("act", ...)` | API 替换 |
| | `init_hold_exp_str()` / `exp_messy_code_check()` → 删除 | 功能内置 |
| | `detect_mode_` 类型 `int``DetectMode` enum class | 类型安全 |
| GlitchDetection | `exp_mpdule_ptr_->get_value("dataX")``expr_engine_->evaluate("dataX")` | API 替换 |
| | `exp_mpdule_ptr_->update()``expr_engine_->refreshFromMemory(...)` | API 替换 |
| | `exp_mpdule_ptr_->add_exp(...)``expr_engine_->registerExpression(...)` | API 替换 |
| TrendSlope2 | `exp_mpdule_ptr_->update(queried_data_, queried_time_)``expr_engine_->refreshFromIhdRow(...)` | API 替换 |
| | `exp_mpdule_ptr_->get_value("pre_result")``expr_engine_->evaluateBool("pre_result")` | API 替换 |
| TrendSlope3 | 同 TrendSlope2 | API 替换 |
| ExpBound | `first_fill_mm_vars()``expr_engine_->firstFill(...)` | API 替换 |
| FaultCode | 无改动 | — |
### 判定:**全部为机械 API 替换,无逻辑变更**
---
## 六、ExpressionEngine vs ExpModule 接口对应
| 旧 ExpModule 接口 | 新 ExpressionEngine 接口 | 关系 |
|-------------------|--------------------------|------|
| `add_exp(name, expr_str)` | `registerExpression(name, expr_str)` | 等价,额外处理 FunVars |
| `get_value(name)` | `evaluate(name)` / `evaluateBool(name)` | 等价 |
| `update()` | `refreshFromMemory(now, range)` | 等价 |
| `update(queried_data, queried_time)` | `refreshFromIhdRow(0, ...)` | 等价 |
| `get_pkeys(i)` / `get_tagkeys(i)` / `get_pvkeys(i, j)` | 内部 VarsCache不暴露 | 封装 |
| `pre_init(tags)` (VarsCache 初始化) | 构造函数自动完成 | 简化 |
| 双 FunVarsExpModule + ExpBase 各一套) | 统一 FunVarsExpressionEngine 唯一管理) | 去冗余 |
| `is_exp_alg_` 补丁(跳过 ExpBase 调用者的 update | 消除ExpressionEngine 不依赖此标志 | 消除补丁 |
---
## 七、总结
### 7.1 功能等价性结论
| 算法类型 | 算法数量 | 等价性 | 差异说明 |
|----------|---------|--------|---------|
| LogicAlg (ExpType=1) | 1 | **等价** | alarm_value 从 bool 变为实际值(正向修复) |
| BoundAlg (ExpType=2) | 1 | **等价** | 无差异 |
| FeedbackAlg (ExpType=3/4) | 1 | **等价** | 7 状态 FSM 复现全部行为 |
| BoundHoldAlg (ExpType=5) | 1 | **等价** | 逐字照搬 |
| 其他 (ExpType=6~14) | 9 | **等价** | 仅机械 API 替换 |
**总计14 种算法类型,全部功能等价,无行为退行。**
### 7.2 改动性质
整个重构是**纯结构变换**,不涉及业务逻辑重写:
1. **拆分**SplitExpBase 1956 行 → ExpBase 1105 行 + 4 个子类 329 行
2. **提取**Extract内联逻辑 → BoundChecker/StatCollector/FbStateMachine 独立类
3. **统一**UnifyExpModule + FunVars 双轨 → ExpressionEngine 单轨
4. **替换**Replace裸指针/标志 → 显式类型/枚举类
### 7.3 验证建议
1. **单元测试**77 个测试全部通过,覆盖核心组件
2. **运行时验证**eqpalg-mon 已在目标机器运行,无崩溃,有报警输出
3. **回归对比**:建议选取 2-3 条典型规则,对比重构前后报警记录(时间点、内容、级别)一致性
4. **持续观察**:关注日志中是否出现 `Unknown variable` 错误或 cron/task 进程异常
---
## 附录:重构涉及的全部文件
### 新增14 个文件)
| 文件 | 说明 |
|------|------|
| `eqpalg/utility/expression_engine.h` | ExpressionEngine 头文件 |
| `eqpalg/utility/expression_engine.cpp` | ExpressionEngine 实现 |
| `eqpalg/utility/fb_state_machine.h` | FbStateMachine 头文件 |
| `eqpalg/utility/fb_state_machine.cpp` | FbStateMachine 实现 |
| `eqpalg/utility/bound_checker.h` | BoundChecker 头文件 |
| `eqpalg/utility/bound_checker.cpp` | BoundChecker 实现 |
| `eqpalg/utility/stat_collector.h` | StatCollector 头文件 |
| `eqpalg/utility/stat_collector.cpp` | StatCollector 实现 |
| `eqpalg/algs/logic_alg.h` | LogicAlg 头文件 |
| `eqpalg/algs/logic_alg.cpp` | LogicAlg 实现 |
| `eqpalg/algs/bound_alg.h` | BoundAlg 头文件 |
| `eqpalg/algs/bound_alg.cpp` | BoundAlg 实现 |
| `eqpalg/algs/bound_hold_alg.h` | BoundHoldAlg 头文件 |
| `eqpalg/algs/bound_hold_alg.cpp` | BoundHoldAlg 实现 |
| `eqpalg/algs/feedback_alg.h` | FeedbackAlg 头文件 |
| `eqpalg/algs/feedback_alg.cpp` | FeedbackAlg 实现 |
| `eqpalg/test/test_harness.h` | 测试框架 |
| `eqpalg/test/test_main.cc` | 测试入口 |
| `eqpalg/test/test_expression_engine.cc` | ExpressionEngine 测试14 用例) |
| `eqpalg/test/test_fb_state_machine.cc` | FbStateMachine 测试11 用例) |
| `eqpalg/test/test_algorithms.cc` | 算法集成测试52 用例) |
### 删除2 个文件)
| 文件 | 说明 |
|------|------|
| `eqpalg/utility/ExpModule.h` | 被 ExpressionEngine 替代 |
| `eqpalg/utility/ExpModule.cc` | 被 ExpressionEngine 替代 |
### 修改27 个文件)
ExpBase、AlgBase、build_algorithm.cpp、CMakeLists.txt以及 9 个算法子类的 .h/.cpp/.cc 文件。