[Boost-users] Boost MSM: No transition from submachine to external state

262 views
Skip to first unread message

stetkas

unread,
Jan 16, 2012, 1:37:20 PM1/16/12
to boost...@lists.boost.org

Using the following example as a template:

http://svn.boost.org/svn/boost/trunk/libs/msm/doc/HTML/examples/CompositeTutorialWithEumlTable.cpp

I am trying to create a state machine that consists of an alarm state and a composite state (submachine). When an alarm event is posted while in the composite state, the state machine is supposed to transition to the Alarm state; however, the machine complains that no transition from the composite state on the alarm event exists. This is similar to the example where the player is in playing and a stop event is received.

Below is a dumbed-down version of my state machine:

Here is the implementation:


// File: StateMachine.cpp

#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/euml/euml.hpp>

namespace msm = boost::msm;
namespace mpl = boost::mpl;
using namespace std;


namespace
{
	static bool s_deviceInited = false;

	struct setDeviceInited {
		template<class Fsm, class Evt, class SourceState, class TargetState>
		void operator()(Evt const& evt, Fsm& fsm, SourceState& ss,
				TargetState& ts) {
			s_deviceInited = true;
			std::cout << "s_deviceInited=" << s_deviceInited << std::endl;
		}
	};

	struct resetDeviceInited {
		template<class Fsm, class Evt, class SourceState, class TargetState>
		void operator()(Evt const& evt, Fsm& fsm, SourceState& ss,
				TargetState& ts) {
			s_deviceInited = true;
			std::cout << "s_deviceInited=" << s_deviceInited << std::endl;
		}
	};

	struct deviceInited {
		template<class Fsm, class Evt, class SourceState, class TargetState>
		bool operator()(Evt const& evt, Fsm& fsm, SourceState& ss,
				TargetState& ts) {
			std::cout << "s_deviceInited=" << s_deviceInited << std::endl;
			return s_deviceInited;
		}
	};

	// events
    struct EvDeviceInited : msm::front::euml::euml_event<EvDeviceInited>{};
    struct EvAlarm : msm::front::euml::euml_event<EvAlarm>{};
    struct EvAlarmCleared : msm::front::euml::euml_event<EvAlarmCleared>{};

    // define some dummy instances for use in the transition table

    EvDeviceInited evDeviceInited;
    EvAlarm evAlarm;
    EvAlarmCleared evAlarmCleared;

    // The list of FSM states

    struct Alarm_TImpl : public msm::front::state<> , public msm::front::euml::euml_state<Alarm_TImpl>
    {
        // every (optional) entry/exit methods get the event passed.
        template <class Event,class FSM>
        void on_entry(Event const&,FSM& ) {std::cout << "entering: Alarm" << std::endl;}
        template <class Event,class FSM>
        void on_exit(Event const&,FSM& ) {std::cout << "leaving: Alarm" << std::endl;}
    };

    struct Operate_TImpl : public msm::front::state<> , public msm::front::euml::euml_state<Operate_TImpl> 
    {	 
        template <class Event,class FSM>
        void on_entry(Event const& ,FSM&) {std::cout << "entering: Operate" << std::endl;}
        template <class Event,class FSM>
        void on_exit(Event const&,FSM& ) {std::cout << "leaving: Operate" << std::endl;}
    };
    
    struct Initialize_TImpl : public msm::front::state<> , public msm::front::euml::euml_state<Initialize_TImpl> 
    {	 
        template <class Event,class FSM>
        void on_entry(Event const& ,FSM&) {std::cout << "entering: Initialize" << std::endl;}
        template <class Event,class FSM>
        void on_exit(Event const&,FSM& ) {std::cout << "leaving: Initialize" << std::endl;}
    };

	Initialize_TImpl initialize;
    Operate_TImpl operate;

    // Normal Submachine front-end
    struct Normal_T : public msm::front::state_machine_def<Normal_T>
    {
        template <class Event,class FSM>
        void on_entry(Event const& ,FSM&) {std::cout << "entering: Normal" << std::endl;}
        template <class Event,class FSM>
        void on_exit(Event const&,FSM& ) {std::cout << "leaving: Normal" << std::endl;}
        
