2014-08-31 14:43:28 +00:00
|
|
|
/** Hook funktions
|
|
|
|
*
|
2015-06-10 15:10:51 +02:00
|
|
|
* Every path can register hook functions which are called at specific events.
|
|
|
|
* A list of supported events is described by enum hook_flags.
|
|
|
|
* Please note that there are several hook callbacks which are hard coded into path_create().
|
2014-08-31 14:43:28 +00:00
|
|
|
*
|
|
|
|
* This file includes some examples.
|
|
|
|
*
|
|
|
|
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
2015-06-02 21:53:04 +02:00
|
|
|
* @copyright 2014-2015, Institute for Automation of Complex Power Systems, EONERC
|
|
|
|
* This file is part of S2SS. All Rights Reserved. Proprietary and confidential.
|
2015-08-07 01:11:43 +02:00
|
|
|
* Unauthorized copying of this file, via any medium is strictly prohibited.
|
2015-06-02 21:53:04 +02:00
|
|
|
*********************************************************************************/
|
2015-03-18 16:13:18 +01:00
|
|
|
|
2014-08-31 14:43:28 +00:00
|
|
|
#include <string.h>
|
2015-09-28 19:18:39 +02:00
|
|
|
#include <math.h>
|
2014-08-31 14:43:28 +00:00
|
|
|
|
2015-06-10 15:05:36 +02:00
|
|
|
#include "timing.h"
|
2015-05-06 11:38:57 +02:00
|
|
|
#include "config.h"
|
2014-08-31 14:43:28 +00:00
|
|
|
#include "msg.h"
|
|
|
|
#include "hooks.h"
|
2015-03-18 16:13:18 +01:00
|
|
|
#include "path.h"
|
|
|
|
#include "utils.h"
|
2015-12-13 00:42:59 +01:00
|
|
|
#include "node.h"
|
2015-06-10 15:10:51 +02:00
|
|
|
|
2015-09-19 15:26:30 +02:00
|
|
|
struct list hooks;
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2016-01-15 15:34:28 +01:00
|
|
|
/* Those references can be used inside the hook callbacks after initializing them with hook_init() */
|
|
|
|
static struct list *hook_nodes = NULL;
|
|
|
|
static struct list *hook_paths = NULL;
|
|
|
|
static struct settings *hook_settings = NULL;
|
|
|
|
|
|
|
|
void hook_init(struct list *nodes, struct list *paths, struct settings *set)
|
|
|
|
{
|
|
|
|
hook_nodes = nodes;
|
|
|
|
hook_paths = paths;
|
|
|
|
hook_settings = set;
|
|
|
|
}
|
|
|
|
|
2016-01-14 22:59:57 +01:00
|
|
|
int hooks_sort_priority(const void *a, const void *b) {
|
|
|
|
struct hook *ha = (struct hook *) a;
|
|
|
|
struct hook *hb = (struct hook *) b;
|
|
|
|
|
|
|
|
return ha->priority - hb->priority;
|
|
|
|
}
|
|
|
|
|
2015-10-09 13:30:41 +02:00
|
|
|
REGISTER_HOOK("print", 99, hook_print, HOOK_MSG)
|
|
|
|
int hook_print(struct path *p, struct hook *h, int when)
|
2015-09-28 19:18:39 +02:00
|
|
|
{
|
2016-01-14 22:59:57 +01:00
|
|
|
struct msg *m = pool_current(&p->pool);
|
2015-11-16 11:24:29 +01:00
|
|
|
double offset = time_delta(&MSG_TS(m), &p->ts.recv);
|
2015-10-09 13:30:41 +02:00
|
|
|
int flags = MSG_PRINT_ALL;
|
|
|
|
|
|
|
|
/* We dont show the offset if its to large */
|
|
|
|
if (offset > 1e9)
|
|
|
|
flags &= ~MSG_PRINT_OFFSET;
|
2015-09-28 19:18:39 +02:00
|
|
|
|
2015-10-09 13:30:41 +02:00
|
|
|
msg_fprint(stdout, m, flags, offset);
|
2015-09-28 19:18:39 +02:00
|
|
|
|
2015-10-09 13:30:41 +02:00
|
|
|
return 0;
|
2015-09-28 19:18:39 +02:00
|
|
|
}
|
|
|
|
|
2015-10-09 13:30:41 +02:00
|
|
|
REGISTER_HOOK("ts", 99, hook_ts, HOOK_MSG)
|
|
|
|
int hook_ts(struct path *p, struct hook *h, int when)
|
2014-08-31 14:43:28 +00:00
|
|
|
{
|
2016-01-14 22:59:57 +01:00
|
|
|
struct msg *m = pool_current(&p->pool);
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-11-16 11:24:29 +01:00
|
|
|
m->ts.sec = p->ts.recv.tv_sec;
|
|
|
|
m->ts.nsec = p->ts.recv.tv_nsec;
|
2014-08-31 14:43:28 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-11-16 10:59:42 +01:00
|
|
|
REGISTER_HOOK("fix_ts", 0, hook_fix_ts, HOOK_INTERNAL | HOOK_MSG)
|
|
|
|
int hook_fix_ts(struct path *p, struct hook *h, int when)
|
|
|
|
{
|
2016-01-14 22:59:57 +01:00
|
|
|
struct msg *m = pool_current(&p->pool);
|
2015-11-16 10:59:42 +01:00
|
|
|
|
|
|
|
if ((m->ts.sec == 0 && m->ts.nsec == 0) ||
|
|
|
|
(m->ts.sec == -1 && m->ts.nsec == -1))
|
|
|
|
hook_ts(p, h, when);
|
2014-08-31 14:43:28 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-09 13:30:41 +02:00
|
|
|
REGISTER_HOOK("skip_unchanged", 99, hook_skip_unchanged, HOOK_PRIVATE | HOOK_ASYNC)
|
|
|
|
int hook_skip_unchanged(struct path *p, struct hook *h, int when)
|
|
|
|
{
|
|
|
|
struct private {
|
|
|
|
double threshold;
|
|
|
|
struct msg previous;
|
2016-01-14 22:59:57 +01:00
|
|
|
} *x = h->private;
|
2015-10-09 13:30:41 +02:00
|
|
|
|
|
|
|
switch (when) {
|
2016-01-15 17:40:17 +01:00
|
|
|
case HOOK_PATH_START:
|
2016-01-14 22:59:57 +01:00
|
|
|
x = h->private = alloc(sizeof(struct private));
|
2015-10-09 13:30:41 +02:00
|
|
|
|
|
|
|
if (!h->parameter)
|
|
|
|
error("Missing parameter for hook 'deduplication'");
|
|
|
|
|
2016-01-14 22:59:57 +01:00
|
|
|
x->threshold = strtof(h->parameter, NULL);
|
|
|
|
if (!x->threshold)
|
2015-10-09 13:30:41 +02:00
|
|
|
error("Failed to parse parameter '%s' for hook 'deduplication'", h->parameter);
|
|
|
|
break;
|
|
|
|
|
2016-01-15 17:40:17 +01:00
|
|
|
case HOOK_PATH_STOP:
|
2016-01-14 22:59:57 +01:00
|
|
|
free(x);
|
2015-10-09 13:30:41 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case HOOK_ASYNC: {
|
|
|
|
int ret = 0;
|
|
|
|
|
2016-01-14 22:59:57 +01:00
|
|
|
struct msg *prev = &x->previous;
|
|
|
|
struct msg *cur = pool_current(&p->pool);
|
2015-10-09 13:30:41 +02:00
|
|
|
|
2016-01-14 22:59:57 +01:00
|
|
|
for (int i = 0; i < MIN(cur->values, prev->values); i++) {
|
|
|
|
if (fabs(cur->data[i].f - prev->data[i].f) > x->threshold)
|
2015-10-09 13:30:41 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = -1; /* no appreciable change in values, we will drop the packet */
|
2015-06-10 15:10:51 +02:00
|
|
|
|
2015-10-09 13:30:41 +02:00
|
|
|
out: memcpy(prev, cur, sizeof(struct msg)); /* save current message for next run */
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-10-09 13:30:41 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-31 14:43:28 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-09 13:30:41 +02:00
|
|
|
REGISTER_HOOK("convert", 99, hook_convert, HOOK_PRIVATE | HOOK_MSG)
|
|
|
|
int hook_convert(struct path *p, struct hook *h, int when)
|
2015-03-18 16:13:18 +01:00
|
|
|
{
|
2015-10-09 13:30:41 +02:00
|
|
|
struct private {
|
|
|
|
enum { TO_FIXED, TO_FLOAT } mode;
|
2016-01-14 22:59:57 +01:00
|
|
|
} *x = h->private;
|
2015-10-09 13:30:41 +02:00
|
|
|
|
|
|
|
switch (when) {
|
2016-01-15 17:40:17 +01:00
|
|
|
case HOOK_PATH_START:
|
2016-01-14 22:59:57 +01:00
|
|
|
x = h->private = alloc(sizeof(struct private));
|
2015-10-09 13:30:41 +02:00
|
|
|
|
|
|
|
if (!h->parameter)
|
|
|
|
error("Missing parameter for hook 'deduplication'");
|
|
|
|
|
|
|
|
if (!strcmp(h->parameter, "fixed"))
|
2016-01-14 22:59:57 +01:00
|
|
|
x->mode = TO_FIXED;
|
2015-10-09 13:30:41 +02:00
|
|
|
else if (!strcmp(h->parameter, "float"))
|
2016-01-14 22:59:57 +01:00
|
|
|
x->mode = TO_FLOAT;
|
2015-10-09 13:30:41 +02:00
|
|
|
else
|
|
|
|
error("Invalid parameter '%s' for hook 'convert'", h->parameter);
|
|
|
|
break;
|
|
|
|
|
2016-01-15 17:40:17 +01:00
|
|
|
case HOOK_PATH_STOP:
|
2016-01-14 22:59:57 +01:00
|
|
|
free(x);
|
2015-10-09 13:30:41 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case HOOK_MSG: {
|
2016-01-14 22:59:57 +01:00
|
|
|
struct msg *m = pool_current(&p->pool);
|
2015-06-10 15:05:36 +02:00
|
|
|
|
2016-01-14 22:59:57 +01:00
|
|
|
for (int i = 0; i < m->values; i++) {
|
|
|
|
switch (x->mode) {
|
2015-10-09 13:30:41 +02:00
|
|
|
/** @todo allow precission to be configured via parameter */
|
|
|
|
case TO_FIXED: m->data[i].i = m->data[i].f * 1e3; break;
|
|
|
|
case TO_FLOAT: m->data[i].f = m->data[i].i; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-03-18 16:13:18 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-09 13:30:41 +02:00
|
|
|
REGISTER_HOOK("fir", 99, hook_fir, HOOK_PRIVATE | HOOK_MSG)
|
|
|
|
int hook_fir(struct path *p, struct hook *h, int when)
|
2014-08-31 14:43:28 +00:00
|
|
|
{
|
2015-10-09 13:30:41 +02:00
|
|
|
/** @todo make this configurable via hook parameters */
|
|
|
|
const static double coeffs[] = HOOK_FIR_COEFFS;
|
2015-12-13 20:30:16 +01:00
|
|
|
char *end;
|
2015-10-09 13:30:41 +02:00
|
|
|
|
|
|
|
struct private {
|
2016-01-14 22:59:57 +01:00
|
|
|
struct pool coeffs;
|
|
|
|
struct pool history;
|
2015-10-09 13:30:41 +02:00
|
|
|
int index;
|
2016-01-14 22:59:57 +01:00
|
|
|
} *x = h->private;
|
2015-09-28 19:53:42 +02:00
|
|
|
|
2015-10-09 13:30:41 +02:00
|
|
|
switch (when) {
|
2016-01-15 17:40:17 +01:00
|
|
|
case HOOK_PATH_START:
|
2015-10-09 13:30:41 +02:00
|
|
|
if (!h->parameter)
|
|
|
|
error("Missing parameter for hook 'fir'");
|
|
|
|
|
2016-01-14 22:59:57 +01:00
|
|
|
x = h->private = alloc(sizeof(struct private));
|
2015-10-09 13:30:41 +02:00
|
|
|
|
2016-01-14 22:59:57 +01:00
|
|
|
pool_create(&x->coeffs, ARRAY_LEN(coeffs), sizeof(double));
|
|
|
|
pool_create(&x->history, ARRAY_LEN(coeffs), sizeof(double));
|
2015-10-09 13:30:41 +02:00
|
|
|
|
2016-01-14 22:59:57 +01:00
|
|
|
/** Fill with static coefficients */
|
|
|
|
memcpy(x->coeffs.buffer, coeffs, sizeof(coeffs));
|
|
|
|
|
|
|
|
x->index = strtol(h->parameter, &end, 10);
|
2015-11-16 11:28:34 +01:00
|
|
|
if (h->parameter == end)
|
2015-10-09 13:30:41 +02:00
|
|
|
error("Invalid parameter '%s' for hook 'fir'", h->parameter);
|
|
|
|
break;
|
|
|
|
|
2016-01-15 17:40:17 +01:00
|
|
|
case HOOK_PATH_STOP:
|
2016-01-14 22:59:57 +01:00
|
|
|
pool_destroy(&x->coeffs);
|
|
|
|
pool_destroy(&x->history);
|
|
|
|
|
|
|
|
free(x);
|
2015-10-09 13:30:41 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case HOOK_MSG: {
|
|
|
|
/* Current value of interest */
|
2016-01-14 22:59:57 +01:00
|
|
|
struct msg *m = pool_current(&p->pool);
|
|
|
|
float *value = &m->data[x->index].f;
|
|
|
|
double *history = pool_current(&x->history);
|
2015-09-28 19:53:42 +02:00
|
|
|
|
2015-10-09 13:30:41 +02:00
|
|
|
/* Save last sample, unfiltered */
|
2016-01-14 22:59:57 +01:00
|
|
|
*history = *value;
|
2015-03-18 16:13:18 +01:00
|
|
|
|
2015-10-09 13:30:41 +02:00
|
|
|
/* Reset accumulator */
|
2016-01-14 22:59:57 +01:00
|
|
|
*value = 0;
|
2015-09-28 19:53:42 +02:00
|
|
|
|
2015-10-09 13:30:41 +02:00
|
|
|
/* FIR loop */
|
2016-01-14 22:59:57 +01:00
|
|
|
for (int i = 0; i < pool_length(&x->coeffs); i++) {
|
|
|
|
double *coeff = pool_get(&x->coeffs, i);
|
|
|
|
double *hist = pool_getrel(&x->history, -i);
|
|
|
|
|
|
|
|
*value += *coeff * *hist;
|
|
|
|
}
|
|
|
|
|
2015-10-09 13:30:41 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2015-03-18 16:13:18 +01:00
|
|
|
|
|
|
|
return 0;
|
2014-08-31 14:43:28 +00:00
|
|
|
}
|
2015-04-01 14:27:52 +02:00
|
|
|
|
2015-10-09 13:30:41 +02:00
|
|
|
REGISTER_HOOK("decimate", 99, hook_decimate, HOOK_PRIVATE | HOOK_POST)
|
|
|
|
int hook_decimate(struct path *p, struct hook *h, int when)
|
2015-04-01 14:27:52 +02:00
|
|
|
{
|
2015-10-09 13:30:41 +02:00
|
|
|
struct private {
|
|
|
|
long ratio;
|
2016-01-14 22:59:57 +01:00
|
|
|
} *x = h->private;
|
2015-10-09 13:30:41 +02:00
|
|
|
|
|
|
|
switch (when) {
|
2016-01-15 17:40:17 +01:00
|
|
|
case HOOK_PATH_START:
|
2015-10-09 13:30:41 +02:00
|
|
|
if (!h->parameter)
|
|
|
|
error("Missing parameter for hook 'decimate'");
|
|
|
|
|
2016-01-14 22:59:57 +01:00
|
|
|
x = h->private = alloc(sizeof(struct private));
|
2015-10-09 13:30:41 +02:00
|
|
|
|
2016-01-14 22:59:57 +01:00
|
|
|
x->ratio = strtol(h->parameter, NULL, 10);
|
|
|
|
if (!x->ratio)
|
2015-10-09 13:30:41 +02:00
|
|
|
error("Invalid parameter '%s' for hook 'decimate'", h->parameter);
|
|
|
|
break;
|
|
|
|
|
2016-01-15 17:40:17 +01:00
|
|
|
case HOOK_PATH_STOP:
|
2016-01-14 22:59:57 +01:00
|
|
|
free(x);
|
2015-10-09 13:30:41 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case HOOK_POST:
|
2016-01-14 22:59:57 +01:00
|
|
|
return p->received % x->ratio;
|
2015-10-09 13:30:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2015-04-01 14:27:52 +02:00
|
|
|
}
|
|
|
|
|
2015-10-09 13:30:41 +02:00
|
|
|
REGISTER_HOOK("skip_first", 99, hook_skip_first, HOOK_PRIVATE | HOOK_POST | HOOK_PATH )
|
|
|
|
int hook_skip_first(struct path *p, struct hook *h, int when)
|
2015-06-10 15:10:51 +02:00
|
|
|
{
|
2015-10-09 13:30:41 +02:00
|
|
|
struct private {
|
|
|
|
double wait; /**< Number of seconds to wait until first message is not skipped */
|
|
|
|
struct timespec started; /**< Timestamp of last simulation restart */
|
2016-01-14 22:59:57 +01:00
|
|
|
} *x = h->private;
|
2015-10-09 13:30:41 +02:00
|
|
|
|
|
|
|
switch (when) {
|
2016-01-15 17:40:17 +01:00
|
|
|
case HOOK_PATH_START:
|
2015-10-09 13:30:41 +02:00
|
|
|
if (!h->parameter)
|
|
|
|
error("Missing parameter for hook 'skip_first'");
|
|
|
|
|
2016-01-14 22:59:57 +01:00
|
|
|
x = h->private = alloc(sizeof(struct private));
|
2015-10-09 13:30:41 +02:00
|
|
|
|
2016-01-15 17:40:17 +01:00
|
|
|
x->started = time_now();
|
2016-01-14 22:59:57 +01:00
|
|
|
x->wait = strtof(h->parameter, NULL);
|
|
|
|
if (!x->wait)
|
2015-10-09 13:30:41 +02:00
|
|
|
error("Invalid parameter '%s' for hook 'skip_first'", h->parameter);
|
|
|
|
break;
|
|
|
|
|
2016-01-15 17:40:17 +01:00
|
|
|
case HOOK_PATH_STOP:
|
2016-01-14 22:59:57 +01:00
|
|
|
free(x);
|
2015-10-09 13:30:41 +02:00
|
|
|
break;
|
2015-06-10 15:10:51 +02:00
|
|
|
|
2015-10-09 13:30:41 +02:00
|
|
|
case HOOK_PATH_RESTART:
|
2016-01-14 22:59:57 +01:00
|
|
|
x->started = p->ts.recv;
|
2015-10-09 13:30:41 +02:00
|
|
|
break;
|
2015-06-10 15:10:51 +02:00
|
|
|
|
2015-10-09 13:30:41 +02:00
|
|
|
case HOOK_POST: {
|
2016-01-14 22:59:57 +01:00
|
|
|
double delta = time_delta(&x->started, &p->ts.recv);
|
|
|
|
return delta < x->wait
|
2015-10-09 13:30:41 +02:00
|
|
|
? -1 /* skip */
|
|
|
|
: 0; /* send */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
REGISTER_HOOK("restart", 1, hook_restart, HOOK_INTERNAL | HOOK_MSG)
|
|
|
|
int hook_restart(struct path *p, struct hook *h, int when)
|
2015-06-10 15:10:51 +02:00
|
|
|
{
|
2016-01-14 22:59:57 +01:00
|
|
|
struct msg *cur = pool_current(&p->pool);
|
|
|
|
struct msg *prev = pool_previous(&p->pool);
|
|
|
|
|
|
|
|
if (cur->sequence == 0 &&
|
|
|
|
prev->sequence <= UINT32_MAX - 32) {
|
2015-06-10 15:10:51 +02:00
|
|
|
warn("Simulation for path %s restarted (prev->seq=%u, current->seq=%u)",
|
2016-01-14 22:59:57 +01:00
|
|
|
path_name(p), prev->sequence, cur->sequence);
|
2015-06-10 15:10:51 +02:00
|
|
|
|
2015-10-09 13:30:41 +02:00
|
|
|
p->sent =
|
|
|
|
p->invalid =
|
|
|
|
p->skipped =
|
|
|
|
p->dropped = 0;
|
|
|
|
p->received = 1;
|
|
|
|
|
|
|
|
if (path_run_hook(p, HOOK_PATH_RESTART))
|
|
|
|
return -1;
|
2015-06-10 15:10:51 +02:00
|
|
|
}
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-06-10 15:10:51 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-09 13:30:41 +02:00
|
|
|
REGISTER_HOOK("verify", 2, hook_verify, HOOK_INTERNAL | HOOK_MSG)
|
|
|
|
int hook_verify(struct path *p, struct hook *h, int when)
|
2015-04-01 14:27:52 +02:00
|
|
|
{
|
2016-01-14 22:59:57 +01:00
|
|
|
struct msg *cur = pool_current(&p->pool);
|
|
|
|
|
|
|
|
int reason = msg_verify(cur);
|
2015-06-10 15:10:51 +02:00
|
|
|
if (reason) {
|
|
|
|
p->invalid++;
|
2015-10-09 13:30:41 +02:00
|
|
|
warn("Received invalid message (reason = %d)", reason);
|
2015-06-10 15:10:51 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-05-06 11:38:57 +02:00
|
|
|
return 0;
|
2015-06-10 15:10:51 +02:00
|
|
|
}
|
|
|
|
|
2015-10-09 13:30:41 +02:00
|
|
|
REGISTER_HOOK("drop", 3, hook_drop, HOOK_INTERNAL | HOOK_MSG)
|
|
|
|
int hook_drop(struct path *p, struct hook *h, int when)
|
2015-06-10 15:10:51 +02:00
|
|
|
{
|
2016-01-14 22:59:57 +01:00
|
|
|
struct msg *cur = pool_current(&p->pool);
|
|
|
|
struct msg *prev = pool_previous(&p->pool);
|
|
|
|
|
|
|
|
int dist = cur->sequence - (int32_t) prev->sequence;
|
2015-06-10 15:10:51 +02:00
|
|
|
if (dist <= 0 && p->received > 1) {
|
|
|
|
p->dropped++;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return 0;
|
2015-08-07 01:11:43 +02:00
|
|
|
}
|
2015-10-09 13:30:41 +02:00
|
|
|
|
|
|
|
REGISTER_HOOK("stats", 2, hook_stats, HOOK_STATS)
|
|
|
|
int hook_stats(struct path *p, struct hook *h, int when)
|
|
|
|
{
|
|
|
|
switch (when) {
|
2016-01-15 17:40:17 +01:00
|
|
|
case HOOK_PATH_START:
|
2015-10-09 13:30:41 +02:00
|
|
|
/** @todo Allow configurable bounds for histograms */
|
2015-11-16 11:28:34 +01:00
|
|
|
hist_create(&p->hist.owd, 0, 1, 100e-3);
|
|
|
|
hist_create(&p->hist.gap_msg, 90e-3, 110e-3, 1e-3);
|
|
|
|
hist_create(&p->hist.gap_recv, 90e-3, 110e-3, 1e-3);
|
|
|
|
hist_create(&p->hist.gap_seq, -HIST_SEQ, +HIST_SEQ, 1);
|
2015-10-09 13:30:41 +02:00
|
|
|
break;
|
|
|
|
|
2015-10-12 16:59:16 +02:00
|
|
|
case HOOK_PRE:
|
|
|
|
/* Exclude first message from statistics */
|
2016-02-07 01:11:22 +01:00
|
|
|
if (p->received > 0)
|
|
|
|
hist_put(&p->hist.gap_recv, time_delta(&p->ts.last, &p->ts.recv));
|
|
|
|
break;
|
2015-10-09 13:30:41 +02:00
|
|
|
|
|
|
|
case HOOK_MSG: {
|
2016-01-14 22:59:57 +01:00
|
|
|
struct msg *cur = pool_current(&p->pool);
|
|
|
|
struct msg *prev = pool_previous(&p->pool);
|
2015-10-09 13:30:41 +02:00
|
|
|
|
2015-10-12 16:59:16 +02:00
|
|
|
/* Exclude first message from statistics */
|
|
|
|
if (p->received > 0) {
|
|
|
|
int dist = cur->sequence - (int32_t) prev->sequence;
|
2015-11-16 11:24:29 +01:00
|
|
|
double delay = time_delta(&MSG_TS(cur), &p->ts.recv);
|
2015-10-12 16:59:16 +02:00
|
|
|
double gap = time_delta(&MSG_TS(prev), &MSG_TS(cur));
|
|
|
|
|
2015-11-16 11:28:34 +01:00
|
|
|
hist_put(&p->hist.gap_msg, gap);
|
|
|
|
hist_put(&p->hist.gap_seq, dist);
|
|
|
|
hist_put(&p->hist.owd, delay);
|
2015-10-12 16:59:16 +02:00
|
|
|
}
|
2015-10-09 13:30:41 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case HOOK_PATH_STOP:
|
2015-11-16 11:28:34 +01:00
|
|
|
if (p->hist.owd.total) { info("One-way delay:"); hist_print(&p->hist.owd); }
|
|
|
|
if (p->hist.gap_recv.total){ info("Inter-message arrival time:"); hist_print(&p->hist.gap_recv); }
|
|
|
|
if (p->hist.gap_msg.total) { info("Inter-message ts gap:"); hist_print(&p->hist.gap_msg); }
|
|
|
|
if (p->hist.gap_seq.total) { info("Inter-message sequence number gaps:"); hist_print(&p->hist.gap_seq); }
|
2016-01-15 17:40:17 +01:00
|
|
|
|
|
|
|
hist_destroy(&p->hist.owd);
|
|
|
|
hist_destroy(&p->hist.gap_msg);
|
|
|
|
hist_destroy(&p->hist.gap_recv);
|
|
|
|
hist_destroy(&p->hist.gap_seq);
|
2015-10-09 13:30:41 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case HOOK_PATH_RESTART:
|
2015-11-16 11:28:34 +01:00
|
|
|
hist_reset(&p->hist.owd);
|
|
|
|
hist_reset(&p->hist.gap_seq);
|
|
|
|
hist_reset(&p->hist.gap_msg);
|
|
|
|
hist_reset(&p->hist.gap_recv);
|
2015-10-09 13:30:41 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case HOOK_PERIODIC: {
|
2016-01-14 22:59:57 +01:00
|
|
|
if (p->received > 1) {
|
|
|
|
struct msg *cur = pool_current(&p->pool);
|
|
|
|
|
2015-11-29 22:45:46 +01:00
|
|
|
stats("%-40.40s|%10.2g|%10.2f|%10u|%10u|%10u|%10u|%10u|%10u|%10u|", path_name(p),
|
2015-11-16 11:28:34 +01:00
|
|
|
p->hist.owd.last, 1 / p->hist.gap_msg.last,
|
2016-01-14 22:59:57 +01:00
|
|
|
p->sent, p->received, p->dropped, p->skipped, p->invalid, p->overrun, cur->values
|
2015-10-12 16:16:25 +02:00
|
|
|
);
|
2016-01-14 22:59:57 +01:00
|
|
|
}
|
2015-10-12 16:16:25 +02:00
|
|
|
else
|
2015-11-29 22:45:46 +01:00
|
|
|
stats("%-40.40s|%10s|%10s|%10u|%10u|%10u|%10u|%10u|%10u|%10s|", path_name(p), "", "",
|
2015-10-12 16:16:25 +02:00
|
|
|
p->sent, p->received, p->dropped, p->skipped, p->invalid, p->overrun, ""
|
|
|
|
);
|
2015-10-09 13:30:41 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-12 16:16:25 +02:00
|
|
|
void hook_stats_header()
|
|
|
|
{
|
2016-01-14 22:59:57 +01:00
|
|
|
#define UNIT(u) "(" YEL(u) ")"
|
2015-10-12 16:16:25 +02:00
|
|
|
|
2015-10-13 12:06:50 +02:00
|
|
|
stats("%-40s|%19s|%19s|%19s|%19s|%19s|%19s|%19s|%10s|%10s|", "Source " MAG("=>") " Destination",
|
2015-10-12 16:16:25 +02:00
|
|
|
"OWD" UNIT("S") " ",
|
|
|
|
"Rate" UNIT("p/S") " ",
|
|
|
|
"Sent" UNIT("p") " ",
|
|
|
|
"Recv" UNIT("p") " ",
|
|
|
|
"Drop" UNIT("p") " ",
|
|
|
|
"Skip" UNIT("p") " ",
|
|
|
|
"Inval" UNIT("p") " ",
|
|
|
|
"Overuns ",
|
|
|
|
"Values "
|
|
|
|
);
|
|
|
|
line();
|
|
|
|
}
|
|
|
|
|
2016-01-14 22:59:57 +01:00
|
|
|
REGISTER_HOOK("stats_send", 99, hook_stats_send, HOOK_PRIVATE | HOOK_PERIODIC)
|
2015-10-09 13:30:41 +02:00
|
|
|
int hook_stats_send(struct path *p, struct hook *h, int when)
|
|
|
|
{
|
|
|
|
struct private {
|
|
|
|
struct node *dest;
|
2016-01-14 22:59:57 +01:00
|
|
|
struct msg *msg;
|
2015-10-09 13:30:41 +02:00
|
|
|
int ratio;
|
2016-01-14 22:59:57 +01:00
|
|
|
} *x = h->private;
|
2015-10-09 13:30:41 +02:00
|
|
|
|
|
|
|
switch (when) {
|
2016-01-15 17:40:17 +01:00
|
|
|
case HOOK_PATH_START:
|
2015-10-09 13:30:41 +02:00
|
|
|
if (!h->parameter)
|
|
|
|
error("Missing parameter for hook 'stats_send'");
|
|
|
|
|
2016-01-15 15:34:28 +01:00
|
|
|
if (!hook_nodes)
|
|
|
|
error("stats_send() hook has no reference to node list");
|
|
|
|
|
2016-01-14 22:59:57 +01:00
|
|
|
x = h->private = alloc(sizeof(struct private));
|
2015-10-09 13:30:41 +02:00
|
|
|
|
2016-01-14 22:59:57 +01:00
|
|
|
x->msg = msg_create(9);
|
2016-01-15 15:34:28 +01:00
|
|
|
x->dest = list_lookup(hook_nodes, h->parameter);
|
2016-01-14 22:59:57 +01:00
|
|
|
if (!x->dest)
|
2015-10-09 13:30:41 +02:00
|
|
|
error("Invalid destination node '%s' for hook 'stats_send'", h->parameter);
|
|
|
|
break;
|
|
|
|
|
2016-01-15 17:40:17 +01:00
|
|
|
case HOOK_PATH_STOP:
|
2016-01-14 22:59:57 +01:00
|
|
|
free(x->msg);
|
|
|
|
free(x);
|
2015-10-09 13:30:41 +02:00
|
|
|
break;
|
|
|
|
|
2016-01-14 22:59:57 +01:00
|
|
|
case HOOK_PERIODIC:
|
|
|
|
x->msg->data[0].f = p->sent;
|
|
|
|
x->msg->data[1].f = p->received;
|
|
|
|
x->msg->data[2].f = p->invalid;
|
|
|
|
x->msg->data[3].f = p->skipped;
|
|
|
|
x->msg->data[4].f = p->dropped;
|
2016-01-15 15:34:28 +01:00
|
|
|
x->msg->data[5].f = p->overrun;
|
|
|
|
x->msg->data[6].f = p->hist.owd.last,
|
|
|
|
x->msg->data[7].f = 1.0 / p->hist.gap_msg.last;
|
|
|
|
x->msg->data[8].f = 1.0 / p->hist.gap_recv.last;
|
2015-10-09 13:30:41 +02:00
|
|
|
|
2016-01-14 22:59:57 +01:00
|
|
|
node_write_single(x->dest, x->msg); /* Send single message with statistics to destination node */
|
2015-10-09 13:30:41 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|