From 4eec070ebda171a4f6687c67ddd218cd4bd98838 Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Tue, 29 Jul 2025 17:09:07 -0400 Subject: [PATCH 1/5] support for --ascii and --unicode --- cli/config.hpp | 8 ++++++-- cli/plotter.hpp | 14 ++++++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/cli/config.hpp b/cli/config.hpp index eb24c18..bf5616e 100644 --- a/cli/config.hpp +++ b/cli/config.hpp @@ -78,9 +78,12 @@ class Input final { }; class Config final { +public: + using OutputType = enum output_type { ASCII, UNICODE, BRAILLE }; + protected: const char *m_self{}; - enum output_type { ASCII, UNICODE, BRAILLE } m_output_type; + OutputType m_output_type; const static blot_color m_first_color{9}; std::vector m_inputs; bool m_display_interval{1}; @@ -100,8 +103,9 @@ class Config final { } } - size_t inputs() const { return m_inputs.size(); } + OutputType output_type() const { return m_output_type; } + size_t inputs() const { return m_inputs.size(); } const Input& input(size_t n) const { return m_inputs.at(n); } Input& input(size_t n) { return m_inputs.at(n); } diff --git a/cli/plotter.hpp b/cli/plotter.hpp index 08ed3f2..ea0ab8f 100644 --- a/cli/plotter.hpp +++ b/cli/plotter.hpp @@ -74,10 +74,20 @@ class Plotter final { double t_add = timing ? blot_double_time() : 0; blot_render_flags flags - = BLOT_RENDER_BRAILLE - | BLOT_RENDER_LEGEND_BELOW + = BLOT_RENDER_LEGEND_BELOW | BLOT_RENDER_CLEAR; + switch (m_config.output_type()) { + case Config::ASCII: + flags = flags | BLOT_RENDER_NO_UNICODE; + break; + case Config::BRAILLE: + flags = flags | BLOT_RENDER_BRAILLE; + break; + default: // UNICODE + break; + } + if (timing) flags = flags | BLOT_RENDER_LEGEND_DETAILS; From f0d57475d92ab0c5ef0f9f21cdd0649fb86e9752 Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Tue, 29 Jul 2025 22:19:45 -0400 Subject: [PATCH 2/5] type to blot data type should work with const --- include/blot.hpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/include/blot.hpp b/include/blot.hpp index 7215c66..07ca40f 100644 --- a/include/blot.hpp +++ b/include/blot.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -86,15 +87,15 @@ class Exception final : public std::exception { template constexpr blot_data_type data_type() { - if constexpr (std::is_same_v) + if constexpr (std::is_same_v, int16_t>) return BLOT_DATA_INT16; - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v, int32_t>) return BLOT_DATA_INT32; - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v, int64_t>) return BLOT_DATA_INT64; - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v, float>) return BLOT_DATA_FLOAT; - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v, double>) return BLOT_DATA_DOUBLE; else BLOT_THROW(EINVAL, "unsupported type"); From 64ea8bc37840b97603706476251b57a566dc5668 Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Tue, 29 Jul 2025 22:20:45 -0400 Subject: [PATCH 3/5] Blot::Figure::plot() will take std::span<> --- include/blot.hpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/blot.hpp b/include/blot.hpp index 07ca40f..c7099d6 100644 --- a/include/blot.hpp +++ b/include/blot.hpp @@ -185,7 +185,7 @@ struct Figure final : public blot_figure { /* add layers */ template - void plot(blot_plot_type plot_type, const std::vector &data_xs, const std::vector &data_ys, blot_color data_color, const char *data_label) { + void plot(blot_plot_type plot_type, const std::span &data_xs, const std::span &data_ys, blot_color data_color, const char *data_label) { GError *error = nullptr; size_t data_count = data_ys.size(); @@ -207,6 +207,11 @@ struct Figure final : public blot_figure { } } + template + void plot(blot_plot_type plot_type, const std::vector &data_xs, const std::vector &data_ys, blot_color data_color, const char *data_label) { + plot(plot_type, data_xs, data_ys, data_color, data_label); + } + template void scatter(const std::vector &data_xs, const std::vector& data_ys, blot_color data_color, const char *data_label) { plot(BLOT_SCATTER, data_xs, data_ys, data_color, data_label); From 48e075ae58c29e197114486626455f18b1b409e9 Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Tue, 29 Jul 2025 22:23:01 -0400 Subject: [PATCH 4/5] blot --limit --- cli/config.cpp | 36 ++++++++++++++++++++++++++++------ cli/config.hpp | 6 ++++++ cli/plotter.hpp | 51 ++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 84 insertions(+), 9 deletions(-) diff --git a/cli/config.cpp b/cli/config.cpp index cc24475..b03b3c8 100644 --- a/cli/config.cpp +++ b/cli/config.cpp @@ -22,7 +22,9 @@ Config::Config(int argc, char *argv[]) bool show_help{}, show_version{}; auto cli_head = ( clipp::option("-h", "--help").set(show_help).doc("This help"), - clipp::option("-V", "--version").set(show_version).doc("Version"), + clipp::option("-V", "--version").set(show_version).doc("Version") + ); + auto cli_mod = "Run modifiers:" % ( clipp::option("-v", "--verbose").call([]{ spdlog::set_level(spdlog::level::debug); }).doc("Enable verbose output"), @@ -35,9 +37,9 @@ Config::Config(int argc, char *argv[]) .doc("Display interval in seconds") ); - auto cli_output = "Output:" % ( - clipp::option("-A", "--ascii").set(m_output_type,ASCII).doc("ASCII output") | - clipp::option("-U", "--unicode").set(m_output_type,UNICODE).doc("Unicode output") | + auto cli_output = "Output:" % one_of ( + clipp::option("-A", "--ascii").set(m_output_type,ASCII).doc("ASCII output"), + clipp::option("-U", "--unicode").set(m_output_type,UNICODE).doc("Unicode output"), clipp::option("-B", "--braille").set(m_output_type,BRAILLE).doc("Braille output") ); @@ -53,6 +55,7 @@ Config::Config(int argc, char *argv[]) auto cli = ( cli_head | ( + cli_mod, cli_output, clipp::repeatable( /* select a plot type */ @@ -98,8 +101,10 @@ Config::Config(int argc, char *argv[]) .doc("Find numbers in input line, pick 1 or 2 positions for X and Y values"), (clipp::option("-r", "--regex") & clipp::value("regex") .call([&](const char *txt) { m_inputs.back().set_regex(txt); })) - .doc("Regex to match numbers from input line") - + .doc("Regex to match numbers from input line"), + (clipp::option("-l", "--limit") & clipp::value("count") + .call([&](const char *txt) { m_inputs.back().set_data_limit(txt); })) + .doc("How many historical values to retain for plotting") ), "Plot modifiers:" % ( @@ -169,6 +174,7 @@ Config::Config(int argc, char *argv[]) bool with_interval{}, no_interval{}; double prev_interval = 1; + size_t prev_data_limit = Input::g_default_data_limit; for (auto &input : m_inputs) { if (input.needs_interval()) { if (!input.interval()) { @@ -177,6 +183,11 @@ Config::Config(int argc, char *argv[]) prev_interval = input.interval(); } } + if (!input.data_limit()) { + input.set_data_limit(prev_data_limit); + } else { + prev_data_limit = input.data_limit(); + } if (!input) { spdlog::error("incomplete plot definition"); std::exit(1); @@ -316,6 +327,19 @@ void Input::set_interval (const std::string &txt) std::exit(1); } } +void Input::set_data_limit (size_t value) +{ + m_data_limit = value; +} +void Input::set_data_limit (const std::string &txt) +{ + auto [_,ec] = std::from_chars(txt.data(), txt.data()+txt.size(), m_data_limit); + if (ec != std::errc{}) { + spdlog::error("failed to parse data limit from '{}': {}", + txt, std::make_error_code(ec).message()); + std::exit(1); + } +} Input::operator bool() const { diff --git a/cli/config.hpp b/cli/config.hpp index bf5616e..b43c227 100644 --- a/cli/config.hpp +++ b/cli/config.hpp @@ -20,6 +20,8 @@ class Input final { WATCH // run program repetitively at interval, each run is one entry }; + static constexpr const size_t g_default_data_limit = 1000000; + protected: blot_plot_type m_plot_type{BLOT_LINE}; Source m_source{NONE}; @@ -27,6 +29,7 @@ class Input final { Extract m_extract; blot_color m_plot_color; double m_interval{0}; + size_t m_data_limit{g_default_data_limit}; public: explicit Input(blot_plot_type plot_type, blot_color color) @@ -63,6 +66,7 @@ class Input final { const Extract& extract() const { return m_extract; } blot_color plot_color() const { return m_plot_color; } double interval() const { return m_interval; } + size_t data_limit() const { return m_data_limit; } /* validate if the configuration looks sane */ operator bool() const; @@ -73,6 +77,8 @@ class Input final { void set_color (const std::string &txt); void set_interval (double interval); void set_interval (const std::string &txt); + void set_data_limit (size_t); + void set_data_limit (const std::string &txt); }; diff --git a/cli/plotter.hpp b/cli/plotter.hpp index ea0ab8f..a51d39a 100644 --- a/cli/plotter.hpp +++ b/cli/plotter.hpp @@ -9,9 +9,49 @@ template class Plotter final { protected: + template + class Storage { + protected: + std::vector m_data; + size_t m_offset{}; + + void relocation_check() { + bool full = m_data.size() == m_data.capacity(); + if (full && m_offset && m_offset > (m_data.capacity()/2)) { + // full but most is empty, so we should relocate the data + m_data.erase(m_data.begin(), m_data.begin() + m_offset); + m_offset = 0; + } + } + + public: + void push_back(const T &v) { + relocation_check(); + m_data.push_back(v); + } + + void erase_front(size_t count = 1) { + if (count > size()) + BLOT_THROW(ERANGE, "count=%zu exceeds size=%zu", count, size()); + m_offset += count; + } + + T* data() noexcept { return m_data.data() + m_offset; } + const T* data() const noexcept { return m_data.data() + m_offset; } + size_t size() const { return m_data.size() - m_offset; } + + std::span span() { + return std::span(data(), size()); + } + std::span span() const { + return std::span(data(), size()); + } + + }; + struct Data { - std::vector m_xs; - std::vector m_ys; + Storage m_xs; + Storage m_ys; }; std::vector m_data; size_t m_count{}; @@ -41,6 +81,11 @@ class Plotter final { m_data[layer].m_xs.push_back(x); m_data[layer].m_ys.push_back(y); m_count ++; + + if (m_data[layer].m_xs.size() > m_config.input(layer).data_limit()) { + m_data[layer].m_xs.erase_front(); + m_data[layer].m_ys.erase_front(); + } } bool have_data() const { return m_count > 0; } @@ -67,7 +112,7 @@ class Plotter final { const auto &data = m_data[i]; const auto &input = m_config.input(i); - fig.plot(input.plot_type(), data.m_xs, data.m_ys, + fig.plot(input.plot_type(), data.m_xs.span(), data.m_ys.span(), input.plot_color(), input.details()); } From 97e1f159e23df20da4b044ac3a0edcf700367a55 Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Tue, 29 Jul 2025 22:29:11 -0400 Subject: [PATCH 5/5] fix the infinite recursion in Blot::Figure::plot() --- include/blot.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/blot.hpp b/include/blot.hpp index c7099d6..5f502b3 100644 --- a/include/blot.hpp +++ b/include/blot.hpp @@ -209,7 +209,10 @@ struct Figure final : public blot_figure { template void plot(blot_plot_type plot_type, const std::vector &data_xs, const std::vector &data_ys, blot_color data_color, const char *data_label) { - plot(plot_type, data_xs, data_ys, data_color, data_label); + plot(plot_type, + std::span(data_xs.data(), data_xs.size()), + std::span(data_ys.data(), data_ys.size()), + data_color, data_label); } template