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
|
|
|
*
|
|
|
|
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
2017-03-03 20:20:13 -04:00
|
|
|
* @copyright 2017, Institute for Automation of Complex Power Systems, EONERC
|
2017-04-27 12:56:43 +02:00
|
|
|
* @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.
|
2017-05-05 19:24:16 +00:00
|
|
|
*
|
2017-04-27 12:56:43 +02:00
|
|
|
* 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.
|
2017-05-05 19:24:16 +00:00
|
|
|
*
|
2017-04-27 12:56:43 +02:00
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2015-09-19 18:54:27 +02:00
|
|
|
**********************************************************************************/
|
|
|
|
|
2015-09-21 17:13:50 +02:00
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
2015-09-19 18:54:27 +02:00
|
|
|
#include <curl/curl.h>
|
|
|
|
#include <jansson.h>
|
2015-10-13 16:11:58 +02:00
|
|
|
#include <pthread.h>
|
2015-12-11 12:35:32 +01:00
|
|
|
#include <unistd.h>
|
2015-09-19 18:54:27 +02:00
|
|
|
|
2017-12-09 02:19:28 +08:00
|
|
|
#include <villas/nodes/ngsi.h>
|
|
|
|
#include <villas/utils.h>
|
|
|
|
#include <villas/timing.h>
|
|
|
|
#include <villas/plugin.h>
|
|
|
|
#include <villas/config.h>
|
2015-09-19 18:54:27 +02:00
|
|
|
|
2015-11-23 16:44:18 +01:00
|
|
|
/* Some global settings */
|
|
|
|
static char *name = NULL;
|
2015-10-11 14:45:54 +02:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
enum ngsi_flags {
|
|
|
|
NGSI_ENTITY_ATTRIBUTES = (1 << 0),
|
|
|
|
NGSI_ENTITY_VALUES = (1 << 1) | NGSI_ENTITY_ATTRIBUTES,
|
|
|
|
NGSI_ENTITY_METADATA = (1 << 2) | NGSI_ENTITY_ATTRIBUTES,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ngsi_metadata {
|
|
|
|
char *name;
|
|
|
|
char *type;
|
|
|
|
char *value;
|
|
|
|
};
|
|
|
|
|
2016-06-08 23:42:44 +02:00
|
|
|
struct ngsi_attribute {
|
|
|
|
char *name;
|
|
|
|
char *type;
|
|
|
|
|
|
|
|
int index;
|
|
|
|
struct list metadata;
|
|
|
|
};
|
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
struct ngsi_response {
|
|
|
|
char *data;
|
|
|
|
size_t len;
|
|
|
|
};
|
|
|
|
|
2016-06-08 23:42:44 +02:00
|
|
|
static json_t* ngsi_build_entity(struct ngsi *i, struct sample *smps[], unsigned cnt, int flags)
|
2015-10-14 12:19:01 +02:00
|
|
|
{
|
2015-10-15 17:36:17 +02:00
|
|
|
json_t *entity = json_pack("{ s: s, s: s, s: b }",
|
2015-10-14 12:19:01 +02:00
|
|
|
"id", i->entity_id,
|
|
|
|
"type", i->entity_type,
|
2015-10-15 17:36:17 +02:00
|
|
|
"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) {
|
|
|
|
json_t *attributes = json_array();
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2017-03-25 21:23:31 +01:00
|
|
|
for (size_t j = 0; j < list_length(&i->mapping); j++) {
|
2017-10-18 15:39:53 +02:00
|
|
|
struct ngsi_attribute *map = (struct ngsi_attribute *) list_at(&i->mapping, j);
|
2017-03-25 21:23:31 +01:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
json_t *attribute = json_pack("{ s: s, s: s }",
|
|
|
|
"name", map->name,
|
|
|
|
"type", map->type
|
|
|
|
);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
if (flags & NGSI_ENTITY_VALUES) { /* Build value vector */
|
|
|
|
json_t *values = json_array();
|
|
|
|
for (int k = 0; k < cnt; k++) {
|
|
|
|
json_array_append_new(values, json_pack("[ f, f, i ]",
|
2016-06-08 23:42:44 +02:00
|
|
|
time_to_double(&smps[k]->ts.origin),
|
2016-07-11 18:19:23 +02:00
|
|
|
smps[k]->data[map->index].f,
|
2016-06-08 23:42:44 +02:00
|
|
|
smps[k]->sequence
|
2015-10-15 17:36:17 +02:00
|
|
|
));
|
|
|
|
}
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
json_object_set(attribute, "value", values);
|
|
|
|
}
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
if (flags & NGSI_ENTITY_METADATA) { /* Create Metadata for attribute */
|
|
|
|
json_t *metadatas = json_array();
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2017-03-25 21:23:31 +01:00
|
|
|
for (size_t i = 0; i < list_length(&map->metadata); i++) {
|
2017-10-18 15:39:53 +02:00
|
|
|
struct ngsi_metadata *meta = (struct ngsi_metadata *) list_at(&map->metadata, i);
|
2017-03-25 21:23:31 +01:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
json_array_append_new(metadatas, json_pack("{ s: s, s: s, s: s }",
|
|
|
|
"name", meta->name,
|
|
|
|
"type", meta->type,
|
|
|
|
"value", meta->value
|
|
|
|
));
|
|
|
|
}
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
json_object_set(attribute, "metadatas", metadatas);
|
|
|
|
}
|
|
|
|
|
|
|
|
json_array_append_new(attributes, attribute);
|
|
|
|
}
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
json_object_set(entity, "attributes", attributes);
|
|
|
|
}
|
|
|
|
|
|
|
|
return entity;
|
2015-10-14 12:19:01 +02:00
|
|
|
}
|
|
|
|
|
2016-06-08 23:42:44 +02:00
|
|
|
static int ngsi_parse_entity(json_t *entity, struct ngsi *i, struct sample *smps[], unsigned cnt)
|
2015-10-14 12:19:01 +02:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
const char *id, *name, *type;
|
|
|
|
|
|
|
|
size_t index;
|
|
|
|
json_t *attribute, *attributes;
|
|
|
|
|
|
|
|
ret = json_unpack(entity, "{ s: s, s: s, s: o }",
|
|
|
|
"id", &id,
|
|
|
|
"type", &type,
|
|
|
|
"attributes", &attributes
|
|
|
|
);
|
|
|
|
if (ret || !json_is_array(attributes))
|
|
|
|
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
|
|
|
|
2016-06-08 23:42:44 +02:00
|
|
|
for (int k = 0; k < cnt; k++)
|
|
|
|
smps[k]->length = json_array_size(attributes);
|
2015-10-14 12:19:01 +02:00
|
|
|
|
|
|
|
json_array_foreach(attributes, index, attribute) {
|
2015-10-15 17:36:17 +02:00
|
|
|
struct ngsi_attribute *map;
|
2015-10-14 12:19:01 +02:00
|
|
|
json_t *metadata, *values, *tuple;
|
|
|
|
|
|
|
|
/* Parse JSON */
|
2015-10-15 17:36:17 +02:00
|
|
|
ret = json_unpack(attribute, "{ s: s, s: s, s: o, s?: o }",
|
2015-10-14 12:19:01 +02:00
|
|
|
"name", &name,
|
|
|
|
"type", &type,
|
|
|
|
"value", &values,
|
|
|
|
"metadatas", &metadata
|
|
|
|
);
|
|
|
|
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 */
|
|
|
|
map = list_lookup(&i->mapping, name);
|
|
|
|
if (!map || strcmp(map->type, type))
|
|
|
|
return -4;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-14 12:19:01 +02:00
|
|
|
/* Check metadata */
|
|
|
|
if (!json_is_array(metadata))
|
|
|
|
return -5;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-14 12:19:01 +02:00
|
|
|
/* Check number of values */
|
|
|
|
if (!json_is_array(values) || json_array_size(values) != cnt)
|
|
|
|
return -6;
|
|
|
|
|
2016-06-08 23:42:44 +02:00
|
|
|
size_t k;
|
|
|
|
json_array_foreach(values, k, tuple) {
|
2015-10-14 12:19:01 +02:00
|
|
|
/* Check sample format */
|
|
|
|
if (!json_is_array(tuple) || json_array_size(tuple) != 3)
|
|
|
|
return -7;
|
|
|
|
|
|
|
|
char *end;
|
|
|
|
const char *value, *ts, *seq;
|
|
|
|
ret = json_unpack(tuple, "[ 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
|
|
|
|
2016-06-08 23:42:44 +02:00
|
|
|
smps[k]->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;
|
|
|
|
|
2016-06-08 23:42:44 +02:00
|
|
|
smps[k]->ts.origin = tss;
|
2016-07-11 18:19:23 +02:00
|
|
|
smps[k]->data[map->index].f = strtof(value, &end);
|
2015-10-14 12:19:01 +02:00
|
|
|
if (value == end)
|
|
|
|
return -10;
|
|
|
|
}
|
|
|
|
}
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-14 12:19:01 +02:00
|
|
|
return cnt;
|
|
|
|
}
|
2015-09-19 18:54:27 +02:00
|
|
|
|
2017-08-03 00:19:27 +02:00
|
|
|
static int ngsi_parse_mapping(struct list *mapping, json_t *cfg)
|
2015-10-15 17:36:17 +02:00
|
|
|
{
|
2017-08-03 00:19:27 +02:00
|
|
|
if (!json_is_array(cfg))
|
2015-10-15 17:36:17 +02:00
|
|
|
return -1;
|
|
|
|
|
2016-06-08 23:42:44 +02:00
|
|
|
list_init(mapping);
|
2015-10-15 17:36:17 +02:00
|
|
|
|
2017-08-03 00:19:27 +02:00
|
|
|
size_t index;
|
2017-10-16 08:08:35 +02:00
|
|
|
json_t *json_token;
|
2017-08-03 00:19:27 +02:00
|
|
|
|
2017-10-16 08:08:35 +02:00
|
|
|
json_array_foreach(cfg, index, json_token) {
|
2017-08-03 00:19:27 +02:00
|
|
|
const char *token;
|
|
|
|
|
2017-10-16 08:08:35 +02:00
|
|
|
token = json_string_value(json_token);
|
2015-10-15 17:36:17 +02:00
|
|
|
if (!token)
|
|
|
|
return -2;
|
|
|
|
|
2017-10-18 15:39:53 +02:00
|
|
|
struct ngsi_attribute *a = (struct ngsi_attribute *) alloc(sizeof(struct ngsi_attribute));
|
2017-07-09 14:36:09 +02:00
|
|
|
|
2017-08-03 00:19:27 +02:00
|
|
|
a->index = index;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
/* Parse Attribute: AttributeName(AttributeType) */
|
|
|
|
int bytes;
|
2017-07-09 14:36:09 +02:00
|
|
|
if (sscanf(token, "%m[^(](%m[^)])%n", &a->name, &a->type, &bytes) != 2)
|
2017-08-03 00:19:27 +02:00
|
|
|
error("Invalid mapping token: '%s'", token);
|
2015-10-15 17:36:17 +02:00
|
|
|
|
2017-05-05 19:24:16 +00:00
|
|
|
token += bytes;
|
2015-10-15 17:36:17 +02:00
|
|
|
|
|
|
|
/* MetadataName(MetadataType)=MetadataValue */
|
2017-07-09 14:36:09 +02:00
|
|
|
list_init(&a->metadata);
|
2017-07-24 19:33:35 +02:00
|
|
|
|
2017-07-09 14:36:09 +02:00
|
|
|
struct ngsi_metadata m;
|
|
|
|
while (sscanf(token, " %m[^(](%m[^)])=%ms%n", &m.name, &m.type, &m.value, &bytes) == 3) {
|
|
|
|
list_push(&a->metadata, memdup(&m, sizeof(m)));
|
2015-10-15 17:36:17 +02:00
|
|
|
token += bytes;
|
|
|
|
}
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2017-07-09 14:36:09 +02:00
|
|
|
/* Metadata: source(string)=name */
|
|
|
|
struct ngsi_metadata s = {
|
2015-10-15 17:36:17 +02:00
|
|
|
.name = "source",
|
|
|
|
.type = "string",
|
2017-07-09 14:36:09 +02:00
|
|
|
.value = name
|
2015-10-15 17:36:17 +02:00
|
|
|
};
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2017-07-09 14:36:09 +02:00
|
|
|
/* Metadata: index(integer)=j */
|
|
|
|
struct ngsi_metadata i = {
|
2015-10-15 17:36:17 +02:00
|
|
|
.name = "index",
|
2017-07-09 14:36:09 +02:00
|
|
|
.type = "integer"
|
2015-10-15 17:36:17 +02:00
|
|
|
};
|
2017-08-03 00:19:27 +02:00
|
|
|
assert(asprintf(&i.value, "%zu", index));
|
2015-10-15 17:36:17 +02:00
|
|
|
|
2017-07-09 14:36:09 +02:00
|
|
|
list_push(&a->metadata, memdup(&s, sizeof(s)));
|
|
|
|
list_push(&a->metadata, memdup(&i, sizeof(i)));
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2017-07-09 14:36:09 +02:00
|
|
|
list_push(mapping, a);
|
2015-10-15 17:36:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ngsi_parse_context_response(json_t *response, int *code, char **reason, json_t **rentity) {
|
|
|
|
int ret;
|
|
|
|
char *codestr;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-15 18:23:57 +02:00
|
|
|
ret = json_unpack(response, "{ s: [ { s: O, s: { s: s, s: s } } ] }",
|
2015-10-15 17:36:17 +02:00
|
|
|
"contextResponses",
|
|
|
|
"contextElement", rentity,
|
|
|
|
"statusCode",
|
|
|
|
"code", &codestr,
|
|
|
|
"reasonPhrase", reason
|
2017-05-05 19:24:16 +00:00
|
|
|
);
|
2015-10-15 17:36:17 +02:00
|
|
|
if (ret) {
|
|
|
|
warn("Failed to find NGSI response code");
|
|
|
|
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)
|
|
|
|
warn("NGSI response: %s %s", codestr, *reason);
|
|
|
|
|
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
|
|
|
|
|
|
|
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
|
|
|
|
2015-09-21 17:13:50 +02:00
|
|
|
mem->data = realloc(mem->data, mem->len + realsize + 1);
|
|
|
|
if(mem->data == NULL) /* out of memory! */
|
|
|
|
error("Not enough memory (realloc returned NULL)");
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2015-10-14 12:19:01 +02:00
|
|
|
static int ngsi_request(CURL *handle, const char *endpoint, const char *operation, json_t *request, json_t **response)
|
2015-09-19 18:54:27 +02:00
|
|
|
{
|
2015-09-21 17:13:50 +02:00
|
|
|
struct ngsi_response chunk = { 0 };
|
2015-10-14 12:19:01 +02:00
|
|
|
char *post = json_dumps(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
|
|
|
|
2017-02-12 14:12:35 -03:00
|
|
|
debug(LOG_NGSI | 18, "Request to context broker: %s\n%s", 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);
|
2015-10-13 16:11:58 +02:00
|
|
|
pthread_setcancelstate(old, NULL);
|
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
if (ret) {
|
|
|
|
warn("HTTP request failed: %s", curl_easy_strerror(ret));
|
|
|
|
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
|
|
|
|
2017-02-12 14:12:35 -03:00
|
|
|
debug(LOG_NGSI | 16, "Request to context broker completed in %.4f seconds", time);
|
|
|
|
debug(LOG_NGSI | 17, "Response from context broker:\n%s", chunk.data);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
*response = json_loads(chunk.data, 0, &err);
|
|
|
|
if (!*response)
|
|
|
|
warn("Received invalid JSON: %s in %s:%u:%u\n%s", 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
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
static int ngsi_request_context_query(CURL *handle, const char *endpoint, json_t *entity, json_t **rentity)
|
|
|
|
{
|
|
|
|
int ret, code;
|
|
|
|
char *reason;
|
|
|
|
|
|
|
|
json_t *response;
|
|
|
|
json_t *request = json_pack("{ s: [ o ] }", "entities", entity);
|
|
|
|
|
|
|
|
ret = ngsi_request(handle, endpoint, "queryContext", request, &response);
|
2015-10-15 18:23:57 +02:00
|
|
|
if (ret)
|
2015-10-15 17:36:17 +02:00
|
|
|
goto out;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
ret = ngsi_parse_context_response(response, &code, &reason, rentity);
|
|
|
|
if (ret)
|
|
|
|
goto out2;
|
|
|
|
|
|
|
|
out2: json_decref(response);
|
|
|
|
out: json_decref(request);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ngsi_request_context_update(CURL *handle, const char *endpoint, const char *action, json_t *entity)
|
|
|
|
{
|
|
|
|
int ret, code;
|
|
|
|
char *reason;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
json_t *response;
|
|
|
|
json_t *request = json_pack("{ s: s, s: [ o ] }",
|
|
|
|
"updateAction", action,
|
|
|
|
"contextElements", entity
|
|
|
|
);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
ret = ngsi_request(handle, endpoint, "updateContext", request, &response);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
json_t *rentity;
|
|
|
|
ret = ngsi_parse_context_response(response, &code, &reason, &rentity);
|
|
|
|
if (ret)
|
|
|
|
goto out2;
|
|
|
|
|
|
|
|
json_decref(rentity);
|
|
|
|
out2: json_decref(response);
|
|
|
|
out: json_decref(request);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-07-16 08:08:17 +02:00
|
|
|
int ngsi_type_start(struct super_node *sn)
|
2015-09-19 18:54:27 +02:00
|
|
|
{
|
2015-09-21 17:13:50 +02:00
|
|
|
return curl_global_init(CURL_GLOBAL_ALL);
|
2015-09-19 18:54:27 +02:00
|
|
|
}
|
|
|
|
|
2018-07-16 08:08:17 +02:00
|
|
|
int ngsi_type_stop()
|
2015-09-19 18:54:27 +02:00
|
|
|
{
|
2015-11-23 16:44:18 +01:00
|
|
|
free(name);
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2017-08-03 00:19:27 +02:00
|
|
|
int ngsi_parse(struct node *n, json_t *cfg)
|
2015-09-19 18:54:27 +02:00
|
|
|
{
|
2017-10-18 15:39:53 +02:00
|
|
|
struct ngsi *i = (struct ngsi *) n->_vd;
|
2015-09-19 18:54:27 +02:00
|
|
|
|
2017-08-03 00:19:27 +02:00
|
|
|
int ret;
|
|
|
|
json_error_t err;
|
2017-10-16 08:08:35 +02:00
|
|
|
json_t *json_mapping;
|
2017-08-03 00:19:27 +02:00
|
|
|
|
|
|
|
/* Default values */
|
|
|
|
i->access_token = NULL; /* disabled by default */
|
|
|
|
i->ssl_verify = 1; /* verify by default */
|
|
|
|
i->timeout = 1; /* default value */
|
|
|
|
i->rate = 5; /* default value */
|
|
|
|
|
2017-09-04 23:16:58 +02:00
|
|
|
ret = json_unpack_ex(cfg, &err, 0, "{ s?: s, s: s, s: s, s: s, s?: b, s?: F, s?: F }",
|
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,
|
2017-10-16 08:08:35 +02:00
|
|
|
"mapping", &json_mapping
|
2017-08-03 00:19:27 +02:00
|
|
|
);
|
|
|
|
if (ret)
|
|
|
|
jerror(&err, "Failed to parse configuration of node %s", node_name(n));
|
2015-09-22 15:58:19 +02:00
|
|
|
|
2017-10-16 08:08:35 +02:00
|
|
|
ret = ngsi_parse_mapping(&i->mapping, json_mapping);
|
2017-08-03 00:19:27 +02:00
|
|
|
if (ret)
|
|
|
|
error("Invalid setting 'mapping' of node %s", node_name(n));
|
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
|
|
|
}
|
|
|
|
|
2015-09-22 12:58:37 +02:00
|
|
|
char * ngsi_print(struct node *n)
|
2015-09-19 18:54:27 +02:00
|
|
|
{
|
2017-10-18 15:39:53 +02:00
|
|
|
struct ngsi *i = (struct ngsi *) n->_vd;
|
2015-09-22 12:58:37 +02:00
|
|
|
|
2016-04-16 21:58:13 +02:00
|
|
|
return strf("endpoint=%s, timeout=%.3f secs, #mappings=%zu",
|
2015-10-14 12:19:01 +02:00
|
|
|
i->endpoint, i->timeout, list_length(&i->mapping));
|
2015-09-19 18:54:27 +02:00
|
|
|
}
|
|
|
|
|
2017-02-18 10:43:01 -05:00
|
|
|
static int ngsi_metadata_destroy(struct ngsi_metadata *meta)
|
2016-06-08 23:42:44 +02:00
|
|
|
{
|
|
|
|
free(meta->value);
|
|
|
|
free(meta->name);
|
|
|
|
free(meta->type);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2017-02-18 10:43:01 -05:00
|
|
|
return 0;
|
2016-06-08 23:42:44 +02:00
|
|
|
}
|
|
|
|
|
2017-02-18 10:43:01 -05:00
|
|
|
static int ngsi_attribute_destroy(struct ngsi_attribute *attr)
|
2016-06-08 23:42:44 +02:00
|
|
|
{
|
|
|
|
free(attr->name);
|
|
|
|
free(attr->type);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2017-02-18 10:43:01 -05:00
|
|
|
list_destroy(&attr->metadata, (dtor_cb_t) ngsi_metadata_destroy, true);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2017-02-18 10:43:01 -05:00
|
|
|
return 0;
|
2016-06-08 23:42:44 +02:00
|
|
|
}
|
|
|
|
|
2015-11-23 16:42:43 +01:00
|
|
|
int ngsi_destroy(struct node *n)
|
|
|
|
{
|
2017-10-18 15:39:53 +02:00
|
|
|
struct ngsi *i = (struct ngsi *) n->_vd;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2017-02-18 10:43:01 -05:00
|
|
|
list_destroy(&i->mapping, (dtor_cb_t) ngsi_attribute_destroy, true);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-11-23 16:42:43 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-03-11 23:30:24 -03:00
|
|
|
int ngsi_start(struct node *n)
|
2015-09-19 18:54:27 +02:00
|
|
|
{
|
2017-10-18 15:39:53 +02:00
|
|
|
struct ngsi *i = (struct ngsi *) n->_vd;
|
2015-10-15 17:36:17 +02:00
|
|
|
int ret;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-09-19 18:54:27 +02:00
|
|
|
i->curl = curl_easy_init();
|
2015-09-21 17:13:50 +02:00
|
|
|
i->headers = NULL;
|
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);
|
2015-09-19 18:54:27 +02:00
|
|
|
i->headers = curl_slist_append(i->headers, buf);
|
|
|
|
}
|
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)
|
|
|
|
warn("Timeout is to large for given rate: %f", i->rate);
|
|
|
|
|
2017-08-22 12:16:33 +02:00
|
|
|
ret = task_init(&i->task, i->rate, CLOCK_MONOTONIC);
|
2017-08-14 14:35:16 +02:00
|
|
|
if (ret)
|
2017-08-22 12:16:33 +02:00
|
|
|
serror("Failed to create task");
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-09-22 15:58:19 +02:00
|
|
|
i->headers = curl_slist_append(i->headers, "Accept: application/json");
|
2015-09-19 18:54:27 +02:00
|
|
|
i->headers = curl_slist_append(i->headers, "Content-Type: application/json");
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-09-22 14:49:42 +02:00
|
|
|
curl_easy_setopt(i->curl, CURLOPT_SSL_VERIFYPEER, i->ssl_verify);
|
2015-09-21 17:13:50 +02:00
|
|
|
curl_easy_setopt(i->curl, CURLOPT_TIMEOUT_MS, i->timeout * 1e3);
|
2015-09-19 18:54:27 +02:00
|
|
|
curl_easy_setopt(i->curl, CURLOPT_HTTPHEADER, i->headers);
|
2017-01-19 21:03:37 -02:00
|
|
|
curl_easy_setopt(i->curl, CURLOPT_USERAGENT, USER_AGENT);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-09-21 17:13:50 +02:00
|
|
|
/* Create entity and atributes */
|
2017-05-05 19:24:16 +00:00
|
|
|
json_t *entity = ngsi_build_entity(i, NULL, 0, NGSI_ENTITY_METADATA);
|
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
ret = ngsi_request_context_update(i->curl, i->endpoint, "APPEND", entity);
|
|
|
|
if (ret)
|
2015-11-29 22:45:46 +01:00
|
|
|
error("Failed to create NGSI context for node %s", node_name(n));
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
json_decref(entity);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
return ret;
|
2015-09-19 18:54:27 +02:00
|
|
|
}
|
|
|
|
|
2017-03-11 23:30:24 -03:00
|
|
|
int ngsi_stop(struct node *n)
|
2015-09-19 18:54:27 +02:00
|
|
|
{
|
2017-10-18 15:39:53 +02:00
|
|
|
struct ngsi *i = (struct ngsi *) n->_vd;
|
2015-10-14 12:19:01 +02:00
|
|
|
int ret;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-14 12:19:01 +02:00
|
|
|
/* Delete complete entity (not just attributes) */
|
2016-01-14 22:59:57 +01:00
|
|
|
json_t *entity = ngsi_build_entity(i, NULL, 0, 0);
|
2015-10-14 12:19:01 +02:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
ret = ngsi_request_context_update(i->curl, i->endpoint, "DELETE", entity);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
json_decref(entity);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-09-19 18:54:27 +02:00
|
|
|
curl_easy_cleanup(i->curl);
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2018-07-11 18:14:29 +02:00
|
|
|
int ngsi_read(struct node *n, struct sample *smps[], unsigned cnt, unsigned *release)
|
2015-10-12 15:29:26 +02:00
|
|
|
{
|
2017-10-18 15:39:53 +02:00
|
|
|
struct ngsi *i = (struct ngsi *) n->_vd;
|
2015-10-12 15:29:26 +02:00
|
|
|
int ret;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2017-09-16 15:33:01 +02:00
|
|
|
if (task_wait(&i->task) == 0)
|
2017-08-22 12:16:33 +02:00
|
|
|
perror("Failed to wait for task");
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
json_t *rentity;
|
2016-06-08 23:42:44 +02:00
|
|
|
json_t *entity = ngsi_build_entity(i, NULL, 0, 0);
|
2015-10-12 15:29:26 +02:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
ret = ngsi_request_context_query(i->curl, i->endpoint, entity, &rentity);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2018-07-11 18:14:29 +02:00
|
|
|
ret = ngsi_parse_entity(rentity, i, smps, cnt);
|
2015-10-15 17:36:17 +02:00
|
|
|
if (ret)
|
|
|
|
goto out2;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-15 18:23:57 +02:00
|
|
|
out2: json_decref(rentity);
|
|
|
|
out: json_decref(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
|
|
|
}
|
|
|
|
|
2018-07-11 18:14:29 +02:00
|
|
|
int ngsi_write(struct node *n, struct sample *smps[], unsigned cnt, unsigned *release)
|
2015-09-19 18:54:27 +02:00
|
|
|
{
|
2017-10-18 15:39:53 +02:00
|
|
|
struct ngsi *i = (struct ngsi *) n->_vd;
|
2015-10-15 17:36:17 +02:00
|
|
|
int ret;
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2018-07-11 18:14:29 +02:00
|
|
|
json_t *entity = ngsi_build_entity(i, smps, cnt, NGSI_ENTITY_VALUES);
|
2015-10-14 12:19:01 +02:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
ret = ngsi_request_context_update(i->curl, i->endpoint, "UPDATE", entity);
|
2017-05-05 19:24:16 +00:00
|
|
|
|
2015-10-15 17:36:17 +02:00
|
|
|
json_decref(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
|
|
|
}
|
|
|
|
|
2017-08-30 00:25:42 +02:00
|
|
|
int ngsi_fd(struct node *n)
|
|
|
|
{
|
2017-10-18 15:39:53 +02:00
|
|
|
struct ngsi *i = (struct ngsi *) n->_vd;
|
2017-09-04 23:16:58 +02:00
|
|
|
|
2017-08-30 00:25:42 +02:00
|
|
|
return task_fd(&i->task);
|
|
|
|
}
|
|
|
|
|
2017-02-12 14:35:05 -03:00
|
|
|
static struct plugin p = {
|
2015-11-23 16:44:01 +01:00
|
|
|
.name = "ngsi",
|
2016-10-08 01:10:12 -04:00
|
|
|
.description = "OMA Next Generation Services Interface 10 (libcurl, libjansson)",
|
2017-02-12 14:35:05 -03:00
|
|
|
.type = PLUGIN_TYPE_NODE,
|
|
|
|
.node = {
|
|
|
|
.vectorize = 0, /* unlimited */
|
|
|
|
.size = sizeof(struct ngsi),
|
2018-07-16 08:08:17 +02:00
|
|
|
.type.start = ngsi_type_start,
|
|
|
|
.type.stop = ngsi_type_stop,
|
2017-02-12 14:35:05 -03:00
|
|
|
.parse = ngsi_parse,
|
|
|
|
.print = ngsi_print,
|
2017-03-11 23:30:24 -03:00
|
|
|
.start = ngsi_start,
|
|
|
|
.stop = ngsi_stop,
|
2017-02-12 14:35:05 -03:00
|
|
|
.read = ngsi_read,
|
|
|
|
.write = ngsi_write,
|
2017-08-30 00:25:42 +02:00
|
|
|
.fd = ngsi_fd
|
2017-02-12 14:35:05 -03:00
|
|
|
}
|
2015-11-23 16:44:01 +01:00
|
|
|
};
|
|
|
|
|
2017-06-08 12:42:46 +02:00
|
|
|
REGISTER_PLUGIN(&p)
|
2017-07-02 23:53:48 +02:00
|
|
|
LIST_INIT_STATIC(&p.node.instances)
|