Skip to content

Dynamic state machine + Controller #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added src/state_machine/ControllerAndSM.dia
Binary file not shown.
Binary file added src/state_machine/ControllerAndSM.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 38 additions & 0 deletions src/state_machine/README.setup_and_run
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
The REAME file attempts to explain how the Controller & SM operate.
Please review the UML included in this folder for further details.

Setup phase:

1. Create a SM: sm = new SDtateMachine();
2. Create state/s: st = new State**();
3. Configure the SM with initial state:
sm->setBaseState(stateNA);
4. Configure the SM by mapping actions/results
to the next state (per state):
sm->mapStateResult2NextState(stateNA, State::RET_SUCCESS, stateInit);
The above can be described as follows:
An event received while in stateNA, triggers the event method on that state,
and if it returns RET_SUCCESS, the SM current state is moved to stateInit.
Any other result value, by default keeps the current state unchanged.

5. Create a controller: controller = new CardController();
6. Create a card/device and bind to controller: card = new Device(controller);
7. Register the card & the SM it needs to work with to the controller:
card.devID = controller->regCard(sm);
The devID is used to identify the card in the controller.
When notifications arrive from the card, they include the devID and event,
and the controller can map the devID to its SM.

Runtime:
1. Start the controller so it will listen to inclomming messages:
controller->run();
Usually such a controller will run on a seperated thread, blocking on
msg_recv();
2. The card/device sends notifications to the controllen it is bind to:
card.sendEvent(CardController::EV_EXIST);
It is usually done through a message service service: msg_send(msg);
The information sent includes the card devID and the event ID.
3. The controller receives the message, using the devID determine the SM
it belongs to and run on it the event.
(it actually executes the event method on the current state)

45 changes: 45 additions & 0 deletions src/state_machine/card_controller.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* card_controller.cpp
*
* Created on: Nov 16, 2014
* Author: edwardh
*/

#include "card_controller.h"

#include <string.h>

#ifndef FOREVER
#define FOREVER() for(;;)
#endif

CardController::CardController() : number_of_registered_cards(0)
{
memset(this->cardSM, 0, MAX_SUPPORTED_CARDS);
}

int CardController::regCard(DStateMachine *sm)
{
this->cardSM[number_of_registered_cards] = sm;
return number_of_registered_cards++;
}

void CardController::run()
{
// Do here some Init stuff...

// Enter the main loop, receive messages from Device/s (Cards),
// identify the sender by devID and lookup his SM.
// We are left with executing the event on the SM.
//
// FOREVER()
// {
// msg = recv_msg();
// switch (msg->event)
// {
// case EV_EXIST:
// this->cardSM[msg->devID]->execEvent(...event...);
// }
// }
}

41 changes: 41 additions & 0 deletions src/state_machine/card_controller.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* card_controller.h
*
* Description:
* The controller is serving the cards (devices) by providing them means
* to register to the controller with their SM and giving each a unique id for communication.
*
* regCard() is used for card registration.
* run() is used for running the controller, usually in it's own task in an endless loop,
* blocking on recv messages.
*
* Created on: Nov 16, 2014
* Author: edwardh
*/

#ifndef STATE_MACHINE_CARD_CONTROLLER_H_
#define STATE_MACHINE_CARD_CONTROLLER_H_

#include "dynamic_state_machine.h"

class CardController
{
public:
CardController();
virtual ~CardController(){}

enum eventID {EV_NONE, EV_EXIST, EV_INIT_SUCCESS, EV_INIT_FAIL, EV_PROV_SUCCESS, EV_PROV_FAIL, EV_COUNT};

int regCard(DStateMachine *sm);

virtual void run();

static const int MAX_SUPPORTED_CARDS = 32;

protected:
DStateMachine *cardSM[MAX_SUPPORTED_CARDS];
int number_of_registered_cards;
};


#endif /* STATE_MACHINE_CARD_CONTROLLER_H_ */
55 changes: 55 additions & 0 deletions src/state_machine/card_states.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* card_states.h
*
* This file contains specific states with their relevant events (as methods).
* For each state, an event will execute the relevant method,
* performing some action and returning a result.
* Note: In the below definition, no actions are performed and each method
* returns a specific result. In real usage, an action/method may return
* with different results.
*
* Created on: Nov 15, 2014
* Author: edwardh
*/

#ifndef STATE_MACHINE_CARD_STATES_H_
#define STATE_MACHINE_CARD_STATES_H_

#include "state.h"


class StateNA : public State
{
public:
virtual stateResult evExist() {return RET_SUCCESS;}
};

class StateInit : public State
{
public:
virtual stateResult evInitSuccess(){return RET_SUCCESS;}
virtual stateResult evInitFail() {return RET_FAIL;}
};

class StateProv : public State
{
public:
virtual stateResult evProvSuccess(){return RET_SUCCESS;}
virtual stateResult evProvFail() {return RET_FAIL;}
};

class StateReady : public State
{
public:
};

class StateFault : public State
{
public:
virtual stateResult evInitSuccess(){return RET_SUCCESS;}
virtual stateResult evInitFail() {return RET_FAIL;}
};



