diff --git a/include/villas/node/config.h.in b/include/villas/node/config.h.in index ba347672c..d3764d56b 100644 --- a/include/villas/node/config.h.in +++ b/include/villas/node/config.h.in @@ -54,6 +54,7 @@ #cmakedefine WITH_API #cmakedefine WITH_HOOKS #cmakedefine WITH_CONFIG +#cmakedefine WITH_GRAPHVIZ /* OS Headers */ #cmakedefine HAS_EVENTFD diff --git a/include/villas/super_node.hpp b/include/villas/super_node.hpp index 6ad16b41f..f54c33faf 100644 --- a/include/villas/super_node.hpp +++ b/include/villas/super_node.hpp @@ -23,6 +23,14 @@ #pragma once +#include + +#ifdef WITH_GRAPHVIZ +#include +#endif + +#include + #include #include #include @@ -108,6 +116,10 @@ public: void stopNodeTypes(); void stopInterfaces(); +#ifdef WITH_GRAPHVIZ + graph_t * getGraph(); +#endif + /** Run periodic hooks of this super node. */ int periodic(); diff --git a/lib/super_node.cpp b/lib/super_node.cpp index 997471c90..ad8337011 100644 --- a/lib/super_node.cpp +++ b/lib/super_node.cpp @@ -532,3 +532,20 @@ int SuperNode::periodic() return 0; } + +#ifdef WITH_GRAPHVIZ +graph_t * SuperNode::getGraph() +{ + /* Create a simple digraph */ + Agraph_t *g; + Agnode_t *n, *m; + Agedge_t *e; + + g = agopen("g", Agdirected, 0); + n = agnode(g, "n", 1); + m = agnode(g, "m", 1); + e = agedge(g, n, m, 0, 1); + + return g; +} +#endif /* WITH_GRAPHVIZ */ diff --git a/packaging/docker/Dockerfile.dev b/packaging/docker/Dockerfile.dev index 1a51bef6b..cba8c13b5 100644 --- a/packaging/docker/Dockerfile.dev +++ b/packaging/docker/Dockerfile.dev @@ -67,6 +67,7 @@ RUN pip install \ # Dependencies RUN dnf -y install \ openssl-devel \ + graphviz-devel \ protobuf-devel \ protobuf-c-devel \ libuuid-devel \ diff --git a/packaging/docker/Dockerfile.dev-centos b/packaging/docker/Dockerfile.dev-centos index e69309d42..80e4765e3 100644 --- a/packaging/docker/Dockerfile.dev-centos +++ b/packaging/docker/Dockerfile.dev-centos @@ -53,6 +53,7 @@ RUN dnf -y install \ # Dependencies RUN dnf -y install \ openssl-devel \ + graphviz-devel \ protobuf-devel \ protobuf-c-devel \ libuuid-devel \ diff --git a/packaging/docker/Dockerfile.dev-debian-multiarch b/packaging/docker/Dockerfile.dev-debian-multiarch index 8df87e819..0f89f593e 100644 --- a/packaging/docker/Dockerfile.dev-debian-multiarch +++ b/packaging/docker/Dockerfile.dev-debian-multiarch @@ -58,6 +58,7 @@ RUN apt-get update && \ RUN apt-get update && \ apt-get install -y \ libssl-dev:${ARCH} \ + libgraphviz-dev:${ARCH} \ libprotobuf-dev:${ARCH} \ libprotobuf-c-dev:${ARCH} \ uuid-dev:${ARCH} \ diff --git a/packaging/docker/Dockerfile.dev-ubuntu b/packaging/docker/Dockerfile.dev-ubuntu index 870f58eaf..9e6a271bc 100644 --- a/packaging/docker/Dockerfile.dev-ubuntu +++ b/packaging/docker/Dockerfile.dev-ubuntu @@ -52,6 +52,7 @@ RUN apt-get update && \ RUN apt-get update && \ apt-get install -y \ libssl-dev \ + libgraphviz-dev \ libprotobuf-dev \ libprotobuf-c-dev \ uuid-dev \ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 65f7fb8bb..d629ccf47 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -51,6 +51,11 @@ target_link_libraries(villas-pipe PUBLIC villas Threads::Threads) add_executable(villas-signal villas-signal.cpp) target_link_libraries(villas-signal PUBLIC villas) +if(WITH_GRAPHVIZ) + add_executable(villas-graph villas-graph.cpp) + target_link_libraries(villas-graph PUBLIC villas) +endif() + if(WITH_WEB) add_executable(villas-relay villas-relay.cpp) target_include_directories(villas-relay PRIVATE ${LIBWEBSOCKETS_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR}) diff --git a/src/villas-graph.cpp b/src/villas-graph.cpp new file mode 100644 index 000000000..d61451725 --- /dev/null +++ b/src/villas-graph.cpp @@ -0,0 +1,158 @@ +/** Create a graph representation of the VILLASnode configuration file + * + * @file + * @author Steffen Vogel + * @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC + * @license GNU General Public License (version 3) + * + * VILLASnode + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * @addtogroup tools Test and debug tools + * @{ + *********************************************************************************/ + +#include + +#include +#include + +#include +#include +#include + +using namespace villas; + +struct GVC_s { + GVCOMMON_t common; + + char *config_path; + boolean config_found; + + /* gvParseArgs */ + char **input_filenames; +}; + +namespace villas { +namespace node { +namespace tools { + +class Graph : public Tool { + +public: + Graph(int argc, char *argv[]) : + Tool(argc, argv, "graph", { SIGUSR1, SIGINT }), + gvc(nullptr), + graph(nullptr) + { + int ret; + + ret = memory_init(DEFAULT_NR_HUGEPAGES); + if (ret) + throw RuntimeError("Failed to initialize memory"); + + this->argv[0] = (char *) "neato"; /* Default layout engine */ + + gvc = gvContext(); + } + + ~Graph() + { + gvFreeContext(gvc); + } + +protected: + GVC_t *gvc; + graph_t *graph; + + std::string configFilename; + + void usage() + { + std::cout << "Usage: villas-graph [OPTIONS]" << std::endl + << "For OPTIONS see dot(1)."; + + printCopyright(); + } + + void parse() + { + gvParseArgs(gvc, argc, argv); + + std::list filenames; + int i; + for (i = 0; gvc->input_filenames[i]; i++) + filenames.emplace_back(gvc->input_filenames[i]); + + if (i == 0) + throw RuntimeError("No configuration file given!"); + + configFilename = filenames.front(); + } + + virtual void handler(int signal, siginfo_t *siginfp, void *) + { +#ifndef _WIN32 + switch (signal) { + case SIGINT: + /* If interrupted we try to produce a partial rendering before exiting */ + if (graph) + gvRenderJobs(gvc, graph); + break; + + + case SIGUSR1: + /* Note that we don't call gvFinalize() so that we don't start event-driven + * devices like -Tgtk or -Txlib */ + exit(gvFreeContext(gvc)); + break; + + default: { } + } +#endif /* _WIN32 */ + } + + int main() + { + int ret; + + villas::node::SuperNode sn; + + sn.parse(configFilename); + sn.check(); + sn.prepare(); + + graph = sn.getGraph(); + + ret = gvLayoutJobs(gvc, graph); /* take layout engine from command line */ + if (ret) + return ret; + + return gvRenderJobs(gvc, graph); + } +}; + +} // namespace tools +} // namespace node +} // namespace villas + +int main(int argc, char *argv[]) +{ + node::tools::Graph t(argc, argv); + + return t.run(); +} + +/** @} */