eis/eqpalg/algs/FBSTATE_DEEP_ANALYSIS.md

25 KiB
Raw Blame History

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_=trueExpModule::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 需要新增的方法

为配合 FbStateMachineVarManager 需要新增以下方法:

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 的管理
  • 通过等价性验证矩阵确保覆盖所有现有路径