I'm using the new Boost 1.44.0 MSM library to produce a state machine. In this state machine there are two classes of events class1
and class2
. class1
events can be processed by either states S1
or S2
while class2
events can only be processed by state S2
.
A special class1
event upgrade_req
requests an upgrade from state S1
to state S2
.
I've implemented that in Boost::MSM as follows:
// State S1 and S2 allow any class1 events
struct class1 {};
// Only state S2 allows class2 events
struct class2 {};
// an upgrade request is a class1 event that requests an upgrade to state 2
struct upgrade_req : public class1 {};
struct MyFSM : public msm::front::state_machine_def< MyFSM >
{
/// State 1. Allows any class1 event
struct S1 : public msm::front::state<>
{
/// functor says "processing event in State 1"
struct ProcessEvent { /* ... */ };
struct internal_transition_table : mpl::vector<
// Event Action Guard
// +-------+-------------+------------+
Internal< class1, ProcessEvent, none >
> {};
}; // S1
/// State 2. Allows any class1 or class2 events
struct S2 : public msm::front::state<>
{
/// functor says "processing event in State 2"
struct ProcessEvent { /* ... */ };
struct internal_transition_table : mpl::vector<
// Event Action Guard
// +-------+-------------+------------+
Internal< class1, ProcessEvent, none >,
Internal< class2, ProcessEvent, none >
> {};
}; // S2
/// everybody starts in state 1
typedef S1 initial_state;
/// send an error if a class2 event was received for state1
struct SendError { /* ... */ };
/// Send a response to the upgrade request
struct SendUpgradeRsp { /* ... */ };
/// functor returns true if the request to upgrade to state 2 is OK.
struct VerifyUpgradeReq { /* ... */ };
struct transition_table : mpl::vector<
// Start Event Next Action Guard
// +------+-------------+------+----------------+------------------+
Row< S1, class1, none, none, none,
Row< S1, class2, S1, SendError, none >,
Row< S1, upgrade_req, S2, SendUpgradRsp, VerifyUpgradeReq >,
Row< S2, class1, none, none, none,
Row< S2, class2, none, none, none >
> {};
}; // MyFSM
My problem is that when I use this as it is, the upgrade_req
event is never processed by the main MyFSM::transition_table
. It is only processed by the S1::internal_transition_table
.
For example:
int main( int argc, char* argv[] )
{
msm::back::state_machine< MyFSM > sm;
sm.start();
sm.process_event( class1() );
sm.process_event( upgrade_req() );
sm.process_event( class2() );
return 0;
}
I would desire the output of this to be:
processing event in State 1.
Upgrade Request OK.
processing event in State 2.
But, what I get is this:
processing event in State 1.
processing event in State 1.
Error. Received class 2 event in State 1.
Does anybody have a suggestion on how I can fix this issue?
Thanks, PaulH
Your problem is that the priority of internal transitions is higher than those defined in the transition table. And update_req being a class1, the internal transiton fires. This is actually conform to the UML standard. MSM offers you a second solution, you can define S1's internal transition with a Row with none as target inside transition_table instead of using an internal_transition_table. If you define it BEFORE the transition S1 + upgrade_reg -> S2, it will have a lesser prio and will be tried only if the other one cannot be considered.
If you absolutely need an internal_transition_table then you can only provide a guard to reject class1 if it's not an update_req.
HTH, Christophe Henry
PS: I only found this post by luck. Posting to the boost user list will guarantee you a much faster answer.