# 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` 重写版本),反馈状态标志被强制清零: ```cpp 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 接口设计 ```cpp // 状态枚举 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 完整状态转换实现 ```cpp 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 需要新增以下方法: ```cpp 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; }; ``` 具体实现直接迁移现有代码: ```cpp void VarManager::snapshotActionStart(TimePoint now) { mm_vars_["stime"] = duration_cast(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() 重构后的完整流程 ```cpp // 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(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(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_ 的混乱,引入统一管理: ```cpp class FunVarsManager { public: // 注册主表达式的状态函数(替代 ExpBase::fun_vars_.add_exp_str) void registerMainExp(const std::string& expStr, std::map* 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 的管理 - 通过等价性验证矩阵确保覆盖所有现有路径