net: lwm2m: Added LwM2M Send operation support
LwM2M engine Send operation support /dp. Send operation based on Composite Read functionality which is not enabled at engine side like composite write. Added Kconfig configurable for composite read path list size. Created generic Read object instance which is shared between general read and Composite Read operation. Signed-off-by: Juha Heiskanen <juha.heiskanen@nordicsemi.no>
This commit is contained in:
parent
a9c1b8d0f2
commit
906feebb33
6 changed files with 466 additions and 64 deletions
|
@ -1199,5 +1199,19 @@ void lwm2m_rd_client_update(void);
|
||||||
*/
|
*/
|
||||||
char *lwm2m_path_log_strdup(char *buf, struct lwm2m_obj_path *path);
|
char *lwm2m_path_log_strdup(char *buf, struct lwm2m_obj_path *path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LwM2M SEND operation to given path list
|
||||||
|
*
|
||||||
|
* @param ctx LwM2M context
|
||||||
|
* @param path_list LwM2M Path string list
|
||||||
|
* @param path_list_size Length of path list. Max size is CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE
|
||||||
|
* @param confirmation_request True request confirmation for operation.
|
||||||
|
*
|
||||||
|
* @return 0 for success or negative in case of error.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int lwm2m_engine_send(struct lwm2m_ctx *ctx, char const *path_list[], uint8_t path_list_size,
|
||||||
|
bool confirmation_request);
|
||||||
|
|
||||||
#endif /* ZEPHYR_INCLUDE_NET_LWM2M_H_ */
|
#endif /* ZEPHYR_INCLUDE_NET_LWM2M_H_ */
|
||||||
/**@} */
|
/**@} */
|
||||||
|
|
|
@ -415,6 +415,12 @@ config LWM2M_RW_SENML_JSON_SUPPORT
|
||||||
help
|
help
|
||||||
Include support for write / parse SENML JSON data
|
Include support for write / parse SENML JSON data
|
||||||
|
|
||||||
|
config LWM2M_COMPOSITE_PATH_LIST_SIZE
|
||||||
|
int "Maximum # of composite read and send operation URL path"
|
||||||
|
default 6
|
||||||
|
help
|
||||||
|
Define path list size for Composite Read and send operation.
|
||||||
|
|
||||||
config LWM2M_DEVICE_PWRSRC_MAX
|
config LWM2M_DEVICE_PWRSRC_MAX
|
||||||
int "Maximum # of device power source records"
|
int "Maximum # of device power source records"
|
||||||
default 5
|
default 5
|
||||||
|
|
|
@ -114,6 +114,8 @@ static sys_slist_t engine_obj_list;
|
||||||
static sys_slist_t engine_obj_inst_list;
|
static sys_slist_t engine_obj_inst_list;
|
||||||
static sys_slist_t engine_service_list;
|
static sys_slist_t engine_service_list;
|
||||||
|
|
||||||
|
#define LWM2M_DP_CLIENT_URI "dp"
|
||||||
|
|
||||||
static K_KERNEL_STACK_DEFINE(engine_thread_stack,
|
static K_KERNEL_STACK_DEFINE(engine_thread_stack,
|
||||||
CONFIG_LWM2M_ENGINE_STACK_SIZE);
|
CONFIG_LWM2M_ENGINE_STACK_SIZE);
|
||||||
static struct k_thread engine_thread_data;
|
static struct k_thread engine_thread_data;
|
||||||
|
@ -3326,49 +3328,13 @@ static int do_read_op(struct lwm2m_message *msg, uint16_t content_format)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int lwm2m_perform_read_op(struct lwm2m_message *msg, uint16_t content_format)
|
static int lwm2m_perform_read_object_instance(struct lwm2m_message *msg,
|
||||||
|
struct lwm2m_engine_obj_inst *obj_inst,
|
||||||
|
uint8_t *num_read)
|
||||||
{
|
{
|
||||||
struct lwm2m_engine_obj_inst *obj_inst = NULL;
|
|
||||||
struct lwm2m_engine_res *res = NULL;
|
struct lwm2m_engine_res *res = NULL;
|
||||||
struct lwm2m_engine_obj_field *obj_field;
|
struct lwm2m_engine_obj_field *obj_field;
|
||||||
struct lwm2m_obj_path temp_path;
|
int ret = 0;
|
||||||
int ret = 0, index;
|
|
||||||
uint8_t num_read = 0U;
|
|
||||||
|
|
||||||
if (msg->path.level >= 2U) {
|
|
||||||
obj_inst = get_engine_obj_inst(msg->path.obj_id, msg->path.obj_inst_id);
|
|
||||||
if (!obj_inst) {
|
|
||||||
/* When Object instace is indicated error have to be reported */
|
|
||||||
return -ENOENT;
|
|
||||||
}
|
|
||||||
} else if (msg->path.level == 1U) {
|
|
||||||
/* find first obj_inst with path's obj_id.
|
|
||||||
* Path level 1 can accept NULL. It define empty payload to response.
|
|
||||||
*/
|
|
||||||
obj_inst = next_engine_obj_inst(msg->path.obj_id, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set output content-format */
|
|
||||||
ret = coap_append_option_int(msg->out.out_cpkt,
|
|
||||||
COAP_OPTION_CONTENT_FORMAT,
|
|
||||||
content_format);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOG_ERR("Error setting response content-format: %d", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = coap_packet_append_payload_marker(msg->out.out_cpkt);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOG_ERR("Error appending payload marker: %d", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* store original path values so we can change them during processing */
|
|
||||||
memcpy(&temp_path, &msg->path, sizeof(temp_path));
|
|
||||||
ret = engine_put_begin(&msg->out, &msg->path);
|
|
||||||
if (ret < 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (obj_inst) {
|
while (obj_inst) {
|
||||||
if (!obj_inst->resources || obj_inst->resource_count == 0U) {
|
if (!obj_inst->resources || obj_inst->resource_count == 0U) {
|
||||||
|
@ -3378,7 +3344,7 @@ int lwm2m_perform_read_op(struct lwm2m_message *msg, uint16_t content_format)
|
||||||
/* update the obj_inst_id as we move through the instances */
|
/* update the obj_inst_id as we move through the instances */
|
||||||
msg->path.obj_inst_id = obj_inst->obj_inst_id;
|
msg->path.obj_inst_id = obj_inst->obj_inst_id;
|
||||||
|
|
||||||
if (msg->path.level <= 1U) {
|
if (msg->path.level <= LWM2M_PATH_LEVEL_OBJECT) {
|
||||||
/* start instance formatting */
|
/* start instance formatting */
|
||||||
ret = engine_put_begin_oi(&msg->out, &msg->path);
|
ret = engine_put_begin_oi(&msg->out, &msg->path);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -3386,23 +3352,15 @@ int lwm2m_perform_read_op(struct lwm2m_message *msg, uint16_t content_format)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (index = 0; index < obj_inst->resource_count; index++) {
|
for (int index = 0; index < obj_inst->resource_count; index++) {
|
||||||
if (msg->path.level > 2 &&
|
if (msg->path.level > LWM2M_PATH_LEVEL_OBJECT_INST &&
|
||||||
msg->path.res_id !=
|
msg->path.res_id != obj_inst->resources[index].res_id) {
|
||||||
obj_inst->resources[index].res_id) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = &obj_inst->resources[index];
|
res = &obj_inst->resources[index];
|
||||||
|
|
||||||
/*
|
|
||||||
* On an entire object instance, we need to set path's
|
|
||||||
* res_id for lwm2m_read_handler to read this specific
|
|
||||||
* resource.
|
|
||||||
*/
|
|
||||||
msg->path.res_id = res->res_id;
|
msg->path.res_id = res->res_id;
|
||||||
obj_field = lwm2m_get_engine_obj_field(obj_inst->obj,
|
obj_field = lwm2m_get_engine_obj_field(obj_inst->obj, res->res_id);
|
||||||
res->res_id);
|
|
||||||
if (!obj_field) {
|
if (!obj_field) {
|
||||||
ret = -ENOENT;
|
ret = -ENOENT;
|
||||||
} else if (!LWM2M_HAS_PERM(obj_field, LWM2M_PERM_R)) {
|
} else if (!LWM2M_HAS_PERM(obj_field, LWM2M_PERM_R)) {
|
||||||
|
@ -3424,13 +3382,12 @@ int lwm2m_perform_read_op(struct lwm2m_message *msg, uint16_t content_format)
|
||||||
return ret;
|
return ret;
|
||||||
} else if (ret < 0) {
|
} else if (ret < 0) {
|
||||||
/* ignore errors unless single read */
|
/* ignore errors unless single read */
|
||||||
if (msg->path.level > 2 &&
|
if (msg->path.level > LWM2M_PATH_LEVEL_OBJECT_INST &&
|
||||||
!LWM2M_HAS_PERM(obj_field,
|
!LWM2M_HAS_PERM(obj_field, BIT(LWM2M_FLAG_OPTIONAL))) {
|
||||||
BIT(LWM2M_FLAG_OPTIONAL))) {
|
|
||||||
LOG_ERR("READ OP: %d", ret);
|
LOG_ERR("READ OP: %d", ret);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
num_read += 1U;
|
*num_read += 1U;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* end resource formatting */
|
/* end resource formatting */
|
||||||
|
@ -3441,7 +3398,7 @@ int lwm2m_perform_read_op(struct lwm2m_message *msg, uint16_t content_format)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* on single read break if errors */
|
/* on single read break if errors */
|
||||||
if (ret < 0 && msg->path.level > 2) {
|
if (ret < 0 && msg->path.level > LWM2M_PATH_LEVEL_OBJECT_INST) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3450,7 +3407,7 @@ int lwm2m_perform_read_op(struct lwm2m_message *msg, uint16_t content_format)
|
||||||
}
|
}
|
||||||
|
|
||||||
move_forward:
|
move_forward:
|
||||||
if (msg->path.level <= 1U) {
|
if (msg->path.level <= LWM2M_PATH_LEVEL_OBJECT) {
|
||||||
/* end instance formatting */
|
/* end instance formatting */
|
||||||
ret = engine_put_end_oi(&msg->out, &msg->path);
|
ret = engine_put_end_oi(&msg->out, &msg->path);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -3458,25 +3415,72 @@ move_forward:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg->path.level <= 1U) {
|
if (msg->path.level <= LWM2M_PATH_LEVEL_OBJECT) {
|
||||||
/* advance to the next object instance */
|
/* advance to the next object instance */
|
||||||
obj_inst = next_engine_obj_inst(msg->path.obj_id,
|
obj_inst = next_engine_obj_inst(msg->path.obj_id, obj_inst->obj_inst_id);
|
||||||
obj_inst->obj_inst_id);
|
|
||||||
} else {
|
} else {
|
||||||
obj_inst = NULL;
|
obj_inst = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = engine_put_end(&msg->out, &msg->path);
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lwm2m_perform_read_op(struct lwm2m_message *msg, uint16_t content_format)
|
||||||
|
{
|
||||||
|
struct lwm2m_engine_obj_inst *obj_inst = NULL;
|
||||||
|
struct lwm2m_obj_path temp_path;
|
||||||
|
int ret = 0;
|
||||||
|
uint8_t num_read = 0U;
|
||||||
|
|
||||||
|
if (msg->path.level >= LWM2M_PATH_LEVEL_OBJECT_INST) {
|
||||||
|
obj_inst = get_engine_obj_inst(msg->path.obj_id,
|
||||||
|
msg->path.obj_inst_id);
|
||||||
|
} else if (msg->path.level == LWM2M_PATH_LEVEL_OBJECT) {
|
||||||
|
/* find first obj_inst with path's obj_id */
|
||||||
|
obj_inst = next_engine_obj_inst(msg->path.obj_id, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!obj_inst) {
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set output content-format */
|
||||||
|
ret = coap_append_option_int(msg->out.out_cpkt,
|
||||||
|
COAP_OPTION_CONTENT_FORMAT,
|
||||||
|
content_format);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG_ERR("Error setting response content-format: %d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = coap_packet_append_payload_marker(msg->out.out_cpkt);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG_ERR("Error appending payload marker: %d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* store original path values so we can change them during processing */
|
||||||
|
memcpy(&temp_path, &msg->path, sizeof(temp_path));
|
||||||
|
|
||||||
|
if (engine_put_begin(&msg->out, &msg->path) < 0) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = lwm2m_perform_read_object_instance(msg, obj_inst, &num_read);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (engine_put_end(&msg->out, &msg->path) < 0) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
/* restore original path values */
|
/* restore original path values */
|
||||||
memcpy(&msg->path, &temp_path, sizeof(temp_path));
|
memcpy(&msg->path, &temp_path, sizeof(temp_path));
|
||||||
|
|
||||||
/* did not read anything even if we should have - on single item */
|
/* did not read anything even if we should have - on single item */
|
||||||
if (ret == 0 && num_read == 0U && msg->path.level == 3U) {
|
if (ret == 0 && num_read == 0U && msg->path.level >= LWM2M_PATH_LEVEL_RESOURCE) {
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5475,4 +5479,240 @@ void lwm2m_engine_clear_duplicate_path(sys_slist_t *lwm2m_path_list, sys_slist_t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int lwm2m_perform_composite_read_root(struct lwm2m_message *msg, uint8_t *num_read)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct lwm2m_engine_obj *obj;
|
||||||
|
struct lwm2m_engine_obj_inst *obj_inst;
|
||||||
|
|
||||||
|
SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_list, obj, node) {
|
||||||
|
/* Security obj MUST NOT be part of registration message */
|
||||||
|
if (obj->obj_id == LWM2M_OBJECT_SECURITY_ID) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg->path.level = 1;
|
||||||
|
msg->path.obj_id = obj->obj_id;
|
||||||
|
|
||||||
|
obj_inst = next_engine_obj_inst(msg->path.obj_id, -1);
|
||||||
|
|
||||||
|
if (!obj_inst) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = lwm2m_perform_read_object_instance(msg, obj_inst, num_read);
|
||||||
|
if (ret == -ENOMEM) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lwm2m_perform_composite_read_op(struct lwm2m_message *msg, uint16_t content_format,
|
||||||
|
sys_slist_t *lwm_path_list)
|
||||||
|
{
|
||||||
|
struct lwm2m_engine_obj_inst *obj_inst = NULL;
|
||||||
|
struct lwm2m_obj_path_list *entry;
|
||||||
|
int ret = 0;
|
||||||
|
uint8_t num_read = 0U;
|
||||||
|
|
||||||
|
/* set output content-format */
|
||||||
|
ret = coap_append_option_int(msg->out.out_cpkt, COAP_OPTION_CONTENT_FORMAT, content_format);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG_ERR("Error setting response content-format: %d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = coap_packet_append_payload_marker(msg->out.out_cpkt);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG_ERR("Error appending payload marker: %d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add object start mark */
|
||||||
|
engine_put_begin(&msg->out, &msg->path);
|
||||||
|
|
||||||
|
/* Read resource from path */
|
||||||
|
SYS_SLIST_FOR_EACH_CONTAINER(lwm_path_list, entry, node) {
|
||||||
|
/* Copy path to message path */
|
||||||
|
memcpy(&msg->path, &entry->path, sizeof(struct lwm2m_obj_path));
|
||||||
|
|
||||||
|
if (msg->path.level >= LWM2M_PATH_LEVEL_OBJECT_INST) {
|
||||||
|
obj_inst = get_engine_obj_inst(msg->path.obj_id, msg->path.obj_inst_id);
|
||||||
|
} else if (msg->path.level == LWM2M_PATH_LEVEL_OBJECT) {
|
||||||
|
/* find first obj_inst with path's obj_id */
|
||||||
|
obj_inst = next_engine_obj_inst(msg->path.obj_id, -1);
|
||||||
|
} else {
|
||||||
|
/* Read rooth Path */
|
||||||
|
ret = lwm2m_perform_composite_read_root(msg, &num_read);
|
||||||
|
if (ret == -ENOMEM) {
|
||||||
|
LOG_ERR("Supported message size is too small for read root");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!obj_inst) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = lwm2m_perform_read_object_instance(msg, obj_inst, &num_read);
|
||||||
|
if (ret == -ENOMEM) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* did not read anything even if we should have - on single item */
|
||||||
|
if (num_read == 0U) {
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add object end mark */
|
||||||
|
if (engine_put_end(&msg->out, &msg->path) < 0) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_send_op(struct lwm2m_message *msg, uint16_t content_format,
|
||||||
|
sys_slist_t *lwm_path_list)
|
||||||
|
{
|
||||||
|
switch (content_format) {
|
||||||
|
#if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
|
||||||
|
case LWM2M_FORMAT_APP_SEML_JSON:
|
||||||
|
return do_send_op_senml_json(msg, lwm_path_list);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
default:
|
||||||
|
LOG_ERR("Unsupported content-format for /dp: %u", content_format);
|
||||||
|
return -ENOMSG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_send_reply_cb(const struct coap_packet *response,
|
||||||
|
struct coap_reply *reply,
|
||||||
|
const struct sockaddr *from)
|
||||||
|
{
|
||||||
|
uint8_t code;
|
||||||
|
|
||||||
|
code = coap_header_get_code(response);
|
||||||
|
LOG_DBG("Send callback (code:%u.%u)",
|
||||||
|
COAP_RESPONSE_CODE_CLASS(code),
|
||||||
|
COAP_RESPONSE_CODE_DETAIL(code));
|
||||||
|
|
||||||
|
if (code == COAP_RESPONSE_CODE_CHANGED) {
|
||||||
|
LOG_INF("Send done!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_ERR("Failed with code %u.%u. Not Retrying.",
|
||||||
|
COAP_RESPONSE_CODE_CLASS(code), COAP_RESPONSE_CODE_DETAIL(code));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_send_timeout_cb(struct lwm2m_message *msg)
|
||||||
|
{
|
||||||
|
LOG_WRN("Send Timeout");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int lwm2m_engine_send(struct lwm2m_ctx *ctx, char const *path_list[], uint8_t path_list_size,
|
||||||
|
bool confirmation_request)
|
||||||
|
{
|
||||||
|
struct lwm2m_message *msg;
|
||||||
|
int ret;
|
||||||
|
uint16_t content_format;
|
||||||
|
|
||||||
|
/* Path list buffer */
|
||||||
|
struct lwm2m_obj_path temp;
|
||||||
|
struct lwm2m_obj_path_list lwm2m_path_list_buf[CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE];
|
||||||
|
sys_slist_t lwm_path_list;
|
||||||
|
sys_slist_t lwm_path_free_list;
|
||||||
|
|
||||||
|
/* Init list */
|
||||||
|
lwm2m_engine_path_list_init(&lwm_path_list, &lwm_path_free_list, lwm2m_path_list_buf,
|
||||||
|
CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE);
|
||||||
|
|
||||||
|
if (path_list_size > CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE) {
|
||||||
|
return -E2BIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select content format use CBOR when it possible */
|
||||||
|
if (IS_ENABLED(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)) {
|
||||||
|
content_format = LWM2M_FORMAT_APP_SEML_JSON;
|
||||||
|
} else {
|
||||||
|
LOG_WRN("SenML CBOR or JSON is not supported");
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse Path to internal used object path format */
|
||||||
|
for (int i = 0; i < path_list_size; i++) {
|
||||||
|
ret = string_to_path(path_list[i], &temp, '/');
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/* Add to linked list */
|
||||||
|
if (lwm2m_engine_add_path_to_list(&lwm_path_list, &lwm_path_free_list, &temp)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Clear path which are part are part of recursive path /1 will include /1/0/1 */
|
||||||
|
lwm2m_engine_clear_duplicate_path(&lwm_path_list, &lwm_path_free_list);
|
||||||
|
|
||||||
|
/* Allocate Message buffer */
|
||||||
|
msg = lwm2m_get_message(ctx);
|
||||||
|
if (!msg) {
|
||||||
|
LOG_ERR("Unable to get a lwm2m message!");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (confirmation_request) {
|
||||||
|
msg->type = COAP_TYPE_CON;
|
||||||
|
msg->reply_cb = do_send_reply_cb;
|
||||||
|
msg->message_timeout_cb = do_send_timeout_cb;
|
||||||
|
} else {
|
||||||
|
msg->type = COAP_TYPE_NON_CON;
|
||||||
|
msg->reply_cb = NULL;
|
||||||
|
msg->message_timeout_cb = NULL;
|
||||||
|
}
|
||||||
|
msg->code = COAP_METHOD_POST;
|
||||||
|
msg->mid = coap_next_id();
|
||||||
|
msg->tkl = LWM2M_MSG_TOKEN_GENERATE_NEW;
|
||||||
|
msg->out.out_cpkt = &msg->cpkt;
|
||||||
|
|
||||||
|
ret = lwm2m_init_message(msg);
|
||||||
|
if (ret) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ret = select_writer(&msg->out, content_format);
|
||||||
|
if (ret) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH,
|
||||||
|
LWM2M_DP_CLIENT_URI,
|
||||||
|
strlen(LWM2M_DP_CLIENT_URI));
|
||||||
|
if (ret < 0) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write requested path data */
|
||||||
|
ret = do_send_op(msg, content_format, &lwm_path_list);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG_ERR("Send (err:%d)", ret);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
LOG_INF("Send op to server (/dp)");
|
||||||
|
lwm2m_send_message_async(msg);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
cleanup:
|
||||||
|
lwm2m_reset_message(msg, true);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
SYS_INIT(lwm2m_engine_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
|
SYS_INIT(lwm2m_engine_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
|
||||||
|
|
|
@ -128,6 +128,9 @@ int lwm2m_register_payload_handler(struct lwm2m_message *msg);
|
||||||
|
|
||||||
int lwm2m_perform_read_op(struct lwm2m_message *msg, uint16_t content_format);
|
int lwm2m_perform_read_op(struct lwm2m_message *msg, uint16_t content_format);
|
||||||
|
|
||||||
|
int lwm2m_perform_composite_read_op(struct lwm2m_message *msg, uint16_t content_format,
|
||||||
|
sys_slist_t *lwm_path_list);
|
||||||
|
|
||||||
int lwm2m_write_handler(struct lwm2m_engine_obj_inst *obj_inst,
|
int lwm2m_write_handler(struct lwm2m_engine_obj_inst *obj_inst,
|
||||||
struct lwm2m_engine_res *res,
|
struct lwm2m_engine_res *res,
|
||||||
struct lwm2m_engine_res_inst *res_inst,
|
struct lwm2m_engine_res_inst *res_inst,
|
||||||
|
|
|
@ -1555,3 +1555,137 @@ end_of_operation:
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t json_parse_composite_read_paths(struct lwm2m_message *msg,
|
||||||
|
sys_slist_t *lwm_path_list,
|
||||||
|
sys_slist_t *lwm_path_free_list)
|
||||||
|
{
|
||||||
|
struct json_in_formatter_data fd;
|
||||||
|
struct lwm2m_obj_path path;
|
||||||
|
bool path_valid;
|
||||||
|
int ret;
|
||||||
|
uint8_t name[MAX_RESOURCE_LEN];
|
||||||
|
uint8_t base_name[MAX_RESOURCE_LEN];
|
||||||
|
uint8_t full_name[MAX_RESOURCE_LEN];
|
||||||
|
uint8_t valid_path_cnt = 0;
|
||||||
|
|
||||||
|
while (json_next_token(&msg->in, &fd)) {
|
||||||
|
if (!(fd.json_flags & T_VALUE)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
path_valid = false;
|
||||||
|
switch (json_atribute_decode(&msg->in, &fd)) {
|
||||||
|
case SENML_JSON_BASE_NAME_ATTRIBUTE:
|
||||||
|
if (fd.value_len >= sizeof(base_name)) {
|
||||||
|
LOG_ERR("Base name too long");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buf_read(base_name, fd.value_len, CPKT_BUF_READ(msg->in.in_cpkt),
|
||||||
|
&fd.value_offset) < 0) {
|
||||||
|
LOG_ERR("Error parsing base name!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
base_name[fd.value_len] = '\0';
|
||||||
|
|
||||||
|
/* Relative name is optional - preinitialize full name with base name */
|
||||||
|
snprintk(full_name, sizeof(full_name), "%s", base_name);
|
||||||
|
|
||||||
|
if (fd.json_flags & T_OBJECT_END) {
|
||||||
|
path_valid = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SENML_JSON_NAME_ATTRIBUTE:
|
||||||
|
|
||||||
|
/* handle resource name */
|
||||||
|
if (fd.value_len >= MAX_RESOURCE_LEN) {
|
||||||
|
LOG_ERR("Relative name too long");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get value for relative path */
|
||||||
|
if (buf_read(name, fd.value_len, CPKT_BUF_READ(msg->in.in_cpkt),
|
||||||
|
&fd.value_offset) < 0) {
|
||||||
|
LOG_ERR("Error parsing relative path!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
name[fd.value_len] = '\0';
|
||||||
|
|
||||||
|
/* combine base_name + name */
|
||||||
|
snprintk(full_name, sizeof(full_name), "%s%s", base_name, name);
|
||||||
|
path_valid = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path_valid) {
|
||||||
|
ret = parse_path(full_name, strlen(full_name), &path);
|
||||||
|
if (ret >= 0) {
|
||||||
|
path.level = ret;
|
||||||
|
if (lwm2m_engine_add_path_to_list(lwm_path_list, lwm_path_free_list,
|
||||||
|
&path) == 0) {
|
||||||
|
valid_path_cnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return valid_path_cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
int do_composite_read_op_senml_json(struct lwm2m_message *msg)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct json_out_formatter_data fd;
|
||||||
|
struct lwm2m_obj_path_list lwm2m_path_list_buf[CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE];
|
||||||
|
sys_slist_t lwm_path_list;
|
||||||
|
sys_slist_t lwm_path_free_list;
|
||||||
|
uint8_t path_list_size;
|
||||||
|
|
||||||
|
/* Init list */
|
||||||
|
lwm2m_engine_path_list_init(&lwm_path_list, &lwm_path_free_list, lwm2m_path_list_buf,
|
||||||
|
CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE);
|
||||||
|
|
||||||
|
/* Parse Path's from SenML JSO payload */
|
||||||
|
path_list_size = json_parse_composite_read_paths(msg, &lwm_path_list, &lwm_path_free_list);
|
||||||
|
if (path_list_size == 0) {
|
||||||
|
LOG_ERR("No Valid Url at msg");
|
||||||
|
return -ESRCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear path which are part are part of recursive path /1 will include /1/0/1 */
|
||||||
|
lwm2m_engine_clear_duplicate_path(&lwm_path_list, &lwm_path_free_list);
|
||||||
|
|
||||||
|
(void)memset(&fd, 0, sizeof(fd));
|
||||||
|
engine_set_out_user_data(&msg->out, &fd);
|
||||||
|
|
||||||
|
/* Detect longest match base name to url */
|
||||||
|
lwm2m_define_longest_match_url_for_base_name(&fd, &lwm_path_list);
|
||||||
|
|
||||||
|
ret = lwm2m_perform_composite_read_op(msg, LWM2M_FORMAT_APP_SEML_JSON, &lwm_path_list);
|
||||||
|
engine_clear_out_user_data(&msg->out);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int do_send_op_senml_json(struct lwm2m_message *msg, sys_slist_t *lwm_path_list)
|
||||||
|
{
|
||||||
|
struct json_out_formatter_data fd;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
(void)memset(&fd, 0, sizeof(fd));
|
||||||
|
engine_set_out_user_data(&msg->out, &fd);
|
||||||
|
|
||||||
|
/* Detect longest match base name to url */
|
||||||
|
lwm2m_define_longest_match_url_for_base_name(&fd, lwm_path_list);
|
||||||
|
|
||||||
|
ret = lwm2m_perform_composite_read_op(msg, LWM2M_FORMAT_APP_SEML_JSON, lwm_path_list);
|
||||||
|
engine_clear_out_user_data(&msg->out);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
|
@ -19,4 +19,9 @@ int do_read_op_senml_json(struct lwm2m_message *msg);
|
||||||
/* General Write single Path operation */
|
/* General Write single Path operation */
|
||||||
int do_write_op_senml_json(struct lwm2m_message *msg);
|
int do_write_op_senml_json(struct lwm2m_message *msg);
|
||||||
|
|
||||||
|
/* Send opearation builder */
|
||||||
|
int do_send_op_senml_json(struct lwm2m_message *msg, sys_slist_t *lwm_path_list);
|
||||||
|
/* API for call composite READ from engine */
|
||||||
|
int do_composite_read_op_senml_json(struct lwm2m_message *msg);
|
||||||
|
|
||||||
#endif /* LWM2M_RW_SENML_JSON_H_ */
|
#endif /* LWM2M_RW_SENML_JSON_H_ */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue