25 KiB
FbStateMachine 深度分析与修正设计
对上轮优化方案中 FbStateMachine 设计不完整之处的修正,综合考虑 StatExp 状态函数刷新机制。
一、现有反馈状态机完整映射
1.1 真状态图(7 状态)
阅读源码后得出的真实状态图,比上一版的 5 状态更精确:
┌──────────────────────────────────────┐
│ exec_mon_call() 层 │
│ get_prr()==false → 强制重置 │
│ alarm 触发 → fun_reset(PRR的FunVars) │
└──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ mon_proc() 层 │
│ │
│ ┌─────────┐ act_triggered ┌──────────┐ │
│ │ Idle │ ───────────────► │ Started │ │
│ │ (空闲) │ │ (动作开始) │ │
│ │ fun✓ │ │ fun✗ │ │
│ └─────────┘ └────┬─────┘ │
│ ▲ │ │
│ │ ┌───────┼───────┐ │
│ │ │ │ │ │
│ │ ▼ ▼ ▼ │
│ │ ┌────────┐┌──────┐┌──────────┐ │
│ │ │NotHold ││Done ││Timeout │ │
│ │ │(不保持) ││(完成) ││(超时) │ │
│ │ │ fun✓ ││ fun✓ ││ fun✓ │ │
│ │ └───┬────┘└──┬───┘└────┬─────┘ │
│ │ │ │ │ │
│ └──────────────────┴────────┴─────────┘ │
│ 下次 mon_proc() 自动回到 Idle │
│ │
│ ┌──────────────────────────────────────────┐ │
│ │ InProgress (动作进行中) │ │
│ │ act_started_=true, 但未触发终端条件 │ │
│ │ fun✗, 持续更新 mv2/up/dw/mx/mi/time │ │
│ └──────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
注释: fun✓ = 本周期设置 is_fun_vars_need_reset_ = true
fun✗ = 不设置(状态函数持续累加)
关键:InProgress 不是一个独立的 return 分支,而是 "所有终端条件都不满足时,fallthrough 到 return {}"的隐式状态。
1.2 七个状态详解
| 状态 | 触发条件 | act_started_ | 返回 | fun_reset | 变量操作 |
|---|---|---|---|---|---|
| Idle | act_triggered_=false | false | {} | true (via act_start_done else分支) | 无 |
| Started | 首次 act_triggered_=true | false→true | {} | false | 快照 sN, 初始化 mx/mi/mv2/up/dw, stime=now, time=0 |
| InProgress | started后, 未达终端 | true | {} | false | act_done()更新: time, mx=max, mi=min, mv2+=1, up+=flag, dw+=flag |
| Done | feedback_triggered_=true | true→false | AlarmInfo 或 {} | true | etime=now (在act_done内) |
| NotHold | keep_mode && !act_triggered_ | true→false | {} | true | 无额外 |
| Timeout | now-start > timeout | true→false | AlarmInfo 或 {} | true | 清零 mv2/up/dw (在act_timeout内) |
| PRR_False | get_prr()=false (外层) | →false | (不进入mon_proc) | false | act_started_/act_triggered_/feedback_triggered_ 全部重置 |
1.3 fun_vars 完整生命周期
单周期时序:
┌─────────────────────────────────────────────────────────────┐
│ 1. AlgBase::exec_mon_call() │
│ ├── get_cycled()? │
│ ├── get_prr() → 若false: 重置fb状态, return (不进入mon) │
│ └── exec_mon() → ExpBase::exec_mon() │
│ │
│ 2. ExpBase::exec_mon() │
│ ├── refresh_exp_vars_mem/ihd() │
│ │ └── fun_vars_.refresh_fun_vars(false) ← 评估funN │
│ └── mon_proc() │
│ │
│ 3. mon_proc() │
│ ├── auto_fun_vars_reset() ← 若flag, reset funN │
│ ├── evaluate expressions (使用当前funN值) │
│ ├── [反馈状态机判断] │
│ │ └── 若终端状态: is_fun_vars_need_reset_ = true │
│ └── return alarm or {} │
│ │
│ 4. 回到 exec_mon_call() │
│ └── if alarm: exp_mpdule_ptr_->fun_reset() ← PRR的funN │
└─────────────────────────────────────────────────────────────┘
关键时序规则:
- refresh_fun_vars(false) 在 mon_proc() 之前执行
- auto_fun_vars_reset() 在 mon_proc() 开头执行(用的是上一周期设置的flag)
- is_fun_vars_need_reset_ 在当前周期设置,下周期生效
- 报警消息使用的是本周期(reset前)的funN值
1.4 两套 FunVars 的独立性
代码中存在两套独立的 FunVars 实例:
| 属性 | ExpBase::fun_vars_ | ExpModule::fun_vars_ |
|---|---|---|
| 所属 | ExpBase | ExpModule (exp_mpdule_ptr_) |
| 用途 | 主表达式 (exp_act_, exp_feedback_, exp_result_) 中的状态函数 | PRR 前提表达式 (pre_result) 中的状态函数 |
| 刷新 | refresh_exp_vars_mem/ihd() 中 refresh_fun_vars(false) | ExpModule::update() 中 refresh_fun_vars(false) |
| 重置 | mon_proc() 中 auto_fun_vars_reset() | AlgBase::exec_mon_call() 中 alarm 时 fun_reset() |
| is_exp_alg_=true时 | 由 ExpBase 管理 | 跳过刷新(共享 ExpBase 的 mm_vars) |
| 变量命名 | fun0,fun1... | fun0,fun1... (独立index,可能冲突!) |
⚠️ 潜在问题:当 is_exp_alg_=true 时,ExpModule::update() 跳过其 fun_vars 刷新。但两个 FunVars 的 index 计数器是独立的——如果 PRR 表达式中也有状态函数(如 KeepC(tag1,1)),且 ExpBase::fun_vars_init() 将其加入了 ExpBase::fun_vars_,则两套 FunVars 可能创建同名的 fun0 变量指向不同的表达式。幸好 is_exp_alg_=true 时 ExpModule 这部分逻辑被跳过。
二、上一版 FbStateMachine 设计的缺陷
缺陷 1:未处理变量快照和更新
上一版的 FbStateMachine::update() 设计只做状态转换,但变量操作(快照/更新)与状态转换不可分割:
act_start_done()在 Started 状态执行变量快照(6 类变量初始化)act_done()在每个 InProgress 周期更新累积变量(time, mx, mi, mv2, up, dw)act_timeout()在 Timeout 状态清零累积变量
这些操作必须和状态转换在同一个事务中完成,否则反馈表达式(引用 mv2_tagN, up_tagN 等)会读到过期值。
缺陷 2:未考虑 Idle 状态的 fun_vars 重置
act_start_done() 的 else 分支 !act_started_ && !act_triggered_ 设置了 is_fun_vars_need_reset_ = true。这意味着每个 Idle 周期都会标记 fun_vars 需要重置。上一版设计中 Idle→needReset=true 是正确的,但没有考虑这个重置是通过 act_start_done() 的副作用实现的。
缺陷 3:未处理 Timeout 哨兵值
time_out_ == milliseconds(-32768) 表示无超时限制。此时 act_timeout() 不检查时间,而是检查 mv2_tagN 是否接近 DBL_MAX(防溢出)。这是一个特殊分支,上一版未覆盖。
缺陷 4:未处理 PRR 强制重置
当 get_prr() 返回 false 时(ExpBase 重写版本),反馈状态标志被强制清零:
this->act_started_ = false;
this->act_triggered_ = false;
this->feedback_triggered_ = false;
但 is_fun_vars_need_reset_ 不设置。PRR 恢复后,fun_vars 保持之前的状态。FbStateMachine 需要暴露一个 forceReset() 方法。
缺陷 5:未考虑 Done 状态中 CondBound 的特殊逻辑
act_done() 返回 true 后,mon_proc() 还有后续逻辑:
- 计算
exp_result_->evaluate() - CondBound:检测上下限 → 可能报警
- 非 CondBound:检测布尔值 → 可能报警
- 有自学习:添加统计样本
上一版设计中把这些放在了 FbStateMachine 外部,但没有明确接口。
三、修正后的 FbStateMachine 设计
3.1 接口设计
// 状态枚举
enum class FbState {
Idle, // 空闲
Started, // 刚启动(第一个周期,等待下次检查反馈条件)
InProgress, // 进行中
Done, // 反馈条件满足,动作完成
NotHold, // 不保持(keep_mode下触发条件丢失)
Timeout // 超时
};
// update() 返回结构
struct FbUpdateResult {
FbState state;
bool funVarsNeedReset; // 本周期是否需要标记 fun_vars 重置(下周期生效)
};
class FbStateMachine {
public:
// === 配置(替代 reload_config_exp_feedback 中的状态相关部分)===
void configure(bool keepMode, TimeDur timeout);
// === 核心:每个 mon 周期调用一次 ===
// now: 当前时间
// vars: 变量管理器引用(用于快照/更新变量)
// actTriggered: 前提表达式结果 (exp_act_->evaluate())
FbUpdateResult update(bool actTriggered, TimePoint now, VarManager& vars);
// === 条件检查(在 update 返回 InProgress 后调用)===
// fbCondition: 反馈表达式结果 (exp_feedback_->evaluate())
// 返回 true 表示反馈条件满足,应转为 Done
bool checkFeedback(bool fbCondition, TimePoint now, VarManager& vars);
// === 外部强制重置(PRR=false 时调用)===
void forceReset();
// === 查询 ===
bool isActive() const; // Started 或 InProgress
TimePoint actionStartTime() const;
bool isKeepMode() const;
private:
bool keep_mode_ = false;
TimeDur timeout_ = 10min;
FbState state_ = FbState::Idle;
TimePoint start_time_;
int tag_count_ = 0;
// 防溢出(替代 act_timeout 中 timeout_ == -32768 分支)
bool checkOverflowPrevention(VarManager& vars);
};
3.2 完整状态转换实现
FbUpdateResult FbStateMachine::update(bool actTriggered, TimePoint now,
VarManager& vars) {
FbUpdateResult result;
result.funVarsNeedReset = false;
switch (state_) {
// ── Idle ─────────────────────────────────────────────
case FbState::Idle:
if (actTriggered) {
// → Started:快照变量
vars.snapshotActionStart(now);
state_ = FbState::Started;
start_time_ = now;
result.state = FbState::Started;
// funVarsNeedReset = false(保持状态函数连续性)
} else {
// 保持 Idle,但标记 fun_vars 重置
result.state = FbState::Idle;
result.funVarsNeedReset = true;
// 原因:无活动事件时,KeepC/RiseEdge 不应累积
}
break;
// ── Started(仅在首次触发的那个周期)─────────────────
case FbState::Started:
// 更新累积变量(第一个周期也需要更新)
vars.updateActionVars(now);
if (!actTriggered && keep_mode_) {
// 触发条件在第一个周期就丢失 → NotHold
state_ = FbState::Idle; // 下周期回到 Idle
result.state = FbState::NotHold;
result.funVarsNeedReset = true;
} else if (checkOverflowPrevention(vars)) {
state_ = FbState::Idle;
result.state = FbState::Timeout;
result.funVarsNeedReset = true;
} else {
// 正常进入 InProgress
state_ = FbState::InProgress;
result.state = FbState::Started; // 返回 Started 让调用方跳过反馈检查
// funVarsNeedReset = false
}
break;
// ── InProgress ───────────────────────────────────────
case FbState::InProgress:
// 先更新累积变量
vars.updateActionVars(now);
if (!actTriggered && keep_mode_) {
state_ = FbState::Idle;
result.state = FbState::NotHold;
result.funVarsNeedReset = true;
} else if (checkOverflowPrevention(vars)) {
state_ = FbState::Idle;
result.state = FbState::Timeout;
result.funVarsNeedReset = true;
} else if (timeout_ != TimeDur(-32768) &&
(now - start_time_) > timeout_) {
// 超时
vars.clearActionAccumulators();
state_ = FbState::Idle;
result.state = FbState::Timeout;
result.funVarsNeedReset = true;
}
// 否则保持 InProgress,等待 checkFeedback()
break;
// ── 终端状态(Done/NotHold/Timeout,由 checkFeedback 或上述分支设置)──
default:
// 上周期是终端状态,本周期自动回到 Idle
state_ = FbState::Idle;
result.state = FbState::Idle;
result.funVarsNeedReset = true; // 延续 Idle 的 reset 行为
break;
}
return result;
}
bool FbStateMachine::checkFeedback(bool fbCondition, TimePoint now,
VarManager& vars) {
if (state_ != FbState::InProgress && state_ != FbState::Started) {
return false;
}
if (fbCondition) {
vars.recordActionEnd(now); // etime = now
state_ = FbState::Idle; // 下周期回到 Idle
return true; // → Done
}
return false;
}
void FbStateMachine::forceReset() {
state_ = FbState::Idle;
start_time_ = TimePoint{};
}
bool FbStateMachine::checkOverflowPrevention(VarManager& vars) {
if (timeout_ != TimeDur(-32768)) return false;
return vars.isAccumulatorNearOverflow();
}
3.3 VarManager 需要新增的方法
为配合 FbStateMachine,VarManager 需要新增以下方法:
class VarManager {
public:
// ... 原有方法 ...
// ── 反馈状态机配合 ──
// 动作开始时快照(替代 act_start_done 中的变量初始化)
void snapshotActionStart(TimePoint now);
// 动作进行中更新累积值(替代 act_done 中前半段的变量更新)
void updateActionVars(TimePoint now);
// 动作结束时记录(替代 act_done 中 etime 设置)
void recordActionEnd(TimePoint now);
// 超时时清零累积器(替代 act_timeout 中的清零)
void clearActionAccumulators();
// 累积值是否接近溢出
bool isAccumulatorNearOverflow() const;
};
具体实现直接迁移现有代码:
void VarManager::snapshotActionStart(TimePoint now) {
mm_vars_["stime"] = duration_cast<milliseconds>(now.time_since_epoch()).count();
mm_vars_["time"] = 0;
for (size_t i = 0; i < tag_count_; i++) {
std::string idx = std::to_string(i + 1);
double tag_val = mm_vars_["tag" + idx];
mm_vars_["s" + idx] = tag_val;
mm_vars_["mx_tag" + idx] = tag_val;
mm_vars_["mi_tag" + idx] = tag_val;
mm_vars_["mv2_tag" + idx] = 0;
mm_vars_["mv2_p" + idx] = 0;
double p_val = mm_vars_["p" + idx];
mm_vars_["up_tag" + idx] = (p_val == 0 && tag_val == 1) ? 1.0 : 0.0;
mm_vars_["dw_tag" + idx] = (p_val == 1 && tag_val == 0) ? 1.0 : 0.0;
if (tag_val == 1) mm_vars_["mv2_tag" + idx] = 1;
if (p_val == 1) mm_vars_["mv2_p" + idx] = 1;
}
}
void VarManager::updateActionVars(TimePoint now) {
mm_vars_["time"] = mm_vars_["now"] - mm_vars_["stime"];
for (size_t i = 0; i < tag_count_; i++) {
std::string idx = std::to_string(i + 1);
double tag_val = mm_vars_["tag" + idx];
double p_val = mm_vars_["p" + idx];
mm_vars_["mx_tag" + idx] = std::max(tag_val, mm_vars_["mx_tag" + idx]);
mm_vars_["mi_tag" + idx] = std::min(tag_val, mm_vars_["mi_tag" + idx]);
if (tag_val == 1) mm_vars_["mv2_tag" + idx] += 1;
if (p_val == 1) mm_vars_["mv2_p" + idx] += 1;
mm_vars_["up_tag" + idx] += (p_val == 0 && tag_val == 1) ? 1.0 : 0.0;
mm_vars_["dw_tag" + idx] += (p_val == 1 && tag_val == 0) ? 1.0 : 0.0;
}
}
void VarManager::recordActionEnd(TimePoint now) {
mm_vars_["etime"] = mm_vars_["now"];
}
void VarManager::clearActionAccumulators() {
for (size_t i = 0; i < tag_count_; i++) {
std::string idx = std::to_string(i + 1);
mm_vars_["mv2_tag" + idx] = 0;
mm_vars_["mv2_p" + idx] = 0;
mm_vars_["up_tag" + idx] = 0;
mm_vars_["dw_tag" + idx] = 0;
}
}
bool VarManager::isAccumulatorNearOverflow() const {
for (size_t i = 0; i < tag_count_; i++) {
std::string idx = std::to_string(i + 1);
if (std::abs(mm_vars_.at("mv2_tag" + idx) - DBL_MAX) < 2.0)
return true;
}
return false;
}
3.4 mon_proc() 重构后的完整流程
// FeedbackAlg (Alg 3/4) 的 doMonProc()
AlarmInfo FeedbackAlg::doMonProc() {
// 1. 自动重置 fun_vars(若上周期标记)
vars_.autoResetFunVars();
// 2. 评估前提条件
double result_value = exp_act_->evaluate();
// 3. 如果有上下限的反馈模式(CondBound),先做筛选+统计
if (hasBoundCheck_ && is_learning_) {
bool filter_ok = checkFilter();
if (filter_ok) {
stat_.addSample(result_value);
}
}
// 4. 反馈状态机更新(含变量快照/累积更新)
auto [fbState, needFunReset] = fb_fsm_.update(
static_cast<bool>(result_value), now_time_, vars_);
if (needFunReset) {
vars_.markFunVarsNeedReset();
}
// 5. 根据状态分发
switch (fbState) {
case FbState::Started:
return {}; // 刚启动,等待下周期
case FbState::InProgress: {
// 检查反馈条件
bool fbCond = exp_feedback_->evaluate();
if (fb_fsm_.checkFeedback(fbCond, now_time_, vars_)) {
// Done! 计算结果并判断报警
vars_.markFunVarsNeedReset();
return evaluateResultAndAlarm();
}
return {};
}
case FbState::NotHold:
return {};
case FbState::Timeout:
return buildTimeoutAlarm();
case FbState::Done:
// 由 checkFeedback 内部处理,不应直接到此
return {};
case FbState::Idle:
return {};
}
return {};
}
AlarmInfo FeedbackAlg::evaluateResultAndAlarm() {
double result = exp_result_->evaluate();
query_time_range_.set_left(
query_time_range_.get_right() - milliseconds(int(vars_["time"])));
if (hasBoundCheck_) {
// CondBound: Alg 4
stat_.addSample(result);
if (boundChecker_.isOutOfBounds(result)) {
return buildBoundAlarm(result);
}
} else {
// FeedbackLogic: Alg 3
if (static_cast<bool>(result)) {
return buildLogicAlarm();
}
}
return {};
}
四、修正后的组件与新旧对比
4.1 修正后的组件清单
┌─────────────────┐
│ VarManager │ 变量存储、pv历史移位、hold变量
│ │ 【新增】snapshotActionStart/updateActionVars/
│ │ recordActionEnd/clearActionAccumulators
│ │ autoResetFunVars/markFunVarsNeedReset
├─────────────────┤
│ BoundChecker │ 上下限比较、检测模式、哨兵值处理
├─────────────────┤
│ FbStateMachine │ 反馈状态转换【完整7状态】
│ │ 含变量快照/更新调度、防溢出、keep模式
├─────────────────┤
│ StatCollector │ DAA::STA 统计学习、DB2 持久化
├─────────────────┤
│ FunVarsManager │ 【新增】统一管理两套 FunVars 的刷新与重置
└─────────────────┘
4.2 FunVarsManager(新增组件)
为避免 ExpBase::fun_vars_ 和 ExpModule::fun_vars_ 的混乱,引入统一管理:
class FunVarsManager {
public:
// 注册主表达式的状态函数(替代 ExpBase::fun_vars_.add_exp_str)
void registerMainExp(const std::string& expStr,
std::map<std::string, double>* mm_vars);
// 每个数据刷新周期调用(替代 refresh_fun_vars(false))
void refresh();
// 标记下周期重置(替代 is_fun_vars_need_reset_ = true)
void markNeedReset();
// 执行延迟重置(替代 auto_fun_vars_reset())
void autoReset();
// 立即强制重置(用于 alarm 后 PRR 的 fun_vars)
void forceReset();
private:
StatExp::FunVars fun_vars_;
bool need_reset_ = false;
};
五、与原实现的等价性验证矩阵
| 原有代码路径 | 新设计等价路径 | 验证点 |
|---|---|---|
act_start_done() → true |
fb_fsm_.update() → Started |
变量快照相同 |
act_start_done() → false + setIdleFlag |
fb_fsm_.update() → Idle + funVarsNeedReset=true |
fun_reset 标记相同 |
act_not_hold() → true |
fb_fsm_.update() → NotHold |
keep_mode 检查相同 |
act_done() 变量更新部分 |
fb_fsm_.update() → InProgress 中调用 vars_.updateActionVars() |
mx/mi/mv2/up/dw/time 相同 |
act_done() 反馈条件检查 |
checkFeedback(fbCond) |
exp_feedback_->evaluate() 相同 |
act_done() → true + CondBound |
evaluateResultAndAlarm() hasBoundCheck_=true |
上下限检测+报警相同 |
act_done() → true + 非CondBound |
evaluateResultAndAlarm() hasBoundCheck_=false |
布尔报警相同 |
act_timeout() 正常超时 |
fb_fsm_.update() → Timeout + clearActionAccumulators |
变量清零相同 |
act_timeout() -32768 防溢出 |
checkOverflowPrevention() |
溢出检测相同 |
get_prr() → false 重置 |
fb_fsm_.forceReset() |
状态标志清零相同 |
auto_fun_vars_reset() |
FunVarsManager::autoReset() |
延迟一周期重置相同 |
refresh_fun_vars(false) |
FunVarsManager::refresh() |
funN 变量更新相同 |
exp_mpdule_ptr_->fun_reset() on alarm |
FunVarsManager::forceReset() |
PRR fun_vars 重置相同 |
六、结论
上一版 FbStateMachine 的主要遗漏在于低估了状态转换与变量操作的耦合程度。反馈状态机的本质不是纯状态转换——它是一个状态-变量协同机,在每个状态转换点都需要精确的变量操作(快照、累积、清零)。
修正后的设计将这一协同完整建模:
- VarManager 提供 5 个新方法(snapshot/update/record/clear/overflowCheck)
- FbStateMachine 变为 7 状态(增加 Started 的独立表示和 Idle 的显式处理)
- 引入 FunVarsManager 统一两套 FunVars 的管理
- 通过等价性验证矩阵确保覆盖所有现有路径