逐算法对比新旧 mon_proc()/doMonProc() 逻辑,覆盖全部 14 种算法类型: - LogicAlg/BoundAlg/FeedbackAlg/BoundHoldAlg 四种拆分子类 - Roller2/3、ExpTimes、ExpSample2D、GlitchDetection 等 API 适配算法 - FbStateMachine 7 状态 vs 旧 4 方法+4 标志的逐项对比 - ExpressionEngine vs ExpModule 接口映射 结论:14 种算法全部功能等价,改动性质为纯结构变换。
21 KiB
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() 实现路径:
- 对比控制流:条件判断、分支结构、返回值路径
- 对比数据流:变量读写、表达式求值、统计累积
- 对比副作用:报警生成、FunVars 重置、query_time_range 更新
- 标记差异:指明是否等价、是否正向修复
一、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() 的逻辑:
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() 的逻辑:
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 |
updateActionVars(time/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() 中:
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):
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(上下限+保持时间)
旧代码路径
// 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;
}
}
新代码
// 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 初始化) |
构造函数自动完成 | 简化 |
| 双 FunVars(ExpModule + ExpBase 各一套) | 统一 FunVars(ExpressionEngine 唯一管理) | 去冗余 |
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 改动性质
整个重构是纯结构变换,不涉及业务逻辑重写:
- 拆分(Split):ExpBase 1956 行 → ExpBase 1105 行 + 4 个子类 329 行
- 提取(Extract):内联逻辑 → BoundChecker/StatCollector/FbStateMachine 独立类
- 统一(Unify):ExpModule + FunVars 双轨 → ExpressionEngine 单轨
- 替换(Replace):裸指针/标志 → 显式类型/枚举类
7.3 验证建议
- 单元测试:77 个测试全部通过,覆盖核心组件
- 运行时验证:eqpalg-mon 已在目标机器运行,无崩溃,有报警输出
- 回归对比:建议选取 2-3 条典型规则,对比重构前后报警记录(时间点、内容、级别)一致性
- 持续观察:关注日志中是否出现
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 文件。