1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-09 00:00:00 +01:00
VILLASnode/lib/nodes/ngsi.cpp

871 lines
20 KiB
C++
Raw Permalink Normal View History

/** Node type: OMA Next Generation Services Interface 9 (NGSI) (FIWARE context broker)
*
* @author Steffen Vogel <post@steffenvogel.de>
2022-03-15 09:28:57 -04:00
* @copyright 2014-2022, Institute for Automation of Complex Power Systems, EONERC
**********************************************************************************/
#include <cstring>
#include <cstdio>
#include <unistd.h>
#include <jansson.h>
#include <pthread.h>
#include <stdio.h>
#include <openssl/err.h>
#include <openssl/opensslv.h>
#include <curl/curl.h>
#include <villas/node_compat.hpp>
#include <villas/nodes/ngsi.hpp>
#include <villas/utils.hpp>
2021-02-16 14:15:14 +01:00
#include <villas/super_node.hpp>
#include <villas/exceptions.hpp>
#include <villas/timing.hpp>
#include <villas/node/config.hpp>
using namespace villas;
2021-05-10 00:12:30 +02:00
using namespace villas::node;
2019-06-04 16:55:38 +02:00
using namespace villas::utils;
2015-11-23 16:44:18 +01:00
/* Some global settings */
#if OPENSSL_VERSION_NUMBER < 0x010003000L
// See: https://curl.haxx.se/libcurl/c/opensslthreadlock.html
#define CURL_SSL_REQUIRES_LOCKING
#endif
#ifdef CURL_SSL_REQUIRES_LOCKING
/** This array will store all of the mutexes available to OpenSSL. */
static pthread_mutex_t *mutex_buf = NULL;
static
void handle_error(const char *file, int lineno, const char *msg)
{
2021-02-16 14:15:14 +01:00
auto logger = logging.get("curl");
logger->error("** {}:{} {}", file, lineno, msg);
ERR_print_errors_fp(stderr);
2021-02-16 14:15:14 +01:00
/* exit(-1); */
}
static
void curl_ssl_locking_function(int mode, int n, const char *file, int line)
{
if (mode & CRYPTO_LOCK)
pthread_mutex_lock(&mutex_buf[n]);
else
pthread_mutex_unlock(&mutex_buf[n]);
}
static
unsigned long curl_ssl_thread_id_function(void)
{
return ((unsigned long) pthread_self());
}
#endif /* CURL_SSL_REQUIRES_LOCKING */
2015-10-11 14:45:54 +02:00
enum NgsiFlags {
NGSI_ENTITY_ATTRIBUTES_IN = (1 << 0),
NGSI_ENTITY_ATTRIBUTES_OUT = (1 << 1),
NGSI_ENTITY_ATTRIBUTES = NGSI_ENTITY_ATTRIBUTES_IN | NGSI_ENTITY_ATTRIBUTES_OUT,
NGSI_ENTITY_VALUES = (1 << 2),
NGSI_ENTITY_METADATA = (1 << 3),
2015-10-15 17:36:17 +02:00
};
class NgsiMetadata {
2015-10-15 17:36:17 +02:00
public:
NgsiMetadata(json_t *json)
{
parse(json);
}
2016-06-08 23:42:44 +02:00
NgsiMetadata(const std::string &nam, const std::string &typ, const std::string &val) :
name(nam),
type(typ),
value(val)
{ }
2016-06-08 23:42:44 +02:00
void parse(json_t *json)
{
int ret;
json_error_t err;
const char *nam, *typ, *val;
ret = json_unpack_ex(json, &err, 0, "{ s: s, s: s, s: s }",
"name", &nam,
"type", &typ,
"value", &val
);
if (ret)
2021-02-16 14:15:14 +01:00
throw ConfigError(json, "node-config-node-ngsi-metadata", "Failed to parse NGSI metadata");
name = nam;
type = typ;
value = val;
}
std::string name;
std::string type;
std::string value;
2015-10-15 17:36:17 +02:00
};
class NgsiAttribute {
public:
std::string name;
std::string type;
size_t index;
std::list<NgsiMetadata> metadata;
NgsiAttribute(json_t *json, size_t j, Signal::Ptr s)
{
parse(json, j, s);
}
void parse(json_t *json, size_t j, Signal::Ptr s)
{
int ret;
json_error_t err;
json_t *json_metadatas = nullptr;
const char *nam = nullptr;
const char *typ = nullptr;
ret = json_unpack_ex(json, &err, 0, "{ s?: s, s?: s, s?: o }",
"ngsi_attribute_name", &nam,
"ngsi_attribute_type", &typ,
"ngsi_metadatas", &json_metadatas
);
if (ret)
throw ConfigError(json, err, "node-config-node-ngsi", "Failed to parse NGSI attribute");
/* Copy values from node signal, if 'ngsi_attribute' settings not provided */
if (s && !nam)
nam = !s->name.empty() ? s->name.c_str() : "";
if (s && !typ)
typ = !s->unit.empty() ? s->unit.c_str() : "";
name = nam;
type = typ;
index = j;
if (json_metadatas) {
if (!json_is_array(json_metadatas))
2021-02-16 14:15:14 +01:00
throw ConfigError(json_metadatas, "node-config-node-ngsi-metadata", "ngsi_metadata must be a list of objects");
json_t *json_metadata;
2022-03-28 16:26:41 +02:00
// cppcheck-suppress unknownMacro
json_array_foreach(json_metadatas, j, json_metadata)
metadata.emplace_back(json_metadata);
}
/* Metadata: index(integer)=j */
metadata.emplace_back("index", "integer", fmt::format("{}", j));
}
json_t * build(const struct Sample * const smps[], unsigned cnt, int flags)
{
json_t *json_attribute = json_pack("{ s: s, s: s }",
"name", name.c_str(),
"type", type.c_str()
);
if (flags & NGSI_ENTITY_VALUES) {
#if NGSI_VECTORS
/* Build value vector */
json_t *json_value = json_array();
for (unsigned k = 0; k < cnt; k++) {
const auto *smp = &smps[k];
const auto *data = &smp->data[index];
auto sig = smp->signals->getByIndex(index);
json_array_append_new(json_value, json_pack("[ f, o, i ]",
time_to_double(smp->ts.origin),
data->toJson(sig->type),
smp->sequence
));
}
#else
const auto *smp = smps[0];
const auto sig = smp->signals->getByIndex(index);
const auto *data = &smp->data[index];
json_t *json_value = data->toJson(sig->type);
#endif
json_object_set(json_attribute, "value", json_value);
}
if (flags & NGSI_ENTITY_METADATA) { /* Create Metadata for attribute */
json_t *json_metadatas = json_array();
for (auto &meta : metadata) {
json_array_append_new(json_metadatas, json_pack("{ s: s, s: s, s: s }",
"name", meta.name.c_str(),
"type", meta.type.c_str(),
"value", meta.value.c_str()
));
}
json_object_set(json_attribute, "metadatas", json_metadatas);
}
return json_attribute;
}
};
struct ngsi_response {
char *data;
size_t len;
};
static
json_t* ngsi_build_entity(NodeCompat *n, const struct Sample * const smps[], unsigned cnt, int flags)
2015-10-14 12:19:01 +02:00
{
auto *i = n->getData<struct ngsi>();
json_t *json_entity = json_pack("{ s: s, s: s, s: b }",
"id", i->entity_id,
"type", i->entity_type,
"isPattern", 0
2015-10-14 12:19:01 +02:00
);
2015-10-15 17:36:17 +02:00
if (flags & NGSI_ENTITY_ATTRIBUTES) {
json_t *json_attrs = json_array();
if (flags & NGSI_ENTITY_ATTRIBUTES_IN) {
for (size_t j = 0; j < list_length(&i->in.signals); j++) {
auto *attr = (NgsiAttribute *) list_at(&i->in.signals, j);
auto *json_attr = attr->build(smps, cnt, flags);
json_array_append_new(json_attrs, json_attr);
}
}
if (flags & NGSI_ENTITY_ATTRIBUTES_OUT) {
for (size_t j = 0; j < list_length(&i->out.signals); j++) {
auto *attr = (NgsiAttribute *) list_at(&i->out.signals, j);
auto *json_attr = attr->build(smps, cnt, flags);
2015-10-15 17:36:17 +02:00
json_array_append_new(json_attrs, json_attr);
}
2015-10-15 17:36:17 +02:00
}
json_object_set(json_entity, "attributes", json_attrs);
2015-10-15 17:36:17 +02:00
}
return json_entity;
2015-10-14 12:19:01 +02:00
}
static
int ngsi_parse_entity(NodeCompat *n, json_t *json_entity, struct Sample * const smps[], unsigned cnt)
2015-10-14 12:19:01 +02:00
{
int ret, length = 0;
2015-10-14 12:19:01 +02:00
const char *id, *name, *type;
auto *i = n->getData<struct ngsi>();
2018-08-07 18:40:32 +02:00
size_t l;
json_error_t err;
json_t *json_attr, *json_attrs;
2015-10-14 12:19:01 +02:00
ret = json_unpack_ex(json_entity, &err, 0, "{ s: s, s: s, s: o }",
2015-10-14 12:19:01 +02:00
"id", &id,
"type", &type,
"attributes", &json_attrs
2015-10-14 12:19:01 +02:00
);
if (ret || !json_is_array(json_attrs))
2015-10-14 12:19:01 +02:00
return -1;
2015-10-14 12:19:01 +02:00
if (strcmp(id, i->entity_id) || strcmp(type, i->entity_type))
return -2;
json_array_foreach(json_attrs, l, json_attr) {
NgsiAttribute *attr;
json_error_t err;
json_t *json_metadata, *json_value;
char *end;
const char *value;
2015-10-14 12:19:01 +02:00
/* Parse JSON */
ret = json_unpack_ex(json_attr, &err, 0, "{ s: s, s: s, s: o, s?: o }",
2015-10-14 12:19:01 +02:00
"name", &name,
"type", &type,
"value", &json_value,
"metadatas", &json_metadata
2015-10-14 12:19:01 +02:00
);
if (ret)
return -3;
2015-10-14 12:19:01 +02:00
/* Check attribute name and type */
attr = list_lookup_name<NgsiAttribute>(&i->in.signals, name);
if (!attr || attr->type != type)
continue; /* skip unknown attributes */
length++;
2015-10-14 12:19:01 +02:00
/* Check metadata */
if (!json_is_array(json_metadata))
2015-10-14 12:19:01 +02:00
return -5;
#ifdef NGSI_VECTORS
json_t *json_tuple;
const char *ts, *seq;
2015-10-14 12:19:01 +02:00
/* Check number of values */
if (!json_is_array(json_value) || json_array_size(json_value) != cnt)
2015-10-14 12:19:01 +02:00
return -6;
2016-06-08 23:42:44 +02:00
size_t k;
json_array_foreach(json_value, k, json_tuple) {
struct Sample *smp = smps[k];
2015-10-14 12:19:01 +02:00
/* Check sample format */
if (!json_is_array(json_tuple) || json_array_size(json_tuple) != 3)
2015-10-14 12:19:01 +02:00
return -7;
ret = json_unpack_ex(json_tuple, &err, 0, "[ s, s, s ]", &ts, &value, &seq);
if (ret)
2015-10-14 12:19:01 +02:00
return -8;
smp->sequence = atoi(seq);
2015-10-14 12:19:01 +02:00
struct timespec tss = time_from_double(strtod(ts, &end));
if (ts == end)
return -9;
smp->ts.origin = tss;
auto *sd = &smp->data[attr->index];
auto sig = n->getInputSignals(false)->getByIndex(attr->index);
if (!sig)
return -11;
if (value[0] == '\0') /* No data on Orion CB? -> Use init value */
*sd = sig->init;
else {
signal_data_parse_str(sd, sig->type, value, &end);
if (value == end)
return -10;
}
}
#else
struct Sample *smp = smps[0];
/* Check number of values */
if (!json_is_string(json_value))
return -6;
value = json_string_value(json_value);
auto *data = &smp->data[attr->index];
auto sig = n->getInputSignals(false)->getByIndex(attr->index);
if (!sig)
return -11;
if (value[0] == '\0') /* No data on Orion CB? -> Use init value */
*data = sig->init;
else {
data->parseString(sig->type, value, &end);
2015-10-14 12:19:01 +02:00
if (value == end)
return -10;
}
#endif
2015-10-14 12:19:01 +02:00
}
for (unsigned k = 0; k < cnt; k++) {
struct Sample *smp = smps[k];
smp->length = length;
smp->signals = n->getInputSignals(false);
smp->flags = (int) SampleFlags::HAS_DATA;
#ifdef NGSI_VECTORS
smp->flags |= (int) (SampleFlags::HAS_SEQUENCE |
SampleFlags::HAS_TS_ORIGIN);
#endif
}
2015-10-14 12:19:01 +02:00
return cnt;
}
static
int ngsi_parse_signals(json_t *json_signals, struct List *ngsi_signals, SignalList::Ptr node_signals)
2015-10-15 17:36:17 +02:00
{
2018-08-07 18:40:32 +02:00
size_t j;
json_t *json_signal;
if (!json_is_array(json_signals))
return -1;
json_array_foreach(json_signals, j, json_signal) {
auto s = node_signals->getByIndex(j);
auto *a = new NgsiAttribute(json_signal, j, s);
if (!a)
throw MemoryAllocationError();
list_push(ngsi_signals, a);
2015-10-15 17:36:17 +02:00
}
return 0;
}
static
int ngsi_parse_context_response(json_t *json_response, int *code, char **reason, json_t **json_rentity, Logger logger) {
2015-10-15 17:36:17 +02:00
int ret;
char *codestr;
json_error_t err;
ret = json_unpack_ex(json_response, &err, 0, "{ s: [ { s: O, s: { s: s, s: s } } ] }",
2015-10-15 17:36:17 +02:00
"contextResponses",
"contextElement", json_rentity,
2015-10-15 17:36:17 +02:00
"statusCode",
"code", &codestr,
"reasonPhrase", reason
);
2015-10-15 17:36:17 +02:00
if (ret) {
2021-02-16 14:15:14 +01:00
logger->warn("Failed to find NGSI response code");
2015-10-15 17:36:17 +02:00
return ret;
}
2015-10-15 17:36:17 +02:00
*code = atoi(codestr);
2015-10-15 17:36:17 +02:00
if (*code != 200)
2021-02-16 14:15:14 +01:00
logger->warn("NGSI response: {} {}", codestr, *reason);
2015-10-15 17:36:17 +02:00
2015-10-15 18:23:57 +02:00
return ret;
2015-10-15 17:36:17 +02:00
}
static
size_t ngsi_request_writer(void *contents, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
struct ngsi_response *mem = (struct ngsi_response *) userp;
2019-04-22 23:43:46 +02:00
mem->data = (char *) realloc(mem->data, mem->len + realsize + 1);
2019-04-22 23:45:38 +02:00
if (mem->data == nullptr) /* out of memory! */
throw MemoryAllocationError();
memcpy(&(mem->data[mem->len]), contents, realsize);
mem->len += realsize;
mem->data[mem->len] = 0;
return realsize;
}
static
int ngsi_request(CURL *handle, const char *endpoint, const char *operation, json_t *json_request, json_t **json_response, Logger logger)
{
struct ngsi_response chunk = { 0 };
char *post = json_dumps(json_request, JSON_INDENT(4));
2015-10-15 17:36:17 +02:00
int old;
double time;
char url[128];
2015-10-15 17:36:17 +02:00
json_error_t err;
snprintf(url, sizeof(url), "%s/v1/%s", endpoint, operation);
curl_easy_setopt(handle, CURLOPT_URL, url);
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ngsi_request_writer);
curl_easy_setopt(handle, CURLOPT_WRITEDATA, (void *) &chunk);
curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, strlen(post));
curl_easy_setopt(handle, CURLOPT_POSTFIELDS, post);
2021-02-16 14:15:14 +01:00
logger->debug("Request to context broker: {}\n{}", url, post);
2015-10-15 17:36:17 +02:00
/* We don't want to leave the handle in an invalid state */
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old);
CURLcode ret = curl_easy_perform(handle);
2019-04-22 23:45:38 +02:00
pthread_setcancelstate(old, nullptr);
2015-10-15 17:36:17 +02:00
if (ret) {
2021-02-16 14:15:14 +01:00
logger->warn("HTTP request failed: {}", curl_easy_strerror(ret));
2015-10-15 17:36:17 +02:00
goto out;
}
2015-09-28 21:32:04 +02:00
curl_easy_getinfo(handle, CURLINFO_TOTAL_TIME, &time);
2021-02-16 14:15:14 +01:00
logger->debug("Request to context broker completed in {} seconds", time);
logger->debug("Response from context broker:\n{}", chunk.data);
*json_response = json_loads(chunk.data, 0, &err);
if (!*json_response)
2021-02-16 14:15:14 +01:00
logger->warn("Received invalid JSON: {} in {}:{}:{}\n{}", err.text, err.source, err.line, err.column, chunk.data);
2015-10-15 17:36:17 +02:00
out: free(post);
free(chunk.data);
2015-10-15 18:23:57 +02:00
return ret;
}
static
int ngsi_request_context_query(CURL *handle, const char *endpoint, json_t *json_entity, json_t **json_rentity, Logger logger)
2015-10-15 17:36:17 +02:00
{
int ret, code;
char *reason;
json_t *json_response;
2022-11-23 10:02:51 +01:00
json_t *json_request = json_pack("{ s: [ O ] }", "entities", json_entity);
2015-10-15 17:36:17 +02:00
2021-02-16 14:15:14 +01:00
ret = ngsi_request(handle, endpoint, "queryContext", json_request, &json_response, logger);
if (ret) {
ret = -1;
2015-10-15 17:36:17 +02:00
goto out;
}
2021-02-16 14:15:14 +01:00
ret = ngsi_parse_context_response(json_response, &code, &reason, json_rentity, logger);
2015-10-15 17:36:17 +02:00
if (ret)
goto out2;
out2: json_decref(json_response);
out: json_decref(json_request);
2015-10-15 17:36:17 +02:00
return ret;
}
static
int ngsi_request_context_update(CURL *handle, const char *endpoint, const char *action, json_t *json_entity, Logger logger)
2015-10-15 17:36:17 +02:00
{
int ret, code;
char *reason;
json_t *json_response;
2022-11-23 10:02:51 +01:00
json_t *json_request = json_pack("{ s: s, s: [ O ] }",
2015-10-15 17:36:17 +02:00
"updateAction", action,
"contextElements", json_entity
2015-10-15 17:36:17 +02:00
);
2021-02-16 14:15:14 +01:00
ret = ngsi_request(handle, endpoint, "updateContext", json_request, &json_response, logger);
2015-10-15 17:36:17 +02:00
if (ret)
goto out;
json_t *json_rentity;
2021-02-16 14:15:14 +01:00
ret = ngsi_parse_context_response(json_response, &code, &reason, &json_rentity, logger);
2015-10-15 17:36:17 +02:00
if (ret)
goto out2;
json_decref(json_rentity);
out2: json_decref(json_response);
out: json_decref(json_request);
2015-10-15 17:36:17 +02:00
return ret;
}
int villas::node::ngsi_type_start(villas::node::SuperNode *sn)
{
#ifdef CURL_SSL_REQUIRES_LOCKING
mutex_buf = new pthread_mutex_t[CRYPTO_num_locks()];
if (!mutex_buf)
return -1;
for (int i = 0; i < CRYPTO_num_locks(); i++)
pthread_mutex_init(&mutex_buf[i], nullptr);
CRYPTO_set_id_callback(curl_ssl_thread_id_function);
CRYPTO_set_locking_callback(curl_ssl_locking_function);
auto logger = logging.get("curl");
logger->info("Setup libcurl/openssl locking primitives");
#endif /* CURL_SSL_REQUIRES_LOCKING */
return curl_global_init(CURL_GLOBAL_ALL);
}
int villas::node::ngsi_type_stop()
{
#ifdef CURL_SSL_REQUIRES_LOCKING
if (!mutex_buf)
return -1;
CRYPTO_set_id_callback(NULL);
CRYPTO_set_locking_callback(NULL);
for (int i = 0; i < CRYPTO_num_locks(); i++)
pthread_mutex_destroy(&mutex_buf[i]);
delete mutex_buf;
#endif /* CURL_SSL_REQUIRES_LOCKING */
2015-11-23 16:44:18 +01:00
curl_global_cleanup();
return 0;
}
int villas::node::ngsi_parse(NodeCompat *n, json_t *json)
{
auto *i = n->getData<struct ngsi>();
int ret;
json_error_t err;
json_t *json_signals_in = nullptr;
json_t *json_signals_out = nullptr;
int create = 1;
int remove = 1;
2021-02-16 14:15:14 +01:00
ret = json_unpack_ex(json, &err, 0, "{ s?: s, s: s, s: s, s: s, s?: b, s?: F, s?: F, s?: b, s?: b, s?: { s?: o }, s?: { s?: o } }",
"access_token", &i->access_token,
"endpoint", &i->endpoint,
"entity_id", &i->entity_id,
"entity_type", &i->entity_type,
"ssl_verify", &i->ssl_verify,
"timeout", &i->timeout,
"rate", &i->rate,
"create", &create,
"delete", &remove,
"in",
"signals", &json_signals_in,
"out",
"signals", &json_signals_out
);
if (ret)
2021-02-16 14:15:14 +01:00
throw ConfigError(json, err, "node-config-node-ngsi");
i->create = create;
i->remove = remove;
2015-09-22 15:58:19 +02:00
if (json_signals_in) {
ret = ngsi_parse_signals(json_signals_in, &i->in.signals, n->in.signals);
if (ret)
throw ConfigError(json_signals_in, "node-config-node-ngsi-in-signals", "Invalid setting 'in.signals' of node {}", n->getName());
}
if (json_signals_out) {
ret = ngsi_parse_signals(json_signals_out, &i->out.signals, n->out.signals);
if (ret)
throw ConfigError(json_signals_out, "node-config-node-ngsi-out-signals", "Invalid setting 'out.signals' of node {}", n->getName());
}
return 0;
}
char * villas::node::ngsi_print(NodeCompat *n)
{
auto *i = n->getData<struct ngsi>();
return strf("endpoint=%s, timeout=%.3f secs",
i->endpoint, i->timeout);
}
int villas::node::ngsi_start(NodeCompat *n)
{
auto *i = n->getData<struct ngsi>();
i->in.curl = curl_easy_init();
i->out.curl = curl_easy_init();
2019-04-22 23:45:38 +02:00
i->headers = nullptr;
if (i->access_token) {
2015-10-14 12:19:01 +02:00
char buf[128];
snprintf(buf, sizeof(buf), "Auth-Token: %s", i->access_token);
i->headers = curl_slist_append(i->headers, buf);
}
2017-08-22 12:16:33 +02:00
/* Create task */
2015-10-14 12:19:01 +02:00
if (i->timeout > 1 / i->rate)
2021-02-16 14:15:14 +01:00
n->logger->warn("Timeout is to large for given rate: {}", i->rate);
2015-10-14 12:19:01 +02:00
2020-03-04 13:06:28 +01:00
i->task.setRate(i->rate);
i->headers = curl_slist_append(i->headers, "Accept: application/json");
i->headers = curl_slist_append(i->headers, "Content-Type: application/json");
CURL *handles[] = { i->in.curl, i->out.curl };
for (unsigned p = 0; p < ARRAY_LEN(handles); p++) {
curl_easy_setopt(handles[p], CURLOPT_SSL_VERIFYPEER, i->ssl_verify);
curl_easy_setopt(handles[p], CURLOPT_TIMEOUT_MS, i->timeout * 1e3);
curl_easy_setopt(handles[p], CURLOPT_HTTPHEADER, i->headers);
curl_easy_setopt(handles[p], CURLOPT_USERAGENT, HTTP_USER_AGENT);
}
/* Create entity and atributes */
if (i->create) {
json_t *json_entity = ngsi_build_entity(n, nullptr, 0, NGSI_ENTITY_ATTRIBUTES | NGSI_ENTITY_METADATA);
2021-02-16 14:15:14 +01:00
int ret = ngsi_request_context_update(i->out.curl, i->endpoint, "APPEND", json_entity, n->logger);
if (ret)
throw RuntimeError("Failed to create NGSI context for node {}", n->getName());
json_decref(json_entity);
}
return 0;
}
int villas::node::ngsi_stop(NodeCompat *n)
{
auto *i = n->getData<struct ngsi>();
2015-10-14 12:19:01 +02:00
int ret;
2020-03-04 13:06:28 +01:00
i->task.stop();
2015-10-14 12:19:01 +02:00
/* Delete complete entity (not just attributes) */
json_t *json_entity = ngsi_build_entity(n, nullptr, 0, 0);
2015-10-14 12:19:01 +02:00
2021-02-16 14:15:14 +01:00
ret = ngsi_request_context_update(i->out.curl, i->endpoint, "DELETE", json_entity, n->logger);
json_decref(json_entity);
curl_easy_cleanup(i->in.curl);
curl_easy_cleanup(i->out.curl);
curl_slist_free_all(i->headers);
2015-10-14 12:19:01 +02:00
return ret;
}
int villas::node::ngsi_read(NodeCompat *n, struct Sample * const smps[], unsigned cnt)
{
auto *i = n->getData<struct ngsi>();
int ret;
2020-03-04 13:06:28 +01:00
if (i->task.wait() == 0)
2021-06-16 10:36:19 -04:00
throw SystemError("Failed to wait for task");
json_t *json_rentity;
json_t *json_entity = ngsi_build_entity(n, nullptr, 0, 0);
2021-02-16 14:15:14 +01:00
ret = ngsi_request_context_query(i->in.curl, i->endpoint, json_entity, &json_rentity, n->logger);
2015-10-15 17:36:17 +02:00
if (ret)
goto out;
ret = ngsi_parse_entity(n, json_rentity, smps, cnt);
2015-10-15 17:36:17 +02:00
if (ret)
goto out2;
out2: json_decref(json_rentity);
out: json_decref(json_entity);
2015-10-14 12:19:01 +02:00
2015-10-15 18:23:57 +02:00
return ret;
}
int villas::node::ngsi_write(NodeCompat *n, struct Sample * const smps[], unsigned cnt)
{
auto *i = n->getData<struct ngsi>();
2015-10-15 17:36:17 +02:00
int ret;
json_t *json_entity = ngsi_build_entity(n, smps, cnt, NGSI_ENTITY_ATTRIBUTES_OUT | NGSI_ENTITY_VALUES);
2015-10-14 12:19:01 +02:00
2021-02-16 14:15:14 +01:00
ret = ngsi_request_context_update(i->out.curl, i->endpoint, "UPDATE", json_entity, n->logger);
json_decref(json_entity);
return ret ? 0 : cnt;
}
int villas::node::ngsi_poll_fds(NodeCompat *n, int fds[])
{
auto *i = n->getData<struct ngsi>();
2020-03-04 13:06:28 +01:00
fds[0] = i->task.getFD();
return 1;
}
int villas::node::ngsi_init(NodeCompat *n)
2020-03-04 13:06:28 +01:00
{
int ret;
auto *i = n->getData<struct ngsi>();
2020-03-04 13:06:28 +01:00
new (&i->task) Task(CLOCK_REALTIME);
ret = list_init(&i->in.signals);
if (ret)
return ret;
ret = list_init(&i->out.signals);
if (ret)
return ret;
/* Default values */
i->access_token = nullptr; /* disabled by default */
i->ssl_verify = 1; /* verify by default */
i->timeout = 1; /* default value */
i->rate = 1; /* default value */
2020-03-04 13:06:28 +01:00
return 0;
}
int villas::node::ngsi_destroy(NodeCompat *n)
2020-03-04 13:06:28 +01:00
{
int ret;
auto *i = n->getData<struct ngsi>();
2020-03-04 13:06:28 +01:00
for (size_t j = 0; j < list_length(&i->in.signals); j++) {
auto *attr = (NgsiAttribute *) list_at(&i->in.signals, j);
delete attr;
}
for (size_t j = 0; j < list_length(&i->out.signals); j++) {
auto *attr = (NgsiAttribute *) list_at(&i->out.signals, j);
delete attr;
}
ret = list_destroy(&i->in.signals);
if (ret)
return ret;
ret = list_destroy(&i->out.signals);
if (ret)
return ret;
2020-03-04 13:06:28 +01:00
i->task.~Task();
return 0;
}
int villas::node::ngsi_reverse(NodeCompat *n)
{
auto *i = n->getData<struct ngsi>();
n->swapSignals();
SWAP(i->in.signals, i->out.signals);
return 0;
}
static NodeCompatType p;
__attribute__((constructor(110)))
static void register_plugin() {
p.name = "ngsi";
p.description = "OMA Next Generation Services Interface 10 (libcurl, libjansson)";
#ifdef NGSI_VECTORS
2021-06-21 16:11:42 -04:00
p.vectorize = 0, /* unlimited */
#else
2021-06-21 16:11:42 -04:00
p.vectorize = 1,
#endif
2021-06-21 16:11:42 -04:00
p.size = sizeof(struct ngsi);
p.type.start = ngsi_type_start;
p.type.stop = ngsi_type_stop;
p.init = ngsi_init;
p.destroy = ngsi_destroy;
p.parse = ngsi_parse;
p.print = ngsi_print;
p.start = ngsi_start;
p.stop = ngsi_stop;
p.read = ngsi_read;
p.write = ngsi_write;
p.poll_fds = ngsi_poll_fds;
p.reverse = ngsi_reverse;
static NodeCompatFactory ncp(&p);
}