-
Notifications
You must be signed in to change notification settings - Fork 26
/
StateMachine.c
154 lines (123 loc) · 4.66 KB
/
StateMachine.c
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
#include "Fault.h"
#include "StateMachine.h"
// @see https://github.com/endurodave/C_StateMachine
// Generates an external event. Called once per external event
// to start the state machine executing
void _SM_ExternalEvent(SM_StateMachine* self, const SM_StateMachineConst* selfConst, BYTE newState, void* pEventData)
{
// If we are supposed to ignore this event
if (newState == EVENT_IGNORED)
{
// Just delete the event data, if any
if (pEventData)
SM_XFree(pEventData);
}
else
{
// TODO - capture software lock here for thread-safety if necessary
// Generate the event
_SM_InternalEvent(self, newState, pEventData);
// Execute state machine based on type of state map defined
if (selfConst->stateMap)
_SM_StateEngine(self, selfConst);
else
_SM_StateEngineEx(self, selfConst);
// TODO - release software lock here
}
}
// Generates an internal event. Called from within a state
// function to transition to a new state
void _SM_InternalEvent(SM_StateMachine* self, BYTE newState, void* pEventData)
{
ASSERT_TRUE(self);
self->pEventData = pEventData;
self->eventGenerated = TRUE;
self->newState = newState;
}
// The state engine executes the state machine states
void _SM_StateEngine(SM_StateMachine* self, const SM_StateMachineConst* selfConst)
{
void* pDataTemp = NULL;
ASSERT_TRUE(self);
ASSERT_TRUE(selfConst);
// While events are being generated keep executing states
while (self->eventGenerated)
{
// Error check that the new state is valid before proceeding
ASSERT_TRUE(self->newState < selfConst->maxStates);
// Get the pointers from the state map
SM_StateFunc state = selfConst->stateMap[self->newState].pStateFunc;
// Copy of event data pointer
pDataTemp = self->pEventData;
// Event data used up, reset the pointer
self->pEventData = NULL;
// Event used up, reset the flag
self->eventGenerated = FALSE;
// Switch to the new current state
self->currentState = self->newState;
// Execute the state action passing in event data
ASSERT_TRUE(state != NULL);
state(self, pDataTemp);
// If event data was used, then delete it
if (pDataTemp)
{
SM_XFree(pDataTemp);
pDataTemp = NULL;
}
}
}
// The state engine executes the extended state machine states
void _SM_StateEngineEx(SM_StateMachine* self, const SM_StateMachineConst* selfConst)
{
BOOL guardResult = TRUE;
void* pDataTemp = NULL;
ASSERT_TRUE(self);
ASSERT_TRUE(selfConst);
// While events are being generated keep executing states
while (self->eventGenerated)
{
// Error check that the new state is valid before proceeding
ASSERT_TRUE(self->newState < selfConst->maxStates);
// Get the pointers from the extended state map
SM_StateFunc state = selfConst->stateMapEx[self->newState].pStateFunc;
SM_GuardFunc guard = selfConst->stateMapEx[self->newState].pGuardFunc;
SM_EntryFunc entry = selfConst->stateMapEx[self->newState].pEntryFunc;
SM_ExitFunc exit = selfConst->stateMapEx[self->currentState].pExitFunc;
// Copy of event data pointer
pDataTemp = self->pEventData;
// Event data used up, reset the pointer
self->pEventData = NULL;
// Event used up, reset the flag
self->eventGenerated = FALSE;
// Execute the guard condition
if (guard != NULL)
guardResult = guard(self, pDataTemp);
// If the guard condition succeeds
if (guardResult == TRUE)
{
// Transitioning to a new state?
if (self->newState != self->currentState)
{
// Execute the state exit action on current state before switching to new state
if (exit != NULL)
exit(self);
// Execute the state entry action on the new state
if (entry != NULL)
entry(self, pDataTemp);
// Ensure exit/entry actions didn't call SM_InternalEvent by accident
ASSERT_TRUE(self->eventGenerated == FALSE);
}
// Switch to the new current state
self->currentState = self->newState;
// Execute the state action passing in event data
ASSERT_TRUE(state != NULL);
state(self, pDataTemp);
}
// If event data was used, then delete it
if (pDataTemp)
{
SM_XFree(pDataTemp);
pDataTemp = NULL;
}
}
}