Skip to content

Commit b5292c9

Browse files
authored
Merge pull request #258 from HyperloopUPV-H8/fix/rework_state_machine
Fix/rework state machine
2 parents 313bf9a + ce7a005 commit b5292c9

2 files changed

Lines changed: 194 additions & 74 deletions

File tree

Inc/ST-LIB_LOW/StateMachine/StateMachine.hpp

Lines changed: 141 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*/
44

55
#pragma once
6-
#include <C++Utilities/CppUtils.hpp>
6+
#include "C++Utilities/CppUtils.hpp"
77
#include "Time/Time.hpp"
88
#include "ErrorHandler/ErrorHandler.hpp"
99

@@ -13,31 +13,100 @@ typedef std::chrono::milliseconds ms;
1313
typedef std::chrono::microseconds us;
1414
typedef std::chrono::seconds s;
1515

16+
enum AlarmType{
17+
LOW_PRECISION,
18+
MID_PRECISION,
19+
HIGH_PRECISION
20+
};
21+
1622
class TimedAction {
1723
public:
1824
function<void()> action;
19-
uint32_t microseconds;
25+
uint32_t period;
26+
AlarmType alarm_precision;
27+
uint8_t id = -1;
2028

2129
TimedAction() = default;
22-
TimedAction(function<void()> action, uint32_t microseconds);
2330
};
2431

2532
class State {
2633
public:
27-
vector<TimedAction> cyclic_actions = {};
34+
vector<TimedAction> cyclic_actions;
2835
vector<function<void()>> on_enter_actions = {};
2936
vector<function<void()>> on_exit_actions = {};
3037
void enter();
3138
void exit();
39+
template<class TimeUnit>
40+
void add_new_timed_action(function<void()> action, chrono::duration<int64_t, TimeUnit> period, AlarmType precision_type);
41+
template<class TimeUnit>
42+
void register_new_timed_action(function<void()> action, chrono::duration<int64_t, TimeUnit> period, AlarmType precision_type);
43+
void unregister_all_timed_actions();
44+
void register_all_timed_actions();
3245
};
3346

