From 6f72bc832cb7ad1c00ebfdf47914793331a7f895 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Wed, 1 Jul 2020 17:02:22 +0200 Subject: [PATCH] api: expose output signals via API --- include/villas/node.h | 13 +++++++++- include/villas/path.h | 3 +++ include/villas/signal.h | 3 +++ lib/api/actions/nodes.cpp | 19 ++++++++++++-- lib/formats/json.cpp | 27 ++------------------ lib/node.cpp | 19 +++++++++++++- lib/path.cpp | 9 +++++++ lib/signal.cpp | 52 ++++++++++++++++++++++++++++++++++++--- 8 files changed, 113 insertions(+), 32 deletions(-) diff --git a/include/villas/node.h b/include/villas/node.h index 52b0a4966..ed7ebc93d 100644 --- a/include/villas/node.h +++ b/include/villas/node.h @@ -71,7 +71,12 @@ struct node { uint64_t sequence; /**< This is a counter of received samples, in case the node-type does not generate sequence numbers itself. */ - std::shared_ptr stats; /**< Statistic counters. This is a pointer to the statistic hooks private data. */ + /** The path which uses this node as a destination. + * Usually every node should be used only by a single path as destination. + * Otherwise samples from different paths would be interleaved. + */ + struct vpath *output_path; + std::shared_ptr stats; /**< Statistic counters. This is a pointer to the statistic hooks private data. */ struct node_direction in, out; @@ -176,6 +181,12 @@ char * node_name(struct node *n); */ char * node_name_long(struct node *n); +/** Return a list of signals which are sent to this node. + * + * This list is derived from the path which uses the node as destination. + */ +struct vlist * node_output_signals(struct node *n); + /** Reverse local and remote socket address. * * @see node_type::reverse diff --git a/include/villas/path.h b/include/villas/path.h index da491a1ea..701f11c84 100644 --- a/include/villas/path.h +++ b/include/villas/path.h @@ -142,6 +142,9 @@ void path_print_stats(struct vpath *p); */ const char * path_name(struct vpath *p); +/** Get a list of signals which is emitted by the path. */ +struct vlist * path_output_signals(struct vpath *n); + /** Reverse a path */ int path_reverse(struct vpath *p, struct vpath *r); diff --git a/include/villas/signal.h b/include/villas/signal.h index d884195c5..a728c6a08 100644 --- a/include/villas/signal.h +++ b/include/villas/signal.h @@ -121,6 +121,7 @@ int signal_list_generate(struct vlist *list, unsigned len, enum SignalType fmt); int signal_list_generate2(struct vlist *list, const char *dt); void signal_list_dump(const struct vlist *list, const union signal_data *data, unsigned len); int signal_list_copy(struct vlist *dst, const struct vlist *src); +json_t * signal_list_to_json(struct vlist *list); enum SignalType signal_type_from_str(const char *str); @@ -140,4 +141,6 @@ int signal_data_parse_str(union signal_data *data, const struct signal *sig, con int signal_data_parse_json(union signal_data *data, const struct signal *sig, json_t *cfg); +json_t * signal_data_to_json(union signal_data *data, const struct signal *sig); + void signal_data_set(union signal_data *data, const struct signal *sig, double val); diff --git a/lib/api/actions/nodes.cpp b/lib/api/actions/nodes.cpp index 9757e3d42..ecc97af61 100644 --- a/lib/api/actions/nodes.cpp +++ b/lib/api/actions/nodes.cpp @@ -48,18 +48,33 @@ public: for (size_t i = 0; i < vlist_length(nodes); i++) { struct node *n = (struct node *) vlist_at(nodes, i); + struct vlist *output_signals; + + json_t *json_node; + json_t *json_signals_in = nullptr; + json_t *json_signals_out = nullptr; + char uuid[37]; uuid_unparse(n->uuid, uuid); - json_t *json_node = json_pack("{ s: s, s: s, s: s, s: i, s: { s: i }, s: { s: i } }", + output_signals = node_output_signals(n); + + json_signals_in = signal_list_to_json(&n->in.signals); + + if (output_signals) + json_signals_out = signal_list_to_json(output_signals); + + json_node = json_pack("{ s: s, s: s, s: s, s: i, s: { s: i, s: o? }, s: { s: i, s: o? } }", "name", node_name_short(n), "uuid", uuid, "state", state_print(n->state), "affinity", n->affinity, "in", "vectorize", n->in.vectorize, + "signals", json_signals_in, "out", - "vectorize", n->out.vectorize + "vectorize", n->out.vectorize, + "signals", json_signals_out ); if (n->stats) diff --git a/lib/formats/json.cpp b/lib/formats/json.cpp index b575cbeaf..51b671de4 100644 --- a/lib/formats/json.cpp +++ b/lib/formats/json.cpp @@ -123,32 +123,9 @@ static int json_pack_sample(struct io *io, json_t **j, struct sample *smp) json_t *json_data = json_array(); for (unsigned i = 0; i < smp->length; i++) { - enum SignalType fmt = sample_format(smp, i); + struct signal *sig = (struct signal *) vlist_at_safe(smp->signals, i); - json_t *json_value; - switch (fmt) { - case SignalType::INTEGER: - json_value = json_integer(smp->data[i].i); - break; - - case SignalType::FLOAT: - json_value = json_real(smp->data[i].f); - break; - - case SignalType::BOOLEAN: - json_value = json_boolean(smp->data[i].b); - break; - - case SignalType::COMPLEX: - json_value = json_pack("{ s: f, s: f }", - "real", std::real(smp->data[i].z), - "imag", std::imag(smp->data[i].z) - ); - break; - - case SignalType::INVALID: - return -1; - } + json_t *json_value = signal_data_to_json(&smp->data[i], sig); json_array_append(json_data, json_value); } diff --git a/lib/node.cpp b/lib/node.cpp index b926a32c8..ca458c481 100644 --- a/lib/node.cpp +++ b/lib/node.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -58,7 +59,11 @@ int node_init(struct node *n, struct node_type *vt) memset(n->_vd, 0, vt->size); - //n->stats = nullptr; + using stats_ptr = std::shared_ptr; + + new (&n->stats) stats_ptr(); + + n->output_path = nullptr; n->name = nullptr; n->_name = nullptr; n->_name_long = nullptr; @@ -409,6 +414,10 @@ int node_destroy(struct node *n) rtnl_cls_put(n->tc_classifier); #endif /* WITH_NETEM */ + using stats_ptr = std::shared_ptr; + + n->stats.~stats_ptr(); + n->state = State::DESTROYED; return 0; @@ -549,6 +558,14 @@ const char * node_name_short(struct node *n) return n->name; } +struct vlist * node_output_signals(struct node *n) +{ + if (n->output_path) + return path_output_signals(n->output_path); + + return nullptr; +} + int node_reverse(struct node *n) { return node_type(n)->reverse ? node_type(n)->reverse(n) : -1; diff --git a/lib/path.cpp b/lib/path.cpp index 39b65c6f9..f48d34ec3 100644 --- a/lib/path.cpp +++ b/lib/path.cpp @@ -403,9 +403,13 @@ int path_parse(struct vpath *p, json_t *cfg, struct vlist *nodes) for (size_t i = 0; i < vlist_length(&destinations); i++) { struct node *n = (struct node *) vlist_at(&destinations, i); + if (!n->output_path) + throw ConfigError(cfg, "node-config-path", "Every node must only be used by a single path as destination"); + auto *pd = new struct vpath_destination; pd->node = n; + pd->node->output_path = p; if (!node_is_enabled(pd->node)) { p->logger->error("Destination {} of path {} is not enabled", node_name(pd->node), path_name(p)); @@ -754,6 +758,11 @@ const char * path_name(struct vpath *p) return p->_name; } +struct vlist * path_output_signals(struct vpath *p) +{ + return &p->signals; +} + int path_uses_node(struct vpath *p, struct node *n) { for (size_t i = 0; i < vlist_length(&p->destinations); i++) { diff --git a/lib/signal.cpp b/lib/signal.cpp index 0db516885..e4d433648 100644 --- a/lib/signal.cpp +++ b/lib/signal.cpp @@ -354,16 +354,37 @@ int signal_list_copy(struct vlist *dst, const struct vlist *src) assert(dst->state == State::INITIALIZED); for (size_t i = 0; i < vlist_length(src); i++) { - struct signal *s = (struct signal *) vlist_at_safe(src, i); + struct signal *sig = (struct signal *) vlist_at_safe(src, i); - signal_incref(s); + signal_incref(sig); - vlist_push(dst, s); + vlist_push(dst, sig); } return 0; } +json_t * signal_list_to_json(struct vlist *list) +{ + json_t *json_signals = json_array(); + + for (size_t i = 0; i < vlist_length(list); i++) { + struct signal *sig = (struct signal *) vlist_at_safe(list, i); + + json_t *json_signal = json_pack("{ s: s?, s: s?, s: s, s: b, s: o }", + "name", sig->name, + "unit", sig->unit, + "type", signal_type_to_str(sig->type), + "enabled", sig->enabled, + "init", signal_data_to_json(&sig->init, sig) + ); + + json_array_append_new(json_signals, json_signal); + } + + return json_signals; +} + /* Signal type */ enum SignalType signal_type_from_str(const char *str) @@ -660,6 +681,31 @@ int signal_data_parse_json(union signal_data *data, const struct signal *sig, js return 0; } +json_t * signal_data_to_json(union signal_data *data, const struct signal *sig) +{ + switch (sig->type) { + case SignalType::INTEGER: + return json_integer(data->i); + + case SignalType::FLOAT: + return json_real(data->f); + + case SignalType::BOOLEAN: + return json_boolean(data->b); + + case SignalType::COMPLEX: + return json_pack("{ s: f, s: f }", + "real", std::real(data->z), + "imag", std::imag(data->z) + ); + + case SignalType::INVALID: + return nullptr; + } + + return nullptr; +} + int signal_data_print_str(const union signal_data *data, const struct signal *sig, char *buf, size_t len) { switch (sig->type) {