diff --git a/README.md b/README.md index add1c69..9ac3c3d 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ KNoT Gateway and KNoT Apps. To install KNoT Protocol, you have to follow the instructions below: 1. `git clone git@github.com:CESARBR/knot-protocol-source.git` -2. `git checkout 891d01d ` to checkout to a hash on devel branch. +2. `git checkout ead9e66` to checkout to a hash on devel branch. 3. Follow instructions on `README` file diff --git a/src/knot_cloud.c b/src/knot_cloud.c index 18b43bf..1768312 100644 --- a/src/knot_cloud.c +++ b/src/knot_cloud.c @@ -53,7 +53,7 @@ static void knot_cloud_device_free(void *data) if (unlikely(!device)) return; - l_queue_destroy(device->schema, l_free); + l_queue_destroy(device->config_list, l_free); l_free(device->id); l_free(device->uuid); l_free(device->name); @@ -74,7 +74,7 @@ static void knot_cloud_msg_destroy(struct knot_cloud_msg *msg) } static void *create_device_item(const char *id, const char *name, - struct l_queue *schema) + struct l_queue *config_list) { struct knot_cloud_device *device; @@ -82,7 +82,7 @@ static void *create_device_item(const char *id, const char *name, device->id = l_strdup(id); device->name = l_strdup(name); device->uuid = l_strdup(id); - device->schema = schema; + device->config_list = config_list; return device; } @@ -146,7 +146,10 @@ static struct knot_cloud_msg *create_msg(const char *routing_key, break; case UNREGISTER_MSG: case AUTH_MSG: - case SCHEMA_MSG: + break; + case CONFIG_MSG: + msg->list = parser_config_to_list(json_str); + has_err = msg->list ? false : true; break; case LIST_MSG: msg->list = parser_queue_from_json_array(json_str, @@ -269,8 +272,8 @@ static int set_knot_cloud_events(const char *id) l_strdup(MQ_EVENT_DEVICE_UNREGISTERED); knot_cloud_events[AUTH_MSG] = l_strdup(binding_key_auth_reply); - knot_cloud_events[SCHEMA_MSG] = - l_strdup(MQ_EVENT_DEVICE_SCHEMA_UPDATED); + knot_cloud_events[CONFIG_MSG] = + l_strdup(MQ_EVENT_DEVICE_CONFIG_UPDATED); knot_cloud_events[LIST_MSG] = l_strdup(binding_key_list_reply); @@ -444,20 +447,20 @@ int knot_cloud_auth_device(const char *id, const char *token) } /** - * knot_cloud_update_schema: + * knot_cloud_update_config: * - * Requests cloud to update the device schema. + * Requests cloud to update the device config. * The confirmation that the cloud received the message comes from a callback - * set in function knot_cloud_read_start with message type SCHEMA_MSG. + * set in function knot_cloud_read_start with message type CONFIG_MSG. * * Returns: 0 if successful and a KNoT error otherwise. */ -int knot_cloud_update_schema(const char *id, struct l_queue *schema_list) +int knot_cloud_update_config(const char *id, struct l_queue *config_list) { char *json_str; int result; - json_str = parser_schema_create_object(id, schema_list); + json_str = parser_config_create_object(id, config_list); if (!json_str) return KNOT_ERR_CLOUD_FAILURE; @@ -466,7 +469,7 @@ int knot_cloud_update_schema(const char *id, struct l_queue *schema_list) * Type: Direct * Name: device * Routing Key - * Name: device.schema.sent + * Name: device.config.sent * Headers * [0]: User Token * Expiration @@ -474,7 +477,7 @@ int knot_cloud_update_schema(const char *id, struct l_queue *schema_list) */ mq_message_data_t mq_message = { MQ_MESSAGE_TYPE_DIRECT, - MQ_EXCHANGE_DEVICE, MQ_CMD_SCHEMA_SENT, + MQ_EXCHANGE_DEVICE, MQ_CMD_CONFIG_SENT, MQ_MSG_EXPIRATION_TIME_MS, json_str, NULL, NULL }; diff --git a/src/knot_cloud.h b/src/knot_cloud.h index 18e78ff..03e9b05 100644 --- a/src/knot_cloud.h +++ b/src/knot_cloud.h @@ -29,7 +29,7 @@ struct knot_cloud_device { char *uuid; char *name; bool online; - struct l_queue *schema; + struct l_queue *config_list; struct l_timeout *unreg_timeout; }; @@ -42,7 +42,7 @@ struct knot_cloud_msg { REGISTER_MSG, UNREGISTER_MSG, AUTH_MSG, - SCHEMA_MSG, + CONFIG_MSG, LIST_MSG, MSG_TYPES_LENGTH } type; @@ -61,7 +61,7 @@ int knot_cloud_set_log_priority(int priority); int knot_cloud_register_device(const char *id, const char *name); int knot_cloud_unregister_device(const char *id); int knot_cloud_auth_device(const char *id, const char *token); -int knot_cloud_update_schema(const char *id, struct l_queue *schema_list); +int knot_cloud_update_config(const char *id, struct l_queue *config_list); int knot_cloud_list_devices(void); int knot_cloud_publish_data(const char *id, uint8_t sensor_id, uint8_t value_type, const knot_value_type *value, diff --git a/src/mq.h b/src/mq.h index 977d213..02880b5 100644 --- a/src/mq.h +++ b/src/mq.h @@ -36,7 +36,7 @@ #define MQ_EVENT_DEVICE_REGISTERED "device.registered" #define MQ_EVENT_DEVICE_UNREGISTERED "device.unregistered" -#define MQ_EVENT_DEVICE_SCHEMA_UPDATED "device.schema.updated" +#define MQ_EVENT_DEVICE_CONFIG_UPDATED "device.config.updated" #define MQ_EVENT_AUTH_REPLY "thingd-auth-reply" #define MQ_EVENT_LIST_REPLY "thingd-list-reply" @@ -45,7 +45,7 @@ #define MQ_CMD_DEVICE_REGISTER "device.register" #define MQ_CMD_DEVICE_UNREGISTER "device.unregister" #define MQ_CMD_DEVICE_AUTH "device.auth" -#define MQ_CMD_SCHEMA_SENT "device.schema.sent" +#define MQ_CMD_CONFIG_SENT "device.config.sent" #define MQ_CMD_DEVICE_LIST "device.list" #define MQ_DEFAULT_CORRELATION_ID "default-corrId" diff --git a/src/parser.c b/src/parser.c index 8148acb..e63ced0 100644 --- a/src/parser.c +++ b/src/parser.c @@ -115,7 +115,7 @@ static void *device_array_item(json_object *array_item, create_device_item_cb cb) { json_object *jobjkey; - struct l_queue *schema; + struct l_queue *config_list; const char *id, *name; /* Getting 'Id': Mandatory field for registered device */ @@ -123,12 +123,14 @@ static void *device_array_item(json_object *array_item, if (!id) return NULL; + /* Getting 'config': Mandatory field for registered device */ if (!json_object_object_get_ex(array_item, - KNOT_JSON_FIELD_SCHEMA, &jobjkey)) + KNOT_JSON_FIELD_CONFIG, &jobjkey)) return NULL; - schema = parser_schema_to_list(json_object_to_json_string(jobjkey)); - if (!schema) + config_list = parser_config_to_list( + json_object_to_json_string(jobjkey)); + if (!config_list) return NULL; /* Getting 'Name' */ @@ -137,7 +139,7 @@ static void *device_array_item(json_object *array_item, if (!name) return NULL; - return cb(id, name, schema); + return cb(id, name, config_list); } /* @@ -188,83 +190,305 @@ static int parse_json2data(json_object *jobj, knot_value_type *kvalue) return olen; } -static json_object *schema_item_create_obj(knot_msg_schema *schema) +static json_object *parse_value_to_json(uint8_t value_type, + knot_value_type *value) +{ + switch (value_type) + { + case KNOT_VALUE_TYPE_INT: + return json_object_new_int(value->val_i); + case KNOT_VALUE_TYPE_FLOAT: + return json_object_new_double(value->val_f); + case KNOT_VALUE_TYPE_BOOL: + return json_object_new_boolean(value->val_b); + case KNOT_VALUE_TYPE_RAW: + return json_object_new_string(value->raw); + case KNOT_VALUE_TYPE_INT64: + return json_object_new_int64(value->val_i64); + case KNOT_VALUE_TYPE_UINT: + return json_object_new_uint64(value->val_u64); + case KNOT_VALUE_TYPE_UINT64: + return json_object_new_uint64(value->val_u64); + default: + break; + } +} + +static json_object *event_item_create_obj(knot_msg_config *config) +{ + json_object *json_event; + + json_event = json_object_new_object(); + + if (config->event.event_flags & KNOT_EVT_FLAG_CHANGE) + json_object_object_add(json_event, KNOT_JSON_FIELD_CHANGE, + json_object_new_boolean(true)); + + json_object_object_add(json_event, KNOT_JSON_FIELD_TIME_SEC, + json_object_new_int(config->event.time_sec)); + + if (config->event.event_flags & KNOT_EVT_FLAG_LOWER_THRESHOLD) + json_object_object_add(json_event, + KNOT_JSON_FIELD_LOWER_THRESHOLD, + parse_value_to_json(config->schema.value_type, + &config->event.lower_limit)); + + if (config->event.event_flags & KNOT_EVT_FLAG_UPPER_THRESHOLD) + json_object_object_add(json_event, + KNOT_JSON_FIELD_UPPER_THRESHOLD, + parse_value_to_json(config->schema.value_type, + &config->event.upper_limit)); + + /* + * Returned JSON object is in the following format: + * + * { + * "change": true, + * "timeSec": 10, + * "lowerThreshold": 1000, + * "upperThreshold": 3000 + * } + * + */ + + return json_event; +} + +static json_object *schema_item_create_obj(knot_msg_config *config) { json_object *json_schema; json_schema = json_object_new_object(); - json_object_object_add(json_schema, KNOT_JSON_FIELD_SENSOR_ID, - json_object_new_int(schema->sensor_id)); json_object_object_add(json_schema, KNOT_JSON_FIELD_VALUE_TYPE, json_object_new_int( - schema->values.value_type)); + config->schema.value_type)); json_object_object_add(json_schema, KNOT_JSON_FIELD_UNIT, json_object_new_int( - schema->values.unit)); + config->schema.unit)); json_object_object_add(json_schema, KNOT_JSON_FIELD_TYPE_ID, json_object_new_int( - schema->values.type_id)); + config->schema.type_id)); json_object_object_add(json_schema, KNOT_JSON_FIELD_DEVICE_NAME, json_object_new_string( - schema->values.name)); + config->schema.name)); /* * Returned JSON object is in the following format: * * { - * "sensorId": 1, - * "valueType": 0xFFF1, + * "typeId": 0xFFF1, * "unit": 0, - * "typeId": 3, + * "valueType": 3, * "name": "Door lock" * } + * */ return json_schema; } -static void schema_item_create_and_append(void *data, void *user_data) +static void config_item_create_and_append(void *data, void *user_data) +{ + knot_msg_config *config = data; + json_object *config_array = user_data; + json_object *schema, *event, *config_obj; + + config_obj = json_object_new_object(); + + json_object_object_add(config_obj, KNOT_JSON_FIELD_SENSOR_ID, + json_object_new_int(config->sensor_id)); + + schema = schema_item_create_obj(config); + json_object_object_add(config_obj, KNOT_JSON_FIELD_SCHEMA, schema); + + event = event_item_create_obj(config); + json_object_object_add(config_obj, KNOT_JSON_FIELD_EVENT, event); + + json_object_array_add(config_array, config_obj); +} + +static int get_event(knot_event *event, json_object *data) { - knot_msg_schema *schema = data; - json_object *schema_list = user_data; - json_object *item; + json_object *jobjkey, *jobj_event; + knot_value_type lower_threshold, upper_threshold; + int sensor_id, time_sec; + uint8_t evt_flag; + bool change; + + if (!json_object_object_get_ex(data, KNOT_JSON_FIELD_EVENT, + &jobj_event)) + return -1; + + evt_flag = KNOT_EVT_FLAG_NONE; + + /* Getting 'change' */ + if (!json_object_object_get_ex(jobj_event, KNOT_JSON_FIELD_CHANGE, + &jobjkey)) + return -1; - item = schema_item_create_obj(schema); - json_object_array_add(schema_list, item); + if (json_object_get_type(jobjkey) != json_type_boolean) + return -1; + + change = json_object_get_boolean(jobjkey); + + if (change) + evt_flag |= KNOT_EVT_FLAG_CHANGE; + + /* Getting 'timeSec' */ + if (json_object_object_get_ex(jobj_event, KNOT_JSON_FIELD_TIME_SEC, + &jobjkey)) { + + if (json_object_get_type(jobjkey) != json_type_int) + return -1; + + time_sec = json_object_get_int(jobjkey); + + evt_flag |= KNOT_EVT_FLAG_TIME; + } + + /* Getting 'lowerThreshold' */ + if (json_object_object_get_ex(jobj_event, + KNOT_JSON_FIELD_LOWER_THRESHOLD, + &jobjkey)) { + if (!parse_json2data(jobjkey, &lower_threshold)) + return -1; + + evt_flag |= KNOT_EVT_FLAG_LOWER_THRESHOLD; + } + + /* Getting 'upperThreshold' */ + if (json_object_object_get_ex(jobj_event, + KNOT_JSON_FIELD_UPPER_THRESHOLD, + &jobjkey)) { + if (!parse_json2data(jobjkey, &upper_threshold)) + return -1; + + evt_flag |= KNOT_EVT_FLAG_UPPER_THRESHOLD; + } + + event->time_sec = time_sec; + event->lower_limit = lower_threshold; + event->upper_limit = upper_threshold; + event->event_flags = evt_flag; + + return 0; } -char *parser_schema_create_object(const char *device_id, - struct l_queue *schema_list) +static int get_schema(knot_schema *schema, json_object *data) +{ + json_object *jobjkey, *jobj_schema; + int sensor_id, value_type, unit, type_id; + const char *name; + + if (!json_object_object_get_ex(data, KNOT_JSON_FIELD_SCHEMA, + &jobj_schema)) + return -1; + + /* Getting 'valueType' */ + if (!json_object_object_get_ex(jobj_schema, KNOT_JSON_FIELD_VALUE_TYPE, + &jobjkey)) + return -1; + + if (json_object_get_type(jobjkey) != json_type_int) + return -1; + + value_type = json_object_get_int(jobjkey); + + /* Getting 'unit' */ + if (!json_object_object_get_ex(jobj_schema, KNOT_JSON_FIELD_UNIT, + &jobjkey)) + return -1; + + if (json_object_get_type(jobjkey) != json_type_int) + return -1; + + unit = json_object_get_int(jobjkey); + + /* Getting 'typeId' */ + if (!json_object_object_get_ex(jobj_schema, KNOT_JSON_FIELD_TYPE_ID, + &jobjkey)) + return -1; + + if (json_object_get_type(jobjkey) != json_type_int) + return -1; + + type_id = json_object_get_int(jobjkey); + + /* Getting 'name' */ + if (!json_object_object_get_ex(jobj_schema, KNOT_JSON_FIELD_DEVICE_NAME, + &jobjkey)) + return -1; + + if (json_object_get_type(jobjkey) != json_type_string) + return -1; + + name = json_object_get_string(jobjkey); + + schema->value_type = value_type; + schema->unit = unit; + schema->type_id = type_id; + strncpy(schema->name, name, sizeof(schema->name) - 1); + + return 0; +} + +static int get_sensor_id(uint8_t *sensor_id, json_object *data) +{ + json_object *jobjkey; + + if (!json_object_object_get_ex(data, KNOT_JSON_FIELD_SENSOR_ID, + &jobjkey)) + return -1; + + if (json_object_get_type(jobjkey) != json_type_int) + return -1; + + *sensor_id = json_object_get_int(jobjkey); + + return 0; +} + +char *parser_config_create_object(const char *device_id, + struct l_queue *config_list) { char *json_str; json_object *json_msg; - json_object *json_schema_array; + json_object *json_array; json_msg = json_object_new_object(); - json_schema_array = json_object_new_array(); json_object_object_add(json_msg, KNOT_JSON_FIELD_DEVICE_ID, json_object_new_string(device_id)); - l_queue_foreach(schema_list, schema_item_create_and_append, - json_schema_array); + json_array = json_object_new_array(); + l_queue_foreach(config_list, config_item_create_and_append, + json_array); - json_object_object_add(json_msg, KNOT_JSON_FIELD_SCHEMA, - json_schema_array); + json_object_object_add(json_msg, KNOT_JSON_FIELD_CONFIG, json_array); /* * Returned JSON object is in the following format: * - * { "id": "fbe64efa6c7f717e", - * "schema" : [{ + * { + * "id": "fbe64efa6c7f717e", + * "config" : [{ * "sensorId": 1, - * "valueType": 0xFFF1, - * "unit": 0, - * "typeId": 3, - * "name": "Door lock" - * }] + * "schema": { + * "typeId": 0xFFF1, + * "unit": 0, + * "valueType": 3, + * "name": "Door lock" + * }, + * "event": { + * "change": true, + * "timeSec": 10, + * "lowerThreshold": 1000, + * "upperThreshold": 3000 + * } + * }], * } + * */ json_str = l_strdup(json_object_to_json_string(json_msg)); json_object_put(json_msg); @@ -449,107 +673,63 @@ char *parser_data_create_object(const char *device_id, uint8_t sensor_id, return (json_str && !has_err) ? json_str : NULL; } -struct l_queue *parser_schema_to_list(const char *json_str) +struct l_queue *parser_config_to_list(const char *json_str) { - json_object *jobjarray, *jobjentry, *jobjkey; + json_object *jobjconfig, *jobjarray, *jobjentry; struct l_queue *list; - knot_msg_schema *entry; - int sensor_id, value_type, unit, type_id; + knot_msg_config *config; uint64_t i; const char *name; + bool err; - jobjarray = json_tokener_parse(json_str); - if (!jobjarray) + jobjconfig = json_tokener_parse(json_str); + if (!jobjconfig) return NULL; - if (json_object_get_type(jobjarray) != json_type_array) { - json_object_put(jobjarray); + if (!json_object_object_get_ex(jobjconfig, KNOT_JSON_FIELD_CONFIG, + &jobjarray)) { + json_object_put(jobjconfig); return NULL; } list = l_queue_new(); - /* Expected JSON object is in the following format: - * - * [ {"sensorId": x, "valueType": w, - * "unit": z "typeId": y, "name": "foo"}] - * } - */ + err = false; for (i = 0; i < json_object_array_length(jobjarray); i++) { - jobjentry = json_object_array_get_idx(jobjarray, i); + config = l_new(knot_msg_config, 1); - /* Getting 'sensorId' */ - if (!json_object_object_get_ex(jobjentry, - KNOT_JSON_FIELD_SENSOR_ID, - &jobjkey)) - break; - - if (json_object_get_type(jobjkey) != json_type_int) - break; - - sensor_id = json_object_get_int(jobjkey); - - /* Getting 'valueType' */ - if (!json_object_object_get_ex(jobjentry, - KNOT_JSON_FIELD_VALUE_TYPE, - &jobjkey)) - break; - - if (json_object_get_type(jobjkey) != json_type_int) - break; - - value_type = json_object_get_int(jobjkey); - - /* Getting 'unit' */ - if (!json_object_object_get_ex(jobjentry, KNOT_JSON_FIELD_UNIT, - &jobjkey)) - break; - - if (json_object_get_type(jobjkey) != json_type_int) + jobjentry = json_object_array_get_idx(jobjarray, i); + if (!jobjentry) { + err = true; break; + } - unit = json_object_get_int(jobjkey); - - /* Getting 'typeId' */ - if (!json_object_object_get_ex(jobjentry, - KNOT_JSON_FIELD_TYPE_ID, - &jobjkey)) + if (get_sensor_id(&config->sensor_id, jobjentry) < 0) { + err = true; break; + } - if (json_object_get_type(jobjkey) != json_type_int) + if (get_schema(&config->schema, jobjentry) < 0) { + err = true; break; + } - type_id = json_object_get_int(jobjkey); - - /* Getting 'name' */ - if (!json_object_object_get_ex(jobjentry, - KNOT_JSON_FIELD_DEVICE_NAME, - &jobjkey)) - break; + if (get_event(&config->event, jobjentry) < 0) { + config->event.event_flags &= KNOT_EVT_FLAG_NONE; + config->event.event_flags |= KNOT_EVT_FLAG_UNREGISTERED; + } - if (json_object_get_type(jobjkey) != json_type_string) + if (!l_queue_push_tail(list, config)) { + err = true; break; - - name = json_object_get_string(jobjkey); - /* - * Validation not required: validation has been performed - * previously when schema has been submitted to the cloud. - */ - entry = l_new(knot_msg_schema, 1); - entry->sensor_id = sensor_id; - entry->values.value_type = value_type; - entry->values.unit = unit; - entry->values.type_id = type_id; - strncpy(entry->values.name, name, - sizeof(entry->values.name) - 1); - - l_queue_push_tail(list, entry); + } } - json_object_put(jobjarray); - if (l_queue_isempty(list)) { - l_queue_destroy(list, NULL); - list = NULL; + if (err) { + l_free(config); + l_queue_destroy(list, l_free); + json_object_put(jobjarray); + return NULL; } return list; diff --git a/src/parser.h b/src/parser.h index 0d35c92..d329c25 100644 --- a/src/parser.h +++ b/src/parser.h @@ -18,28 +18,34 @@ #define KNOT_JSON_FIELD_DEVICE_NAME "name" #define KNOT_JSON_FIELD_DEVICE_ID "id" #define KNOT_JSON_FIELD_DEVICE_TOKEN "token" -#define KNOT_JSON_FIELD_SCHEMA "schema" +#define KNOT_JSON_FIELD_CONFIG "config" #define KNOT_JSON_FIELD_DATA "data" #define KNOT_JSON_FIELD_DEVICES "devices" #define KNOT_JSON_FIELD_SENSOR_ID "sensorId" +#define KNOT_JSON_FIELD_SCHEMA "schema" +#define KNOT_JSON_FIELD_EVENT "event" #define KNOT_JSON_FIELD_SENSOR_IDS "sensorIds" #define KNOT_JSON_FIELD_VALUE "value" #define KNOT_JSON_FIELD_VALUE_TYPE "valueType" #define KNOT_JSON_FIELD_UNIT "unit" #define KNOT_JSON_FIELD_TYPE_ID "typeId" #define KNOT_JSON_FIELD_ERROR "error" +#define KNOT_JSON_FIELD_CHANGE "change" +#define KNOT_JSON_FIELD_TIME_SEC "timeSec" +#define KNOT_JSON_FIELD_LOWER_THRESHOLD "lowerThreshold" +#define KNOT_JSON_FIELD_UPPER_THRESHOLD "upperThreshold" typedef void *(create_device_item_cb) (const char *id, const char *name, struct l_queue *schema); -char *parser_schema_create_object(const char *device_id, - struct l_queue *schema_list); +char *parser_config_create_object(const char *device_id, + struct l_queue *config_list); struct l_queue *parser_update_to_list(const char *json_str); char *parser_data_create_object(const char *device_id, uint8_t sensor_id, uint8_t value_type, const knot_value_type *value, uint8_t kval_len); -struct l_queue *parser_schema_to_list(const char *json_str); +struct l_queue *parser_config_to_list(const char *json_str); struct l_queue *parser_queue_from_json_array(const char *json_str, create_device_item_cb item_cb); struct l_queue *parser_request_to_list(const char *json_str);