doubao_chat/lib/net/libduer-device/modules/dcs/lightduer_dcs_router.c

1529 lines
44 KiB
C

/**
* Copyright (2017) Baidu Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* File: lightduer_dcs_router.c
* Auth: Gang Chen (chengang12@baidu.com)
* Desc: Light duer dcs directive router.
*/
#include "duerapp_config.h"
#include "lightduer_dcs_router.h"
#include "lightduer_dcs.h"
#include "printf.h"
#include <stdlib.h>
#include <string.h>
#include "lightduer_dcs_local.h"
#include "lightduer_log.h"
#include "lightduer_memory.h"
#include "lightduer_connagent.h"
#include "lightduer_lib.h"
#include "lightduer_ca.h"
#include "lightduer_mutex.h"
#include "lightduer_ds_log_dcs.h"
#include "lightduer_random.h"
#include "lightduer_queue_cache.h"
#ifdef DUER_EVENT_HANDLER
#include "lightduer_handler.h"
#else
#include "lightduer_event_emitter.h"
#endif // DUER_EVENT_HANDLER
#include "lightduer_ds_log_e2e.h"
#include "lightduer_voice.h"
typedef struct {
char *name_space;
duer_directive_list *list;
size_t count;
} directive_namespace_list_t;
typedef enum {
STANDARD_DCS_DIRECTIVE,
PRIVATE_PROTOCAL_DIRECTIVE,
} directive_source_t;
typedef struct {
baidu_json *directive;
int index;
directive_source_t source;
} directive_node_t;
static volatile size_t s_namespace_count;
static volatile directive_namespace_list_t *s_directive_namespace_list;
static volatile size_t s_client_context_cb_count;
static volatile dcs_client_context_handler *s_dcs_client_context_cb;
static duer_qcache_handler s_directive_queue;
static duer_mutex_t s_directive_queue_lock;
static volatile duer_bool s_wait_dialog_finish;
static volatile duer_bool s_is_initialized;
static volatile duer_bool s_is_speak_pending;
static volatile int s_speaking_index;
static const char *IOT_CLOUD_EXTRA_KEY = "iot_cloud_extra";
static const char *INDEX_KEY = "index";
static const int DUMMY_INDEX = 0;
static dcs_directive_hook_handler s_dcs_directive_hook_cb = NULL;
static dcs_event_hook_handler s_dcs_hook_event_handler = NULL;
static const char *NAMESPACE_DLP = "ai.dueros.device_interface.dlp";
#define DUER_PRINT_BJSON_ESTIMATED_SIZE_FOR_REPORT_EXCEPTION (1024)
static duer_status_t duer_handle_directive(baidu_json *directive,
int index,
directive_source_t source);
duer_status_t duer_reg_client_context_cb_internal(dcs_client_context_handler cb)
{
size_t size = 0;
duer_status_t ret = DUER_OK;
dcs_client_context_handler *list = NULL;
int i = 0;
if (!cb) {
DUER_LOGE("Failed to add client context handler: param error\n");
DUER_DS_LOG_REPORT_DCS_PARAM_ERROR();
ret = DUER_ERR_INVALID_PARAMETER;
return ret;
}
if (s_dcs_client_context_cb) {
while (i < s_client_context_cb_count) {
if (s_dcs_client_context_cb[i] == cb) {
DUER_LOGD("client context cb had been registered");
return ret;
}
i++;
}
}
if (!s_dcs_client_context_cb) {
s_dcs_client_context_cb = DUER_MALLOC(sizeof(dcs_client_context_handler));
if (!s_dcs_client_context_cb) {
DUER_LOGE("Failed to add dcs client context cb: no memory\n");
DUER_DS_LOG_REPORT_DCS_MEMORY_ERROR();
ret = DUER_ERR_MEMORY_OVERFLOW;
return ret;
}
} else {
size = sizeof(dcs_client_context_handler) * (s_client_context_cb_count + 1);
list = (dcs_client_context_handler *)DUER_REALLOC((void *)s_dcs_client_context_cb, size);
if (list) {
s_dcs_client_context_cb = list;
} else {
DUER_LOGE("Failed to add dcs client context cb: no memory\n");
DUER_DS_LOG_REPORT_DCS_MEMORY_ERROR();
ret = DUER_ERR_MEMORY_OVERFLOW;
return ret;
}
}
s_dcs_client_context_cb[s_client_context_cb_count] = cb;
s_client_context_cb_count++;
return ret;
}
void duer_reg_dcs_hook_directive_hdl(dcs_directive_hook_handler cb)
{
if (!s_dcs_directive_hook_cb) {
s_dcs_directive_hook_cb = cb;
} else {
DUER_LOGI("dlp directive has been initialed");
}
}
void duer_reg_dcs_hook_event_hdl(dcs_event_hook_handler cb)
{
if (cb) {
duer_reg_dcs_event_hook_hdl_internal(cb);
s_dcs_hook_event_handler = cb;
}
}
void duer_dcs_hook_event_status_report(baidu_json *status)
{
if (s_dcs_hook_event_handler) {
s_dcs_hook_event_handler(status);
}
}
duer_status_t duer_reg_client_context_cb(dcs_client_context_handler cb)
{
duer_status_t ret = DUER_OK;
DUER_DCS_CRITICAL_ENTER();
if (s_is_initialized) {
ret = duer_reg_client_context_cb_internal(cb);
} else {
DUER_LOGW("DCS has not been initialed");
ret = DUER_ERR_FAILED;
}
DUER_DCS_CRITICAL_EXIT();
return ret;
}
baidu_json *duer_create_dcs_event(const char *name_space, const char *name, duer_bool need_msg_id)
{
baidu_json *event = NULL;
baidu_json *header = NULL;
baidu_json *payload = NULL;
const char *msg_id = NULL;
event = baidu_json_CreateObject();
if (event == NULL) {
goto error_out;
}
header = baidu_json_CreateObject();
if (header == NULL) {
goto error_out;
}
baidu_json_AddStringToObjectCS(header, DCS_NAMESPACE_KEY, name_space);
baidu_json_AddStringToObjectCS(header, DCS_NAME_KEY, name);
if (need_msg_id) {
msg_id = duer_generate_msg_id_internal();
if (msg_id) {
baidu_json_AddStringToObject(header, DCS_MESSAGE_ID_KEY, msg_id);
DUER_LOGD("msg_id: %s", msg_id);
}
}
baidu_json_AddItemToObjectCS(event, DCS_HEADER_KEY, header);
payload = baidu_json_CreateObject();
if (payload == NULL) {
goto error_out;
}
baidu_json_AddItemToObjectCS(event, DCS_PAYLOAD_KEY, payload);
return event;
error_out:
DUER_DS_LOG_REPORT_DCS_MEMORY_ERROR();
if (event) {
baidu_json_Delete(event);
}
return NULL;
}
baidu_json *duer_get_client_context_internal(void)
{
baidu_json *client_context = NULL;
baidu_json *state = NULL;
int i = 0;
client_context = baidu_json_CreateArray();
if (!client_context) {
DUER_DS_LOG_REPORT_DCS_MEMORY_ERROR();
DUER_LOGE("Memory overlow");
return NULL;
}
while ((i < s_client_context_cb_count) && (s_dcs_client_context_cb[i])) {
state = s_dcs_client_context_cb[i]();
if (state) {
baidu_json_AddItemToArray(client_context, state);
}
i++;
}
return client_context;
}
static directive_namespace_list_t *duer_get_namespace_node(const char *name_space)
{
int i = 0;
size_t size = 0;
char *new_namespace = NULL;
directive_namespace_list_t *list = NULL;
if (!s_directive_namespace_list) {
s_directive_namespace_list = DUER_MALLOC(sizeof(*s_directive_namespace_list));
if (!s_directive_namespace_list) {
return NULL;
}
DUER_MEMSET((void *)s_directive_namespace_list, 0, sizeof(*s_directive_namespace_list));
s_directive_namespace_list->name_space = duer_strdup_internal(name_space);
if (!s_directive_namespace_list->name_space) {
DUER_FREE((void *)s_directive_namespace_list);
s_directive_namespace_list = NULL;
return NULL;
}
s_namespace_count++;
return (directive_namespace_list_t *)s_directive_namespace_list;
}
for (i = 0; i < s_namespace_count; i++) {
if (DUER_STRCMP(name_space, s_directive_namespace_list[i].name_space) == 0) {
return (directive_namespace_list_t *)&s_directive_namespace_list[i];
}
}
new_namespace = duer_strdup_internal(name_space);
if (!new_namespace) {
return NULL;
}
size = sizeof(*s_directive_namespace_list) * (s_namespace_count + 1);
list = DUER_REALLOC((void *)s_directive_namespace_list, size);
if (!list) {
DUER_FREE(new_namespace);
return NULL;
}
s_directive_namespace_list = list;
DUER_MEMSET((void *)&s_directive_namespace_list[s_namespace_count],
0,
sizeof(*s_directive_namespace_list));
s_directive_namespace_list[s_namespace_count].name_space = new_namespace;
return (directive_namespace_list_t *)&s_directive_namespace_list[s_namespace_count++];
}
static duer_status_t duer_directive_duplicate(directive_namespace_list_t *node,
const duer_directive_list *directive)
{
int i = 0;
char *name = NULL;
duer_directive_list *list = NULL;
if (node->list) {
for (i = 0; i < node->count; i++) {
if (DUER_STRCMP(node->list[i].directive_name, directive->directive_name) == 0
&& node->list[i].cb == directive->cb) {
return DUER_OK;
}
}
}
name = duer_strdup_internal(directive->directive_name);
if (!name) {
DUER_DS_LOG_REPORT_DCS_MEMORY_ERROR();
return DUER_ERR_MEMORY_OVERFLOW;
}
if (node->list) {
list = (duer_directive_list *)DUER_REALLOC(node->list,
sizeof(*directive) * (node->count + 1));
} else {
list = (duer_directive_list *)DUER_MALLOC(sizeof(*directive));
}
if (!list) {
DUER_FREE(name);
DUER_DS_LOG_REPORT_DCS_MEMORY_ERROR();
return DUER_ERR_MEMORY_OVERFLOW;
}
list[node->count].cb = directive->cb;
list[node->count].directive_name = name;
node->list = list;
node->count++;
return DUER_OK;
}
duer_status_t duer_add_dcs_directive_internal(const duer_directive_list *directive,
size_t count,
const char *name_space)
{
int i = 0;
duer_status_t ret = DUER_OK;
directive_namespace_list_t *node;
DUER_LOGI("namespace: %s", name_space);
if (!directive || (count == 0) || !name_space) {
DUER_LOGE("Failed to add dcs directive: param error");
DUER_DS_LOG_REPORT_DCS_PARAM_ERROR();
ret = DUER_ERR_INVALID_PARAMETER;
goto exit;
}
node = duer_get_namespace_node(name_space);
if (!node) {
ret = DUER_ERR_MEMORY_OVERFLOW;
DUER_LOGE("Failed to add dcs directive: no memory");
DUER_DS_LOG_REPORT_DCS_MEMORY_ERROR();
goto exit;
}
for (i = 0; i < count; i++) {
ret = duer_directive_duplicate(node, &directive[i]);
if (ret != DUER_OK) {
DUER_LOGE("Failed to add dcs directive: %s, errcode: %d",
directive[i].directive_name,
ret);
break;
}
}
exit:
if ((ret != DUER_OK) && (ret != DUER_ERR_INVALID_PARAMETER)) {
while (i < count) {
duer_ds_log_dcs_add_directive_fail(directive[i++].directive_name);
}
}
return ret;
}
static duer_status_t duer_directive_enqueue(baidu_json *directive,
int index,
directive_source_t source)
{
duer_status_t rs = DUER_OK;
directive_node_t *node = NULL;
directive_node_t *tmp_node = NULL;
duer_qcache_iterator *p = NULL; // used to find the location to insert the directive
duer_qcache_iterator *target = NULL; // insert the directive after q
node = (directive_node_t *)DUER_MALLOC(sizeof(directive_node_t));
if (!node) {
DUER_LOGE("Failed to cache directive: memory overlow");
rs = DUER_ERR_MEMORY_OVERFLOW;
goto RET;
}
node->directive = baidu_json_Duplicate(directive, 0);
if (!node->directive) {
rs = DUER_ERR_MEMORY_OVERFLOW;
goto RET;
}
node->index = index;
node->source = source;
p = duer_qcache_head(s_directive_queue);
while (p) {
tmp_node = (directive_node_t *)duer_qcache_data(p);
if (!tmp_node) {
DUER_LOGE("Failed to get directive node");
rs = DUER_ERR_FAILED;
goto RET;
}
if (tmp_node->index > index) {
break;
}
target = p;
p = duer_qcache_next(p);
}
rs = duer_qcache_insert(s_directive_queue, target, node);
if (rs != DUER_OK) {
DUER_LOGE("Failed to insert directive to queue");
}
RET:
if (rs != DUER_OK) {
if (node) {
baidu_json_release(node->directive);
DUER_FREE(node);
}
}
return rs;
}
duer_status_t duer_add_dcs_directive(const duer_directive_list *directive,
size_t count,
const char *name_space)
{
duer_status_t ret = DUER_OK;
DUER_DCS_CRITICAL_ENTER();
if (s_is_initialized) {
ret = duer_add_dcs_directive_internal(directive, count, name_space);
} else {
DUER_LOGW("DCS has not been initialed");
ret = DUER_ERR_FAILED;
}
DUER_DCS_CRITICAL_EXIT();
return ret;
}
static duer_status_t duer_dialog_finish_cb(const baidu_json *directive)
{
DUER_DCS_CRITICAL_ENTER();
duer_listen_stop_internal();
s_wait_dialog_finish = DUER_FALSE;
duer_play_channel_control_internal(DCS_DIALOG_FINISHED);
DUER_DCS_CRITICAL_EXIT();
return DUER_OK;
}
void duer_dialog_started(void)
{
s_wait_dialog_finish = DUER_TRUE;
}
static baidu_json *duer_create_directive(const char *namespace,
const char *name,
const char *dialog_id)
{
baidu_json *directive = NULL;
baidu_json *header = NULL;
baidu_json *payload = NULL;
const char *msg_id = NULL;
directive = baidu_json_CreateObject();
if (directive == NULL) {
goto error_out;
}
header = baidu_json_CreateObject();
if (header == NULL) {
goto error_out;
}
baidu_json_AddItemToObjectCS(directive, DCS_HEADER_KEY, header);
baidu_json_AddStringToObjectCS(header, DCS_NAMESPACE_KEY, namespace);
baidu_json_AddStringToObjectCS(header, DCS_NAME_KEY, name);
baidu_json_AddStringToObject(header, DCS_DIALOG_REQUEST_ID_KEY, dialog_id);
msg_id = duer_generate_msg_id_internal();
if (msg_id) {
baidu_json_AddStringToObject(header, DCS_MESSAGE_ID_KEY, msg_id);
DUER_LOGD("msg_id: %s", msg_id);
}
payload = baidu_json_CreateObject();
if (payload == NULL) {
goto error_out;
}
baidu_json_AddItemToObjectCS(directive, DCS_PAYLOAD_KEY, payload);
return directive;
error_out:
DUER_DS_LOG_REPORT_DCS_MEMORY_ERROR();
if (directive) {
baidu_json_Delete(directive);
}
return NULL;
}
static duer_status_t duer_session_finish_callback(void *data)
{
baidu_json *directive = (baidu_json *)data;
if (directive == NULL) {
directive = duer_create_directive(DCS_PRIVATE_NAMESPACE,
DCS_DIALOGUE_FINISHED_NAME,
duer_get_request_id_internal());
}
if (directive) {
duer_handle_directive(directive, 1024, PRIVATE_PROTOCAL_DIRECTIVE);
}
baidu_json_Delete(directive);
return DUER_OK;
}
static dcs_directive_handler *duer_dcs_get_handlers(const char *name_space,
const char *name,
int *count)
{
directive_namespace_list_t *node = NULL;
dcs_directive_handler *ret = NULL;
dcs_directive_handler *buf = NULL;
int i = 0;
*count = 0;
if (!s_is_initialized) {
return NULL;
}
for (i = 0; i < s_namespace_count; i++) {
if (DUER_STRCMP(s_directive_namespace_list[i].name_space,
name_space) == 0) {
node = (directive_namespace_list_t *)&s_directive_namespace_list[i];
}
}
if (!node) {
return NULL;
}
for (i = 0; i < node->count; i++) {
if (DUER_STRCMP(node->list[i].directive_name, name) == 0) {
if (!ret) {
buf = DUER_MALLOC((*count + 1) * sizeof(*ret));
} else {
buf = DUER_REALLOC(ret, (*count + 1) * sizeof(*ret));
}
if (!buf) {
DUER_LOGE("Failed to get all handlers");
break;
}
ret = buf;
ret[*count] = node->list[i].cb;
*count = *count + 1;
}
}
return ret;
}
duer_status_t duer_execute_directive(baidu_json *directive)
{
int i = 0;
int hdlr_count = 0;
duer_status_t rs = DUER_OK;
baidu_json *name = NULL;
baidu_json *name_space = NULL;
baidu_json *header = NULL;
dcs_directive_handler *handler = NULL;
header = baidu_json_GetObjectItem(directive, DCS_HEADER_KEY);
if (!header) {
DUER_LOGE("Failed to parse directive header");
rs = DUER_MSG_RSP_BAD_REQUEST;
goto RET;
}
name_space = baidu_json_GetObjectItem(header, DCS_NAMESPACE_KEY);
if (!name_space || !name_space->valuestring) {
DUER_LOGE("Failed to parse directive namespace");
rs = DUER_MSG_RSP_BAD_REQUEST;
goto RET;
}
name = baidu_json_GetObjectItem(header, DCS_NAME_KEY);
if (!name || !name->valuestring) {
DUER_LOGE("Failed to parse directive name");
rs = DUER_MSG_RSP_BAD_REQUEST;
goto RET;
}
DUER_LOGI("Directive namespace: %s", name_space->valuestring);
DUER_LOGI("Directive name: %s", name->valuestring);
if (!DUER_MEMCMP(name_space->valuestring, NAMESPACE_DLP, DUER_STRLEN(NAMESPACE_DLP))) {
if (s_dcs_directive_hook_cb) {
s_dcs_directive_hook_cb(directive);
}
goto RET;
}
#ifdef DUER_MONITOR_TEST_ENABLE
duer_dcs_monitor_test_add_result(name_space->valuestring, name->valuestring);
#endif
DUER_DCS_CRITICAL_ENTER();
handler = duer_dcs_get_handlers(name_space->valuestring, name->valuestring, &hdlr_count);
DUER_DCS_CRITICAL_EXIT();
if (handler) {
for (i = 0; i < hdlr_count; i++) {
rs = handler[i](directive);
if (rs != DUER_OK) {
goto RET;
}
}
} else {
DUER_LOGE("unrecognized directive: %s, namespace: %s\n",
name->valuestring,
name_space->valuestring);
rs = DUER_MSG_RSP_NOT_FOUND;
}
RET:
if (handler) {
DUER_FREE(handler);
}
return rs;
}
static duer_bool duer_is_directive_equal(const char *namespace1,
const char *name1,
const char *namespace2,
const char *name2)
{
if (namespace1 && name1 && namespace2 && name2) {
if ((DUER_STRCMP(name1, name2) == 0) && (DUER_STRCMP(namespace1, namespace2) == 0)) {
return DUER_TRUE;
}
}
return DUER_FALSE;
}
static duer_bool duer_is_speak_directive(baidu_json *directive)
{
baidu_json *name = NULL;
baidu_json *name_space = NULL;
baidu_json *header = NULL;
duer_bool is_speak_directive = DUER_FALSE;
header = baidu_json_GetObjectItem(directive, DCS_HEADER_KEY);
if (header) {
name = baidu_json_GetObjectItem(header, DCS_NAME_KEY);
name_space = baidu_json_GetObjectItem(header, DCS_NAMESPACE_KEY);
if (name && name_space && name->valuestring && name_space->valuestring) {
is_speak_directive = duer_is_directive_equal(name_space->valuestring,
name->valuestring,
DCS_VOICE_OUTPUT_NAMESPACE,
DCS_SPEAK_NAME);
}
}
return is_speak_directive;
}
static void duer_dcs_check_and_report_exception(directive_node_t *node, duer_status_t rs)
{
baidu_json *value = NULL;
char *payload = NULL;
if (rs == DUER_OK) {
return;
}
if (!node || !node->directive) {
return;
}
value = baidu_json_CreateObject();
if (!value) {
DUER_DS_LOG_REPORT_DCS_MEMORY_ERROR();
return;
}
baidu_json_AddItemReferenceToObject(value, DCS_DIRECTIVE_KEY, node->directive);
#ifdef DUER_BJSON_PRINT_WITH_ESTIMATED_SIZE
payload = baidu_json_PrintBuffered(value,
DUER_PRINT_BJSON_ESTIMATED_SIZE_FOR_REPORT_EXCEPTION, 0);
#else
payload = baidu_json_PrintUnformatted(value);
#endif // DUER_BJSON_PRINT_WITH_ESTIMATED_SIZE
baidu_json_Delete(value);
value = NULL;
if (!payload) {
DUER_DS_LOG_REPORT_DCS_MEMORY_ERROR();
return;
}
baidu_json_Delete(node->directive);
node->directive = NULL;
if (rs == DUER_MSG_RSP_NOT_FOUND) {
value = duer_get_exception_internal(payload, 0,
DCS_UNSUPPORTED_OPERATION_TYPE, "Unknow directive");
} else if (rs == DUER_MSG_RSP_BAD_REQUEST) {
value = duer_get_exception_internal(payload, 0,
DCS_UNEXPECTED_INFORMATION_RECEIVED_TYPE,
"Bad directive");
} else {
value = duer_get_exception_internal(payload, 0, DCS_INTERNAL_ERROR_TYPE, "Internal error");
}
if (payload) {
baidu_json_release(payload);
}
rs = DUER_ERR_FAILED;
if (value) {
rs = duer_dcs_data_report_internal(value, DUER_TRUE);
baidu_json_Delete(value);
value = NULL;
}
if (rs != DUER_OK) {
duer_ds_log_dcs_event_report_fail(DCS_EXCEPTION_ENCOUNTERED_NAME);
}
}
static void duer_execute_caching_directive(int what, void *data)
{
duer_bool is_speak_directive = DUER_FALSE;
duer_status_t rs = DUER_OK;
directive_node_t *node = NULL;
duer_mutex_lock(s_directive_queue_lock);
do {
if (!s_directive_queue) {
DUER_LOGW("s_directive_queue = null");
break;
}
if (duer_qcache_length(s_directive_queue) == 0) {
break;
}
node = duer_qcache_pop(s_directive_queue);
if (!node || !node->directive) {
DUER_LOGE("Failed to get directive");
break;
}
is_speak_directive = duer_is_speak_directive(node->directive);
if (is_speak_directive) {
s_wait_dialog_finish = DUER_TRUE;
s_is_speak_pending = DUER_TRUE;
s_speaking_index = node->index;
}
rs = duer_execute_directive(node->directive);
if (node->source == STANDARD_DCS_DIRECTIVE) {
duer_dcs_check_and_report_exception(node, rs);
}
if (node->directive) {
baidu_json_Delete(node->directive);
node->directive = NULL;
}
DUER_FREE(node);
if (!s_is_speak_pending) {
if (duer_qcache_length(s_directive_queue) > 0) {
#ifdef DUER_EVENT_HANDLER
duer_handler_handle(duer_execute_caching_directive, 0, NULL);
#else
duer_emitter_emit(duer_execute_caching_directive, 0, NULL);
#endif // DUER_EVENT_HANDLER
}
}
} while (0);
duer_mutex_unlock(s_directive_queue_lock);
}
const char *duer_create_request_id(void)
{
return duer_create_request_id_internal(DCS_DIALOG_NONE);
}
static void duer_clear_directive_queue(int what, void *data)
{
directive_node_t *node = NULL;
duer_mutex_lock(s_directive_queue_lock);
s_speaking_index = DUMMY_INDEX;
if (s_is_speak_pending) {
duer_dcs_stop_speak_handler();
s_is_speak_pending = DUER_FALSE;
}
s_wait_dialog_finish = DUER_FALSE;
duer_speech_on_stop_internal();
if (!s_directive_queue) {
duer_mutex_unlock(s_directive_queue_lock);
return;
}
do {
node = duer_qcache_pop(s_directive_queue);
if (node) {
baidu_json_Delete(node->directive);
DUER_FREE(node);
}
} while (duer_qcache_length(s_directive_queue) > 0);
duer_mutex_unlock(s_directive_queue_lock);
}
void duer_cancel_caching_directive_internal()
{
#ifdef DUER_EVENT_HANDLER
duer_handler_handle(duer_clear_directive_queue, 0, NULL);
#else
duer_emitter_emit(duer_clear_directive_queue, 0, NULL);
#endif // DUER_EVENT_HANDLER
}
duer_bool duer_wait_dialog_finished_internal(void)
{
return s_wait_dialog_finish;
}
void duer_speak_directive_finished_internal(void)
{
duer_mutex_lock(s_directive_queue_lock);
s_speaking_index = DUMMY_INDEX;
s_is_speak_pending = DUER_FALSE;
do {
if (!s_directive_queue) {
DUER_LOGW("s_directive_queue = null");
break;
}
if (duer_qcache_length(s_directive_queue) > 0) {
#ifdef DUER_EVENT_HANDLER
duer_handler_handle(duer_execute_caching_directive, 0, NULL);
#else
duer_emitter_emit(duer_execute_caching_directive, 0, NULL);
#endif // DUER_EVENT_HANDLER
}
} while (0);
duer_mutex_unlock(s_directive_queue_lock);
}
#ifdef DUER_STATISTICS_E2E
static void duer_e2e_response(const char *name_space, const char *name)
{
duer_bool is_e2e_response_directive = DUER_FALSE;
if (!duer_ds_e2e_wait_response()) {
return;
}
is_e2e_response_directive = duer_is_directive_equal(name_space,
name,
DCS_VOICE_OUTPUT_NAMESPACE,
DCS_SPEAK_NAME);
if (!is_e2e_response_directive) {
is_e2e_response_directive = duer_is_directive_equal(name_space,
name,
DCS_AUDIO_PLAYER_NAMESPACE,
DCS_PLAY_NAME);
}
if (is_e2e_response_directive) {
duer_ds_e2e_event(DUER_E2E_RESPONSE);
}
}
#endif
static duer_status_t duer_handle_directive(baidu_json *directive,
int index,
directive_source_t source)
{
baidu_json *dialog_id = NULL;
baidu_json *name = NULL;
baidu_json *header = NULL;
baidu_json *name_space = NULL;
const char *current_dialog_id = NULL;
duer_status_t rs = DUER_OK;
duer_bool is_unblock_directive = DUER_FALSE;
header = baidu_json_GetObjectItem(directive, DCS_HEADER_KEY);
if (!header) {
DUER_LOGE("Failed to parse header");
return DUER_MSG_RSP_BAD_REQUEST;
}
dialog_id = baidu_json_GetObjectItem(header, DCS_DIALOG_REQUEST_ID_KEY);
// execute the directive immediately if no dialog_id item
if (!dialog_id) {
rs = duer_execute_directive(directive);
return rs;
}
if (!dialog_id->valuestring) {
DUER_LOGE("Failed to parse dialog_id");
return DUER_MSG_RSP_BAD_REQUEST;
}
current_dialog_id = duer_get_request_id_internal();
if (!current_dialog_id) {
DUER_LOGE("Failed to get current dialog id");
return DUER_ERR_FAILED;
}
name = baidu_json_GetObjectItem(header, DCS_NAME_KEY);
if (!name || !name->valuestring) {
DUER_LOGE("Failed to parse name");
return DUER_MSG_RSP_BAD_REQUEST;
}
if (DUER_STRCMP(current_dialog_id, dialog_id->valuestring)) {
DUER_LOGI("Drop the directive for old dialog_Id: %s, current dialog_Id: %s",
dialog_id->valuestring, current_dialog_id);
duer_ds_log_dcs_directive_drop(current_dialog_id,
dialog_id->valuestring,
name->valuestring);
return DUER_OK;
}
name_space = baidu_json_GetObjectItem(header, DCS_NAMESPACE_KEY);
if (!name_space || !name_space->valuestring) {
DUER_LOGE("Failed to parse namespace");
return DUER_MSG_RSP_BAD_REQUEST;
}
#ifdef DUER_STATISTICS_E2E
duer_e2e_response(name_space->valuestring, name->valuestring);
#endif
is_unblock_directive = duer_is_directive_equal(name_space->valuestring,
name->valuestring,
DCS_VOICE_INPUT_NAMESPACE,
DCS_STOP_LISTEN_NAME);
if (!is_unblock_directive) {
is_unblock_directive = duer_is_directive_equal(name_space->valuestring,
name->valuestring,
DCS_SCREEN_NAMESPACE,
DCS_RENDER_VOICE_INPUT_TEXT_NAME);
}
if (is_unblock_directive) {
DUER_LOGD("Unblocked directive");
rs = duer_execute_directive(directive);
return rs;
}
duer_mutex_lock(s_directive_queue_lock);
if (s_directive_queue) {
rs = duer_directive_enqueue(directive, index, source);
if (rs == DUER_OK) {
if ((duer_qcache_length(s_directive_queue) == 1 && !s_is_speak_pending)
|| (s_is_speak_pending && index < s_speaking_index)) {
#ifdef DUER_EVENT_HANDLER
rs = duer_handler_handle(duer_execute_caching_directive, 0, NULL);
#else
rs = duer_emitter_emit(duer_execute_caching_directive, 0, NULL);
#endif // DUER_EVENT_HANDLER
}
} else {
DUER_LOGE("Failed to enqueue directive");
}
DUER_LOGD("queue len: %d", duer_qcache_length(s_directive_queue));
}
duer_mutex_unlock(s_directive_queue_lock);
return rs;
}
// now the last parameter payload is not used
static duer_status_t duer_dcs_directive_parse(duer_msg_t *msg,
baidu_json **value,
baidu_json **directive,
baidu_json **header,
baidu_json **name_space,
baidu_json **name,
char **payload)
{
duer_status_t rs = DUER_OK;
baidu_json *directive_header = NULL;
do {
if (!s_is_initialized) {
DUER_LOGW("DCS has been uninitialized");
rs = DUER_MSG_RSP_NOT_FOUND;
break;
}
if (!msg) {
rs = DUER_MSG_RSP_BAD_REQUEST;
break;
}
if (!value || !directive) {
rs = DUER_ERR_INVALID_PARAMETER;
break;
}
if (!msg->payload || msg->payload_len <= 0) {
rs = DUER_MSG_RSP_BAD_REQUEST;
break;
}
*value = baidu_json_Parse((const char *)msg->payload);
if (*value == NULL) {
DUER_LOGE("Failed to parse payload");
rs = DUER_ERR_FAILED;
break;
}
*directive = baidu_json_GetObjectItem(*value, DCS_DIRECTIVE_KEY);
if (*directive == NULL) {
DUER_LOGE("Failed to parse directive");
rs = DUER_MSG_RSP_BAD_REQUEST;
break;
}
if (!header && !name_space && !name) {
break;
}
directive_header = baidu_json_GetObjectItem(*directive, DCS_HEADER_KEY);
if (!directive_header) {
DUER_LOGE("Failed to parse header");
rs = DUER_MSG_RSP_BAD_REQUEST;
break;
}
if (header) {
*header = directive_header;
}
if (name_space) {
*name_space = baidu_json_GetObjectItem(directive_header, DCS_NAMESPACE_KEY);
if (*name_space == NULL || (*name_space)->valuestring == NULL) {
DUER_LOGE("Failed to parse directive namespace");
rs = DUER_MSG_RSP_BAD_REQUEST;
break;
}
}
if (name) {
*name = baidu_json_GetObjectItem(directive_header, DCS_NAME_KEY);
if (*name == NULL || (*name)->valuestring == NULL) {
DUER_LOGE("Failed to parse directive namespace");
rs = DUER_MSG_RSP_BAD_REQUEST;
break;
}
}
} while (0);
return rs;
}
static int duer_get_directive_index(baidu_json *value, const char *name)
{
baidu_json *index = NULL;
baidu_json *extra_info = NULL;
extra_info = baidu_json_GetObjectItem(value, IOT_CLOUD_EXTRA_KEY);
if (extra_info) {
index = baidu_json_GetObjectItem(extra_info, INDEX_KEY);
#if 0
baidu_json *timestamp = baidu_json_GetObjectItem(extra_info, "timestamp");
DUER_LOGI("directive name:%s, timestamp:%s, index:%d",
name,
(timestamp && timestamp->valuestring) ? timestamp->valuestring : "",
index ? index->valueint : DUMMY_INDEX);
#endif
}
return index ? index->valueint : DUMMY_INDEX;
}
static duer_status_t duer_dcs_router(duer_context ctx, duer_msg_t *msg, duer_addr_t *addr)
{
char *payload = NULL;
duer_status_t rs = DUER_OK;
baidu_json *directive = NULL;
baidu_json *value = NULL;
duer_msg_code_e msg_code = DUER_MSG_RSP_CHANGED;
baidu_json *name = NULL;
int index = DUMMY_INDEX;
DUER_LOGV("Enter");
do {
rs = duer_dcs_directive_parse(msg, &value, &directive, NULL, NULL, &name, NULL);
if (rs != DUER_OK) {
break;
}
index = duer_get_directive_index(value, name->valuestring);
rs = duer_handle_directive(directive, index, STANDARD_DCS_DIRECTIVE);
} while (0);
if (rs != DUER_OK) {
if (rs == DUER_MSG_RSP_NOT_FOUND) {
duer_report_exception_internal((const char *)msg->payload,
msg->payload_len,
DCS_UNSUPPORTED_OPERATION_TYPE,
"Unknow directive");
msg_code = DUER_MSG_RSP_NOT_FOUND;
} else if (rs == DUER_MSG_RSP_BAD_REQUEST) {
duer_report_exception_internal((const char *)msg->payload,
msg->payload_len,
DCS_UNEXPECTED_INFORMATION_RECEIVED_TYPE,
"Bad directive");
msg_code = DUER_MSG_RSP_BAD_REQUEST;
} else if (msg->payload_len == 2
&& DUER_STRNCMP((const char *)msg->payload, "\r\n", 2) == 0) {
DUER_LOGW("Receive an empty message");
msg_code = DUER_MSG_RSP_INTERNAL_SERVER_ERROR;
} else {
duer_report_exception_internal((const char *)msg->payload,
msg->payload_len,
DCS_INTERNAL_ERROR_TYPE,
"Internal error");
msg_code = DUER_MSG_RSP_INTERNAL_SERVER_ERROR;
}
}
duer_response(msg, msg_code, NULL, 0);
if (payload) {
DUER_FREE(payload);
}
if (value) {
baidu_json_Delete(value);
}
return rs;
}
static duer_status_t duer_private_protocal_cb(duer_context ctx, duer_msg_t *msg, duer_addr_t *addr)
{
duer_status_t rs = DUER_OK;
baidu_json *value = NULL;
baidu_json *name = NULL;
baidu_json *name_space = NULL;
baidu_json *header = NULL;
baidu_json *directive = NULL;
baidu_json *client_context = NULL;
baidu_json *response_json = NULL;
char *response_data = NULL;
size_t response_data_size = 0;
duer_bool seperated_response = DUER_FALSE;
duer_msg_code_e msg_code = DUER_MSG_RSP_CHANGED;
int index = DUMMY_INDEX;
DUER_LOGV("Enter");
rs = duer_dcs_directive_parse(msg, &value, &directive, &header, &name_space, &name, NULL);
if (rs != DUER_OK) {
goto RET;
}
if (DUER_STRCMP(DCS_GET_STATUS_NAME, name->valuestring) == 0
&& DUER_STRCMP(DCS_PRIVATE_NAMESPACE, name_space->valuestring) == 0) {
if (msg->token_len > 0) {
DUER_LOGI("response, mid:%d", msg->msg_id);
duer_response(msg, DUER_MSG_EMPTY_MESSAGE, NULL, 0);// ack in empty message first!
seperated_response = DUER_TRUE;
}
DUER_DCS_CRITICAL_ENTER();
client_context = duer_get_client_context_internal();
DUER_DCS_CRITICAL_EXIT();
if (client_context) {
response_json = baidu_json_CreateObject();
if (response_json) {
baidu_json_AddItemToObjectCS(response_json, DCS_CLIENT_CONTEXT_KEY, client_context);
//response_data = baidu_json_PrintUnformatted(response_json);
//response_data_size = response_data ? DUER_STRLEN(response_data) : 0;
} else {
DUER_LOGE("Failed to create response msg: memory too low");
baidu_json_Delete(client_context);
rs = DUER_ERR_MEMORY_OVERFLOW;
}
}
} else {
index = duer_get_directive_index(value, name->valuestring);
rs = duer_handle_directive(directive, index, PRIVATE_PROTOCAL_DIRECTIVE);
}
RET:
if (rs != DUER_OK) {
if (rs == DUER_MSG_RSP_BAD_REQUEST) {
msg_code = DUER_MSG_RSP_BAD_REQUEST;
} else {
msg_code = DUER_MSG_RSP_INTERNAL_SERVER_ERROR;
}
}
if (seperated_response == DUER_FALSE) {
duer_response(msg, msg_code, response_data, response_data_size);
} else {
DUER_LOGI("seperated response, token_len:%d", msg->token_len);
duer_seperate_response((const char *)msg->token, msg->token_len, msg_code, response_json);
}
if (response_json) {
baidu_json_Delete(response_json);
}
if (response_data) {
DUER_FREE(response_data);
}
if (value) {
baidu_json_Delete(value);
}
return rs;
}
static duer_status_t duer_iot_cloud_cb(duer_context ctx, duer_msg_t *msg, duer_addr_t *addr)
{
duer_status_t rs = DUER_OK;
baidu_json *value = NULL;
baidu_json *directive = NULL;
duer_msg_code_e msg_code = DUER_MSG_RSP_CHANGED;
DUER_LOGV("Enter");
rs = duer_dcs_directive_parse(msg, &value, &directive, NULL, NULL, NULL, NULL);
if (rs == DUER_OK) {
rs = duer_execute_directive(directive);
}
if (rs != DUER_OK) {
if (rs == DUER_MSG_RSP_NOT_FOUND) {
msg_code = DUER_MSG_RSP_NOT_FOUND;
} else if (rs == DUER_MSG_RSP_BAD_REQUEST) {
msg_code = DUER_MSG_RSP_BAD_REQUEST;
} else {
msg_code = DUER_MSG_RSP_INTERNAL_SERVER_ERROR;
}
}
duer_response(msg, msg_code, NULL, 0);
if (value) {
baidu_json_Delete(value);
}
return rs;
}
void duer_dcs_dialog_cancel(void)
{
DUER_DCS_CRITICAL_ENTER();
if (s_is_initialized) {
duer_create_request_id_internal(DCS_DIALOG_NONE);
} else {
DUER_LOGW("DCS has not been initialized");
}
DUER_DCS_CRITICAL_EXIT();
}
static duer_status_t dummy_directive_cb(const baidu_json *directive)
{
return DUER_OK;
}
int duer_dcs_data_send(baidu_json *data)
{
return duer_data_report(data);
}
int duer_dcs_data_report(baidu_json *data)
{
return duer_dcs_data_report_internal(data, DUER_TRUE);
}
static duer_status_t duer_dlp_cb(duer_context ctx, duer_msg_t *msg, duer_addr_t *addr)
{
duer_status_t rs = DUER_OK;
baidu_json *value = NULL;
baidu_json *directive = NULL;
duer_msg_code_e msg_code = DUER_MSG_RSP_CHANGED;
DUER_LOGV("Enter");
rs = duer_dcs_directive_parse(msg, &value, &directive, NULL, NULL, NULL, NULL);
if (rs == DUER_OK) {
rs = duer_execute_directive(directive);
}
if (rs != DUER_OK) {
if (rs == DUER_MSG_RSP_NOT_FOUND) {
msg_code = DUER_MSG_RSP_NOT_FOUND;
} else if (rs == DUER_MSG_RSP_BAD_REQUEST) {
msg_code = DUER_MSG_RSP_BAD_REQUEST;
} else {
msg_code = DUER_MSG_RSP_INTERNAL_SERVER_ERROR;
}
}
duer_response(msg, msg_code, NULL, 0);
if (value) {
baidu_json_Delete(value);
}
return rs;
}
void duer_dcs_framework_init()
{
duer_res_t res[] = {
{
DUER_RES_MODE_DYNAMIC,
DUER_RES_OP_PUT,
(char *)DCS_DUER_DIRECTIVE_PATH,
{
duer_dcs_router
}
},
{
DUER_RES_MODE_DYNAMIC,
DUER_RES_OP_PUT,
(char *)DCS_DUER_PRIVATE_PATH,
{
duer_private_protocal_cb
}
},
{
DUER_RES_MODE_DYNAMIC,
DUER_RES_OP_PUT,
(char *)DCS_IOTCLOUD_DIRECTIVE_PATH,
{
duer_iot_cloud_cb
}
},
{
DUER_RES_MODE_DYNAMIC,
DUER_RES_OP_PUT,
(char *)DCS_DUER_DLP_PATH,
{
duer_dlp_cb
}
},
};
duer_add_resources(res, sizeof(res) / sizeof(res[0]));
duer_ds_e2e_set_dialog_id_cb(duer_get_request_id_internal);
duer_reg_dialog_id_cb(duer_get_request_id_internal);
duer_dcs_local_init_internal();
DUER_DCS_CRITICAL_ENTER();
if (s_is_initialized) {
DUER_DCS_CRITICAL_EXIT();
DUER_LOGW("DCS has been initialized");
return;
}
duer_dcs_sys_exit_internal();
if (!s_directive_queue) {
s_directive_queue = duer_qcache_create();
if (!s_directive_queue) {
DUER_LOGE("Failed to create DCS directive queue");
}
}
if (!s_directive_queue_lock) {
s_directive_queue_lock = duer_mutex_create();
if (!s_directive_queue_lock) {
DUER_LOGE("Failed to create s_directive_queue_lock");
}
}
duer_directive_list directive = {DCS_DIALOGUE_FINISHED_NAME, duer_dialog_finish_cb};
duer_add_dcs_directive_internal(&directive, 1, DCS_PRIVATE_NAMESPACE);
// Unnecessary directives might be received for some special reason, exception event will be
// reported to cloud if these directives have no handler. Hence we register default callbacks
// for them.
duer_directive_list list[] = {
{DCS_RENDER_PLAYER_INFO, dummy_directive_cb},
{DCS_RENDER_AUDIO_LIST, dummy_directive_cb},
{DCS_RENDER_ALBUM_LIST, dummy_directive_cb},
};
duer_add_dcs_directive_internal(list,
sizeof(list) / sizeof(list[0]),
DCS_SCREEN_EXTENDED_CARD_NAMESPACE);
duer_directive_list screen_directive[] = {
{DCS_RENDER_VOICE_INPUT_TEXT_NAME, dummy_directive_cb},
{DCS_RENDER_CARD_NAME, dummy_directive_cb},
};
duer_add_dcs_directive_internal(screen_directive,
sizeof(screen_directive) / sizeof(screen_directive[0]),
DCS_SCREEN_NAMESPACE);
s_is_initialized = DUER_TRUE;
duer_declare_sys_interface_internal();
duer_dcs_play_control_init_internal();
DUER_DCS_CRITICAL_EXIT();
}
void duer_dcs_uninitialize(void)
{
int i = 0;
int j = 0;
DUER_DCS_CRITICAL_ENTER();
if (!s_is_initialized) {
DUER_LOGW("DCS has not been initialized");
DUER_DCS_CRITICAL_EXIT();
return;
}
duer_dcs_sys_exit_internal();
s_is_initialized = DUER_FALSE;
while (i < s_namespace_count) {
if (s_directive_namespace_list[i].name_space) {
DUER_FREE(s_directive_namespace_list[i].name_space);
}
if (s_directive_namespace_list[i].list) {
for (j = 0; j < s_directive_namespace_list[i].count; j++) {
if (s_directive_namespace_list[i].list[j].directive_name) {
DUER_FREE((void *)s_directive_namespace_list[i].list[j].directive_name);
}
}
DUER_FREE(s_directive_namespace_list[i].list);
}
i++;
}
s_namespace_count = 0;
if (s_directive_namespace_list) {
DUER_FREE((void *)s_directive_namespace_list);
s_directive_namespace_list = NULL;
}
if (s_dcs_client_context_cb) {
DUER_FREE((void *)s_dcs_client_context_cb);
s_dcs_client_context_cb = NULL;
}
s_client_context_cb_count = 0;
duer_mutex_lock(s_directive_queue_lock);
if (s_directive_queue) {
duer_qcache_destroy(s_directive_queue);
s_directive_queue = NULL;
}
duer_mutex_unlock(s_directive_queue_lock);
duer_sys_interface_uninitialize_internal();
duer_dcs_alert_uninitialize_internal();
duer_dcs_audio_player_uninitialize_internal();
duer_dcs_device_control_uninitialize_internal();
duer_dcs_play_control_uninitialize_internal();
duer_dcs_infrared_control_uninitialize_internal();
duer_dcs_voice_output_uninitialize_internal();
duer_dcs_voice_input_uninitialize_internal();
duer_dcs_screen_uninitialize_internal();
duer_dcs_local_uninitialize_internal();
DUER_DCS_CRITICAL_EXIT();
}
baidu_json *duer_dcs_get_client_context(void)
{
baidu_json *client_context = NULL;
DUER_DCS_CRITICAL_ENTER();
client_context = duer_get_client_context_internal();
DUER_DCS_CRITICAL_EXIT();
return client_context;
}