1

I try to use boost::MSM to implement a simple state machine for testing purpose. There are several events which have to be processed in the right order, so i defer the other events, which are currently not allowed. I try to defer the events in the transition table, to have one place to look for it, despite having all states to look, what events they defer.

I tried to change the transition rows to change their priorities, but it didnt help. If i unkomment the anonymous transition from live_got_image to live_wait_for_image, it works as expected, but i want to transition automaticly to repeatedly enter the states live_wait_for_image/live_got_image until the ev_stop_live get sent.

I expect the following output:

entering: fsm_master
entering: not_ready
no transition from state 0 on event struct `anonymous namespace'::ev_stop_live
leaving: not_ready
starting: live_wait_for_image
leaving: live_wait_for_image
starting: live_got_image
leaving: live_got_image
entering: not_ready

but actually i get the following output:

entering: fsm_master
entering: not_ready
no transition from state 0 on event struct `anonymous namespace'::ev_stop_live
leaving: not_ready
starting: live_wait_for_image
leaving: live_wait_for_image
starting: live_got_image
leaving: live_got_image
starting: live_wait_for_image   

I use boost 1.57.0 and MSVC-2013 (VS-12).

Could anybody get me a useful hint?

Georg

Here is the code:

#include <iostream>
// back-end
#include <boost/msm/back/state_machine.hpp>
//front-end
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>

namespace msm = boost::msm;
namespace mpl = boost::mpl;

namespace 
{
    using namespace boost::msm::front;

    // events
    struct ev_start_stop {};
    struct ev_start_live {};
    struct ev_stop_live {};
    struct ev_learn {};
    struct ev_load {};

    struct ev_got_image { int payload;  };

    // front end: define the FSM structure
    struct fsm_master_ : public msm::front::state_machine_def<fsm_master_> {
        typedef int activate_deferred_events;

        template <class Event, class FSM>
        void on_entry(Event const&, FSM&)
        {
            std::cout << "entering: fsm_master" << std::endl;
        }
        template <class Event, class FSM>
        void on_exit(Event const&, FSM&)
        {
            std::cout << "leaving: fsm_master" << std::endl;
        }

        // the fsm states which are not sub state machines
        struct not_ready : public msm::front::state<> {
            template <class Event, class FSM>
            void on_entry(Event const&, FSM&) { std::cout << "entering: not_ready" << std::endl; }
            template <class Event, class FSM>
            void on_exit(Event const&, FSM&) { std::cout << "leaving: not_ready" << std::endl; }
        };

        // The list of FSM states
        struct live_wait_for_image : public msm::front::state<>
        {
            template <class Event, class FSM>
            void on_entry(Event const&, FSM&) { std::cout << "starting: live_wait_for_image" << std::endl; }
            template <class Event, class FSM>
            void on_exit(Event const&, FSM&) { std::cout << "leaving: live_wait_for_image" << std::endl; }
        };

        struct live_got_image : public msm::front::state<>
        {
            // here we got the image and we can check, if we need to react on an external event
            // otherwise transit to wait_for_image and fetch the next image
            template <class Event, class FSM>
            void on_entry(Event const&, FSM&) { std::cout << "starting: live_got_image" << std::endl; }
            template <class Event, class FSM>
            void on_exit(Event const&, FSM&) { std::cout << "leaving: live_got_image" << std::endl; }
        };

        // ---------------------------------------------------
        // initial state: fsm_master_
        typedef not_ready   initial_state;

        typedef fsm_master_ l;
        struct transition_table : mpl::vector<
            //      Start           Event            Next             Action              Guard
            //    +---------------------+----------------+-------------------+-------------------+----------------+
            _row < not_ready,           ev_start_live,   live_wait_for_image                                       >,

            _row < live_wait_for_image, ev_got_image,    live_got_image                                            >,
             Row < live_wait_for_image, ev_stop_live,    none                , Defer             , none            >,

            _row < live_got_image,      none,            live_wait_for_image                                       >,
            _row < live_got_image,      ev_stop_live,    not_ready                                                 >
            //    +---------------------+----------------+-------------------+-------------------+----------------+
        > {};

        // Replaces the default no-transition response.
        template <class FSM, class Event>
        void no_transition(Event const& e, FSM&, int state)
        {
            std::cout << "no transition from state " << state
                << " on event " << typeid(e).name() << std::endl;
        }

    };

    // back-end: fsm_master
    typedef msm::back::state_machine<fsm_master_> fsm_master;
}

void test() {
    fsm_master s;
    s.start();

    s.process_event(ev_stop_live()); // this gets no transitioned

    s.process_event(ev_start_live()); // this one enters live
    s.process_event(ev_stop_live()); // this gets defered   
    s.process_event(ev_got_image()); // this one enters got_image and the stop live should get processed. 

    // now we should be in not ready again, but we are still in live_wait_for_image

}
int main()
{
    test();
    return 0;
}
4

1 に答える 1

1

Christophe からのメーリング リストに次のような回答がありました。


やあ、

boost::MSM を使用して、テスト目的で単純なステート マシンを実装しようとしています。正しい順序で処理する必要があるイベントがいくつかあるため、現在許可されていない他のイベントを延期します。遷移テーブルのイベントを延期して、すべての状態を確認する必要があるにもかかわらず、イベントを延期する場所を 1 か所にします。

トランジション行を変更して優先度を変更しようとしましたが、役に立ちませんでした。live_got_image から live_wait_for_image への匿名遷移のコメントを外すと、期待どおりに動作しますが、自動的に遷移して、ev_stop_live が送信されるまで状態 live_wait_for_image/live_got_image を繰り返し入力したいと考えています。

うまくいかないのではないかと心配しています。MSM が準拠しようとしている UML 標準によると、匿名遷移は遅延イベントよりも優先度が高くなります。キューに遅延イベントがある場合、匿名遷移でガードを使用して無効にすることができます。次に例を示します。

struct OnlyIfNoDeferred
{
    template<class Event, class Fsm, class SourceState, class TargetState>
    bool operator()(Event const &, Fsm & aFsm, SourceState &, TargetState &)
    {
        return aFsm.get_deferred_queue().empty();
    }
};

匿名トランジションについて話しているので、「繰り返し」と読みましたが、別の匿名トランジションを追加しないように注意してください。そうしないと、匿名トランジションの無限ループに陥る可能性があります。

HTH、

クリストフ

于 2015-03-14T10:28:25.717 に答える