Skip to content

Commit

Permalink
feat: refactor to enable teardown of GridUI
Browse files Browse the repository at this point in the history
  • Loading branch information
Tasssadar committed Apr 30, 2024
1 parent 6796f32 commit 0b4f8f2
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 42 deletions.
2 changes: 1 addition & 1 deletion library.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"dependencies": [
{
"name": "RB3201-RBProtocol",
"version": "https://github.com/RoboticsBrno/RB3201-RBProtocol-library/archive/refs/tags/v12.0.0.zip"
"version": "https://github.com/RoboticsBrno/RB3201-RBProtocol-library/archive/refs/tags/v13.0.0.zip"
}
],
"build": {
Expand Down
2 changes: 1 addition & 1 deletion platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ framework = arduino
upload_speed = 921600
monitor_speed = 115200

lib_deps = https://github.com/RoboticsBrno/RB3201-RBProtocol-library/archive/refs/tags/v12.0.0.zip
lib_deps = https://github.com/RoboticsBrno/RB3201-RBProtocol-library/archive/refs/tags/v13.0.0.zip
16 changes: 7 additions & 9 deletions src/builder/widget.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,8 @@ class BuilderMixin {

protected:
void addCallback(const std::string& name, callback_t cb) {
auto& all = self().m_state.callbacks();

auto old = all.find(name);
if (old != all.end()) {
delete static_cast<callback_t*>(old->second);
}

auto* cbHeap = new callback_t(cb);
all[name] = static_cast<void*>(cbHeap); // fuj
auto* cbHeap = static_cast<void*>(new callback_t(cb)); // fuj
self().m_state.addCallback(&callbackTrampoline, &callbackDeleter, name, cbHeap);
}

private:
Expand All @@ -69,6 +62,11 @@ class BuilderMixin {
Constructed w(state);
(*static_cast<callback_t*>(cb))(w);
}

static void callbackDeleter(void *cb) {
auto *cb_typed = static_cast<callback_t*>(cb);
delete cb_typed;
}
};

class Widget {
Expand Down
64 changes: 55 additions & 9 deletions src/gridui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ static void defaultOnPacketReceived(const std::string& cmd, rbjson::Object* pkt)

_GridUi::_GridUi()
: m_protocol(nullptr)
, m_protocol_ours(false)
, m_update_timer(nullptr)
, m_web_server_task(nullptr)
, m_state_mustarrive_id(UINT32_MAX)
, m_states_modified(false) {
}
Expand All @@ -36,6 +39,7 @@ void _GridUi::begin(rb::Protocol* protocol, int cols, int rows, bool enableSplit
}

m_protocol = protocol;
m_protocol_ours = false;

m_layout.reset(new rbjson::Object);
m_layout->set("cols", cols);
Expand All @@ -44,15 +48,18 @@ void _GridUi::begin(rb::Protocol* protocol, int cols, int rows, bool enableSplit
}

rb::Protocol* _GridUi::begin(const char* owner, const char* deviceName) {
auto protocol = new rb::Protocol(owner, deviceName, "Compiled at " __DATE__ " " __TIME__, defaultOnPacketReceived);
// Start serving the web page
m_web_server_task = rb_web_start(80);
if(m_web_server_task == NULL) {
ESP_LOGE("GridUI", "failed to call rb_web_start");
return nullptr;
}

auto protocol = new rb::Protocol(owner, deviceName, "Compiled at " __DATE__ " " __TIME__, defaultOnPacketReceived);
protocol->start();

// Start serving the web page
rb_web_start(80);

begin(protocol);

m_protocol_ours = true;
return protocol;
}

Expand All @@ -79,6 +86,45 @@ uint16_t _GridUi::generateUuidLocked() const {
}
}

void _GridUi::end() {
auto *protocol = m_protocol.load();
if(!protocol) {
ESP_LOGE("GridUI", "end() called when not initialized!");
return;
}

if(m_protocol_ours) {
protocol->stop();
delete protocol;
}
m_protocol = nullptr;

if(m_update_timer) {
esp_timer_stop(m_update_timer);
esp_timer_delete(m_update_timer);
m_update_timer = nullptr;
}

if(m_web_server_task) {
rb_web_stop(m_web_server_task);
m_web_server_task = nullptr;
}

{
std::lock_guard<std::mutex> guard(m_states_mu);
m_states.clear();
m_states.shrink_to_fit();
m_widgets.clear();
m_widgets.shrink_to_fit();
}

m_layout.reset();
m_state_mustarrive_id = 0;
m_states_modified = false;
m_tab_changed = false;
m_tab = 0;
}

void _GridUi::commit() {
std::lock_guard<std::mutex> l(m_states_mu);
if (!m_layout) {
Expand Down Expand Up @@ -124,9 +170,9 @@ void _GridUi::commit() {
.skip_unhandled_events = false,
#endif
};
esp_timer_handle_t timer;
esp_timer_create(&args, &timer);
esp_timer_start_periodic(timer, 50 * 1000);

esp_timer_create(&args, &m_update_timer);
esp_timer_start_periodic(m_update_timer, 50 * 1000);
}

bool _GridUi::handleRbPacket(const std::string& cmd, rbjson::Object* pkt) {
Expand Down Expand Up @@ -181,7 +227,7 @@ void _GridUi::stateChangeTask(void* selfVoid) {
if (self->m_states_modified.exchange(false)) {
std::unique_ptr<rbjson::Object> pkt(new rbjson::Object);
{
std::lock_guard<std::mutex>(self->m_states_mu);
std::lock_guard<std::mutex> guard(self->m_states_mu);
char buf[6];
std::unique_ptr<rbjson::Object> state(new rbjson::Object);

Expand Down
11 changes: 9 additions & 2 deletions src/gridui.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ class _GridUi {

void commit();

// Deinitializes GridUi. Frees the protocol, if it was created by GridUI and not by the app.
void end();

bool handleRbPacket(const std::string& command, rbjson::Object* pkt);

rb::Protocol* protocol() const { return m_protocol.load(); }
Expand Down Expand Up @@ -148,8 +151,8 @@ class _GridUi {
if (!checkUuidFreeLocked(uuid))
uuid = generateUuidLocked();

auto* state = new WidgetState(uuid, x, y, w, h, tab, &T::callbackTrampoline);
m_states.push_back(std::unique_ptr<WidgetState>(state));
auto* state = new WidgetState(uuid, x, y, w, h, tab);
m_states.emplace_back(std::unique_ptr<WidgetState>(state));

auto* widget = new T(T::name(), *state);
m_widgets.push_back(std::unique_ptr<T>(widget));
Expand Down Expand Up @@ -181,7 +184,11 @@ class _GridUi {
std::vector<std::unique_ptr<WidgetState>> m_states;

std::atomic<rb::Protocol*> m_protocol;
bool m_protocol_ours;

std::unique_ptr<rbjson::Object> m_layout;
esp_timer_handle_t m_update_timer;
TaskHandle_t m_web_server_task;

mutable std::mutex m_states_mu;
uint32_t m_state_mustarrive_id;
Expand Down
9 changes: 5 additions & 4 deletions src/widgets/widget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@

namespace gridui {

WidgetState Widget::emptyState(0, 0, 0, 0, 0, 0, [](void* cb, WidgetState* state) {});
std::mutex WidgetState::m_mutex;

WidgetState::WidgetState(uint16_t uuid, float x, float y, float w, float h, uint16_t tab, cb_trampoline_t cb_trampoline)
: m_cb_trampoline(cb_trampoline)
, m_uuid(uuid)
WidgetState Widget::emptyState(0, 0, 0, 0, 0, 0);

WidgetState::WidgetState(uint16_t uuid, float x, float y, float w, float h, uint16_t tab)
: m_uuid(uuid)
, m_bloom_global(0)
, m_bloom_tick(0) {

Expand Down
69 changes: 53 additions & 16 deletions src/widgets/widget.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,48 @@ template <typename Self, typename Finished>
class BuilderMixin;
};

class WidgetState;

class CallbacksHolder {
friend class WidgetState;
public:
typedef void (*cb_trampoline_t)(void*, WidgetState*);
typedef void (*cb_deleter_t)(void*);

~CallbacksHolder() {
for(const auto& itr : m_callbacks) {
m_cb_deleter(itr.second);
}
}

void call(WidgetState* state, const std::string& event) {
auto itr = m_callbacks.find(event);
if (itr != m_callbacks.end()) {
(*m_cb_trampoline)(itr->second, state);
}
}

void add(const std::string& event, void *cb) {
auto itr = m_callbacks.find(event);
if(itr != m_callbacks.end()) {
m_cb_deleter(cb);
itr->second = cb;
} else {
m_callbacks[event] = cb;
}
}

private:
CallbacksHolder(cb_trampoline_t trampoline, cb_deleter_t deleter) : m_cb_trampoline(trampoline), m_cb_deleter(deleter) { }

CallbacksHolder(const WidgetState&) = delete;
CallbacksHolder& operator=(const WidgetState&) = delete;

std::map<std::string, void*> m_callbacks;
const cb_trampoline_t m_cb_trampoline;
const cb_deleter_t m_cb_deleter;
};

/**
* @defgroup widgets_constructed Layout widgets
* Classes in this module are used to modify state of the already constructed Layout.
Expand All @@ -30,10 +72,8 @@ class WidgetState {
template <typename Self, typename Finished>
friend class builder::BuilderMixin;

typedef void (*cb_trampoline_t)(void*, WidgetState*);

public:
WidgetState(uint16_t uuid, float x, float y, float w, float h, uint16_t tab, cb_trampoline_t cb_trampoline);
WidgetState(uint16_t uuid, float x, float y, float w, float h, uint16_t tab);

uint16_t uuid() const { return m_uuid; }
const rbjson::Object& data() const { return m_data; }
Expand All @@ -45,6 +85,9 @@ class WidgetState {
void markChanged(const std::string& key);

private:
// Each mutex is ~100 bytes of heap allocation. Let's keep just one for this.
static std::mutex m_mutex;

WidgetState(const WidgetState&) = delete;
WidgetState& operator=(const WidgetState&) = delete;

Expand All @@ -59,20 +102,17 @@ class WidgetState {
m_mutex.unlock();
}

std::map<std::string, void*>& callbacks() {
if (!m_callbacks) {
m_callbacks.reset(new std::map<std::string, void*>);
void addCallback(CallbacksHolder::cb_trampoline_t trampoline, CallbacksHolder::cb_deleter_t deleter, const std::string& event, void *cb) {
if (!m_cb_holder) {
m_cb_holder.reset(new CallbacksHolder(trampoline, deleter));
}
return *m_callbacks.get();
m_cb_holder->add(event, cb);
}

void call(const std::string& event) {
if (!m_callbacks)
if (!m_cb_holder)
return;
auto itr = m_callbacks->find(event);
if (itr != m_callbacks->end()) {
(*m_cb_trampoline)(itr->second, this);
}
m_cb_holder->call(this, event);
}

void markChangedLocked(const std::string& key);
Expand All @@ -83,10 +123,7 @@ class WidgetState {
bool remarkAllChanges();

rbjson::Object m_data;
const cb_trampoline_t m_cb_trampoline;
std::unique_ptr<std::map<std::string, void*>> m_callbacks;

mutable std::mutex m_mutex;
std::unique_ptr<CallbacksHolder> m_cb_holder;

const uint16_t m_uuid;

Expand Down

0 comments on commit 0b4f8f2

Please sign in to comment.