        typedef Initialize_TImpl initial_state;
        
    	// Playing has a transition table 
        BOOST_MSM_EUML_DECLARE_TRANSITION_TABLE((
        //  +------------------------------------------------------------------------------+
            operate == initialize [deviceInited()],
            
            initialize == initialize + evDeviceInited / setDeviceInited()
        //  +------------------------------------------------------------------------------+
        ),transition_table )

    };
    
    // Normal Submachine back-end
    typedef boost::msm::back::state_machine<Normal_T> Normal_THelper;
    struct Normal_TImpl : public Normal_THelper,
                          public msm::front::euml::euml_state<Normal_THelper>
    {
    };


    //to make the transition table more readable
    Alarm_TImpl alarm;
    Normal_TImpl normal;

    // front-end: define the FSM structure 
    struct StateMachine_T : public msm::front::state_machine_def<StateMachine_T>
    {

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

        // the initial state of the player SM. Must be defined
        typedef Normal_TImpl initial_state;

        BOOST_MSM_EUML_DECLARE_TRANSITION_TABLE((
        		normal + evAlarm / resetDeviceInited() == alarm,
        		alarm + evAlarmCleared == normal
          ),transition_table)

        // 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;
        }
    };
    
    // Link the back-end
    typedef msm::back::state_machine<StateMachine_T> StateMachine;

    void test()
    {        
    	StateMachine p;

        p.start(); 

        p.process_event(evAlarm);
        p.process_event(evAlarmCleared);
        
        p.process_event(evDeviceInited);
        p.process_event(evAlarm);
        p.process_event(evAlarmCleared);

        p.process_event(evDeviceInited);
        p.process_event(evAlarm);
        p.process_event(evAlarmCleared);

        p.process_event(evDeviceInited);
        p.process_event(evAlarm);
        p.process_event(evAlarmCleared);

        p.stop();
    }
}

int gpp_main()
{
    test();
    return 0;
}

Here is the output:

StateMachine
entering: Normal
entering: Initialize
s_deviceInited=0
no transition from state 2 on event N12_GLOBAL__N_17EvAlarmE
no transition from state 2 on event N12_GLOBAL__N_114EvAlarmClearedE
leaving: Initialize
s_deviceInited=1
entering: Initialize
s_deviceInited=1
leaving: Initialize
entering: Operate
no transition from state 2 on event N12_GLOBAL__N_17EvAlarmE
no transition from state 2 on event N12_GLOBAL__N_114EvAlarmClearedE
no transition from state 2 on event N12_GLOBAL__N_114EvDeviceInitedE
no transition from state 2 on event N12_GLOBAL__N_17EvAlarmE
no transition from state 2 on event N12_GLOBAL__N_114EvAlarmClearedE
no transition from state 2 on event N12_GLOBAL__N_114EvDeviceInitedE
no transition from state 2 on event N12_GLOBAL__N_17EvAlarmE
no transition from state 2 on event N12_GLOBAL__N_114EvAlarmClearedE
leaving: Operate
leaving: Normal
leaving: StateMachine

See anything that could be causing my problem?



View this message in context: Boost MSM: No transition from submachine to external state
Sent from the Boost - Users mailing list archive at Nabble.com.

Christophe Henry

unread,
Jan 17, 2012, 3:58:52 PM1/17/12
to boost...@lists.boost.org
Hi,
 
