diff --git a/include/villas/config_helper.h b/include/villas/config_helper.h new file mode 100644 index 000000000..125f5b023 --- /dev/null +++ b/include/villas/config_helper.h @@ -0,0 +1,37 @@ +/** Helpers for configuration parsers. + * + * @author Steffen Vogel + * @copyright 2017, Institute for Automation of Complex Power Systems, EONERC + * @license GNU General Public License (version 3) + * + * VILLASnode + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *********************************************************************************/ + +#pragma once + +#include +#include + +#include "sample.h" + +/* Convert a libconfig object to a jansson object */ +json_t * config_to_json(config_setting_t *cfg); + +/* Convert a jansson object into a libconfig object. */ +int json_to_config(json_t *json, config_setting_t *parent); + +/* Create a libconfig object from command line parameters. */ +int config_parse_cli(config_t *cfg, int argc, char *argv[]); diff --git a/include/villas/hook.h b/include/villas/hook.h index 3db1fbd38..b73a0c49a 100644 --- a/include/villas/hook.h +++ b/include/villas/hook.h @@ -34,42 +34,13 @@ #pragma once -#include -#include -#include - -#include "log_config.h" -#include "queue.h" -#include "list.h" -#include "super_node.h" +#include "hook_type.h" #include "common.h" /* Forward declarations */ struct path; -struct hook; struct sample; -struct super_node; - -struct hook_type { - int priority; /**< Default priority of this hook type. */ - bool builtin; /**< Should we add this hook by default to every path?. */ - - size_t size; /**< Size of allocation for struct hook::_vd */ - - int (*parse)(struct hook *h, config_setting_t *cfg); - - int (*init)(struct hook *h); /**< Called before path is started to parseHOOK_DESTROYs. */ - int (*destroy)(struct hook *h); /**< Called after path has been stopped to release memory allocated by HOOK_INIT */ - - int (*start)(struct hook *h); /**< Called whenever a path is started; before threads are created. */ - int (*stop)(struct hook *h); /**< Called whenever a path is stopped; after threads are destoyed. */ - - int (*periodic)(struct hook *h);/**< Called periodically. Period is set by global 'stats' option in the configuration file. */ - int (*restart)(struct hook *h); /**< Called whenever a new simulation case is started. This is detected by a sequence no equal to zero. */ - - int (*read)(struct hook *h, struct sample *smps[], size_t *cnt); /**< Called for every single received samples. */ - int (*write)(struct hook *h, struct sample *smps[], size_t *cnt); /**< Called for every single sample which will be sent. */ -}; +struct list; /** Descriptor for user defined hooks. See hooks[]. */ struct hook { @@ -96,6 +67,8 @@ int hook_init(struct hook *h, struct hook_type *vt, struct path *p); */ int hook_parse(struct hook *h, config_setting_t *cfg); +int hook_parse_cli(struct hook *h, int argc, char *argv[]); + int hook_destroy(struct hook *h); int hook_start(struct hook *h); diff --git a/include/villas/hook_type.h b/include/villas/hook_type.h new file mode 100644 index 000000000..a6c45ece1 --- /dev/null +++ b/include/villas/hook_type.h @@ -0,0 +1,66 @@ +/** Hook funktions + * + * Every path can register a hook function which is called for every received + * message. This can be used to debug the data flow, get statistics + * or alter the message. + * + * This file includes some examples. + * + * @file + * @author Steffen Vogel + * @copyright 2017, Institute for Automation of Complex Power Systems, EONERC + * @license GNU General Public License (version 3) + * + * VILLASnode + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/** + * @addtogroup hooks User-defined hook functions + * @ingroup path + * @{ + *********************************************************************************/ + +#pragma once + +#include +#include + +#include + +/* Forward declarations */ +struct hook; +struct sample; + +struct hook_type { + int priority; /**< Default priority of this hook type. */ + bool builtin; /**< Should we add this hook by default to every path?. */ + + size_t size; /**< Size of allocation for struct hook::_vd */ + + int (*parse)(struct hook *h, config_setting_t *cfg); + int (*parse_cli)(struct hook *h, int argc, char *argv[]); + + int (*init)(struct hook *h); /**< Called before path is started to parseHOOK_DESTROYs. */ + int (*destroy)(struct hook *h); /**< Called after path has been stopped to release memory allocated by HOOK_INIT */ + + int (*start)(struct hook *h); /**< Called whenever a path is started; before threads are created. */ + int (*stop)(struct hook *h); /**< Called whenever a path is stopped; after threads are destoyed. */ + + int (*periodic)(struct hook *h);/**< Called periodically. Period is set by global 'stats' option in the configuration file. */ + int (*restart)(struct hook *h); /**< Called whenever a new simulation case is started. This is detected by a sequence no equal to zero. */ + + int (*read)(struct hook *h, struct sample *smps[], size_t *cnt); /**< Called for every single received samples. */ + int (*write)(struct hook *h, struct sample *smps[], size_t *cnt); /**< Called for every single sample which will be sent. */ +}; diff --git a/include/villas/json.h b/include/villas/sample_io_json.h similarity index 84% rename from include/villas/json.h rename to include/villas/sample_io_json.h index 481a18839..0f0e4e7ac 100644 --- a/include/villas/json.h +++ b/include/villas/sample_io_json.h @@ -1,4 +1,4 @@ -/** JSON serializtion of various objects. +/** JSON serializtion sample data. * * @author Steffen Vogel * @copyright 2017, Institute for Automation of Complex Power Systems, EONERC @@ -23,15 +23,9 @@ #pragma once #include -#include #include "sample.h" -/* Convert a libconfig object to a libjansson object */ -json_t * config_to_json(config_setting_t *cfg); - -int json_to_config(json_t *json, config_setting_t *parent); - int sample_io_json_pack(json_t **j, struct sample *s, int flags); int sample_io_json_unpack(json_t *j, struct sample *s, int *flags); diff --git a/lib/Makefile.villas.inc b/lib/Makefile.villas.inc index 8061ef2d0..e4f06e974 100644 --- a/lib/Makefile.villas.inc +++ b/lib/Makefile.villas.inc @@ -10,12 +10,12 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . ################################################################################### @@ -31,7 +31,8 @@ LIB_SRCS += $(addprefix lib/nodes/, file.c cbuilder.c shmem.c signal.c) \ log.c log_config.c utils.c super_node.c hist.c timing.c pool.c \ list.c queue.c queue_signalled.c memory.c advio.c web.c api.c \ plugin.c node_type.c stats.c mapping.c sample_io.c shmem.c \ - json.c crypt.c compat.c log_table.c log_helper.c \ + config_helper.c sample_io_json.c crypt.c compat.c log_table.c \ + log_helper.c \ ) LIB_LDFLAGS = -shared diff --git a/lib/api/actions/config.c b/lib/api/actions/config.c index 24e4a44e5..db42997d1 100644 --- a/lib/api/actions/config.c +++ b/lib/api/actions/config.c @@ -25,7 +25,8 @@ #include "api.h" #include "utils.h" #include "plugin.h" -#include "json.h" +#include "config_helper.h" +#include "super_node.h" static int api_config(struct api_action *h, json_t *args, json_t **resp, struct api_session *s) { @@ -43,4 +44,4 @@ static struct plugin p = { .api.cb = api_config }; -REGISTER_PLUGIN(&p) \ No newline at end of file +REGISTER_PLUGIN(&p) diff --git a/lib/api/actions/nodes.c b/lib/api/actions/nodes.c index 4b31141f8..121dd9dce 100644 --- a/lib/api/actions/nodes.c +++ b/lib/api/actions/nodes.c @@ -24,8 +24,9 @@ #include "plugin.h" #include "node.h" +#include "super_node.h" #include "utils.h" -#include "json.h" +#include "config_helper.h" #include "api.h" diff --git a/lib/api/actions/paths.c b/lib/api/actions/paths.c index 10adb0616..e89e21ce5 100644 --- a/lib/api/actions/paths.c +++ b/lib/api/actions/paths.c @@ -25,8 +25,9 @@ #include "plugin.h" #include "path.h" #include "utils.h" -#include "json.h" +#include "config_helper.h" #include "stats.h" +#include "super_node.h" #include "api.h" @@ -40,7 +41,7 @@ static int api_paths(struct api_action *r, json_t *args, json_t **resp, struct a json_t *json_path = json_pack("{ s: i }", "state", p->state ); - + if (p->stats) json_object_set(json_path, "stats", stats_json(p->stats)); diff --git a/lib/json.c b/lib/config_helper.c similarity index 63% rename from lib/json.c rename to lib/config_helper.c index 022ac0943..82aa65791 100644 --- a/lib/json.c +++ b/lib/config_helper.c @@ -1,4 +1,4 @@ -/** JSON serializtion of various objects. +/** Helpers for configuration parsers. * * @author Steffen Vogel * @copyright 2017, Institute for Automation of Complex Power Systems, EONERC @@ -20,7 +20,8 @@ * along with this program. If not, see . *********************************************************************************/ -#include "json.h" +#include "config_helper.h" +#include "utils.h" static int json_to_config_type(int type) { @@ -138,102 +139,22 @@ int json_to_config(json_t *json, config_setting_t *parent) return 0; } -int sample_io_json_pack(json_t **j, struct sample *s, int flags) -{ - json_error_t err; - json_t *json_data = json_array(); - - for (int i = 0; i < s->length; i++) { - json_t *json_value = sample_get_data_format(s, i) - ? json_integer(s->data[i].i) - : json_real(s->data[i].f); - - json_array_append(json_data, json_value); - } - - *j = json_pack_ex(&err, 0, "{ s: { s: [ I, I ], s: [ I, I ], s: [ I, I ] }, s: I, s: o }", - "ts", - "origin", s->ts.origin.tv_sec, s->ts.origin.tv_nsec, - "received", s->ts.received.tv_sec, s->ts.received.tv_nsec, - "sent", s->ts.sent.tv_sec, s->ts.sent.tv_nsec, - "sequence", s->sequence, - "data", json_data); - - if (!*j) - return -1; - - return 0; -} - -int sample_io_json_unpack(json_t *j, struct sample *s, int *flags) -{ - int ret, i; - json_t *json_data, *json_value; - - ret = json_unpack(j, "{ s: { s: [ I, I ], s: [ I, I ], s: [ I, I ] }, s: I, s: o }", - "ts", - "origin", &s->ts.origin.tv_sec, &s->ts.origin.tv_nsec, - "received", &s->ts.received.tv_sec, &s->ts.received.tv_nsec, - "sent", &s->ts.sent.tv_sec, &s->ts.sent.tv_nsec, - "sequence", &s->sequence, - "data", &json_data); - - if (ret) - return ret; - - s->length = 0; - - json_array_foreach(json_data, i, json_value) { - switch (json_typeof(json_value)) { - case JSON_REAL: - s->data[i].f = json_real_value(json_value); - sample_set_data_format(s, i, SAMPLE_DATA_FORMAT_FLOAT); - break; - - case JSON_INTEGER: - s->data[i].f = json_integer_value(json_value); - sample_set_data_format(s, i, SAMPLE_DATA_FORMAT_INT); - break; - - default: - return -1; - } - - s->length++; - } - - return 0; -} - -int sample_io_json_fprint(FILE *f, struct sample *s, int flags) +int config_parse_cli(config_t *cfg, int argc, char *argv[]) { int ret; - json_t *json; + char *str = NULL; - ret = sample_io_json_pack(&json, s, flags); - if (ret) - return ret; + for (int i = 0; i < argc; i++) + str = strcatf(&str, "%s", argv[i]); - ret = json_dumpf(json, f, 0); + if (!str) + return 0; - json_decref(json); + config_set_auto_convert(cfg, 1); - return ret; -} - -int sample_io_json_fscan(FILE *f, struct sample *s, int *flags) -{ - int ret; - json_t *json; - json_error_t err; - - json = json_loadf(f, JSON_DISABLE_EOF_CHECK, &err); - if (!json) - return -1; - - ret = sample_io_json_unpack(json, s, flags); - - json_decref(json); - - return ret; + ret = config_read_string(cfg, str); + + free(str); + + return ret != CONFIG_TRUE; } diff --git a/lib/hook.c b/lib/hook.c index a40ff61c3..090c3c051 100644 --- a/lib/hook.c +++ b/lib/hook.c @@ -31,6 +31,7 @@ #include "utils.h" #include "node.h" #include "plugin.h" +#include "config_helper.h" int hook_init(struct hook *h, struct hook_type *vt, struct path *p) { @@ -70,6 +71,38 @@ int hook_parse(struct hook *h, config_setting_t *cfg) return 0; } +int hook_parse_cli(struct hook *h, int argc, char *argv[]) +{ + int ret; + + if (h->_vt->parse_cli) { + ret = h->_vt->parse_cli(h, argc, argv); + if (ret) + return ret; + + h->state = STATE_PARSED; + } + else { + config_t cfg; + config_setting_t *cfg_root; + + config_init(&cfg); + + ret = config_parse_cli(&cfg, argc, argv); + if (ret) + goto out; + + cfg_root = config_root_setting(&cfg); + + ret = hook_parse(h, cfg_root); + +out: + config_destroy(&cfg); + } + + return ret; +} + int hook_destroy(struct hook *h) { int ret; @@ -112,14 +145,14 @@ int hook_periodic(struct hook *h) int hook_restart(struct hook *h) { debug(LOG_HOOK | 10, "Running hook %s: type=restart, priority=%d", plugin_name(h->_vt), h->priority); - + return h->_vt->restart ? h->_vt->restart(h) : 0; } int hook_read(struct hook *h, struct sample *smps[], size_t *cnt) { debug(LOG_HOOK | 10, "Running hook %s: type=read, priority=%d, cnt=%zu", plugin_name(h->_vt), h->priority, *cnt); - + return h->_vt->read ? h->_vt->read(h, smps, cnt) : 0; } @@ -196,7 +229,7 @@ int hook_parse_list(struct list *list, config_setting_t *cfg, struct path *o) continue; /* We ignore all non hook settings in this libconfig object setting */ struct hook *h = alloc(sizeof(struct hook)); - + ret = hook_init(h, &p->hook, o); if (ret) cerror(cfg_hook, "Failed to initialize hook"); diff --git a/lib/hooks/stats_send.c b/lib/hooks/stats_send.c index 40029f990..5c2fb7921 100644 --- a/lib/hooks/stats_send.c +++ b/lib/hooks/stats_send.c @@ -28,6 +28,7 @@ #include "plugin.h" #include "stats.h" #include "path.h" +#include "super_node.h" struct stats_send { struct node *dest; @@ -60,7 +61,7 @@ static int stats_send_parse(struct hook *h, config_setting_t *cfg) if (config_setting_lookup_string(cfg, "destination", &dest)) { assert(h->path); - + p->dest = list_lookup(&h->path->super_node->nodes, dest); if (!p->dest) cerror(cfg, "Invalid destination node '%s' for hook '%s'", dest, plugin_name(h->_vt)); @@ -145,4 +146,4 @@ static struct plugin p = { REGISTER_PLUGIN(&p) -/** @} */ \ No newline at end of file +/** @} */ diff --git a/lib/node.c b/lib/node.c index 69b57a073..da9812ebd 100644 --- a/lib/node.c +++ b/lib/node.c @@ -28,6 +28,7 @@ #include "utils.h" #include "config.h" #include "plugin.h" +#include "config_helper.h" int node_init(struct node *n, struct node_type *vt) { @@ -87,11 +88,30 @@ int node_parse_cli(struct node *n, int argc, char *argv[]) n->vectorize = 1; n->name = "cli"; - ret = n->_vt->parse_cli ? n->_vt->parse_cli(n, argc, argv) : 0; - if (ret) - error("Failed to parse node '%s'", node_name(n)); + if (n->_vt->parse_cli) { + ret = n->_vt->parse_cli(n, argc, argv); + if (ret) + return ret; - n->state = STATE_PARSED; + n->state = STATE_PARSED; + } + else { + config_t cfg; + config_setting_t *cfg_root; + + config_init(&cfg); + + ret = config_parse_cli(&cfg, argc, argv); + if (ret) + goto out; + + cfg_root = config_root_setting(&cfg); + + ret = node_parse(n, cfg_root); + +out: + config_destroy(&cfg); + } return ret; } diff --git a/lib/nodes/fpga.c b/lib/nodes/fpga.c index 9dacb3a04..557d86620 100644 --- a/lib/nodes/fpga.c +++ b/lib/nodes/fpga.c @@ -16,6 +16,7 @@ #include "utils.h" #include "timing.h" #include "plugin.h" +#include "super_node.h" #include "fpga/card.h" @@ -291,4 +292,3 @@ static struct plugin p = { REGISTER_PLUGIN(&p) LIST_INIT_STATIC(&p.node.instances) - \ No newline at end of file diff --git a/lib/sample_io.c b/lib/sample_io.c index 79ef6d536..078936a3c 100644 --- a/lib/sample_io.c +++ b/lib/sample_io.c @@ -22,9 +22,9 @@ #include -#include "json.h" #include "sample.h" #include "sample_io.h" +#include "sample_io_json.h" #include "timing.h" int sample_io_fprint(FILE *f, struct sample *s, enum sample_io_format fmt, int flags) @@ -195,4 +195,3 @@ skip: if (fgets(line, sizeof(line), f) == NULL) return sample_io_villas_scan(line, s, fl); } - diff --git a/lib/sample_io_json.c b/lib/sample_io_json.c new file mode 100644 index 000000000..033edd82c --- /dev/null +++ b/lib/sample_io_json.c @@ -0,0 +1,123 @@ +/** JSON serializtion sample data. + * + * @author Steffen Vogel + * @copyright 2017, Institute for Automation of Complex Power Systems, EONERC + * @license GNU General Public License (version 3) + * + * VILLASnode + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *********************************************************************************/ + +#include "sample_io_json.h" + +int sample_io_json_pack(json_t **j, struct sample *s, int flags) +{ + json_error_t err; + json_t *json_data = json_array(); + + for (int i = 0; i < s->length; i++) { + json_t *json_value = sample_get_data_format(s, i) + ? json_integer(s->data[i].i) + : json_real(s->data[i].f); + + json_array_append(json_data, json_value); + } + + *j = json_pack_ex(&err, 0, "{ s: { s: [ I, I ], s: [ I, I ], s: [ I, I ] }, s: I, s: o }", + "ts", + "origin", s->ts.origin.tv_sec, s->ts.origin.tv_nsec, + "received", s->ts.received.tv_sec, s->ts.received.tv_nsec, + "sent", s->ts.sent.tv_sec, s->ts.sent.tv_nsec, + "sequence", s->sequence, + "data", json_data); + + if (!*j) + return -1; + + return 0; +} + +int sample_io_json_unpack(json_t *j, struct sample *s, int *flags) +{ + int ret, i; + json_t *json_data, *json_value; + + ret = json_unpack(j, "{ s: { s: [ I, I ], s: [ I, I ], s: [ I, I ] }, s: I, s: o }", + "ts", + "origin", &s->ts.origin.tv_sec, &s->ts.origin.tv_nsec, + "received", &s->ts.received.tv_sec, &s->ts.received.tv_nsec, + "sent", &s->ts.sent.tv_sec, &s->ts.sent.tv_nsec, + "sequence", &s->sequence, + "data", &json_data); + + if (ret) + return ret; + + s->length = 0; + + json_array_foreach(json_data, i, json_value) { + switch (json_typeof(json_value)) { + case JSON_REAL: + s->data[i].f = json_real_value(json_value); + sample_set_data_format(s, i, SAMPLE_DATA_FORMAT_FLOAT); + break; + + case JSON_INTEGER: + s->data[i].f = json_integer_value(json_value); + sample_set_data_format(s, i, SAMPLE_DATA_FORMAT_INT); + break; + + default: + return -1; + } + + s->length++; + } + + return 0; +} + +int sample_io_json_fprint(FILE *f, struct sample *s, int flags) +{ + int ret; + json_t *json; + + ret = sample_io_json_pack(&json, s, flags); + if (ret) + return ret; + + ret = json_dumpf(json, f, 0); + + json_decref(json); + + return ret; +} + +int sample_io_json_fscan(FILE *f, struct sample *s, int *flags) +{ + int ret; + json_t *json; + json_error_t err; + + json = json_loadf(f, JSON_DISABLE_EOF_CHECK, &err); + if (!json) + return -1; + + ret = sample_io_json_unpack(json, s, flags); + + json_decref(json); + + return ret; +} diff --git a/lib/stats.c b/lib/stats.c index 02cf2bf62..7fdcb3889 100644 --- a/lib/stats.c +++ b/lib/stats.c @@ -20,6 +20,8 @@ * along with this program. If not, see . *********************************************************************************/ +#include + #include "stats.h" #include "hist.h" #include "timing.h" @@ -58,7 +60,7 @@ int stats_destroy(struct stats *s) hist_destroy(&s->histograms[i]); free(s->delta); - + return 0; } diff --git a/lib/super_node.c b/lib/super_node.c index 63ed40879..14e11379e 100644 --- a/lib/super_node.c +++ b/lib/super_node.c @@ -38,7 +38,7 @@ #include "plugin.h" #include "memory.h" #include "config.h" -#include "json.h" +#include "config_helper.h" #include "kernel/rt.h" diff --git a/tests/unit/config_json.c b/tests/unit/config_json.c index 3034c8a34..8f372eb33 100644 --- a/tests/unit/config_json.c +++ b/tests/unit/config_json.c @@ -26,7 +26,7 @@ #include #include "utils.h" -#include "json.h" +#include "config_helper.h" const char *cfg_example = "test : \n" "{\n" diff --git a/tools/conf2json.c b/tools/conf2json.c index 20d37aec3..2b63a18e7 100644 --- a/tools/conf2json.c +++ b/tools/conf2json.c @@ -23,7 +23,7 @@ #include #include -#include +#include #include void usage()