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

ngsi: rewrite NGSI/FIWARE OCB node-type using C++ primitives

This commit is contained in:
Steffen Vogel 2020-08-17 12:52:08 +02:00
parent d7f89fdb29
commit f8c3ec3647
2 changed files with 283 additions and 172 deletions

View file

@ -51,6 +51,9 @@ struct ngsi {
const char *entity_type; /**< The type of the entity */
const char *access_token; /**< An optional authentication token which will be sent as HTTP header. */
bool create; /**< Weather we want to create the context element during startup. */
bool remove; /**< Weather we want to delete the context element during startup. */
double timeout; /**< HTTP timeout in seconds */
double rate; /**< Rate used for polling. */
@ -90,6 +93,9 @@ int ngsi_start(struct node *n);
/** @see node_type::stop */
int ngsi_stop(struct node *n);
/** @see node_type::reverse */
int ngsi_reverse(struct node *n);
/** @see node_type::read */
int ngsi_read(struct node *n, struct sample *smps[], unsigned cnt, unsigned *release);

View file

@ -41,24 +41,161 @@ using namespace villas::utils;
/* Some global settings */
static char *name = nullptr;
enum ngsi_flags {
NGSI_ENTITY_ATTRIBUTES = (1 << 0),
NGSI_ENTITY_VALUES = (1 << 1) | NGSI_ENTITY_ATTRIBUTES,
NGSI_ENTITY_METADATA = (1 << 2) | NGSI_ENTITY_ATTRIBUTES,
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),
};
struct ngsi_metadata {
char *name;
char *type;
char *value;
class NgsiMetadata {
public:
NgsiMetadata(json_t *json)
{
parse(json);
}
NgsiMetadata(const std::string &nam, const std::string &typ, const std::string &val) :
name(nam),
type(typ),
value(val)
{ }
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)
throw ConfigError(json, "Failed to parse NGSI metadata");
name = nam;
type = typ;
value = val;
}
std::string name;
std::string type;
std::string value;
};
struct ngsi_attribute {
char *name;
char *type;
class NgsiAttribute {
int index;
struct vlist metadata;
public:
std::string name;
std::string type;
size_t index;
std::list<NgsiMetadata> metadata;
NgsiAttribute(json_t *json, size_t j, struct signal *s)
{
parse(json, j, s);
}
void parse(json_t *json, size_t j, struct signal *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 ? s->name : "";
if (s && !typ)
typ = s->unit ? s->unit : "";
name = nam;
type = typ;
index = j;
if (json_metadatas) {
if (!json_is_array(json_metadatas))
throw ConfigError(json_metadatas, "node-config-ngsi-metadata", "ngsi_metadata must be a list of objects");
json_t *json_metadata;
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(struct sample *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++) {
struct sample *smp = &smps[k];
union signal_data *sd = &smp->data[index];
struct signal *sig = (struct signal *) vlist_at_safe(smp->signals, index);
json_array_append_new(json_value, json_pack("[ f, o, i ]",
time_to_double(smp->ts.origin),
signal_data_to_json(sd, sig),
smp->sequence
));
}
#else
struct sample *smp = smps[0];
union signal_data *sd = &smp->data[index];
struct signal *sig = (struct signal *) vlist_at_safe(smp->signals, index);
json_t *json_value = signal_data_to_json(sd, sig);
#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 {
@ -66,46 +203,6 @@ struct ngsi_response {
size_t len;
};
static json_t * ngsi_build_attribute(struct ngsi_attribute *attr, struct sample *smps[], unsigned cnt, int flags)
{
json_t *json_attribute = json_pack("{ s: s, s: s }",
"name", attr->name,
"type", attr->type
);
if (flags & NGSI_ENTITY_VALUES) { /* Build value vector */
json_t *json_values = json_array();
for (unsigned k = 0; k < cnt; k++) {
json_array_append_new(json_values, json_pack("[ f, f, i ]",
time_to_double(&smps[k]->ts.origin),
smps[k]->data[attr->index].f,
smps[k]->sequence
));
}
json_object_set(json_attribute, "value", json_values);
}
if (flags & NGSI_ENTITY_METADATA) { /* Create Metadata for attribute */
json_t *json_metadatas = json_array();
for (size_t i = 0; i < vlist_length(&attr->metadata); i++) {
struct ngsi_metadata *meta = (struct ngsi_metadata *) vlist_at(&attr->metadata, i);
json_array_append_new(json_metadatas, json_pack("{ s: s, s: s, s: s }",
"name", meta->name,
"type", meta->type,
"value", meta->value
));
}
json_object_set(json_attribute, "metadatas", json_metadatas);
}
return json_attribute;
}
static json_t* ngsi_build_entity(struct node *n, struct sample *smps[], unsigned cnt, int flags)
{
struct ngsi *i = (struct ngsi *) n->_vd;
@ -119,20 +216,24 @@ static json_t* ngsi_build_entity(struct node *n, struct sample *smps[], unsigned
if (flags & NGSI_ENTITY_ATTRIBUTES) {
json_t *json_attrs = json_array();
for (size_t j = 0; j < vlist_length(&i->in.signals); j++) {
struct ngsi_attribute *attr = (struct ngsi_attribute *) vlist_at(&i->in.signals, j);
if (flags & NGSI_ENTITY_ATTRIBUTES_IN) {
for (size_t j = 0; j < vlist_length(&i->in.signals); j++) {
auto *attr = (NgsiAttribute *) vlist_at(&i->in.signals, j);
auto *json_attr = ngsi_build_attribute(attr, smps, cnt, flags);
auto *json_attr = attr->build(smps, cnt, flags);
json_array_append_new(json_attrs, json_attr);
json_array_append_new(json_attrs, json_attr);
}
}
for (size_t j = 0; j < vlist_length(&i->out.signals); j++) {
struct ngsi_attribute *attr = (struct ngsi_attribute *) vlist_at(&i->out.signals, j);
if (flags & NGSI_ENTITY_ATTRIBUTES_OUT) {
for (size_t j = 0; j < vlist_length(&i->out.signals); j++) {
auto *attr = (NgsiAttribute *) vlist_at(&i->out.signals, j);
auto *json_attr = ngsi_build_attribute(attr, smps, cnt, flags);
auto *json_attr = attr->build(smps, cnt, flags);
json_array_append_new(json_attrs, json_attr);
json_array_append_new(json_attrs, json_attr);
}
}
json_object_set(json_entity, "attributes", json_attrs);
@ -164,23 +265,26 @@ static int ngsi_parse_entity(struct node *n, json_t *json_entity, struct sample
return -2;
json_array_foreach(json_attrs, l, json_attr) {
struct ngsi_attribute *attr;
NgsiAttribute *attr;
json_error_t err;
json_t *json_metadata, *json_values, *json_tuple;
json_t *json_metadata, *json_value;
char *end;
const char *value;
/* Parse JSON */
ret = json_unpack_ex(json_attr, &err, 0, "{ s: s, s: s, s: o, s?: o }",
"name", &name,
"type", &type,
"value", &json_values,
"value", &json_value,
"metadatas", &json_metadata
);
if (ret)
return -3;
/* Check attribute name and type */
attr = (struct ngsi_attribute *) vlist_lookup(&i->in.signals, name);
if (!attr || strcmp(attr->type, type))
attr = (NgsiAttribute *) vlist_lookup(&i->in.signals, name);
if (!attr || attr->type != type)
continue; /* skip unknown attributes */
length++;
@ -189,38 +293,82 @@ static int ngsi_parse_entity(struct node *n, json_t *json_entity, struct sample
if (!json_is_array(json_metadata))
return -5;
#ifdef NGSI_VECTORS
json_t *json_tuple;
const char *ts, *seq;
/* Check number of values */
if (!json_is_array(json_values) || json_array_size(json_values) != cnt)
if (!json_is_array(json_value) || json_array_size(json_value) != cnt)
return -6;
size_t k;
json_array_foreach(json_values, k, json_tuple) {
json_array_foreach(json_value, k, json_tuple) {
struct sample *smp = smps[k];
/* Check sample format */
if (!json_is_array(json_tuple) || json_array_size(json_tuple) != 3)
return -7;
char *end;
const char *value, *ts, *seq;
ret = json_unpack_ex(json_tuple, &err, 0, "[ s, s, s ]", &ts, &value, &seq);
if (ret)
return -8;
smps[k]->sequence = atoi(seq);
smp->sequence = atoi(seq);
struct timespec tss = time_from_double(strtod(ts, &end));
if (ts == end)
return -9;
smps[k]->ts.origin = tss;
smps[k]->data[attr->index].f = strtof(value, &end);
smp->ts.origin = tss;
union signal_data *sd = &smp->data[attr->index];
struct signal *sig = (struct signal *) vlist_at_safe(&n->in.signals, 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, 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);
union signal_data *sd = &smp->data[attr->index];
struct signal *sig = (struct signal *) vlist_at_safe(&n->in.signals, 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, value, &end);
if (value == end)
return -10;
}
#endif
}
for (unsigned k = 0; k < cnt; k++) {
smps[k]->length = length;
smps[k]->signals = &n->in.signals;
struct sample *smp = smps[k];
smp->length = length;
smp->signals = &n->in.signals;
smp->flags = (int) SampleFlags::HAS_DATA;
#ifdef NGSI_VECTORS
smp->flags |= (int) (SampleFlags::HAS_SEQUENCE |
SampleFlags::HAS_TS_ORIGIN);
#endif
}
return cnt;
@ -228,10 +376,8 @@ static int ngsi_parse_entity(struct node *n, json_t *json_entity, struct sample
static int ngsi_parse_signals(json_t *json_signals, struct vlist *ngsi_signals, struct vlist *node_signals)
{
int ret;
size_t j;
json_error_t err;
json_t *json_signal, *json_metadata;
json_t *json_signal;
if (!json_is_array(json_signals))
return -1;
@ -239,71 +385,10 @@ static int ngsi_parse_signals(json_t *json_signals, struct vlist *ngsi_signals,
json_array_foreach(json_signals, j, json_signal) {
auto *s = (struct signal *) vlist_at_safe(node_signals, j);
auto *a = new struct ngsi_attribute;
auto *a = new NgsiAttribute(json_signal, j, s);
if (!a)
throw MemoryAllocationError();
memset(a, 0, sizeof(struct ngsi_attribute));
a->index = j;
json_t *json_ngsi_metadata = nullptr;
ret = json_unpack_ex(json_signal, &err, 0, "{ s?: s, s?: s, s?: o }",
"ngsi_attribute_name", &a->name,
"ngsi_attribute_type", &a->type,
"ngsi_metadatas", &json_ngsi_metadata
);
if (ret)
return ret;
/* Copy values from node signal, if 'ngsi_attribute' settings not provided */
if (s != nullptr && a->name == nullptr)
a->name = strdup(s->name ? s->name : "");
if (s != nullptr && a->type == nullptr)
a->type = strdup(s->unit ? s->unit : "");
ret = vlist_init(&a->metadata);
if (ret)
return ret;
if (json_ngsi_metadata) {
if (!json_is_array(json_ngsi_metadata))
throw ConfigError(json_ngsi_metadata, "node-config-ngsi-metadata", "ngsi_metadata must be a list of objects");
json_array_foreach(json_ngsi_metadata, j, json_metadata) {
auto *md = new struct ngsi_metadata;
if (!md)
return -1;
memset(md, 0, sizeof(struct ngsi_metadata));
ret = json_unpack_ex(json_metadata, &err, 0, "{ s: s, s: s, s: s }",
"name", &md->name,
"type", &md->type,
"value", &md->value
);
if (ret)
return ret;
vlist_push(&a->metadata, md);
}
}
/* Metadata: index(integer)=j */
auto *md_idx = new struct ngsi_metadata;
if (!md_idx)
return -1;
md_idx->name = strdup("index");
md_idx->type = strdup("integer");
asprintf(&md_idx->value, "%zu", j);
assert(md_idx->name && md_idx->type && md_idx->value);
vlist_push(&a->metadata, md_idx);
vlist_push(ngsi_signals, a);
}
@ -343,7 +428,7 @@ static size_t ngsi_request_writer(void *contents, size_t size, size_t nmemb, voi
mem->data = (char *) realloc(mem->data, mem->len + realsize + 1);
if (mem->data == nullptr) /* out of memory! */
error("Not enough memory (realloc returned nullptr)");
throw MemoryAllocationError();
memcpy(&(mem->data[mem->len]), contents, realsize);
mem->len += realsize;
@ -468,7 +553,10 @@ int ngsi_parse(struct node *n, json_t *cfg)
json_t *json_signals_in = nullptr;
json_t *json_signals_out = nullptr;
ret = json_unpack_ex(cfg, &err, 0, "{ s?: s, s: s, s: s, s: s, s?: b, s?: F, s?: F, s?: { s?: o }, s?: { s?: o } }",
int create = 1;
int remove = 1;
ret = json_unpack_ex(cfg, &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,
@ -476,24 +564,29 @@ int ngsi_parse(struct node *n, json_t *cfg)
"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)
jerror(&err, "Failed to parse configuration of node %s", node_name(n));
throw ConfigError(cfg, err, "node-config-node-ngsi", "Failed to parse configuration of node {}", node_name(n));
i->create = create;
i->remove = remove;
if (json_signals_in) {
ret = ngsi_parse_signals(json_signals_in, &i->in.signals, &n->in.signals);
if (ret)
error("Invalid setting 'in.signals' of node %s", node_name(n));
throw ConfigError(json_signals_in, "node-config-node-ngsi-in-signals", "Invalid setting 'in.signals' of node {}", node_name(n));
}
if (json_signals_out) {
ret = ngsi_parse_signals(json_signals_out, &i->out.signals, &n->out.signals);
if (ret)
error("Invalid setting 'out.signals' of node %s", node_name(n));
throw ConfigError(json_signals_out, "node-config-node-ngsi-out-signals", "Invalid setting 'out.signals' of node {}", node_name(n));
}
return 0;
@ -507,25 +600,6 @@ char * ngsi_print(struct node *n)
i->endpoint, i->timeout);
}
static int ngsi_metadata_destroy(struct ngsi_metadata *meta)
{
free(meta->value);
free(meta->name);
free(meta->type);
return 0;
}
static int ngsi_attribute_destroy(struct ngsi_attribute *attr)
{
free(attr->name);
free(attr->type);
vlist_destroy(&attr->metadata, (dtor_cb_t) ngsi_metadata_destroy, true);
return 0;
}
int ngsi_start(struct node *n)
{
struct ngsi *i = (struct ngsi *) n->_vd;
@ -555,13 +629,15 @@ int ngsi_start(struct node *n)
curl_easy_setopt(i->curl, CURLOPT_USERAGENT, USER_AGENT);
/* Create entity and atributes */
json_t *json_entity = ngsi_build_entity(n, nullptr, 0, NGSI_ENTITY_METADATA);
if (i->create) {
json_t *json_entity = ngsi_build_entity(n, nullptr, 0, NGSI_ENTITY_ATTRIBUTES | NGSI_ENTITY_METADATA);
ret = ngsi_request_context_update(i->curl, i->endpoint, "APPEND", json_entity);
if (ret)
error("Failed to create NGSI context for node %s", node_name(n));
ret = ngsi_request_context_update(i->curl, i->endpoint, "APPEND", json_entity);
if (ret)
throw RuntimeError("Failed to create NGSI context for node {}", node_name(n));
json_decref(json_entity);
json_decref(json_entity);
}
return ret;
}
@ -616,7 +692,7 @@ int ngsi_write(struct node *n, struct sample *smps[], unsigned cnt, unsigned *re
struct ngsi *i = (struct ngsi *) n->_vd;
int ret;
json_t *json_entity = ngsi_build_entity(n, smps, cnt, NGSI_ENTITY_VALUES);
json_t *json_entity = ngsi_build_entity(n, smps, cnt, NGSI_ENTITY_ATTRIBUTES_OUT | NGSI_ENTITY_VALUES);
ret = ngsi_request_context_update(i->curl, i->endpoint, "UPDATE", json_entity);
@ -663,11 +739,24 @@ int ngsi_destroy(struct node *n)
int ret;
struct ngsi *i = (struct ngsi *) n->_vd;
ret = vlist_destroy(&i->in.signals, (dtor_cb_t) ngsi_attribute_destroy, true);
for (size_t j = 0; j < vlist_length(&i->in.signals); j++) {
auto *attr = (NgsiAttribute *) vlist_at(&i->in.signals, j);
delete attr;
}
for (size_t j = 0; j < vlist_length(&i->out.signals); j++) {
auto *attr = (NgsiAttribute *) vlist_at(&i->out.signals, j);
delete attr;
}
ret = vlist_destroy(&i->in.signals);
if (ret)
return ret;
ret = vlist_destroy(&i->out.signals, (dtor_cb_t) ngsi_attribute_destroy, true);
ret = vlist_destroy(&i->out.signals);
if (ret)
return ret;
@ -676,6 +765,17 @@ int ngsi_destroy(struct node *n)
return 0;
}
int ngsi_reverse(struct node *n)
{
struct ngsi *i = (struct ngsi *) n->_vd;
SWAP(n->in.signals, n->out.signals);
SWAP(i->in.signals, i->out.signals);
return 0;
}
static struct plugin p;
__attribute__((constructor(110)))
@ -684,7 +784,11 @@ static void register_plugin() {
p.description = "OMA Next Generation Services Interface 10 (libcurl, libjansson)";
p.type = PluginType::NODE;
p.node.instances.state = State::DESTROYED;
#ifdef NGSI_VECTORS
p.node.vectorize = 0, /* unlimited */
#else
p.node.vectorize = 1,
#endif
p.node.size = sizeof(struct ngsi);
p.node.type.start = ngsi_type_start;
p.node.type.stop = ngsi_type_stop;
@ -697,6 +801,7 @@ static void register_plugin() {
p.node.read = ngsi_read;
p.node.write = ngsi_write;
p.node.poll_fds = ngsi_poll_fds;
p.node.reverse = ngsi_reverse;
vlist_init(&p.node.instances);
vlist_push(&plugins, &p);