1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-09 00:00:00 +01:00

improved configuration file format for hooks

This commit is contained in:
Steffen Vogel 2017-03-17 02:52:59 -03:00
parent 5b93da867a
commit 8e53725c33
15 changed files with 212 additions and 141 deletions

View file

@ -148,8 +148,6 @@ paths = (
rate = 100, # Send message over this path with a fixed (constant) rate (default: 0).
# Setting this value to 0 will disable this feature.
hook = "print", # Register custom hook funktion (see src/hooks.c)
poolsize = 30 # The amount of samples which are kept in a circular buffer.
# This number must be larger than the 'vectorize' settings of all
# associated input and output nodes!
@ -160,23 +158,31 @@ paths = (
in = "opal_node", # There's only a single source node allowed!
out = [ "udp_node", "tcp_node" ], # Multiple destination nodes are supported too.
hook = [ "print", "decimate:10" ] # Same is true for hooks.
# Multipe hook functions are executed in the order they are specified here.
},
{
in = "socket_node",
out = "file_node", # This path includes all available example hooks.
hook = [
"ts", # Replace the timestamp of messages with the time of reception
"skip_unchanged:0.1", # Skip messages whose values have not changed more than 0.1 from the previous message.
"skip_first:10", # Skip all messages which have been received in the first 10 seconds
"print", # Print all messages to stdout
"decimate:2", # Only forward every 2nd message
"convert:fixed", # Convert all values to fixed precission. Use 'float' to convert to floating point.
"fir:0" # Apply finite impulse response filter to first value.
# Coefficients are hard-coded in 'include/config.h'.
]
# A complete list of supported hooks
print = {
output = "stdout"
priority = 0
},
ts = {
priority = 1
}
decimate = {
ratio = 2 # Only forward every 2nd message
},
convert = {
mode = "fixed" # Convert all values to fixed precission. Use 'float' to convert to floating point.
},
skip_first = {
seconds = 10 # Skip the first 10 seconds of this path
},
shift = {
mode = "origin", # Shift origin timestam of samples by +10 seconds
offset = 10
}
}
);

View file

@ -40,6 +40,10 @@ paths = (
{
in = "node1", # Name of the node we listen to (see above)
out = "node1", # And we loop back to the origin
hook = ["print"]
# Hooks
print = {
output = "stdout"
}
}
);

View file

@ -40,6 +40,10 @@ paths = (
{
in = "node1", # Name of the node we listen to (see above)
out = "node2", # And we loop back to the origin
hook = ["print"]
# Hooks
print = {
output = "stdout"
}
}
);

View file

@ -40,6 +40,10 @@ paths = (
{
in = "node1", # Name of the node we listen to (see above)
out = "node2", # And we loop back to the origin
hook = ["print"]
# Hooks
print = {
output = "stdout"
}
}
);

View file

@ -40,6 +40,10 @@ paths = (
{
in = "node1", # Name of the node we listen to (see above)
out = "node1", # And we loop back to the origin
hook = ["print"]
# Hooks
print = {
output = "stdout"
}
}
);

View file

@ -41,6 +41,10 @@ paths = (
{
in = "node1", # Name of the node we listen to (see above)
out = "node1", # And we loop back to the origin
hook = ["print"]
# Hooks
print = {
output = "stdout"
}
}
);

View file

@ -62,6 +62,5 @@ paths = (
{
in = "node1", # Name of the node we listen to (see above)
out = "node2", # And we loop back to the origin
hook = ["decimate:2", "print"]
}
);

View file

