-
Notifications
You must be signed in to change notification settings - Fork 40
/
Copy pathStateMachine.h
328 lines (270 loc) · 11.2 KB
/
StateMachine.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
#ifndef _STATE_MACHINE_H
#define _STATE_MACHINE_H
#include "DataTypes.h"
#include <stdio.h>
#include <typeinfo>
#include "Fault.h"
// If EXTERNAL_EVENT_NO_HEAP_DATA is defined it changes how a client sends data to the
// state machine. When undefined, the ExternalEvent() pData argument must be created on the heap.
// The state machine will automatically delete the EventData pointer during state execution.
// When defined, clients must not heap allocate EventData with operator new. InternalEvent()
// used inside the state machine always heap allocates event data.
//#define EXTERNAL_EVENT_NO_HEAP_DATA 1
// @see https://github.com/endurodave/StateMachine
// David Lafreniere
// Uncomment the include below the XALLOCATOR line to use the xallocator instead
// of the global heap. Any EventData, or derived class thereof, created with
// new/delete will be routed to the xallocator. See xallocator.h for more info.
//#include "xallocator.h"
/// @beief Unique state machine event data must inherit from this class.
class EventData
{
public:
virtual ~EventData() {}
//XALLOCATOR
};
typedef EventData NoEventData;
class StateMachine;
/// @brief Abstract state base class that all states inherit from.
class StateBase
{
public:
/// Called by the state machine engine to execute a state action. If a guard condition
/// exists and it evaluates to false, the state action will not execute.
/// @param[in] sm - A state machine instance.
/// @param[in] data - The event data.
virtual void InvokeStateAction(StateMachine* sm, const EventData* data) const = 0;
};
/// @brief StateAction takes three template arguments: A state machine class,
/// a state function event data type (derived from EventData) and a state machine
/// member function pointer.
template <class SM, class Data, void (SM::*Func)(const Data*)>
class StateAction : public StateBase
{
public:
/// @see StateBase::InvokeStateAction
virtual void InvokeStateAction(StateMachine* sm, const EventData* data) const
{
// Downcast the state machine and event data to the correct derived type
SM* derivedSM = static_cast<SM*>(sm);
// If this check fails, there is a mismatch between the STATE_DECLARE
// event data type and the data type being sent to the state function.
// For instance, given the following state defintion:
// STATE_DECLARE(MyStateMachine, MyStateFunction, MyEventData)
// The following internal event transition is valid:
// InternalEvent(ST_MY_STATE_FUNCTION, new MyEventData());
// This next internal event is not valid and causes the assert to fail:
// InternalEvent(ST_MY_STATE_FUNCTION, new OtherEventData());
const Data* derivedData = dynamic_cast<const Data*>(data);
ASSERT_TRUE(derivedData != NULL);
// Call the state function
(derivedSM->*Func)(derivedData);
}
};
/// @brief Abstract guard base class that all guards classes inherit from.
class GuardBase
{
public:
/// Called by the state machine engine to execute a guard condition action. If guard
/// condition evaluates to TRUE the state action is executed. If FALSE, no state transition
/// is performed.
/// @param[in] sm - A state machine instance.
/// @param[in] data - The event data.
/// @return Returns TRUE if no guard condition or the guard condition evaluates to TRUE.
virtual BOOL InvokeGuardCondition(StateMachine* sm, const EventData* data) const = 0;
};
/// @brief GuardCondition takes three template arguments: A state machine class,
/// a state function event data type (derived from EventData) and a state machine
/// member function pointer.
template <class SM, class Data, BOOL (SM::*Func)(const Data*)>
class GuardCondition : public GuardBase
{
public:
virtual BOOL InvokeGuardCondition(StateMachine* sm, const EventData* data) const
{
SM* derivedSM = static_cast<SM*>(sm);
const Data* derivedData = dynamic_cast<const Data*>(data);
ASSERT_TRUE(derivedData != NULL);
// Call the guard function
return (derivedSM->*Func)(derivedData);
}
};
/// @brief Abstract entry base class that all entry classes inherit from.
class EntryBase
{
public:
/// Called by the state machine engine to execute a state entry action. Called when
/// entering a state.
/// @param[in] sm - A state machine instance.
/// @param[in] data - The event data.
virtual void InvokeEntryAction(StateMachine* sm, const EventData* data) const = 0;
};
/// @brief EntryAction takes three template arguments: A state machine class,
/// a state function event data type (derived from EventData) and a state machine
/// member function pointer.
template <class SM, class Data, void (SM::*Func)(const Data*)>
class EntryAction : public EntryBase
{
public:
virtual void InvokeEntryAction(StateMachine* sm, const EventData* data) const
{
SM* derivedSM = static_cast<SM*>(sm);
const Data* derivedData = dynamic_cast<const Data*>(data);
ASSERT_TRUE(derivedData != NULL);
// Call the entry function
(derivedSM->*Func)(derivedData);
}
};
/// @brief Abstract exit base class that all exit classes inherit from.
class ExitBase
{
public:
/// Called by the state machine engine to execute a state exit action. Called when
/// leaving a state.
/// @param[in] sm - A state machine instance.
virtual void InvokeExitAction(StateMachine* sm) const = 0;
};
/// @brief ExitAction takes two template arguments: A state machine class and
/// a state machine member function pointer.
template <class SM, void (SM::*Func)(void)>
class ExitAction : public ExitBase
{
public:
virtual void InvokeExitAction(StateMachine* sm) const
{
SM* derivedSM = static_cast<SM*>(sm);
// Call the exit function
(derivedSM->*Func)();
}
};
/// @brief A structure to hold a single row within the state map.
struct StateMapRow
{
const StateBase* const State;
};
/// @brief A structure to hold a single row within the extended state map.
struct StateMapRowEx
{
const StateBase* const State;
const GuardBase* const Guard;
const EntryBase* const Entry;
const ExitBase* const Exit;
};
/// @brief StateMachine implements a software-based state machine.
class StateMachine
{
public:
enum { EVENT_IGNORED = 0xFE, CANNOT_HAPPEN };
/// Constructor.
/// @param[in] maxStates - the maximum number of state machine states.
StateMachine(BYTE maxStates, BYTE initialState = 0);
virtual ~StateMachine() {}
/// Gets the current state machine state.
/// @return Current state machine state.
BYTE GetCurrentState() { return m_currentState; }
/// Gets the maximum number of state machine states.
/// @return The maximum state machine states.
BYTE GetMaxStates() { return MAX_STATES; }
protected:
/// External state machine event.
/// @param[in] newState - the state machine state to transition to.
/// @param[in] pData - the event data sent to the state.
void ExternalEvent(BYTE newState, const EventData* pData = NULL);
/// Internal state machine event. These events are generated while executing
/// within a state machine state.
/// @param[in] newState - the state machine state to transition to.
/// @param[in] pData - the event data sent to the state.
void InternalEvent(BYTE newState, const EventData* pData = NULL);
private:
/// The maximum number of state machine states.
const BYTE MAX_STATES;
/// The current state machine state.
BYTE m_currentState;
/// The new state the state machine has yet to transition to.
BYTE m_newState;
/// Set to TRUE when an event is generated.
BOOL m_eventGenerated;
/// The state event data pointer.
const EventData* m_pEventData;
/// Gets the state map as defined in the derived class. The BEGIN_STATE_MAP,
/// STATE_MAP_ENTRY and END_STATE_MAP macros are used to assist in creating the
/// map. A state machine only needs to return a state map using either GetStateMap()
/// or GetStateMapEx() but not both.
/// @return An array of StateMapRow pointers with the array size MAX_STATES or
/// NULL if the state machine uses the GetStateMapEx().
virtual const StateMapRow* GetStateMap() = 0;
/// Gets the extended state map as defined in the derived class. The BEGIN_STATE_MAP_EX,
/// STATE_MAP_ENTRY_EX, STATE_MAP_ENTRY_ALL_EX, and END_STATE_MAP_EX macros are used to
/// assist in creating the map. A state machine only needs to return a state map using
/// either GetStateMap() or GetStateMapEx() but not both.
/// @return An array of StateMapRowEx pointers with the array size MAX_STATES or
/// NULL if the state machine uses the GetStateMap().
virtual const StateMapRowEx* GetStateMapEx() = 0;
/// Set a new current state.
/// @param[in] newState - the new state.
void SetCurrentState(BYTE newState) { m_currentState = newState; }
/// State machine engine that executes the external event and, optionally, all
/// internal events generated during state execution.
void StateEngine(void);
void StateEngine(const StateMapRow* const pStateMap);
void StateEngine(const StateMapRowEx* const pStateMapEx);
};
#define STATE_DECLARE(stateMachine, stateName, eventData) \
void ST_##stateName(const eventData*); \
StateAction<stateMachine, eventData, &stateMachine::ST_##stateName> stateName;
#define STATE_DEFINE(stateMachine, stateName, eventData) \
void stateMachine::ST_##stateName(const eventData* data)
#define GUARD_DECLARE(stateMachine, guardName, eventData) \
BOOL GD_##guardName(const eventData*); \
GuardCondition<stateMachine, eventData, &stateMachine::GD_##guardName> guardName;
#define GUARD_DEFINE(stateMachine, guardName, eventData) \
BOOL stateMachine::GD_##guardName(const eventData* data)
#define ENTRY_DECLARE(stateMachine, entryName, eventData) \
void EN_##entryName(const eventData*); \
EntryAction<stateMachine, eventData, &stateMachine::EN_##entryName> entryName;
#define ENTRY_DEFINE(stateMachine, entryName, eventData) \
void stateMachine::EN_##entryName(const eventData* data)
#define EXIT_DECLARE(stateMachine, exitName) \
void EX_##exitName(void); \
ExitAction<stateMachine, &stateMachine::EX_##exitName> exitName;
#define EXIT_DEFINE(stateMachine, exitName) \
void stateMachine::EX_##exitName(void)
#define BEGIN_TRANSITION_MAP \
static const BYTE TRANSITIONS[] = {\
#define TRANSITION_MAP_ENTRY(entry)\
entry,
#define END_TRANSITION_MAP(data) \
};\
ASSERT_TRUE(GetCurrentState() < ST_MAX_STATES); \
ExternalEvent(TRANSITIONS[GetCurrentState()], data); \
C_ASSERT((sizeof(TRANSITIONS)/sizeof(BYTE)) == ST_MAX_STATES);
#define PARENT_TRANSITION(state) \
if (GetCurrentState() >= ST_MAX_STATES && \
GetCurrentState() < GetMaxStates()) { \
ExternalEvent(state); \
return; }
#define BEGIN_STATE_MAP \
private:\
virtual const StateMapRowEx* GetStateMapEx() { return NULL; }\
virtual const StateMapRow* GetStateMap() {\
static const StateMapRow STATE_MAP[] = {
#define STATE_MAP_ENTRY(stateName)\
stateName,
#define END_STATE_MAP \
}; \
C_ASSERT((sizeof(STATE_MAP)/sizeof(StateMapRow)) == ST_MAX_STATES); \
return &STATE_MAP[0]; }
#define BEGIN_STATE_MAP_EX \
private:\
virtual const StateMapRow* GetStateMap() { return NULL; }\
virtual const StateMapRowEx* GetStateMapEx() {\
static const StateMapRowEx STATE_MAP[] = {
#define STATE_MAP_ENTRY_EX(stateName)\
{ stateName, 0, 0, 0 },
#define STATE_MAP_ENTRY_ALL_EX(stateName, guardName, entryName, exitName)\
{ stateName, guardName, entryName, exitName },
#define END_STATE_MAP_EX \
}; \
C_ASSERT((sizeof(STATE_MAP)/sizeof(StateMapRowEx)) == ST_MAX_STATES); \
return &STATE_MAP[0]; }
#endif // _STATE_MACHINE_H