2014-07-14 11:49:44 +00:00
|
|
|
/** Message paths.
|
2014-06-05 09:34:29 +00:00
|
|
|
*
|
|
|
|
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
|
|
|
* @copyright 2014, Institute for Automation of Complex Power Systems, EONERC
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
2014-06-25 17:50:30 +00:00
|
|
|
#include <unistd.h>
|
2014-06-05 09:34:36 +00:00
|
|
|
#include <errno.h>
|
2014-06-25 17:50:30 +00:00
|
|
|
#include <signal.h>
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
#include <sys/syscall.h>
|
2014-06-05 09:34:29 +00:00
|
|
|
|
|
|
|
#include "utils.h"
|
|
|
|
#include "path.h"
|
|
|
|
|
2014-06-25 17:50:30 +00:00
|
|
|
#define sigev_notify_thread_id _sigev_un._tid
|
|
|
|
|
2014-12-05 12:39:52 +01:00
|
|
|
/** Linked list of paths */
|
|
|
|
struct path *paths;
|
|
|
|
|
2014-06-25 17:50:30 +00:00
|
|
|
/** Send messages */
|
|
|
|
static void * path_send(void *arg)
|
|
|
|
{
|
2014-07-04 15:58:10 +00:00
|
|
|
int sig;
|
2014-06-25 17:50:30 +00:00
|
|
|
struct path *p = (struct path *) arg;
|
|
|
|
timer_t tmr;
|
|
|
|
sigset_t set;
|
|
|
|
|
|
|
|
struct sigevent sev = {
|
|
|
|
.sigev_notify = SIGEV_THREAD_ID,
|
|
|
|
.sigev_signo = SIGALRM,
|
|
|
|
.sigev_notify_thread_id = syscall(SYS_gettid)
|
|
|
|
};
|
|
|
|
|
|
|
|
struct itimerspec its = {
|
|
|
|
.it_interval = timespec_rate(p->rate),
|
|
|
|
.it_value = { 1, 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
sigemptyset(&set);
|
|
|
|
sigaddset(&set, SIGALRM);
|
|
|
|
if(pthread_sigmask(SIG_BLOCK, &set, NULL))
|
|
|
|
perror("Set signal mask");
|
|
|
|
|
|
|
|
if (timer_create(CLOCK_REALTIME, &sev, &tmr))
|
|
|
|
perror("Failed to create timer");
|
|
|
|
|
|
|
|
if (timer_settime(tmr, 0, &its, NULL))
|
|
|
|
perror("Failed to start timer");
|
|
|
|
|
|
|
|
while (1) {
|
2014-12-05 12:39:52 +01:00
|
|
|
sigwait(&set, &sig); /* blocking wait for next timer tick */
|
2014-07-04 15:58:10 +00:00
|
|
|
if (p->last) {
|
2014-12-05 12:39:52 +01:00
|
|
|
node_write(p->out, p->last);
|
2014-07-04 15:58:10 +00:00
|
|
|
p->last = NULL;
|
|
|
|
p->sent++;
|
|
|
|
}
|
2014-06-25 17:50:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Receive messages */
|
2014-06-05 09:34:29 +00:00
|
|
|
static void * path_run(void *arg)
|
|
|
|
{
|
|
|
|
struct path *p = (struct path *) arg;
|
2014-07-18 16:05:44 +00:00
|
|
|
struct msg *m = malloc(sizeof(struct msg));
|
|
|
|
if (!m)
|
2014-09-11 14:40:54 +00:00
|
|
|
error("Failed to allocate memory for message!");
|
2014-12-05 12:39:52 +01:00
|
|
|
|
|
|
|
/* Open deferred TCP connection */
|
|
|
|
node_start_defer(p->in);
|
|
|
|
node_start_defer(p->out);
|
2014-06-05 09:34:29 +00:00
|
|
|
|
2014-06-25 17:50:30 +00:00
|
|
|
/* Main thread loop */
|
2014-06-05 09:35:04 +00:00
|
|
|
while (1) {
|
2014-12-05 12:39:52 +01:00
|
|
|
node_read(p->in, m); /* Receive message */
|
|
|
|
|
2014-09-11 14:40:58 +00:00
|
|
|
p->received++;
|
|
|
|
|
|
|
|
/* Check header fields */
|
|
|
|
if (m->version != MSG_VERSION ||
|
|
|
|
m->type != MSG_TYPE_DATA) {
|
|
|
|
p->invalid++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update histogram */
|
|
|
|
int dist = (UINT16_MAX + m->sequence - p->sequence) % UINT16_MAX;
|
|
|
|
if (dist > UINT16_MAX / 2)
|
|
|
|
dist -= UINT16_MAX;
|
2014-09-09 09:03:06 +00:00
|
|
|
|
2014-09-11 14:40:58 +00:00
|
|
|
int idx = HIST_SEQ / 2 + dist;
|
|
|
|
if (idx < HIST_SEQ && idx >= 0)
|
|
|
|
p->histogram[idx]++;
|
|
|
|
|
|
|
|
/* Handle simulation restart */
|
2014-09-11 15:09:32 +00:00
|
|
|
if (m->sequence == 0 && abs(dist) > 16) {
|
2014-09-09 13:42:36 +00:00
|
|
|
path_stats(p);
|
2014-09-11 14:40:58 +00:00
|
|
|
warn("Simulation for path %s " MAG("=>") " %s "
|
|
|
|
"restarted (p->seq=%u, m->seq=%u, dist=%d)",
|
|
|
|
p->in->name, p->out->name,
|
|
|
|
p->sequence, m->sequence, dist);
|
2014-09-09 13:42:36 +00:00
|
|
|
|
2014-09-10 12:24:49 +00:00
|
|
|
/* Reset counters */
|
2014-09-09 13:42:36 +00:00
|
|
|
p->sent = 0;
|
2014-09-11 15:09:30 +00:00
|
|
|
p->received = 1;
|
2014-09-09 13:42:36 +00:00
|
|
|
p->invalid = 0;
|
|
|
|
p->skipped = 0;
|
|
|
|
p->dropped = 0;
|
2014-09-10 12:24:49 +00:00
|
|
|
|
|
|
|
/* Reset sequence no tracking */
|
|
|
|
memset(p->histogram, 0, sizeof(p->histogram));
|
2014-09-09 13:42:36 +00:00
|
|
|
}
|
2014-09-11 14:40:58 +00:00
|
|
|
else if (dist <= 0 && p->received > 1) {
|
2014-09-09 09:03:06 +00:00
|
|
|
p->dropped++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-09-11 14:40:58 +00:00
|
|
|
/* Call hook callbacks */
|
2014-09-09 09:03:06 +00:00
|
|
|
if (p->hook && p->hook(m, p)) {
|
|
|
|
p->skipped++;
|
2014-07-04 15:58:11 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-09-09 09:03:06 +00:00
|
|
|
/* Update last known sequence number */
|
2014-09-08 14:45:46 +00:00
|
|
|
p->sequence = m->sequence;
|
2014-09-09 09:03:06 +00:00
|
|
|
p->last = m;
|
2014-09-08 14:45:46 +00:00
|
|
|
|
2014-06-25 17:50:30 +00:00
|
|
|
/* At fixed rate mode, messages are send by another thread */
|
2014-09-09 09:03:06 +00:00
|
|
|
if (!p->rate) {
|
2014-12-05 12:39:52 +01:00
|
|
|
node_write(p->out, m); /* Send message */
|
2014-09-04 13:30:38 +00:00
|
|
|
p->sent++;
|
2014-09-09 09:03:06 +00:00
|
|
|
}
|
2014-06-05 09:34:29 +00:00
|
|
|
}
|
|
|
|
|
2014-07-18 16:05:44 +00:00
|
|
|
free(m);
|
|
|
|
|
2014-06-05 09:34:29 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int path_start(struct path *p)
|
2014-12-05 12:39:52 +01:00
|
|
|
{ INDENT
|
|
|
|
info("Starting path: %12s " GRN("=>") " %-12s", p->in->name, p->out->name);
|
|
|
|
|
2014-06-25 17:50:30 +00:00
|
|
|
/* At fixed rate mode, we start another thread for sending */
|
|
|
|
if (p->rate)
|
2014-09-09 09:03:01 +00:00
|
|
|
pthread_create(&p->sent_tid, NULL, &path_send, (void *) p);
|
2014-06-25 17:50:30 +00:00
|
|
|
|
2014-09-09 09:03:01 +00:00
|
|
|
return pthread_create(&p->recv_tid, NULL, &path_run, (void *) p);
|
2014-06-05 09:34:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int path_stop(struct path *p)
|
2014-12-05 12:39:52 +01:00
|
|
|
{ INDENT
|
|
|
|
info("Stopping path: %12s " RED("=>") " %-12s", p->in->name, p->out->name);
|
|
|
|
|
2014-09-09 09:03:01 +00:00
|
|
|
pthread_cancel(p->recv_tid);
|
|
|
|
pthread_join(p->recv_tid, NULL);
|
2014-06-25 17:50:30 +00:00
|
|
|
|
|
|
|
if (p->rate) {
|
2014-09-09 09:03:01 +00:00
|
|
|
pthread_cancel(p->sent_tid);
|
|
|
|
pthread_join(p->sent_tid, NULL);
|
2014-06-25 17:50:30 +00:00
|
|
|
}
|
2014-06-05 09:34:29 +00:00
|
|
|
|
2014-09-09 09:03:11 +00:00
|
|
|
if (p->received) {
|
|
|
|
path_stats(p);
|
2014-09-09 11:11:29 +00:00
|
|
|
hist_plot(p->histogram, HIST_SEQ);
|
2014-09-09 09:03:11 +00:00
|
|
|
hist_dump(p->histogram, HIST_SEQ);
|
|
|
|
}
|
|
|
|
|
2014-06-05 09:35:04 +00:00
|
|
|
return 0;
|
2014-06-05 09:34:29 +00:00
|
|
|
}
|
2014-07-04 09:47:28 +00:00
|
|
|
|
|
|
|
void path_stats(struct path *p)
|
|
|
|
{
|
2014-07-04 15:58:10 +00:00
|
|
|
info("%12s " MAG("=>") " %-12s: %-8u %-8u %-8u %-8u %-8u",
|
2014-07-04 09:47:28 +00:00
|
|
|
p->in->name, p->out->name,
|
2014-09-09 09:03:06 +00:00
|
|
|
p->sent, p->received, p->dropped, p->skipped, p->invalid
|
2014-07-04 15:58:10 +00:00
|
|
|
);
|
2014-07-04 09:47:28 +00:00
|
|
|
}
|