From fe3857a9d9a6d6a34c148202225c6c7b2ae8a465 Mon Sep 17 00:00:00 2001 From: "Ritesh.K" Date: Sun, 14 Dec 2025 22:22:04 +0100 Subject: [PATCH 01/13] add feature to read multiple files at once Signed-off-by: Ritesh.K --- include/villas/nodes/file.hpp | 22 +++ lib/nodes/file.cpp | 256 ++++++++++++++++++++++++++++++++-- 2 files changed, 268 insertions(+), 10 deletions(-) diff --git a/include/villas/nodes/file.hpp b/include/villas/nodes/file.hpp index f148ac364..9a3d8f1b0 100644 --- a/include/villas/nodes/file.hpp +++ b/include/villas/nodes/file.hpp @@ -7,7 +7,11 @@ #pragma once +#include #include +#include +#include +#include #include #include @@ -57,6 +61,24 @@ struct file { struct timespec epoch; // The epoch timestamp from the configuration. struct timespec offset; // An offset between the timestamp in the input file and the current time + + std::vector samples; + size_t read_pos; + + enum class ReadMode { RATE_BASED, READ_ALL } read_mode; + bool read; + + // For multi-file support + std::vector uri_templates; + std::vector uris; + + std::map> + file_samples; // Map: filename --> filesamples + std::map + file_read_pos; // Map: filename --> current read position in file + bool multi_file_mode; + size_t + total_files_size; //To calculate the total number of lines to be read across multiple files }; char *file_print(NodeCompat *n); diff --git a/lib/nodes/file.cpp b/lib/nodes/file.cpp index 3b6166869..72896538e 100644 --- a/lib/nodes/file.cpp +++ b/lib/nodes/file.cpp @@ -6,8 +6,15 @@ */ #include +#include +#include #include +#include +#include +#include +#include +#include #include #include #include @@ -20,6 +27,9 @@ #include #include +#include "villas/config_class.hpp" +#include "villas/sample.hpp" + using namespace villas; using namespace villas::node; using namespace villas::utils; @@ -76,16 +86,17 @@ int villas::node::file_parse(NodeCompat *n, json_t *json) { const char *uri_tmpl = nullptr; const char *eof = nullptr; const char *epoch = nullptr; + const char *read_mode = nullptr; double epoch_flt = 0; ret = json_unpack_ex(json, &err, 0, "{ s: s, s?: o, s?: { s?: s, s?: F, s?: s, s?: F, s?: " - "i, s?: i }, s?: { s?: b, s?: i } }", + "i, s?: i, s?: s }, s?: { s?: b, s?: i } }", "uri", &uri_tmpl, "format", &json_format, "in", "eof", &eof, "rate", &f->rate, "epoch_mode", &epoch, "epoch", &epoch_flt, "buffer_size", &f->buffer_size_in, "skip", - &f->skip_lines, "out", "flush", &f->flush, "buffer_size", - &f->buffer_size_out); + &f->skip_lines, "read_mode", &read_mode, "out", "flush", + &f->flush, "buffer_size", &f->buffer_size_out); if (ret) throw ConfigError(json, err, "node-config-node-file"); @@ -127,6 +138,36 @@ int villas::node::file_parse(NodeCompat *n, json_t *json) { throw RuntimeError("Invalid value '{}' for setting 'epoch'", epoch); } + if (read_mode) { + if (!strcmp(read_mode, "all")) + f->read_mode = file::ReadMode::READ_ALL; + else if (!strcmp(read_mode, "rate_based")) + f->read_mode = file::ReadMode::RATE_BASED; + else + throw RuntimeError("Invalid value '{}' for setting 'read_mode'", + read_mode); + } + + json_t *json_uri = json_object_get(json, "uri"); + json_t *json_uris = json_object_get(json, "uris"); + + if (json_uris && json_is_array(json_uris)) { + f->multi_file_mode = true; + size_t idx; + json_t *json_uri_item; + + json_array_foreach(json_uris, idx, json_uri_item) { + if (json_is_string(json_uri_item)) { + const char *uri_str = json_string_value(json_uri_item); + f->uri_templates.push_back(std::string(uri_str)); + } + } + } else if (json_uri && json_is_string(json_uri)) { + f->multi_file_mode = false; + } else + throw ConfigError(json, "node-config-node-file", + "Must specify either 'uri' or 'uris'"); + return 0; } @@ -136,6 +177,7 @@ char *villas::node::file_print(NodeCompat *n) { const char *epoch_str = nullptr; const char *eof_str = nullptr; + const char *read_mode = nullptr; switch (f->epoch_mode) { case file::EpochMode::DIRECT: @@ -181,11 +223,25 @@ char *villas::node::file_print(NodeCompat *n) { break; } - strcatf( - &buf, - "uri=%s, out.flush=%s, in.skip=%d, in.eof=%s, in.epoch=%s, in.epoch=%.2f", - f->uri ? f->uri : f->uri_tmpl, f->flush ? "yes" : "no", f->skip_lines, - eof_str, epoch_str, time_to_double(&f->epoch)); + switch (f->read_mode) { + case file::ReadMode::READ_ALL: + read_mode = "all"; + break; + + case file::ReadMode::RATE_BASED: + read_mode = "rate_based"; + break; + + default: + read_mode = ""; + break; + } + + strcatf(&buf, + "uri=%s, out.flush=%s, in.skip=%d, in.eof=%s, in.epoch=%s, " + "in.epoch=%.2f, in.read_mode=%s", + f->uri ? f->uri : f->uri_tmpl, f->flush ? "yes" : "no", f->skip_lines, + eof_str, epoch_str, time_to_double(&f->epoch), read_mode); if (f->rate) strcatf(&buf, ", in.rate=%.1f", f->rate); @@ -245,6 +301,59 @@ int villas::node::file_start(NodeCompat *n) { f->formatter->start(n->getInputSignals(false)); + //Experimental code for testing multi file read mode + if (f->multi_file_mode && f->read_mode == file::ReadMode::READ_ALL) { + struct timespec now = time_now(); + + for (const auto &uri_tmpl : f->uri_templates) { + char *resolved_uri = file_format_name(uri_tmpl.c_str(), &now); + std::string filename = resolved_uri; + delete[] resolved_uri; + + FILE *file_stream = fopen(filename.c_str(), "r"); + if (!file_stream) { + n->logger->warn("Failed to open file: {}", filename); + continue; + } + + std::vector file_data; + while (true) { + Sample *smp = sample_alloc_mem(n->getInputSignals(false)->size()); + if (!smp) { + n->logger->error("Failed to allocate sample for file: {}", + filename.c_str()); + break; + } + + int ret = f->formatter->scan(file_stream, smp); + if (ret < 0) { + sample_free(smp); + n->logger->error("Failed to scan file stream"); + break; + } + if (ret == 0) { + sample_free(smp); + n->logger->debug("Finished reating from file_stream"); + break; + } + + file_data.push_back(smp); + } + + f->file_samples[filename] = std::move(file_data); + f->file_read_pos[filename] = 0; + f->uris.push_back(filename); + } + + for (auto &file_sample_pair : f->file_samples) { + const std::vector ¤t_file = file_sample_pair.second; + f->total_files_size += current_file.size(); + } + f->read_pos = 0; + + return 0; + } + // Open file f->stream_out = fopen(f->uri, "a+"); if (!f->stream_out) @@ -298,11 +407,50 @@ int villas::node::file_start(NodeCompat *n) { sample_free(smp); + if (f->read_mode == file::ReadMode::READ_ALL) { + Sample *smp; + int ret; + + while (!feof(f->stream_in)) { + smp = sample_alloc_mem(n->getInputSignals(false)->size()); + if (!smp) { + n->logger->error("Failed to allocate samples"); + break; + } + + ret = f->formatter->scan(f->stream_in, smp); + if (ret < 0) { + sample_free(smp); + break; + } + + f->samples.push_back(smp); + } + + f->read_pos = 0; + } return 0; } int villas::node::file_stop(NodeCompat *n) { auto *f = n->getData(); + n->logger->info("Stopping node {}", n->getName()); + // Experimental code to implement multi file read + if (f->multi_file_mode) { + for (auto &[filename, samples] : f->file_samples) { + for (auto smp : samples) { + sample_free(smp); + } + } + f->file_samples.clear(); + f->file_read_pos.clear(); + f->uris.clear(); + } + + for (auto smp : f->samples) { + sample_free(smp); + } + f->samples.clear(); f->task.stop(); @@ -311,15 +459,98 @@ int villas::node::file_stop(NodeCompat *n) { return 0; } - int villas::node::file_read(NodeCompat *n, struct Sample *const smps[], unsigned cnt) { auto *f = n->getData(); + // size_t file_pos = 0; + + // Experimental code to implement multi file read + if (f->multi_file_mode && f->read_mode == file::ReadMode::READ_ALL) { + unsigned read_count = 0; + + //Trying sequential read + + for (const auto &filename : f->uris) { + if (read_count >= cnt) + break; + auto &samples = f->file_samples[filename]; + size_t &pos = f->file_read_pos[filename]; + while (pos < samples.size() && read_count < cnt) { + sample_copy(smps[read_count], samples[pos++]); + f->read_pos++; + read_count++; + } + } + + if (f->read_pos == f->total_files_size) { + n->logger->info("Reached end of buffer"); + n->setState(State::STOPPING); + return -1; + } + return (read_count > 0) ? read_count : -1; + } + int ret; uint64_t steps; assert(cnt == 1); + if (f->read_mode == file::ReadMode::READ_ALL) { + unsigned read_count = 0; + while (f->read_pos < f->samples.size() && read_count < cnt) { + + sample_copy(smps[read_count], f->samples[f->read_pos++]); + if (f->epoch_mode == file::EpochMode::ORIGINAL) { + //No waiting + } else if (f->rate) { + steps = f->task.wait(); + if (steps == 0) + throw SystemError("Failed to wait for timer"); + else if (steps != 1) + n->logger->warn("Missed steps: {}", steps - 1); + + smps[read_count]->ts.origin = time_now(); + } else { + smps[read_count]->ts.origin = + time_add(&smps[read_count]->ts.origin, &f->offset); + f->task.setNext(&smps[read_count]->ts.origin); + + steps = f->task.wait(); + if (steps == 0) + throw SystemError("Failed to wait for timer"); + else if (steps != 1) + n->logger->warn("Missed steps: {}", steps - 1); + } + read_count++; + } + + if (f->read_pos == f->samples.size()) { + switch (f->eof_mode) { + case file::EOFBehaviour::REWIND: + n->logger->info("Rewind input file"); + + f->read_pos = 0; + break; + + case file::EOFBehaviour::SUSPEND: + usleep(100000); // 100ms sleep + + // Try to download more data if this is a remote file. + clearerr(f->stream_in); + break; + + case file::EOFBehaviour::STOP: + default: + n->logger->info("Reached end of buffer"); + n->setState(State::STOPPING); + return -1; + } + return -1; + } + + return read_count; + } + retry: ret = f->formatter->scan(f->stream_in, smps, cnt); if (ret <= 0) { @@ -419,6 +650,9 @@ int villas::node::file_init(NodeCompat *n) { // We require a real-time clock here as we can sync against the // timestamps in the file. new (&f->task) Task(CLOCK_REALTIME); + new (&f->file_samples) decltype(f->file_samples)(); // std::map ctor + new (&f->file_read_pos) decltype(f->file_read_pos)(); + new (&f->uris) decltype(f->uris)(); // Default values f->rate = 0; @@ -428,7 +662,9 @@ int villas::node::file_init(NodeCompat *n) { f->buffer_size_in = 0; f->buffer_size_out = 0; f->skip_lines = 0; - + f->read_mode = file::ReadMode::RATE_BASED; + f->read = false; + f->total_files_size = 0; f->formatter = nullptr; return 0; From 255f60595b08cae12991c09edbf68e8ec7788c20 Mon Sep 17 00:00:00 2001 From: "Ritesh.K" Date: Tue, 23 Dec 2025 16:01:52 +0000 Subject: [PATCH 02/13] add implementation for new hook create chronics Signed-off-by: Ritesh.K --- CMakeLists.txt | 2 + include/villas/hooks/create_chronics.hpp | 75 ++++ lib/hooks/CMakeLists.txt | 2 + lib/hooks/create_chronics.cpp | 467 +++++++++++++++++++++++ packaging/deps.sh | 23 +- 5 files changed, 568 insertions(+), 1 deletion(-) create mode 100644 include/villas/hooks/create_chronics.hpp create mode 100644 lib/hooks/create_chronics.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index cb64b0cce..f39e5c1e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -115,6 +115,8 @@ pkg_check_modules(CGRAPH IMPORTED_TARGET libcgraph>=2.30) pkg_check_modules(GVC IMPORTED_TARGET libgvc>=2.30) pkg_check_modules(LIBUSB IMPORTED_TARGET libusb-1.0>=1.0.23) pkg_check_modules(NANOMSG IMPORTED_TARGET nanomsg) +pkg_check_modules(BZIP2 IMPORTED_TARGET bzip2) + if(NOT NANOMSG_FOUND) pkg_check_modules(NANOMSG IMPORTED_TARGET libnanomsg>=1.0.0) endif() diff --git a/include/villas/hooks/create_chronics.hpp b/include/villas/hooks/create_chronics.hpp new file mode 100644 index 000000000..6e980f68f --- /dev/null +++ b/include/villas/hooks/create_chronics.hpp @@ -0,0 +1,75 @@ +/* Create Chronics hook. + * + * Author: Ritesh Karki + * SPDX-FileCopyrightText: 2014-2025 Institute for Automation of Complex Power Systems, RWTH Aachen University + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +namespace villas { +namespace node { + +struct GridMapping { + std::unordered_map load_bus; + std::unordered_map sgen_bus; +}; + +struct ChronicsOptions { + std::filesystem::path loads_dir; + std::filesystem::path sgens_dir; + std::filesystem::path grid_path; + std::filesystem::path output_dir; + int round_decimals =3; + bool compress = true; + bool negate_sgens = true; + float voltage = 20.0; + + static ChronicsOptions from_json(const json_t &cfg); +}; + +class ChronicsHook : public Hook { + + public: + using Hook::Hook; + + void run(); + void flush(); + GridMapping load_grid(); + void discover_files(); + void process_load_files(); + void process_sgen_files(); + void process_samples_from_file(const std::string &filename, const std::vector &samples); + void round_values(); + void write_outputs(); + void discover_files_from_node(struct file *f); + + void prepare() override; + void start() override; + void stop() override; + + + private: + ChronicsOptions options; + GridMapping mapping; + std::vector load_files; + std::vector sgen_files; + std::vector> load_p_columns; + std::vector> load_q_columns; + std::vector> prod_p_columns; + std::vector> prod_q_columns; + std::vector> prod_v_columns; + std::vector column_names; + std::string load_col_names; + std::string sgen_col_names; + bool done; + unsigned sgen_idx; +}; + +} +} diff --git a/lib/hooks/CMakeLists.txt b/lib/hooks/CMakeLists.txt index 83d2f13f3..00bf7e6c7 100644 --- a/lib/hooks/CMakeLists.txt +++ b/lib/hooks/CMakeLists.txt @@ -36,6 +36,7 @@ set(HOOK_SRC skip_first.cpp stats.cpp ts.cpp + create_chronics.cpp ) if(WITH_LUA) @@ -49,3 +50,4 @@ endif() add_library(hooks STATIC ${HOOK_SRC}) target_include_directories(hooks PUBLIC ${INCLUDE_DIRS}) target_link_libraries(hooks PUBLIC ${LIBRARIES}) +target_link_libraries(hooks PUBLIC ${BZIP2_LIBRARIES}) diff --git a/lib/hooks/create_chronics.cpp b/lib/hooks/create_chronics.cpp new file mode 100644 index 000000000..24414c29a --- /dev/null +++ b/lib/hooks/create_chronics.cpp @@ -0,0 +1,467 @@ +/* Create Chronics hook. + * + * Author: Ritesh Karki + * SPDX-FileCopyrightText: 2014-2025 Institute for Automation of Complex Power Systems, RWTH Aachen University + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "villas/node_compat.hpp" +#include "villas/nodes/file.hpp" + +using namespace villas; +using namespace villas::node; + +static int extract_file_number(const std::filesystem::path &file) { + static const std::regex re("(\\d+)"); + std::smatch match; + + std::string name = file.stem().string(); + + if (!std::regex_search(name, match, re)) + throw std::runtime_error("No numeric index in filename: " + file.string()); + + return std::stoi(match.str(1)); +} + +static double round_dec(double value, unsigned decimals) { + double scale = + std::round(value * static_cast(std::pow(10, decimals))) / + std::pow(10, decimals); + return scale; +} + +static void parse_table(const nlohmann::json &table_df, + std::unordered_map &target, + const std::string col) { + const nlohmann::json &load = table_df.at("_object").at(col); + std::string s = table_df["_object"][col]["_object"].get(); + + std::string load_obj_str = load.at("_object").get(); + nlohmann::json load_df = nlohmann::json::parse(load_obj_str); + + const auto &cols = load_df.at("columns"); + auto it = std::find(cols.begin(), cols.end(), "bus"); + if (it == cols.end()) { + throw std::runtime_error("load dataframe does not contain a 'bus' column"); + } + size_t bus_col = static_cast(std::distance(cols.begin(), it)); + + const auto &idxs = load_df.at("index"); + const auto &rows = load_df.at("data"); + + if (idxs.size() != rows.size()) { + throw std::runtime_error("'index' and 'data' length mismatch"); + } + + for (size_t i = 0; i < rows.size(); ++i) { + int idx = idxs.at(i).get(); + const auto &row = rows.at(i); + const auto &bus_cell = row.at(bus_col); + + int bus = bus_cell.get(); + target[idx] = bus; + } +} + +static std::vector +glob_sorted(const std::filesystem::path &dir, const std::string &prefix) { + std::vector files; + if (!std::filesystem::exists(dir)) + throw std::runtime_error("Directory missing: " + dir.string()); + + for (auto &entry : std::filesystem::directory_iterator(dir)) { + if (!entry.is_regular_file()) + continue; + auto name = entry.path().filename().string(); + if (name.rfind(prefix, 0) == 0 && entry.path().extension() == ".csv") + files.push_back(entry.path()); + } + + std::sort(files.begin(), files.end(), [](const auto &a, const auto &b) { + return extract_file_number(a) < extract_file_number(b); + }); + + return files; +} + +//Create Chronics members +ChronicsOptions ChronicsOptions::from_json(const json_t &config) { + + char *json_string = json_dumps(&config, JSON_COMPACT); + nlohmann::json cfg = nlohmann::json::parse(json_string); + + ChronicsOptions opts; + + if (!cfg.contains("loads_dir") || !cfg.contains("sgens_dir") || + !cfg.contains("grid") || !cfg.contains("output")) + throw std::runtime_error( + "chronics: loads_dir, sgens_dir, grid, output are required"); + + opts.loads_dir = cfg.at("loads_dir").get(); + opts.sgens_dir = cfg.at("sgens_dir").get(); + opts.grid_path = cfg.at("grid").get(); + opts.output_dir = cfg.at("output").get(); + + if (cfg.contains("round_decimals")) + opts.round_decimals = cfg.at("round_decimals").get(); + + if (cfg.contains("compress")) + opts.compress = cfg.at("compress").get(); + + if (cfg.contains("voltage")) + opts.voltage = cfg.at("voltage").get(); + + return opts; +} + +GridMapping ChronicsHook::load_grid() { + std::ifstream in(options.grid_path); + + if (!in) + throw std::runtime_error("Cannot open grid file: " + + options.grid_path.string()); + + nlohmann::json grid_json; + in >> grid_json; + + GridMapping mapping; + + parse_table(grid_json, mapping.load_bus, "load"); + parse_table(grid_json, mapping.sgen_bus, "sgen"); + + return mapping; +} + +void ChronicsHook::discover_files() { + + load_files = glob_sorted(options.loads_dir, "Load"); + sgen_files = glob_sorted(options.sgens_dir, "SGen"); + + if (load_files.empty() && sgen_files.empty()) + throw std::runtime_error("chronics_hook: no csv files found"); +} + +void ChronicsHook::discover_files_from_node(struct file *f) { + load_files.clear(); + sgen_files.clear(); + + for (const auto &[filename, samples] : f->file_samples) { + std::filesystem::path path(filename); + std::string name = path.filename().string(); + } + + load_files = glob_sorted(options.loads_dir, "Load"); + sgen_files = glob_sorted(options.sgens_dir, "SGen"); + + std::sort(load_files.begin(), load_files.end(), + [](const auto &a, const auto &b) { + return extract_file_number(a) < extract_file_number(b); + }); + std::sort(sgen_files.begin(), sgen_files.end(), + [](const auto &a, const auto &b) { + return extract_file_number(a) < extract_file_number(b); + }); +} + +void ChronicsHook::process_load_files() { + + auto *file_node = dynamic_cast(node); + if (file_node) { + auto *f = file_node->getData(); + + if (f->multi_file_mode) { + // Process files from file node + for (const auto &file_path : load_files) { + std::string filename = file_path.string(); + auto it = f->file_samples.find(filename); + if (it != f->file_samples.end()) + process_samples_from_file(filename, it->second); + } + } else + std::runtime_error("Multi file mode is not active"); + } +} + +void ChronicsHook::process_sgen_files() { + auto *file_node = dynamic_cast(node); + if (file_node) { + auto *f = file_node->getData(); + + if (f->multi_file_mode) { + // Process files from file node + for (const auto &file_path : sgen_files) { + std::string filename = file_path.string(); + auto it = f->file_samples.find(filename); + if (it != f->file_samples.end()) { + process_samples_from_file(filename, it->second); + } + } + } else + std::runtime_error("Multi file mode is not active"); + } +} + +void ChronicsHook::process_samples_from_file( + const std::string &filename, const std::vector &samples) { + + if (samples.empty()) { + logger->warn("No samples read from file: {}", filename); + return; + } + + auto *first_sample = samples[0]; + if (!first_sample->signals) { + throw std::runtime_error("Sample has no signal list: " + filename); + } + + // auto p_norm_sig = first_sample->signals->getByName("P_norm"); + auto p_norm_sig = first_sample->signals->getByName("signal0"); + auto q_norm_sig = first_sample->signals->getByName("signal1"); + + if (!p_norm_sig || !q_norm_sig) + throw std::runtime_error("File missing P_norm or Q_norm signals: " + + filename); + + size_t p_norm_idx = first_sample->signals->getIndexByName("signal0"); + size_t q_norm_idx = first_sample->signals->getIndexByName("signal1"); + + std::vector p_norm, + q_norm; /* , prod_p_norm, prod_q_norm, prod_v_norm; */ + for (auto *smp : samples) { + if (smp->length > p_norm_idx && smp->length > q_norm_idx) { + p_norm.push_back(smp->data[p_norm_idx].f); + q_norm.push_back(smp->data[q_norm_idx].f); + } + } + + std::filesystem::path path(filename); + int element_idx = extract_file_number(path); + std::string::size_type is_load = + path.filename().string().rfind("Load", 0 == 0); + + int bus_id; + if (is_load != std::string::npos) { + auto it = mapping.load_bus.find(element_idx); + if (it == mapping.load_bus.end()) + throw std::runtime_error("Load index missing in grid mapping: " + + std::to_string(element_idx)); + + bus_id = it->second; + + load_p_columns.push_back(std::move(p_norm)); + load_q_columns.push_back(std::move(q_norm)); + // Set column names for load files + int col_idx = static_cast(load_p_columns.size()); + std::string col_name = + "load_" + std::to_string(bus_id) + "_" + std::to_string(col_idx - 1); + // TODO: Logic to write the column names to the column name vector + if (!load_col_names.empty()) + load_col_names += ';'; + load_col_names += col_name; + + } else { + + auto it = mapping.sgen_bus.find(element_idx); + if (it == mapping.sgen_bus.end()) + throw std::runtime_error("SGen index missing in grid mapping: " + + std::to_string(element_idx)); + + bus_id = it->second; + prod_p_columns.push_back(std::move(p_norm)); + prod_q_columns.push_back(std::move(q_norm)); + + // Create a vector for prod_v + std::vector prod_v; + for (size_t i = 0; i < prod_p_columns[0].size(); i++) { + // prod_v[i] = options.voltage; + prod_v.push_back(options.voltage); + } + prod_v_columns.push_back(prod_v); + + std::string col_name = + "sgen_" + std::to_string(bus_id) + "_" + std::to_string(sgen_idx); + if (!sgen_col_names.empty()) + sgen_col_names += ';'; + sgen_col_names += col_name; + sgen_idx += 1; + } +} + +void ChronicsHook::round_values() { + + for (auto &col : load_p_columns) + for (double &v : col) + v = round_dec(v, options.round_decimals); + + for (auto &col : load_q_columns) + for (double &v : col) + v = round_dec(v, options.round_decimals); +} + +static void write_csv(const std::filesystem::path &path, + const std::string &header, + const std::vector> &columns) { + std::ofstream out(path); + if (!out) + throw std::runtime_error("Cannot open output file: " + path.string()); + + out << header; + out << '\n'; + + size_t rows = columns.empty() ? 0 : columns.front().size(); + for (size_t r = 0; r < rows; r++) { + for (size_t c = 0; c < columns.size(); c++) { + if (c) + out << ';'; + out << columns[c][r]; + } + out << '\n'; + } +} + +static void bz2_write_all(BZFILE *bzf, const std::string &s) { + int err = BZ_OK; + BZ2_bzWrite(&err, bzf, const_cast(s.data()), + static_cast(s.size())); + if (err != BZ_OK) + throw std::runtime_error("BZ2_bzWrite failed"); +} + +static void write_bz2(const std::filesystem::path &path, + const std::string &header, + const std::vector> &columns) { + + FILE *fp = std::fopen(path.string().c_str(), "wb"); + if (!fp) + throw std::runtime_error("Cannot open output file: " + path.string()); + + int err = BZ_OK; + BZFILE *bzf = BZ2_bzWriteOpen(&err, fp, 9, 0, 30); + if (!bzf || err != BZ_OK) { + std::fclose(fp); + throw std::runtime_error("BZ2_bzWriteOpen failed"); + } + + try { + bz2_write_all(bzf, header); + bz2_write_all(bzf, "\n"); + + const size_t rows = columns.empty() ? 0 : columns.front().size(); + for (size_t r = 0; r < rows; ++r) { + std::ostringstream line; + for (size_t c = 0; c < columns.size(); ++c) { + if (c) + line << ';'; + line << columns[c][r]; + } + line << '\n'; + bz2_write_all(bzf, line.str()); + } + unsigned int nbytes_in = 0, nbytes_out = 0; + BZ2_bzWriteClose(&err, bzf, /*abandon*/ 0, &nbytes_in, &nbytes_out); + bzf = nullptr; // safety + std::fclose(fp); + if (err != BZ_OK) + throw std::runtime_error("BZ2_bzWriteClose failed"); + } catch (...) { + int dummy = BZ_OK; + if (bzf) + BZ2_bzWriteClose(&dummy, bzf, /*abandon*/ 1, nullptr, nullptr); + std::fclose(fp); + throw; + } +} + +void ChronicsHook::write_outputs() { + std::filesystem::create_directories(options.output_dir); + if (!options.compress) { + auto load_p_path = options.output_dir / "load_p.csv"; + auto load_q_path = options.output_dir / "load_q.csv"; + auto prod_p_path = options.output_dir / "prod_p.csv"; + auto prod_q_path = options.output_dir / "prod_q.csv"; + auto prod_v_path = options.output_dir / "prod_v.csv"; + write_csv(load_p_path, load_col_names, load_p_columns); + write_csv(load_q_path, load_col_names, load_q_columns); + write_csv(prod_p_path, sgen_col_names, prod_p_columns); + write_csv(prod_q_path, sgen_col_names, prod_q_columns); + write_csv(prod_v_path, sgen_col_names, prod_v_columns); + } else { + auto load_p_bzip2_path = options.output_dir / "load_p.csv.bz2"; + auto load_q_bzip2_path = options.output_dir / "load_q.csv.bz2"; + auto prod_p_bzip2_path = options.output_dir / "prod_p.csv.bz2"; + auto prod_q_bzip2_path = options.output_dir / "prod_q.csv.bz2"; + auto prod_v_bzip2_path = options.output_dir / "prod_v.csv.bz2"; + write_bz2(load_p_bzip2_path, load_col_names, load_p_columns); + write_bz2(load_q_bzip2_path, load_col_names, load_q_columns); + write_bz2(prod_p_bzip2_path, sgen_col_names, prod_q_columns); + write_bz2(prod_q_bzip2_path, sgen_col_names, prod_q_columns); + write_bz2(prod_v_bzip2_path, sgen_col_names, prod_v_columns); + } +} + +void ChronicsHook::run() { + process_load_files(); + process_sgen_files(); + round_values(); + write_outputs(); + done = 1; +} + +void ChronicsHook::flush() {} + +void ChronicsHook::prepare() { + + try { + sgen_idx = 0; + options = ChronicsOptions::from_json(*config); + mapping = load_grid(); + } catch (const std::exception &ex) { + logger->error("chronics prepare failed: {}", ex.what()); + } +} + +void ChronicsHook::start() { + auto *file_node = dynamic_cast(node); + if (file_node) { + auto *f = file_node->getData(); + + if (f->multi_file_mode) { + discover_files_from_node(f); + run(); + } + } +} + +void ChronicsHook::stop() { + if (done) + state = State::STOPPED; + return Hook::stop(); +} + +static char n[] = "create_chronics"; +static char d[] = "This hook creates chronics from csv files"; +static HookPlugin + p; diff --git a/packaging/deps.sh b/packaging/deps.sh index b1b65db09..d3195c184 100644 --- a/packaging/deps.sh +++ b/packaging/deps.sh @@ -475,7 +475,6 @@ if ! pkg-config "fmt >= 6.1.2" && \ --parallel ${PARALLEL} popd fi - # Build & Install spdlog if ! pkg-config "spdlog >= 1.8.2" && \ should_build "spdlog" "for logging" "required"; then @@ -602,6 +601,28 @@ if ! cmake --find-package -DNAME=ghc_filesystem -DCOMPILER_ID=GNU -DLANGUAGE=CXX popd fi +# Build and install nlohmann/josn required for hook create_chronics +if ! find ${PREFIX}/{include,share} -name "*nlohmann*" 2>/dev/null | grep -q . && \ + should_build "nlohman_json" "for the delta-sharing node-type"; then + git clone https://github.com/nlohmann/json.git json + mkdir -p json/build + pushd json/build + cmake .. + make ${MAKE_OPTS} install + popd +fi + +# Build and install Bzip2 required for hook create_chronics +if ! [ -f /usr/local/include/bzlib.h ] && ldconfig -p | grep -q libbz2 && \ + should_build "BZip2" "for create_chronics hook"; then + git clone https://github.com/libarchive/bzip2.git bzip2 + mkdir -p bzip2/build + pushd bzip2/build + cmake .. + make ${MAKE_OPTS} install + popd +fi + popd >/dev/null # Update linker cache From 4cda1c47ada2b1a142ea3bcb0dcb9d3595d57b3e Mon Sep 17 00:00:00 2001 From: "Ritesh.K" Date: Wed, 31 Dec 2025 11:47:04 +0100 Subject: [PATCH 03/13] fix issue with faulty bzip2 check in deps.sh Signed-off-by: Ritesh.K --- .../include/villas/kernel/devices/device.hpp | 2 +- .../villas/kernel/devices/ip_device.hpp | 2 +- .../villas/kernel/devices/linux_driver.hpp | 4 +- .../villas/kernel/devices/platform_device.hpp | 4 +- common/include/villas/utils.hpp | 8 +- .../lib/kernel/devices/device_connection.cpp | 2 +- fpga/include/villas/fpga/ips/i2c.hpp | 5 +- fpga/include/villas/fpga/platform_card.hpp | 2 +- fpga/include/villas/fpga/utils.hpp | 6 +- fpga/lib/core_connection.cpp | 2 +- fpga/src/villas-fpga-pipe.cpp | 2 +- include/villas/hooks/create_chronics.hpp | 74 +++++++-------- include/villas/nodes/iec61850.hpp | 3 +- include/villas/nodes/test_rtt.hpp | 4 +- lib/nodes/can.cpp | 10 +- packaging/deps.sh | 2 +- python/villas/node/villas_pb2.py | 91 +++++++++++++------ tests/unit/queue_signalled.cpp | 16 ++-- 18 files changed, 136 insertions(+), 103 deletions(-) diff --git a/common/include/villas/kernel/devices/device.hpp b/common/include/villas/kernel/devices/device.hpp index d6602da10..a3b1a8656 100644 --- a/common/include/villas/kernel/devices/device.hpp +++ b/common/include/villas/kernel/devices/device.hpp @@ -19,7 +19,7 @@ namespace devices { class Device { public: - virtual ~Device(){}; + virtual ~Device() {}; virtual std::optional> driver() const = 0; virtual std::optional iommu_group() const = 0; diff --git a/common/include/villas/kernel/devices/ip_device.hpp b/common/include/villas/kernel/devices/ip_device.hpp index 4ec192f08..374383e78 100644 --- a/common/include/villas/kernel/devices/ip_device.hpp +++ b/common/include/villas/kernel/devices/ip_device.hpp @@ -25,7 +25,7 @@ class IpDevice : public PlatformDevice { private: IpDevice() = delete; IpDevice(const fs::path valid_path) //! Dont allow unvalidated paths - : PlatformDevice(valid_path){}; + : PlatformDevice(valid_path) {}; public: size_t addr() const; diff --git a/common/include/villas/kernel/devices/linux_driver.hpp b/common/include/villas/kernel/devices/linux_driver.hpp index b1c01cb14..74dadb4f0 100644 --- a/common/include/villas/kernel/devices/linux_driver.hpp +++ b/common/include/villas/kernel/devices/linux_driver.hpp @@ -33,11 +33,11 @@ class LinuxDriver : public Driver { public: LinuxDriver(const fs::path path) : LinuxDriver(path, path / fs::path(BIND_DEFAULT), - path / fs::path(UNBIND_DEFAULT)){}; + path / fs::path(UNBIND_DEFAULT)) {}; LinuxDriver(const fs::path path, const fs::path bind_path, const fs::path unbind_path) - : path(path), bind_path(bind_path), unbind_path(unbind_path){}; + : path(path), bind_path(bind_path), unbind_path(unbind_path) {}; public: void attach(const Device &device) const override; diff --git a/common/include/villas/kernel/devices/platform_device.hpp b/common/include/villas/kernel/devices/platform_device.hpp index 1071a5a84..bad32f2cb 100644 --- a/common/include/villas/kernel/devices/platform_device.hpp +++ b/common/include/villas/kernel/devices/platform_device.hpp @@ -29,12 +29,12 @@ class PlatformDevice : public Device { public: PlatformDevice(const fs::path path) : PlatformDevice(path, fs::path(PROBE_DEFAULT), - path / fs::path(OVERRIDE_DEFAULT)){}; + path / fs::path(OVERRIDE_DEFAULT)) {}; PlatformDevice(const fs::path path, const fs::path probe_path, const fs::path override_path) : m_path(path), m_probe_path(probe_path), - m_override_path(override_path){}; + m_override_path(override_path) {}; // Implement device interface std::optional> driver() const override; diff --git a/common/include/villas/utils.hpp b/common/include/villas/utils.hpp index 1ebb1cf08..0a0ad01e9 100644 --- a/common/include/villas/utils.hpp +++ b/common/include/villas/utils.hpp @@ -52,7 +52,7 @@ #ifdef ALIGN #undef ALIGN #endif -#define ALIGN(x, a) ALIGN_MASK(x, (uintptr_t)(a)-1) +#define ALIGN(x, a) ALIGN_MASK(x, (uintptr_t)(a) - 1) #define ALIGN_MASK(x, m) (((uintptr_t)(x) + (m)) & ~(m)) #define IS_ALIGNED(x, a) (ALIGN(x, a) == (uintptr_t)x) @@ -64,13 +64,13 @@ } while (0) // Round-up integer division -#define CEIL(x, y) (((x) + (y)-1) / (y)) +#define CEIL(x, y) (((x) + (y) - 1) / (y)) // Get nearest up-rounded power of 2 -#define LOG2_CEIL(x) (1 << (villas::utils::log2i((x)-1) + 1)) +#define LOG2_CEIL(x) (1 << (villas::utils::log2i((x) - 1) + 1)) // Check if the number is a power of 2 -#define IS_POW2(x) (((x) != 0) && !((x) & ((x)-1))) +#define IS_POW2(x) (((x) != 0) && !((x) & ((x) - 1))) // Calculate the number of elements in an array. #define ARRAY_LEN(a) (sizeof(a) / sizeof(a)[0]) diff --git a/common/lib/kernel/devices/device_connection.cpp b/common/lib/kernel/devices/device_connection.cpp index b32872f02..28b156e79 100644 --- a/common/lib/kernel/devices/device_connection.cpp +++ b/common/lib/kernel/devices/device_connection.cpp @@ -18,7 +18,7 @@ namespace devices { DeviceConnection::DeviceConnection( std::shared_ptr vfio_device) - : logger(villas::Log::get("DeviceConnection")), vfio_device(vfio_device){}; + : logger(villas::Log::get("DeviceConnection")), vfio_device(vfio_device) {}; DeviceConnection DeviceConnection::from( const villas::kernel::devices::Device &device, diff --git a/fpga/include/villas/fpga/ips/i2c.hpp b/fpga/include/villas/fpga/ips/i2c.hpp index b90f34fac..1854b1cf8 100644 --- a/fpga/include/villas/fpga/ips/i2c.hpp +++ b/fpga/include/villas/fpga/ips/i2c.hpp @@ -20,8 +20,7 @@ namespace fpga { namespace ip { #define I2C_SWTICH_ADDR 0x70 -#define I2C_SWITCH_CHANNEL_MAP \ - { 0x20, 0x80, 0x02, 0x08, 0x10, 0x40, 0x01, 0x04 } +#define I2C_SWITCH_CHANNEL_MAP {0x20, 0x80, 0x02, 0x08, 0x10, 0x40, 0x01, 0x04} #define I2C_IOEXT_ADDR 0x20 #define I2C_IOEXT_REG_DIR 0x03 #define I2C_IOEXT_REG_OUT 0x01 @@ -48,7 +47,7 @@ class I2c : public Node { public: Switch(I2c *i2c, uint8_t address, Logger logger = villas::Log::get("i2c")) : i2c(i2c), address(address), channel(0), readOnce(false), switchLock(), - logger(logger){}; + logger(logger) {}; Switch(const Switch &other) = delete; Switch &operator=(const Switch &other) = delete; void setChannel(uint8_t channel); diff --git a/fpga/include/villas/fpga/platform_card.hpp b/fpga/include/villas/fpga/platform_card.hpp index 22fc4b93b..e122caf0d 100644 --- a/fpga/include/villas/fpga/platform_card.hpp +++ b/fpga/include/villas/fpga/platform_card.hpp @@ -25,7 +25,7 @@ class PlatformCard : public Card { public: PlatformCard(std::shared_ptr vfioContainer); - ~PlatformCard(){}; + ~PlatformCard() {}; std::vector core_connections; diff --git a/fpga/include/villas/fpga/utils.hpp b/fpga/include/villas/fpga/utils.hpp index d8493063c..f7025b990 100644 --- a/fpga/include/villas/fpga/utils.hpp +++ b/fpga/include/villas/fpga/utils.hpp @@ -90,7 +90,7 @@ class BufferedSampleFormatter { BufferedSampleFormatter(const size_t bufSamples, const size_t bufSampleSize) : buf(bufSamples * bufSampleSize + 1), // Leave room for a final `\0' bufSamples(bufSamples), bufSampleSize(bufSampleSize), - currentBufLoc(0){}; + currentBufLoc(0) {}; BufferedSampleFormatter() = delete; BufferedSampleFormatter(const BufferedSampleFormatter &) = delete; virtual char *nextBufPos() { return &buf[(currentBufLoc++) * bufSampleSize]; } @@ -99,7 +99,7 @@ class BufferedSampleFormatter { class BufferedSampleFormatterShort : public BufferedSampleFormatter { public: BufferedSampleFormatterShort(size_t bufSizeInSamples) - : BufferedSampleFormatter(bufSizeInSamples, formatStringSize){}; + : BufferedSampleFormatter(bufSizeInSamples, formatStringSize) {}; virtual void format(float value) override { size_t chars; @@ -120,7 +120,7 @@ class BufferedSampleFormatterLong : public BufferedSampleFormatter { public: BufferedSampleFormatterLong(size_t bufSizeInSamples) : BufferedSampleFormatter(bufSizeInSamples, formatStringSize), - sampleCnt(0){}; + sampleCnt(0) {}; virtual void format(float value) override { if (std::snprintf(nextBufPos(), formatStringSize + 1, formatString, diff --git a/fpga/lib/core_connection.cpp b/fpga/lib/core_connection.cpp index 242009a34..c70d96536 100644 --- a/fpga/lib/core_connection.cpp +++ b/fpga/lib/core_connection.cpp @@ -20,7 +20,7 @@ CoreConnection::CoreConnection( std::shared_ptr ip, villas::kernel::devices::DeviceConnection device_connection) : logger(villas::Log::get("CoreConnection")), ip(ip), - device_connection(device_connection){}; + device_connection(device_connection) {}; CoreConnection CoreConnection::from(std::shared_ptr ip, diff --git a/fpga/src/villas-fpga-pipe.cpp b/fpga/src/villas-fpga-pipe.cpp index 3aba1657a..0b18ba1ae 100644 --- a/fpga/src/villas-fpga-pipe.cpp +++ b/fpga/src/villas-fpga-pipe.cpp @@ -78,7 +78,7 @@ int main(int argc, char *argv[]) { for (auto aurora : aurora_channels) aurora->dump(); - // Configure Crossbar switch + // Configure Crossbar switch #if 1 aurora_channels[3]->connect(aurora_channels[3]->getDefaultMasterPort(), dma->getDefaultSlavePort()); diff --git a/include/villas/hooks/create_chronics.hpp b/include/villas/hooks/create_chronics.hpp index 6e980f68f..19a5eb33e 100644 --- a/include/villas/hooks/create_chronics.hpp +++ b/include/villas/hooks/create_chronics.hpp @@ -5,13 +5,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include -#include #include #include #include + #include +#include +#include + namespace villas { namespace node { @@ -25,7 +27,7 @@ struct ChronicsOptions { std::filesystem::path sgens_dir; std::filesystem::path grid_path; std::filesystem::path output_dir; - int round_decimals =3; + int round_decimals = 3; bool compress = true; bool negate_sgens = true; float voltage = 20.0; @@ -35,41 +37,41 @@ struct ChronicsOptions { class ChronicsHook : public Hook { - public: - using Hook::Hook; - - void run(); - void flush(); - GridMapping load_grid(); - void discover_files(); - void process_load_files(); - void process_sgen_files(); - void process_samples_from_file(const std::string &filename, const std::vector &samples); - void round_values(); - void write_outputs(); - void discover_files_from_node(struct file *f); +public: + using Hook::Hook; - void prepare() override; - void start() override; - void stop() override; + void run(); + void flush(); + GridMapping load_grid(); + void discover_files(); + void process_load_files(); + void process_sgen_files(); + void process_samples_from_file(const std::string &filename, + const std::vector &samples); + void round_values(); + void write_outputs(); + void discover_files_from_node(struct file *f); + void prepare() override; + void start() override; + void stop() override; - private: - ChronicsOptions options; - GridMapping mapping; - std::vector load_files; - std::vector sgen_files; - std::vector> load_p_columns; - std::vector> load_q_columns; - std::vector> prod_p_columns; - std::vector> prod_q_columns; - std::vector> prod_v_columns; - std::vector column_names; - std::string load_col_names; - std::string sgen_col_names; - bool done; - unsigned sgen_idx; +private: + ChronicsOptions options; + GridMapping mapping; + std::vector load_files; + std::vector sgen_files; + std::vector> load_p_columns; + std::vector> load_q_columns; + std::vector> prod_p_columns; + std::vector> prod_q_columns; + std::vector> prod_v_columns; + std::vector column_names; + std::string load_col_names; + std::string sgen_col_names; + bool done; + unsigned sgen_idx; }; -} -} +} // namespace node +} // namespace villas diff --git a/include/villas/nodes/iec61850.hpp b/include/villas/nodes/iec61850.hpp index d8cc7ec19..a63192ae5 100644 --- a/include/villas/nodes/iec61850.hpp +++ b/include/villas/nodes/iec61850.hpp @@ -19,8 +19,7 @@ #include #ifndef CONFIG_GOOSE_DEFAULT_DST_ADDRESS -#define CONFIG_GOOSE_DEFAULT_DST_ADDRESS \ - { 0x01, 0x0c, 0xcd, 0x01, 0x00, 0x01 } +#define CONFIG_GOOSE_DEFAULT_DST_ADDRESS {0x01, 0x0c, 0xcd, 0x01, 0x00, 0x01} #endif namespace villas { diff --git a/include/villas/nodes/test_rtt.hpp b/include/villas/nodes/test_rtt.hpp index 4732dda09..c398b1bdf 100644 --- a/include/villas/nodes/test_rtt.hpp +++ b/include/villas/nodes/test_rtt.hpp @@ -58,7 +58,7 @@ class TestRTT : public Node { : node(n), id(id), rate(rate), warmup(warmup), cooldown(cooldown), 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){}; + missed_warmup(0), filename(filename) {}; int start(); int stop(); @@ -95,7 +95,7 @@ class TestRTT : public Node { : Node(id, name), task(), formatter(nullptr), stream(nullptr), current(), shutdown(false) {} - virtual ~TestRTT(){}; + virtual ~TestRTT() {}; int prepare() override; diff --git a/lib/nodes/can.cpp b/lib/nodes/can.cpp index 784691ccf..489aa3723 100644 --- a/lib/nodes/can.cpp +++ b/lib/nodes/can.cpp @@ -300,20 +300,20 @@ static int can_conv_from_raw(union SignalData *sig, void *from, int size, case SignalType::INTEGER: switch (size) { case 1: - sig->i = (int64_t) * (int8_t *)from; + sig->i = (int64_t)*(int8_t *)from; return 0; case 2: - sig->i = (int64_t) * (int16_t *)from; + sig->i = (int64_t)*(int16_t *)from; return 0; case 3: - sig->i = (int64_t) * (int16_t *)from; - sig->i += ((int64_t) * ((int8_t *)(from) + 2)) << 16; + sig->i = (int64_t)*(int16_t *)from; + sig->i += ((int64_t)*((int8_t *)(from) + 2)) << 16; return 0; case 4: - sig->i = (int64_t) * (int32_t *)from; + sig->i = (int64_t)*(int32_t *)from; return 0; case 8: diff --git a/packaging/deps.sh b/packaging/deps.sh index d3195c184..bec355df7 100644 --- a/packaging/deps.sh +++ b/packaging/deps.sh @@ -613,7 +613,7 @@ if ! find ${PREFIX}/{include,share} -name "*nlohmann*" 2>/dev/null | grep -q . & fi # Build and install Bzip2 required for hook create_chronics -if ! [ -f /usr/local/include/bzlib.h ] && ldconfig -p | grep -q libbz2 && \ +if !{ [ -f /usr/local/include/bzlib.h ] && ldconfig -p} | grep -q libbz2 && \ should_build "BZip2" "for create_chronics hook"; then git clone https://github.com/libarchive/bzip2.git bzip2 mkdir -p bzip2/build diff --git a/python/villas/node/villas_pb2.py b/python/villas/node/villas_pb2.py index b764662c0..b101463f7 100644 --- a/python/villas/node/villas_pb2.py +++ b/python/villas/node/villas_pb2.py @@ -1,44 +1,77 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! -# NO CHECKED-IN PROTOBUF GENCODE # source: villas.proto -# Protobuf Python Version: 6.31.1 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import runtime_version as _runtime_version +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder -_runtime_version.ValidateProtobufRuntimeVersion( - _runtime_version.Domain.PUBLIC, - 6, - 31, - 1, - '', - 'villas.proto' -) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() + + DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cvillas.proto\x12\x0bvillas.node\"/\n\x07Message\x12$\n\x07samples\x18\x01 \x03(\x0b\x32\x13.villas.node.Sample\"\xfe\x01\n\x06Sample\x12,\n\x04type\x18\x01 \x02(\x0e\x32\x18.villas.node.Sample.Type:\x04\x44\x41TA\x12\x10\n\x08sequence\x18\x02 \x01(\x04\x12)\n\tts_origin\x18\x03 \x01(\x0b\x32\x16.villas.node.Timestamp\x12+\n\x0bts_received\x18\x04 \x01(\x0b\x32\x16.villas.node.Timestamp\x12\x11\n\tnew_frame\x18\x05 \x01(\x08\x12\"\n\x06values\x18\x64 \x03(\x0b\x32\x12.villas.node.Value\"%\n\x04Type\x12\x08\n\x04\x44\x41TA\x10\x01\x12\t\n\x05START\x10\x02\x12\x08\n\x04STOP\x10\x03\"&\n\tTimestamp\x12\x0b\n\x03sec\x18\x01 \x02(\r\x12\x0c\n\x04nsec\x18\x02 \x02(\r\"Z\n\x05Value\x12\x0b\n\x01\x66\x18\x01 \x01(\x01H\x00\x12\x0b\n\x01i\x18\x02 \x01(\x03H\x00\x12\x0b\n\x01\x62\x18\x03 \x01(\x08H\x00\x12!\n\x01z\x18\x04 \x01(\x0b\x32\x14.villas.node.ComplexH\x00\x42\x07\n\x05value\"%\n\x07\x43omplex\x12\x0c\n\x04real\x18\x01 \x02(\x02\x12\x0c\n\x04imag\x18\x02 \x02(\x02') -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'villas_pb2', _globals) -if not _descriptor._USE_C_DESCRIPTORS: - DESCRIPTOR._loaded_options = None - _globals['_MESSAGE']._serialized_start=29 - _globals['_MESSAGE']._serialized_end=76 - _globals['_SAMPLE']._serialized_start=79 - _globals['_SAMPLE']._serialized_end=333 - _globals['_SAMPLE_TYPE']._serialized_start=296 - _globals['_SAMPLE_TYPE']._serialized_end=333 - _globals['_TIMESTAMP']._serialized_start=335 - _globals['_TIMESTAMP']._serialized_end=373 - _globals['_VALUE']._serialized_start=375 - _globals['_VALUE']._serialized_end=465 - _globals['_COMPLEX']._serialized_start=467 - _globals['_COMPLEX']._serialized_end=504 + + +_MESSAGE = DESCRIPTOR.message_types_by_name['Message'] +_SAMPLE = DESCRIPTOR.message_types_by_name['Sample'] +_TIMESTAMP = DESCRIPTOR.message_types_by_name['Timestamp'] +_VALUE = DESCRIPTOR.message_types_by_name['Value'] +_COMPLEX = DESCRIPTOR.message_types_by_name['Complex'] +_SAMPLE_TYPE = _SAMPLE.enum_types_by_name['Type'] +Message = _reflection.GeneratedProtocolMessageType('Message', (_message.Message,), { + 'DESCRIPTOR' : _MESSAGE, + '__module__' : 'villas_pb2' + # @@protoc_insertion_point(class_scope:villas.node.Message) + }) +_sym_db.RegisterMessage(Message) + +Sample = _reflection.GeneratedProtocolMessageType('Sample', (_message.Message,), { + 'DESCRIPTOR' : _SAMPLE, + '__module__' : 'villas_pb2' + # @@protoc_insertion_point(class_scope:villas.node.Sample) + }) +_sym_db.RegisterMessage(Sample) + +Timestamp = _reflection.GeneratedProtocolMessageType('Timestamp', (_message.Message,), { + 'DESCRIPTOR' : _TIMESTAMP, + '__module__' : 'villas_pb2' + # @@protoc_insertion_point(class_scope:villas.node.Timestamp) + }) +_sym_db.RegisterMessage(Timestamp) + +Value = _reflection.GeneratedProtocolMessageType('Value', (_message.Message,), { + 'DESCRIPTOR' : _VALUE, + '__module__' : 'villas_pb2' + # @@protoc_insertion_point(class_scope:villas.node.Value) + }) +_sym_db.RegisterMessage(Value) + +Complex = _reflection.GeneratedProtocolMessageType('Complex', (_message.Message,), { + 'DESCRIPTOR' : _COMPLEX, + '__module__' : 'villas_pb2' + # @@protoc_insertion_point(class_scope:villas.node.Complex) + }) +_sym_db.RegisterMessage(Complex) + +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _MESSAGE._serialized_start=29 + _MESSAGE._serialized_end=76 + _SAMPLE._serialized_start=79 + _SAMPLE._serialized_end=333 + _SAMPLE_TYPE._serialized_start=296 + _SAMPLE_TYPE._serialized_end=333 + _TIMESTAMP._serialized_start=335 + _TIMESTAMP._serialized_end=373 + _VALUE._serialized_start=375 + _VALUE._serialized_end=465 + _COMPLEX._serialized_start=467 + _COMPLEX._serialized_end=504 # @@protoc_insertion_point(module_scope) diff --git a/tests/unit/queue_signalled.cpp b/tests/unit/queue_signalled.cpp index 4d16a8983..8b9a59980 100644 --- a/tests/unit/queue_signalled.cpp +++ b/tests/unit/queue_signalled.cpp @@ -92,15 +92,15 @@ void *polled_consumer(void *ctx) { ParameterizedTestParameters(queue_signalled, simple) { static struct param params[] = { - {QueueSignalledMode::AUTO, 0, false}, - {QueueSignalledMode::PTHREAD, 0, false}, - {QueueSignalledMode::PTHREAD, 0, false}, - {QueueSignalledMode::PTHREAD, (int)QueueSignalledFlags::PROCESS_SHARED, - false}, - {QueueSignalledMode::POLLING, 0, false}, + {QueueSignalledMode::AUTO, 0, false}, + {QueueSignalledMode::PTHREAD, 0, false}, + {QueueSignalledMode::PTHREAD, 0, false}, + {QueueSignalledMode::PTHREAD, (int)QueueSignalledFlags::PROCESS_SHARED, + false}, + {QueueSignalledMode::POLLING, 0, false}, #if defined(__linux__) && defined(HAS_EVENTFD) - {QueueSignalledMode::EVENTFD, 0, false}, - {QueueSignalledMode::EVENTFD, 0, true} + {QueueSignalledMode::EVENTFD, 0, false}, + {QueueSignalledMode::EVENTFD, 0, true} #endif }; From a07f0593a497e12a06e2d1ba2dc721a0d8b4ba75 Mon Sep 17 00:00:00 2001 From: "Ritesh.K" Date: Wed, 31 Dec 2025 15:31:12 +0100 Subject: [PATCH 04/13] fix issue with faulty bzip2 check in deps.sh Signed-off-by: Ritesh.K --- packaging/deps.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/deps.sh b/packaging/deps.sh index bec355df7..0c85774bf 100644 --- a/packaging/deps.sh +++ b/packaging/deps.sh @@ -613,7 +613,7 @@ if ! find ${PREFIX}/{include,share} -name "*nlohmann*" 2>/dev/null | grep -q . & fi # Build and install Bzip2 required for hook create_chronics -if !{ [ -f /usr/local/include/bzlib.h ] && ldconfig -p} | grep -q libbz2 && \ +if ! { [ -f /usr/local/include/bzlib.h ] && ldconfig -p | grep -q libbz2; } && \ should_build "BZip2" "for create_chronics hook"; then git clone https://github.com/libarchive/bzip2.git bzip2 mkdir -p bzip2/build From 32cb936e38db34876049873c03b2ce20cabcea06 Mon Sep 17 00:00:00 2001 From: "Ritesh.K" Date: Sun, 4 Jan 2026 21:56:33 +0100 Subject: [PATCH 05/13] fix (hook-create-chronics) include integration test for hook create-chronics, fix issue raised during cppcheck Signed-off-by: Ritesh.K --- lib/hooks/create_chronics.cpp | 2 +- tests/integration/hook-create-chronics.sh | 130 ++++++++++++++++++++++ 2 files changed, 131 insertions(+), 1 deletion(-) create mode 100755 tests/integration/hook-create-chronics.sh diff --git a/lib/hooks/create_chronics.cpp b/lib/hooks/create_chronics.cpp index 24414c29a..fa7880a32 100644 --- a/lib/hooks/create_chronics.cpp +++ b/lib/hooks/create_chronics.cpp @@ -53,7 +53,7 @@ static double round_dec(double value, unsigned decimals) { static void parse_table(const nlohmann::json &table_df, std::unordered_map &target, - const std::string col) { + const std::string &col) { const nlohmann::json &load = table_df.at("_object").at(col); std::string s = table_df["_object"][col]["_object"].get(); diff --git a/tests/integration/hook-create-chronics.sh b/tests/integration/hook-create-chronics.sh new file mode 100755 index 000000000..057228683 --- /dev/null +++ b/tests/integration/hook-create-chronics.sh @@ -0,0 +1,130 @@ +#!/usr/bin/env bash +# +# Integration test for hook create-chronics. +# +# Author: Ritesh Karki +# SPDX-FileCopyrightText: 2014-2026 Institute for Automation of Complex Power Systems, RWTH Aachen University +# SPDX-License-Identifier: Apache-2.0 + +echo "Test not ready" +exit 99 + + +DIR=$(mktemp -d) +pushd ${DIR} + +function finish { + popd + rm -rf ${DIR} +} + +trap finish EXIT + +cat > config.conf << EOF +nodes = { + + file_node = { + type = "file" + format = { + type = "csv" + separator = "," + delimiter = "\n" + skip_first_line = false + header = true + } + uris = ("./Load1.csv", "./SGen.csv") + uri = "test_uri" + in = { + read_mode = "all" + } + + hooks = ( + { + type = "create_chronics" + loads_dir = "./" + sgens_dir = "./" + grid = "./grid_file.json" + output = "./test_output" + round_decimals = 3 + compress = true + } + ) + }, + dummy_node = { + type = "file" + format = "csv" + uri = "dummy" + out = { + + } + } +} + +paths = ( + { + in = "file_node", + out = "dummy_node" + } +) +EOF + +cat > Load1.csv << EOF +secs,nsecs,offset,sequence,P_norm,Q_norm +0,83333333,0.0,0,0.016932429412331206,0.009857843460539759 +0,166666666,0.0,1,0.016932429412331206,0.009282331392780813 +0,250000000,0.0,2,0.016932429412331206,0.008983608863673546 +0,333333333,0.0,3,0.016932429412331206,0.008961675873217962 +0,416666666,0.0,4,0.016932429412331206,0.0092165235308626 +0,500000000,0.0,5,0.016932429412331206,0.009670519541245412 +0,583333333,0.0,6,0.016932429412331206,0.009857843460539759 +0,666666666,0.0,7,0.01693242416294213,0.009700854102832137 +0,750000000,0.0,8,0.01693241891355305,0.009267075206472835 +0,833333333,0.0,9,0.016932429412331206,0.008961675873217962 +0,916666666,0.0,10,0.01693245040988752,0.008852188731969274 +1,0,0.0,11,0.016932476656832916,0.008909808395991422 +1,83333333,0.0,12,0.016932429412331206,0.008961675873217962 +1,166666666,0.0,13,0.016932303426993315,0.008978976886362093 +1,250000000,0.0,14,0.01693216169348819,0.008967472512770878 +1,333333333,0.0,15,0.016932429412331206,0.008961675873217962 +1,416666666,0.0,16,0.016933174825580388,0.008967330263947493 +1,500000000,0.0,17,0.016933993730276667,0.008978745732024092 +1,583333333,0.0,18,0.016932429412331206,0.008961675873217962 +1,666666666,0.0,19,0.016928077668784944,0.008910430734593728 +1,750000000,0.0,20,0.016923321722279623,0.008853477861931195 +1,833333333,0.0,21,0.016932429412331206,0.008961675873217962 +1,916666666,0.0,22,0.016957783961581444,0.00926349231423384 +2,0,0.0,23,0.01698551648408434,0.009693368258501533 +EOF + +cat > SGen1.csv << EOF +secs,nsecs,offset,sequence,P_norm,Q_norm +0,83333333,0.0,0,0.0,0.0 +0,166666666,0.0,1,0.0,0.0 +0,250000000,0.0,2,0.0,0.0 +0,333333333,0.0,3,0.0,0.0 +0,416666666,0.0,4,0.0,0.0 +0,500000000,0.0,5,0.0,0.0 +0,583333333,0.0,6,0.0,0.0 +0,666666666,0.0,7,0.0,0.0 +0,750000000,0.0,8,0.0,0.0 +0,833333333,0.0,9,0.0,0.0 +0,916666666,0.0,10,0.0,0.0 +1,0,0.0,11,0.0,0.0 +1,83333333,0.0,12,0.0,0.0 +1,166666666,0.0,13,0.0,0.0 +1,250000000,0.0,14,0.0,0.0 +1,333333333,0.0,15,0.0,0.0 +1,416666666,0.0,16,0.0,0.0 +1,500000000,0.0,17,0.0,0.0 +1,583333333,0.0,18,0.0,0.0 +1,666666666,0.0,19,0.0,0.0 +1,750000000,0.0,20,0.0,0.0 +1,833333333,0.0,21,0.0,0.0 +1,916666666,0.0,22,0.0,0.0 +EOF + +# TODO: Add an example grid net file + +villas node confif.conf + +# TODO: Compare with pre-generated csv files From d0a9c1cd2f0c9e155e7df2f24b0551206930a791 Mon Sep 17 00:00:00 2001 From: "Ritesh.K" Date: Sun, 4 Jan 2026 22:36:36 +0100 Subject: [PATCH 06/13] fix (hook-create-chronics): Fix filename mismatch to avoid integration check fail on CI Signed-off-by: Ritesh.K --- .../{hook-create-chronics.sh => hook-create_chronics.sh} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/integration/{hook-create-chronics.sh => hook-create_chronics.sh} (100%) diff --git a/tests/integration/hook-create-chronics.sh b/tests/integration/hook-create_chronics.sh similarity index 100% rename from tests/integration/hook-create-chronics.sh rename to tests/integration/hook-create_chronics.sh From 9db5ec18dc26ad4c0317c32a8469a03623dc3f2d Mon Sep 17 00:00:00 2001 From: "Ritesh.K" Date: Mon, 12 Jan 2026 00:04:00 +0100 Subject: [PATCH 07/13] fix (hook-create-chronics: fix build issue with nix due to missing bzip2 Signed-off-by: Ritesh.K --- packaging/nix/villas.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packaging/nix/villas.nix b/packaging/nix/villas.nix index 246150a67..a440c0879 100644 --- a/packaging/nix/villas.nix +++ b/packaging/nix/villas.nix @@ -48,6 +48,7 @@ pkg-config, stdenv, system, + bzip2, # Optional dependencies boxfort, comedilib, @@ -151,6 +152,7 @@ stdenv.mkDerivation { curl spdlog bash + bzip2 ] ++ lib.optionals withExtraTesting [ boxfort From a0143ab7bb3bf74b4b7d9bd41a834b6fa8d145ff Mon Sep 17 00:00:00 2001 From: "Ritesh.K" Date: Mon, 12 Jan 2026 00:42:16 +0100 Subject: [PATCH 08/13] fix (hook-create-chronics: fix build issue with nix due to missing nlohmann/json pkg Signed-off-by: Ritesh.K --- packaging/nix/villas.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packaging/nix/villas.nix b/packaging/nix/villas.nix index a440c0879..6e1483c11 100644 --- a/packaging/nix/villas.nix +++ b/packaging/nix/villas.nix @@ -48,6 +48,7 @@ pkg-config, stdenv, system, + nlohmann_json, bzip2, # Optional dependencies boxfort, @@ -152,6 +153,7 @@ stdenv.mkDerivation { curl spdlog bash + nlohmann_json bzip2 ] ++ lib.optionals withExtraTesting [ From 0e6bebeb58ea1b2067688a3a0f1b5f5993606029 Mon Sep 17 00:00:00 2001 From: "Ritesh.K" Date: Mon, 12 Jan 2026 13:37:12 +0100 Subject: [PATCH 09/13] fix (hook-create-chronics): Fix cpp file format for CI pre-commit tests Signed-off-by: Ritesh.K --- common/include/villas/kernel/devices/device.hpp | 2 +- .../include/villas/kernel/devices/ip_device.hpp | 2 +- .../villas/kernel/devices/linux_driver.hpp | 4 ++-- .../villas/kernel/devices/platform_device.hpp | 4 ++-- common/include/villas/utils.hpp | 8 ++++---- common/lib/kernel/devices/device_connection.cpp | 2 +- fpga/include/villas/fpga/ips/i2c.hpp | 5 +++-- fpga/include/villas/fpga/platform_card.hpp | 2 +- fpga/include/villas/fpga/utils.hpp | 6 +++--- fpga/lib/core_connection.cpp | 2 +- fpga/src/villas-fpga-pipe.cpp | 2 +- include/villas/nodes/iec61850.hpp | 3 ++- include/villas/nodes/test_rtt.hpp | 4 ++-- lib/nodes/can.cpp | 10 +++++----- tests/unit/queue_signalled.cpp | 16 ++++++++-------- 15 files changed, 37 insertions(+), 35 deletions(-) diff --git a/common/include/villas/kernel/devices/device.hpp b/common/include/villas/kernel/devices/device.hpp index a3b1a8656..d6602da10 100644 --- a/common/include/villas/kernel/devices/device.hpp +++ b/common/include/villas/kernel/devices/device.hpp @@ -19,7 +19,7 @@ namespace devices { class Device { public: - virtual ~Device() {}; + virtual ~Device(){}; virtual std::optional> driver() const = 0; virtual std::optional iommu_group() const = 0; diff --git a/common/include/villas/kernel/devices/ip_device.hpp b/common/include/villas/kernel/devices/ip_device.hpp index 374383e78..4ec192f08 100644 --- a/common/include/villas/kernel/devices/ip_device.hpp +++ b/common/include/villas/kernel/devices/ip_device.hpp @@ -25,7 +25,7 @@ class IpDevice : public PlatformDevice { private: IpDevice() = delete; IpDevice(const fs::path valid_path) //! Dont allow unvalidated paths - : PlatformDevice(valid_path) {}; + : PlatformDevice(valid_path){}; public: size_t addr() const; diff --git a/common/include/villas/kernel/devices/linux_driver.hpp b/common/include/villas/kernel/devices/linux_driver.hpp index 74dadb4f0..b1c01cb14 100644 --- a/common/include/villas/kernel/devices/linux_driver.hpp +++ b/common/include/villas/kernel/devices/linux_driver.hpp @@ -33,11 +33,11 @@ class LinuxDriver : public Driver { public: LinuxDriver(const fs::path path) : LinuxDriver(path, path / fs::path(BIND_DEFAULT), - path / fs::path(UNBIND_DEFAULT)) {}; + path / fs::path(UNBIND_DEFAULT)){}; LinuxDriver(const fs::path path, const fs::path bind_path, const fs::path unbind_path) - : path(path), bind_path(bind_path), unbind_path(unbind_path) {}; + : path(path), bind_path(bind_path), unbind_path(unbind_path){}; public: void attach(const Device &device) const override; diff --git a/common/include/villas/kernel/devices/platform_device.hpp b/common/include/villas/kernel/devices/platform_device.hpp index bad32f2cb..1071a5a84 100644 --- a/common/include/villas/kernel/devices/platform_device.hpp +++ b/common/include/villas/kernel/devices/platform_device.hpp @@ -29,12 +29,12 @@ class PlatformDevice : public Device { public: PlatformDevice(const fs::path path) : PlatformDevice(path, fs::path(PROBE_DEFAULT), - path / fs::path(OVERRIDE_DEFAULT)) {}; + path / fs::path(OVERRIDE_DEFAULT)){}; PlatformDevice(const fs::path path, const fs::path probe_path, const fs::path override_path) : m_path(path), m_probe_path(probe_path), - m_override_path(override_path) {}; + m_override_path(override_path){}; // Implement device interface std::optional> driver() const override; diff --git a/common/include/villas/utils.hpp b/common/include/villas/utils.hpp index 0a0ad01e9..1ebb1cf08 100644 --- a/common/include/villas/utils.hpp +++ b/common/include/villas/utils.hpp @@ -52,7 +52,7 @@ #ifdef ALIGN #undef ALIGN #endif -#define ALIGN(x, a) ALIGN_MASK(x, (uintptr_t)(a) - 1) +#define ALIGN(x, a) ALIGN_MASK(x, (uintptr_t)(a)-1) #define ALIGN_MASK(x, m) (((uintptr_t)(x) + (m)) & ~(m)) #define IS_ALIGNED(x, a) (ALIGN(x, a) == (uintptr_t)x) @@ -64,13 +64,13 @@ } while (0) // Round-up integer division -#define CEIL(x, y) (((x) + (y) - 1) / (y)) +#define CEIL(x, y) (((x) + (y)-1) / (y)) // Get nearest up-rounded power of 2 -#define LOG2_CEIL(x) (1 << (villas::utils::log2i((x) - 1) + 1)) +#define LOG2_CEIL(x) (1 << (villas::utils::log2i((x)-1) + 1)) // Check if the number is a power of 2 -#define IS_POW2(x) (((x) != 0) && !((x) & ((x) - 1))) +#define IS_POW2(x) (((x) != 0) && !((x) & ((x)-1))) // Calculate the number of elements in an array. #define ARRAY_LEN(a) (sizeof(a) / sizeof(a)[0]) diff --git a/common/lib/kernel/devices/device_connection.cpp b/common/lib/kernel/devices/device_connection.cpp index 28b156e79..b32872f02 100644 --- a/common/lib/kernel/devices/device_connection.cpp +++ b/common/lib/kernel/devices/device_connection.cpp @@ -18,7 +18,7 @@ namespace devices { DeviceConnection::DeviceConnection( std::shared_ptr vfio_device) - : logger(villas::Log::get("DeviceConnection")), vfio_device(vfio_device) {}; + : logger(villas::Log::get("DeviceConnection")), vfio_device(vfio_device){}; DeviceConnection DeviceConnection::from( const villas::kernel::devices::Device &device, diff --git a/fpga/include/villas/fpga/ips/i2c.hpp b/fpga/include/villas/fpga/ips/i2c.hpp index 1854b1cf8..b90f34fac 100644 --- a/fpga/include/villas/fpga/ips/i2c.hpp +++ b/fpga/include/villas/fpga/ips/i2c.hpp @@ -20,7 +20,8 @@ namespace fpga { namespace ip { #define I2C_SWTICH_ADDR 0x70 -#define I2C_SWITCH_CHANNEL_MAP {0x20, 0x80, 0x02, 0x08, 0x10, 0x40, 0x01, 0x04} +#define I2C_SWITCH_CHANNEL_MAP \ + { 0x20, 0x80, 0x02, 0x08, 0x10, 0x40, 0x01, 0x04 } #define I2C_IOEXT_ADDR 0x20 #define I2C_IOEXT_REG_DIR 0x03 #define I2C_IOEXT_REG_OUT 0x01 @@ -47,7 +48,7 @@ class I2c : public Node { public: Switch(I2c *i2c, uint8_t address, Logger logger = villas::Log::get("i2c")) : i2c(i2c), address(address), channel(0), readOnce(false), switchLock(), - logger(logger) {}; + logger(logger){}; Switch(const Switch &other) = delete; Switch &operator=(const Switch &other) = delete; void setChannel(uint8_t channel); diff --git a/fpga/include/villas/fpga/platform_card.hpp b/fpga/include/villas/fpga/platform_card.hpp index e122caf0d..22fc4b93b 100644 --- a/fpga/include/villas/fpga/platform_card.hpp +++ b/fpga/include/villas/fpga/platform_card.hpp @@ -25,7 +25,7 @@ class PlatformCard : public Card { public: PlatformCard(std::shared_ptr vfioContainer); - ~PlatformCard() {}; + ~PlatformCard(){}; std::vector core_connections; diff --git a/fpga/include/villas/fpga/utils.hpp b/fpga/include/villas/fpga/utils.hpp index f7025b990..d8493063c 100644 --- a/fpga/include/villas/fpga/utils.hpp +++ b/fpga/include/villas/fpga/utils.hpp @@ -90,7 +90,7 @@ class BufferedSampleFormatter { BufferedSampleFormatter(const size_t bufSamples, const size_t bufSampleSize) : buf(bufSamples * bufSampleSize + 1), // Leave room for a final `\0' bufSamples(bufSamples), bufSampleSize(bufSampleSize), - currentBufLoc(0) {}; + currentBufLoc(0){}; BufferedSampleFormatter() = delete; BufferedSampleFormatter(const BufferedSampleFormatter &) = delete; virtual char *nextBufPos() { return &buf[(currentBufLoc++) * bufSampleSize]; } @@ -99,7 +99,7 @@ class BufferedSampleFormatter { class BufferedSampleFormatterShort : public BufferedSampleFormatter { public: BufferedSampleFormatterShort(size_t bufSizeInSamples) - : BufferedSampleFormatter(bufSizeInSamples, formatStringSize) {}; + : BufferedSampleFormatter(bufSizeInSamples, formatStringSize){}; virtual void format(float value) override { size_t chars; @@ -120,7 +120,7 @@ class BufferedSampleFormatterLong : public BufferedSampleFormatter { public: BufferedSampleFormatterLong(size_t bufSizeInSamples) : BufferedSampleFormatter(bufSizeInSamples, formatStringSize), - sampleCnt(0) {}; + sampleCnt(0){}; virtual void format(float value) override { if (std::snprintf(nextBufPos(), formatStringSize + 1, formatString, diff --git a/fpga/lib/core_connection.cpp b/fpga/lib/core_connection.cpp index c70d96536..242009a34 100644 --- a/fpga/lib/core_connection.cpp +++ b/fpga/lib/core_connection.cpp @@ -20,7 +20,7 @@ CoreConnection::CoreConnection( std::shared_ptr ip, villas::kernel::devices::DeviceConnection device_connection) : logger(villas::Log::get("CoreConnection")), ip(ip), - device_connection(device_connection) {}; + device_connection(device_connection){}; CoreConnection CoreConnection::from(std::shared_ptr ip, diff --git a/fpga/src/villas-fpga-pipe.cpp b/fpga/src/villas-fpga-pipe.cpp index 0b18ba1ae..3aba1657a 100644 --- a/fpga/src/villas-fpga-pipe.cpp +++ b/fpga/src/villas-fpga-pipe.cpp @@ -78,7 +78,7 @@ int main(int argc, char *argv[]) { for (auto aurora : aurora_channels) aurora->dump(); - // Configure Crossbar switch + // Configure Crossbar switch #if 1 aurora_channels[3]->connect(aurora_channels[3]->getDefaultMasterPort(), dma->getDefaultSlavePort()); diff --git a/include/villas/nodes/iec61850.hpp b/include/villas/nodes/iec61850.hpp index a63192ae5..d8cc7ec19 100644 --- a/include/villas/nodes/iec61850.hpp +++ b/include/villas/nodes/iec61850.hpp @@ -19,7 +19,8 @@ #include #ifndef CONFIG_GOOSE_DEFAULT_DST_ADDRESS -#define CONFIG_GOOSE_DEFAULT_DST_ADDRESS {0x01, 0x0c, 0xcd, 0x01, 0x00, 0x01} +#define CONFIG_GOOSE_DEFAULT_DST_ADDRESS \ + { 0x01, 0x0c, 0xcd, 0x01, 0x00, 0x01 } #endif namespace villas { diff --git a/include/villas/nodes/test_rtt.hpp b/include/villas/nodes/test_rtt.hpp index c398b1bdf..4732dda09 100644 --- a/include/villas/nodes/test_rtt.hpp +++ b/include/villas/nodes/test_rtt.hpp @@ -58,7 +58,7 @@ class TestRTT : public Node { : node(n), id(id), rate(rate), warmup(warmup), cooldown(cooldown), 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) {}; + missed_warmup(0), filename(filename){}; int start(); int stop(); @@ -95,7 +95,7 @@ class TestRTT : public Node { : Node(id, name), task(), formatter(nullptr), stream(nullptr), current(), shutdown(false) {} - virtual ~TestRTT() {}; + virtual ~TestRTT(){}; int prepare() override; diff --git a/lib/nodes/can.cpp b/lib/nodes/can.cpp index 489aa3723..784691ccf 100644 --- a/lib/nodes/can.cpp +++ b/lib/nodes/can.cpp @@ -300,20 +300,20 @@ static int can_conv_from_raw(union SignalData *sig, void *from, int size, case SignalType::INTEGER: switch (size) { case 1: - sig->i = (int64_t)*(int8_t *)from; + sig->i = (int64_t) * (int8_t *)from; return 0; case 2: - sig->i = (int64_t)*(int16_t *)from; + sig->i = (int64_t) * (int16_t *)from; return 0; case 3: - sig->i = (int64_t)*(int16_t *)from; - sig->i += ((int64_t)*((int8_t *)(from) + 2)) << 16; + sig->i = (int64_t) * (int16_t *)from; + sig->i += ((int64_t) * ((int8_t *)(from) + 2)) << 16; return 0; case 4: - sig->i = (int64_t)*(int32_t *)from; + sig->i = (int64_t) * (int32_t *)from; return 0; case 8: diff --git a/tests/unit/queue_signalled.cpp b/tests/unit/queue_signalled.cpp index 8b9a59980..4d16a8983 100644 --- a/tests/unit/queue_signalled.cpp +++ b/tests/unit/queue_signalled.cpp @@ -92,15 +92,15 @@ void *polled_consumer(void *ctx) { ParameterizedTestParameters(queue_signalled, simple) { static struct param params[] = { - {QueueSignalledMode::AUTO, 0, false}, - {QueueSignalledMode::PTHREAD, 0, false}, - {QueueSignalledMode::PTHREAD, 0, false}, - {QueueSignalledMode::PTHREAD, (int)QueueSignalledFlags::PROCESS_SHARED, - false}, - {QueueSignalledMode::POLLING, 0, false}, + {QueueSignalledMode::AUTO, 0, false}, + {QueueSignalledMode::PTHREAD, 0, false}, + {QueueSignalledMode::PTHREAD, 0, false}, + {QueueSignalledMode::PTHREAD, (int)QueueSignalledFlags::PROCESS_SHARED, + false}, + {QueueSignalledMode::POLLING, 0, false}, #if defined(__linux__) && defined(HAS_EVENTFD) - {QueueSignalledMode::EVENTFD, 0, false}, - {QueueSignalledMode::EVENTFD, 0, true} + {QueueSignalledMode::EVENTFD, 0, false}, + {QueueSignalledMode::EVENTFD, 0, true} #endif }; From e1e09bf47572e6f6b590ec39885bdab5426118bf Mon Sep 17 00:00:00 2001 From: "Ritesh.K" Date: Mon, 12 Jan 2026 14:17:38 +0100 Subject: [PATCH 10/13] fix (hook-create-chronics): Add configuration example for create_chronics hook Signed-off-by: Ritesh.K --- etc/examples/hooks/create_chronics.conf | 45 +++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 etc/examples/hooks/create_chronics.conf diff --git a/etc/examples/hooks/create_chronics.conf b/etc/examples/hooks/create_chronics.conf new file mode 100644 index 000000000..c72d3e455 --- /dev/null +++ b/etc/examples/hooks/create_chronics.conf @@ -0,0 +1,45 @@ +nodes = { + + file_node = { + type = "file" + format = { + type = "csv" + separator = "," + delimiter = "\n" + skip_first_line = false + header = true + } + uris = ("./Load1.csv", "./SGen.csv") + uri = "test_uri" + in = { + read_mode = "all" + } + + hooks = ( + { + type = "create_chronics" + loads_dir = "./" + sgens_dir = "./" + grid = "./grid_file.json" + output = "./test_output" + round_decimals = 3 + compress = true + } + ) + }, + dummy_node = { + type = "file" + format = "csv" + uri = "dummy" + out = { + + } + } +} + +paths = ( + { + in = "file_node", + out = "dummy_node" + } +) From ec1044770313e95e6491f3b4d24ee8f7af541151 Mon Sep 17 00:00:00 2001 From: "Ritesh.K" Date: Mon, 12 Jan 2026 16:27:39 +0100 Subject: [PATCH 11/13] fix (hook-create-chronics): Fix build issue with fedora-minimal Signed-off-by: Ritesh.K --- packaging/docker/Dockerfile.fedora-minimal | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packaging/docker/Dockerfile.fedora-minimal b/packaging/docker/Dockerfile.fedora-minimal index addcd6505..fa3c92015 100644 --- a/packaging/docker/Dockerfile.fedora-minimal +++ b/packaging/docker/Dockerfile.fedora-minimal @@ -24,7 +24,8 @@ RUN dnf -y install \ jansson-devel \ spdlog-devel \ fmt-devel \ - libwebsockets-devel + libwebsockets-devel \ + bzip2-devel ENV LC_ALL=C.UTF-8 ENV LANG=C.UTF-8 From fa417e621fc4b4029e1b50c3ea378e4bc4958bde Mon Sep 17 00:00:00 2001 From: "Ritesh.K" Date: Mon, 12 Jan 2026 16:28:24 +0100 Subject: [PATCH 12/13] fix (hook-create-chronics): Add missing copyright and licensing information. Signed-off-by: Ritesh.K --- etc/examples/hooks/create_chronics.conf | 3 +++ 1 file changed, 3 insertions(+) diff --git a/etc/examples/hooks/create_chronics.conf b/etc/examples/hooks/create_chronics.conf index c72d3e455..86b9b6c53 100644 --- a/etc/examples/hooks/create_chronics.conf +++ b/etc/examples/hooks/create_chronics.conf @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2014-2026 Institute for Automation of Complex Power Systems, RWTH Aachen University +# SPDX-License-Identifier: Apache-2.0 + nodes = { file_node = { From 61807015713c8c50450178a679fdf862ad764f3a Mon Sep 17 00:00:00 2001 From: "Ritesh.K" Date: Mon, 12 Jan 2026 16:44:14 +0100 Subject: [PATCH 13/13] fix (hook-create-chronics): Fix build issue with fedora-minimal, add missing dependencies Signed-off-by: Ritesh.K --- packaging/docker/Dockerfile.fedora-minimal | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packaging/docker/Dockerfile.fedora-minimal b/packaging/docker/Dockerfile.fedora-minimal index fa3c92015..4df7777c4 100644 --- a/packaging/docker/Dockerfile.fedora-minimal +++ b/packaging/docker/Dockerfile.fedora-minimal @@ -25,7 +25,9 @@ RUN dnf -y install \ spdlog-devel \ fmt-devel \ libwebsockets-devel \ - bzip2-devel + bzip2-devel \ + json-devel + ENV LC_ALL=C.UTF-8 ENV LANG=C.UTF-8