msm状态机,也可以被称作是流程图,实现起来的方法有很多,现在我在这里说一下Boost.msm的实现方式。
Foa,你需要一张流程图,这张流程图决定了你整个程序的动作流程,借用一张boost官方例子
这张图里面有几个点需要注意的,也是这张图的关键点
五个状态,定义了五个流程
状态之间的连接线,定义了从一个状态转换到另一个状态的方式
初始的状态位
从第一项开始说,首先要定义一个总的状态机类,这个类继承于boost::msm::front::state_machine_def
class player_ : public boost::msm::front::state_machine_def < player_ >
class player_ : public boost::msm::front::state_machine_def<player_>
{
public:
//code...
}
class player_ : public boost::msm::front::state_machine_def<player_>
{
public:
//code...
}
这个状态机的定义都放在这个类里面
在其中,定义了与这个状态机有关的组件,其中就包括了状态本体的组件
class Open : public boost::msm::front::state <>
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; }
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; }
};
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 >
// +---------+------------+-----------+---------------------------+----------------------------+
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 >
// +---------+------------+-----------+---------------------------+----------------------------+
> {};
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的判断函数,举个栗子
cd_detected ( std::string name, DiskTypeEnum diskType )
void store_cd_info ( const cd_detected& ) { std::cout << "player::store_cd_info\n" ; }
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"; }
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;
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;
}
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;
using initial_state = Empty;
using initial_state = Empty;
这样就基本结束了,你可以写一个test函数来测试这个状态机
void pstate ( const player& p )
std::cout << " -> " << state_names [ p. current_state ()[ 0 ]] << std::endl;
// needed to start the highest-level SM. This will call on_entry and mark the start of the SM
// go to Open, call on_exit on Empty, then action, then on_entry on Open
p. process_event ( open_close ()) ;
p. process_event ( open_close ()) ;
// will be rejected, wrong disk type
p. process_event ( cd_detected ( "louie, louie" , DISK_DVD )) ;
p. process_event ( cd_detected ( "louie, louie" , DISK_CD )) ;
// at this point, Play is active
p. process_event ( pause ()) ;
p. process_event ( end_pause ()) ;
p. process_event ( pause ()) ;
// event leading to the same state
// no action method called as it is not present in the transition table
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;
}
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