47+
template<class TimeUnit>
48+
void State::register_new_timed_action(function<void()> action, chrono::duration<int64_t, TimeUnit> period, AlarmType precision_type){
49+
TimedAction timed_action = {};
50+
timed_action.alarm_precision = precision_type;
51+
timed_action.action = action;
52+
uint32_t miliseconds = chrono::duration_cast<chrono::milliseconds>(period).count();
53+
uint32_t microseconds = chrono::duration_cast<chrono::microseconds>(period).count();
54+
switch (precision_type) {
55+
case LOW_PRECISION:
56+
timed_action.period = miliseconds;
57+
timed_action.id = Time::register_low_precision_alarm(miliseconds, action);
58+
break;
59+
case MID_PRECISION:
60+
timed_action.period = microseconds;
61+
timed_action.id = Time::register_mid_precision_alarm(microseconds, action).value();
62+
break;
63+
case HIGH_PRECISION:
64+
timed_action.period = microseconds;
65+
timed_action.id = Time::register_high_precision_alarm(microseconds, action).value();
66+
break;
67+
default:
68+
ErrorHandler("Alarm Precision Type does not exist, AlarmType: %d", precision_type);
69+
return;
70+
break;
71+
}
72+
cyclic_actions.push_back(timed_action);
73+
}
74+
75+
template<class TimeUnit>
76+
void State::add_new_timed_action(function<void()> action, chrono::duration<int64_t, TimeUnit> period, AlarmType precision_type){
77+
TimedAction timed_action = {};
78+
timed_action.alarm_precision = precision_type;
79+
timed_action.action = action;
80+
uint32_t miliseconds = chrono::duration_cast<chrono::milliseconds>(period).count();
81+
uint32_t microseconds = chrono::duration_cast<chrono::microseconds>(period).count();
82+
switch (precision_type) {
83+
case LOW_PRECISION:
84+
timed_action.period = miliseconds;
85+
break;
86+
case MID_PRECISION:
87+
timed_action.period = microseconds;
88+
break;
89+
case HIGH_PRECISION:
90+
timed_action.period = microseconds;
91+
break;
92+
default:
93+
ErrorHandler("Alarm Precision Type does not exist, AlarmType: %d", precision_type);
94+
return;
95+
break;
96+
}
97+
cyclic_actions.push_back(timed_action);
98+
}
99+
100+
34101
class StateMachine {
35102
public:
36103
typedef uint8_t state_id;
37104

38105
state_id initial_state = 0;
39106
state_id current_state = 0;
40107

108+
bool is_on = true;
109+
41110

42111
StateMachine() = default;
43112
StateMachine(state_id initial_state);
@@ -46,9 +115,19 @@ class StateMachine {
46115
void add_transition(state_id old_state, state_id new_state, function<bool()> transition);
47116

48117
template<class TimeUnit>
49-
void add_cyclic_action(function<void()> action, chrono::duration<int64_t, TimeUnit> period);
118+
void add_low_precision_cyclic_action(function<void()> action, chrono::duration<int64_t, TimeUnit> period);
50119
template<class TimeUnit>
51-
void add_cyclic_action(function<void()> action, chrono::duration<int64_t, TimeUnit> period, state_id state);
120+
void add_low_precision_cyclic_action(function<void()> action, chrono::duration<int64_t, TimeUnit> period, state_id state);
121+
122+
template<class TimeUnit>
123+
void add_mid_precision_cyclic_action(function<void()> action, chrono::duration<int64_t, TimeUnit> period);
124+
template<class TimeUnit>
125+
void add_mid_precision_cyclic_action(function<void()> action, chrono::duration<int64_t, TimeUnit> period, state_id state);
126+
127+
template<class TimeUnit>
128+
void add_high_precision_cyclic_action(function<void()> action, chrono::duration<int64_t, TimeUnit> period);
129+
template<class TimeUnit>
130+
void add_high_precision_cyclic_action(function<void()> action, chrono::duration<int64_t, TimeUnit> period, state_id state);
52131

53132
void add_enter_action(function<void()> action);
54133
void add_enter_action(function<void()> action, state_id state);
@@ -65,42 +144,76 @@ class StateMachine {
65144
private:
66145
unordered_map<state_id, State> states;
67146
unordered_map<state_id, unordered_map<state_id, function<bool()>>> transitions;
68-
69147
unordered_map<state_id, StateMachine*> nested_state_machine;
70-
vector<uint8_t> current_state_timed_actions_in_milliseconds;
71-
vector<uint8_t> current_state_timed_actions_in_microseconds;
72148
};
73149

74150
template<class TimeUnit>
75-
void StateMachine::add_cyclic_action(function<void()> action, chrono::duration<int64_t, TimeUnit> period, state_id state) {
151+
void StateMachine::add_low_precision_cyclic_action(function<void()> action, chrono::duration<int64_t, TimeUnit> period, state_id state) {
152+
if (not states.contains(state)) {
153+
ErrorHandler("The state %d is not added to the state machine", state);
154+
return;
155+
}
156+
157+
uint32_t microseconds = (uint32_t)chrono::duration_cast<chrono::microseconds>(period).count();
158+
159+
if(microseconds % 1000 != 0){
160+
ErrorHandler("Low precision cyclic action does not have enough resolution for the desired period, Desired period: %d uS", microseconds);
161+
return;
162+
}
163+
164+
if(state == current_state && is_on) states[state].register_new_timed_action(action, period, LOW_PRECISION);
165+
else states[state].add_new_timed_action(action, period,LOW_PRECISION);
166+
}
167+
168+
template<class TimeUnit>
169+
void StateMachine::add_low_precision_cyclic_action(function<void()> action, chrono::duration<int64_t, TimeUnit> period){
170+
add_low_precision_cyclic_action(action, period, current_state);
171+
}
172+
173+
template<class TimeUnit>
174+
void StateMachine::add_mid_precision_cyclic_action(function<void()> action, chrono::duration<int64_t, TimeUnit> period, state_id state) {
175+
if (not states.contains(state)) {
176+
ErrorHandler("The state %d is not added to the state machine", state);
177+
return;
178+
}
179+
180+
uint32_t microseconds = (uint32_t)chrono::duration_cast<chrono::microseconds>(period).count();
181+
182+
if(microseconds % 50 != 0){
183+
ErrorHandler("Mid precision cyclic action does not have enough resolution for the desired period, Desired period: %d uS", microseconds);
184+
return;
185+
}
186+
187+
if(state == current_state && is_on) states[state].register_new_timed_action(action, period, MID_PRECISION);
188+
else states[state].add_new_timed_action(action, period,MID_PRECISION);
189+
}
190+
191+
template<class TimeUnit>
192+
void StateMachine::add_mid_precision_cyclic_action(function<void()> action, chrono::duration<int64_t, TimeUnit> period){
193+
add_mid_precision_cyclic_action(action, period, current_state);
194+
}
195+
196+
template<class TimeUnit>
197+
void StateMachine::add_high_precision_cyclic_action(function<void()> action, chrono::duration<int64_t, TimeUnit> period, state_id state) {
76198
if (not states.contains(state)) {
77199
ErrorHandler("The state %d is not added to the state machine", state);
78200
return;
79201
}
80202

81203
uint32_t microseconds = (uint32_t)chrono::duration_cast<chrono::microseconds>(period).count();
82-
TimedAction timed_action(action, microseconds);
83-
states[state].cyclic_actions.push_back(timed_action);
84-
85-
if (state == current_state) {
86-
if (microseconds % 1000) {
87-
optional<uint8_t> optional_id = Time::register_high_precision_alarm(microseconds, action);
88-
if (not optional_id) {
89-
ErrorHandler("The high precision alarm could not be registered");
90-
return;
91-
}
92-
current_state_timed_actions_in_microseconds.push_back(optional_id.value());
93-
}
94-
else {
95-
uint8_t id = Time::register_low_precision_alarm(microseconds, action);
96-
current_state_timed_actions_in_milliseconds.push_back(id);
97-
}
204+
205+
if(microseconds < 1){
206+
ErrorHandler("High precision cyclic action does not have enough resolution for the desired period, Desired period: %d uS", microseconds);
207+
return;
98208
}
209+
210+
if(state == current_state && is_on) states[state].register_new_timed_action(action, period, HIGH_PRECISION);
211+
else states[state].add_new_timed_action(action, period,HIGH_PRECISION);
99212
}
100213

101214
template<class TimeUnit>
102-
void StateMachine::add_cyclic_action(function<void()> action, chrono::duration<int64_t, TimeUnit> period){
103-
add_cyclic_action(action, period, current_state);
215+
void StateMachine::add_high_precision_cyclic_action(function<void()> action, chrono::duration<int64_t, TimeUnit> period){
216+
add_high_precision_cyclic_action(action, period, current_state);
104217
}
105218

106219
#endif

Src/ST-LIB_LOW/StateMachine/StateMachine.cpp

Lines changed: 53 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@
55
#include "StateMachine/StateMachine.hpp"
66
#include "ErrorHandler/ErrorHandler.hpp"
77

8-
TimedAction::TimedAction(function<void()> action, uint32_t microseconds) :
9-
action(action), microseconds(microseconds) {}
10-
118
void State::enter() {
129
for (function<void()> action : on_enter_actions) {
1310
action();
@@ -20,6 +17,44 @@ void State::exit() {
2017
}
2118
}
2219

20+
void State::unregister_all_timed_actions(){
21+
for(const TimedAction& timed_action : cyclic_actions){
22+
switch(timed_action.alarm_precision){
23+
case LOW_PRECISION:
24+
Time::unregister_low_precision_alarm(timed_action.id);
25+
break;
26+
case MID_PRECISION:
27+
Time::unregister_mid_precision_alarm(timed_action.id);
28+
break;
29+
case HIGH_PRECISION:
30+
Time::unregister_high_precision_alarm(timed_action.id);
31+
break;
32+
default:
33+
ErrorHandler("Cannot unregister timed action with erroneus alarm precision, Alarm Precision Type: %d", timed_action.alarm_precision);
34+
break;
35+
}
36+
}
37+
}
38+
39+
void State::register_all_timed_actions(){
40+
for(TimedAction& timed_action : cyclic_actions){
41+
switch(timed_action.alarm_precision){
42+
case LOW_PRECISION:
43+
timed_action.id = Time::register_low_precision_alarm(timed_action.period, timed_action.action);
44+
break;
45+
case MID_PRECISION:
46+
timed_action.id = Time::register_mid_precision_alarm(timed_action.period, timed_action.action).value();
47+
break;
48+
case HIGH_PRECISION:
49+
timed_action.id = Time::register_high_precision_alarm(timed_action.period, timed_action.action).value();
50+
break;
51+
default:
52+
ErrorHandler("Cannot register timed action with erroneus alarm precision, Alarm Precision Type: %d", timed_action.alarm_precision);
53+
break;
54+
}
55+
}
56+
}
57+
2358
/**
2459
* This is a constructor for a StateMachine object that initializes the initial state and creates a
2560
* State object for it.
@@ -152,18 +187,19 @@ void StateMachine::add_transition(uint8_t old_state, uint8_t new_state,
152187
* @param state The state to which the nested state machine is being added.
153188
*/
154189
void StateMachine::add_state_machine(StateMachine& state_machine, uint8_t state) {
190+
if(nested_state_machine.contains(state)){
191+
ErrorHandler("Only one Nested State Machine can be added per state, tried to add to state: %d", state);
192+
return;
193+
}
194+
195+
if(not state_machine.states[state_machine.current_state].cyclic_actions.empty()){
196+
ErrorHandler("Nested State Machine current state has actions registered, must be empty until nesting");
197+
}
198+
155199
nested_state_machine[state] = &state_machine;
156200

157201
if (current_state != state) {
158-
for (uint8_t timed_action : state_machine.current_state_timed_actions_in_microseconds) {
159-
Time::unregister_high_precision_alarm(timed_action);
160-
}
161-
state_machine.current_state_timed_actions_in_microseconds.clear();
162-
163-
for (uint8_t timed_action : state_machine.current_state_timed_actions_in_milliseconds) {
164-
Time::unregister_low_precision_alarm(timed_action);
165-
}
166-
state_machine.current_state_timed_actions_in_milliseconds.clear();
202+
state_machine.is_on = false;
167203
}
168204
}
169205

@@ -189,27 +225,11 @@ void StateMachine::force_change_state(uint8_t new_state) {
189225
return;
190226
}
191227

192-
for (uint8_t timed_action : current_state_timed_actions_in_microseconds) {
193-
Time::unregister_high_precision_alarm(timed_action);
194-
}
195-
current_state_timed_actions_in_microseconds.clear();
196-
197-
for (uint8_t timed_action : current_state_timed_actions_in_milliseconds) {
198-
Time::unregister_low_precision_alarm(timed_action);
199-
}
200-
current_state_timed_actions_in_milliseconds.clear();
228+
states[current_state].unregister_all_timed_actions();
201229

202230
if (nested_state_machine.contains(current_state)) {
203231
StateMachine* nested_sm = nested_state_machine[current_state];
204-
for (uint8_t timed_action : nested_sm->current_state_timed_actions_in_microseconds) {
205-
Time::unregister_high_precision_alarm(timed_action);
206-
}
207-
nested_sm->current_state_timed_actions_in_microseconds.clear();
208-
209-
for (uint8_t timed_action : nested_sm->current_state_timed_actions_in_milliseconds) {
210-
Time::unregister_low_precision_alarm(timed_action);
211-
}
212-
nested_sm->current_state_timed_actions_in_milliseconds.clear();
232+
nested_sm->states[nested_sm->current_state].unregister_all_timed_actions();
213233
}
214234

215235
states[current_state].exit();
@@ -221,22 +241,9 @@ void StateMachine::force_change_state(uint8_t new_state) {
221241
if (nested_state_machine.contains(current_state)) {
222242
StateMachine* nested_sm = nested_state_machine[current_state];
223243
nested_sm->current_state = nested_sm->initial_state;
244+
nested_sm->is_on = true;
245+
nested_sm->states[nested_sm->current_state].register_all_timed_actions();
224246
}
225247

226-
for (TimedAction timed_action : states[current_state].cyclic_actions) {
227-
if (timed_action.microseconds % 1000 == 0) {
228-
optional<uint8_t> optional_id = Time::register_low_precision_alarm(timed_action.microseconds/1000, timed_action.action);
229-
if (not optional_id) {
230-
ErrorHandler("The timed action could not be registered");
231-
}
232-
current_state_timed_actions_in_milliseconds.push_back(optional_id.value());
233-
234-
} else {
235-
optional<uint8_t> optional_id = Time::register_high_precision_alarm(timed_action.microseconds, timed_action.action);
236-
if (not optional_id) {
237-
ErrorHandler("The timed action could not be registered");
238-
}
239-
current_state_timed_actions_in_microseconds.push_back(optional_id.value());
240-
}
241-
}
248+
states[current_state].register_all_timed_actions();
242249
}

0 commit comments

Comments
 (0)