Practical Sub Machine Example
Sub state machine ScanFinger is reused as sub-machine state in Register finger print state machine twice. At the RightScanning state, Entry1's incoming event is Start Button Pressed. At the LeftScanning state, Entry1's incoming event is Completed. They are not the same. Normally, they are not convertible each other. In this case, you can choose the approach1 or approach3 in the previous page. The following code is implemented by approach1.
#include <iostream> #include <boost/msm/back/state_machine.hpp> #include <boost/msm/front/state_machine_def.hpp> #include <boost/msm/front/functor_row.hpp> namespace { namespace msm = boost::msm; namespace msmf = boost::msm::front; namespace mpl = boost::mpl; // ----- Events struct StartButtonPressed {}; struct CheckSucceeded {}; struct CheckFailed {}; struct ScanSucceeded {}; struct ScanFailed {}; struct Completed { enum Result { OK, NG }; Completed(CheckFailed const&):r_(NG) {} Completed(ScanSucceeded const&):r_(OK) {} Completed(ScanFailed const&):r_(NG) {} Result result() const { return r_; } private: Result r_; }; // ----- State machine struct Sm1_:msmf::state_machine_def<Sm1_> { struct Waiting_:msmf::state<> { template <class Event,class Fsm> void on_entry(Event const&, Fsm&) const { std::cout << "Press Start Button" << std::endl; } }; struct ScanFinger_:msmf::state_machine_def<ScanFinger_> { struct AliveChecking:msmf::state<> { template <class Event,class Fsm> void on_entry(Event const&, Fsm&) const { std::cout << "Alive checking..." << std::endl; } }; struct Scanning:msmf::state<> { template <class Event,class Fsm> void on_entry(Event const&, Fsm&, typename boost::enable_if<boost::is_same<Event, boost::any> >::type* = 0) const { std::cout << "Scanning..." << std::endl; } template <class Event,class Fsm> void on_entry(Event const& e, Fsm&, typename boost::disable_if<boost::is_same<Event, boost::any> >::type* = 0) const {} }; struct ScanOnly:msmf::entry_pseudo_state<> {}; struct Exit1:msmf::exit_pseudo_state<Completed> {}; // Set initial state typedef mpl::vector<AliveChecking> initial_state; // Transition table struct transition_table:mpl::vector< // Start Event Next Action Guard msmf::Row < AliveChecking, CheckSucceeded, Scanning, msmf::none, msmf::none >, msmf::Row < AliveChecking, CheckFailed, Exit1, msmf::none, msmf::none >, msmf::Row < ScanOnly, boost::any, Scanning, msmf::none, msmf::none >, msmf::Row < Scanning, ScanSucceeded, Exit1, msmf::none, msmf::none >, msmf::Row < Scanning, ScanFailed, Exit1, msmf::none, msmf::none > > {}; }; struct RightScanning_:ScanFinger_ {}; struct LeftScanning_:ScanFinger_ {}; typedef msm::back::state_machine<RightScanning_> RightScanning; typedef msm::back::state_machine<LeftScanning_> LeftScanning; // Actions struct RightMessage { template <class Event, class Fsm, class SourceState, class TargetState> void operator()(Event const&, Fsm&, SourceState&, TargetState&) { std::cout << "Right hand please." << std::endl; } }; struct LeftMessage { template <class Event, class Fsm, class SourceState, class TargetState> void operator()(Event const&, Fsm&, SourceState&, TargetState&) { std::cout << "Left hand please." << std::endl; } }; struct FailMessage { template <class Event, class Fsm, class SourceState, class TargetState> void operator()(Event const&, Fsm&, SourceState&, TargetState&) { std::cout << "Register Failed." << std::endl; } }; struct CompleteMessage { template <class Event, class Fsm, class SourceState, class TargetState> void operator()(Event const&, Fsm&, SourceState&, TargetState&) { std::cout << "Register Completed." << std::endl; } }; // Guards struct IsResultOk { template <class Event, class Fsm, class SourceState, class TargetState> bool operator()(Event const& e, Fsm&, SourceState&, TargetState&) { return e.result() == Completed::OK; } }; // Set initial state typedef Waiting_ initial_state; // Transition table struct transition_table:mpl::vector< // Start Event Next Action Guard msmf::Row < Waiting_, StartButtonPressed, RightScanning::entry_pt <RightScanning_::ScanOnly>, RightMessage, msmf::none >, msmf::Row < RightScanning::exit_pt <RightScanning_::Exit1>, Completed, Waiting_, FailMessage, msmf::none >, // else msmf::Row < RightScanning::exit_pt <RightScanning_::Exit1>, Completed, LeftScanning::entry_pt <LeftScanning_::ScanOnly>, LeftMessage, IsResultOk >, msmf::Row < LeftScanning::exit_pt <LeftScanning_::Exit1>, Completed, Waiting_, FailMessage, msmf::none >, // else msmf::Row < LeftScanning::exit_pt <LeftScanning_::Exit1>, Completed, Waiting_, CompleteMessage, IsResultOk > > {}; }; // Pick a back-end typedef msm::back::state_machine<Sm1_> Sm1; void test() { Sm1 sm1; sm1.start(); std::cout << "=== 1. Success Scenario ===" << std::endl; std::cout << "> Send StartButtonPressed()" << std::endl; sm1.process_event(StartButtonPressed()); std::cout << "> Send ScanSucceeded()" << std::endl; sm1.process_event(ScanSucceeded()); std::cout << "> Send ScanSucceeded()" << std::endl; sm1.process_event(ScanSucceeded()); std::cout << "=== 2. Right Fail Scenario ===" << std::endl; std::cout << "> Send StartButtonPressed()" << std::endl; sm1.process_event(StartButtonPressed()); std::cout << "> Send ScanFailed()" << std::endl; sm1.process_event(ScanFailed()); std::cout << "=== 3. Left Fail Scenario ===" << std::endl; std::cout << "> Send StartButtonPressed()" << std::endl; sm1.process_event(StartButtonPressed()); std::cout << "> Send ScanSucceeded()" << std::endl; sm1.process_event(ScanSucceeded()); std::cout << "> Send ScanFailed()" << std::endl; sm1.process_event(ScanFailed()); } } int main() { test(); return 0; } // Output: // // Press Start Button // === 1. Success Scenario === // > Send StartButtonPressed() // Right hand please. // Scanning... // > Send ScanSucceeded() // Left hand please. // Scanning... // > Send ScanSucceeded() // Register Completed. // Press Start Button // === 2. Right Fail Scenario === // > Send StartButtonPressed() // Right hand please. // Scanning... // > Send ScanFailed() // Register Failed. // Press Start Button // === 3. Left Fail Scenario === // > Send StartButtonPressed() // Right hand please. // Scanning... // > Send ScanSucceeded() // Left hand please. // Scanning... // > Send ScanFailed() // Register Failed. // Press Start Button
Event propergation from exit point pseudo state to outer state
Exit point pseudo state has a template parameter as a converted event.
struct Exit1:msmf::exit_pseudo_state<Completed> {};
This means that exit pseudo state's incoming events is converted to the template parameter event. For example, ScanScceeded and ScanFailed is converted to Completed.
// Transition table struct transition_table:mpl::vector< // Start Event Next Action Guard msmf::Row < Entry1, boost::any, Scanning, msmf::none, msmf::none >, msmf::Row < Scanning, ScanSucceeded, Exit1, msmf::none, msmf::none >, msmf::Row < Scanning, ScanFailed, Exit1, msmf::none, msmf::none > > {};
So, the class Completed has to have the conversion constructors.
struct Completed { enum Result { OK, NG }; Completed(ScanSucceeded const&):r_(OK) {} Completed(ScanFailed const&):r_(NG) {} Result result() const { return r_; } private: Result r_; };
The converted event is used in the transition table of outer state machine.
// Transition table struct transition_table:mpl::vector< // Start Event Next Action Guard msmf::Row < Waiting_, StartButtonPressed, RightScanning::entry_pt <RightScanning_::Entry1>, RightMessage, msmf::none >, msmf::Row < RightScanning::exit_pt <RightScanning_::Exit1>, Completed, Waiting_, FailMessage, msmf::none >, // else msmf::Row < RightScanning::exit_pt <RightScanning_::Exit1>, Completed, LeftScanning::entry_pt <LeftScanning_::Entry1>, LeftMessage, IsResultOk >, msmf::Row < LeftScanning::exit_pt <LeftScanning_::Exit1>, Completed, Waiting_, FailMessage, msmf::none >, // else msmf::Row < LeftScanning::exit_pt <LeftScanning_::Exit1>, Completed, Waiting_, CompleteMessage, IsResultOk > > {};
The meaning of "// else" commented rows are described here.
So, you can use the information that from inner state machine in outer state machine.
// Guards struct IsResultOk { template <class Event, class Fsm, class SourceState, class TargetState> bool operator()(Event const& e, Fsm&, SourceState&, TargetState&) { return e.result() == Completed::OK; } };
page revision: 4, last edited: 10 Sep 2012 05:28