mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #19996 from benpicco/topogen_enhance
tools/zep_dispatch: enhancements and fixes
This commit is contained in:
commit
b47771cd5e
46
dist/tools/zep_dispatch/main.c
vendored
46
dist/tools/zep_dispatch/main.c
vendored
@ -131,17 +131,28 @@ static void dispatch_loop(int sock, int tap, dispatch_cb_t dispatch, void *ctx)
|
|||||||
|
|
||||||
static topology_t topology;
|
static topology_t topology;
|
||||||
static const char *graphviz_file = "example.gv";
|
static const char *graphviz_file = "example.gv";
|
||||||
|
static const char *pidfile;
|
||||||
static void _info_handler(int signal)
|
static void _info_handler(int signal)
|
||||||
{
|
{
|
||||||
if (signal != SIGUSR1) {
|
switch (signal) {
|
||||||
return;
|
case SIGUSR1:
|
||||||
}
|
if (topology_print(graphviz_file, &topology)) {
|
||||||
|
fprintf(stderr, "can't open %s\n", graphviz_file);
|
||||||
if (topology_print(graphviz_file, &topology)) {
|
}
|
||||||
fprintf(stderr, "can't open %s\n", graphviz_file);
|
else {
|
||||||
}
|
printf("graph written to %s\n", graphviz_file);
|
||||||
else {
|
}
|
||||||
printf("graph written to %s\n", graphviz_file);
|
break;
|
||||||
|
case SIGUSR2:
|
||||||
|
topology_print_stats(&topology, true);
|
||||||
|
break;
|
||||||
|
case SIGINT:
|
||||||
|
case SIGTERM:
|
||||||
|
if (pidfile) {
|
||||||
|
unlink(pidfile);
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,6 +201,7 @@ static void _print_help(const char *progname)
|
|||||||
|
|
||||||
fprintf(stderr, "\noptional arguments:\n");
|
fprintf(stderr, "\noptional arguments:\n");
|
||||||
fprintf(stderr, "\t-t <file>\tLoad toplogy from file\n");
|
fprintf(stderr, "\t-t <file>\tLoad toplogy from file\n");
|
||||||
|
fprintf(stderr, "\t-p <file>\tStore PID in file\n");
|
||||||
fprintf(stderr, "\t-s <seed>\tRandom seed used to simulate packet loss\n");
|
fprintf(stderr, "\t-s <seed>\tRandom seed used to simulate packet loss\n");
|
||||||
fprintf(stderr, "\t-g <file>\tFile to dump topology as Graphviz visualisation on SIGUSR1\n");
|
fprintf(stderr, "\t-g <file>\tFile to dump topology as Graphviz visualisation on SIGUSR1\n");
|
||||||
fprintf(stderr, "\t-w <interface>\tSend frames to virtual 802.15.4 "
|
fprintf(stderr, "\t-w <interface>\tSend frames to virtual 802.15.4 "
|
||||||
@ -210,7 +222,7 @@ int main(int argc, char **argv)
|
|||||||
.ai_flags = AI_NUMERICHOST,
|
.ai_flags = AI_NUMERICHOST,
|
||||||
};
|
};
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, "t:s:g:w:")) != -1) {
|
while ((c = getopt(argc, argv, "t:s:g:w:p:")) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 't':
|
case 't':
|
||||||
topo_file = optarg;
|
topo_file = optarg;
|
||||||
@ -227,6 +239,9 @@ int main(int argc, char **argv)
|
|||||||
return tap_fd;
|
return tap_fd;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'p':
|
||||||
|
pidfile = optarg;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
_print_help(progname);
|
_print_help(progname);
|
||||||
exit(1);
|
exit(1);
|
||||||
@ -257,6 +272,9 @@ int main(int argc, char **argv)
|
|||||||
if (graphviz_file) {
|
if (graphviz_file) {
|
||||||
signal(SIGUSR1, _info_handler);
|
signal(SIGUSR1, _info_handler);
|
||||||
}
|
}
|
||||||
|
signal(SIGUSR2, _info_handler);
|
||||||
|
signal(SIGTERM, _info_handler);
|
||||||
|
signal(SIGINT, _info_handler);
|
||||||
|
|
||||||
struct addrinfo *server_addr;
|
struct addrinfo *server_addr;
|
||||||
int res = getaddrinfo(argv[0], argv[1],
|
int res = getaddrinfo(argv[0], argv[1],
|
||||||
@ -282,6 +300,14 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
freeaddrinfo(server_addr);
|
freeaddrinfo(server_addr);
|
||||||
|
|
||||||
|
if (pidfile) {
|
||||||
|
FILE *pf = fopen(pidfile, "w");
|
||||||
|
if (pf) {
|
||||||
|
fprintf(pf, "%u", getpid());
|
||||||
|
fclose(pf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (topology.flat) {
|
if (topology.flat) {
|
||||||
dispatch_loop(sock, tap_fd, _send_flat, &topology.nodes);
|
dispatch_loop(sock, tap_fd, _send_flat, &topology.nodes);
|
||||||
}
|
}
|
||||||
|
98
dist/tools/zep_dispatch/topogen.c
vendored
98
dist/tools/zep_dispatch/topogen.c
vendored
@ -13,6 +13,10 @@
|
|||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifndef CONFIG_USE_NUMERIC_NAMES
|
||||||
|
#define CONFIG_USE_NUMERIC_NAMES 1
|
||||||
|
#endif
|
||||||
|
|
||||||
struct node {
|
struct node {
|
||||||
char name[8];
|
char name[8];
|
||||||
int x;
|
int x;
|
||||||
@ -27,6 +31,7 @@ struct world {
|
|||||||
unsigned h;
|
unsigned h;
|
||||||
unsigned num_nodes;
|
unsigned num_nodes;
|
||||||
struct node *nodes;
|
struct node *nodes;
|
||||||
|
bool grid;
|
||||||
};
|
};
|
||||||
|
|
||||||
static unsigned random_range(unsigned lower, unsigned upper)
|
static unsigned random_range(unsigned lower, unsigned upper)
|
||||||
@ -35,13 +40,31 @@ static unsigned random_range(unsigned lower, unsigned upper)
|
|||||||
return lower + rand() % range;
|
return lower + rand() % range;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct node *node_generate(struct node *n, const struct world *w,
|
static struct node *node_generate(struct node *node, const struct world *w,
|
||||||
unsigned range)
|
unsigned range, unsigned idx)
|
||||||
{
|
{
|
||||||
n->x = random_range(0, w->w);
|
if (w->grid) {
|
||||||
n->y = random_range(0, w->h);
|
float width = w->w;
|
||||||
n->r = range;
|
float height = w->h;
|
||||||
return n;
|
float num = w->num_nodes;
|
||||||
|
|
||||||
|
/* https://math.stackexchange.com/a/1039514 */
|
||||||
|
float n_x = sqrtf(num * width/height
|
||||||
|
+ powf(width - height, 2)/(4 * height*height))
|
||||||
|
- (width - height)/2;
|
||||||
|
float n_y = num / n_x;
|
||||||
|
|
||||||
|
unsigned step_x = width / n_x;
|
||||||
|
unsigned step_y = height / n_y;
|
||||||
|
|
||||||
|
node->x = (idx * step_x + step_x / 2) % w->w;
|
||||||
|
node->y = ((idx * step_x + step_x / 2) / w->w) * step_y + step_y / 2;
|
||||||
|
} else {
|
||||||
|
node->x = random_range(0, w->w);
|
||||||
|
node->y = random_range(0, w->h);
|
||||||
|
}
|
||||||
|
node->r = range;
|
||||||
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
static double node_distance(const struct node *a, const struct node *b)
|
static double node_distance(const struct node *a, const struct node *b)
|
||||||
@ -51,7 +74,7 @@ static double node_distance(const struct node *a, const struct node *b)
|
|||||||
|
|
||||||
static double node_distance_weight(const struct node *a, const struct node *b)
|
static double node_distance_weight(const struct node *a, const struct node *b)
|
||||||
{
|
{
|
||||||
double w = 1 - node_distance(a, b) / a->r;
|
double w = 1 - pow(node_distance(a, b), 2) / pow(a->r, 2);
|
||||||
|
|
||||||
if (w < 0) {
|
if (w < 0) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -61,13 +84,18 @@ static double node_distance_weight(const struct node *a, const struct node *b)
|
|||||||
|
|
||||||
static void node_name(struct node *n, unsigned idx)
|
static void node_name(struct node *n, unsigned idx)
|
||||||
{
|
{
|
||||||
|
if (CONFIG_USE_NUMERIC_NAMES) {
|
||||||
|
snprintf(n->name, sizeof(n->name), "n%03u", (uint16_t)idx + 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
char *s = n->name;
|
char *s = n->name;
|
||||||
const char *end = s + sizeof(n->name) - 1;
|
const char *end = s + sizeof(n->name) - 1;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
uint8_t rem = idx % 26;
|
uint8_t rem = idx % 26;
|
||||||
|
idx /= 26;
|
||||||
*s++ = 'A' + rem;
|
*s++ = 'A' + rem;
|
||||||
idx -= rem;
|
|
||||||
} while (idx && s != end);
|
} while (idx && s != end);
|
||||||
*s = 0;
|
*s = 0;
|
||||||
}
|
}
|
||||||
@ -82,9 +110,15 @@ static void world_gen(struct world *w, unsigned num_nodes,
|
|||||||
w->nodes = calloc(num_nodes, sizeof(*w->nodes));
|
w->nodes = calloc(num_nodes, sizeof(*w->nodes));
|
||||||
|
|
||||||
for (unsigned i = 0; i < num_nodes; ++i) {
|
for (unsigned i = 0; i < num_nodes; ++i) {
|
||||||
node_generate(&w->nodes[i], w, random_range(range - var, range + var));
|
node_generate(&w->nodes[i], w, random_range(range - var, range + var), i);
|
||||||
node_name(&w->nodes[i], i);
|
node_name(&w->nodes[i], i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!w->grid) {
|
||||||
|
/* place first node at origin */
|
||||||
|
w->nodes[0].x = 0;
|
||||||
|
w->nodes[0].y = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned _color(const struct node *n, unsigned base)
|
static unsigned _color(const struct node *n, unsigned base)
|
||||||
@ -154,22 +188,35 @@ static void _calc_connections(struct node *nodes, unsigned num)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _print_distance(struct node *nodes, unsigned num, bool recursive)
|
static void _print_distance(struct node *nodes, unsigned num, bool recursive, bool binary)
|
||||||
{
|
{
|
||||||
struct node *start = nodes;
|
struct node *start = nodes;
|
||||||
|
|
||||||
|
if (recursive) {
|
||||||
|
for (unsigned i = 0; i < num; ++i) {
|
||||||
|
printf("%s\n", nodes[i].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (unsigned i = 1; i < num; ++i) {
|
for (unsigned i = 1; i < num; ++i) {
|
||||||
struct node *n = &nodes[i];
|
struct node *n = &nodes[i];
|
||||||
|
|
||||||
double to_node = node_distance_weight(start, n);
|
double to_node = node_distance_weight(start, n);
|
||||||
double from_node = node_distance_weight(n, start);
|
double from_node = node_distance_weight(n, start);
|
||||||
|
|
||||||
if (to_node > 0 || from_node > 0) {
|
if (binary) {
|
||||||
|
if (to_node >= 0.5) {
|
||||||
|
printf("%s\t%s\n", start->name, n->name);
|
||||||
|
}
|
||||||
|
if (from_node >= 0.5) {
|
||||||
|
printf("%s\t%s\n", n->name, start->name);
|
||||||
|
}
|
||||||
|
} else if (to_node > 0 || from_node > 0) {
|
||||||
printf("%s\t%s\t%.2f\t%.2f\n", start->name, n->name, to_node, from_node);
|
printf("%s\t%s\t%.2f\t%.2f\n", start->name, n->name, to_node, from_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recursive) {
|
if (recursive) {
|
||||||
_print_distance(n, num - i, false);
|
_print_distance(n, num - i, false, binary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -183,7 +230,18 @@ static void _print_help(const char *name)
|
|||||||
" [-r <range>]"
|
" [-r <range>]"
|
||||||
" [-v <variance of range>]"
|
" [-v <variance of range>]"
|
||||||
" [-n <nodes>]"
|
" [-n <nodes>]"
|
||||||
|
" [-b][-g]"
|
||||||
"\n", name);
|
"\n", name);
|
||||||
|
|
||||||
|
puts("\nOptions:");
|
||||||
|
puts("\t-s <seed>\trandom seed used for topology generation");
|
||||||
|
puts("\t-w <width>\twidth of the 2D topology");
|
||||||
|
puts("\t-h <height>\theight of the 2D topology");
|
||||||
|
puts("\t-r <range>\tRadio range of the nodes");
|
||||||
|
puts("\t-v <variance>\tmaximal random variance of radio range");
|
||||||
|
puts("\t-n <nodes>\tnumber of nodes in the topology");
|
||||||
|
puts("\t-b\t\tbinary links: link quality is rounded to 100% or 0%");
|
||||||
|
puts("\t-g\t\tnodes are organized as a grid");
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
@ -196,10 +254,18 @@ int main(int argc, char** argv)
|
|||||||
unsigned range = 25;
|
unsigned range = 25;
|
||||||
unsigned var = 0;
|
unsigned var = 0;
|
||||||
unsigned num = 10;
|
unsigned num = 10;
|
||||||
|
bool binary = false;
|
||||||
|
bool grid = false;
|
||||||
char c;
|
char c;
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, "s:w:h:r:v:n:")) != -1) {
|
while ((c = getopt(argc, argv, "s:w:h:r:v:n:bg")) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
case 'b':
|
||||||
|
binary = true;
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
grid = true;
|
||||||
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
seed = atoi(optarg);
|
seed = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
@ -226,12 +292,14 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
srand(seed);
|
srand(seed);
|
||||||
|
|
||||||
struct world w;
|
struct world w = {
|
||||||
|
.grid = grid,
|
||||||
|
};
|
||||||
world_gen(&w, num, width, height, range, var);
|
world_gen(&w, num, width, height, range, var);
|
||||||
|
|
||||||
printf("# seed = %u\n", seed);
|
printf("# seed = %u\n", seed);
|
||||||
puts("# Connections");
|
puts("# Connections");
|
||||||
_print_distance(w.nodes, w.num_nodes, true);
|
_print_distance(w.nodes, w.num_nodes, true, binary);
|
||||||
puts("");
|
puts("");
|
||||||
puts("");
|
puts("");
|
||||||
puts("# Node\tX\tY\trange\tcolor");
|
puts("# Node\tX\tY\trange\tcolor");
|
||||||
|
53
dist/tools/zep_dispatch/topology.c
vendored
53
dist/tools/zep_dispatch/topology.c
vendored
@ -26,6 +26,8 @@ struct node {
|
|||||||
char name[NODE_NAME_MAX_LEN];
|
char name[NODE_NAME_MAX_LEN];
|
||||||
uint8_t mac[HW_ADDR_MAX_LEN];
|
uint8_t mac[HW_ADDR_MAX_LEN];
|
||||||
struct sockaddr_in6 addr;
|
struct sockaddr_in6 addr;
|
||||||
|
uint32_t num_tx;
|
||||||
|
uint32_t num_rx;
|
||||||
uint8_t mac_len;
|
uint8_t mac_len;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -88,15 +90,20 @@ static bool _parse_line(char *line, list_node_t *nodes, list_node_t *edges)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *a = strtok(line, "\t ");
|
char *a = strtok(line, "\n\t ");
|
||||||
char *b = strtok(NULL, "\n\t ");
|
char *b = strtok(NULL, "\n\t ");
|
||||||
char *e_ab = strtok(NULL, "\n\t ");
|
char *e_ab = strtok(NULL, "\n\t ");
|
||||||
char *e_ba = strtok(NULL, "\n\t ");
|
char *e_ba = strtok(NULL, "\n\t ");
|
||||||
|
|
||||||
if (a == NULL || b == NULL) {
|
if (a == NULL) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (b == NULL) {
|
||||||
|
_find_or_create_node(nodes, a);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* add node with a defined MAC address */
|
/* add node with a defined MAC address */
|
||||||
if (strcmp(b, ":=") == 0) {
|
if (strcmp(b, ":=") == 0) {
|
||||||
struct node *n = _find_or_create_node(nodes, a);
|
struct node *n = _find_or_create_node(nodes, a);
|
||||||
@ -182,6 +189,26 @@ int topology_print(const char *file, const topology_t *t)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void topology_print_stats(const topology_t *t, bool reset)
|
||||||
|
{
|
||||||
|
uint32_t tx_total = 0;
|
||||||
|
|
||||||
|
puts("{ nodes: [");
|
||||||
|
for (list_node_t *node = t->nodes.next; node; node = node->next) {
|
||||||
|
struct node *super = container_of(node, struct node, next);
|
||||||
|
|
||||||
|
tx_total += super->num_tx;
|
||||||
|
|
||||||
|
printf("\t{ name: %s, tx: %u, rx: %u }%c\n",
|
||||||
|
super->name, super->num_tx, super->num_rx, node->next ? ',' : ' ');
|
||||||
|
if (reset) {
|
||||||
|
super->num_tx = 0;
|
||||||
|
super->num_rx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("], tx_total: %u }\n", tx_total);
|
||||||
|
}
|
||||||
|
|
||||||
int topology_parse(const char *file, topology_t *out)
|
int topology_parse(const char *file, topology_t *out)
|
||||||
{
|
{
|
||||||
FILE *in;
|
FILE *in;
|
||||||
@ -217,6 +244,8 @@ void topology_send(const topology_t *t, int sock,
|
|||||||
const struct sockaddr_in6 *src_addr,
|
const struct sockaddr_in6 *src_addr,
|
||||||
void *buffer, size_t len)
|
void *buffer, size_t len)
|
||||||
{
|
{
|
||||||
|
struct node *sender = NULL;
|
||||||
|
|
||||||
if (t->has_sniffer) {
|
if (t->has_sniffer) {
|
||||||
sendto(sock, buffer, len, 0,
|
sendto(sock, buffer, len, 0,
|
||||||
(struct sockaddr *)&t->sniffer_addr, sizeof(t->sniffer_addr));
|
(struct sockaddr *)&t->sniffer_addr, sizeof(t->sniffer_addr));
|
||||||
@ -230,24 +259,36 @@ void topology_send(const topology_t *t, int sock,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (memcmp(&super->a->addr, src_addr, sizeof(*src_addr)) == 0) {
|
if (memcmp(&super->a->addr, src_addr, sizeof(*src_addr)) == 0) {
|
||||||
|
if (sender == NULL) {
|
||||||
|
sender = super->a;
|
||||||
|
sender->num_tx++;
|
||||||
|
}
|
||||||
|
|
||||||
/* packet loss */
|
/* packet loss */
|
||||||
if (random() > super->weight_a_b * RAND_MAX) {
|
if (random() > super->weight_a_b * RAND_MAX) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
zep_set_lqi(buffer, super->weight_a_b * 0xFF);
|
zep_set_lqi(buffer, super->weight_a_b * 0xFF);
|
||||||
sendto(sock, buffer, len, 0,
|
sendto(sock, buffer, len, 0,
|
||||||
(struct sockaddr *)&super->b->addr,
|
(struct sockaddr *)&super->b->addr,
|
||||||
sizeof(super->b->addr));
|
sizeof(super->b->addr));
|
||||||
|
super->b->num_rx++;
|
||||||
}
|
}
|
||||||
else if (memcmp(&super->b->addr, src_addr, sizeof(*src_addr)) == 0) {
|
else if (memcmp(&super->b->addr, src_addr, sizeof(*src_addr)) == 0) {
|
||||||
|
if (sender == NULL) {
|
||||||
|
sender = super->b;
|
||||||
|
sender->num_tx++;
|
||||||
|
}
|
||||||
|
|
||||||
/* packet loss */
|
/* packet loss */
|
||||||
if (random() > super->weight_b_a * RAND_MAX) {
|
if (random() > super->weight_b_a * RAND_MAX) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
zep_set_lqi(buffer, super->weight_b_a * 0xFF);
|
zep_set_lqi(buffer, super->weight_b_a * 0xFF);
|
||||||
sendto(sock, buffer, len, 0,
|
sendto(sock, buffer, len, 0,
|
||||||
(struct sockaddr *)&super->a->addr,
|
(struct sockaddr *)&super->a->addr,
|
||||||
sizeof(super->a->addr));
|
sizeof(super->a->addr));
|
||||||
|
super->a->num_rx++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -296,7 +337,9 @@ bool topology_add(topology_t *t, const uint8_t *mac, uint8_t mac_len,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("adding node %s\n", _fmt_addr(addr_str, sizeof(addr_str), mac, mac_len));
|
printf("adding node %s as %s\n",
|
||||||
|
_fmt_addr(addr_str, sizeof(addr_str), mac, mac_len),
|
||||||
|
(char *)empty->name);
|
||||||
|
|
||||||
/* add new node to empty spot */
|
/* add new node to empty spot */
|
||||||
memcpy(empty->mac, mac, sizeof(empty->mac));
|
memcpy(empty->mac, mac, sizeof(empty->mac));
|
||||||
|
7
dist/tools/zep_dispatch/topology.h
vendored
7
dist/tools/zep_dispatch/topology.h
vendored
@ -48,6 +48,13 @@ int topology_parse(const char *file, topology_t *out);
|
|||||||
*/
|
*/
|
||||||
int topology_print(const char *file_out, const topology_t *t);
|
int topology_print(const char *file_out, const topology_t *t);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Print send / receive statistics
|
||||||
|
*
|
||||||
|
* @param[in] t The topology to render
|
||||||
|
*/
|
||||||
|
void topology_print_stats(const topology_t *t, bool reset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Populate a spot in the topology with a connected node
|
* @brief Populate a spot in the topology with a connected node
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user