#endif /* STATE_MACHINE_CARD_STATES_H_ */
46 changes: 46 additions & 0 deletions src/state_machine/device.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* device.h
*
* Description:
* A device is a generalization of a card, which has a reference to a controller
* and gets from the controller an ID (identifying it at the controller).
*
* A device can send an event to the controller through the sendEvent method.
*
* Created on: Nov 16, 2014
* Author: edwardh
*/

#ifndef STATE_MACHINE_DEVICE_H_
#define STATE_MACHINE_DEVICE_H_

#include "card_controller.h"

class Device
{
public:
Device(CardController *controller) : devID(-1), ctrl(controller) {}
virtual ~Device(){};

virtual void sendEvent(CardController::eventID evID)
{
(void)evID;
/*
* Build a message and send it to the controller.
* msg->event = evID;
* msg->devID = this->devID;
* send_msg_to_controller(msg);
*/
}

int devID;

// // For debug only (actually for tests. TODO: Should be cleaned up and moved to the test.
// CardController::eventID lastEvent() {return last_event_id;}
// CardController::eventID last_event_id;
private:
CardController *ctrl;
};


#endif /* STATE_MACHINE_DEVICE_H_ */
38 changes: 38 additions & 0 deletions src/state_machine/dynamic_state_machine.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* dynamic_state_machine.cpp
*
* Created on: Nov 15, 2014
* Author: edwardh
*/


#include "dynamic_state_machine.h"



void DStateMachine::setBaseState(State *st)
{
this->state = st;
}


void DStateMachine::mapDefault(State *default_nextState)
{
this->default_next_state = default_nextState;
}


void DStateMachine::mapStateResult2NextState(State *st, State::stateResult ret_st, State *nextSt)
{
if(ret_st < State::RET_COUNT)
st->stateMap[ret_st] = nextSt;
}


void DStateMachine::execEvent(State::stateResult result)
{
State *nextState = this->state->stateMap[result];

if(NULL != nextState)
this->state = nextState;
}
65 changes: 65 additions & 0 deletions src/state_machine/dynamic_state_machine.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* dynamic_state_machine.h
*
* A State Machine implementation with configurable state transitions.
* It maintains state decoupling, allowing it to scale and support multiple setups.
*
* Setup/Definition logic:
* - Create a State Machine.
* - Create states.
* - Set the SM initial state.
* - Set the SM default behavior: Action to take if no map is found.
* (NULL specifies to remain in the same state)
* - Map State & Result to the next State. (state, result, nextState)
* Receiving an event wile in state, triggers a state operation, resulting in a StateResult.
* SM State is changed to the nextState based on the result.
*
* Operation:
* - Usually called from a controller that receives events in an asynchronous mode.
* Usage example:
* State *state = sm->getCurrentState();
* sm->execEvent(state->evExist());
* // At this stage sm->getCurrentState() will return a new state.
*
*
* Created on: Nov 15, 2014
* Author: edwardh
*/

#ifndef DYNAMIC_STATE_MACHINE_H_
#define DYNAMIC_STATE_MACHINE_H_

#include "state.h"

class DStateMachine
{
public:
// SM Setup //
void setBaseState(State *st);

void mapDefault(State *default_nextState);

void mapStateResult2NextState(State *st, State::stateResult ret_st, State *nextSt);

// SM Operations //
void execEvent(State::stateResult result); //TODO: Ugly, it should not process the event at the calee, but in the SM.
// Need to convert this into a function pointer (or find a better solution).

State *getCurrentState(){return state;};

private:
State *state;
State *default_next_state;

/*
* Currently, the State MAP is maintained by each state independently.
* Located at the State "interface" to decouple individual states from each other.
* A different design approach could be to keep & manage the MAP here, in the SM, however
* it will require a special data structure to be maintain.
* It needs to be reconsidered again, some more thought should be put into this.
*/
};



#endif /* DYNAMIC_STATE_MACHINE_H_ */
38 changes: 38 additions & 0 deletions src/state_machine/state.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* state.h
*
* Description:
* The State interface defines the available events with their default action.
* (Here the defaults are to do nothing and return a FAIL response)
* In addition, it contains the storage of the state map (mapping result to next state).
* (Each state has such a map which is populated by the SM during configuration)
*
* Created on: Nov 15, 2014
* Author: edwardh
*/

#ifndef STATE_MACHINE_STATE_H_
#define STATE_MACHINE_STATE_H_

#include "string.h"

class State
{
public:
State(){memset(stateMap, 0, sizeof(stateMap));}
virtual ~State(){}

// RET_COUNT must be left last, representing the number of possible results.
enum stateResult {RET_SUCCESS=0, RET_FAIL, RET_COUNT};

virtual stateResult evExist() {return RET_FAIL;}
virtual stateResult evInitSuccess(){return RET_FAIL;}
virtual stateResult evInitFail() {return RET_FAIL;}
virtual stateResult evProvSuccess(){return RET_FAIL;}
virtual stateResult evProvFail() {return RET_FAIL;}

State *stateMap[RET_COUNT];
};


#endif /* STATE_MACHINE_STATE_H_ */
Loading