![]() |
User Manual, Developers Guide and API Documentation |
![]() |
First of all we need to find a model for the finite state machine. We know that the Lamp Control should turn on the lamp if a movement was detected and if it's currently night. Thinking about this we can derive that we need at least three states for the FSM to represent the different behaviours: Day, Night, LampOn, since the FSM must react different to input if it's day or night and different again, if the light is currently turned on (e.g., at day the light must not be turned on if a movement was detected).
At run time the finite state machine will be in one of these three states. Being in these states it needs to react to certain signals (input) with some action. The action may also include a state change (e.g., the finite state machine is in state "Night" and the signal "light" arrives, the state would change to "Day"). The signals needed here are: dark (to switch from Day to Night), light (to switch from Nigth to Day), movementDetcted (to switch the Lamp on) and timerElapsed (to switch the lamp off after some time). With these states and signals, the model of our finite state machine "Lamp Control" looks like this:
Summary of the model:
After this model has been identified it needs to be transfered to C++. The framework provided by wns::fsm::FSM will help us to get the model implemented fast and efficiently. Most of the following steps contain a sentence describing in recipe style what needs to be done.
The signals have to be modeled as pure virtual methods of a class. Make sure the class is really an interface (does not contain any variables, is stateless). The States classes will be derived from this interface later. The interface for our signals looks like this:
class LightControlSignals
{
public:
// the four signals as pure virtual functions
virtual LightControlSignals*
light() = 0;
virtual LightControlSignals*
dark() = 0;
virtual LightControlSignals*
movementDetected() = 0;
virtual LightControlSignals*
timeElapsed() = 0;
// abstract interface with virtual functions needs
// virtual destructor (otherwise g++ complains)
virtual
~LightControlSignals()
{}
protected:
// only called from derived classes
LightControlSignals()
{}
private:
LightControlSignals(const LightControlSignals&);
};
The finite state machine is also characterized by a set of variables which it holds. All these variables have to be implemented in a struct or class which will be used by the wns::fsm::FSM template later. For simplicity our example assumes the class definig the variables is called "Lamp" and contains only one variable called "on":
struct Lamp { Lamp() : on(false) {} bool on; };
The template wns::fsm::FSM instantiated with Variables and Signal will not implement the finite state machine itself. It will provide an interface for the final finite state machine. To have a handy alias create a typedef to this interface with the signal interface from above ("LightControlSignals") and the variables that characterize the finite state machine ("Lamp"):
typedef FSM<LightControlSignals, Lamp> LightControlInterface;
We're going to use the previously introduced typedef defining our FSM to derive a real implementation of this FSM. All methods of the signal interface ("LightControlSignals") need to be defined here:
class LightControl : public LightControlInterface { public: LightControl(const LightControlInterface::VariablesType& v); void movementDetected(); void timeElapsed(); void light(); void dark(); Lamp& lamp() { return getVariables(); } };
The implementation of the methods forward the method call simply to the current state object:
FSMTest::LightControl::LightControl(const FSMTest::LightControlInterface::VariablesType& v) : FSMTest::LightControlInterface(v) { changeState(createState<Night>()); } void FSMTest::LightControl::movementDetected() { changeState(getState()->movementDetected()); } void FSMTest::LightControl::timeElapsed() { changeState(getState()->timeElapsed()); } void FSMTest::LightControl::light() { changeState(getState()->light()); } void FSMTest::LightControl::dark() { changeState(getState()->dark()); }
The constructor must set the FSM to a valid initial state (here Night).
Each state is represented by a different class. These classes will be instantiated and deleted by the FSM. Here the definition of the states (Day, Night and LampOn) are presented:
class Day : public LightControlInterface::StateInterface { public: Day(LightControlInterface* lci) : LightControlInterface::StateInterface(lci, "wns_fsm_tests_Day") {} virtual StateInterface* movementDetected(); virtual StateInterface* timeElapsed(); virtual StateInterface* light(); virtual StateInterface* dark(); };
class Night : public LightControlInterface::StateInterface { public: Night(LightControlInterface* lci) : LightControlInterface::StateInterface(lci, "wns_fsm_tests_Night") {} virtual StateInterface* movementDetected(); virtual StateInterface* timeElapsed(); virtual StateInterface* light(); virtual StateInterface* dark(); };
class LampOn : public LightControlInterface::StateInterface { public: LampOn(LightControlInterface* lci) : LightControlInterface::StateInterface(lci, "wns_fsm_tests_LampOn") {} virtual StateInterface* movementDetected(); virtual StateInterface* timeElapsed(); virtual StateInterface* light(); virtual StateInterface* dark(); };
The state must be derived from an type exported by the interface of the FSM ("Light Control Interface", the typedef) named "StateInterface"
The constructor must take a pointer to the interface of the FSM
STATIC_FACTORY_REGISTER_WITH_CREATOR(FSMTest::Day, FSMTest::LightControlInterface::StateInterface, "wns_fsm_tests_Day", FSMConfigCreator); FSMTest::Day::StateInterface* FSMTest::Day::movementDetected() { // ignore return this; } FSMTest::Day::StateInterface* FSMTest::Day::timeElapsed() { throw(Exception("Signal only valid in state LampOn")); return this; } FSMTest::Day::StateInterface* FSMTest::Day::light() { // ignore return this; } FSMTest::Day::StateInterface* FSMTest::Day::dark() { return getFSM()->createState<Night>(); }
STATIC_FACTORY_REGISTER_WITH_CREATOR(FSMTest::Night, FSMTest::LightControlInterface::StateInterface, "wns_fsm_tests_Night", FSMConfigCreator); FSMTest::Night::StateInterface* FSMTest::Night::movementDetected() { vars().on = true; return getFSM()->createState<LampOn>(); } FSMTest::Night::StateInterface* FSMTest::Night::timeElapsed() { throw(Exception("Signal only valid in state LampOn")); return this; } FSMTest::Night::StateInterface* FSMTest::Night::light() { return getFSM()->createState<Day>(); } FSMTest::Night::StateInterface* FSMTest::Night::dark() { // ignore return this; }
STATIC_FACTORY_REGISTER_WITH_CREATOR(FSMTest::LampOn, FSMTest::LightControlInterface::StateInterface, "wns_fsm_tests_LampOn", FSMConfigCreator); FSMTest::LampOn::StateInterface* FSMTest::LampOn::movementDetected() { // ignore return this; } FSMTest::LampOn::StateInterface* FSMTest::LampOn::timeElapsed() { vars().on = false; return getFSM()->createState<Night>(); } FSMTest::LampOn::StateInterface* FSMTest::LampOn::light() { vars().on = false; return getFSM()->createState<Day>(); } FSMTest::LampOn::StateInterface* FSMTest::LampOn::dark() { // ignore return this; }
1.5.5