diff --git a/doc/openapi/components/schemas/config/node_obj.yaml b/doc/openapi/components/schemas/config/node_obj.yaml index c3e796171..6629d20c8 100644 --- a/doc/openapi/components/schemas/config/node_obj.yaml +++ b/doc/openapi/components/schemas/config/node_obj.yaml @@ -20,7 +20,6 @@ discriminator: propertyName: type mapping: amqp: nodes/_amqp.yaml - api: nodes/_api.yaml can: nodes/_can.yaml comedi: nodes/_comedi.yaml ethercat: nodes/_ethercat.yaml diff --git a/doc/openapi/components/schemas/config/nodes/_api.yaml b/doc/openapi/components/schemas/config/nodes/_api.yaml deleted file mode 100644 index 8c7cb8603..000000000 --- a/doc/openapi/components/schemas/config/nodes/_api.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# yaml-language-server: $schema=http://json-schema.org/draft-07/schema -# SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University -# SPDX-License-Identifier: Apache-2.0 ---- -allOf: -- $ref: ../node_obj.yaml -- $ref: api.yaml diff --git a/doc/openapi/components/schemas/config/nodes/api.yaml b/doc/openapi/components/schemas/config/nodes/api.yaml deleted file mode 100644 index d259227db..000000000 --- a/doc/openapi/components/schemas/config/nodes/api.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# yaml-language-server: $schema=http://json-schema.org/draft-07/schema -# SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University -# SPDX-License-Identifier: Apache-2.0 ---- -allOf: -- $ref: ../node.yaml -- type: object - properties: - in: - type: object - required: - - signals - properties: - signals: - type: array - items: - $ref: ./signals/api_signal.yaml - - out: - type: object - required: - - signals - properties: - signals: - type: array - items: - $ref: ./signals/api_signal.yaml diff --git a/doc/openapi/components/schemas/config/nodes/signals/api_signal.yaml b/doc/openapi/components/schemas/config/nodes/signals/api_signal.yaml deleted file mode 100644 index f12870322..000000000 --- a/doc/openapi/components/schemas/config/nodes/signals/api_signal.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# yaml-language-server: $schema=http://json-schema.org/draft-07/schema -# SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University -# SPDX-License-Identifier: Apache-2.0 ---- -allOf: -- type: object - properties: - description: - type: string - description: A human readable description of the channel. - - payload: - description: | - Describes the type of information which is exchanged over the channel. - type: string - enum: - - events - - samples - - range: - oneOf: - - type: object - description: Limits for numeric datatypes - properties: - min: - type: number - max: - type: number - - - type: array - description: A list of allowed string values for string datatype - items: - type: string - - rate: - type: number - description: | - Expected refresh-rate in Hertz of this channel - Does not apply channels which have event payloads. - -- $ref: ../../signal.yaml diff --git a/include/villas/api/requests/universal.hpp b/include/villas/api/requests/universal.hpp deleted file mode 100644 index ab1d2a183..000000000 --- a/include/villas/api/requests/universal.hpp +++ /dev/null @@ -1,34 +0,0 @@ -/* Universal Data-exchange API request. - * - * Author: Steffen Vogel - * SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include -#include - -namespace villas { -namespace node { - -// Forward declarations -class Node; - -namespace api { - -class UniversalRequest : public NodeRequest { - -protected: - APINode *api_node; - -public: - using NodeRequest::NodeRequest; - - void prepare() override; -}; - -} // namespace api -} // namespace node -} // namespace villas diff --git a/include/villas/api/universal.hpp b/include/villas/api/universal.hpp deleted file mode 100644 index 96ac12447..000000000 --- a/include/villas/api/universal.hpp +++ /dev/null @@ -1,53 +0,0 @@ - -/* Universal Data-exchange API request. - * - * Author: Steffen Vogel - * SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include -#include -#include - -#include - -#include - -namespace villas { -namespace node { -namespace api { -namespace universal { - -enum PayloadType { SAMPLES = 0, EVENTS = 1 }; - -// Channel (uAPI) is a synonym for signal (VILLAS) -class Channel { -public: - std::string description; - PayloadType payload; - double range_min; - double range_max; - std::vector range_options; - double rate; - bool readable; - bool writable; - - using Ptr = std::shared_ptr; - - void parse(json_t *json); - json_t *toJson(Signal::Ptr sig) const; -}; - -class ChannelList : public std::vector { - -public: - void parse(json_t *json, bool readable, bool writable); -}; - -} // namespace universal -} // namespace api -} // namespace node -} // namespace villas diff --git a/include/villas/nodes/api.hpp b/include/villas/nodes/api.hpp deleted file mode 100644 index daad5cecb..000000000 --- a/include/villas/nodes/api.hpp +++ /dev/null @@ -1,49 +0,0 @@ -/* Node type: Universal Data-exchange API (v2). - * - * @see https://github.com/ERIGrid2/JRA-3.1-api - * Author: Steffen Vogel - * SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include - -#include -#include - -namespace villas { -namespace node { - -// Forward declarations -struct Sample; - -class APINode : public Node { - -public: - APINode(const uuid_t &id = {}, const std::string &name = ""); - - struct Direction { - Sample *sample; - api::universal::ChannelList channels; - pthread_cond_t cv; - pthread_mutex_t mutex; - }; - - // Accessed by api::universal::SignalRequest - Direction read, write; - - int prepare() override; - - int check() override; - -protected: - int parse(json_t *json) override; - - int _read(struct Sample *smps[], unsigned cnt) override; - int _write(struct Sample *smps[], unsigned cnt) override; -}; - -} // namespace node -} // namespace villas diff --git a/lib/api/CMakeLists.txt b/lib/api/CMakeLists.txt index 70974bdeb..1a0cdd2d8 100644 --- a/lib/api/CMakeLists.txt +++ b/lib/api/CMakeLists.txt @@ -9,11 +9,9 @@ set(API_SRC session.cpp request.cpp response.cpp - universal.cpp requests/node.cpp requests/path.cpp - requests/universal.cpp requests/status.cpp requests/capabiltities.cpp @@ -30,11 +28,6 @@ set(API_SRC requests/paths.cpp requests/path_info.cpp requests/path_action.cpp - - requests/universal/status.cpp - requests/universal/info.cpp - requests/universal/channel.cpp - requests/universal/channels.cpp ) if(WITH_GRAPHVIZ) diff --git a/lib/api/requests/universal.cpp b/lib/api/requests/universal.cpp deleted file mode 100644 index f4ffee732..000000000 --- a/lib/api/requests/universal.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/* Universal Data-exchange API request. - * - * Author: Steffen Vogel - * SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University - * SPDX-License-Identifier: Apache-2.0 - */ - -#include - -using namespace villas::node; -using namespace villas::node::api; -using namespace villas::node::api::universal; - -void UniversalRequest::prepare() { - NodeRequest::prepare(); - - api_node = dynamic_cast(node); - if (!api_node) - throw Error::badRequest(nullptr, "Node {} is not an univeral API node!", - node->getNameShort()); -} diff --git a/lib/api/requests/universal/channel.cpp b/lib/api/requests/universal/channel.cpp deleted file mode 100644 index df543acbc..000000000 --- a/lib/api/requests/universal/channel.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* The Universal Data-exchange API. - * - * Author: Steffen Vogel - * SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include - -namespace villas { -namespace node { -namespace api { -namespace universal { - -class ChannelRequest : public UniversalRequest { -public: - using UniversalRequest::UniversalRequest; - - Response *executeGet(const std::string &signalName, PayloadType payload) { - if (body != nullptr) - throw Error::badRequest(nullptr, - "This endpoint does not accept any body data"); - - pthread_mutex_lock(&api_node->write.mutex); - - auto *smp = api_node->write.sample; - if (!smp) { - pthread_mutex_unlock(&api_node->write.mutex); - throw Error(HTTP_STATUS_NOT_FOUND, nullptr, "No data available"); - } - - auto idx = smp->signals->getIndexByName(signalName); - if (idx < 0) { - pthread_mutex_unlock(&api_node->write.mutex); - throw Error(HTTP_STATUS_NOT_FOUND, nullptr, "Unknown signal id: {}", - signalName); - } - - auto sig = smp->signals->getByIndex(idx); - auto ch = api_node->write.channels.at(idx); - - if (payload != ch->payload) - throw Error::badRequest(nullptr, "Mismatching payload type"); - - auto *json_signal = - json_pack("{ s: f, s: o, s: s, s: s, s: s }", "timestamp", - time_to_double(&smp->ts.origin), "value", - smp->data[idx].toJson(sig->type), "validity", "unknown", - "source", "unknown", "timesource", "unknown"); - - if (smp->length <= (unsigned)idx) - smp->length = idx + 1; - - smp->flags |= (int)SampleFlags::HAS_TS_ORIGIN | (int)SampleFlags::HAS_DATA; - - pthread_mutex_unlock(&api_node->write.mutex); - - return new JsonResponse(session, HTTP_STATUS_OK, json_signal); - } - - Response *executePut(const std::string &signalName, PayloadType payload) { - int ret; - - pthread_mutex_lock(&api_node->read.mutex); - - auto *smp = api_node->read.sample; - if (!smp) { - pthread_mutex_unlock(&api_node->read.mutex); - throw Error(HTTP_STATUS_INTERNAL_SERVER_ERROR, nullptr, - "Not initialized yet"); - } - - auto idx = smp->signals->getIndexByName(signalName); - if (idx < 0) { - pthread_mutex_unlock(&api_node->read.mutex); - throw Error::badRequest(nullptr, "Unknown signal id: {}", signalName); - } - - auto sig = smp->signals->getByIndex(idx); - auto ch = api_node->read.channels.at(idx); - - if (payload != ch->payload) - throw Error::badRequest(nullptr, "Mismatching payload type"); - - double timestamp = 0; - json_t *json_value; - const char *validity = nullptr; - const char *source = nullptr; - const char *timesource = nullptr; - - json_error_t err; - ret = json_unpack_ex(body, &err, 0, "{ s: F, s: o, s?: s, s?: s, s?: s }", - "timestamp", ×tamp, "value", &json_value, - "validity", &validity, "timesource", ×ource, - "source", &source); - if (ret) { - pthread_mutex_unlock(&api_node->read.mutex); - throw Error::badRequest(nullptr, "Malformed body: {}", err.text); - } - - if (validity) - logger->warn("Attribute 'validity' is not supported by VILLASnode"); - - if (source) - logger->warn("Attribute 'source' is not supported by VILLASnode"); - - if (timesource) - logger->warn("Attribute 'timesource' is not supported by VILLASnode"); - - ret = smp->data[idx].parseJson(sig->type, json_value); - if (ret) { - pthread_mutex_unlock(&api_node->read.mutex); - throw Error::badRequest(nullptr, "Malformed value"); - } - - smp->ts.origin = time_from_double(timestamp); - - pthread_cond_signal(&api_node->read.cv); - pthread_mutex_unlock(&api_node->read.mutex); - - return new JsonResponse(session, HTTP_STATUS_OK, json_object()); - } - - Response *execute() override { - auto const &signalName = matches[2]; - auto const &subResource = matches[3]; - - PayloadType payload; - if (subResource == "event") - payload = PayloadType::EVENTS; - else if (subResource == "sample") - payload = PayloadType::EVENTS; - else - throw Error::badRequest(nullptr, "Unsupported sub-resource: {}", - subResource); - - switch (method) { - case Session::Method::GET: - return executeGet(signalName, payload); - case Session::Method::PUT: - return executePut(signalName, payload); - default: - throw Error::invalidMethod(this); - } - } -}; - -// Register API requests -static char n[] = "universal/channel/sample"; -static char r[] = - "/universal/(" RE_NODE_NAME ")/channel/([a-z0-9_-]+)/(sample|event)"; -static char d[] = "Retrieve or send samples via universal data-exchange API"; -static RequestPlugin p; - -} // namespace universal -} // namespace api -} // namespace node -} // namespace villas diff --git a/lib/api/requests/universal/channels.cpp b/lib/api/requests/universal/channels.cpp deleted file mode 100644 index 89ccfba8a..000000000 --- a/lib/api/requests/universal/channels.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* The Universal Data-exchange API. - * - * Author: Steffen Vogel - * SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include - -namespace villas { -namespace node { -namespace api { -namespace universal { - -class SignalsRequest : public UniversalRequest { -public: - using UniversalRequest::UniversalRequest; - - Response *execute() override { - if (method != Session::Method::GET) - throw Error::invalidMethod(this); - - if (body != nullptr) - throw Error::badRequest(nullptr, - "This endpoint does not accept any body data"); - - auto *json_sigs = json_array(); - - for (size_t i = 0; i < std::min(api_node->getOutputSignals()->size(), - api_node->write.channels.size()); - i++) { - auto sig = api_node->getOutputSignals()->at(i); - auto ch = api_node->write.channels.at(i); - auto *json_sig = ch->toJson(sig); - - json_array_append(json_sigs, json_sig); - } - - for (size_t i = 0; i < std::min(api_node->getInputSignals()->size(), - api_node->read.channels.size()); - i++) { - auto sig = api_node->getInputSignals()->at(i); - auto ch = api_node->read.channels.at(i); - auto *json_sig = ch->toJson(sig); - - json_array_append(json_sigs, json_sig); - } - - return new JsonResponse(session, HTTP_STATUS_OK, json_sigs); - } -}; - -// Register API requests -static char n[] = "universal/channels"; -static char r[] = "/universal/(" RE_NODE_NAME ")/channels"; -static char d[] = "Get channels of universal data-exchange API node"; -static RequestPlugin p; - -} // namespace universal -} // namespace api -} // namespace node -} // namespace villas diff --git a/lib/api/requests/universal/info.cpp b/lib/api/requests/universal/info.cpp deleted file mode 100644 index fa8219c81..000000000 --- a/lib/api/requests/universal/info.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* The Universal Data-exchange API. - * - * Author: Steffen Vogel - * SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include -#include - -namespace villas { -namespace node { -namespace api { -namespace universal { - -class InfoRequest : public UniversalRequest { -public: - using UniversalRequest::UniversalRequest; - - Response *execute() override { - if (method != Session::Method::GET) - throw Error::invalidMethod(this); - - if (body != nullptr) - throw Error::badRequest(nullptr, - "This endpoint does not accept any body data"); - - auto *info = json_pack("{ s: s, s: s, s: { s: s, s: s } }", // - "id", node->getNameShort().c_str(), // - "uuid", uuid::toString(node->getUuid()).c_str(), - - "transport", // - "type", "villas", // - "version", PROJECT_VERSION); - - return new JsonResponse(session, HTTP_STATUS_OK, info); - } -}; - -// Register API requests -static char n[] = "universal/info"; -static char r[] = "/universal/(" RE_NODE_NAME ")/info"; -static char d[] = "Get details of universal data-exchange API"; -static RequestPlugin p; - -} // namespace universal -} // namespace api -} // namespace node -} // namespace villas diff --git a/lib/api/requests/universal/status.cpp b/lib/api/requests/universal/status.cpp deleted file mode 100644 index 47fd5c152..000000000 --- a/lib/api/requests/universal/status.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* The Universal Data-exchange API. - * - * Author: Steffen Vogel - * SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include - -namespace villas { -namespace node { -namespace api { -namespace universal { - -class StatusRequest : public UniversalRequest { -public: - using UniversalRequest::UniversalRequest; - - Response *execute() override { - if (method != Session::Method::GET) - throw Error::invalidMethod(this); - - if (body != nullptr) - throw Error::badRequest(nullptr, - "This endpoint does not accept any body data"); - - auto *json_response = - json_pack("{ s: s }", - // TODO: Add connectivity check or heuristic here. - "connected", "unknown"); - - return new JsonResponse(session, HTTP_STATUS_OK, json_response); - } -}; - -// Register API requests -static char n[] = "universal/status"; -static char r[] = "/universal/(" RE_NODE_NAME ")/status"; -static char d[] = "Get status of universal data-exchange API"; -static RequestPlugin p; - -} // namespace universal -} // namespace api -} // namespace node -} // namespace villas diff --git a/lib/api/universal.cpp b/lib/api/universal.cpp deleted file mode 100644 index 83d99e7c3..000000000 --- a/lib/api/universal.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* API Response. - * - * Author: Steffen Vogel - * SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include - -#include -#include - -using namespace villas::node::api::universal; - -void ChannelList::parse(json_t *json, bool readable, bool writable) { - if (!json_is_array(json)) - throw ConfigError(json, "node-config-node-api-signals", - "Signal list of API node must be an array"); - - clear(); - - size_t i; - json_t *json_channel; - json_array_foreach (json, i, json_channel) { - auto channel = std::make_shared(); - - channel->parse(json_channel); - channel->readable = readable; - channel->writable = writable; - - push_back(channel); - } -} - -void Channel::parse(json_t *json) { - const char *desc = nullptr; - const char *pl = nullptr; - json_t *json_range = nullptr; - - json_error_t err; - int ret = json_unpack_ex(json, &err, 0, "{ s?: s, s?: s, s?: o, s?: F }", - "description", &desc, "payload", &pl, "range", - &json_range, "rate", &rate); - if (ret) - throw ConfigError(json, err, "node-config-node-api-signals"); - - if (desc) - description = desc; - - if (pl) { - if (!strcmp(pl, "samples")) - payload = PayloadType::SAMPLES; - else if (!strcmp(pl, "events")) - payload = PayloadType::EVENTS; - else - throw ConfigError(json, "node-config-node-api-signals-payload", - "Invalid payload type: {}", pl); - } - - range_min = std::numeric_limits::quiet_NaN(); - range_max = std::numeric_limits::quiet_NaN(); - if (json_range) { - if (json_is_array(json_range)) { - ret = json_unpack_ex(json, &err, 0, "{ s?: F, s?: F }", "min", &range_min, - "max", &range_max); - if (ret) - throw ConfigError(json, err, "node-config-node-api-signals-range", - "Failed to parse channel range"); - } else if (json_is_object(json_range)) { - size_t i; - json_t *json_option; - - range_options.clear(); - - json_array_foreach (json_range, i, json_option) { - if (!json_is_string(json_option)) - throw ConfigError(json, err, "node-config-node-api-signals-range", - "Channel range options must be strings"); - - auto *option = json_string_value(json_option); - range_options.push_back(option); - } - } else - throw ConfigError(json, "node-config-node-api-signals-range", - "Channel range must be an array or object"); - } -} - -json_t *Channel::toJson(Signal::Ptr sig) const { - json_error_t err; - json_t *json_ch = json_pack_ex( - &err, 0, "{ s: s, s: s, s: b, s: b }", "id", sig->name.c_str(), - "datatype", signalTypeToString(sig->type).c_str(), "readable", - (int)readable, "writable", (int)writable); - - if (!description.empty()) - json_object_set(json_ch, "description", json_string(description.c_str())); - - if (!sig->unit.empty()) - json_object_set(json_ch, "unit", json_string(sig->unit.c_str())); - - if (rate > 0) - json_object_set(json_ch, "rate", json_real(rate)); - - switch (payload) { - case PayloadType::EVENTS: - json_object_set(json_ch, "payload", json_string("events")); - break; - - case PayloadType::SAMPLES: - json_object_set(json_ch, "payload", json_string("samples")); - break; - - default: { - } - } - - switch (sig->type) { - case SignalType::FLOAT: { - if (std::isnan(range_min) && std::isnan(range_max)) - break; - - json_t *json_range = json_object(); - - if (!std::isnan(range_min)) - json_object_set(json_range, "min", json_real(range_min)); - - if (!std::isnan(range_max)) - json_object_set(json_range, "max", json_real(range_max)); - - json_object_set(json_ch, "range", json_range); - break; - } - - default: { - } - } - - return json_ch; -} diff --git a/lib/nodes/CMakeLists.txt b/lib/nodes/CMakeLists.txt index 068ad9fba..b04521a58 100644 --- a/lib/nodes/CMakeLists.txt +++ b/lib/nodes/CMakeLists.txt @@ -8,10 +8,6 @@ set(NODE_SRC loopback_internal.cpp ) -if(WITH_WEB) - list(APPEND NODE_SRC api.cpp) -endif() - if(LIBNL3_ROUTE_FOUND) list(APPEND LIBRARIES PkgConfig::LIBNL3_ROUTE) endif() diff --git a/lib/nodes/api.cpp b/lib/nodes/api.cpp deleted file mode 100644 index 89b9dfe37..000000000 --- a/lib/nodes/api.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* Node type: Universal Data-exchange API (v2). - * - * @see https://github.com/ERIGrid2/JRA-3.1-api - * Author: Steffen Vogel - * SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University - * SPDX-License-Identifier: Apache-2.0 - */ - -#include - -#include -#include -#include - -using namespace villas; -using namespace villas::node; -using namespace villas::node::api::universal; - -APINode::APINode(const uuid_t &id, const std::string &name) - : Node(id, name), read(), write() { - int ret; - auto dirs = std::vector{&read, &write}; - - for (auto dir : dirs) { - ret = pthread_mutex_init(&dir->mutex, nullptr); - if (ret) - throw RuntimeError("failed to initialize mutex"); - - ret = pthread_cond_init(&dir->cv, nullptr); - if (ret) - throw RuntimeError("failed to initialize mutex"); - } -} - -int APINode::prepare() { - auto signals_in = getInputSignals(false); - - read.sample = sample_alloc_mem(signals_in->size()); - if (!read.sample) - throw MemoryAllocationError(); - - write.sample = sample_alloc_mem(64); - if (!write.sample) - throw MemoryAllocationError(); - - unsigned j = 0; - for (auto sig : *signals_in) - read.sample->data[j++] = sig->init; - - read.sample->length = j; - read.sample->signals = signals_in; - - return Node::prepare(); -} - -int APINode::check() { - for (auto &ch : read.channels) { - if (ch->payload != PayloadType::SAMPLES) - return -1; - } - - for (auto &ch : write.channels) { - if (ch->payload != PayloadType::SAMPLES) - return -1; - } - - return Node::check(); -} - -int APINode::_read(struct Sample *smps[], unsigned cnt) { - assert(cnt == 1); - - pthread_cond_wait(&read.cv, &read.mutex); - - sample_copy(smps[0], read.sample); - - return 1; -} - -int APINode::_write(struct Sample *smps[], unsigned cnt) { - assert(cnt == 1); - - sample_copy(write.sample, smps[0]); - - pthread_cond_signal(&write.cv); - - return 1; -} - -int APINode::parse(json_t *json) { - int ret = Node::parse(json); - if (ret) - return ret; - - json_t *json_signals_in = nullptr; - json_t *json_signals_out = nullptr; - - json_error_t err; - ret = json_unpack_ex(json, &err, 0, "{ s?: { s?: o }, s?: { s?: o } }", "in", - "signals", &json_signals_in, "out", "signals", - &json_signals_out); - if (ret) - throw ConfigError(json, err, "node-config-node-api"); - - if (json_signals_in) - read.channels.parse(json_signals_in, false, true); - - if (json_signals_out) - write.channels.parse(json_signals_out, true, false); - - return 0; -} - -// Register node -static char n[] = "api"; -static char d[] = "A node providing a HTTP REST interface"; -static NodePlugin - p;