diff --git a/documentation/Clients.md b/documentation/Clients.md index a5a6226fc..288058399 100644 --- a/documentation/Clients.md +++ b/documentation/Clients.md @@ -4,6 +4,7 @@ @subpage opal @subpage file @subpage socket +@subpage ngsi Every server needs clients which act as sinks / sources for simulation data. In case of S2SS these clients are called _nodes_. @@ -13,5 +14,6 @@ Take a look at the `clients/` directory for them: - RTDS via GTFPGA and PCIexpress - OPAL via Asynchronous Process and UDP - OPAL via Asynchronous Process and Shared memory + - NGSI 9/10 (FIRWARE context broker) - Labview (*planned*) - Simulink (*planned*) diff --git a/server/etc/example.conf b/server/etc/example.conf index 52920e29b..d11fb3c11 100644 --- a/server/etc/example.conf +++ b/server/etc/example.conf @@ -111,6 +111,21 @@ nodes = { id = "1ab8:4005", # The PCIe vendor:device ID (see third column in 'lspci -n' output) rate = 1 + }, + ngsi_node = { + type = "ngsi", + + ### The following settings are specific to the gtfpga node-type!! ### + endpoint = "http://46.101.131.212:1026/v1", # The HTTP / REST endpoint of the FIRWARE context broker + + mapping = [ # Format: "entityID(entityType).AttributeId(AttributeType)" + "rtds_sub1(voltage3).v1(float), + "rtds_sub1(voltage3).v2(float)", + "rtds_sub1(voltage3).v3(float)", + "rtds_sub2(power3).p1(float)", + "rtds_sub2(power3).p2(float)", + "rtds_sub2(power3).p3(float)" + ] } }; diff --git a/server/include/ngsi.h b/server/include/ngsi.h new file mode 100644 index 000000000..33430799c --- /dev/null +++ b/server/include/ngsi.h @@ -0,0 +1,86 @@ +/** Node type: NGSI 9/10 (FIWARE context broker) + * + * This file implements the NGSI protocol as a node type. + * + * @see https://forge.fiware.org/plugins/mediawiki/wiki/fiware/index.php/FI-WARE_NGSI-10_Open_RESTful_API_Specification + * @file + * @author Steffen Vogel + * @copyright 2014-2015, Institute for Automation of Complex Power Systems, EONERC + * This file is part of S2SS. All Rights Reserved. Proprietary and confidential. + * Unauthorized copying of this file, via any medium is strictly prohibited. + */ +/** + * @addtogroup ngsi FIRWARE NGSI 9/10 RESTful HTTP API + * @ingroup node + * @{ + **********************************************************************************/ + +#ifndef _NGSI_H_ +#define _NGSI_H_ + +#include + +#include "list.h" +#include "config.h" +#include "msg.h" +#include "cfg.h" +#include "node.h" + +struct node; + +struct ngsi_attribute { + char *name; + char *type; + + int index; +}; + +struct ngsi_entity { + char *name; + char *type; + + struct list attributes; + struct list metadata; +}; + +struct ngsi { + char *endpoint; + char *token; + + struct list entities; + + struct curl_slist *headers; + CURL *curl; +}; + +/** Initialize global NGSI settings and maps shared memory regions. + * + * @see node_vtable::init + */ +int ngsi_init(int argc, char *argv[], struct settings *set); + +/** Free global NGSI settings and unmaps shared memory regions. + * + * @see node_vtable::deinit + */ +int ngsi_deinit(); + +/** @see node_vtable::parse */ +int ngsi_parse(config_setting_t *cfg, struct node *n); + +/** @see node_vtable::print */ +int ngsi_print(struct node *n, char *buf, int len); + +/** @see node_vtable::open */ +int ngsi_open(struct node *n); + +/** @see node_vtable::close */ +int ngsi_close(struct node *n); + +/** @see node_vtable::read */ +int ngsi_read(struct node *n, struct msg *pool, int poolsize, int first, int cnt); + +/** @see node_vtable::write */ +int ngsi_write(struct node *n, struct msg *pool, int poolsize, int first, int cnt); + +#endif /** _NGSI_H_ @} */ \ No newline at end of file diff --git a/server/src/ngsi.c b/server/src/ngsi.c new file mode 100644 index 000000000..35ce35b12 --- /dev/null +++ b/server/src/ngsi.c @@ -0,0 +1,179 @@ +/** Node type: FIWARE NGSI 9/10 + * + * This file implements the NGSI protocol as a node type. + * + * @see https://forge.fiware.org/plugins/mediawiki/wiki/fiware/index.php/FI-WARE_NGSI-10_Open_RESTful_API_Specification + * @author Steffen Vogel + * @copyright 2014-2015, Institute for Automation of Complex Power Systems, EONERC + * This file is part of S2SS. All Rights Reserved. Proprietary and confidential. + * Unauthorized copying of this file, via any medium is strictly prohibited. + **********************************************************************************/ + +#include +#include + +#include "ngsi.h" +#include "utils.h" + +/* NGSI Entity, Attribute & Metadata structure */ + +void ngsi_entity_dtor(void *ptr) +{ + struct ngsi_entity *entity = ptr; + + list_destroy(&entity->attributes); + list_destroy(&entity->metadata); + + free(entity->name); + free(entity->type); +} + +void ngsi_attribute_dtor(void *ptr) +{ + struct ngsi_attribute *entity = ptr; + + free(entity->name); + free(entity->type); +} + +struct ngsi_entity * ngsi_entity() +{ + struct ngsi_entity *entity = alloc(sizeof(struct ngsi_entity)); + + list_init(&entity->attributes, ngsi_attribute_dtor); + list_init(&entity->metadata, ngsi_attribute_dtor); + + return entity; +} + +/* Node type */ + +int ngsi_init(int argc, char *argv[], struct settings *set) +{ + curl_global_init(CURL_GLOBAL_ALL); +} + +int ngsi_deinit() +{ + curl_global_cleanup(); +} + +int ngsi_parse(config_setting_t *cfg, struct node *n) +{ + struct ngsi *i = n->ngsi; + + config_setting_t *mapping = config_setting_get_member(cfg, "mapping"); + if (!mapping || !config_setting_is_array(mapping)) + cerror(cfg, "Missing entity.attribute mapping for node '%s", n->name); + + for (int i = 0; i < config_setting_length(mapping); i++) { + const char *token = config_setting_get_string_elem(mapping, i); + if (token) { + if (sscanf(token, "%ms(%ms).%ms", &entity, &type, &attribue) != 2) + cerror(mapping, "Invalid entity.attribute token: '%s'", token); + + + } + cerror(mapping, "Invalid entity.attribute token"); + } +} + +int ngsi_print(struct node *n, char *buf, int len) +{ + struct ngsi *i = n->ngsi; + + return snprintf(buf, len, "endpoint=%s, token=%s, entities=%u", + i->endpoint, i->token, i->entities); +} + +int ngsi_open(struct node *n) +{ + char buf[128]; + struct ngsi *i = n->ngsi; + + i->curl = curl_easy_init(); + i->headers = NULL: + + if (i->token) { + snprintf(buf, sizeof(buf), "Auth-Token: %s", i->token); + i->headers = curl_slist_append(i->headers, buf); + } + + i->headers = curl_slist_append(i->headers, "User-Agent: S2SS " VERSION); + i->headers = curl_slist_append(i->headers, "Content-Type: application/json"); + + snprintf(buf, sizeof(buf), "%s/v1/%s") + + curl_easy_setopt(i->curl, CURLOPT_URL, url); + curl_easy_setopt(i->curl, CURLOPT_POSTFIELDSIZE, -1); + curl_easy_setopt(i->curl, CURLOPT_HTTPHEADER, i->headers); + + /* Create entity */ + char *post = ngsi_update_context(n, "APPEND"); + + return 0; +} + +int ngsi_close(struct node *n) +{ + struct ngsi *i = n->ngsi; + + curl_easy_cleanup(i->curl); + curl_slist_free_all(i->headers); + + return 0; +} + +static int ngsi_query_context(struct node *n) +{ + struct ngsi *i = n->ngsi; + + return 0; +} + +static char * ngsi_update_context(struct node *n, char *action) +{ + struct ngsi *i = n->ngsi; + + json_t *root = json_object(); + json_t *elements = json_array(); + + for (int i = 0; i < i->entities; i++) { + json_t *entity = json_object(); + json_t *attributes = json_object(); + + json_object_set(entity, "type", json_string("Room")); + json_object_set(entity, "isPattern", json_false()); + json_object_set(entity, "id", json_string("Room1")); + } + + json_object_set(root, "contextElements", conextElements); + json_object_set(root, "updateAction", json_string(action)); + + return json_dumps(root, JSON_COMPACT); +} + +int ngsi_read(struct node *n, struct msg *pool, int poolsize, int first, int cnt) +{ + struct ngsi *i = n->ngsi; + + return -1; /** @todo not yet implemented */ +} + +int ngsi_write(struct node *n, struct msg *pool, int poolsize, int first, int cnt) +{ + struct ngsi *i = n->ngsi; + + long result; + char *post = ngsi_update_context(n, "UPDATE"); + + curl_easy_setopt(i->curl, CURLOPT_COPYPOSTFIELDS, post); + + curl_easy_perform(i->curl); + + curl_easy_getinfo(i->curl, CURLINFO_RESPONSE_CODE, &result); + + return result == 200 ? 1 : 0 +} + +REGISTER_NODE_TYPE(NGSI, "ngsi", ngsi) \ No newline at end of file diff --git a/server/src/node.c b/server/src/node.c index cf5bc15db..c160e43a3 100644 --- a/server/src/node.c +++ b/server/src/node.c @@ -108,6 +108,11 @@ struct node * node_create() void node_destroy(struct node *n) { switch (n->vt->type) { +#ifdef ENABLE_NGSI + case NGSI: + list_destroy(n->ngsi->entities); + break; +#endif #ifdef ENABLE_SOCKET case BSD_SOCKET: rtnl_qdisc_put(n->socket->tc_qdisc);