Skip to content

Commit 26b05cf

Browse files
committed
Refactor SerialPABotBase status thread.
1 parent 9265dcd commit 26b05cf

File tree

8 files changed

+328
-357
lines changed

8 files changed

+328
-357
lines changed

SerialPrograms/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,7 @@ file(GLOB MAIN_SOURCES
729729
Source/Controllers/SerialPABotBase/SerialPABotBase_Routines_Protocol.cpp
730730
Source/Controllers/SerialPABotBase/SerialPABotBase_Routines_Protocol.h
731731
Source/Controllers/SerialPABotBase/SerialPABotBase_SelectorWidget.h
732+
Source/Controllers/SerialPABotBase/SerialPABotBase_StatusThread.h
732733
Source/Integrations/DiscordIntegrationSettings.cpp
733734
Source/Integrations/DiscordIntegrationSettings.h
734735
Source/Integrations/DiscordIntegrationTable.cpp

SerialPrograms/Source/Controllers/HidControllers/HID_Keyboard_SerialPABotBase.cpp

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*
55
*/
66

7+
#include "CommonFramework/Options/Environment/ThemeSelectorOption.h"
78
#include "Controllers/SerialPABotBase/SerialPABotBase_Routines_Protocol.h"
89
#include "HID_Keyboard_SerialPABotBase.h"
910

@@ -46,24 +47,17 @@ SerialPABotBase_Keyboard::SerialPABotBase_Keyboard(
4647
if (current_controller != ControllerType::HID_Keyboard){
4748
throw SerialProtocolException(logger, PA_CURRENT_FUNCTION, "Failed to set controller type.");
4849
}
50+
51+
m_status_thread.reset(new SerialPABotBase::ControllerStatusThread(
52+
connection, *this
53+
));
4954
}
5055
SerialPABotBase_Keyboard::~SerialPABotBase_Keyboard(){
5156
stop();
52-
// m_status_thread.join();
5357
}
5458
void SerialPABotBase_Keyboard::stop(){
55-
if (m_stopping.exchange(true)){
56-
return;
57-
}
5859
Keyboard::stop();
59-
m_scope.cancel(nullptr);
60-
{
61-
std::unique_lock<std::mutex> lg(m_sleep_lock);
62-
if (m_serial){
63-
m_serial->notify_all();
64-
}
65-
m_cv.notify_all();
66-
}
60+
m_status_thread.reset();
6761
}
6862

6963

@@ -109,7 +103,42 @@ void SerialPABotBase_Keyboard::execute_state(
109103
const SuperscalarScheduler::ScheduleEntry& entry
110104
){
111105
// TODO
106+
m_logger.log("SerialPABotBase_Keyboard::execute_state() - Not implemented yet.", COLOR_RED);
107+
}
108+
109+
110+
111+
void SerialPABotBase_Keyboard::update_status(Cancellable& cancellable){
112+
pabb_MsgAckRequestI32 response;
113+
m_serial->issue_request_and_wait(
114+
SerialPABotBase::MessageControllerStatus(),
115+
&cancellable
116+
).convert<PABB_MSG_ACK_REQUEST_I32>(m_logger, response);
117+
118+
uint32_t status = response.data;
119+
bool status_connected = status & 1;
120+
bool status_ready = status & 2;
121+
122+
std::string str;
123+
str += "Connected: " + (status_connected
124+
? html_color_text("Yes", theme_friendly_darkblue())
125+
: html_color_text("No", COLOR_RED)
126+
);
127+
str += " - Ready: " + (status_ready
128+
? html_color_text("Yes", theme_friendly_darkblue())
129+
: html_color_text("No", COLOR_RED)
130+
);
131+
132+
m_handle.set_status_line1(str);
112133
}
134+
void SerialPABotBase_Keyboard::stop_with_error(std::string error_message){
135+
{
136+
WriteSpinLock lg(m_error_lock);
137+
m_error_string = error_message;
138+
}
139+
m_serial->stop(std::move(error_message));
140+
}
141+
113142

114143

115144

SerialPrograms/Source/Controllers/HidControllers/HID_Keyboard_SerialPABotBase.h

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include "ClientSource/Connection/BotBase.h"
1111
#include "Controllers/SerialPABotBase/SerialPABotBase_Connection.h"
12+
#include "Controllers/SerialPABotBase/SerialPABotBase_StatusThread.h"
1213
#include "HID_Keyboard.h"
1314
#include "HID_KeyboardWithScheduler.h"
1415

@@ -19,7 +20,8 @@ namespace HidControllers{
1920

2021
class SerialPABotBase_Keyboard final :
2122
public Keyboard,
22-
public KeyboardControllerWithScheduler
23+
public KeyboardControllerWithScheduler,
24+
private SerialPABotBase::ControllerStatusThreadCallback
2325
{
2426
public:
2527
using ContextType = KeyboardContext;
@@ -95,13 +97,8 @@ class SerialPABotBase_Keyboard final :
9597

9698

9799
public:
98-
void stop_with_error(std::string error_message){
99-
{
100-
WriteSpinLock lg(m_error_lock);
101-
m_error_string = error_message;
102-
}
103-
m_serial->stop(std::move(error_message));
104-
}
100+
virtual void update_status(Cancellable& cancellable) override;
101+
virtual void stop_with_error(std::string error_message) override;
105102

106103
std::string error_string() const{
107104
ReadSpinLock lg(m_error_lock);
@@ -134,12 +131,7 @@ class SerialPABotBase_Keyboard final :
134131
mutable SpinLock m_error_lock;
135132
std::string m_error_string;
136133

137-
138-
CancellableHolder<CancellableScope> m_scope;
139-
std::atomic<bool> m_stopping;
140-
std::mutex m_sleep_lock;
141-
std::condition_variable m_cv;
142-
// std::thread m_status_thread;
134+
std::unique_ptr<SerialPABotBase::ControllerStatusThread> m_status_thread;
143135
};
144136

145137

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/* SerialPABotBase: Status Thread Helper
2+
*
3+
* From: https://github.com/PokemonAutomation/
4+
*
5+
* This is a helper class to reuse the code for the status updater thread.
6+
*
7+
*/
8+
9+
#ifndef PokemonAutomation_SerialPABotBase_StatusThread_H
10+
#define PokemonAutomation_SerialPABotBase_StatusThread_H
11+
12+
#include "Common/Cpp/PrettyPrint.h"
13+
#include "SerialPABotBase_Connection.h"
14+
15+
namespace PokemonAutomation{
16+
namespace SerialPABotBase{
17+
18+
19+
20+
21+
struct ControllerStatusThreadCallback{
22+
virtual void update_status(Cancellable& cancellable) = 0;
23+
virtual void stop_with_error(std::string message) = 0;
24+
};
25+
26+
27+
28+
29+
class ControllerStatusThread{
30+
public:
31+
ControllerStatusThread(
32+
SerialPABotBase_Connection& connection,
33+
ControllerStatusThreadCallback& callback
34+
)
35+
: m_connection(connection)
36+
, m_callback(callback)
37+
, m_stopping(false)
38+
, m_status_thread(&ControllerStatusThread::status_thread, this)
39+
{}
40+
~ControllerStatusThread(){
41+
if (m_stopping.exchange(true)){
42+
return;
43+
}
44+
m_scope.cancel(nullptr);
45+
{
46+
std::unique_lock<std::mutex> lg(m_sleep_lock);
47+
BotBaseController* botbase = m_connection.botbase();
48+
if (botbase){
49+
botbase->notify_all();
50+
}
51+
m_cv.notify_all();
52+
}
53+
m_status_thread.join();
54+
}
55+
56+
private:
57+
void status_thread(){
58+
constexpr std::chrono::milliseconds PERIOD(1000);
59+
std::atomic<WallClock> last_ack(current_time());
60+
61+
std::thread watchdog([&, this]{
62+
WallClock next_ping = current_time();
63+
while (true){
64+
if (m_stopping.load(std::memory_order_relaxed) || !m_connection.is_ready()){
65+
break;
66+
}
67+
68+
auto last = current_time() - last_ack.load(std::memory_order_relaxed);
69+
std::chrono::duration<double> seconds = last;
70+
if (last > 2 * PERIOD){
71+
std::string text = "Last Ack: " + tostr_fixed(seconds.count(), 3) + " seconds ago";
72+
m_connection.set_status_line1(text, COLOR_RED);
73+
// m_logger.log("Connection issue detected. Turning on all logging...");
74+
// settings.log_everything.store(true, std::memory_order_release);
75+
}
76+
77+
std::unique_lock<std::mutex> lg(m_sleep_lock);
78+
if (m_stopping.load(std::memory_order_relaxed) || !m_connection.is_ready()){
79+
break;
80+
}
81+
82+
WallClock now = current_time();
83+
next_ping += PERIOD;
84+
if (now + PERIOD < next_ping){
85+
next_ping = now + PERIOD;
86+
}
87+
m_cv.wait_until(lg, next_ping);
88+
}
89+
});
90+
91+
92+
WallClock next_ping = current_time();
93+
while (true){
94+
if (m_stopping.load(std::memory_order_relaxed) || !m_connection.is_ready()){
95+
break;
96+
}
97+
98+
std::string error;
99+
try{
100+
m_callback.update_status(m_scope);
101+
last_ack.store(current_time(), std::memory_order_relaxed);
102+
}catch (OperationCancelledException&){
103+
break;
104+
}catch (InvalidConnectionStateException&){
105+
break;
106+
}catch (SerialProtocolException& e){
107+
error = e.message();
108+
}catch (ConnectionException& e){
109+
error = e.message();
110+
}catch (...){
111+
error = "Unknown error.";
112+
}
113+
if (!error.empty()){
114+
m_connection.set_status_line1(error, COLOR_RED);
115+
m_callback.stop_with_error(std::move(error));
116+
}
117+
118+
std::unique_lock<std::mutex> lg(m_sleep_lock);
119+
if (m_stopping.load(std::memory_order_relaxed) || !m_connection.is_ready()){
120+
break;
121+
}
122+
123+
WallClock now = current_time();
124+
next_ping += PERIOD;
125+
if (now + PERIOD < next_ping){
126+
next_ping = now + PERIOD;
127+
}
128+
m_cv.wait_until(lg, next_ping);
129+
}
130+
131+
{
132+
std::unique_lock<std::mutex> lg(m_sleep_lock);
133+
m_cv.notify_all();
134+
}
135+
watchdog.join();
136+
}
137+
138+
private:
139+
SerialPABotBase::SerialPABotBase_Connection& m_connection;
140+
ControllerStatusThreadCallback& m_callback;
141+
CancellableHolder<CancellableScope> m_scope;
142+
std::atomic<bool> m_stopping;
143+
std::mutex m_sleep_lock;
144+
std::condition_variable m_cv;
145+
std::thread m_status_thread;
146+
};
147+
148+
149+
150+
151+
152+
153+
}
154+
}
155+
#endif

0 commit comments

Comments
 (0)