Skip to content

Commit 8bcacfd

Browse files
committed
Reimplementing application flow using the State Design Pattern.
1 parent 1813c99 commit 8bcacfd

File tree

3 files changed

+355
-156
lines changed

3 files changed

+355
-156
lines changed
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
/**************************************************************************************
2+
* INCLUDE
3+
**************************************************************************************/
4+
5+
#include "AppState.h"
6+
7+
/**************************************************************************************
8+
* CONSTANT
9+
**************************************************************************************/
10+
11+
static int const SAMPLE_BUF_SIZE = 6*100*2; /* 20 seconds. */
12+
static float const HOME_POS[6] = {157.5, 157.5, 157.5, 157.5, 157.5, 90.0};
13+
14+
15+
/**************************************************************************************
16+
* GLOBAL VARIABLES
17+
**************************************************************************************/
18+
19+
extern const char * btnm_map[];
20+
extern lv_obj_t * counter;
21+
extern lv_obj_t * btnm;
22+
23+
static float sample_buf[SAMPLE_BUF_SIZE];
24+
static int sample_cnt;
25+
26+
IdleState LearnAndRepeatApp::_idle_state;
27+
RecordState LearnAndRepeatApp::_record_state;
28+
ReplayState LearnAndRepeatApp::_replay_state;
29+
ZeroState LearnAndRepeatApp::_zero_state;
30+
31+
/**************************************************************************************
32+
* State
33+
**************************************************************************************/
34+
35+
State * State::handle_OnZeroPosition()
36+
{
37+
return &LearnAndRepeatApp::_zero_state;
38+
}
39+
40+
/**************************************************************************************
41+
* IdleState
42+
**************************************************************************************/
43+
44+
void IdleState::onEnter()
45+
{
46+
lv_btnmatrix_set_btn_ctrl(btnm, 2, LV_BTNMATRIX_CTRL_CHECKED);
47+
}
48+
49+
void IdleState::onExit()
50+
{
51+
lv_btnmatrix_clear_btn_ctrl(btnm, 2, LV_BTNMATRIX_CTRL_CHECKED);
52+
}
53+
54+
State * IdleState::handle_OnRecord()
55+
{
56+
return &LearnAndRepeatApp::_record_state;
57+
}
58+
59+
State * IdleState::handle_OnReplay()
60+
{
61+
return &LearnAndRepeatApp::_replay_state;
62+
}
63+
64+
/**************************************************************************************
65+
* RecordState
66+
**************************************************************************************/
67+
68+
void RecordState::onEnter()
69+
{
70+
btnm_map[0] = "STOP";
71+
lv_btnmatrix_set_btn_ctrl(btnm, 0, LV_BTNMATRIX_CTRL_CHECKED);
72+
73+
Braccio.disengage();
74+
sample_cnt = 0;
75+
}
76+
77+
void RecordState::onExit()
78+
{
79+
btnm_map[0] = "RECORD";
80+
lv_btnmatrix_clear_btn_ctrl(btnm, 0, LV_BTNMATRIX_CTRL_CHECKED);
81+
lv_btnmatrix_clear_btn_ctrl(btnm, 1, LV_BTNMATRIX_CTRL_DISABLED);
82+
lv_label_set_text_fmt(counter, "Counter: %d" , 0);
83+
84+
Braccio.engage();
85+
}
86+
87+
State * RecordState::handle_OnRecord()
88+
{
89+
return &LearnAndRepeatApp::_idle_state;
90+
}
91+
92+
State * RecordState::handle_OnTimerTick()
93+
{
94+
/* The sample buffer is full. */
95+
if (sample_cnt >= SAMPLE_BUF_SIZE) {
96+
return &LearnAndRepeatApp::_idle_state;
97+
}
98+
99+
/* We still have space, let's sample some data. */
100+
Braccio.positions(sample_buf + sample_cnt);
101+
sample_cnt += 6;
102+
103+
/* Update sample counter. */
104+
lv_label_set_text_fmt(counter, "Counter: %d" , (sample_cnt / 6));
105+
106+
return this;
107+
}
108+
109+
/**************************************************************************************
110+
* ReplayState
111+
**************************************************************************************/
112+
113+
void ReplayState::onEnter()
114+
{
115+
_replay_cnt = 0;
116+
117+
btnm_map[2] = "STOP";
118+
lv_btnmatrix_set_btn_ctrl(btnm, 0, LV_BTNMATRIX_CTRL_DISABLED);
119+
lv_btnmatrix_set_btn_ctrl(btnm, 1, LV_BTNMATRIX_CTRL_CHECKED);
120+
}
121+
122+
void ReplayState::onExit()
123+
{
124+
btnm_map[2] = "REPLAY";
125+
lv_btnmatrix_clear_btn_ctrl(btnm, 0, LV_BTNMATRIX_CTRL_DISABLED);
126+
lv_btnmatrix_clear_btn_ctrl(btnm, 1, LV_BTNMATRIX_CTRL_CHECKED);
127+
lv_label_set_text_fmt(counter, "Counter: %d" , 0);
128+
}
129+
130+
State * ReplayState::handle_OnReplay()
131+
{
132+
return &LearnAndRepeatApp::_idle_state;
133+
}
134+
135+
State * ReplayState::handle_OnTimerTick()
136+
{
137+
/* All samples have been replayed. */
138+
if (_replay_cnt >= sample_cnt) {
139+
return &LearnAndRepeatApp::_idle_state;
140+
}
141+
142+
/* Replay recorded movements. */
143+
Braccio.moveTo(sample_buf[_replay_cnt + 0],
144+
sample_buf[_replay_cnt + 1],
145+
sample_buf[_replay_cnt + 2],
146+
sample_buf[_replay_cnt + 3],
147+
sample_buf[_replay_cnt + 4],
148+
sample_buf[_replay_cnt + 5]);
149+
_replay_cnt += 6;
150+
151+
lv_label_set_text_fmt(counter, "Counter: %d" , (_replay_cnt / 6));
152+
153+
return this;
154+
}
155+
156+
/**************************************************************************************
157+
* ReplayState
158+
**************************************************************************************/
159+
160+
State * ZeroState::handle_OnTimerTick()
161+
{
162+
return &LearnAndRepeatApp::_idle_state;
163+
}
164+
165+
void ZeroState::onEnter()
166+
{
167+
lv_btnmatrix_set_btn_ctrl(btnm, 1, LV_BTNMATRIX_CTRL_DISABLED);
168+
lv_btnmatrix_set_btn_ctrl(btnm, 2, LV_BTNMATRIX_CTRL_CHECKED);
169+
170+
Braccio.engage();
171+
delay(100);
172+
Braccio.moveTo(HOME_POS[0], HOME_POS[1], HOME_POS[2], HOME_POS[3], HOME_POS[4], HOME_POS[5]);
173+
delay(500);
174+
}
175+
176+
void ZeroState::onExit()
177+
{
178+
lv_btnmatrix_clear_btn_ctrl(btnm, 2, LV_BTNMATRIX_CTRL_CHECKED);
179+
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
#ifndef LEARN_AND_REPEAT_APP_STATE_H_
2+
#define LEARN_AND_REPEAT_APP_STATE_H_
3+
4+
/**************************************************************************************
5+
* INCLUDE
6+
**************************************************************************************/
7+
8+
#include <Braccio++.h>
9+
10+
/**************************************************************************************
11+
* TYPEDEF
12+
**************************************************************************************/
13+
14+
enum class EventSource
15+
{
16+
Button_Record, Button_Replay, Button_ZeroPosition, TimerTick
17+
};
18+
19+
enum class StateName
20+
{
21+
Record, Replay, Idle, Zero
22+
};
23+
24+
/**************************************************************************************
25+
* CLASS DECLARATION
26+
**************************************************************************************/
27+
28+
class State
29+
{
30+
public:
31+
virtual ~State() { }
32+
virtual void onEnter() { Serial.println("State::onEnter"); }
33+
virtual void onExit() { Serial.println("State::onExit"); }
34+
virtual StateName name() = 0;
35+
State * update(EventSource const evt_src)
36+
{
37+
State * next_state = this;
38+
switch (evt_src)
39+
{
40+
case EventSource::Button_Record: next_state = handle_OnRecord(); break;
41+
case EventSource::Button_Replay: next_state = handle_OnReplay(); break;
42+
case EventSource::Button_ZeroPosition: next_state = handle_OnZeroPosition(); break;
43+
case EventSource::TimerTick: next_state = handle_OnTimerTick(); break;
44+
}
45+
return next_state;
46+
}
47+
48+
protected:
49+
virtual State * handle_OnRecord() { return this; }
50+
virtual State * handle_OnReplay() { return this; }
51+
virtual State * handle_OnZeroPosition();
52+
virtual State * handle_OnTimerTick() { return this; }
53+
};
54+
55+
class IdleState : public State
56+
{
57+
public:
58+
virtual ~IdleState() { }
59+
virtual StateName name() override { return StateName::Idle; }
60+
virtual void onEnter() override;
61+
virtual void onExit() override;
62+
63+
protected:
64+
virtual State * handle_OnRecord() override;
65+
virtual State * handle_OnReplay() override;
66+
};
67+
68+
class RecordState : public State
69+
{
70+
public:
71+
virtual ~RecordState() { }
72+
virtual StateName name() override { return StateName::Record; }
73+
virtual void onEnter() override;
74+
virtual void onExit() override;
75+
76+
protected:
77+
virtual State * handle_OnRecord() override;
78+
virtual State * handle_OnTimerTick() override;
79+
};
80+
81+
class ReplayState : public State
82+
{
83+
public:
84+
virtual ~ReplayState() { }
85+
virtual StateName name() override { return StateName::Replay; }
86+
virtual void onEnter() override;
87+
virtual void onExit() override;
88+
89+
protected:
90+
virtual State * handle_OnReplay() override;
91+
virtual State * handle_OnTimerTick() override;
92+
93+
private:
94+
int _replay_cnt;
95+
};
96+
97+
class ZeroState : public State
98+
{
99+
public:
100+
virtual ~ZeroState() { }
101+
virtual StateName name() override { return StateName::Zero; }
102+
virtual void onEnter() override;
103+
virtual void onExit() override;
104+
105+
protected:
106+
virtual State * handle_OnTimerTick() override;
107+
};
108+
109+
class LearnAndRepeatApp
110+
{
111+
public:
112+
LearnAndRepeatApp()
113+
: _state{nullptr}
114+
, _mtx{}
115+
{ }
116+
117+
static IdleState _idle_state;
118+
static RecordState _record_state;
119+
static ReplayState _replay_state;
120+
static ZeroState _zero_state;
121+
122+
void update(EventSource const evt_src)
123+
{
124+
mbed::ScopedLock<rtos::Mutex> lock(_mtx);
125+
126+
if (!_state)
127+
{
128+
_state = &_zero_state;
129+
_state->onEnter();
130+
return;
131+
}
132+
133+
State * next_state = _state->update(evt_src);
134+
135+
if (next_state->name() != _state->name())
136+
{
137+
_state->onExit();
138+
_state = next_state;
139+
_state->onEnter();
140+
}
141+
}
142+
143+
private:
144+
State * _state;
145+
rtos::Mutex _mtx;
146+
};
147+
148+
#endif /* LEARN_AND_REPEAT_APP_STATE_H_ */

0 commit comments

Comments
 (0)