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 eb24c18..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,14 +77,19 @@ 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);
};
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 +109,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..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,17 +112,27 @@ 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());
}
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;
diff --git a/include/blot.hpp b/include/blot.hpp
index 7215c66..5f502b3 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");
@@ -184,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();
@@ -206,6 +207,14 @@ 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,
+ std::span(data_xs.data(), data_xs.size()),
+ std::span(data_ys.data(), data_ys.size()),
+ 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);