From 0601ede07bd488270d9c8220d8946b486dd98d31 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Tue, 28 May 2024 10:03:53 +0200 Subject: [PATCH] test_rtt: Rework calculation of test duration Signed-off-by: Steffen Vogel --- .../schemas/config/nodes/test_rtt.yaml | 12 +- etc/examples/nodes/test_rtt.conf | 11 +- include/villas/nodes/test_rtt.hpp | 23 +++- lib/nodes/test_rtt.cpp | 124 ++++++++++++------ 4 files changed, 122 insertions(+), 48 deletions(-) diff --git a/doc/openapi/components/schemas/config/nodes/test_rtt.yaml b/doc/openapi/components/schemas/config/nodes/test_rtt.yaml index d76a08f63..2aa753daa 100644 --- a/doc/openapi/components/schemas/config/nodes/test_rtt.yaml +++ b/doc/openapi/components/schemas/config/nodes/test_rtt.yaml @@ -58,7 +58,7 @@ allOf: - 10 - 100 - limit: + count: description: | The resulting test-case will send the number of samples specified by this setting. This setting is exclusive with the `duration` setting. @@ -72,4 +72,14 @@ allOf: type: number example: 60.0 + mode: + type: string + enum: + - min + - max + - at_least_count + - at_least_duration + - stop_after_count + - stop_after_duration + - $ref: ../node.yaml diff --git a/etc/examples/nodes/test_rtt.conf b/etc/examples/nodes/test_rtt.conf index 5fba2e525..3ed87f740 100644 --- a/etc/examples/nodes/test_rtt.conf +++ b/etc/examples/nodes/test_rtt.conf @@ -24,6 +24,15 @@ nodes = { # last test case has been completed. shutdown = true; + # One of: + # - min + # - max + # - at_least_count + # - at_least_duration + # - stop_after_count + # - stop_after_duration + mode = "at_least_count" + # The list of test cases # Each test case can specify a single or an array of rates and values # If arrays are used, we will generate multiple test cases with all @@ -37,7 +46,7 @@ nodes = { values = [ 5, 10, 20], # The number of samples which should be send during this test case - limit = 100 + count = 100 }, { # An array of sending rates in Hz diff --git a/include/villas/nodes/test_rtt.hpp b/include/villas/nodes/test_rtt.hpp index 560d545ad..d46cc9c8e 100644 --- a/include/villas/nodes/test_rtt.hpp +++ b/include/villas/nodes/test_rtt.hpp @@ -34,12 +34,12 @@ protected: double cooldown; // Number of seconds to wait between tests. unsigned values; - unsigned limit; // The number of samples we send per test. + unsigned count; // The number of samples we send per test. unsigned sent; unsigned received; unsigned missed; - unsigned limit_warmup; // The number of samples we send during warmup. + unsigned count_warmup; // The number of samples we send during warmup. unsigned sent_warmup; unsigned received_warmup; unsigned missed_warmup; @@ -54,11 +54,11 @@ protected: public: Case(TestRTT *n, int id, int rate, float warmup, float cooldown, int values, - int limit, int limit_warmup, const std::string &filename) + int count, int count_warmup, const std::string &filename) : node(n), id(id), rate(rate), warmup(warmup), cooldown(cooldown), - values(values), limit(limit), sent(0), received(0), missed(0), - limit_warmup(limit_warmup), sent_warmup(0), received_warmup(0), missed_warmup(0), - filename(filename){}; + values(values), count(count), sent(0), received(0), missed(0), + count_warmup(count_warmup), sent_warmup(0), received_warmup(0), + missed_warmup(0), filename(filename){}; int start(); int stop(); @@ -76,6 +76,15 @@ protected: bool shutdown; + enum Mode { + MIN, + MAX, + STOP_COUNT, + STOP_DURATION, + AT_LEAST_COUNT, + AT_LEAST_DURATION + } mode; + virtual int _read(struct Sample *smps[], unsigned cnt); virtual int _write(struct Sample *smps[], unsigned cnt); @@ -83,7 +92,7 @@ protected: public: TestRTT(const uuid_t &id = {}, const std::string &name = "") : Node(id, name), task(CLOCK_MONOTONIC), formatter(nullptr), - stream(nullptr), shutdown(false) {} + stream(nullptr), shutdown(false), mode(Mode::AT_LEAST_COUNT) {} virtual ~TestRTT(){}; diff --git a/lib/nodes/test_rtt.cpp b/lib/nodes/test_rtt.cpp index 8da30b7b1..f37197e67 100644 --- a/lib/nodes/test_rtt.cpp +++ b/lib/nodes/test_rtt.cpp @@ -26,9 +26,9 @@ static SuperNode *sn = nullptr; int TestRTT::Case::start() { node->logger->info("Starting case #{}/{}: filename={}, rate={}/s, values={}, " - "limit={}smps, warmup={:.3f}s, cooldown={:.3f}s", + "count={}smps, warmup={:.3f}s, cooldown={:.3f}s", id + 1, node->cases.size(), filename_formatted, rate, - values, limit, warmup, cooldown); + values, count, warmup, cooldown); // Open file node->stream = fopen(filename_formatted.c_str(), "a+"); @@ -59,7 +59,10 @@ int TestRTT::Case::stop() { if (ret) throw SystemError("Failed to close file"); - node->logger->info("Stopping case #{}/{}: limit={}smps, sent={}smps, received={}smps, missed={}smps, duration={:.3f}s", id + 1, node->cases.size(), limit, sent, received, missed, time_delta(&started, &stopped)); + node->logger->info("Stopping case #{}/{}: count={}smps, sent={}smps, " + "received={}smps, missed={}smps, duration={:.3f}s", + id + 1, node->cases.size(), count, sent, received, missed, + time_delta(&started, &stopped)); return 0; } @@ -67,16 +70,17 @@ int TestRTT::Case::stop() { json_t *TestRTT::Case::getMetadata() { json_t *json_warmup = nullptr; - if (limit_warmup > 0) { - json_warmup = json_pack("{ s: i, s: i, s: i, s: i }", "limit", limit_warmup, - "sent", sent_warmup, "received", received_warmup, "missed", missed_warmup); + if (count_warmup > 0) { + json_warmup = json_pack("{ s: i, s: i, s: i, s: i }", "count", count_warmup, + "sent", sent_warmup, "received", received_warmup, + "missed", missed_warmup); } return json_pack( "{ s: i, s: f, s: i, s: f, s: f, s: i, s: i, s: i, s: i, s: o* }", "id", id, "rate", rate, "values", values, "started", time_to_double(&started), - "stopped", time_to_double(&stopped), "limit", limit, - "sent", sent, "received", received, "missed", missed, "warmup", json_warmup); + "stopped", time_to_double(&stopped), "count", count, "sent", sent, + "received", received, "missed", missed, "warmup", json_warmup); } int TestRTT::prepare() { @@ -110,6 +114,7 @@ int TestRTT::parse(json_t *json) { const char *output_str = "."; const char *prefix_str = nullptr; + const char *mode_str = nullptr; double warmup_default = 0; double cooldown_default = 0; int shutdown_ = -1; @@ -121,11 +126,11 @@ int TestRTT::parse(json_t *json) { ret = json_unpack_ex( json, &err, 0, - "{ s?: s, s?: s, s?: o, s?: F, s?: F, s?: o, s?: o, s: o, s?: b }", + "{ s?: s, s?: s, s?: o, s?: F, s?: F, s?: o, s?: o, s: o, s?: b, s?: s }", "prefix", &prefix_str, "output", &output_str, "format", &json_format, "cooldown", &cooldown_default, "warmup", &warmup_default, "values", &json_values_default, "rates", &json_rates_default, "cases", &json_cases, - "shutdown", &shutdown_); + "shutdown", &shutdown_, "mode", &mode_str); if (ret) throw ConfigError(json, err, "node-config-node-test-rtt"); @@ -135,6 +140,24 @@ int TestRTT::parse(json_t *json) { if (shutdown_ > 0) shutdown = shutdown_ > 0; + if (mode_str) { + if (strcmp(mode_str, "min")) + mode = Mode::MIN; + else if (strcmp(mode_str, "max")) + mode = Mode::MIN; + else if (strcmp(mode_str, "stop_after_count")) + mode = Mode::STOP_COUNT; + else if (strcmp(mode_str, "stop_after_duration")) + mode = Mode::STOP_DURATION; + else if (strcmp(mode_str, "at_least_count")) + mode = Mode::AT_LEAST_COUNT; + else if (strcmp(mode_str, "at_least_duration")) + mode = Mode::AT_LEAST_DURATION; + else + throw ConfigError(json, "node-config-node-test-rtt-mode", + "Invalid mode: {}. Must be 'min' or 'max'", mode_str); + } + // Formatter auto *fmt = json_format ? FormatFactory::make(json_format) : FormatFactory::make("villas.human"); @@ -163,7 +186,7 @@ int TestRTT::parse(json_t *json) { int id = 0; json_array_foreach(json_cases, i, json_case) { - int limit = -1; + int count = -1; double duration = -1; // in secs double cooldown = cooldown_default; // in secs double warmup = warmup_default; // in secs @@ -174,15 +197,10 @@ int TestRTT::parse(json_t *json) { json_t *json_values = json_values_default; ret = json_unpack_ex(json_case, &err, 0, "{ s?: o, s?: o, s?: i, s?: F }", - "rates", &json_rates, "values", &json_values, "limit", - &limit, "duration", &duration, "warmup", &warmup, + "rates", &json_rates, "values", &json_values, "count", + &count, "duration", &duration, "warmup", &warmup, "cooldown", &cooldown); - if (limit > 0 && duration > 0) - throw ConfigError( - json_case, "node-config-node-test-rtt-duration", - "The settings 'duration' and 'limit' must be used exclusively"); - if (!json_is_array(json_rates) && !json_is_number(json_rates)) throw ConfigError( json_case, "node-config-node-test-rtt-rates", @@ -221,21 +239,49 @@ int TestRTT::parse(json_t *json) { for (int rate : rates) { for (int value : values) { - int lim, lim_warmup; - if (limit > 0) - lim = limit; - else if (duration > 0) - lim = duration * rate; - else - lim = 1000; // Default value + int count_effective; + int count_duration = duration * rate; + int count_warmup = warmup * rate; - lim_warmup = warmup * rate; + switch (mode) { + case Mode::MIN: + count_effective = MIN(count, count_duration); + break; + + case Mode::MAX: + count_effective = MAX(count, count_duration); + break; + + case Mode::STOP_COUNT: + count_effective = count_duration; + if (count_effective > count) + count_effective = count; + break; + + case Mode::STOP_DURATION: + count_effective = count; + if (count_effective > count_duration) + count_effective = count_duration; + break; + + case Mode::AT_LEAST_COUNT: + count_effective = count_duration; + if (count_effective < count) + count_effective = count; + break; + + case Mode::AT_LEAST_DURATION: + count_effective = count; + if (count_effective < count_duration) + count_effective = count_duration; + break; + } auto filename = fmt::format("{}/{}_values{}_rate{}.log", output, prefix, value, rate); - cases.emplace_back(this, id++, rate, warmup, cooldown, value, lim, - lim_warmup, filename); + cases.emplace_back(this, id++, rate, warmup, cooldown, value, + count_effective, count_warmup, filename); } } } @@ -245,8 +291,8 @@ int TestRTT::parse(json_t *json) { const std::string &TestRTT::getDetails() { if (details.empty()) { - details = fmt::format("output={}, prefix={}, #cases={}, shutdown={}", output, prefix, - cases.size(), shutdown); + details = fmt::format("output={}, prefix={}, #cases={}, shutdown={}", + output, prefix, cases.size(), shutdown); } return details; @@ -297,7 +343,7 @@ int TestRTT::_read(struct Sample *smps[], unsigned cnt) { if (steps > 1) { logger->warn("Skipped {} steps", steps - 1); - if (current->sent_warmup < current->limit_warmup) + if (current->sent_warmup < current->count_warmup) current->missed_warmup += steps - 1; else current->missed += steps - 1; @@ -305,7 +351,7 @@ int TestRTT::_read(struct Sample *smps[], unsigned cnt) { // Cooldown of case completed.. if (current->sent + current->sent_warmup >= - current->limit + current->limit_warmup) { + current->count + current->count_warmup) { ret = current->stop(); if (ret) return ret; @@ -328,12 +374,12 @@ int TestRTT::_read(struct Sample *smps[], unsigned cnt) { if (ret) return ret; - if (current->limit_warmup > 0) + if (current->count_warmup > 0) logger->info("Starting warmup phase. Sending {} samples...", - current->limit_warmup); - } else if (current->sent == current->limit_warmup) + current->count_warmup); + } else if (current->sent == current->count_warmup) logger->info("Completed warmup phase. Sending {} samples...", - current->limit); + current->count); auto now = time_now(); @@ -349,14 +395,14 @@ int TestRTT::_read(struct Sample *smps[], unsigned cnt) { (int)SampleFlags::HAS_TS_ORIGIN; smp->signals = getInputSignals(false); - if (current->sent_warmup < current->limit_warmup) + if (current->sent_warmup < current->count_warmup) current->sent_warmup++; else current->sent++; } if (current->sent + current->sent_warmup >= - current->limit + current->limit_warmup) { + current->count + current->count_warmup) { if (current->cooldown > 0) { logger->info("Entering cooldown phase. Waiting {} seconds...", current->cooldown); @@ -384,7 +430,7 @@ int TestRTT::_write(struct Sample *smps[], unsigned cnt) { continue; } - if (smp->sequence < current->limit_warmup) { + if (smp->sequence < current->count_warmup) { // Skip samples from warmup phase current->received_warmup++; continue;