this looks like an old problem (definition of composites in eUML) which I thought was long solved, but well, seems like no :(
I'll need to have a look at it. In the meantime, please use the functor front-end, which is safer. I attach a working version with this front-end to get you going until I solve this.
 
Cheers,
Christophe
 
 
CompositeNoEuml.cpp

Christophe Henry

unread,
Jan 17, 2012, 4:38:16 PM1/17/12
to boost...@lists.boost.org
----- Original Message -----
From: stetkas
Newsgroups: gmane.comp.lib.boost.user
Sent: Monday, January 16, 2012 7:37 PM
Subject: Boost MSM: No transition from submachine to externalstate

Using the following example as a template:

http://svn.boost.org/svn/boost/trunk/libs/msm/doc/HTML/examples/CompositeTutorialWithEumlTable.cpp

I am trying to create a state machine that consists of an alarm state and a composite state (submachine). When an alarm event is posted while in the composite state, the state machine is supposed to transition to the Alarm state; however, the machine complains that no transition from the composite state on the alarm event exists. This is similar to the example where the player is in playing and a stop event is received.

Below is a dumbed-down version of my state machine:


Hi again,
 
ah I remember now. I changed the example last june because of a problem with exit points.
If you write:
 
typedef boost::msm::back::state_machine<Normal_T> Normal_THelper;
struct Normal_TImpl : public Normal_THelper,
                          public msm::front::euml::euml_state<Normal_TImpl >
{
};
Then it will work in your case but likely fail if faced with exit points. If you have none, you can use this trick temporarily while I have a look at this.
Otherwise you'll need to use the example I just posted, eUML is still in development and an experimental front-end.
 
Christophe

stetkas

unread,
Jan 17, 2012, 10:38:06 PM1/17/12
to boost...@lists.boost.org
Thanks Christophe. I will make the change to see it work; but based on your
comments, I'll probably switch to one of the other front-ends.

Thanks,

Steve


--
View this message in context: http://boost.2283326.n4.nabble.com/Boost-MSM-No-transition-from-submachine-to-external-state-tp4300991p4305587.html


Sent from the Boost - Users mailing list archive at Nabble.com.

_______________________________________________
Boost-users mailing list
Boost...@lists.boost.org
http://lists.boost.org/mailman/listinfo.cgi/boost-users

Christophe Henry

unread,
Jan 18, 2012, 6:00:14 PM1/18/12
to boost...@lists.boost.org
> Thanks Christophe. I will make the change to see it work; but based on
> your
> comments, I'll probably switch to one of the other front-ends.
>
> Thanks,
>
> Steve

What I mean is that eUML is not used as long as other front-ends and
therefore less tested. But there has been so far no bug I haven't been able
to fix within a few days.
In the case you reported, I have a fix which seems to be working (and
reduces the syntax needed), I just need to play with it a bit more before
committing it.

Christophe

stetkas

unread,
Jan 20, 2012, 9:29:15 PM1/20/12
to boost...@lists.boost.org
Oh I know; but I just cannot put something listed as experimental in
production code that's all.

Thanks for your help!

Steve

--
View this message in context: http://boost.2283326.n4.nabble.com/Boost-MSM-No-transition-from-submachine-to-external-state-tp4300991p4315163.html


Sent from the Boost - Users mailing list archive at Nabble.com.

Christophe Henry

unread,
Jan 24, 2012, 3:46:09 PM1/24/12
to boost...@lists.boost.org

>> Thanks Christophe. I will make the change to see it work; but based on
>> your
>> comments, I'll probably switch to one of the other front-ends.
>>
>> Thanks,
>>
>> Steve
>
> What I mean is that eUML is not used as long as other front-ends and
> therefore less tested. But there has been so far no bug I haven't been
> able to fix within a few days.
> In the case you reported, I have a fix which seems to be working (and
> reduces the syntax needed), I just need to play with it a bit more before
> committing it.


Hi,

as promised, I fixed this problem (trunk rev. 76655). It even requires less
typing. You define a submachine with front-end like always and use it in
another state machine. In your case, you'd have:

typedef boost::msm::back::state_machine<Normal_T> Normal_TImpl;

The remaining code is unchanged:
Alarm_TImpl alarm;

BOOST_MSM_EUML_DECLARE_TRANSITION_TABLE((
normal + evAlarm / resetDeviceInited() == alarm,
alarm + evAlarmCleared == normal
),transition_table)

You probably moved to another front-end, but in case you want to try eUML,
this problem is fixed.

Cheers,

Reply all
Reply to author
Forward
0 new messages