Skip to content

Commit bd35e17

Browse files
author
Gin
committed
refactor stop button logic
1 parent 6aac876 commit bd35e17

File tree

8 files changed

+149
-149
lines changed

8 files changed

+149
-149
lines changed

Common/Cpp/Options/ButtonOption.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <atomic>
99
#include "Common/Cpp/Containers/Pimpl.tpp"
1010
#include "Common/Cpp/Concurrency/SpinLock.h"
11+
#include "Common/Cpp/StringTools.h"
1112
//#include "Common/Cpp/Json/JsonValue.h"
1213
#include "ButtonOption.h"
1314

@@ -178,6 +179,68 @@ void ButtonOption::set_label(std::string label){
178179

179180

180181

182+
struct DeferredStopButtonOption::Data{
183+
std::atomic<bool> m_stop_requested;
184+
std::string m_ready_text;
185+
std::string m_pressed_text;
186+
187+
Data(const std::string& iteration_name)
188+
: m_stop_requested(false)
189+
, m_ready_text("Stop after Current " + StringTools::capitalize(iteration_name))
190+
, m_pressed_text("Program will stop after current " + StringTools::uncapitalize(iteration_name) + "...")
191+
{}
192+
};
193+
194+
195+
DeferredStopButtonOption::ResetOnExit::ResetOnExit(DeferredStopButtonOption& button)
196+
: m_button(button)
197+
{
198+
button.reset_and_ready();
199+
}
200+
201+
DeferredStopButtonOption::ResetOnExit::~ResetOnExit(){
202+
m_button.set_idle();
203+
}
204+
205+
206+
DeferredStopButtonOption::~DeferredStopButtonOption(){
207+
}
208+
209+
DeferredStopButtonOption::DeferredStopButtonOption(
210+
const std::string& iteration_name,
211+
int button_height,
212+
int text_size
213+
)
214+
: ButtonOption(
215+
"<b>Stop after current " + StringTools::uncapitalize(iteration_name) + ":",
216+
ButtonCell::DISABLED,
217+
"Stop after Current " + StringTools::capitalize(iteration_name),
218+
button_height,
219+
text_size
220+
)
221+
, m_data(CONSTRUCT_TOKEN, iteration_name)
222+
{}
223+
224+
bool DeferredStopButtonOption::should_stop() const{
225+
return m_data->m_stop_requested.load(std::memory_order_relaxed);
226+
}
227+
228+
void DeferredStopButtonOption::reset_and_ready(){
229+
m_data->m_stop_requested.store(false, std::memory_order_relaxed);
230+
this->set_enabled(true);
231+
this->set_text(m_data->m_ready_text);
232+
}
233+
234+
void DeferredStopButtonOption::set_idle(){
235+
this->set_enabled(false);
236+
this->set_text(m_data->m_ready_text);
237+
}
238+
239+
void DeferredStopButtonOption::press_button(){
240+
m_data->m_stop_requested.store(true, std::memory_order_relaxed);
241+
this->set_enabled(false);
242+
this->set_text(m_data->m_pressed_text);
243+
}
181244

182245

183246
}

Common/Cpp/Options/ButtonOption.h

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class ButtonCell : public ConfigOption{
2626
};
2727

