Reputation: 665
I have a simple state machine, it receives 3 types of messages and depending on message type it sends corresponding response. In the normal situations when correct messages received in correct order my state machine works perfect.
But in cases when unexpected message is received, no_transition
is called, which must fire error_detected
event, which must be handled by normal_workflow
state. But no_transition
is called 2 times, because there are 2 orthogonal regions. But I need fire error_detected
event only in case of normal_workflow
state. So how to determine current active state within no_trasition
?
Here is my code,
#include <iostream>
#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>
#include <boost/msm/front/euml/common.hpp>
#include <boost/msm/front/euml/operator.hpp>
#include <boost/msm/front/euml/state_grammar.hpp>
namespace msm = boost::msm;
namespace mpl = boost::mpl;
using namespace msm::front;
using namespace msm::front::euml;
namespace
{
// events
//
struct received_type_1_msg { received_type_1_msg(){ std::cout << "received_type_1_msg" << std::endl; } };
struct received_type_2_msg { received_type_2_msg(){ std::cout << "received_type_2_msg" << std::endl; } };
struct received_type_3_msg { received_type_3_msg(){ std::cout << "received_type_3_msg" << std::endl; } };
struct err_detected { err_detected(){ std::cout << "err_detected" << std::endl; } };
// front end
//
struct test_sm_ : public msm::front::state_machine_def<test_sm_>
{
// states
//
struct idle : public msm::front::state<>
{
template <class event,class fsm>
void on_entry(event const& evt,fsm& sm)
{
std::cout << "idle" << std::endl;
}
};
struct wait_type_2_msg : public msm::front::state<>
{
template <class event,class fsm>
void on_entry(event const& evt,fsm& sm)
{
std::cout << "wait_type_1_msg"<< std::endl;
}
};
struct wait_type_3_msg : public msm::front::state<>
{
template <class event,class fsm>
void on_entry(event const& evt,fsm& sm)
{
std::cout << "wait_type_3_msg"<< std::endl;
}
};
struct normal_workflow : public msm::front::state<>
{
template <class event,class fsm>
void on_entry(event const& evt,fsm& sm)
{
std::cout << "normal_workflow"<< std::endl;
}
};
// initial state
//
typedef mpl::vector2<idle, normal_workflow> initial_state;
// transition actions
//
struct send_type_1_rsp
{
template<class event, class fsm, class src_state, class dst_state>
void operator()(event const& evt, fsm&, src_state&, dst_state&)
{
std::cout << "send_type_1_rsp"<< std::endl;
}
};
struct send_type_2_rsp
{
template<class event, class fsm, class src_state, class dst_state>
void operator()(event const& evt, fsm&, src_state&, dst_state&)
{
std::cout << "send_type_2_rsp"<< std::endl;
}
};
struct send_type_3_rsp
{
template<class event, class fsm, class src_state, class dst_state>
void operator()(event const& evt, fsm&, src_state&, dst_state&)
{
std::cout << "send_type_3_rsp"<< std::endl;
}
};
struct send_error_rsp
{
template<class event, class fsm, class src_state, class dst_state>
void operator()(event const& evt, fsm&, src_state&, dst_state&)
{
std::cout << "send_error_rsp"<< std::endl;
}
};
struct transition_table : mpl::vector<
// Start Event Next Action Guard
// +---------------------------+-------------------------------+---------------------------+------------------------------+--------+
Row < idle , received_type_1_msg , wait_type_2_msg , send_type_1_rsp , none >,
Row < wait_type_2_msg , received_type_2_msg , wait_type_3_msg , send_type_2_rsp , none >,
Row < wait_type_3_msg , received_type_3_msg , idle , send_type_3_rsp , none >,
// +---------------------------+-------------------------------+---------------------------+------------------------------+--------+
Row < normal_workflow , err_detected , idle , send_error_rsp , none >
// +---------------------------+-------------------------------+---------------------------+------------------------------+--------+
>{};
// no transition
//
template <class fsm,class event>
void no_transition(event const& e, fsm& sm,int state)
{
std::cout << "no transition" << std::endl;
//sm.process_event(err_detected());
}
};
typedef msm::back::state_machine<test_sm_> test_sm;
}
int main(int argc, char** argv)
{
test_sm sm;
sm.start();
sm.process_event(received_type_1_msg());
sm.process_event(received_type_2_msg());
// wrong message received
//
sm.process_event(received_type_2_msg());
return 0;
}
One solution is via using state argument which is passed to no_transition
. Is there any other solution? Because something like this look not good:
template <class fsm,class event>
void no_transition(event const& e, fsm& sm,int state)
{
// without this condition err_detected event will fired twise, because sm have 2 regions
//
if(state == 3)
{
// call this event only in NORMAL_WORKFLOW state, because it handled within this state
//
sm.process_event(err_detected());
}
}
Upvotes: 2
Views: 1085
Reputation: 11
Well, IMHO, the usage of orthogonal regions is Good. however, the error handling event still need to be fired by yourself. no_transition is the only function when MSM goes wrong. so, I am doing it the same way as you did.
Upvotes: 1