From db67c468bc5c554e9a5ae0fcdc0c4abfb1842693 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Fri, 25 Feb 2022 09:58:33 -0500 Subject: [PATCH] go: add first version of Golang node-type support --- CMakeLists.txt | 43 +-- include/villas/nodes/go.hpp | 133 +++++++++ lib/nodes/CMakeLists.txt | 6 + lib/nodes/go.cpp | 283 ++++++++++++++++++++ lib/nodes/go/CMakeLists.txt | 18 ++ lib/nodes/go/example.go | 264 ------------------ lib/nodes/go/go.mod | 5 + lib/nodes/go/go.sum | 2 + lib/nodes/go/helper.go | 13 + lib/nodes/go/main.go | 160 +++++++++++ lib/nodes/go/node.go | 12 - lib/nodes/go/pkg/node.go | 42 +++ lib/nodes/go/pkg/nodes/loopback/loopback.go | 74 +++++ lib/nodes/go/pkg/nodes/webrtc/webrtc.go | 67 +++++ lib/nodes/go/pkg/registry.go | 13 + lib/nodes/go/types.go | 1 - 16 files changed, 840 insertions(+), 296 deletions(-) create mode 100644 include/villas/nodes/go.hpp delete mode 100644 lib/nodes/go/example.go create mode 100644 lib/nodes/go/go.mod create mode 100644 lib/nodes/go/go.sum create mode 100644 lib/nodes/go/helper.go create mode 100644 lib/nodes/go/main.go delete mode 100644 lib/nodes/go/node.go create mode 100644 lib/nodes/go/pkg/node.go create mode 100644 lib/nodes/go/pkg/nodes/loopback/loopback.go create mode 100644 lib/nodes/go/pkg/nodes/webrtc/webrtc.go create mode 100644 lib/nodes/go/pkg/registry.go delete mode 100644 lib/nodes/go/types.go diff --git a/CMakeLists.txt b/CMakeLists.txt index 54512b8ac..26fc02773 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,6 +106,7 @@ endif() # Check programs find_program(PROTOBUFC_COMPILER NAMES protoc-c) find_program(PROTOBUF_COMPILER NAMES protoc) +find_program(GO NAMES go) set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig:/usr/local/lib64/pkgconfig:/usr/local/share/pkgconfig:/usr/lib64/pkgconfig") @@ -163,19 +164,14 @@ else() endif() # Build options -cmake_dependent_option(WITH_HOOKS "Build with support for processing hook plugins" ON "" OFF) -cmake_dependent_option(WITH_WEB "Build with internal webserver" ON "LIBWEBSOCKETS_FOUND" OFF) cmake_dependent_option(WITH_API "Build with remote control API" ON "" OFF) -cmake_dependent_option(WITH_CONFIG "Build with support for libconfig configuration syntax" ON "LIBCONFIG_FOUND" OFF) -cmake_dependent_option(WITH_OPENMP "Build with support for OpenMP for parallel hooks" ON "OPENMP_FOUND" OFF) -cmake_dependent_option(WITH_SRC "Build executables" ON "TOPLEVEL_PROJECT" OFF) -cmake_dependent_option(WITH_FPGA "Build with support for VILLASfpga" ON "XIL_FOUND; FOUND_SUBMODULE_FPGA" OFF) -cmake_dependent_option(WITH_TOOLS "Build auxilary tools" ON "TOPLEVEL_PROJECT" OFF) -cmake_dependent_option(WITH_TESTS "Run tests" ON "TOPLEVEL_PROJECT" OFF) -cmake_dependent_option(WITH_PLUGINS "Build plugins" ON "TOPLEVEL_PROJECT" OFF) cmake_dependent_option(WITH_CLIENTS "Build client applications" ON "TOPLEVEL_PROJECT" OFF) +cmake_dependent_option(WITH_CONFIG "Build with support for libconfig configuration syntax" ON "LIBCONFIG_FOUND" OFF) cmake_dependent_option(WITH_DOC "Build documentation" ON "TOPLEVEL_PROJECT" OFF) +cmake_dependent_option(WITH_FPGA "Build with support for VILLASfpga" ON "XIL_FOUND; FOUND_SUBMODULE_FPGA" OFF) +cmake_dependent_option(WITH_GO "Build with Go" ON "GO" OFF) cmake_dependent_option(WITH_GRAPHVIZ "Build with Graphviz" ON "CGRAPH_FOUND; GVC_FOUND" OFF) +cmake_dependent_option(WITH_HOOKS "Build with support for processing hook plugins" ON "" OFF) cmake_dependent_option(WITH_LUA "Build with Lua" ON "LUA_FOUND" OFF) cmake_dependent_option(WITH_NODE_AMQP "Build with amqp node-type" ON "RABBITMQ_C_FOUND" OFF) cmake_dependent_option(WITH_NODE_CAN "Build with can node-type" ON "" OFF) @@ -185,6 +181,7 @@ cmake_dependent_option(WITH_NODE_EXAMPLE "Build with example node-type" cmake_dependent_option(WITH_NODE_EXEC "Build with exec node-type" ON "" OFF) cmake_dependent_option(WITH_NODE_FILE "Build with file node-type" ON "" OFF) cmake_dependent_option(WITH_NODE_FPGA "Build with fpga node-type" ON "WITH_FPGA" OFF) +cmake_dependent_option(WITH_NODE_GO "Build with Go-based nodes-types" ON "WITH_GO" OFF) cmake_dependent_option(WITH_NODE_IEC61850 "Build with iec61850 node-types" ON "LIBIEC61850_FOUND" OFF) cmake_dependent_option(WITH_NODE_INFINIBAND "Build with infiniband node-type" ON "IBVerbs_FOUND; RDMACM_FOUND" OFF) # Infiniband node-type is currenly broken cmake_dependent_option(WITH_NODE_INFLUXDB "Build with influxdb node-type" ON "" OFF) @@ -205,6 +202,12 @@ cmake_dependent_option(WITH_NODE_TEST_RTT "Build with test_rtt node-type" cmake_dependent_option(WITH_NODE_ULDAQ "Build with uldaq node-type" ON "LIBULDAQ_FOUND" OFF) cmake_dependent_option(WITH_NODE_WEBSOCKET "Build with websocket node-type" ON "WITH_WEB; LIBWEBSOCKETS_FOUND" OFF) cmake_dependent_option(WITH_NODE_ZEROMQ "Build with zeromq node-type" ON "LIBZMQ_FOUND" OFF) +cmake_dependent_option(WITH_OPENMP "Build with support for OpenMP for parallel hooks" ON "OPENMP_FOUND" OFF) +cmake_dependent_option(WITH_PLUGINS "Build plugins" ON "TOPLEVEL_PROJECT" OFF) +cmake_dependent_option(WITH_SRC "Build executables" ON "TOPLEVEL_PROJECT" OFF) +cmake_dependent_option(WITH_TESTS "Run tests" ON "TOPLEVEL_PROJECT" OFF) +cmake_dependent_option(WITH_TOOLS "Build auxilary tools" ON "TOPLEVEL_PROJECT" OFF) +cmake_dependent_option(WITH_WEB "Build with internal webserver" ON "LIBWEBSOCKETS_FOUND" OFF) # Add more build configurations include(cmake/config/Debug.cmake) @@ -255,24 +258,26 @@ configure_file( ) # Show feature summary -add_feature_info(HOOKS WITH_HOOKS "Build with support for processing hook plugins") -add_feature_info(WEB WITH_WEB "Build with internal webserver") add_feature_info(API WITH_API "Build with remote control API") -add_feature_info(CONFIG WITH_CONFIG "Build with support for libconfig configuration syntax") -add_feature_info(SRC WITH_SRC "Build executables") -add_feature_info(FPGA WITH_FPGA "Build with FPGA support") -add_feature_info(TOOLS WITH_TOOLS "Build auxilary tools") -add_feature_info(TESTS WITH_TESTS "Run tests") -add_feature_info(PLUGINS WITH_PLUGINS "Build plugins") add_feature_info(CLIENTS WITH_CLIENTS "Build client applications") +add_feature_info(CONFIG WITH_CONFIG "Build with support for libconfig configuration syntax") +add_feature_info(DOC WITH_DOC "Build documentation") +add_feature_info(FPGA WITH_FPGA "Build with FPGA support") +add_feature_info(GO WITH_GO "Build with Go code") add_feature_info(GRAPHVIZ WITH_GRAPHVIZ "Build with Graphviz support") +add_feature_info(HOOKS WITH_HOOKS "Build with support for processing hook plugins") add_feature_info(LUA WITH_LUA "Build with Lua support") add_feature_info(OPENMP WITH_OPENMP "Build with OpenMP support") -add_feature_info(DOC WITH_DOC "Build documentation") +add_feature_info(PLUGINS WITH_PLUGINS "Build plugins") +add_feature_info(SRC WITH_SRC "Build executables") +add_feature_info(TESTS WITH_TESTS "Run tests") +add_feature_info(TOOLS WITH_TOOLS "Build auxilary tools") +add_feature_info(WEB WITH_WEB "Build with internal webserver") add_feature_info(NODE_AMQP WITH_NODE_AMQP "Build with amqp node-type") add_feature_info(NODE_COMEDI WITH_NODE_COMEDI "Build with comedi node-type") add_feature_info(NODE_FILE WITH_NODE_FILE "Build with file node-type") +add_feature_info(NODE_GO WITH_NODE_GO "Build with Go-based nodes-types") add_feature_info(NODE_IEC61850 WITH_NODE_IEC61850 "Build with iec61850 node-types") add_feature_info(NODE_INFINIBAND WITH_NODE_INFINIBAND "Build with infiniband node-type") add_feature_info(NODE_INFLUXDB WITH_NODE_INFLUXDB "Build with influxdb node-type") @@ -282,6 +287,7 @@ add_feature_info(NODE_MQTT WITH_NODE_MQTT "Build with add_feature_info(NODE_NANOMSG WITH_NODE_NANOMSG "Build with nanomsg node-type") add_feature_info(NODE_NGSI WITH_NODE_NGSI "Build with ngsi node-type") add_feature_info(NODE_OPAL WITH_NODE_OPAL "Build with opal node-type") +add_feature_info(NODE_REDIS WITH_NODE_REDIS "Build with redis node-type") add_feature_info(NODE_RTP WITH_NODE_RTP "Build with rtp node-type") add_feature_info(NODE_SHMEM WITH_NODE_SHMEM "Build with shmem node-type") add_feature_info(NODE_SIGNAL_GENERATOR WITH_NODE_SIGNAL "Build with signal node-type") @@ -292,7 +298,6 @@ add_feature_info(NODE_TEST_RTT WITH_NODE_TEST_RTT "Build with add_feature_info(NODE_ULDAQ WITH_NODE_ULDAQ "Build with uldaq node-type") add_feature_info(NODE_WEBSOCKET WITH_NODE_WEBSOCKET "Build with websocket node-type") add_feature_info(NODE_ZEROMQ WITH_NODE_ZEROMQ "Build with zeromq node-type") -add_feature_info(NODE_REDIS WITH_NODE_REDIS "Build with redis node-type") if(TOPLEVEL_PROJECT) feature_summary(WHAT ALL VAR FEATURES) diff --git a/include/villas/nodes/go.hpp b/include/villas/nodes/go.hpp new file mode 100644 index 000000000..9df6d4c5b --- /dev/null +++ b/include/villas/nodes/go.hpp @@ -0,0 +1,133 @@ +/** Node-type implemeted in Go language + * + * @file + * @author Steffen Vogel + * @copyright 2014-2022, 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 . + *********************************************************************************/ + +#pragma once + +#include + +namespace villas { +namespace node { + +/* Forward declarations */ +struct Sample; +class Format; + +class GoNode : public Node { + +protected: + void *node; + + std::string _details; + + Format *formatter; + + virtual + int _read(struct Sample * smps[], unsigned cnt); + + virtual + int _write(struct Sample * smps[], unsigned cnt); + +public: + GoNode(void *n); + + virtual + ~GoNode(); + + virtual + int parse(json_t *json, const uuid_t sn_uuid); + + virtual + std::vector getPollFDs(); + + virtual + std::vector getNetemFDs(); + + virtual + const std::string & getDetails(); + + virtual + int prepare(); + + virtual + int check(); + + virtual + int start(); + + virtual + int stop(); + + virtual + int pause(); + + virtual + int resume(); + + virtual + int restart() + { + assert(state == State::STARTED); + + logger->info("Restarting node"); + + return GoNodeRestart(node); + } + + virtual + int reverse() + { + return GoNodeReverse(node); + } +}; + +class GoNodeFactory : public NodeFactory { + +protected: + std::string type; + +public: + GoNodeFactory(char *t) : + NodeFactory(), + type(t) + { } + + virtual + Node * make(); + + virtual + std::string getName() const; + + virtual + std::string getDescription() const; +}; + +class GoPluginRegistry : public plugin::SubRegistry { + +public: + GoPluginRegistry(); + + plugin::List<> lookup(); +}; + +} /* namespace node */ +} /* namespace villas */ diff --git a/lib/nodes/CMakeLists.txt b/lib/nodes/CMakeLists.txt index 3acc9b6a8..054f73556 100644 --- a/lib/nodes/CMakeLists.txt +++ b/lib/nodes/CMakeLists.txt @@ -60,6 +60,12 @@ if(WITH_NODE_EXEC) list(APPEND NODE_SRC exec.cpp) endif() +if(WITH_NODE_GO) + add_subdirectory(go) + list(APPEND LIBRARIES nodes-go) + list(APPEND NODE_SRC go.cpp) +endif() + # Enable Universal Library for Linux DAQ devices (libuldaq) if(WITH_NODE_ULDAQ) list(APPEND NODE_SRC uldaq.cpp) diff --git a/lib/nodes/go.cpp b/lib/nodes/go.cpp index e69de29bb..fd113674a 100644 --- a/lib/nodes/go.cpp +++ b/lib/nodes/go.cpp @@ -0,0 +1,283 @@ +/** Node-type implemeted in Go language + * + * @file + * @author Steffen Vogel + * @copyright 2014-2022, 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 . + *********************************************************************************/ + +#include +#include + +extern "C" { + #include +} + +#include +#include + +using namespace villas::node; + +GoNode::GoNode(void *n) : + Node(), + node(n) +{ } + +GoNode::~GoNode() +{ + GoNodeClose(node); +} + +int GoNode::parse(json_t *json, const uuid_t sn_uuid) +{ + int ret = Node::parse(json, sn_uuid); + if (ret) + return ret; + + json_t *json_format = nullptr; + json_error_t err; + + ret = json_unpack_ex(json, &err, 0, "{ s?: o }", + "format", &json_format + ); + if (ret) + throw ConfigError(json, err, "node-config-node-format", "Failed to parse node configuration"); + + formatter = json_format + ? FormatFactory::make(json_format) + : FormatFactory::make("json"); + if (!formatter) + throw ConfigError(json_format, "node-config-node-format", "Invalid format configuration"); + + auto *cfg = json_dumps(json, JSON_COMPACT); + ret = GoNodeParse(node, cfg); + free(cfg); + return ret; +} + +int GoNode::check() +{ + int ret = Node::check(); + if (ret) + return ret; + + return GoNodeCheck(node); +} + +int GoNode::prepare() +{ + int ret = GoNodePrepare(node); + if (ret) + return ret; + + return Node::prepare(); +} + +int GoNode::start() +{ + int ret; + + assert(state == State::PREPARED || + state == State::PAUSED); + + /* Initialize IO */ + formatter->start(getInputSignals(false)); + + ret = GoNodeStart(node); + if (ret) + return ret; + + ret = Node::start(); + if (!ret) + state = State::STARTED; + + return ret; +} + +int GoNode::stop() { + int ret; + + assert(state == State::STARTED || + state == State::PAUSED || + state == State::STOPPING); + + ret = GoNodeStop(node); + if (ret) + return ret; + + ret = Node::stop(); + if (!ret) + state = State::STOPPED; + + return ret; +} + +int GoNode::pause() +{ + int ret; + + ret = Node::pause(); + if (ret) + return ret; + + ret = GoNodePause(node); + if (!ret) + state = State::PAUSED; + + return ret; +} + +int GoNode::resume() +{ + int ret; + + ret = Node::resume(); + if (ret) + return ret; + + ret = GoNodeResume(node); + if (!ret) + state = State::STARTED; + + return ret; +} + +const std::string & GoNode::getDetails() +{ + if (_details.empty()) { + auto *d = GoNodeDetails(node); + _details = std::string(d); + free(d); + } + + return _details; +} + +std::vector GoNode::getPollFDs() +{ + auto ret = GoNodeGetPollFDs(node); + if (ret.r1) + return {}; + + auto begin = (int *) ret.r0.data; + auto end = begin + ret.r0.len; + + return std::vector(begin, end); +} + +std::vector GoNode::getNetemFDs() +{ + auto ret = GoNodeGetNetemFDs(node); + if (ret.r1) + return {}; + + auto begin = (int *) ret.r0.data; + auto end = begin + ret.r0.len; + + return std::vector(begin, end); +} + +int GoNode::_read(struct Sample * smps[], unsigned cnt) +{ + int ret; + size_t rbytes; + + auto d = GoNodeRead(node); + if (d.r1) + return d.r1; + + ret = formatter->sscan((const char*) d.r0.data, d.r0.len, &rbytes, smps, cnt); + if (ret < 0 || (size_t) d.r0.len != rbytes) + logger->warn("Received invalid packet: ret={}, bytes={}, rbytes={}", ret, d.r0.len, rbytes); + + logger->info("Received {} bytes: {}", d.r0.len, (char *) d.r0.data); + + return ret; +} + +int GoNode::_write(struct Sample * smps[], unsigned cnt) +{ + int ret; + char buf[4096]; + size_t wbytes; + + ret = formatter->sprint(buf, 4096, &wbytes, smps, cnt); + if (ret < 0) + return ret; + + GoSlice slice = { + data: buf, + len: GoInt(wbytes), + cap: 4096 + }; + + ret = GoNodeWrite(node, slice); + if (ret) + return ret; + + return cnt; +} + +Node * GoNodeFactory::make() +{ + auto *nt = NewGoNode((char *) type.c_str()); + if (!nt) + return nullptr; + + auto *n = new GoNode(nt); + + init(n); + + return n; +} + +std::string GoNodeFactory::getName() const +{ + return type; +} + +std::string GoNodeFactory::getDescription() const +{ + return "Go-based node-type"; +} + +GoPluginRegistry::GoPluginRegistry() { + if (plugin::registry == nullptr) + plugin::registry = new plugin::Registry(); + + plugin::registry->addSubRegistry(this); +} + +villas::plugin::List<> GoPluginRegistry::lookup() +{ + plugin::List<> plugins; + + auto nt = GoNodeTypes(); + auto ntl = std::vector(nt.r1, nt.r1+nt.r0); + + for (auto nt : ntl) { + plugins.push_back(new GoNodeFactory(nt)); + free(nt); + } + + free(nt.r1); + + return plugins; +} + +static GoPluginRegistry pr; diff --git a/lib/nodes/go/CMakeLists.txt b/lib/nodes/go/CMakeLists.txt index e69de29bb..42a206021 100644 --- a/lib/nodes/go/CMakeLists.txt +++ b/lib/nodes/go/CMakeLists.txt @@ -0,0 +1,18 @@ +set(LIB libnodes-go.a) +file(GLOB_RECURSE SRCS *.go) + +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${LIB} + DEPENDS ${SRCS} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND env CGO_ENABLED=1 GOPATH=${GOPATH} go build -buildmode=c-archive -o "${CMAKE_CURRENT_BINARY_DIR}/${LIB}" ${CMAKE_GO_FLAGS} . + COMMENT "Building Go library ${LIB}") + +add_custom_target(libvillas-go DEPENDS ${LIB} ${HEADER}) + +add_library(nodes-go STATIC IMPORTED GLOBAL) +add_dependencies(nodes-go libvillas-go) +set_target_properties(nodes-go + PROPERTIES + IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/${LIB}" + INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR} +) diff --git a/lib/nodes/go/example.go b/lib/nodes/go/example.go deleted file mode 100644 index 38e9db97e..000000000 --- a/lib/nodes/go/example.go +++ /dev/null @@ -1,264 +0,0 @@ -package node - -import "C" -import ( - "time" - "unsafe" - - gopointer "github.com/mattn/go-pointer" -) - -func registerNode(name string, ctor CreateNode) { - -} - -type Signal struct { -} - -type Sample struct { - Timestamp struct { - Origin time.Time - Received time.Time - } - - Data []interface{} -} - -type Node interface { - Close() error - - Prepare() error - - Parse(cfg []byte) error - - Check() error - - Start() error - Stop() error - - Pause() error - Resume() error - Restart() error - - Read(smps []Sample) (int, error) - Write(smps []Sample) (int, error) - - Reverse() error - - GetPollFDs() ([]int, error) - GetNetemFDs() ([]int, error) - - // GetMemoryType() - - Details() string -} - -type CreateNode func() Node - -// Wrapper - -func intSliceToCArray(s []int) unsafe.Pointer { - b := (*[1 << 16]byte)(unsafe.Pointer(&s[0]))[0 : len(s)*8 : len(s)*8] - return C.CBytes(unsafe.Pointer(&b[0])) -} - -func errorToInt(err error) int { - if err == nil { - return -1 - } else { - return -22 - } -} - -//export NodeClose -func NodeClose(p unsafe.Pointer) int { - n := gopointer.Restore(p).(Node) - return errorToInt(n.Close()) -} - -//export NodePrepare -func NodePrepare(p unsafe.Pointer) int { - n := gopointer.Restore(p).(Node) - return errorToInt(n.Prepare()) -} - -//export NodeParse -func NodeParse(p unsafe.Pointer, c unsafe.Pointer) int { - cfg := gopointer.Restore(c).([]byte) - n := gopointer.Restore(p).(Node) - return errorToInt(n.Parse(cfg)) -} - -//export NodeCheck -func NodeCheck(p unsafe.Pointer) int { - n := gopointer.Restore(p).(Node) - return errorToInt(n.Check()) -} - -//export NodeStart -func NodeStart(p unsafe.Pointer) int { - n := gopointer.Restore(p).(Node) - return errorToInt(n.Start()) -} - -//export NodeStop -func NodeStop(p unsafe.Pointer) error { - n := gopointer.Restore(p).(Node) - return errorToInt(n.Stop()) -} - -//export NodePause -func NodePause(p unsafe.Pointer) int { - n := gopointer.Restore(p).(Node) - return errorToInt(n.Pause()) -} - -//export NodeResume -func NodeResume(p unsafe.Pointer) int { - n := gopointer.Restore(p).(Node) - return errorToInt(n.Resume()) -} - -//export NodeRestart -func NodeRestart(p unsafe.Pointer) int { - n := gopointer.Restore(p).(Node) - return errorToInt(n.Restart()) -} - -//export NodeRead -func NodeRead(p unsafe.Pointer, smps []Sample) (int, error) { - n := gopointer.Restore(p).(Node) - return n.Read(smps) -} - -//export NodeWrite -func NodeWrite(p unsafe.Pointer, smps []Sample) (int, int) { - n := gopointer.Restore(p).(Node) - r, err := n.Write(smps) - return r, errorToInt(err) -} - -//export NodeReverse -func NodeReverse(p unsafe.Pointer) int { - n := gopointer.Restore(p).(Node) - return errorToInt(n.Reverse()) -} - -//export NodeGetPollFDs -func NodeGetPollFDs(p unsafe.Pointer) (unsafe.Pointer, int) { - n := gopointer.Restore(p).(Node) - f, err := n.GetPollFDs() - if err == nil { - return intSliceToCArray(f), 0 - } else { - return nil, errorToInt(err) - } -} - -//export NodeGetNetemFDs -func NodeGetNetemFDs(p unsafe.Pointer) (unsafe.Pointer, int) { - n := gopointer.Restore(p).(Node) - f, err := n.GetNetemFDs() - if err == nil { - return intSliceToCArray(f), 0 - } else { - return nil, errorToInt(err) - } -} - -//export NodeDetails -func NodeDetails(p unsafe.Pointer) *C.char { - n := gopointer.Restore(p).(Node) - d := n.Details() - return C.CString(d) -} - -// Example - -type ExampleNode struct { - Node -} - -func NewExampleNode() Node { - return &ExampleNode{} -} - -func (n *ExampleNode) Close() error { - - return nil -} - -func (n *ExampleNode) Prepare() error { - - return nil -} - -func (n *ExampleNode) Parse(cfg []byte) error { - - return nil -} - -func (n *ExampleNode) Check() error { - - return nil -} - -func (n *ExampleNode) Start() error { - - return nil -} - -func (n *ExampleNode) Stop() error { - - return nil -} - -func (n *ExampleNode) Pause() error { - - return nil -} - -func (n *ExampleNode) Resume() error { - - return nil -} - -func (n *ExampleNode) Restart() error { - - return nil -} - -func (n *ExampleNode) Read(smps []Sample) (int, error) { - - return 0, nil -} - -func (n *ExampleNode) Write(smps []Sample) (int, error) { - - return 0, nil -} - -func (n *ExampleNode) Reverse() error { - - return nil -} - -func (n *ExampleNode) GetPollFDs() ([]int, error) { - - return []int{}, nil -} - -func (n *ExampleNode) GetNetemFDs() ([]int, error) { - return []int{}, nil -} - -func (n *ExampleNode) Details() string { - - return "" -} - -// init - -func init() { - registerNode("example-go", NewExampleNode) -} diff --git a/lib/nodes/go/go.mod b/lib/nodes/go/go.mod new file mode 100644 index 000000000..a1c8a569a --- /dev/null +++ b/lib/nodes/go/go.mod @@ -0,0 +1,5 @@ +module git.rwth-aachen.de/acs/public/villas/node + +go 1.16 + +require github.com/mattn/go-pointer v0.0.1 diff --git a/lib/nodes/go/go.sum b/lib/nodes/go/go.sum new file mode 100644 index 000000000..1fdefeab4 --- /dev/null +++ b/lib/nodes/go/go.sum @@ -0,0 +1,2 @@ +github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0= +github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc= diff --git a/lib/nodes/go/helper.go b/lib/nodes/go/helper.go new file mode 100644 index 000000000..895612cb3 --- /dev/null +++ b/lib/nodes/go/helper.go @@ -0,0 +1,13 @@ +package main + +import ( + "C" +) + +func errorToInt(e error) C.int { + if e == nil { + return 0 + } else { + return -1 + } +} diff --git a/lib/nodes/go/main.go b/lib/nodes/go/main.go new file mode 100644 index 000000000..65bbe73d8 --- /dev/null +++ b/lib/nodes/go/main.go @@ -0,0 +1,160 @@ +package main + +/* + */ +import "C" + +import ( + "unsafe" + + node "git.rwth-aachen.de/acs/public/villas/node/pkg" + gopointer "github.com/mattn/go-pointer" + + _ "git.rwth-aachen.de/acs/public/villas/node/pkg/nodes/loopback" + _ "git.rwth-aachen.de/acs/public/villas/node/pkg/nodes/webrtc" +) + +func main() {} + +//export GoNodeTypes +func GoNodeTypes() (int, **C.char) { + var nodeTypes = node.NodeTypes() + var n = len(nodeTypes) + + types := (**C.char)(C.malloc(C.size_t(n) * C.size_t(unsafe.Sizeof(uintptr(0))))) + slice := (*[1 << 31]*C.char)(unsafe.Pointer(types))[:n:n] + + i := 0 + for typ, _ := range nodeTypes { + slice[i] = C.CString(typ) + i++ + } + + return n, types +} + +//export NewGoNode +func NewGoNode(t *C.char) unsafe.Pointer { + var nodeTypes = node.NodeTypes() + + typ := C.GoString(t) + ctor, ok := nodeTypes[typ] + if !ok { + return unsafe.Pointer(nil) + } + + node := ctor() + + ptr := gopointer.Save(node) + return ptr +} + +//export GoNodeClose +func GoNodeClose(p unsafe.Pointer) C.int { + n := gopointer.Restore(p).(node.Node) + return errorToInt(n.Close()) +} + +//export GoNodePrepare +func GoNodePrepare(p unsafe.Pointer) C.int { + n := gopointer.Restore(p).(node.Node) + return errorToInt(n.Prepare()) +} + +//export GoNodeParse +func GoNodeParse(p unsafe.Pointer, c *C.char) C.int { + scfg := C.GoString(c) + bcfg := []byte(scfg) + + n := gopointer.Restore(p).(node.Node) + return errorToInt(n.Parse(bcfg)) +} + +//export GoNodeCheck +func GoNodeCheck(p unsafe.Pointer) C.int { + n := gopointer.Restore(p).(node.Node) + return errorToInt(n.Check()) +} + +//export GoNodeStart +func GoNodeStart(p unsafe.Pointer) C.int { + n := gopointer.Restore(p).(node.Node) + return errorToInt(n.Start()) +} + +//export GoNodeStop +func GoNodeStop(p unsafe.Pointer) C.int { + n := gopointer.Restore(p).(node.Node) + return errorToInt(n.Stop()) +} + +//export GoNodePause +func GoNodePause(p unsafe.Pointer) C.int { + n := gopointer.Restore(p).(node.Node) + return errorToInt(n.Pause()) +} + +//export GoNodeResume +func GoNodeResume(p unsafe.Pointer) C.int { + n := gopointer.Restore(p).(node.Node) + return errorToInt(n.Resume()) +} + +//export GoNodeRestart +func GoNodeRestart(p unsafe.Pointer) C.int { + n := gopointer.Restore(p).(node.Node) + return errorToInt(n.Restart()) +} + +//export GoNodeRead +func GoNodeRead(p unsafe.Pointer) ([]byte, C.int) { + n := gopointer.Restore(p).(node.Node) + + d, err := n.Read() + if err != nil { + return nil, errorToInt(err) + } + + return d, 0 +} + +//export GoNodeWrite +func GoNodeWrite(p unsafe.Pointer, data []byte) C.int { + n := gopointer.Restore(p).(node.Node) + return errorToInt(n.Write(data)) +} + +//export GoNodeReverse +func GoNodeReverse(p unsafe.Pointer) C.int { + n := gopointer.Restore(p).(node.Node) + return errorToInt(n.Reverse()) +} + +//export GoNodeGetPollFDs +func GoNodeGetPollFDs(p unsafe.Pointer) ([]int, C.int) { + n := gopointer.Restore(p).(node.Node) + f, err := n.GetPollFDs() + if err != nil { + return nil, errorToInt(err) + } + + return f, 0 +} + +//export GoNodeGetNetemFDs +func GoNodeGetNetemFDs(p unsafe.Pointer) ([]int, C.int) { + n := gopointer.Restore(p).(node.Node) + f, err := n.GetNetemFDs() + if err != nil { + return nil, errorToInt(err) + } + + return f, 0 +} + +//export GoNodeDetails +func GoNodeDetails(p unsafe.Pointer) *C.char { + n := gopointer.Restore(p).(node.Node) + d := n.Details() + return C.CString(d) +} diff --git a/lib/nodes/go/node.go b/lib/nodes/go/node.go deleted file mode 100644 index 06172f356..000000000 --- a/lib/nodes/go/node.go +++ /dev/null @@ -1,12 +0,0 @@ -package node - -/* -#include - - -*/ -import "C" - -func registerNode(name string, ctor CreateNode) { - -} diff --git a/lib/nodes/go/pkg/node.go b/lib/nodes/go/pkg/node.go new file mode 100644 index 000000000..8fe878667 --- /dev/null +++ b/lib/nodes/go/pkg/node.go @@ -0,0 +1,42 @@ +package node + +type NodeCtor func() Node + +type Node interface { + Close() error + + Prepare() error + + Parse(cfg []byte) error + + Check() error + + Start() error + Stop() error + + Pause() error + Resume() error + Restart() error + + Read() ([]byte, error) + Write(data []byte) error + + Reverse() error + + GetPollFDs() ([]int, error) + GetNetemFDs() ([]int, error) + + // GetMemoryType() + + Details() string +} + +type NodeConfig struct { + Type string `json:"type"` + + In struct { + } `json:"in"` + + Out struct { + } `json:"out"` +} diff --git a/lib/nodes/go/pkg/nodes/loopback/loopback.go b/lib/nodes/go/pkg/nodes/loopback/loopback.go new file mode 100644 index 000000000..11bdfb870 --- /dev/null +++ b/lib/nodes/go/pkg/nodes/loopback/loopback.go @@ -0,0 +1,74 @@ +package nodes + +import ( + "encoding/json" + "fmt" + + node "git.rwth-aachen.de/acs/public/villas/node/pkg" +) + +type LoopbackNode struct { + node.Node + + channel chan []byte + + Config LoopbackConfig +} + +type LoopbackConfig struct { + node.NodeConfig + + Value int `json:"value"` +} + +func NewLoopbackNode() node.Node { + return &LoopbackNode{ + channel: make(chan []byte, 1024), + } +} + +func (n *LoopbackNode) Parse(c []byte) error { + return json.Unmarshal(c, &n.Config) +} + +func (n *LoopbackNode) Check() error { + return nil +} + +func (n *LoopbackNode) Prepare() error { + return nil +} + +func (n *LoopbackNode) Start() error { + return nil +} + +func (n *LoopbackNode) Stop() error { + return nil +} + +func (n *LoopbackNode) Read() ([]byte, error) { + return <-n.channel, nil +} + +func (n *LoopbackNode) Write(data []byte) error { + n.channel <- data + + return nil +} + +func (n *LoopbackNode) PollFDs() []int { + return nil +} + +func (n *LoopbackNode) NetemFDs() []int { + return nil +} + +func (n *LoopbackNode) Details() string { + return fmt.Sprintf("value=%d", n.Config.Value) +} + +func init() { + node.RegisterNode("go.loopback", NewLoopbackNode) +} diff --git a/lib/nodes/go/pkg/nodes/webrtc/webrtc.go b/lib/nodes/go/pkg/nodes/webrtc/webrtc.go new file mode 100644 index 000000000..208366ea9 --- /dev/null +++ b/lib/nodes/go/pkg/nodes/webrtc/webrtc.go @@ -0,0 +1,67 @@ +package nodes + +import ( + "encoding/json" + "net/url" + + node "git.rwth-aachen.de/acs/public/villas/node/pkg" +) + +type WebRTCNode struct { + node.Node + + Config WebRTCConfig +} + +type WebRTCConfig struct { + RendezvouzToken string `json:"token,omitempty"` + RendezvouzAddress url.URL `json:"address,omitempty"` +} + +func NewWebRTCNode() node.Node { + return &WebRTCNode{} +} + +func (n *WebRTCNode) Parse(c []byte) error { + return json.Unmarshal(c, &n.Config) +} + +func (n *WebRTCNode) Check() error { + return nil +} + +func (n *WebRTCNode) Prepare() error { + return nil +} + +func (n *WebRTCNode) Start() error { + return nil +} + +func (n *WebRTCNode) Stop() error { + return nil +} + +func (n *WebRTCNode) Read() ([]byte, error) { + return nil, nil +} + +func (n *WebRTCNode) Write(data []byte) error { + return nil +} + +func (n *WebRTCNode) PollFDs() []int { + return nil +} + +func (n *WebRTCNode) NetemFDs() []int { + return nil +} + +func (n *WebRTCNode) Details() string { + return "this-is-my-webrtc-node" +} + +func init() { + node.RegisterNode("webrtc", NewWebRTCNode) +} diff --git a/lib/nodes/go/pkg/registry.go b/lib/nodes/go/pkg/registry.go new file mode 100644 index 000000000..d8948ece9 --- /dev/null +++ b/lib/nodes/go/pkg/registry.go @@ -0,0 +1,13 @@ +package node + +var ( + goNodeTypes = map[string]NodeCtor{} +) + +func RegisterNode(name string, ctor NodeCtor) { + goNodeTypes[name] = ctor +} + +func NodeTypes() map[string]NodeCtor { + return goNodeTypes +} diff --git a/lib/nodes/go/types.go b/lib/nodes/go/types.go deleted file mode 100644 index 2b4023a62..000000000 --- a/lib/nodes/go/types.go +++ /dev/null @@ -1 +0,0 @@ -package node