提升状态图 - 使用triggering_event将事件数据传递给状态构造函数

时间:2016-11-16 14:23:20

标签: c++ boost boost-statechart

我希望能够将事件中的数据传递给由于转换而构建的状态。

从2009年的this帖子和2010年的this个帖子中建议使用triggering_event()。但是,我无法在任何地方找到演示如何使用它的示例。

有人可以帮忙吗?

1 个答案:

答案 0 :(得分:0)

以下示例是基于所描述的电机状态机here的Boost状态图实现。我没有使用custom_reaction来提高EvSetSpeed事件的速度(如此处的其他状态),而是修改它以在Start状态的构造函数中调用triggering_event()。

注意事项:

  1. boost状态图here的旧文档指出:" triggering_event的调用者需要进行类型检查或转换返回值",因为triggering_event返回一个const event_base指针。
  2. 在构造期间调用triggering_event需要调用状态来自state,而不是simple_state。这是因为triggering_event正在从构造函数访问状态机。为了它的价值,我还注意到文档说明使用triggering_event通常意味着状态机设计存在问题,应该避免使用。
  3. 也许更熟悉boost状态图的人可以对此发表评论,但我发现如果我希望它转换为Running(我的反应列表状态),我必须在Start状态构造函数中重新发布触发事件。
  4. 最后,值得一提的是,尝试在状态机的初始状态的构造函数中调用triggering_event将导致返回null值。这是因为初始状态是在启动调用期间构建的,其中没有触发事件。

    #include <boost/statechart/state_machine.hpp>
    #include <boost/statechart/simple_state.hpp>
    #include <boost/statechart/state.hpp>
    #include <boost/statechart/event.hpp>
    #include <boost/statechart/in_state_reaction.hpp>
    #include <boost/statechart/transition.hpp>
    #include <boost/mpl/list.hpp>
    #include <boost/statechart/custom_reaction.hpp>
    #include <iostream>
    
    namespace mpl = boost::mpl;
    namespace sc = boost::statechart;
    
    class EvHalt : public sc::event< EvHalt > {};
    class EvSetSpeed : public sc::event< EvSetSpeed >
    {
    public:
        EvSetSpeed(int speed) : _speed(speed) {}
        int getSpeed() const { return _speed; };
    
    private:
        int _speed;
    };
    
    class Idle;
    class Start;
    class Stop;
    class Running;
    
    class Motor : public sc::state_machine<Motor, Idle> {};
    
    class Idle : public sc::simple_state<Idle, Motor>
    {
    public:
        typedef mpl::list<
            sc::transition< EvSetSpeed, Start >,
            sc::transition< EvHalt, Idle > > reactions;
    
        Idle() { std::cout << "Entering Idle State" << std::endl; };
        ~Idle() { std::cout << "Exiting Idle State" << std::endl; };
    };
    
    class Start : public sc::state<Start, Motor>
    {
    public:
        typedef mpl::list<
            sc::transition< EvSetSpeed, Running >,
            sc::transition< EvHalt, Stop  > > reactions;
    
        Start(my_context ctx) : _speed(0), my_base(ctx)
        { 
            auto trigEvt = dynamic_cast<const EvSetSpeed*>(triggering_event());
            if (trigEvt) // returns true if trigEvt is indeed an EvSetSpeed event
            {
                _speed = trigEvt->getSpeed();
                post_event(*trigEvt);
                std::cout << "Entering Start State at speed " << _speed << std::endl;
            }
            else
                std::cout << "Entering Start State. Triggering Event is NOT EvSetSpeed." << std::endl;
        };
        ~Start() { std::cout << "Exiting Start State" << std::endl; };
    private:
        int _speed;  // Unused in this example.
    };
    
    class Running : public sc::simple_state<Running, Motor>
    {
    public:
        typedef mpl::list<
            sc::custom_reaction< EvSetSpeed >,
            sc::custom_reaction< EvHalt  > > reactions;
    
        sc::result react(const EvSetSpeed& ev)
        {
            std::cout << "In Running Mode: Set Speed to " << ev.getSpeed() << std::endl;
            return discard_event();
        }
        sc::result react(const EvHalt& ev)
        {
            std::cout << "In Running Mode: Halting Motor" << std::endl;
            post_event(ev);
            return transit<Stop>();
        }
    
        Running() { std::cout << "Entering Running State" << std::endl; };
        ~Running() { std::cout << "Exiting Running State" << std::endl; };
    };
    
    class Stop : public sc::simple_state<Stop, Motor>
    {
    public:
        typedef sc::transition< EvHalt, Idle > reactions;
    
        Stop() { std::cout << "Entering Stop State" << std::endl; };
        ~Stop() { std::cout << "Exiting Stop State" << std::endl; };
    };
    
    int main()
    {
        Motor myMotor;
        myMotor.initiate();
        myMotor.process_event(EvSetSpeed(100));
        myMotor.process_event(EvSetSpeed(200));
        myMotor.process_event(EvHalt());
        myMotor.process_event(EvSetSpeed(300));
        myMotor.process_event(EvHalt());
        myMotor.process_event(EvHalt());
        return 0;
    }