2828
public:
29-
~ButtonCell();
29+
virtual ~ButtonCell();
3030
ButtonCell(const ButtonCell& x);
3131
ButtonCell(
3232
std::string text,
@@ -55,7 +55,7 @@ class ButtonCell : public ConfigOption{
5555
int button_height() const;
5656
int text_size() const;
5757

58-
void press_button();
58+
virtual void press_button();
5959

6060
// virtual void load_json(const JsonValue& json) override;
6161
// virtual JsonValue to_json() const override;
@@ -75,7 +75,7 @@ class ButtonCell : public ConfigOption{
7575

7676
class ButtonOption : public ButtonCell{
7777
public:
78-
~ButtonOption();
78+
virtual ~ButtonOption();
7979
ButtonOption(const ButtonOption& x) = delete;
8080
ButtonOption(
8181
std::string label, // the description of the button, shown on the left side of the button
@@ -104,6 +104,62 @@ class ButtonOption : public ButtonCell{
104104

105105

106106

107+
// A button that allows graceful stopping after completing the current unit of work.
108+
// This button manages its own state and can be queried to determine if a stop has been requested.
109+
//
110+
// Usage:
111+
// 1. Add as a member: DeferredStopButtonOption STOP_BUTTON;
112+
// 2. In constructor: PA_ADD_OPTION(STOP_BUTTON);
113+
// 3. At program start:
114+
// STOP_BUTTON.reset_and_enable();
115+
// DeferredStopButtonOption::ResetOnExit reset_on_exit(STOP_BUTTON);
116+
// 4. In main loop: if (STOP_BUTTON.should_stop()) break;
117+
// 5. On program exit: ResetOnExit will automatically reset the button via RAII
118+
class DeferredStopButtonOption : public ButtonOption{
119+
public:
120+
virtual ~DeferredStopButtonOption();
121+
DeferredStopButtonOption(const DeferredStopButtonOption& x) = delete;
122+
123+
// iteration_name: The name of the unit of work (e.g., "Batch", "Round", "Iteration")
124+
// Used to construct button text like "Stop after Current Batch"
125+
// Can be capitalized or uncapitalized.
126+
DeferredStopButtonOption(
127+
const std::string& iteration_name = "Iteration",
128+
int button_height = 0,
129+
int text_size = 16
130+
);
131+
132+
// RAII guard to automatically reset button when program exits
133+
// Define this guard at beginning of a program running session.
134+
class ResetOnExit{
135+
public:
136+
ResetOnExit(DeferredStopButtonOption& button);
137+
~ResetOnExit();
138+
private:
139+
DeferredStopButtonOption& m_button;
140+
};
141+
142+
// Called by the program to check if user has pressed the button to
143+
// request a stop.
144+
bool should_stop() const;
145+
146+
virtual void press_button() override;
147+
148+
private:
149+
// Reset the button to be ready to press during a program run.
150+
// This is usually called by ResetOnExit.
151+
void reset_and_ready();
152+
153+
// Set button to idle statle after whena program finishes running.
154+
// This is usually called by ResetOnExit.
155+
void set_idle();
156+
157+
friend ResetOnExit;
158+
struct Data;
159+
Pimpl<Data> m_data;
160+
};
161+
162+
107163

108164
}
109165
#endif

SerialPrograms/Source/PokemonLZA/Programs/Farming/PokemonLZA_JacintheInfiniteFarmer.cpp

Lines changed: 6 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,8 @@ std::unique_ptr<StatsTracker> JacintheInfiniteFarmer_Descriptor::make_stats() co
9595
}
9696

9797

98-
JacintheInfiniteFarmer::~JacintheInfiniteFarmer(){
99-
STOP_AFTER_CURRENT.remove_listener(*this);
100-
}
101-
10298
JacintheInfiniteFarmer::JacintheInfiniteFarmer()
103-
: m_stop_after_current(false)
99+
: STOP_AFTER_CURRENT("Round")
104100
, NUM_ROUNDS(
105101
"<b>Number of Rounds to Run:</b><br>"
106102
"Zero will run until 'Stop after Current Round' is pressed or the program is manually stopped.</b>",
@@ -137,35 +133,9 @@ JacintheInfiniteFarmer::JacintheInfiniteFarmer()
137133
PA_ADD_OPTION(NUM_ROUNDS);
138134
PA_ADD_OPTION(GO_HOME_WHEN_DONE);
139135
PA_ADD_OPTION(NOTIFICATIONS);
140-
141-
STOP_AFTER_CURRENT.set_idle();
142-
STOP_AFTER_CURRENT.add_listener(*this);
143-
}
144-
145-
146-
147-
JacintheInfiniteFarmer::StopButton::StopButton()
148-
: ButtonOption(
149-
"<b>Stop after current round:",
150-
"Stop after current round",
151-
0, 16
152-
)
153-
{}
154-
void JacintheInfiniteFarmer::StopButton::set_idle(){
155-
this->set_enabled(false);
156-
this->set_text("Stop after Current Round");
157-
}
158-
void JacintheInfiniteFarmer::StopButton::set_ready(){
159-
this->set_enabled(true);
160-
this->set_text("Stop after Current Round");
161-
}
162-
void JacintheInfiniteFarmer::StopButton::set_pressed(){
163-
this->set_enabled(false);
164-
this->set_text("Program will stop after current round...");
165136
}
166137

167138

168-
169139
bool JacintheInfiniteFarmer::talk_to_jacinthe(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
170140
JacintheInfiniteFarmer_Descriptor::Stats& stats = env.current_stats<JacintheInfiniteFarmer_Descriptor::Stats>();
171141

@@ -217,7 +187,7 @@ bool JacintheInfiniteFarmer::talk_to_jacinthe(SingleSwitchProgramEnvironment& en
217187
// - When we lose to Jacinthe, we have some dialog after the battle and returns to the overworld.
218188
env.log("Detected A button.");
219189
env.console.overlay().add_log("Button A Detected");
220-
if (m_stop_after_current.load(std::memory_order_relaxed)){
190+
if (STOP_AFTER_CURRENT.should_stop()){
221191
return true; // true means the program should stop
222192
}
223193

@@ -234,7 +204,7 @@ bool JacintheInfiniteFarmer::talk_to_jacinthe(SingleSwitchProgramEnvironment& en
234204
seen_selection_arrow = true;
235205
// This is when Jacinthe is asking whether you want
236206
// to start the battle or continue the battle
237-
if (m_stop_after_current.load(std::memory_order_relaxed)){
207+
if (STOP_AFTER_CURRENT.should_stop()){
238208
env.console.overlay().add_log("Dialog Choice: Cancel");
239209
pbf_press_button(context, BUTTON_B, 160ms, 80ms);
240210
} else{
@@ -331,41 +301,20 @@ void JacintheInfiniteFarmer::run_round(SingleSwitchProgramEnvironment& env, ProC
331301
}
332302

333303

334-
class JacintheInfiniteFarmer::ResetOnExit{
335-
public:
336-
ResetOnExit(StopButton& button)
337-
: m_button(button)
338-
{}
339-
~ResetOnExit(){
340-
m_button.set_idle();
341-
}
342-
343-
private:
344-
StopButton& m_button;
345-
};
346-
347-
void JacintheInfiniteFarmer::on_press(){
348-
global_logger_tagged().log("Stop after current requested...");
349-
m_stop_after_current.store(true, std::memory_order_relaxed);
350-
STOP_AFTER_CURRENT.set_pressed();
351-
}
352-
353304
void JacintheInfiniteFarmer::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
354305
assert_16_9_720p_min(env.logger(), env.console);
355306

356307
JacintheInfiniteFarmer_Descriptor::Stats& stats = env.current_stats<JacintheInfiniteFarmer_Descriptor::Stats>();
357-
m_stop_after_current.store(false, std::memory_order_relaxed);
358-
STOP_AFTER_CURRENT.set_ready();
359-
ResetOnExit reset_button_on_exit(STOP_AFTER_CURRENT);
308+
309+
DeferredStopButtonOption::ResetOnExit reset_on_exit(STOP_AFTER_CURRENT);
360310
pbf_mash_button(context, BUTTON_B, 1000ms);
361311

362312
// auto lobby = env.console.video().snapshot();
363313

364314
while (true){
365315
send_program_status_notification(env, NOTIFICATION_STATUS_UPDATE);
366316
if (NUM_ROUNDS != 0 && stats.rounds >= NUM_ROUNDS) {
367-
m_stop_after_current.store(true, std::memory_order_relaxed);
368-
STOP_AFTER_CURRENT.set_pressed();
317+
break;
369318
}
370319
if (talk_to_jacinthe(env, context)){
371320
break;

SerialPrograms/Source/PokemonLZA/Programs/Farming/PokemonLZA_JacintheInfiniteFarmer.h

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
#ifndef PokemonAutomation_PokemonLZA_JacintheInfiniteFarmer_H
88
#define PokemonAutomation_PokemonLZA_JacintheInfiniteFarmer_H
99

10-
#include <atomic>
1110
#include "Common/Cpp/Options/BooleanCheckBoxOption.h"
1211
#include "Common/Cpp/Options/SimpleIntegerOption.h"
1312
#include "Common/Cpp/Options/ButtonOption.h"
@@ -36,12 +35,10 @@ class JacintheInfiniteFarmer_Descriptor : public SingleSwitchProgramDescriptor{
3635
};
3736

3837

39-
class JacintheInfiniteFarmer : public SingleSwitchProgramInstance, public ButtonListener{
38+
class JacintheInfiniteFarmer : public SingleSwitchProgramInstance{
4039
public:
41-
~JacintheInfiniteFarmer();
4240
JacintheInfiniteFarmer();
4341

44-
virtual void on_press() override;
4542
virtual void program(SingleSwitchProgramEnvironment& env, ProControllerContext& context) override;
4643

4744
private:
@@ -58,17 +55,7 @@ class JacintheInfiniteFarmer : public SingleSwitchProgramInstance, public Button
5855
void run_round(SingleSwitchProgramEnvironment& env, ProControllerContext& context);
5956

6057
private:
61-
class StopButton : public ButtonOption{
62-
public:
63-
StopButton();
64-
void set_idle();
65-
void set_ready();
66-
void set_pressed();
67-
};
68-
class ResetOnExit;
69-
70-
std::atomic<bool> m_stop_after_current;
71-
StopButton STOP_AFTER_CURRENT;
58+
DeferredStopButtonOption STOP_AFTER_CURRENT;
7259
SimpleIntegerOption<uint32_t> NUM_ROUNDS;
7360
GoHomeWhenDoneOption GO_HOME_WHEN_DONE;
7461

0 commit comments

Comments
 (0)