@ -96,19 +96,21 @@ enum hook_when {
struct hook_type {
enum hook_when when; /**< The type of the hook as a bitfield */
hook_cb_t cb; /**< The hook callback function as a function pointer. */
int priority; /**< A priority to change the order of execution within one type of hook */
int priority; /**< Default priority of this hook type. */
};
/** Descriptor for user defined hooks. See hooks[]. */
struct hook {
enum state state;
const char *parameter; /**< A parameter string for this hook. Can be used to configure the hook behaviour. */
struct sample *prev, *last;
struct hook_type *_vt; /**< C++ like Vtable pointer. */
void *_vd; /**< Private data for this hook. This pointer can be used to pass data between consecutive calls of the callback. */
int priority; /**< A priority to change the order of execution within one type of hook. */
config_setting_t *cfg;
};
/** Save references to global nodes, paths and settings */
@ -142,10 +144,18 @@ int hook_cmp_priority(const void *a, const void *b);
*/
void * hook_storage(struct hook *h, int when, size_t len, ctor_cb_t ctor, dtor_cb_t dtor);
/** Parse an array or single hook function.
/** Parses an object of hooks
*
* Examples:
* hooks = [ "print", "fir" ]
* hooks = "log"
* Example:
*
* {
* stats = {
* output = "stdout"
* },
* skip_first = {
* seconds = 10
* },
* hooks = [ "print" ]
* }
*/
int hook_parse_list(struct list *list, config_setting_t *cfg);
int hook_parse_list(struct list *list, config_setting_t *cfg, struct super_node *sn);

View file

@ -31,6 +31,7 @@ int hook_init(struct hook *h, struct hook_type *vt, struct super_node *sn)
assert(h->state == STATE_DESTROYED);
h->_vt = vt;
h->priority = vt->priority;
ret = hook_run(h, HOOK_INIT, &i);
if (ret)
@ -43,29 +44,14 @@ int hook_init(struct hook *h, struct hook_type *vt, struct super_node *sn)
int hook_parse(struct hook *h, config_setting_t *cfg)
{
const char *hookline;
char *name, *param;
struct plugin *p;
int ret;
assert(h->state != STATE_DESTROYED);
hookline = config_setting_get_string(cfg);
if (!hookline)
cerror(cfg, "Invalid hook function");
h->cfg = cfg;
name = strtok((char *) hookline, ":");
param = strtok(NULL, "");
config_setting_lookup_int(h->cfg, "priority", &h->priority);
p = plugin_lookup(PLUGIN_TYPE_HOOK, name);
if (!p)
cerror(cfg, "Unknown hook function '%s'", name);
if (p->hook.when & HOOK_AUTO)
cerror(cfg, "Hook '%s' is built-in and can not be added manually.", name);
h->parameter = param;
/* Parse hook arguments */
ret = hook_run(h, HOOK_PARSE, NULL);
if (ret)
@ -96,12 +82,12 @@ int hook_cmp_priority(const void *a, const void *b)
struct hook *ha = (struct hook *) a;
struct hook *hb = (struct hook *) b;
return ha->_vt->priority - hb->_vt->priority;
return ha->priority - hb->priority;
}
int hook_run(struct hook *h, int when, struct hook_info *i)
{
debug(LOG_HOOK | 22, "Running hook '%s' when=%u, prio=%u, cnt=%zu", plugin_name(h->_vt), when, h->_vt->priority, i ? i->count : 0);
debug(LOG_HOOK | 22, "Running hook '%s' when=%u, prio=%u, cnt=%zu", plugin_name(h->_vt), when, h->priority, i ? i->count : 0);
return h->_vt->when & when ? h->_vt->cb(h, when, i) : 0;
}
@ -129,33 +115,40 @@ void * hook_storage(struct hook *h, int when, size_t len, ctor_cb_t ctor, dtor_c
return h->_vd;
}
/** Parse an array or single hook function.
*
* Examples:
* hooks = [ "print", "fir" ]
* hooks = "log"
*/
int hook_parse_list(struct list *list, config_setting_t *cfg)
int hook_parse_list(struct list *list, config_setting_t *cfg, struct super_node *sn)
{
struct hook h;
struct plugin *p;
int ret;
if (!config_setting_is_group(cfg))
cerror(cfg, "Hooks must be configured with an object");
switch (config_setting_type(cfg)) {
case CONFIG_TYPE_STRING:
hook_parse(&h, cfg);
list_push(list, memdup(&h, sizeof(h)));
break;
int priority = 10;
for (int i = 0; i < config_setting_length(cfg); i++) {
config_setting_t *cfg_hook = config_setting_get_elem(cfg, i);
const char *name = config_setting_name(cfg_hook);
p = plugin_lookup(PLUGIN_TYPE_HOOK, name);
if (!p)
continue; /* We ignore all non hook settings in this libconfig object setting */
if (!config_setting_is_group(cfg_hook))
cerror(cfg_hook, "The 'hooks' setting must be an array of strings.");
ret = hook_init(&h, &p->hook, sn);
if (ret)
continue;
h.priority = priority++;
ret = hook_parse(&h, cfg_hook);
if (ret)
continue;
case CONFIG_TYPE_ARRAY:
for (int i = 0; i < config_setting_length(cfg); i++) {
config_setting_t *cfg_hook = config_setting_get_elem(cfg, i);
hook_parse(&h, cfg_hook);
list_push(list, memdup(&h, sizeof(h)));
}
break;
default:
cerror(cfg, "Invalid hook functions");
list_push(list, memdup(&h, sizeof(h)));
}
return list_length(list);

View file

@ -20,17 +20,22 @@ static int hook_convert(struct hook *h, int when, struct hook_info *j)
} mode;
} *private = hook_storage(h, when, sizeof(*private), NULL, NULL);
const char *mode;
switch (when) {
case HOOK_PARSE:
if (!h->parameter)
error("Missing parameter for hook: '%s'", plugin_name(h->_vt));
if (!strcmp(h->parameter, "fixed"))
if (!h->cfg)
error("Missing configuration for hook: '%s'", plugin_name(h->_vt));
if (!config_setting_lookup_string(h->cfg, "mode", &mode))
cerror(h->cfg, "Missing setting 'mode' for hook '%s'", plugin_name(h->_vt));
if (!strcmp(mode, "fixed"))
private->mode = TO_FIXED;
else if (!strcmp(h->parameter, "float"))
else if (!strcmp(mode, "float"))
private->mode = TO_FLOAT;
else
error("Invalid parameter '%s' for hook 'convert'", h->parameter);
error("Invalid parameter '%s' for hook 'convert'", mode);
break;
case HOOK_READ:
@ -56,7 +61,7 @@ static struct plugin p = {
.hook = {
.priority = 99,
.cb = hook_convert,
.when = HOOK_STORAGE | HOOK_READ
.when = HOOK_STORAGE | HOOK_PARSE | HOOK_READ
}
};

View file

@ -14,22 +14,24 @@
static int hook_decimate(struct hook *h, int when, struct hook_info *j)
{
struct {
unsigned ratio;
int ratio;
unsigned counter;
} *private = hook_storage(h, when, sizeof(*private), NULL, NULL);
switch (when) {
case HOOK_PARSE:
if (!h->parameter)
error("Missing parameter for hook: '%s'", plugin_name(h->_vt));
private->ratio = strtol(h->parameter, NULL, 10);
if (!private->ratio)
error("Invalid parameter '%s' for hook 'decimate'", h->parameter);
case HOOK_INIT:
private->counter = 0;
break;
case HOOK_PARSE:
if (!h->cfg)
error("Missing configuration for hook: '%s'", plugin_name(h->_vt));
if (!config_setting_lookup_int(h->cfg, "ratio", &private->ratio))
cerror(h->cfg, "Missing setting 'ratio' for hook '%s'", plugin_name(h->_vt));
break;
case HOOK_READ:
assert(j->samples);
@ -57,7 +59,7 @@ static struct plugin p = {
.hook = {
.priority = 99,
.cb = hook_decimate,
.when = HOOK_STORAGE | HOOK_DESTROY | HOOK_READ
.when = HOOK_STORAGE | HOOK_PARSE | HOOK_DESTROY | HOOK_READ
}
};

View file

@ -27,55 +27,55 @@ static int hook_shift(struct hook *h, int when, struct hook_info *j)
SHIFT_SEQUENCE
} mode;
} *private = hook_storage(h, when, sizeof(*private), NULL, NULL);
const char *mode;
switch (when) {
case HOOK_INIT:
private->mode = SHIFT_TS_ORIGIN; /* Default mode */
break;
case HOOK_PARSE:
if (!h->parameter)
error("Missing parameter for hook: '%s'", plugin_name(h->_vt));
if (!h->cfg)
error("Missing configuration for hook: '%s'", plugin_name(h->_vt));
char *endptr, *off;
char *cpy = strdup(h->parameter);
char *tok1 = strtok(cpy, ",");
char *tok2 = strtok(NULL, ",");
if (tok2) {
off = tok2;
if (!strcmp(tok1, "origin"))
if (config_setting_lookup_string(h->cfg, "mode", &mode)) {
if (!strcmp(mode, "origin"))
private->mode = SHIFT_TS_ORIGIN;
else if (!strcmp(tok1, "received"))
else if (!strcmp(mode, "received"))
private->mode = SHIFT_TS_RECEIVED;
else if (!strcmp(tok1, "sent"))
else if (!strcmp(mode, "sent"))
private->mode = SHIFT_TS_SENT;
else if (!strcmp(tok1, "sequence"))
else if (!strcmp(mode, "sequence"))
private->mode = SHIFT_SEQUENCE;
else
error("Invalid mode parameter for hook '%s'", plugin_name(h->_vt));
}
else {
off = tok1;
private->mode = SHIFT_TS_ORIGIN; /* Default mode */
error("Invalid mode parameter '%s' for hook '%s'", mode, plugin_name(h->_vt));
}
switch (private->mode) {
case SHIFT_TS_ORIGIN:
case SHIFT_TS_RECEIVED:
case SHIFT_TS_SENT:
private->offset.ts = time_from_double(strtod(off, &endptr));
case SHIFT_TS_SENT: {
double offset;
if (!config_setting_lookup_float(h->cfg, "offset", &offset))
cerror(h->cfg, "Missing setting 'offset' for hook '%s'", plugin_name(h->_vt));
private->offset.ts = time_from_double(offset);
break;
}
case SHIFT_SEQUENCE:
private->offset.seq = strtoul(off, &endptr, 10);
case SHIFT_SEQUENCE: {
int offset;
if (!config_setting_lookup_int(h->cfg, "offset", &offset))
cerror(h->cfg, "Missing setting 'offset' for hook '%s'", plugin_name(h->_vt));
private->offset.seq = offset;
break;
}
}
if (endptr == off)
error("Invalid offset parameter for hook '%s'", plugin_name(h->_vt));
free(cpy);
break;
case HOOK_READ:

View file

@ -8,6 +8,8 @@
* @{
*/
#include <libconfig.h>
#include "hook.h"
#include "plugin.h"
#include "timing.h"
@ -24,20 +26,19 @@ static int hook_skip_first(struct hook *h, int when, struct hook_info *j)
} state;
} *private = hook_storage(h, when, sizeof(*private), NULL, NULL);
char *endptr;
double wait;
switch (when) {
case HOOK_PARSE:
if (!h->parameter)
error("Missing parameter for hook: '%s'", plugin_name(h->_vt));
wait = strtof(h->parameter, &endptr);
if (h->parameter == endptr)
error("Invalid parameter '%s' for hook 'skip_first'", h->parameter);
case HOOK_PARSE: {
double wait;
if (!h->cfg)
error("Missing configuration for hook: '%s'", plugin_name(h->_vt));
if (!config_setting_lookup_float(h->cfg, "seconds", &wait))
cerror(h->cfg, "Missing setting 'seconds' for hook '%s'", plugin_name(h->_vt));
private->skip = time_from_double(wait);
break;
}
case HOOK_PATH_START:
case HOOK_PATH_RESTART:

View file

@ -63,12 +63,17 @@ static int hook_stats_send(struct hook *h, int when, struct hook_info *j)
assert(j->nodes);
assert(j->path);
if (!h->parameter)
error("Missing parameter for hook '%s'", plugin_name(h->_vt));
if (!h->cfg)
error("Missing configuration for hook '%s'", plugin_name(h->_vt));
private->dest = list_lookup(j->nodes, h->parameter);
const char *dest;
if (!config_setting_lookup_string(h->cfg, "destination", &dest))
cerror(h->cfg, "Missing setting 'destination' for hook '%s'", plugin_name(h->_vt));
private->dest = list_lookup(j->nodes, dest);
if (!private->dest)
error("Invalid destination node '%s' for hook '%s'", h->parameter, plugin_name(h->_vt));
cerror(h->cfg, "Invalid destination node '%s' for hook '%s'", dest, plugin_name(h->_vt));
break;
case HOOK_PATH_START:

View file

@ -24,15 +24,46 @@
#include "config.h"
static int hook_parse_cli(struct hook *h, char *param)
{
int ret;
config_t cfg;
config_setting_t *cfg_root;
config_init(&cfg);
config_set_auto_convert(&cfg, 1);
cfg_root = config_root_setting(&cfg);
ret = config_read_string(&cfg, param);
if (ret != CONFIG_TRUE)
error("Failed to parse argument '%s': %s", param, config_error_text(&cfg));
config_write(&cfg, stdout);
ret = hook_parse(h, cfg_root);
if (ret)
error("Failed to parse hook settings");
config_destroy(&cfg);
return 0;
}
static void usage()
{
printf("Usage: villas-hook [OPTIONS] NAME [PARAMETER] \n");
printf("Usage: villas-hook [OPTIONS] NAME [PARAMETER1 PARAMTER2 ...] \n");
printf(" NAME the name of the hook function to run\n");
printf(" PARAM the name of the node to which samples are sent and received from\n\n");
printf(" OPTIONS are:\n");
printf(" -h show this help\n");
printf(" -d LVL set debug level to LVL\n");
printf(" -v CNT process CNT samples at once\n\n");
printf(" -v CNT process CNT samples at once\n");
printf("\n");
printf("Example:");
printf(" villas-signal random | villas-hook skip_first seconds=10\n");
printf("\n");
print_copyright();
}
@ -45,7 +76,7 @@ int main(int argc, char *argv[])
level = V;
cnt = 1;
char *name, *parameter;
char *name;
struct log log;
struct plugin *p;
@ -86,17 +117,16 @@ int main(int argc, char *argv[])
error("Failed to initilize memory pool");
name = argv[optind];
parameter = argc >= optind + 2 ? argv[optind + 1] : NULL;
h.parameter = parameter;
p = plugin_lookup(PLUGIN_TYPE_HOOK, name);
if (!p)
error("Unknown hook function '%s'", argv[optind]);
hook_init(&h, &p->hook, NULL);
if (argc > optind + 1)
hook_parse_cli(&h, argv[optind + 1]);
hook_run(&h, HOOK_PARSE, &hi);
hook_run(&h, HOOK_PATH_START, &hi);
while (!feof(stdin)) {