/* Hook-releated functions. * * Author: Steffen Vogel * SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include using namespace villas; using namespace villas::node; void HookList::parse(json_t *json, int mask, Path *o, Node *n) { if (!json_is_array(json)) throw ConfigError(json, "node-config-hook", "Hooks must be configured as a list of hook objects"); size_t i; json_t *json_hook; json_array_foreach(json, i, json_hook) { int ret; const char *type; Hook::Ptr h; json_error_t err; json_t *json_config; switch (json_typeof(json_hook)) { case JSON_STRING: type = json_string_value(json_hook); json_config = json_object(); break; case JSON_OBJECT: ret = json_unpack_ex(json_hook, &err, 0, "{ s: s }", "type", &type); if (ret) throw ConfigError(json_hook, err, "node-config-hook", "Failed to parse hook"); json_config = json_hook; break; default: throw ConfigError(json_hook, "node-config-hook", "Hook must be configured by simple string or object"); } auto hf = plugin::registry->lookup(type); if (!hf) throw ConfigError(json_hook, "node-config-hook", "Unknown hook type '{}'", type); if (!(hf->getFlags() & mask)) throw ConfigError(json_hook, "node-config-hook", "Hook '{}' not allowed here", type); h = hf->make(o, n); h->parse(json_config); push_back(h); } } void HookList::check() { for (auto h : *this) h->check(); } void HookList::prepare(SignalList::Ptr signals, int m, Path *p, Node *n) { if (!m) goto skip_add; // Add internal hooks if they are not already in the list for (auto f : plugin::registry->lookup()) { if ((f->getFlags() & m) == m) { auto h = f->make(p, n); push_back(h); } } skip_add: // Remove filters which are not enabled remove_if([](Hook::Ptr h) { return !h->isEnabled(); }); // We sort the hooks according to their priority sort([](const value_type &a, const value_type b) { return a->getPriority() < b->getPriority(); }); unsigned i = 0; auto sigs = signals; for (auto h : *this) { h->prepare(sigs); sigs = h->getSignals(); auto logger = h->getLogger(); logger->debug("Signal list after hook #{}:", i++); if (logger->level() <= spdlog::level::debug) sigs->dump(logger); } } int HookList::process(struct Sample *smps[], unsigned cnt) { unsigned current, processed = 0; if (size() == 0) return cnt; for (current = 0; current < cnt; current++) { struct Sample *smp = smps[current]; for (auto h : *this) { auto ret = h->process(smp); smp->signals = h->getSignals(); switch (ret) { case Hook::Reason::ERROR: return -1; case Hook::Reason::OK: continue; case Hook::Reason::SKIP_SAMPLE: goto skip; case Hook::Reason::STOP_PROCESSING: goto stop; } } stop: SWAP(smps[processed], smps[current]); processed++; skip: {} } return processed; } void HookList::periodic() { for (auto h : *this) h->periodic(); } void HookList::start() { for (auto h : *this) h->start(); } void HookList::stop() { for (auto h : *this) h->stop(); } SignalList::Ptr HookList::getSignals() const { auto h = back(); if (!h) return nullptr; return h->getSignals(); } unsigned HookList::getSignalsMaxCount() const { unsigned max_cnt = 0; for (auto h : *this) { unsigned sigs_cnt = h->getSignals()->size(); if (sigs_cnt > max_cnt) max_cnt = sigs_cnt; } return max_cnt; } json_t *HookList::toJson() const { json_t *json_hooks = json_array(); for (auto h : *this) json_array_append(json_hooks, h->getConfig()); return json_hooks; } void HookList::dump(Logger logger, std::string subject) const { logger->debug("Hooks of {}:", subject); unsigned i = 0; for (auto h : *this) logger->debug(" {}: {}", i++, h->getFactory()->getName()); }