198 lines
5.2 KiB
C++
198 lines
5.2 KiB
C++
|
|
// eqpalg/test/test_fb_state_machine.cc
|
|||
|
|
#include "test_harness.h"
|
|||
|
|
#include <eqpalg/utility/fb_state_machine.h>
|
|||
|
|
#include <chrono>
|
|||
|
|
#include <map>
|
|||
|
|
#include <string>
|
|||
|
|
|
|||
|
|
using namespace std::chrono;
|
|||
|
|
|
|||
|
|
// 测试辅助:构建最小环境
|
|||
|
|
struct FbTestEnv {
|
|||
|
|
std::map<std::string, double> vars;
|
|||
|
|
FbStateMachine fsm;
|
|||
|
|
TimePoint now;
|
|||
|
|
|
|||
|
|
FbTestEnv(size_t tag_count = 2) {
|
|||
|
|
// 模拟已初始化的变量状态
|
|||
|
|
for (size_t i = 0; i < tag_count; i++) {
|
|||
|
|
std::string idx = std::to_string(i + 1);
|
|||
|
|
vars["tag" + idx] = 0.0;
|
|||
|
|
vars["p" + idx] = 0.0;
|
|||
|
|
vars["now"] = 0;
|
|||
|
|
}
|
|||
|
|
now = std::chrono::system_clock::now();
|
|||
|
|
vars["now"] = duration_cast<milliseconds>(now.time_since_epoch()).count();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void setTag(int n, double val) {
|
|||
|
|
std::string idx = std::to_string(n);
|
|||
|
|
vars["p" + idx] = vars["tag" + idx]; // p = old tag
|
|||
|
|
vars["tag" + idx] = val;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void advanceTime(int ms) {
|
|||
|
|
now += milliseconds(ms);
|
|||
|
|
vars["now"] = duration_cast<milliseconds>(now.time_since_epoch()).count();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
FbUpdateResult step(bool trigger) {
|
|||
|
|
return fsm.update(trigger, now, vars, 2);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ==================== 初始状态 ====================
|
|||
|
|
|
|||
|
|
TEST(initial_state_is_idle) {
|
|||
|
|
FbTestEnv env;
|
|||
|
|
CHECK(env.fsm.currentState() == FbState::Idle);
|
|||
|
|
CHECK_EQ(env.fsm.isActive(), false);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== Idle → Started ====================
|
|||
|
|
|
|||
|
|
TEST(idle_to_started_when_triggered) {
|
|||
|
|
FbTestEnv env;
|
|||
|
|
auto result = env.step(true);
|
|||
|
|
CHECK(result.state == FbState::Started);
|
|||
|
|
CHECK_EQ(env.fsm.isActive(), true);
|
|||
|
|
CHECK_EQ(result.funVarsNeedReset, false);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TEST(idle_stays_idle_when_not_triggered) {
|
|||
|
|
FbTestEnv env;
|
|||
|
|
auto result = env.step(false);
|
|||
|
|
CHECK(result.state == FbState::Idle);
|
|||
|
|
CHECK_EQ(result.funVarsNeedReset, true); // 空闲时重置 fun_vars
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== Started → InProgress → Done ====================
|
|||
|
|
|
|||
|
|
TEST(started_proceeds_to_done_via_feedback) {
|
|||
|
|
FbTestEnv env;
|
|||
|
|
|
|||
|
|
// 首次触发 → Started
|
|||
|
|
auto r1 = env.step(true);
|
|||
|
|
CHECK(r1.state == FbState::Started);
|
|||
|
|
|
|||
|
|
// Started 自动推进到 InProgress(第二次 update)
|
|||
|
|
auto r2 = env.step(true);
|
|||
|
|
CHECK(r2.state == FbState::InProgress || r2.state == FbState::Started);
|
|||
|
|
// 如果第二次还在 Started,第三次一定到 InProgress
|
|||
|
|
if (r2.state == FbState::Started) {
|
|||
|
|
r2 = env.step(true);
|
|||
|
|
}
|
|||
|
|
CHECK(r2.state == FbState::InProgress);
|
|||
|
|
|
|||
|
|
// 反馈条件满足 → Done
|
|||
|
|
bool done = env.fsm.checkFeedback(true, env.now, env.vars);
|
|||
|
|
CHECK_EQ(done, true);
|
|||
|
|
CHECK(env.fsm.currentState() == FbState::Idle); // 自动回到 Idle
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== NotHold ====================
|
|||
|
|
|
|||
|
|
TEST(keep_mode_not_hold_when_trigger_lost) {
|
|||
|
|
FbTestEnv env;
|
|||
|
|
env.fsm.configure(true, milliseconds(600000)); // keep_mode=true
|
|||
|
|
|
|||
|
|
// 触发 → Started
|
|||
|
|
env.step(true);
|
|||
|
|
// → InProgress
|
|||
|
|
env.step(true);
|
|||
|
|
// 触发丢失 → NotHold
|
|||
|
|
auto r = env.step(false);
|
|||
|
|
CHECK(r.state == FbState::NotHold);
|
|||
|
|
CHECK_EQ(r.funVarsNeedReset, true);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== Timeout ====================
|
|||
|
|
|
|||
|
|
TEST(timeout_when_exceeds_limit) {
|
|||
|
|
FbTestEnv env;
|
|||
|
|
env.fsm.configure(false, milliseconds(100)); // 100ms timeout
|
|||
|
|
|
|||
|
|
// 触发 → Started → InProgress
|
|||
|
|
env.step(true);
|
|||
|
|
env.step(true);
|
|||
|
|
|
|||
|
|
// 超时
|
|||
|
|
env.advanceTime(200);
|
|||
|
|
auto r = env.step(true);
|
|||
|
|
CHECK(r.state == FbState::Timeout);
|
|||
|
|
CHECK_EQ(r.funVarsNeedReset, true);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== 变量快照 ====================
|
|||
|
|
|
|||
|
|
TEST(snapshot_on_action_start) {
|
|||
|
|
FbTestEnv env;
|
|||
|
|
env.setTag(1, 3.0);
|
|||
|
|
env.setTag(2, 7.0);
|
|||
|
|
|
|||
|
|
env.step(true); // → Started(快照发生)
|
|||
|
|
|
|||
|
|
CHECK_FLOAT_EQ(env.vars["s1"], 3.0, 0.001);
|
|||
|
|
CHECK_FLOAT_EQ(env.vars["s2"], 7.0, 0.001);
|
|||
|
|
CHECK_FLOAT_EQ(env.vars["time"], 0.0, 0.001);
|
|||
|
|
CHECK_FLOAT_EQ(env.vars["mv2_tag1"], 0.0, 0.001);
|
|||
|
|
// stime 应该被设置
|
|||
|
|
CHECK(env.vars["stime"] > 0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== 变量累积 ====================
|
|||
|
|
|
|||
|
|
TEST(action_vars_accumulate_in_progress) {
|
|||
|
|
FbTestEnv env;
|
|||
|
|
env.setTag(1, 1.0); // tag1=1
|
|||
|
|
env.setTag(2, 0.0);
|
|||
|
|
|
|||
|
|
env.step(true); // Started
|
|||
|
|
env.step(true); // InProgress (累积发生)
|
|||
|
|
|
|||
|
|
CHECK_EQ(env.vars["mv2_tag1"] >= 1.0, true); // tag1==1 被计数
|
|||
|
|
CHECK_FLOAT_EQ(env.vars["mx_tag1"], 1.0, 0.001);
|
|||
|
|
CHECK_FLOAT_EQ(env.vars["mi_tag2"], 0.0, 0.001);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== forceReset ====================
|
|||
|
|
|
|||
|
|
TEST(force_reset_returns_to_idle) {
|
|||
|
|
FbTestEnv env;
|
|||
|
|
env.step(true); // Started
|
|||
|
|
CHECK_EQ(env.fsm.isActive(), true);
|
|||
|
|
|
|||
|
|
env.fsm.forceReset();
|
|||
|
|
CHECK(env.fsm.currentState() == FbState::Idle);
|
|||
|
|
CHECK_EQ(env.fsm.isActive(), false);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== 非 keep 模式下触发丢失 ====================
|
|||
|
|
|
|||
|
|
TEST(no_keep_mode_trigger_loss_stays_in_progress) {
|
|||
|
|
FbTestEnv env;
|
|||
|
|
env.fsm.configure(false, milliseconds(600000)); // keep_mode=false
|
|||
|
|
|
|||
|
|
env.step(true); // Started
|
|||
|
|
env.step(true); // InProgress
|
|||
|
|
auto r = env.step(false); // 触发丢失但 keep_mode=false
|
|||
|
|
CHECK(r.state == FbState::InProgress); // 不退出
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== 终端状态自动回归 Idle ====================
|
|||
|
|
|
|||
|
|
TEST(terminal_state_returns_to_idle_next_cycle) {
|
|||
|
|
FbTestEnv env;
|
|||
|
|
env.fsm.configure(false, milliseconds(10));
|
|||
|
|
|
|||
|
|
env.step(true);
|
|||
|
|
env.step(true);
|
|||
|
|
env.advanceTime(20);
|
|||
|
|
auto r = env.step(true);
|
|||
|
|
CHECK(r.state == FbState::Timeout);
|
|||
|
|
|
|||
|
|
// 下一周期自动回到 Idle
|
|||
|
|
auto r2 = env.step(false);
|
|||
|
|
CHECK(r2.state == FbState::Idle);
|
|||
|
|
}
|