Skip to content

Rewrite examples/Braccio_Learn_and_Repeat using State design pattern #71

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

Merged
merged 20 commits into from
May 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
d7368cc
Fix: Prevent sample array (values) from overflowing.
aentinger Apr 27, 2022
250343d
Show a count of captured joint states instead of individual sample cn…
aentinger Apr 27, 2022
f6931cf
Prevent race conditions due to multi-threaded LVGL access.
aentinger Apr 28, 2022
671909c
Fix: Do not perform a last capture when the sample cnt has been excee…
aentinger May 2, 2022
e7d0fac
Fix: Execute state code every 100 ms, instead of a 100 ms delay, whic…
aentinger May 2, 2022
34ea2ba
Fix: Real sample time was in fact 200 seconds, not 20 (comment/realit…
aentinger May 2, 2022
9016b55
Simplify state transition logic.
aentinger May 2, 2022
6f22436
Use enum classes for increased type safety.
aentinger May 2, 2022
295955e
Adding separators for better readability.
aentinger May 2, 2022
7324dc3
Extract code for RECORD state into separate function.
aentinger May 2, 2022
b1bcbf1
Extract code for REPLAY state into separate function.
aentinger May 2, 2022
c883902
Extract code for ZERO_POSITION state into separate function.
aentinger May 2, 2022
1813c99
Replace if state selection with switch/case.
aentinger May 2, 2022
8bcacfd
Reimplementing application flow using the State Design Pattern.
aentinger May 3, 2022
e4b1be0
temp save.
aentinger May 3, 2022
a801ac3
Move button enable code within the application.
aentinger May 4, 2022
0a057b5
Replace static states with dynamic allocation (simplifies init code).
aentinger May 4, 2022
4353e42
Ensure that the REPLAY button gets disabled as soon as you start reco…
aentinger May 4, 2022
0b7749e
Restore behaviour that both joystick click and ENTER button work.
aentinger May 4, 2022
b2384df
Sampling every 50 instead of very 100 ms yields a smoother replay.
aentinger May 4, 2022
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
256 changes: 256 additions & 0 deletions examples/Braccio_Learn_and_Repeat/AppState.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
/**************************************************************************************
* INCLUDE
**************************************************************************************/

#include "AppState.h"

/**************************************************************************************
* DEFINE
**************************************************************************************/

#define COLOR_TEAL 0x00878F
#define COLOR_LIGHT_TEAL 0x62AEB2
#define COLOR_ORANGE 0xE47128

#define BUTTON_ENTER 6

/**************************************************************************************
* CONSTANT
**************************************************************************************/

static int const SAMPLE_BUF_SIZE = 6*200*2; /* 20 seconds. */
static float const HOME_POS[6] = {157.5, 157.5, 157.5, 157.5, 157.5, 90.0};

/**************************************************************************************
* GLOBAL VARIABLES
**************************************************************************************/

lv_obj_t * counter;
lv_obj_t * btnm;
const char * btnm_map[] = { "RECORD", "\n", "REPLAY", "\n", "ZERO_POSITION", "\n", "\0" };

static float sample_buf[SAMPLE_BUF_SIZE];
static int sample_cnt;

extern LearnAndRepeatApp app;

/**************************************************************************************
* FUNCTION DEFINITION
**************************************************************************************/

static void event_handler_menu(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);

if (code == LV_EVENT_CLICKED || (code == LV_EVENT_KEY && Braccio.getKey() == BUTTON_ENTER))
{
lv_obj_t * obj = lv_event_get_target(e);
uint32_t const id = lv_btnmatrix_get_selected_btn(obj);

switch (id)
{
case 0: app.update(EventSource::Button_Record); break;
case 1: app.update(EventSource::Button_Replay); break;
case 2: app.update(EventSource::Button_ZeroPosition); break;
}
}
}

void custom_main_menu()
{
Braccio.lvgl_lock();
static lv_style_t style_focus;
lv_style_init(&style_focus);
lv_style_set_outline_color(&style_focus, lv_color_hex(COLOR_ORANGE));
lv_style_set_outline_width(&style_focus, 4);

static lv_style_t style_btn;
lv_style_init(&style_btn);
lv_style_set_bg_color(&style_btn, lv_color_hex(COLOR_LIGHT_TEAL));
lv_style_set_text_color(&style_btn, lv_color_white());

btnm = lv_btnmatrix_create(lv_scr_act());
lv_obj_set_size(btnm, 240, 240);
lv_btnmatrix_set_map(btnm, btnm_map);
lv_obj_align(btnm, LV_ALIGN_CENTER, 0, 0);

lv_obj_add_style(btnm, &style_btn, LV_PART_ITEMS);
lv_obj_add_style(btnm, &style_focus, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);

lv_btnmatrix_set_btn_ctrl(btnm, 0, LV_BTNMATRIX_CTRL_DISABLED);
lv_btnmatrix_set_btn_ctrl(btnm, 1, LV_BTNMATRIX_CTRL_DISABLED);
lv_btnmatrix_set_btn_ctrl(btnm, 2, LV_BTNMATRIX_CTRL_DISABLED);

lv_btnmatrix_set_one_checked(btnm, true);
lv_btnmatrix_set_selected_btn(btnm, 0);
lv_btnmatrix_set_btn_ctrl(btnm, 2, LV_BTNMATRIX_CTRL_CHECKED);

counter = lv_label_create(btnm);
lv_label_set_text_fmt(counter, "Counter: %d" , 0);
lv_obj_align(counter, LV_ALIGN_CENTER, 0, 80);

lv_obj_add_event_cb(btnm, event_handler_menu, LV_EVENT_ALL, NULL);
Braccio.lvgl_unlock();

Braccio.connectJoystickTo(btnm);
}

