msm状态机,也可以被称作是流程图,实现起来的方法有很多,现在我在这里说一下Boost.msm的实现方式。
Foa,你需要一张流程图,这张流程图决定了你整个程序的动作流程,借用一张boost官方例子
这张图里面有几个点需要注意的,也是这张图的关键点
- 五个状态,定义了五个流程
- 状态之间的连接线,定义了从一个状态转换到另一个状态的方式
- 初始的状态位
从第一项开始说,首先要定义一个总的状态机类,这个类继承于boost::msm::front::state_machine_def
class player_ : public boost::msm::front::state_machine_def<player_> { public: //code... }
这个状态机的定义都放在这个类里面
在其中,定义了与这个状态机有关的组件,其中就包括了状态本体的组件
class Open : public boost::msm::front::state<> { public: template <class Event, class FSM> void on_entry(const Event&, FSM&) { std::cout << "entering: Open" << std::endl; } template <class Event, class FSM> void on_exit(const Event&, FSM&) { std::cout << "leaving: Open" << std::endl; } };
本体里面其实只有两个函数,一个定义进入状态的函数,一个定义退出状态的函数。
其次要定义的就是状态与状态直接的连接函数,这个其实是作为一个list写在代码中的
struct transition_table : mpl::vector< // Start Event Target Action Guard // +---------+------------+-----------+---------------------------+----------------------------+ a_row< Stopped , play , Playing , &player_::start_playback >, a_row< Stopped , open_close , Open , &player_::open_drawer >, _row< Stopped , stop , Stopped >, // +---------+------------+-----------+---------------------------+----------------------------+ a_row< Open , open_close , Empty , &player_::close_drawer >, // +---------+------------+-----------+---------------------------+----------------------------+ a_row< Empty , open_close , Open , &player_::open_drawer >, row< Empty , cd_detected, Stopped , &player_::store_cd_info , &player_::good_disk_format >, row< Empty , cd_detected, Playing , &player_::store_cd_info , &player_::auto_start >, // +---------+------------+-----------+---------------------------+----------------------------+ a_row< Playing , stop , Stopped , &player_::stop_playback >, a_row< Playing , pause , Paused , &player_::pause_playback >, a_row< Playing , open_close , Open , &player_::stop_and_open >, // +---------+------------+-----------+---------------------------+----------------------------+ a_row< Paused , end_pause , Playing , &player_::resume_playback >, a_row< Paused , stop , Stopped , &player_::stop_playback >, a_row< Paused , open_close , Open , &player_::stop_and_open > // +---------+------------+-----------+---------------------------+----------------------------+ > {};
这个vector有五个参数,Start、Event、Target、Action、Guard,Start代表箭头的起始位,也就是status当前所在的位置;Event代表的是一个信号,当状态机收到一个信号的时候,就会在这个vector里面搜索以当前status为起始点时匹配这个信号的row;而收到Event信号以后,就会执行这一个row对应的Action,Action里面一般定义了一个函数的指针;执行完了Action之后,status就会变为这一个row对应的Target。Guard参数是一个特殊的参数,它是一个返回值为bool的函数,这个函数决定了一点,就是这一行row应不应该执行。
梳理一下执行的具体步骤,分为这几步:
- 当状态机处于某一状态时,收到一个类型为Event的触发信号
- 执行Guard(若有),如果返回结果为false,中断后续步骤,保留现有状态
- 离开现有状态,触发on_exit
- 执行Action(若有)
- 进入Target状态,触发on_entry
然后就是Guard和Action函数的组成了,这两个函数的输入都是一致的,都是由Event规定的结构体来组成的,区别就在于返回值,Action函数是一个无返回的void函数,而Guard则是一个返回值为bool的判断函数,举个栗子
struct cd_detected { cd_detected(std::string name, DiskTypeEnum diskType) : name(name), disc_type(diskType) { } std::string name; DiskTypeEnum disc_type; }; void store_cd_info(const cd_detected&) { std::cout << "player::store_cd_info\n"; }
bool good_disk_format(const cd_detected& evt) { if (evt.disc_type != DISK_CD) { std::cout << "wrong disk, sorry" << std::endl; return false; } return true; }
上面一个为Action函数,下面的为Guard函数,它们的输入都为规定好的Event结构体。
最后是初始状态的定义函数,如果不定义初始状态,会导致启动时找不到状态而报错
using initial_state = Empty;
这样就基本结束了,你可以写一个test函数来测试这个状态机
void pstate(const player& p) { std::cout << " -> " << state_names[p.current_state()[0]] << std::endl; } void test() { player p; // needed to start the highest-level SM. This will call on_entry and mark the start of the SM p.start(); // go to Open, call on_exit on Empty, then action, then on_entry on Open p.process_event(open_close()); pstate(p); p.process_event(open_close()); pstate(p); // will be rejected, wrong disk type p.process_event(cd_detected("louie, louie", DISK_DVD)); pstate(p); p.process_event(cd_detected("louie, louie", DISK_CD)); pstate(p); p.process_event(play()); // at this point, Play is active p.process_event(pause()); pstate(p); // go back to Playing p.process_event(end_pause()); pstate(p); p.process_event(pause()); pstate(p); p.process_event(stop()); pstate(p); // event leading to the same state // no action method called as it is not present in the transition table p.process_event(stop()); pstate(p); } int main() { test(); return 0; }
TODO:错误处理
Be First to Comment