diff options
| author | Paul Oliver <contact@pauloliver.dev> | 2026-05-26 00:02:01 +0200 |
|---|---|---|
| committer | Paul Oliver <contact@pauloliver.dev> | 2026-05-30 15:06:02 +0200 |
| commit | 2fc73d820d0442ab813c1ac11dff6d7cb1d8f50b (patch) | |
| tree | f3a4920ca09555fe3a684b4935a7cc2117b3f2d9 /data | |
| parent | 848c5cca715c514ce6e57e0fa7ef8e71b09c0b08 (diff) | |
Adds working data client
Diffstat (limited to 'data')
| -rw-r--r-- | data/client.cpp | 596 | ||||
| -rw-r--r-- | data/server.c | 89 |
2 files changed, 468 insertions, 217 deletions
diff --git a/data/client.cpp b/data/client.cpp index 730ea7a..227ebaa 100644 --- a/data/client.cpp +++ b/data/client.cpp @@ -1,189 +1,249 @@ #include <arpa/inet.h> #include <GLFW/glfw3.h> -#include <imgui.h> #include <imgui_impl_glfw.h> #include <imgui_impl_opengl3.h> #include <implot.h> #include <json-c/json.h> -#include <limits.h> #include <math.h> #include <signal.h> #include <threads.h> -#include <initializer_list> +#include <algorithm> +#include <array> +#include <map> +#include <tuple> +#include <vector> #include "logger.c" -#define FETCH_INTERVAL 10 -#define FETCH_INTERVAL_SUBDIV 1000 - -#define COLOR_BG ImVec4(0.f, 0.f, 0.f, 1.f) +// ---------------------------------------------------------------------------- +// Defines +// ---------------------------------------------------------------------------- +#define COLOR_BLACK ImVec4(0.f, 0.f, 0.f, 1.f) #define FONT_SIZE 12.f #define FONT_SOURCE "/usr/share/fonts/droid/DroidSansMono.ttf" #define GLSL_VERSION "#version 130" -#define WINDOW_STYLE (ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings) +#define PLOT_MAX_COLS 8 +#define PLOT_MIN_COLS 1 +#define PLOT_SCROLL_OFFSET 28.f -#define DEFAULT_ENTRIES 2000 -#define DEFAULT_NTH 1 -#define DEFAULT_X_AXIS 0 -#define DEFAULT_X_LOW 0 -#define DEFAULT_X_HIGH INT_MAX -#define DEFAULT_HM_LEFT 0 -#define DEFAULT_HM_PIXEL_COUNT 0x400 +#define IMGUI_WINDOW_FLAGS ( \ + ImGuiWindowFlags_NoBackground | \ + ImGuiWindowFlags_NoDecoration | \ + ImGuiWindowFlags_NoMove | \ + ImGuiWindowFlags_NoSavedSettings \ +) -#define PLOT_MIN_COLS 1 -#define PLOT_MAX_COLS 8 +#define DATA_FETCH_INTERVAL 10 +#define DATA_FETCH_INTERVAL_SUBDIV 1000 + +#define DEFVAL_ENTRIES 0x800l +#define DEFVAL_NTH 1l +#define DEFVAL_X_AXIS 0 +#define DEFVAL_X_LOW 0l +#define DEFVAL_X_HIGH INT64_MAX +#define DEFVAL_HM_LEFT 0l +#define DEFVAL_HM_PIXEL_COUNT 0x400l +// ---------------------------------------------------------------------------- +// Declarations +// ---------------------------------------------------------------------------- enum Status { STATUS_STOPPED, STATUS_RUNNING, STATUS_STOPPING, }; -struct Plot { - Plot(const char *name, const char *section, std::initializer_list<const char *> cols) - : name(name), section(section), cols(cols.begin()) {} +struct CompString { + bool operator()(const char *a, const char *b) const { + return strcmp(a, b) < 0; + } +}; +struct Plot { const char *name; const char *section; - const char *const *cols; + std::vector<const char *> traces; }; -// Window globals -GLFWwindow *g_window; -ImGuiIO *g_io; -ImGuiStyle *g_imgui_style; -ImPlotStyle *g_implot_style; +// ---------------------------------------------------------------------------- +// Plots +// ---------------------------------------------------------------------------- +#include "arch_plots.cpp" -// Data globals -const char *g_x_axes[] = { +template <class T, size_t N, size_t M> +std::array<T, N + M> merge_array(const std::array<T, N> &first, const std::array<T, M> &second) { + std::array<T, N + M> result{}; + for (size_t i = 0; i < N; i++) result[i] = first[i]; + for (size_t i = 0; i < M; i++) result[N + i] = second[i]; + return result; +} + +std::array g_core_traces = std::to_array<const char *>({ + "rowid", "step", -#define FOR_CORE(i) "cycl_" #i, +#define FOR_CORE(i) \ + "cycl_" #i, \ + "mall_" #i, \ + "pnum_" #i, \ + "pfst_" #i, \ + "plst_" #i, \ + "amb0_" #i, \ + "amb1_" #i, \ + "emb0_" #i, \ + "emb1_" #i, \ + "eliv_" #i, \ + "edea_" #i, FOR_CORES #undef FOR_CORE -}; +}); -int g_status; -int g_entries = DEFAULT_ENTRIES; -int g_nth = DEFAULT_NTH; -int g_x_axis = DEFAULT_X_AXIS; -int g_x_low = DEFAULT_X_LOW; -int g_x_high = DEFAULT_X_HIGH; -int g_hm_left = DEFAULT_HM_LEFT; -int g_hm_pixel_count = DEFAULT_HM_PIXEL_COUNT; -int g_hm_pixel_pow; // calculate on startup -int g_x_current; -thrd_t g_data_fetching_thread; - -// Plot globals -Plot g_plots[] = { - Plot("cycl", "general", { +std::array g_core_plots = std::to_array<Plot>({ + {"cycl", "general", { #define FOR_CORE(i) "cycl_" #i, FOR_CORES #undef FOR_CORE - nullptr, - }), - Plot("mall", "general", { + }}, + {"mall", "general", { #define FOR_CORE(i) "mall_" #i, FOR_CORES #undef FOR_CORE - nullptr, - }), - Plot("pnum", "general", { + }}, + {"pnum", "general", { #define FOR_CORE(i) "pnum_" #i, FOR_CORES #undef FOR_CORE - nullptr, - }), - Plot("ppop", "general", { + }}, + {"ppop", "general", { #define FOR_CORE(i) "pfst_" #i, "plst_" #i, FOR_CORES #undef FOR_CORE - nullptr, - }), - Plot("ambs", "general", { + }}, + {"ambs", "general", { #define FOR_CORE(i) "amb0_" #i, "amb1_" #i, FOR_CORES #undef FOR_CORE - nullptr, - }), - Plot("eevs", "general", { + }}, + {"eevs", "general", { #define FOR_CORE(i) "emb0_" #i, "emb1_" #i, "eliv_" #i, "edea_" #i, FOR_CORES #undef FOR_CORE - nullptr, - }), + }}, +}); + +std::map<const char *, std::vector<ImS64>, CompString> g_trace_map; +std::array g_traces = merge_array(g_core_traces, g_arch_traces); +std::array g_plots = merge_array(g_core_plots, g_arch_plots); + +#define TRACE_COUNT g_traces.size() +#define PLOT_COUNT g_plots.size() + +// ---------------------------------------------------------------------------- +// Globals +// ---------------------------------------------------------------------------- +GLFWwindow *g_window; +ImGuiIO *g_imgui_io; +ImGuiStyle *g_imgui_style; +ImPlotStyle *g_implot_style; + +const char *g_x_axes[] = { + "rowid", + "step", +#define FOR_CORE(i) "cycl_" #i, + FOR_CORES +#undef FOR_CORE }; -#define PLOT_COUNT (int)(sizeof(g_plots) / sizeof(g_plots[0])) +int g_status; +int g_x_axis = DEFVAL_X_AXIS; +int64_t g_entries = DEFVAL_ENTRIES; +int64_t g_nth = DEFVAL_NTH; +int64_t g_x_low = DEFVAL_X_LOW; +int64_t g_x_high = DEFVAL_X_HIGH; +int64_t g_hm_left = DEFVAL_HM_LEFT; +int64_t g_hm_pixel_count = DEFVAL_HM_PIXEL_COUNT; +int64_t g_hm_pixel_pow; // calculate on startup +int64_t g_x_current = -1l; + +thrd_t g_fetching_thread; +mtx_t g_fetching_mutex; +int g_trace_len; +int g_trace_offset; // Layout globals -float g_data_col_width; bool g_data_col_visible = true; +bool g_plot_maximized; +bool g_plot_scroll; +float g_plot_scroll_current; +float g_plot_scroll_to; +float g_data_col_width; +float g_plot_cells_top[PLOT_MAX_COLS][PLOT_COUNT]; +float g_plot_cells_bottom[PLOT_MAX_COLS][PLOT_COUNT]; +Plot *g_plot_cells[PLOT_MAX_COLS][PLOT_COUNT]; +Plot *g_plot_selected = &g_plots[0]; int g_plot_cols = 2; int g_plot_col_selected; int g_plot_row_selected; -Plot *g_plot_cells[PLOT_MAX_COLS][PLOT_COUNT]; -Plot *g_plot_selected = &g_plots[0]; -bool g_plot_maximized; // ---------------------------------------------------------------------------- // Data functions // ---------------------------------------------------------------------------- -int data_calc_max_hm_pixel_pow(void) { - return (int)floor(log2((float)(MVEC_SIZE - g_hm_left) / (float)g_hm_pixel_count)); +int64_t data_max_hm_pixel_pow(void) { + return (int64_t)floor(log2((double)(MVEC_SIZE - g_hm_left) / (double)g_hm_pixel_count)); } -void data_clamp(int *field, int low, int high) { - assert(field); - if (*field < low) *field = low; - if (*field > high) *field = high; -} - -void data_touched(void) { - data_clamp(&g_entries, 1, DEFAULT_ENTRIES); - data_clamp(&g_nth, DEFAULT_NTH, INT_MAX); - data_clamp(&g_x_low, DEFAULT_X_LOW, INT_MAX); - data_clamp(&g_x_high, g_x_low + 1, DEFAULT_X_HIGH); +void data_on_field_change(void) { + g_entries = std::clamp(g_entries, 1l, DEFVAL_ENTRIES); + g_nth = std::clamp(g_nth, DEFVAL_NTH, INT64_MAX); + g_x_low = std::clamp(g_x_low, DEFVAL_X_LOW, INT64_MAX); + g_x_high = std::clamp(g_x_high, g_x_low + 1l, DEFVAL_X_HIGH); #if !defined(MVEC_LOOP) - data_clamp(&g_hm_left, DEFAULT_HM_LEFT, MVEC_SIZE); + g_hm_left = std::clamp(g_hm_left, DEFVAL_HM_LEFT, (int64_t)MVEC_SIZE); #endif - data_clamp(&g_hm_pixel_count, 1, DEFAULT_HM_PIXEL_COUNT); - data_clamp(&g_hm_pixel_pow, 0, data_calc_max_hm_pixel_pow()); - g_x_current = 0; + g_hm_pixel_count = std::clamp(g_hm_pixel_count, 1l, DEFVAL_HM_PIXEL_COUNT); + g_hm_pixel_pow = std::clamp(g_hm_pixel_pow, 0l, data_max_hm_pixel_pow()); + + g_x_current = -1l; + + for (auto &trace : g_trace_map) { + trace.second.clear(); + } } -void data_reset_values(void) { - g_entries = DEFAULT_ENTRIES; - g_nth = DEFAULT_NTH; - g_x_axis = DEFAULT_X_AXIS; - g_x_low = DEFAULT_X_LOW; - g_x_high = DEFAULT_X_HIGH; - g_hm_left = DEFAULT_HM_LEFT; - g_hm_pixel_count = DEFAULT_HM_PIXEL_COUNT; - g_hm_pixel_pow = data_calc_max_hm_pixel_pow(); +void data_reset_fields(void) { + g_entries = DEFVAL_ENTRIES; + g_nth = DEFVAL_NTH; + g_x_axis = DEFVAL_X_AXIS; + g_x_low = DEFVAL_X_LOW; + g_x_high = DEFVAL_X_HIGH; + g_hm_left = DEFVAL_HM_LEFT; + g_hm_pixel_count = DEFVAL_HM_PIXEL_COUNT; + g_hm_pixel_pow = data_max_hm_pixel_pow(); + + g_trace_len = 0; + g_trace_offset = 0; + + data_on_field_change(); } void data_reset_plot_cells(void) { - for (int i = 0; i < PLOT_MAX_COLS; i++) { - for (int j = 0; j < PLOT_COUNT; j++) { - g_plot_cells[i][j] = nullptr; - } - } + memset(g_plot_cells, 0, PLOT_MAX_COLS * PLOT_COUNT * sizeof(nullptr)); + memset(g_plot_cells_top, 0, PLOT_MAX_COLS * PLOT_COUNT * sizeof(float)); + memset(g_plot_cells_bottom, 0, PLOT_MAX_COLS * PLOT_COUNT * sizeof(float)); } void data_fetch(void) { struct json_object *request = json_object_new_object(); json_object_object_add(request, "request", json_object_new_string("data")); - json_object_object_add(request, "entries", json_object_new_int(g_entries)); - json_object_object_add(request, "nth", json_object_new_int(g_nth)); + json_object_object_add(request, "entries", json_object_new_int64(g_entries)); + json_object_object_add(request, "nth", json_object_new_int64(g_nth)); json_object_object_add(request, "x-axis", json_object_new_string(g_x_axes[g_x_axis])); - json_object_object_add(request, "x-low", json_object_new_int(g_x_low)); - json_object_object_add(request, "x-high", json_object_new_int(g_x_high)); - json_object_object_add(request, "hm-left", json_object_new_int(g_hm_left)); - json_object_object_add(request, "hm-pixel-count", json_object_new_int(g_hm_pixel_count)); - json_object_object_add(request, "hm-pixel-pow", json_object_new_int(g_hm_pixel_pow)); - json_object_object_add(request, "x-current", json_object_new_int(g_x_current)); + json_object_object_add(request, "x-low", json_object_new_int64(g_x_low)); + json_object_object_add(request, "x-high", json_object_new_int64(g_x_high)); + json_object_object_add(request, "hm-left", json_object_new_int64(g_hm_left)); + json_object_object_add(request, "hm-pixel-count", json_object_new_int64(g_hm_pixel_count)); + json_object_object_add(request, "hm-pixel-pow", json_object_new_int64(g_hm_pixel_pow)); + json_object_object_add(request, "x-current", json_object_new_int64(g_x_current)); const char *request_str = json_object_to_json_string(request); log_info("Sending request to server: %s", request_str); @@ -201,20 +261,52 @@ void data_fetch(void) { const char *response_str = json_object_to_json_string(response); log_info("Server responded with: %s", response_str); + mtx_lock(&g_fetching_mutex); + + json_object_object_foreach(response, key, value) { + if (g_trace_map.contains(key)) { + for (size_t i = 0; i < json_object_array_length(value); i++) { + ImS64 point = json_object_get_int64(json_object_array_get_idx(value, i)); + g_trace_map.at(key).push_back(point); + } + } + } + + g_x_current = g_trace_map.at(g_x_axes[g_x_axis]).back(); json_object_put(request); json_object_put(response); + + g_trace_len = g_trace_map.at("rowid").size(); + + if (g_trace_len >= g_entries * 2) { + for (auto &trace : g_trace_map) { + trace.second.erase(trace.second.begin(), trace.second.end() - g_entries); + } + + g_trace_len = g_trace_map.at("rowid").size(); + } + + g_trace_offset = 0; + + if (g_trace_len > g_entries) { + g_trace_offset = g_trace_len - g_entries; + g_trace_len = g_entries; + } + + mtx_unlock(&g_fetching_mutex); } int data_fetching_thread(void *data) { (void)data; + assert(!data); assert(g_status == STATUS_RUNNING || g_status == STATUS_STOPPING); while (g_status == STATUS_RUNNING) { data_fetch(); - for (int i = 0; i < FETCH_INTERVAL_SUBDIV && g_status == STATUS_RUNNING; i++) { - usleep((FETCH_INTERVAL * 1000000) / FETCH_INTERVAL_SUBDIV); + for (int i = 0; i < DATA_FETCH_INTERVAL_SUBDIV && g_status == STATUS_RUNNING; i++) { + usleep((DATA_FETCH_INTERVAL * 1000000) / DATA_FETCH_INTERVAL_SUBDIV); } } @@ -226,27 +318,36 @@ int data_fetching_thread(void *data) { void data_start_fetching(void) { log_info("Starting data fetching thread"); g_status = STATUS_RUNNING; - thrd_create(&g_data_fetching_thread, (thrd_start_t)data_fetching_thread, nullptr); + thrd_create(&g_fetching_thread, (thrd_start_t)data_fetching_thread, nullptr); } void data_stop_fetching(void) { assert(g_status == STATUS_RUNNING); log_info("Stopping data fetching thread"); g_status = STATUS_STOPPING; - thrd_join(g_data_fetching_thread, nullptr); + thrd_join(g_fetching_thread, nullptr); } // ---------------------------------------------------------------------------- // GUI functions // ---------------------------------------------------------------------------- -void gui_print_data_col(void) { +void gui_render_data_input(const char *label, int64_t *target) { + assert(label); + assert(target); + + if (ImGui::InputScalar(label, ImGuiDataType_U64, target, nullptr, nullptr, "%#lx")) { + data_on_field_change(); + } +} + +void gui_render_data_col(void) { const ImGuiViewport *viewport = ImGui::GetMainViewport(); - const ImVec2 next_win_pos = viewport->Pos; - const ImVec2 next_win_size = ImVec2(-1.f, viewport->Size.y); + const ImVec2 win_pos = viewport->Pos; + const ImVec2 win_size = ImVec2(-1.f, viewport->Size.y); - ImGui::SetNextWindowPos(next_win_pos); - ImGui::SetNextWindowSize(next_win_size); - ImGui::Begin("data-col", nullptr, WINDOW_STYLE); + ImGui::SetNextWindowPos(win_pos); + ImGui::SetNextWindowSize(win_size); + ImGui::Begin("data-col", nullptr, IMGUI_WINDOW_FLAGS); g_data_col_width = ImGui::GetWindowWidth(); ImGui::SeparatorText("SALIS data client"); @@ -267,81 +368,127 @@ void gui_print_data_col(void) { switch (g_status) { case STATUS_STOPPED: - if (ImGui::InputInt("entries", &g_entries, 0, 0, ImGuiInputTextFlags_CharsDecimal)) data_touched(); - if (ImGui::InputInt("nth", &g_nth, 0, 0, ImGuiInputTextFlags_CharsDecimal)) data_touched(); + gui_render_data_input("entries", &g_entries); + gui_render_data_input("nth", &g_nth); if (ImGui::BeginCombo("x-axis", g_x_axes[g_x_axis])) { - for (int i = 0; i < CORES + 1; i++) { + for (int i = 0; i < CORES + 2; i++) { if (ImGui::Selectable(g_x_axes[i], g_x_axis == i)) { + data_reset_fields(); g_x_axis = i; - data_touched(); } } ImGui::EndCombo(); } - if (ImGui::InputInt("x-low", &g_x_low, 0, 0, ImGuiInputTextFlags_CharsDecimal)) data_touched(); - if (ImGui::InputInt("x-high", &g_x_high, 0, 0, ImGuiInputTextFlags_CharsDecimal)) data_touched(); - if (ImGui::InputInt("hm-left", &g_hm_left, 0, 0, ImGuiInputTextFlags_CharsDecimal)) data_touched(); - if (ImGui::InputInt("hm-pxl-count", &g_hm_pixel_count, 0, 0, ImGuiInputTextFlags_CharsDecimal)) data_touched(); - if (ImGui::InputInt("hm-pxl-pow", &g_hm_pixel_pow, 0, 0, ImGuiInputTextFlags_CharsDecimal)) data_touched(); - if (ImGui::Button("Run", ImVec2(-1.f, 0.f))) data_start_fetching(); - if (ImGui::Button("Reset", ImVec2(-1.f, 0.f))) data_reset_values(); - + gui_render_data_input("x-low", &g_x_low); + gui_render_data_input("x-high", &g_x_high); + gui_render_data_input("hm-left", &g_hm_left); + gui_render_data_input("hm-pxl-count", &g_hm_pixel_count); + gui_render_data_input("hm-pxl-pow", &g_hm_pixel_pow); break; case STATUS_RUNNING: case STATUS_STOPPING: - ImGui::LabelText("entries", "%d", g_entries); - ImGui::LabelText("nth", "%d", g_nth); + ImGui::LabelText("entries", "%#lx", g_entries); + ImGui::LabelText("nth", "%#lx", g_nth); ImGui::LabelText("x-axis", "%s", g_x_axes[g_x_axis]); - ImGui::LabelText("x-low", "%d", g_x_low); - ImGui::LabelText("x-high", "%d", g_x_high); - ImGui::LabelText("hm-left", "%d", g_hm_left); - ImGui::LabelText("hm-pxl-count", "%d", g_hm_pixel_count); - ImGui::LabelText("hm-pxl-pow", "%d", g_hm_pixel_pow); + ImGui::LabelText("x-low", "%#lx", g_x_low); + ImGui::LabelText("x-high", "%#lx", g_x_high); + ImGui::LabelText("hm-left", "%#lx", g_hm_left); + ImGui::LabelText("hm-pxl-count", "%#lx", g_hm_pixel_count); + ImGui::LabelText("hm-pxl-pow", "%#lx", g_hm_pixel_pow); + } + + switch (g_status) { + case STATUS_STOPPED: + if (ImGui::Button("Run", ImVec2(-1.f, 0.f))) { + data_start_fetching(); + } - if (g_status == STATUS_RUNNING) { - if (ImGui::Button("Stop", ImVec2(-1.f, 0.f))) data_stop_fetching(); - ImGui::LabelText("##", "Running"); - } else { - ImGui::LabelText("##", "Stopping"); + if (ImGui::Button("Reset", ImVec2(-1.f, 0.f))) { + data_reset_fields(); } + + break; + case STATUS_RUNNING: + if (ImGui::Button("Stop", ImVec2(-1.f, 0.f))) { + data_stop_fetching(); + } + + ImGui::LabelText("##", "Running"); + break; + case STATUS_STOPPING: + ImGui::LabelText("##", "Stopping"); + break; } ImGui::SeparatorText("Layout"); ImGui::SliderInt("cols", &g_plot_cols, PLOT_MIN_COLS, PLOT_MAX_COLS); - ImGui::End(); } -void gui_print_plots(void) { +void gui_render_plot(const Plot *plot, const ImVec2 &frame_size=ImVec2(-1.f, 0.f)) { + int plot_flags = ImPlotFlags_NoMenus; + int axis_flags = ImPlotAxisFlags_NoMenus; + + if (g_status != STATUS_STOPPED) { + ImPlot::SetNextAxesToFit(); + plot_flags |= ImPlotFlags_NoInputs | ImPlotFlags_NoMouseText; + axis_flags |= ImPlotAxisFlags_NoHighlight; + } + + if (ImPlot::BeginPlot(plot->name, frame_size, plot_flags)) { + ImPlot::SetupAxes(nullptr, nullptr, axis_flags, axis_flags); + ImS64 *x = g_trace_map.at(g_x_axes[g_x_axis]).data() + g_trace_offset; + + for (auto &trace : plot->traces) { + ImS64 *y = g_trace_map.at(trace).data() + g_trace_offset; + ImPlot::PlotLine(trace, x, y, g_trace_len); + } + + ImPlot::EndPlot(); + } +} + +void gui_render_plots(void) { const char *section_current = g_plots[0].section; const char *section_next = nullptr; bool plots_covered[PLOT_COUNT] = { 0 }; const ImGuiViewport *viewport = ImGui::GetMainViewport(); - const ImVec2 next_win_pos = g_data_col_visible ? ImVec2(g_data_col_width, viewport->Pos.y) : viewport->Pos; - const ImVec2 next_win_size = g_data_col_visible ? ImVec2(viewport->Size.x - g_data_col_width, -1.f) : ImVec2(viewport->Size.x, -1.f); + const ImVec2 win_pos = g_data_col_visible ? ImVec2(g_data_col_width, viewport->Pos.y) : viewport->Pos; + const ImVec2 win_size = g_data_col_visible ? ImVec2(viewport->Size.x - g_data_col_width, -1.f) : ImVec2(viewport->Size.x, -1.f); - ImGui::SetNextWindowPos(next_win_pos); - ImGui::SetNextWindowSize(next_win_size); - ImGui::Begin("plots", nullptr, WINDOW_STYLE); + if (g_plot_scroll) { + ImGui::SetNextWindowScroll(ImVec2(-1.f, g_plot_scroll_to)); + g_plot_scroll = false; + g_plot_scroll_to = 0.f; + } + + ImGui::SetNextWindowPos(win_pos); + ImGui::SetNextWindowSize(win_size); + ImGui::Begin("plots", nullptr, IMGUI_WINDOW_FLAGS); + g_plot_scroll_current = ImGui::GetScrollY(); int col = 0; int row = 0; + mtx_lock(&g_fetching_mutex); + while (section_current) { ImGui::SeparatorText(section_current); ImGui::BeginTable("plots-table", g_plot_cols); - for (int i = 0; i < PLOT_COUNT; i++) { + for (size_t i = 0; i < PLOT_COUNT; i++) { if (g_plots[i].section != section_current) { section_next = (!section_next && !plots_covered[i]) ? g_plots[i].section : section_next; continue; } ImGui::TableNextColumn(); + g_plot_cells[col][row] = &g_plots[i]; + g_plot_cells_top[col][row] = ImGui::GetCursorPosY(); if (&g_plots[i] == g_plot_selected) { g_plot_col_selected = col; @@ -349,102 +496,106 @@ void gui_print_plots(void) { g_implot_style->Colors[ImPlotCol_FrameBg] = g_imgui_style->Colors[ImGuiCol_FrameBg]; } - if (ImPlot::BeginPlot(g_plots[i].name)) { - //int test_x[] = {0,1,2,3}; - //int test_y1[] = {1,2,3,4}; - //int test_y2[] = {2,4,8,16}; - //ImPlot::PlotLine("test1", test_x, test_y1, 4); - //ImPlot::PlotLine("test2", test_x, test_y2, 4); - ImPlot::EndPlot(); - } + gui_render_plot(&g_plots[i]); - g_implot_style->Colors[ImPlotCol_FrameBg] = COLOR_BG; + if (&g_plots[i] == g_plot_selected) { + g_implot_style->Colors[ImPlotCol_FrameBg] = COLOR_BLACK; + } - g_plot_cells[col][row] = &g_plots[i]; + g_plot_cells_bottom[col][row] = ImGui::GetCursorPosY(); col = (col + 1) % g_plot_cols; row += col ? 0 : 1; - plots_covered[i] = true; } section_current = section_next; section_next = nullptr; ImGui::EndTable(); - row += col ? 1 : 0; col = 0; } + mtx_unlock(&g_fetching_mutex); + ImGui::End(); } -void gui_print_plot_maximized(void) { +void gui_render_plot_maximized(void) { const ImGuiViewport *viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(viewport->Pos); ImGui::SetNextWindowSize(viewport->Size); - ImGui::Begin("plot-fullscreen", nullptr, WINDOW_STYLE); + ImGui::Begin("plot-fullscreen", nullptr, IMGUI_WINDOW_FLAGS); - if (ImPlot::BeginPlot(g_plot_selected->name, viewport->Size)) { - //int test_x[] = {0,1,2,3}; - //int test_y1[] = {1,2,3,4}; - //int test_y2[] = {2,4,8,16}; - //ImPlot::PlotLine("test1", test_x, test_y1, 4); - //ImPlot::PlotLine("test2", test_x, test_y2, 4); - ImPlot::EndPlot(); - } + gui_render_plot(g_plot_selected, viewport->Size); ImGui::End(); } -void gui_print(void) { +void gui_plot_queue_scroll(int col_selected, int row_selected) { + const ImGuiViewport *viewport = ImGui::GetMainViewport(); + float plot_top = g_plot_cells_top[col_selected][row_selected]; + float plot_bottom = g_plot_cells_bottom[col_selected][row_selected]; + float win_top = g_plot_scroll_current; + float win_bottom = win_top + viewport->Size.y; + + if (plot_bottom > win_bottom) { + g_plot_scroll_to = win_top + (plot_bottom - win_bottom); + g_plot_scroll = true; + } + + if (plot_top < win_top) { + g_plot_scroll_to = plot_top - PLOT_SCROLL_OFFSET; + g_plot_scroll = true; + } +} + +void gui_render(void) { if (g_plot_maximized) { - gui_print_plot_maximized(); - } else { - if (g_data_col_visible) gui_print_data_col(); - gui_print_plots(); + gui_render_plot_maximized(); + return; } + + if (g_data_col_visible) { + gui_render_data_col(); + } + + gui_render_plots(); } // ---------------------------------------------------------------------------- // Main functions // ---------------------------------------------------------------------------- -void sig_handler(int signo) { +void win_sig_handler(int signo) { (void)signo; log_warn("Signal received, will stop SALIS data client..."); - if (g_status == STATUS_RUNNING) data_stop_fetching(); + + if (g_status == STATUS_RUNNING) { + data_stop_fetching(); + } + glfwSetWindowShouldClose(g_window, GLFW_TRUE); } -void glfw_error_callback(int error, const char* description) { +void win_error_callback(int error, const char* description) { log_warn("GLFW error %d: %s", error, description); } -void glfw_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { +void win_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { (void)window; (void)scancode; - if (action != GLFW_PRESS) return; + if (action != GLFW_PRESS || ImGui::IsAnyItemActive()) { + return; + } if (g_plot_maximized) { - switch (mods) { - case GLFW_MOD_CONTROL: - switch (key) { - case GLFW_KEY_C: - glfwSetWindowShouldClose(g_window, GLFW_TRUE); - break; - } - - break; - - case 0: - switch (key) { - case GLFW_KEY_F: - g_plot_maximized = !g_plot_maximized; - break; - } + if (mods == GLFW_MOD_CONTROL && key == GLFW_KEY_C) { + glfwSetWindowShouldClose(g_window, GLFW_TRUE); + } - break; + if (mods == 0 && key == GLFW_KEY_F) { + g_plot_maximized = !g_plot_maximized; } return; @@ -484,14 +635,27 @@ void glfw_key_callback(GLFWwindow* window, int key, int scancode, int action, in case GLFW_KEY_UP: g_plot_row_selected -= g_plot_row_selected ? 1 : 0; g_plot_selected = g_plot_cells[g_plot_col_selected][g_plot_row_selected]; + gui_plot_queue_scroll(g_plot_col_selected, g_plot_row_selected); break; case GLFW_KEY_DOWN: - g_plot_row_selected += (g_plot_row_selected < PLOT_COUNT - 1 && g_plot_cells[g_plot_col_selected][g_plot_row_selected + 1]) ? 1 : 0; + g_plot_row_selected += (g_plot_row_selected < (int)PLOT_COUNT - 1 && g_plot_cells[g_plot_col_selected][g_plot_row_selected + 1]) ? 1 : 0; g_plot_selected = g_plot_cells[g_plot_col_selected][g_plot_row_selected]; + gui_plot_queue_scroll(g_plot_col_selected, g_plot_row_selected); break; case GLFW_KEY_F: g_plot_maximized = !g_plot_maximized; break; + case GLFW_KEY_SPACE: + switch (g_status) { + case STATUS_STOPPED: + data_start_fetching(); + break; + case STATUS_RUNNING: + data_stop_fetching(); + break; + } + + break; } break; @@ -502,20 +666,20 @@ int main(int argc, char **argv) { (void)argc; (void)argv; - signal(SIGINT, sig_handler); - signal(SIGTERM, sig_handler); + signal(SIGINT, win_sig_handler); + signal(SIGTERM, win_sig_handler); log_info("Starting SALIS data client"); log_info("Initializing GLFW"); - glfwSetErrorCallback(glfw_error_callback); + glfwSetErrorCallback(win_error_callback); glfwInitHint(GLFW_WAYLAND_LIBDECOR, GLFW_WAYLAND_DISABLE_LIBDECOR); if (!glfwInit()) assert(false); float scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfwGetPrimaryMonitor()); g_window = glfwCreateWindow((int)(800 * scale), (int)(600 * scale), "SALIS data client", nullptr, nullptr); assert(g_window); - glfwSetKeyCallback(g_window, glfw_key_callback); + glfwSetKeyCallback(g_window, win_key_callback); glfwMakeContextCurrent(g_window); glfwSwapInterval(1); // enable vsync @@ -524,26 +688,32 @@ int main(int argc, char **argv) { ImGui::CreateContext(); ImPlot::CreateContext(); - g_io = &ImGui::GetIO(); - g_io->Fonts->AddFontFromFileTTF(FONT_SOURCE); - g_io->IniFilename = nullptr; + g_imgui_io = &ImGui::GetIO(); + g_imgui_io->Fonts->AddFontFromFileTTF(FONT_SOURCE); + g_imgui_io->IniFilename = nullptr; g_imgui_style = &ImGui::GetStyle(); - g_imgui_style->Colors[ImGuiCol_WindowBg] = COLOR_BG; + g_imgui_style->Colors[ImGuiCol_WindowBg] = COLOR_BLACK; g_imgui_style->FontScaleDpi = scale; g_imgui_style->FontSizeBase = FONT_SIZE; g_imgui_style->ItemSpacing = ImVec2(g_imgui_style->ItemSpacing.x, 2.f); g_imgui_style->ScaleAllSizes(scale); g_implot_style = &ImPlot::GetStyle(); - g_implot_style->Colors[ImPlotCol_FrameBg] = COLOR_BG; + g_implot_style->Colors[ImPlotCol_FrameBg] = COLOR_BLACK; ImGui_ImplGlfw_InitForOpenGL(g_window, true); ImGui_ImplOpenGL3_Init(GLSL_VERSION); - g_hm_pixel_pow = data_calc_max_hm_pixel_pow(); + for (auto &i : g_traces) { + g_trace_map[i] = {}; + g_trace_map[i].reserve(g_entries); + } + + g_hm_pixel_pow = data_max_hm_pixel_pow(); + + mtx_init(&g_fetching_mutex, mtx_plain); - // Main loop while (!glfwWindowShouldClose(g_window)) { glfwPollEvents(); @@ -551,10 +721,11 @@ int main(int argc, char **argv) { ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); - gui_print(); + gui_render(); ImGui::Render(); - int display_w, display_h; + int display_w; + int display_h; glfwGetFramebufferSize(g_window, &display_w, &display_h); glViewport(0, 0, display_w, display_h); glClearColor(0.f, 0.f, 0.f, 1.f); @@ -563,6 +734,8 @@ int main(int argc, char **argv) { glfwSwapBuffers(g_window); } + mtx_destroy(&g_fetching_mutex); + ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImPlot::DestroyContext(); @@ -571,5 +744,6 @@ int main(int argc, char **argv) { log_info("Stopping SALIS data client"); glfwDestroyWindow(g_window); glfwTerminate(); + return 0; } diff --git a/data/server.c b/data/server.c index 8581b83..d54d853 100644 --- a/data/server.c +++ b/data/server.c @@ -15,9 +15,44 @@ struct Socket { struct sockaddr_in addr; }; +struct json_object *g_response_header; + // ---------------------------------------------------------------------------- -// SQL functions +// SQL callbacks // ---------------------------------------------------------------------------- +void sql_callback_add_column_name(sqlite3_stmt *sql_stmt, void *data) { + assert(sql_stmt); + assert(data); + assert(sqlite3_column_type(sql_stmt, 0) == SQLITE_TEXT); + assert(!strcmp(sqlite3_column_name(sql_stmt, 0), "name")); + + const char *col_name = (const char *)sqlite3_column_text(sql_stmt, 0); + struct json_object *response = (struct json_object *)data; + + if (!json_object_object_get_ex(response, col_name, NULL)) { + json_object_object_add(response, col_name, json_object_new_array()); + } +} + +void sql_callback_add_data(sqlite3_stmt *sql_stmt, void *data) { + assert(sql_stmt); + assert(data); + + struct json_object *response = (struct json_object *)data; + + for (int i = 0; i < sqlite3_column_count(sql_stmt); i++) { + const char *col_name = sqlite3_column_name(sql_stmt, i); + struct json_object *col_data = json_object_object_get(response, col_name); + + if (col_data) { + if (sqlite3_column_type(sql_stmt, i) == SQLITE_BLOB) { + // TODO: render blobs in parallel + } else { + json_object_array_add(col_data, json_object_new_int64(sqlite3_column_int64(sql_stmt, i))); + } + } + } +} // ---------------------------------------------------------------------------- // Main functions @@ -25,6 +60,7 @@ struct Socket { void sig_handler(int signo) { (void)signo; log_warn("Signal received, will stop SALIS data server"); + json_object_put(g_response_header); sql_close(); exit(0); } @@ -65,15 +101,45 @@ void respond_data(int socket_fd, struct json_object *request) { const char *request_str = json_object_to_json_string(request); log_info("Client requested simulation data with the following parameters: %s", request_str); + const char *x_axis = json_object_get_string(json_object_object_get(request, "x-axis")); + int64_t x_current = json_object_get_int64(json_object_object_get(request, "x-current")); + int64_t x_high = json_object_get_int64(json_object_object_get(request, "x-high")); + int64_t nth = json_object_get_int64(json_object_object_get(request, "nth")); + int64_t entries = json_object_get_int64(json_object_object_get(request, "entries")); + + const char *x_axis_pref = (!strcmp(x_axis, "rowid") || !strcmp(x_axis, "step")) ? "core." : ""; + + struct json_object *response = NULL; + json_object_deep_copy(g_response_header, &response, NULL); + + sql_exec( + 0, NULL, NULL, + sql_callback_add_data, + response, + "select * from (" + "select core.rowid, core.step, * from core inner join arch " + "where core.rowid = arch.rowid and %s%s > %ld and %s%s <= %ld and core.rowid %% %ld == 0 " + "order by %s%s desc limit %ld" + ") order by %s asc;", + x_axis_pref, + x_axis, + x_current, + x_axis_pref, + x_axis, + x_high, + nth, + x_axis_pref, + x_axis, + entries, + x_axis + ); - struct json_object *response = json_object_new_object(); - json_object_object_add(response, "response", json_object_new_string("hello!")); const char *response_str = json_object_to_json_string(response); - log_info("Sending response to client: %s", response_str); + log_info("Responding to client with: %s", response_str); json_object_to_fd(socket_fd, response, 0); + json_object_put(response); shutdown(socket_fd, SHUT_WR); - json_object_put(response); } int handle_client(struct Socket *socket) { @@ -120,6 +186,17 @@ int main(void) { signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); + log_info("Creating response header"); + g_response_header = json_object_new_object(); + json_object_object_add(g_response_header, "rowid", json_object_new_array()); + sql_exec( + 0, NULL, NULL, + sql_callback_add_column_name, + g_response_header, + "select name from pragma_table_info('core') union " + "select name from pragma_table_info('arch');" + ); + log_info("Binding to port: %d", PORT); int opt = 1; int socket_fd = socket(AF_INET, SOCK_STREAM, 0); @@ -130,8 +207,8 @@ int main(void) { socket_addr.sin_port = htons(PORT); bind(socket_fd, (struct sockaddr *)&socket_addr, sizeof(struct sockaddr_in)); - log_info("Listening..."); listen(socket_fd, BACKLOG); + log_info("Listening..."); while (true) { struct Socket *socket = calloc(1, sizeof(struct Socket)); |