/**************************************************************************************
* State
**************************************************************************************/

State * State::handle_OnZeroPosition()
{
return new ZeroState();
}

/**************************************************************************************
* IdleState
**************************************************************************************/

void IdleState::onEnter()
{
lv_btnmatrix_set_btn_ctrl(btnm, 2, LV_BTNMATRIX_CTRL_CHECKED);
}

void IdleState::onExit()
{
lv_btnmatrix_clear_btn_ctrl(btnm, 2, LV_BTNMATRIX_CTRL_CHECKED);
}

State * IdleState::handle_OnRecord()
{
return new RecordState();
}

State * IdleState::handle_OnReplay()
{
return new ReplayState();
}

/**************************************************************************************
* RecordState
**************************************************************************************/

void RecordState::onEnter()
{
btnm_map[0] = "STOP";
lv_btnmatrix_set_btn_ctrl(btnm, 0, LV_BTNMATRIX_CTRL_CHECKED);
lv_btnmatrix_set_btn_ctrl(btnm, 1, LV_BTNMATRIX_CTRL_DISABLED);

Braccio.disengage();
sample_cnt = 0;
}

void RecordState::onExit()
{
btnm_map[0] = "RECORD";
lv_btnmatrix_clear_btn_ctrl(btnm, 0, LV_BTNMATRIX_CTRL_CHECKED);
lv_btnmatrix_clear_btn_ctrl(btnm, 1, LV_BTNMATRIX_CTRL_DISABLED);
lv_label_set_text_fmt(counter, "Counter: %d" , 0);

Braccio.engage();
}

State * RecordState::handle_OnRecord()
{
return new IdleState();
}

State * RecordState::handle_OnTimerTick()
{
/* The sample buffer is full. */
if (sample_cnt >= SAMPLE_BUF_SIZE) {
return new IdleState();
}

/* We still have space, let's sample some data. */
Braccio.positions(sample_buf + sample_cnt);
sample_cnt += 6;

/* Update sample counter. */
lv_label_set_text_fmt(counter, "Counter: %d" , (sample_cnt / 6));

return this;
}

/**************************************************************************************
* ReplayState
**************************************************************************************/

void ReplayState::onEnter()
{
btnm_map[2] = "STOP";
lv_btnmatrix_set_btn_ctrl(btnm, 0, LV_BTNMATRIX_CTRL_DISABLED);
lv_btnmatrix_set_btn_ctrl(btnm, 1, LV_BTNMATRIX_CTRL_CHECKED);
}

void ReplayState::onExit()
{
btnm_map[2] = "REPLAY";
lv_btnmatrix_clear_btn_ctrl(btnm, 0, LV_BTNMATRIX_CTRL_DISABLED);
lv_btnmatrix_clear_btn_ctrl(btnm, 1, LV_BTNMATRIX_CTRL_CHECKED);
lv_label_set_text_fmt(counter, "Counter: %d" , 0);
}

State * ReplayState::handle_OnReplay()
{
return new IdleState();
}

State * ReplayState::handle_OnTimerTick()
{
/* All samples have been replayed. */
if (_replay_cnt >= sample_cnt) {
return new IdleState();
}

/* Replay recorded movements. */
Braccio.moveTo(sample_buf[_replay_cnt + 0],
sample_buf[_replay_cnt + 1],
sample_buf[_replay_cnt + 2],
sample_buf[_replay_cnt + 3],
sample_buf[_replay_cnt + 4],
sample_buf[_replay_cnt + 5]);
_replay_cnt += 6;

lv_label_set_text_fmt(counter, "Counter: %d" , (_replay_cnt / 6));

return this;
}

/**************************************************************************************
* ReplayState
**************************************************************************************/

State * ZeroState::handle_OnTimerTick()
{
return new IdleState();
}

void ZeroState::onEnter()
{
lv_btnmatrix_set_btn_ctrl(btnm, 1, LV_BTNMATRIX_CTRL_DISABLED);
lv_btnmatrix_set_btn_ctrl(btnm, 2, LV_BTNMATRIX_CTRL_CHECKED);

Braccio.engage();
delay(100);
Braccio.moveTo(HOME_POS[0], HOME_POS[1], HOME_POS[2], HOME_POS[3], HOME_POS[4], HOME_POS[5]);
delay(500);
}

void ZeroState::onExit()
{
lv_btnmatrix_clear_btn_ctrl(btnm, 2, LV_BTNMATRIX_CTRL_CHECKED);
}

/**************************************************************************************
* LearnAndRepeatApp
**************************************************************************************/

void LearnAndRepeatApp::enableButtons()
{
/* Enable buttons once init is complete. */
lv_btnmatrix_clear_btn_ctrl(btnm, 0, LV_BTNMATRIX_CTRL_DISABLED);
lv_btnmatrix_clear_btn_ctrl(btnm, 2, LV_BTNMATRIX_CTRL_DISABLED);
}
Loading