2018-07-11 18:14:29 +02:00
|
|
|
/** Node type: OMA Next Generation Services Interface 9 (NGSI) (FIWARE context broker)
|
2015-09-19 18:54:27 +02:00
|
|
|
*
|
2022-12-14 17:41:58 +01:00
|
|
|
* @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
|
2015-09-19 18:54:27 +02:00
|
|
|
**********************************************************************************/
|
|
|
|
|
2019-06-23 16:57:00 +02:00
|
|
|
#include <cstring>
|
|
|
|
#include <cstdio>
|
2015-09-21 17:13:50 +02:00
|
|
|
|
2020-08-24 21:01:48 +02:00
|
|
|
#include <unistd.h>
|
2015-09-19 18:54:27 +02:00
|
|
|
#include <jansson.h>
|
2015-10-13 16:11:58 +02:00
|
|
|
#include <pthread.h>
|
2020-08-24 21:01:48 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <openssl/err.h>
|
|
|
|
#include <openssl/opensslv.h>
|
|
|
|
#include <curl/curl.h>
|
2015-09-19 18:54:27 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
#include <villas/node_compat.hpp>
|
2019-04-23 00:12:31 +02:00
|
|
|
#include <villas/nodes/ngsi.hpp>
|
2019-04-23 13:15:00 +02:00
|
|
|
#include <villas/utils.hpp>
|
2021-02-16 14:15:14 +01:00
|
|
|
#include <villas/super_node.hpp>
|
2020-07-04 16:22:10 +02:00
|
|
|
#include <villas/exceptions.hpp>
|
2021-08-10 10:12:48 -04:00
|
|
|
#include <villas/timing.hpp>
|
|
|
|
#include <villas/node/config.hpp>
|
2015-09-19 18:54:27 +02:00
|
|
|
|
2020-07-04 16:22:10 +02:00
|
|
|
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 */
|
2020-08-24 21:01:48 +02:00
|
|
|
|
|
|
|
#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;
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
static
|
2020-08-24 21:01:48 +02:00
|
|
|
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);
|
|
|
|
|
2020-08-24 21:01:48 +02:00
|
|
|
ERR_print_errors_fp(stderr);
|
2021-02-16 14:15:14 +01:00
|
|
|
|
2020-08-24 21:01:48 +02:00
|
|
|
/* exit(-1); */
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
static
|
|
|
|
void curl_ssl_locking_function(int mode, int n, const char *file, int line)
|
2020-08-24 21:01:48 +02:00
|
|
|
{
|
|
|
|
if (mode & CRYPTO_LOCK)
|
|
|
|
pthread_mutex_lock(&mutex_buf[n]);
|
|
|
|
else
|
|
|
|
pthread_mutex_unlock(&mutex_buf[n]);
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
static
|
|
|
|
unsigned long curl_ssl_thread_id_function(void)
|
2020-08-24 21:01:48 +02:00
|
|
|
{
|
|
|
|
return ((unsigned long) pthread_self());
|
|
|
|
}
|
|
|
|
#endif /* CURL_SSL_REQUIRES_LOCKING */
|
2015-10-11 14:45:54 +02:00
|
|
|
|
2020-08-17 12:52:08 +02:00
|
|
|
enum NgsiFlags {
|
2020-08-24 21:01:48 +02:00
|
|
|
NGSI_ENTITY_ATTRIBUTES_IN = (1 << 0),
|
2020-08-17 12:52:08 +02:00
|
|
|
NGSI_ENTITY_ATTRIBUTES_OUT = (1 << 1),
|
2020-08-24 21:01:48 +02:00
|
|
|
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
|
|
|
};
|
|
|
|
|
2020-08-17 12:52:08 +02:00
|
|
|
class NgsiMetadata {
|
2015-10-15 17:36:17 +02:00
|
|
|
|
2020-08-17 12:52:08 +02:00
|
|
|
public:
|
|
|
|
NgsiMetadata(json_t *json)
|
|
|
|
{
|
|
|
|
parse(json);
|
|
|
|
}
|
2016-06-08 23:42:44 +02:00
|
|
|
|
2020-08-17 12:52:08 +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
|
|
|
|
2020-08-17 12:52:08 +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");
|
2020-08-17 12:52:08 +02:00
|
|
|
|
|
|
|
name = nam;
|
|
|
|
type = typ;
|
|
|
|
value = val;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string name;
|
|
|
|
std::string type;
|
|
|
|
std::string value;
|
2015-10-15 17:36:17 +02:00
|
|
|
};
|
|
|
|
|
2020-08-17 12:52:08 +02:00
|
|
|
class NgsiAttribute {
|
|
|
|
|
|
|
|
public:
|
|
|
|
std::string name;
|
|
|
|
std::string type;
|
|
|
|
|
|
|
|
size_t index;
|
|
|
|
std::list<NgsiMetadata> metadata;
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
NgsiAttribute(json_t *json, size_t j, Signal::Ptr s)
|
2020-08-17 12:52:08 +02:00
|
|
|
{
|
|
|
|
parse(json, j, s);
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
void parse(json_t *json, size_t j, Signal::Ptr s)
|
2020-08-17 12:52:08 +02:00
|
|
|
{
|
|
|
|
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)
|
2021-08-10 10:12:48 -04:00
|
|
|
nam = !s->name.empty() ? s->name.c_str() : "";
|
2020-08-17 12:52:08 +02:00
|
|
|
|
|
|
|
if (s && !typ)
|
2021-08-10 10:12:48 -04:00
|
|
|
typ = !s->unit.empty() ? s->unit.c_str() : "";
|
2020-08-05 17:27:54 +02:00
|
|
|
|
2020-08-17 12:52:08 +02:00
|
|
|
name = nam;
|
|
|
|
type = typ;
|
|
|
|
index = j;
|
2020-08-05 17:27:54 +02:00
|
|
|
|
2020-08-17 12:52:08 +02:00
|
|
|
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");
|
2020-08-17 12:52:08 +02:00
|
|
|
|
|
|
|
json_t *json_metadata;
|
2022-03-28 16:26:41 +02:00
|
|
|
|
|
|
|
// cppcheck-suppress unknownMacro
|
2020-08-17 12:52:08 +02:00
|
|
|
json_array_foreach(json_metadatas, j, json_metadata)
|
|
|
|
metadata.emplace_back(json_metadata);
|
2020-08-05 17:27:54 +02:00
|
|
|
}
|
|
|
|
|
2020-08-17 12:52:08 +02:00
|
|
|
/* Metadata: index(integer)=j */
|
|
|
|
metadata.emplace_back("index", "integer", fmt::format("{}", j));
|
2020-08-05 17:27:54 +02:00
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
json_t * build(const struct Sample * const smps[], unsigned cnt, int flags)
|
2020-08-17 12:52:08 +02:00
|
|
|
{
|
|
|
|
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++) {
|
2021-08-10 10:12:48 -04:00
|
|
|
const auto *smp = &smps[k];
|
|
|
|
const auto *data = &smp->data[index];
|
2023-01-18 11:38:08 +00:00
|
|
|
auto sig = smp->signals->getByIndex(index);
|
2020-08-17 12:52:08 +02:00
|
|
|
|
|
|
|
json_array_append_new(json_value, json_pack("[ f, o, i ]",
|
|
|
|
time_to_double(smp->ts.origin),
|
2021-08-10 10:12:48 -04:00
|
|
|
data->toJson(sig->type),
|
2020-08-17 12:52:08 +02:00
|
|
|
smp->sequence
|
|
|
|
));
|
|
|
|
}
|
|
|
|
#else
|
2021-08-10 10:12:48 -04:00
|
|
|
const auto *smp = smps[0];
|
|
|
|
const auto sig = smp->signals->getByIndex(index);
|
|
|
|
const auto *data = &smp->data[index];
|
2020-08-05 17:27:54 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
json_t *json_value = data->toJson(sig->type);
|
2020-08-17 12:52:08 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
json_object_set(json_attribute, "value", json_value);
|
2020-08-05 17:27:54 +02:00
|
|
|
}
|
|
|
|
|
2020-08-17 12:52:08 +02:00
|
|
|
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 }",
|
2020-08-24 21:01:48 +02:00
|
|
|
"name", meta.name.c_str(),
|
|
|
|
"type", meta.type.c_str(),
|
2020-08-17 12:52:08 +02:00
|
|
|
"value", meta.value.c_str()
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
json_object_set(json_attribute, "metadatas", json_metadatas);
|
|
|
|
}
|
|
|
|
|
|
|
|
return json_attribute;
|
2020-08-05 17:27:54 +02:00
|
|
|
}
|
2020-08-17 12:52:08 +02:00
|
|
|
};
|
2020-08-05 17:27:54 +02:00
|
|
|
|
2020-08-17 12:52:08 +02:00
|
|
|
struct ngsi_response {
|
|
|
|
char *data;
|
|
|
|
size_t len;
|
|
|
|
};
|
2020-08-05 17:27:54 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
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
|
|
|
{
|
2021-08-10 10:12:48 -04:00
|
|
|
auto *i = n->getData<struct ngsi>();
|
2020-07-06 15:07:05 +02:00
|
|
|
|
2020-08-05 17:27:54 +02:00
|
|
|
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
|
|
|
);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
if (flags & NGSI_ENTITY_ATTRIBUTES) {
|
2020-08-05 17:27:54 +02:00
|
|
|
json_t *json_attrs = json_array();
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-08-17 12:52:08 +02:00
|
|
|
if (flags & NGSI_ENTITY_ATTRIBUTES_IN) {
|
2021-08-10 10:12:48 -04:00
|
|
|
for (size_t j = 0; j < list_length(&i->in.signals); j++) {
|
|
|
|
auto *attr = (NgsiAttribute *) list_at(&i->in.signals, j);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-08-17 12:52:08 +02:00
|
|
|
auto *json_attr = attr->build(smps, cnt, flags);
|
2017-03-25 21:23:31 +01:00
|
|
|
|
2020-08-17 12:52:08 +02:00
|
|
|
json_array_append_new(json_attrs, json_attr);
|
|
|
|
}
|
2020-08-05 17:27:54 +02:00
|
|
|
}
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-08-17 12:52:08 +02:00
|
|
|
if (flags & NGSI_ENTITY_ATTRIBUTES_OUT) {
|
2021-08-10 10:12:48 -04:00
|
|
|
for (size_t j = 0; j < list_length(&i->out.signals); j++) {
|
|
|
|
auto *attr = (NgsiAttribute *) list_at(&i->out.signals, j);
|
2020-08-05 17:27:54 +02:00
|
|
|
|
2020-08-17 12:52:08 +02:00
|
|
|
auto *json_attr = attr->build(smps, cnt, flags);
|
2015-10-15 17:36:17 +02:00
|
|
|
|
2020-08-17 12:52:08 +02:00
|
|
|
json_array_append_new(json_attrs, json_attr);
|
|
|
|
}
|
2015-10-15 17:36:17 +02:00
|
|
|
}
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-08-05 17:27:54 +02:00
|
|
|
json_object_set(json_entity, "attributes", json_attrs);
|
2015-10-15 17:36:17 +02:00
|
|
|
}
|
|
|
|
|
2020-08-05 17:27:54 +02:00
|
|
|
return json_entity;
|
2015-10-14 12:19:01 +02:00
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04: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
|
|
|
{
|
2020-08-05 17:27:54 +02:00
|
|
|
int ret, length = 0;
|
2015-10-14 12:19:01 +02:00
|
|
|
const char *id, *name, *type;
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
auto *i = n->getData<struct ngsi>();
|
2020-07-06 15:07:05 +02:00
|
|
|
|
2018-08-07 18:40:32 +02:00
|
|
|
size_t l;
|
2019-03-23 20:49:19 +01:00
|
|
|
json_error_t err;
|
2020-08-05 17:27:54 +02:00
|
|
|
json_t *json_attr, *json_attrs;
|
2015-10-14 12:19:01 +02:00
|
|
|
|
2020-08-05 17:27:54 +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,
|
2020-08-05 17:27:54 +02:00
|
|
|
"attributes", &json_attrs
|
2015-10-14 12:19:01 +02:00
|
|
|
);
|
2020-08-05 17:27:54 +02:00
|
|
|
if (ret || !json_is_array(json_attrs))
|
2015-10-14 12:19:01 +02:00
|
|
|
return -1;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-14 12:19:01 +02:00
|
|
|
if (strcmp(id, i->entity_id) || strcmp(type, i->entity_type))
|
|
|
|
return -2;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-08-05 17:27:54 +02:00
|
|
|
json_array_foreach(json_attrs, l, json_attr) {
|
2020-08-17 12:52:08 +02:00
|
|
|
NgsiAttribute *attr;
|
2019-03-23 20:49:19 +01:00
|
|
|
json_error_t err;
|
2020-08-17 12:52:08 +02:00
|
|
|
json_t *json_metadata, *json_value;
|
|
|
|
|
|
|
|
char *end;
|
|
|
|
const char *value;
|
2015-10-14 12:19:01 +02:00
|
|
|
|
|
|
|
/* Parse JSON */
|
2020-08-05 17:27:54 +02:00
|
|
|
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,
|
2020-08-17 12:52:08 +02:00
|
|
|
"value", &json_value,
|
2020-08-05 17:27:54 +02:00
|
|
|
"metadatas", &json_metadata
|
2015-10-14 12:19:01 +02:00
|
|
|
);
|
|
|
|
if (ret)
|
|
|
|
return -3;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-14 12:19:01 +02:00
|
|
|
/* Check attribute name and type */
|
2021-08-10 10:12:48 -04:00
|
|
|
attr = list_lookup_name<NgsiAttribute>(&i->in.signals, name);
|
2020-08-17 12:52:08 +02:00
|
|
|
if (!attr || attr->type != type)
|
2020-08-05 17:27:54 +02:00
|
|
|
continue; /* skip unknown attributes */
|
|
|
|
|
|
|
|
length++;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-14 12:19:01 +02:00
|
|
|
/* Check metadata */
|
2020-08-05 17:27:54 +02:00
|
|
|
if (!json_is_array(json_metadata))
|
2015-10-14 12:19:01 +02:00
|
|
|
return -5;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-08-17 12:52:08 +02:00
|
|
|
#ifdef NGSI_VECTORS
|
|
|
|
json_t *json_tuple;
|
|
|
|
const char *ts, *seq;
|
|
|
|
|
2015-10-14 12:19:01 +02:00
|
|
|
/* Check number of values */
|
2020-08-17 12:52:08 +02:00
|
|
|
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;
|
2020-08-17 12:52:08 +02:00
|
|
|
json_array_foreach(json_value, k, json_tuple) {
|
2021-08-10 10:12:48 -04:00
|
|
|
struct Sample *smp = smps[k];
|
2020-08-17 12:52:08 +02:00
|
|
|
|
2015-10-14 12:19:01 +02:00
|
|
|
/* Check sample format */
|
2020-08-05 17:27:54 +02:00
|
|
|
if (!json_is_array(json_tuple) || json_array_size(json_tuple) != 3)
|
2015-10-14 12:19:01 +02:00
|
|
|
return -7;
|
|
|
|
|
2020-08-05 17:27:54 +02:00
|
|
|
ret = json_unpack_ex(json_tuple, &err, 0, "[ s, s, s ]", &ts, &value, &seq);
|
2017-05-05 19:24:16 +00:00
|
|
|
if (ret)
|
2015-10-14 12:19:01 +02:00
|
|
|
return -8;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-08-17 12:52:08 +02:00
|
|
|
smp->sequence = atoi(seq);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-14 12:19:01 +02:00
|
|
|
struct timespec tss = time_from_double(strtod(ts, &end));
|
|
|
|
if (ts == end)
|
|
|
|
return -9;
|
|
|
|
|
2020-08-17 12:52:08 +02:00
|
|
|
smp->ts.origin = tss;
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
auto *sd = &smp->data[attr->index];
|
2023-01-18 11:38:08 +00:00
|
|
|
auto sig = n->getInputSignals(false)->getByIndex(attr->index);
|
2020-08-17 12:52:08 +02:00
|
|
|
if (!sig)
|
|
|
|
return -11;
|
|
|
|
|
|
|
|
if (value[0] == '\0') /* No data on Orion CB? -> Use init value */
|
|
|
|
*sd = sig->init;
|
|
|
|
else {
|
2021-02-19 06:37:21 +01:00
|
|
|
signal_data_parse_str(sd, sig->type, value, &end);
|
2020-08-17 12:52:08 +02:00
|
|
|
if (value == end)
|
|
|
|
return -10;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
2021-08-10 10:12:48 -04:00
|
|
|
struct Sample *smp = smps[0];
|
2020-08-17 12:52:08 +02:00
|
|
|
|
|
|
|
/* Check number of values */
|
|
|
|
if (!json_is_string(json_value))
|
|
|
|
return -6;
|
|
|
|
|
|
|
|
value = json_string_value(json_value);
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
auto *data = &smp->data[attr->index];
|
|
|
|
auto sig = n->getInputSignals(false)->getByIndex(attr->index);
|
2020-08-17 12:52:08 +02:00
|
|
|
if (!sig)
|
|
|
|
return -11;
|
|
|
|
|
|
|
|
if (value[0] == '\0') /* No data on Orion CB? -> Use init value */
|
2021-08-10 10:12:48 -04:00
|
|
|
*data = sig->init;
|
2020-08-17 12:52:08 +02:00
|
|
|
else {
|
2021-08-10 10:12:48 -04:00
|
|
|
data->parseString(sig->type, value, &end);
|
2015-10-14 12:19:01 +02:00
|
|
|
if (value == end)
|
|
|
|
return -10;
|
|
|
|
}
|
2020-08-17 12:52:08 +02:00
|
|
|
#endif
|
2015-10-14 12:19:01 +02:00
|
|
|
}
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-08-05 17:27:54 +02:00
|
|
|
for (unsigned k = 0; k < cnt; k++) {
|
2021-08-10 10:12:48 -04:00
|
|
|
struct Sample *smp = smps[k];
|
2020-08-17 12:52:08 +02:00
|
|
|
|
|
|
|
smp->length = length;
|
2021-08-10 10:12:48 -04:00
|
|
|
smp->signals = n->getInputSignals(false);
|
2020-08-17 12:52:08 +02:00
|
|
|
smp->flags = (int) SampleFlags::HAS_DATA;
|
|
|
|
|
|
|
|
#ifdef NGSI_VECTORS
|
|
|
|
smp->flags |= (int) (SampleFlags::HAS_SEQUENCE |
|
2020-08-24 21:01:48 +02:00
|
|
|
SampleFlags::HAS_TS_ORIGIN);
|
2020-08-17 12:52:08 +02:00
|
|
|
#endif
|
2020-08-05 17:27:54 +02:00
|
|
|
}
|
|
|
|
|
2015-10-14 12:19:01 +02:00
|
|
|
return cnt;
|
|
|
|
}
|
2015-09-19 18:54:27 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
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;
|
2020-08-17 12:52:08 +02:00
|
|
|
json_t *json_signal;
|
2017-08-03 00:19:27 +02:00
|
|
|
|
2020-08-05 17:27:54 +02:00
|
|
|
if (!json_is_array(json_signals))
|
|
|
|
return -1;
|
2017-08-03 00:19:27 +02:00
|
|
|
|
2020-08-05 17:27:54 +02:00
|
|
|
json_array_foreach(json_signals, j, json_signal) {
|
2021-08-10 10:12:48 -04:00
|
|
|
auto s = node_signals->getByIndex(j);
|
2020-08-17 12:52:08 +02:00
|
|
|
auto *a = new NgsiAttribute(json_signal, j, s);
|
2020-07-04 16:22:10 +02:00
|
|
|
if (!a)
|
|
|
|
throw MemoryAllocationError();
|
2017-07-09 14:36:09 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
list_push(ngsi_signals, a);
|
2015-10-15 17:36:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
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;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2019-03-23 20:49:19 +01:00
|
|
|
json_error_t err;
|
|
|
|
|
2020-08-05 17:27:54 +02:00
|
|
|
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",
|
2020-08-05 17:27:54 +02:00
|
|
|
"contextElement", json_rentity,
|
2015-10-15 17:36:17 +02:00
|
|
|
"statusCode",
|
|
|
|
"code", &codestr,
|
|
|
|
"reasonPhrase", reason
|
2017-05-05 19:24:16 +00:00
|
|
|
);
|
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;
|
|
|
|
}
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
*code = atoi(codestr);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
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
|
|
|
}
|
2015-09-21 17:13:50 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
static
|
|
|
|
size_t ngsi_request_writer(void *contents, size_t size, size_t nmemb, void *userp)
|
2015-09-19 18:54:27 +02:00
|
|
|
{
|
2015-09-21 17:13:50 +02:00
|
|
|
size_t realsize = size * nmemb;
|
|
|
|
struct ngsi_response *mem = (struct ngsi_response *) userp;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
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! */
|
2020-08-17 12:52:08 +02:00
|
|
|
throw MemoryAllocationError();
|
2015-09-21 17:13:50 +02:00
|
|
|
|
|
|
|
memcpy(&(mem->data[mem->len]), contents, realsize);
|
|
|
|
mem->len += realsize;
|
|
|
|
mem->data[mem->len] = 0;
|
2015-09-19 18:54:27 +02:00
|
|
|
|
2015-09-21 17:13:50 +02:00
|
|
|
return realsize;
|
2015-09-19 18:54:27 +02:00
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
static
|
|
|
|
int ngsi_request(CURL *handle, const char *endpoint, const char *operation, json_t *json_request, json_t **json_response, Logger logger)
|
2015-09-19 18:54:27 +02:00
|
|
|
{
|
2015-09-21 17:13:50 +02:00
|
|
|
struct ngsi_response chunk = { 0 };
|
2020-08-05 17:27:54 +02:00
|
|
|
char *post = json_dumps(json_request, JSON_INDENT(4));
|
2015-10-15 17:36:17 +02:00
|
|
|
int old;
|
|
|
|
double time;
|
2015-10-09 17:20:58 +02:00
|
|
|
char url[128];
|
2015-10-15 17:36:17 +02:00
|
|
|
json_error_t err;
|
|
|
|
|
2015-10-09 17:20:58 +02:00
|
|
|
snprintf(url, sizeof(url), "%s/v1/%s", endpoint, operation);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-09 17:20:58 +02:00
|
|
|
curl_easy_setopt(handle, CURLOPT_URL, url);
|
2015-09-21 17:13:50 +02:00
|
|
|
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ngsi_request_writer);
|
2017-05-05 19:24:16 +00:00
|
|
|
curl_easy_setopt(handle, CURLOPT_WRITEDATA, (void *) &chunk);
|
2015-09-21 17:13:50 +02:00
|
|
|
curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, strlen(post));
|
|
|
|
curl_easy_setopt(handle, CURLOPT_POSTFIELDS, post);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2021-02-16 14:15:14 +01:00
|
|
|
logger->debug("Request to context broker: {}\n{}", url, post);
|
2015-10-13 16:11:58 +02:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
/* We don't want to leave the handle in an invalid state */
|
2015-10-13 16:11:58 +02:00
|
|
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old);
|
2015-09-21 17:13:50 +02:00
|
|
|
CURLcode ret = curl_easy_perform(handle);
|
2019-04-22 23:45:38 +02:00
|
|
|
pthread_setcancelstate(old, nullptr);
|
2015-10-13 16:11:58 +02:00
|
|
|
|
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-19 18:54:27 +02:00
|
|
|
|
2015-09-28 21:32:04 +02:00
|
|
|
curl_easy_getinfo(handle, CURLINFO_TOTAL_TIME, &time);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
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);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-08-05 17:27:54 +02:00
|
|
|
*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);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
out: free(post);
|
2015-09-21 17:13:50 +02:00
|
|
|
free(chunk.data);
|
|
|
|
|
2015-10-15 18:23:57 +02:00
|
|
|
return ret;
|
2015-09-21 17:13:50 +02:00
|
|
|
}
|
2015-09-19 18:54:27 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
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;
|
|
|
|
|
2020-08-05 17:27:54 +02:00
|
|
|
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);
|
2020-08-24 21:01:48 +02:00
|
|
|
if (ret) {
|
|
|
|
ret = -1;
|
2015-10-15 17:36:17 +02:00
|
|
|
goto out;
|
2020-08-24 21:01:48 +02:00
|
|
|
}
|
2017-05-05 19:24:16 +00:00
|
|
|
|
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;
|
|
|
|
|
2020-08-05 17:27:54 +02:00
|
|
|
out2: json_decref(json_response);
|
|
|
|
out: json_decref(json_request);
|
2015-10-15 17:36:17 +02:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
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;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-08-05 17:27:54 +02:00
|
|
|
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,
|
2020-08-05 17:27:54 +02:00
|
|
|
"contextElements", json_entity
|
2015-10-15 17:36:17 +02:00
|
|
|
);
|
2017-05-05 19:24:16 +00: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;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-08-05 17:27:54 +02:00
|
|
|
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;
|
|
|
|
|
2020-08-05 17:27:54 +02:00
|
|
|
json_decref(json_rentity);
|
|
|
|
out2: json_decref(json_response);
|
|
|
|
out: json_decref(json_request);
|
2015-10-15 17:36:17 +02:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int villas::node::ngsi_type_start(villas::node::SuperNode *sn)
|
2015-09-19 18:54:27 +02:00
|
|
|
{
|
2020-08-24 21:01:48 +02:00
|
|
|
#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);
|
|
|
|
|
2022-12-24 15:02:05 +01:00
|
|
|
auto logger = logging.get("curl");
|
|
|
|
logger->info("Setup libcurl/openssl locking primitives");
|
2020-08-24 21:01:48 +02:00
|
|
|
#endif /* CURL_SSL_REQUIRES_LOCKING */
|
|
|
|
|
2015-09-21 17:13:50 +02:00
|
|
|
return curl_global_init(CURL_GLOBAL_ALL);
|
2015-09-19 18:54:27 +02:00
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int villas::node::ngsi_type_stop()
|
2015-09-19 18:54:27 +02:00
|
|
|
{
|
2020-08-24 21:01:48 +02:00
|
|
|
#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
|
|
|
|
2015-09-19 18:54:27 +02:00
|
|
|
curl_global_cleanup();
|
2015-09-21 17:13:50 +02:00
|
|
|
|
|
|
|
return 0;
|
2015-09-19 18:54:27 +02:00
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int villas::node::ngsi_parse(NodeCompat *n, json_t *json)
|
2015-09-19 18:54:27 +02:00
|
|
|
{
|
2021-08-10 10:12:48 -04:00
|
|
|
auto *i = n->getData<struct ngsi>();
|
2015-09-19 18:54:27 +02:00
|
|
|
|
2017-08-03 00:19:27 +02:00
|
|
|
int ret;
|
|
|
|
json_error_t err;
|
2020-08-05 17:27:54 +02:00
|
|
|
json_t *json_signals_in = nullptr;
|
|
|
|
json_t *json_signals_out = nullptr;
|
2017-08-03 00:19:27 +02:00
|
|
|
|
2020-08-17 12:52:08 +02:00
|
|
|
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 } }",
|
2017-08-03 00:19:27 +02:00
|
|
|
"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,
|
2020-08-17 12:52:08 +02:00
|
|
|
"create", &create,
|
|
|
|
"delete", &remove,
|
2020-08-05 17:27:54 +02:00
|
|
|
"in",
|
|
|
|
"signals", &json_signals_in,
|
|
|
|
"out",
|
|
|
|
"signals", &json_signals_out
|
2017-08-03 00:19:27 +02:00
|
|
|
);
|
|
|
|
if (ret)
|
2021-02-16 14:15:14 +01:00
|
|
|
throw ConfigError(json, err, "node-config-node-ngsi");
|
2020-08-17 12:52:08 +02:00
|
|
|
|
|
|
|
i->create = create;
|
|
|
|
i->remove = remove;
|
2015-09-22 15:58:19 +02:00
|
|
|
|
2020-08-05 17:27:54 +02:00
|
|
|
if (json_signals_in) {
|
2021-08-10 10:12:48 -04:00
|
|
|
ret = ngsi_parse_signals(json_signals_in, &i->in.signals, n->in.signals);
|
2020-08-05 17:27:54 +02:00
|
|
|
if (ret)
|
2023-01-10 15:25:27 +01:00
|
|
|
throw ConfigError(json_signals_in, "node-config-node-ngsi-in-signals", "Invalid setting 'in.signals' of node {}", n->getName());
|
2020-08-05 17:27:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (json_signals_out) {
|
2021-08-10 10:12:48 -04:00
|
|
|
ret = ngsi_parse_signals(json_signals_out, &i->out.signals, n->out.signals);
|
2020-08-05 17:27:54 +02:00
|
|
|
if (ret)
|
2023-01-10 15:25:27 +01:00
|
|
|
throw ConfigError(json_signals_out, "node-config-node-ngsi-out-signals", "Invalid setting 'out.signals' of node {}", n->getName());
|
2020-08-05 17:27:54 +02:00
|
|
|
}
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-09-21 17:13:50 +02:00
|
|
|
return 0;
|
2015-09-19 18:54:27 +02:00
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
char * villas::node::ngsi_print(NodeCompat *n)
|
2015-09-19 18:54:27 +02:00
|
|
|
{
|
2021-08-10 10:12:48 -04:00
|
|
|
auto *i = n->getData<struct ngsi>();
|
2015-09-22 12:58:37 +02:00
|
|
|
|
2020-08-05 17:27:54 +02:00
|
|
|
return strf("endpoint=%s, timeout=%.3f secs",
|
|
|
|
i->endpoint, i->timeout);
|
2015-09-19 18:54:27 +02:00
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int villas::node::ngsi_start(NodeCompat *n)
|
2015-09-19 18:54:27 +02:00
|
|
|
{
|
2021-08-10 10:12:48 -04:00
|
|
|
auto *i = n->getData<struct ngsi>();
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-08-24 21:01:48 +02:00
|
|
|
i->in.curl = curl_easy_init();
|
|
|
|
i->out.curl = curl_easy_init();
|
2019-04-22 23:45:38 +02:00
|
|
|
i->headers = nullptr;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-12 15:29:26 +02:00
|
|
|
if (i->access_token) {
|
2015-10-14 12:19:01 +02:00
|
|
|
char buf[128];
|
2015-10-12 15:29:26 +02:00
|
|
|
snprintf(buf, sizeof(buf), "Auth-Token: %s", i->access_token);
|
2019-03-26 14:04:50 +01:00
|
|
|
i->headers = curl_slist_append(i->headers, buf);
|
2015-09-19 18:54:27 +02:00
|
|
|
}
|
2017-05-05 19:24:16 +00:00
|
|
|
|
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);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2019-03-26 14:04:50 +01:00
|
|
|
i->headers = curl_slist_append(i->headers, "Accept: application/json");
|
|
|
|
i->headers = curl_slist_append(i->headers, "Content-Type: application/json");
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-08-24 21:01:48 +02:00
|
|
|
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);
|
2020-10-20 22:17:55 +02:00
|
|
|
curl_easy_setopt(handles[p], CURLOPT_USERAGENT, HTTP_USER_AGENT);
|
2020-08-24 21:01:48 +02:00
|
|
|
}
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-09-21 17:13:50 +02:00
|
|
|
/* Create entity and atributes */
|
2020-08-17 12:52:08 +02:00
|
|
|
if (i->create) {
|
|
|
|
json_t *json_entity = ngsi_build_entity(n, nullptr, 0, NGSI_ENTITY_ATTRIBUTES | NGSI_ENTITY_METADATA);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2021-02-16 14:15:14 +01:00
|
|
|
int ret = ngsi_request_context_update(i->out.curl, i->endpoint, "APPEND", json_entity, n->logger);
|
2020-08-17 12:52:08 +02:00
|
|
|
if (ret)
|
2023-01-10 15:25:27 +01:00
|
|
|
throw RuntimeError("Failed to create NGSI context for node {}", n->getName());
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-08-17 12:52:08 +02:00
|
|
|
json_decref(json_entity);
|
|
|
|
}
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-09-10 18:23:25 +02:00
|
|
|
return 0;
|
2015-09-19 18:54:27 +02:00
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int villas::node::ngsi_stop(NodeCompat *n)
|
2015-09-19 18:54:27 +02:00
|
|
|
{
|
2021-08-10 10:12:48 -04:00
|
|
|
auto *i = n->getData<struct ngsi>();
|
2015-10-14 12:19:01 +02:00
|
|
|
int ret;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
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) */
|
2020-08-05 17:27:54 +02:00
|
|
|
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);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-08-05 17:27:54 +02:00
|
|
|
json_decref(json_entity);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-08-24 21:01:48 +02:00
|
|
|
curl_easy_cleanup(i->in.curl);
|
|
|
|
curl_easy_cleanup(i->out.curl);
|
2019-03-26 14:04:50 +01:00
|
|
|
curl_slist_free_all(i->headers);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-14 12:19:01 +02:00
|
|
|
return ret;
|
2015-09-19 18:54:27 +02:00
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int villas::node::ngsi_read(NodeCompat *n, struct Sample * const smps[], unsigned cnt)
|
2015-10-12 15:29:26 +02:00
|
|
|
{
|
2021-08-10 10:12:48 -04:00
|
|
|
auto *i = n->getData<struct ngsi>();
|
2015-10-12 15:29:26 +02:00
|
|
|
int ret;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
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");
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-08-05 17:27:54 +02:00
|
|
|
json_t *json_rentity;
|
|
|
|
json_t *json_entity = ngsi_build_entity(n, nullptr, 0, 0);
|
2015-10-12 15:29:26 +02:00
|
|
|
|
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;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-08-05 17:27:54 +02:00
|
|
|
ret = ngsi_parse_entity(n, json_rentity, smps, cnt);
|
2015-10-15 17:36:17 +02:00
|
|
|
if (ret)
|
|
|
|
goto out2;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-08-05 17:27:54 +02:00
|
|
|
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;
|
2015-09-19 18:54:27 +02:00
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int villas::node::ngsi_write(NodeCompat *n, struct Sample * const smps[], unsigned cnt)
|
2015-09-19 18:54:27 +02:00
|
|
|
{
|
2021-08-10 10:12:48 -04:00
|
|
|
auto *i = n->getData<struct ngsi>();
|
2015-10-15 17:36:17 +02:00
|
|
|
int ret;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-08-17 12:52:08 +02:00
|
|
|
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);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2020-08-05 17:27:54 +02:00
|
|
|
json_decref(json_entity);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2018-07-11 18:14:29 +02:00
|
|
|
return ret ? 0 : cnt;
|
2015-09-19 18:54:27 +02:00
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int villas::node::ngsi_poll_fds(NodeCompat *n, int fds[])
|
2017-08-30 00:25:42 +02:00
|
|
|
{
|
2021-08-10 10:12:48 -04:00
|
|
|
auto *i = n->getData<struct ngsi>();
|
2017-09-04 23:16:58 +02:00
|
|
|
|
2020-03-04 13:06:28 +01:00
|
|
|
fds[0] = i->task.getFD();
|
2019-01-21 15:47:34 +01:00
|
|
|
|
|
|
|
return 1;
|
2017-08-30 00:25:42 +02:00
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int villas::node::ngsi_init(NodeCompat *n)
|
2020-03-04 13:06:28 +01:00
|
|
|
{
|
2020-08-05 17:27:54 +02:00
|
|
|
int ret;
|
2021-08-10 10:12:48 -04:00
|
|
|
auto *i = n->getData<struct ngsi>();
|
2020-03-04 13:06:28 +01:00
|
|
|
|
|
|
|
new (&i->task) Task(CLOCK_REALTIME);
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
ret = list_init(&i->in.signals);
|
2020-08-05 17:27:54 +02:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
ret = list_init(&i->out.signals);
|
2020-08-05 17:27:54 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int villas::node::ngsi_destroy(NodeCompat *n)
|
2020-03-04 13:06:28 +01:00
|
|
|
{
|
2020-08-05 17:27:54 +02:00
|
|
|
int ret;
|
2021-08-10 10:12:48 -04:00
|
|
|
auto *i = n->getData<struct ngsi>();
|
2020-03-04 13:06:28 +01:00
|
|
|
|
2020-08-17 12:52:08 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
for (size_t j = 0; j < list_length(&i->in.signals); j++) {
|
|
|
|
auto *attr = (NgsiAttribute *) list_at(&i->in.signals, j);
|
2020-08-17 12:52:08 +02:00
|
|
|
|
|
|
|
delete attr;
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
for (size_t j = 0; j < list_length(&i->out.signals); j++) {
|
|
|
|
auto *attr = (NgsiAttribute *) list_at(&i->out.signals, j);
|
2020-08-17 12:52:08 +02:00
|
|
|
|
|
|
|
delete attr;
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
ret = list_destroy(&i->in.signals);
|
2020-08-05 17:27:54 +02:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
ret = list_destroy(&i->out.signals);
|
2020-08-05 17:27:54 +02:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2020-03-04 13:06:28 +01:00
|
|
|
|
|
|
|
i->task.~Task();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int villas::node::ngsi_reverse(NodeCompat *n)
|
2020-08-17 12:52:08 +02:00
|
|
|
{
|
2021-08-10 10:12:48 -04:00
|
|
|
auto *i = n->getData<struct ngsi>();
|
2020-08-17 12:52:08 +02:00
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
n->swapSignals();
|
2020-08-17 12:52:08 +02:00
|
|
|
SWAP(i->in.signals, i->out.signals);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
static NodeCompatType p;
|
2019-04-23 00:36:06 +02:00
|
|
|
|
|
|
|
__attribute__((constructor(110)))
|
|
|
|
static void register_plugin() {
|
|
|
|
p.name = "ngsi";
|
|
|
|
p.description = "OMA Next Generation Services Interface 10 (libcurl, libjansson)";
|
2020-08-17 12:52:08 +02:00
|
|
|
#ifdef NGSI_VECTORS
|
2021-06-21 16:11:42 -04:00
|
|
|
p.vectorize = 0, /* unlimited */
|
2020-08-17 12:52:08 +02:00
|
|
|
#else
|
2021-06-21 16:11:42 -04:00
|
|
|
p.vectorize = 1,
|
2020-08-17 12:52:08 +02:00
|
|
|
#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;
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
static NodeCompatFactory ncp(&p);
|
2019-04-23 13:15:00 +02:00
|
|
|
}
|