eis/eqpalg/algs/FBSTATE_DEEP_ANALYSIS.md

574 lines
25 KiB
Markdown
Raw Permalink 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.

# 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 需要新增的方法
为配合 FbStateMachineVarManager 需要新增以下方法:
```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<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() 重构后的完整流程
```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<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_ 的混乱,引入统一管理:
```cpp
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 的管理
- 通过等价性验证矩阵确保覆盖所有现有路径