From bf12303258aede29a629b92e71d23502f0109c98 Mon Sep 17 00:00:00 2001 From: Edimar Valentin Date: Sat, 8 Mar 2025 18:46:37 -0400 Subject: [PATCH 1/5] rgboard setup --- rgboard/Makefile | 31 +++++++++++++++++++ rgboard/src/main.cpp | 71 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 rgboard/Makefile create mode 100644 rgboard/src/main.cpp diff --git a/rgboard/Makefile b/rgboard/Makefile new file mode 100644 index 000000000..b06d1414c --- /dev/null +++ b/rgboard/Makefile @@ -0,0 +1,31 @@ +# Compiler settings +CXX = g++ +CXXFLAGS=-Wall -O3 -g -Wextra -Wno-unused-parameter + +# Source files and executable +SRCS = src/main.cpp +OBJECTS = $(SRCS:.cpp=.o) +BINARIES = rgboard + +# Where our library resides +RGB_LIB_DISTRIBUTION=.. +RGB_INCDIR=$(RGB_LIB_DISTRIBUTION)/include +RGB_LIBDIR=$(RGB_LIB_DISTRIBUTION)/lib +RGB_LIBRARY_NAME=rgbmatrix +RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a +LDFLAGS+=-L$(RGB_LIBDIR) -l$(RGB_LIBRARY_NAME) -lrt -lm -lpthread + +# Default target: Build the executable +all: $(BINARIES) + +# Compile .o files +src/%.o: src/%.cpp + $(CXX) $(CXXFLAGS) -I$(RGB_INCDIR) -c -o $@ $< + +# Link everything into the final executable +rgboard: $(OBJECTS) $(RGB_LIBRARY) + $(CXX) $(CXXFLAGS) -o $@ $(OBJECTS) $(LDFLAGS) + +# Clean up build files +clean: + rm -f src/*.o $(BINARIES) diff --git a/rgboard/src/main.cpp b/rgboard/src/main.cpp new file mode 100644 index 000000000..18ca54ab2 --- /dev/null +++ b/rgboard/src/main.cpp @@ -0,0 +1,71 @@ +// +// Created by evalentin on 08/03/25. +// + +#include "led-matrix.h" + +#include +#include +#include + + +using rgb_matrix::RGBMatrix; +using rgb_matrix::Canvas; + +volatile bool interrupt_received = false; +static void InterruptHandler(int signo) { + interrupt_received = true; +} + + +static void DrawOnCanvas(Canvas *canvas) { + /* + * Let's create a simple animation. We use the canvas to draw + * pixels. We wait between each step to have a slower animation. + */ + canvas->Fill(0, 0, 255); + + int center_x = canvas->width() / 2; + int center_y = canvas->height() / 2; + float radius_max = canvas->width() / 2; + float angle_step = 1.0 / 360; + for (float a = 0, r = 0; r < radius_max; a += angle_step, r += angle_step) { + if (interrupt_received) + return; + float dot_x = cos(a * 2 * M_PI) * r; + float dot_y = sin(a * 2 * M_PI) * r; + canvas->SetPixel(center_x + dot_x, center_y + dot_y, 255, 0, 0); + usleep(1 * 1000); // wait a little to slow down things. + } +} + + +int main(int argc, char *argv[]){ + RGBMatrix::Options defaults; + defaults.hardware_mapping = "regular"; + defaults.rows = 32; + defaults.chain_length = 1; + defaults.parallel = 2; + defaults.show_refresh_rate = true; + + Canvas * canvas = RGBMatrix::CreateFromFlags(&argc, &argv, &defaults); + + if (canvas == NULL) + { + return 1; + } + + // It is always good to set up a signal handler to cleanly exit when we + // receive a CTRL-C for instance. The DrawOnCanvas() routine is looking + // for that. + signal(SIGTERM, InterruptHandler); + signal(SIGINT, InterruptHandler); + + DrawOnCanvas(canvas); // Using the canvas. + + // Animation finished. Shut down the RGB matrix. + canvas->Clear(); + delete canvas; + + return 0; +} \ No newline at end of file From 291998d33cc936832ad2cb19745a47ebe821c26d Mon Sep 17 00:00:00 2001 From: Edimar Valentin Date: Sun, 9 Mar 2025 14:42:10 -0400 Subject: [PATCH 2/5] display module set up --- rgboard/Makefile | 4 +-- rgboard/include/display.h | 17 +++++++++++ rgboard/src/display.cpp | 28 +++++++++++++++++ rgboard/src/main.cpp | 64 ++++++++++----------------------------- 4 files changed, 63 insertions(+), 50 deletions(-) create mode 100644 rgboard/include/display.h create mode 100644 rgboard/src/display.cpp diff --git a/rgboard/Makefile b/rgboard/Makefile index b06d1414c..53bae27b9 100644 --- a/rgboard/Makefile +++ b/rgboard/Makefile @@ -3,7 +3,7 @@ CXX = g++ CXXFLAGS=-Wall -O3 -g -Wextra -Wno-unused-parameter # Source files and executable -SRCS = src/main.cpp +SRCS = src/main.cpp src/display.cpp OBJECTS = $(SRCS:.cpp=.o) BINARIES = rgboard @@ -20,7 +20,7 @@ all: $(BINARIES) # Compile .o files src/%.o: src/%.cpp - $(CXX) $(CXXFLAGS) -I$(RGB_INCDIR) -c -o $@ $< + $(CXX) $(CXXFLAGS) -Iinclude -I$(RGB_INCDIR) -c -o $@ $< # Link everything into the final executable rgboard: $(OBJECTS) $(RGB_LIBRARY) diff --git a/rgboard/include/display.h b/rgboard/include/display.h new file mode 100644 index 000000000..675684b0c --- /dev/null +++ b/rgboard/include/display.h @@ -0,0 +1,17 @@ +// +// Created by evalentin on 09/03/25. +// + +#ifndef DISPLAY_H +#define DISPLAY_H + +#include "led-matrix.h" + +#include +#include +#include + +using rgb_matrix::RGBMatrix; +using rgb_matrix::Canvas; +void DrawOnCanvas(Canvas *canvas); +#endif //DISPLAY_H diff --git a/rgboard/src/display.cpp b/rgboard/src/display.cpp new file mode 100644 index 000000000..ce2235242 --- /dev/null +++ b/rgboard/src/display.cpp @@ -0,0 +1,28 @@ +// +// Created by evalentin on 09/03/25. +// + +#include "../include/display.h" + +using rgb_matrix::RGBMatrix; +using rgb_matrix::Canvas; + + +void DrawOnCanvas(Canvas *canvas) { + /* + * Let's create a simple animation. We use the canvas to draw + * pixels. We wait between each step to have a slower animation. + */ + canvas->Fill(0, 255, 0); + + int center_x = canvas->width() / 2; + int center_y = canvas->height() / 2; + float radius_max = canvas->width() / 2; + float angle_step = 1.0 / 360; + for (float a = 0, r = 0; r < radius_max; a += angle_step, r += angle_step) { + float dot_x = cos(a * 2 * M_PI) * r; + float dot_y = sin(a * 2 * M_PI) * r; + canvas->SetPixel(center_x + dot_x, center_y + dot_y, 0, 0, 40); + usleep(1 * 1000); // wait a little to slow down things. + } +} \ No newline at end of file diff --git a/rgboard/src/main.cpp b/rgboard/src/main.cpp index 18ca54ab2..b7c1ff363 100644 --- a/rgboard/src/main.cpp +++ b/rgboard/src/main.cpp @@ -2,64 +2,31 @@ // Created by evalentin on 08/03/25. // -#include "led-matrix.h" +#define ROWS 32 +#define COLS 64 +#define PARALLEL 2 +#define HARDWARE "regular" +#define GPIO_SLOWDOWN 4 -#include -#include -#include - - -using rgb_matrix::RGBMatrix; -using rgb_matrix::Canvas; - -volatile bool interrupt_received = false; -static void InterruptHandler(int signo) { - interrupt_received = true; -} - - -static void DrawOnCanvas(Canvas *canvas) { - /* - * Let's create a simple animation. We use the canvas to draw - * pixels. We wait between each step to have a slower animation. - */ - canvas->Fill(0, 0, 255); - - int center_x = canvas->width() / 2; - int center_y = canvas->height() / 2; - float radius_max = canvas->width() / 2; - float angle_step = 1.0 / 360; - for (float a = 0, r = 0; r < radius_max; a += angle_step, r += angle_step) { - if (interrupt_received) - return; - float dot_x = cos(a * 2 * M_PI) * r; - float dot_y = sin(a * 2 * M_PI) * r; - canvas->SetPixel(center_x + dot_x, center_y + dot_y, 255, 0, 0); - usleep(1 * 1000); // wait a little to slow down things. - } -} +#include "display.h" int main(int argc, char *argv[]){ RGBMatrix::Options defaults; - defaults.hardware_mapping = "regular"; - defaults.rows = 32; - defaults.chain_length = 1; - defaults.parallel = 2; - defaults.show_refresh_rate = true; + rgb_matrix::RuntimeOptions runtime_options; + + defaults.hardware_mapping = HARDWARE; // or e.g. "adafruit-hat" + defaults.rows = ROWS; + defaults.cols = COLS; + defaults.parallel = PARALLEL; - Canvas * canvas = RGBMatrix::CreateFromFlags(&argc, &argv, &defaults); + runtime_options.gpio_slowdown = GPIO_SLOWDOWN; + + Canvas *canvas = RGBMatrix::CreateFromOptions(defaults, runtime_options); if (canvas == NULL) - { return 1; - } - // It is always good to set up a signal handler to cleanly exit when we - // receive a CTRL-C for instance. The DrawOnCanvas() routine is looking - // for that. - signal(SIGTERM, InterruptHandler); - signal(SIGINT, InterruptHandler); DrawOnCanvas(canvas); // Using the canvas. @@ -68,4 +35,5 @@ int main(int argc, char *argv[]){ delete canvas; return 0; + return 0; } \ No newline at end of file From 7f84064c2dcfd68722bf5cd26cd266f3cf099a03 Mon Sep 17 00:00:00 2001 From: Edimar Valentin Date: Fri, 2 May 2025 14:28:05 -0400 Subject: [PATCH 3/5] get jwt on login --- rgboard/Makefile | 28 +++++---- rgboard/include/queue-client.h | 23 +++++++ rgboard/src/main.cpp | 27 +------- rgboard/src/queue-client.cpp | 112 +++++++++++++++++++++++++++++++++ 4 files changed, 153 insertions(+), 37 deletions(-) create mode 100644 rgboard/include/queue-client.h create mode 100644 rgboard/src/queue-client.cpp diff --git a/rgboard/Makefile b/rgboard/Makefile index 53bae27b9..3aab6983b 100644 --- a/rgboard/Makefile +++ b/rgboard/Makefile @@ -1,31 +1,35 @@ # Compiler settings CXX = g++ -CXXFLAGS=-Wall -O3 -g -Wextra -Wno-unused-parameter +CXXFLAGS = -Wall -O3 -g -Wextra -Wno-unused-parameter # Source files and executable -SRCS = src/main.cpp src/display.cpp +SRCS = src/main.cpp src/display.cpp src/queue-client.cpp OBJECTS = $(SRCS:.cpp=.o) BINARIES = rgboard -# Where our library resides -RGB_LIB_DISTRIBUTION=.. -RGB_INCDIR=$(RGB_LIB_DISTRIBUTION)/include -RGB_LIBDIR=$(RGB_LIB_DISTRIBUTION)/lib -RGB_LIBRARY_NAME=rgbmatrix -RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a -LDFLAGS+=-L$(RGB_LIBDIR) -l$(RGB_LIBRARY_NAME) -lrt -lm -lpthread +# Where our RGB library resides +RGB_LIB_DISTRIBUTION = .. +RGB_INCDIR = $(RGB_LIB_DISTRIBUTION)/include +RGB_LIBDIR = $(RGB_LIB_DISTRIBUTION)/lib +RGB_LIBRARY_NAME = rgbmatrix +RGB_LIBRARY = $(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a -# Default target: Build the executable +# Linker flags: add libcurl and jsoncpp +LDFLAGS += -L$(RGB_LIBDIR) -l$(RGB_LIBRARY_NAME) -lcurl -ljsoncpp -lrt -lm -lpthread + +# Default target all: $(BINARIES) # Compile .o files src/%.o: src/%.cpp $(CXX) $(CXXFLAGS) -Iinclude -I$(RGB_INCDIR) -c -o $@ $< -# Link everything into the final executable +# Link final binary rgboard: $(OBJECTS) $(RGB_LIBRARY) $(CXX) $(CXXFLAGS) -o $@ $(OBJECTS) $(LDFLAGS) -# Clean up build files +# Clean up clean: rm -f src/*.o $(BINARIES) + +.PHONY: all clean diff --git a/rgboard/include/queue-client.h b/rgboard/include/queue-client.h new file mode 100644 index 000000000..0808a15ae --- /dev/null +++ b/rgboard/include/queue-client.h @@ -0,0 +1,23 @@ +// +// Created by evalentin on 01/05/25. +// + +#ifndef QUEUE_CLIENT_H +#define QUEUE_CLIENT_H +#include + +class QueueClient +{ +public: + QueueClient(std::string email, std::string password); + std::string GetDesign(); + +private: + std::string jwt; + std::string email; + std::string password; + + std::string GetJWT(); + static size_t CurlWriteCallback(void* contents, size_t size, size_t nmemb, std::string* output); +}; +#endif //QUEUE_CLIENT_H diff --git a/rgboard/src/main.cpp b/rgboard/src/main.cpp index b7c1ff363..1c5872f4c 100644 --- a/rgboard/src/main.cpp +++ b/rgboard/src/main.cpp @@ -8,32 +8,9 @@ #define HARDWARE "regular" #define GPIO_SLOWDOWN 4 -#include "display.h" - +#include "queue-client.h" int main(int argc, char *argv[]){ - RGBMatrix::Options defaults; - rgb_matrix::RuntimeOptions runtime_options; - - defaults.hardware_mapping = HARDWARE; // or e.g. "adafruit-hat" - defaults.rows = ROWS; - defaults.cols = COLS; - defaults.parallel = PARALLEL; - - runtime_options.gpio_slowdown = GPIO_SLOWDOWN; - - Canvas *canvas = RGBMatrix::CreateFromOptions(defaults, runtime_options); - - if (canvas == NULL) - return 1; - - - DrawOnCanvas(canvas); // Using the canvas. - - // Animation finished. Shut down the RGB matrix. - canvas->Clear(); - delete canvas; - - return 0; + auto client = QueueClient("abc", "123"); return 0; } \ No newline at end of file diff --git a/rgboard/src/queue-client.cpp b/rgboard/src/queue-client.cpp new file mode 100644 index 000000000..969fd29b7 --- /dev/null +++ b/rgboard/src/queue-client.cpp @@ -0,0 +1,112 @@ +// +// Created by evalentin on 01/05/25. +// + +#include "../include/queue-client.h" +#include +#include +#include + +#define LOGIN_URL "http://localhost:5000/login" + + +QueueClient::QueueClient(std::string email, std::string password) +{ + this->email = std::move(email); + this->password = std::move(password); + jwt = GetJWT(); +} + + +std::string QueueClient::GetJWT() +{ + if (this->email.empty()) + { + std::printf("Email field is empty.\n"); + return ""; + } + else if (password.empty()) + { + std::printf("Password field is empty.\n"); + return ""; + } + + CURL* curl = curl_easy_init(); + + if (!curl) + { + printf("Curl init failed\n"); + return ""; + } + + std::string response; + Json::Value body; + + // Doing this in C++ is crazy, even the IDE is confused. + body["email"] = this->email; + body["password"] = this->password; + + Json::StreamWriterBuilder writer; + + std::string json_body = Json::writeString(writer, body); + + struct curl_slist* headers = nullptr; + + headers = curl_slist_append(headers, "Content-Type: application/json"); + + // Set options + curl_easy_setopt(curl, CURLOPT_URL, LOGIN_URL); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_body.c_str()); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); + + // Send request + CURLcode result = curl_easy_perform(curl); + curl_slist_free_all(headers); + curl_easy_cleanup(curl); + + if (result != CURLE_OK) + { + fprintf(stderr, "problem: %s\n", curl_easy_strerror(result)); + return ""; + } + + Json::CharReaderBuilder reader; + Json::Value json_response; + std::string errs; + + std::istringstream s(response); + + if (!Json::parseFromStream(reader, s, &json_response, &errs)) + { + printf("Failed to parse response JSON: %s\n", errs.c_str()); + return ""; + } + + if (json_response.isMember("access_token")) + { + std::string token = json_response["access_token"].asString(); + return token; + } + + std::string error_message = json_response["error"].asString(); + printf("Login failed: no token in response.\nError: %s\n", error_message.c_str()); + return ""; +} + +std::string QueueClient::GetDesign() +{ + + if (this->jwt.empty()) + { + return ""; + } +} + +size_t QueueClient::CurlWriteCallback(void* contents, size_t size, size_t nmemb, std::string* output) +{ + size_t total = size * nmemb; + output->append((char*)contents, total); + return total; +} From 4468ef135b8bec3f2f5ea4b8c0f152e5b8aadb2f Mon Sep 17 00:00:00 2001 From: Edimar Valentin Date: Sat, 3 May 2025 01:38:28 -0400 Subject: [PATCH 4/5] initial working iteration --- rgboard/include/display.h | 6 +- rgboard/include/queue-client.h | 9 ++- rgboard/src/display.cpp | 64 ++++++++++++----- rgboard/src/main.cpp | 50 ++++++++++++- rgboard/src/queue-client.cpp | 125 +++++++++++++++++++++++++++++++-- 5 files changed, 222 insertions(+), 32 deletions(-) diff --git a/rgboard/include/display.h b/rgboard/include/display.h index 675684b0c..e217f2a42 100644 --- a/rgboard/include/display.h +++ b/rgboard/include/display.h @@ -7,11 +7,9 @@ #include "led-matrix.h" -#include -#include -#include +#include // JSON parsing using rgb_matrix::RGBMatrix; using rgb_matrix::Canvas; -void DrawOnCanvas(Canvas *canvas); +void DrawDesignOnCanvas(Canvas* canvas, const Json::Value& pixel_data); #endif //DISPLAY_H diff --git a/rgboard/include/queue-client.h b/rgboard/include/queue-client.h index 0808a15ae..beb9c1c6b 100644 --- a/rgboard/include/queue-client.h +++ b/rgboard/include/queue-client.h @@ -5,18 +5,25 @@ #ifndef QUEUE_CLIENT_H #define QUEUE_CLIENT_H #include +#include class QueueClient { public: QueueClient(std::string email, std::string password); - std::string GetDesign(); + bool GetDesign(); + [[nodiscard]] int GetDisplayDuration() const; + Json::Value GetPixelData(); private: std::string jwt; std::string email; std::string password; + // We store in these variables and get from here. Can't return a string and an int in the same function. + int display_duration{}; + Json::Value pixel_data; + std::string GetJWT(); static size_t CurlWriteCallback(void* contents, size_t size, size_t nmemb, std::string* output); }; diff --git a/rgboard/src/display.cpp b/rgboard/src/display.cpp index ce2235242..9770faf07 100644 --- a/rgboard/src/display.cpp +++ b/rgboard/src/display.cpp @@ -3,26 +3,56 @@ // #include "../include/display.h" +#include // std::string +#include // std::stringstream, std::istringstream +#include // std::cerr, std::cout + using rgb_matrix::RGBMatrix; using rgb_matrix::Canvas; +void HexToRGB(const std::string& hex, int& r, int& g, int& b); + +void DrawDesignOnCanvas(Canvas* canvas, const Json::Value& pixel_data) +{ + constexpr int GRID_SIZE = 8; // pixel data is 8x scaled + canvas->Clear(); + + for (const auto& key : pixel_data.getMemberNames()) + { + int x = 0, y = 0; + char comma; + + std::stringstream coord_stream(key); + coord_stream >> x >> comma >> y; + + // downscale + x /= GRID_SIZE; + y /= GRID_SIZE; -void DrawOnCanvas(Canvas *canvas) { - /* - * Let's create a simple animation. We use the canvas to draw - * pixels. We wait between each step to have a slower animation. - */ - canvas->Fill(0, 255, 0); - - int center_x = canvas->width() / 2; - int center_y = canvas->height() / 2; - float radius_max = canvas->width() / 2; - float angle_step = 1.0 / 360; - for (float a = 0, r = 0; r < radius_max; a += angle_step, r += angle_step) { - float dot_x = cos(a * 2 * M_PI) * r; - float dot_y = sin(a * 2 * M_PI) * r; - canvas->SetPixel(center_x + dot_x, center_y + dot_y, 0, 0, 40); - usleep(1 * 1000); // wait a little to slow down things. + const std::string hex_color = pixel_data[key].asString(); + int r, g, b; + HexToRGB(hex_color, r, g, b); + + if (x >= 0 && x < canvas->width() && y >= 0 && y < canvas->height()) + { + canvas->SetPixel(x, y, r, g, b); + } + } +} + + +// Convert hex string to RGB integers +void HexToRGB(const std::string& hex, int& r, int& g, int& b) +{ + if (hex.length() == 7 && hex[0] == '#') + { + r = std::stoi(hex.substr(1, 2), nullptr, 16); + g = std::stoi(hex.substr(3, 2), nullptr, 16); + b = std::stoi(hex.substr(5, 2), nullptr, 16); + } + else + { + r = g = b = 0; // fallback to black } -} \ No newline at end of file +} diff --git a/rgboard/src/main.cpp b/rgboard/src/main.cpp index 1c5872f4c..9206aec87 100644 --- a/rgboard/src/main.cpp +++ b/rgboard/src/main.cpp @@ -8,9 +8,53 @@ #define HARDWARE "regular" #define GPIO_SLOWDOWN 4 +#include + #include "queue-client.h" +#include "display.h" + +int main(int argc, char* argv[]) +{ + // Matrix configuration + rgb_matrix::RGBMatrix::Options defaults; + rgb_matrix::RuntimeOptions runtime_options; + + defaults.hardware_mapping = HARDWARE; + defaults.rows = ROWS; + defaults.cols = COLS; + defaults.parallel = PARALLEL; + + runtime_options.gpio_slowdown = GPIO_SLOWDOWN; + + // Initialize matrix + rgb_matrix::Canvas* canvas = rgb_matrix::CreateMatrixFromOptions(defaults, runtime_options); + if (canvas == nullptr) + { + std::fprintf(stderr, "Failed to initialize matrix.\n"); + return 1; + } + + // Authenticate with client + QueueClient client("email", "password"); + + // main loop: fetch + draw + wait + while (true) + { + if (client.GetDesign()) + { + const Json::Value& pixel_data = client.GetPixelData(); + int duration = client.GetDisplayDuration(); + + DrawDesignOnCanvas(canvas, pixel_data); + std::printf("Displaying design for %d seconds.\n", duration); + sleep(duration); + } + else + { + std::fprintf(stderr, "Failed to get design. Retrying in 5 seconds...\n"); + sleep(5); + } + } -int main(int argc, char *argv[]){ - auto client = QueueClient("abc", "123"); return 0; -} \ No newline at end of file +} diff --git a/rgboard/src/queue-client.cpp b/rgboard/src/queue-client.cpp index 969fd29b7..90edf781d 100644 --- a/rgboard/src/queue-client.cpp +++ b/rgboard/src/queue-client.cpp @@ -4,10 +4,10 @@ #include "../include/queue-client.h" #include -#include #include #define LOGIN_URL "http://localhost:5000/login" +#define DEQUEUE_URL "http://localhost:5000/queue_item/dequeue" QueueClient::QueueClient(std::string email, std::string password) @@ -90,23 +90,134 @@ std::string QueueClient::GetJWT() return token; } - std::string error_message = json_response["error"].asString(); - printf("Login failed: no token in response.\nError: %s\n", error_message.c_str()); - return ""; + std::string error_message = json_response["error"].asString(); + printf("Login failed: no token in response.\nError: %s\n", error_message.c_str()); + return ""; } -std::string QueueClient::GetDesign() +bool QueueClient::GetDesign() { + auto perform_request = [this](std::string& response) -> long + { + CURL* curl = curl_easy_init(); + if (!curl) + { + std::printf("Curl init failed\n"); + return -1; + } + + struct curl_slist* headers = nullptr; + std::string auth_header = "Authorization: Bearer " + this->jwt; + headers = curl_slist_append(headers, "Content-Type: application/json"); + headers = curl_slist_append(headers, auth_header.c_str()); + + curl_easy_setopt(curl, CURLOPT_URL, DEQUEUE_URL); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl, CURLOPT_POST, 1L); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ""); // empty body + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); + + CURLcode result = curl_easy_perform(curl); + + long http_code = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); + + curl_slist_free_all(headers); + curl_easy_cleanup(curl); + + if (result != CURLE_OK) + { + std::fprintf(stderr, "Request failed: %s\n", curl_easy_strerror(result)); + return -1; + } + + return http_code; + }; + + // First attempt + std::string response; + long status_code = perform_request(response); - if (this->jwt.empty()) + // If unauthorized, refresh token and try again + if (status_code == 401) { - return ""; + std::printf("JWT expired or invalid. Getting new token...\n"); + this->jwt = GetJWT(); + if (this->jwt.empty()) + { + std::printf("Failed to refresh JWT.\n"); + return false; + } + + response.clear(); + status_code = perform_request(response); } + + if (status_code != 200) + { + std::printf("Server responded with HTTP %ld\n", status_code); + return false; + } + + // Parse JSON response + Json::CharReaderBuilder reader; + Json::Value json_response; + std::string errs; + std::istringstream s(response); + + if (!Json::parseFromStream(reader, s, &json_response, &errs)) + { + std::printf("Failed to parse response JSON: %s\n", errs.c_str()); + return false; + } + + if (json_response.isMember("pixel_data") && json_response.isMember("duration")) + { + std::string raw = json_response["pixel_data"].asString(); + + // Strip "Design " prefix if present + const std::string prefix = "Design "; + if (raw.rfind(prefix, 0) == 0) + raw = raw.substr(prefix.length()); + + Json::CharReaderBuilder builder; + Json::Value parsed; + std::string errs; + std::istringstream ss(raw); + + if (!Json::parseFromStream(builder, ss, &parsed, &errs)) { + std::fprintf(stderr, "Failed to parse pixel_data string: %s\n", errs.c_str()); + return false; + } + + this->pixel_data = parsed; + this->display_duration = json_response["duration"].asInt(); + return true; + } + + std::string error_message = json_response.get("error", "Unknown error").asString(); + std::printf("Failed to get design: %s\n", error_message.c_str()); + return false; } + size_t QueueClient::CurlWriteCallback(void* contents, size_t size, size_t nmemb, std::string* output) { size_t total = size * nmemb; output->append((char*)contents, total); return total; } + + +// Getters + +int QueueClient::GetDisplayDuration() const +{ + return this->display_duration; +} + +Json::Value QueueClient::GetPixelData() +{ + return this->pixel_data; +} From 153eb012e33639403e1a2489adbcfc0084bed8e5 Mon Sep 17 00:00:00 2001 From: Edimar Valentin Date: Sun, 4 May 2025 01:05:08 -0400 Subject: [PATCH 5/5] update to match new queue system --- rgboard/src/queue-client.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/rgboard/src/queue-client.cpp b/rgboard/src/queue-client.cpp index 90edf781d..0e52a201a 100644 --- a/rgboard/src/queue-client.cpp +++ b/rgboard/src/queue-client.cpp @@ -7,7 +7,7 @@ #include #define LOGIN_URL "http://localhost:5000/login" -#define DEQUEUE_URL "http://localhost:5000/queue_item/dequeue" +#define CURRENT_URL "http://localhost:5000/rotation/current" QueueClient::QueueClient(std::string email, std::string password) @@ -111,10 +111,9 @@ bool QueueClient::GetDesign() headers = curl_slist_append(headers, "Content-Type: application/json"); headers = curl_slist_append(headers, auth_header.c_str()); - curl_easy_setopt(curl, CURLOPT_URL, DEQUEUE_URL); + curl_easy_setopt(curl, CURLOPT_URL, CURRENT_URL); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); - curl_easy_setopt(curl, CURLOPT_POST, 1L); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ""); // empty body + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); // use GET curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); @@ -172,9 +171,10 @@ bool QueueClient::GetDesign() return false; } - if (json_response.isMember("pixel_data") && json_response.isMember("duration")) + if (json_response.isMember("image") && json_response["image"].isMember("pixel_data") && json_response. + isMember("time_left")) { - std::string raw = json_response["pixel_data"].asString(); + std::string raw = json_response["image"]["pixel_data"].asString(); // Strip "Design " prefix if present const std::string prefix = "Design "; @@ -186,13 +186,14 @@ bool QueueClient::GetDesign() std::string errs; std::istringstream ss(raw); - if (!Json::parseFromStream(builder, ss, &parsed, &errs)) { + if (!Json::parseFromStream(builder, ss, &parsed, &errs)) + { std::fprintf(stderr, "Failed to parse pixel_data string: %s\n", errs.c_str()); return false; } this->pixel_data = parsed; - this->display_duration = json_response["duration"].asInt(); + this->display_duration = json_response["time_left"].asInt(); return true; }