添加角色配置at指令测试,待uI验证
authorluozheng <1732036298@qq.com>
Wed, 24 Sep 2025 08:14:56 +0000 (16:14 +0800)
committerluozheng <1732036298@qq.com>
Wed, 24 Sep 2025 08:14:56 +0000 (16:14 +0800)
12 files changed:
code/sdk/application/wearable/ln/nativeapp/nativeui/include/uiservice/ui_service.h
code/sdk/application/wearable/ln/nativeapp/nativeui/uiservice/src/ui_service.cpp
code/sdk/zhihui/leiniao/imsl_ln/leiniao_api_at.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api.h
code/sdk/zhihui/leiniao/imsl_ln/ln_api_device_info.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api_speech_chat.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api_story_choose.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api_story_random.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api_token.c
code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.cpp
code/sdk/zhihui/leiniao/ln_api_cpp/ln_api_interface.h

index 91f1172..d661807 100644 (file)
@@ -53,6 +53,7 @@ typedef enum {
     HANDLE_JUMP_OTA_VIEW, //处理跳转ota界面
     HAND_SCHEDULE_REMINDER_NOTIFY, // 日程提醒通知
     HAND_TASK_REMINDER_NOTIFY, // 任务提醒通知
+    HAND_CHARACTER_CONFIG_CHANGE_NOTIFY, // 角色配置更改通知
     MAX_NUM_HANDLER, 
 } UiServiceMsgId;
 
index ebe38b5..7d44279 100644 (file)
@@ -87,12 +87,14 @@ typedef struct
 static void HandleJumpOtaView(Request *request);
 static void HandScheduleReminderNotify(Request *request);
 static void HandTaskReminderNotify(Request *request);
+static void HandTaskCharacterConfigNotify(Request *request);
 
 // 消息处理程序项数组(每个消息类型对应一个处理函数,函数名以 Handle 开头)
 MsgHandlerEntry msgHandlers[] = {
     {HANDLE_JUMP_OTA_VIEW, HandleJumpOtaView},
     {HAND_SCHEDULE_REMINDER_NOTIFY,HandScheduleReminderNotify},
-    {HAND_TASK_REMINDER_NOTIFY,HandTaskReminderNotify}
+    {HAND_TASK_REMINDER_NOTIFY,HandTaskReminderNotify},
+    {HAND_CHARACTER_CONFIG_CHANGE_NOTIFY,HandTaskCharacterConfigNotify},
 
 };
 /* -------------------------------------------------------------------------- */
@@ -314,6 +316,11 @@ void HandTaskReminderNotify(Request *request)
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_UI_SERVICE, "HandTaskReminderNotify msg_id = %d",request->msgId);
     int status = *(int *)request->data;
 }
+void HandTaskCharacterConfigNotify(Request *request)
+{
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_UI_SERVICE, "HandTaskCharacterConfigNotify msg_id = %d",request->msgId);
+    int status = *(int *)request->data;
+}
 /* -------------------------------------------------------------------------- */
 #ifdef __cplusplus
 }
index b087106..04653c1 100644 (file)
@@ -1151,6 +1151,75 @@ static uint32_t ln_schedule_reminder_cfg_at(uint8_t *para, uint32_t paraLen, cha
     return ERRCODE_SUCC;
 }
 
+/* AT^LN_CHARACTER_CFG[=characterName,eyesShape,hairColor,eyesColor,hairStyle,screenImageAsEmoji] */
+static uint32_t ln_character_config_change_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+{
+    VAD_UNUSED(strCommonCmd);
+    const char *deviceId = uapi_ln_get_device_id();
+    const char *id = (deviceId && deviceId[0]) ? deviceId : "DEVICE_LOCAL";
+
+    /* 默认值(与示例一致) */
+    const char *characterId = "char_at";
+    const char *characterName = "小艾";
+    const char *eyesShape = "upturned-eyes";
+    const char *hairColor = "blue";
+    const char *eyesColor = "green";
+    const char *hairStyle = "side-parted-comma";
+    const char *screenImage = "https://example.com/new_avatar.bin";
+    int screenImageAsEmoji = 1; // true
+
+    if (para != NULL && paraLen > 0) {
+        uint8_t argv[LN_MAX_ARGC][LN_MAX_ARGV_LEN] = {0};
+        uint32_t argc = 0;
+        (void)ConvertCmdToParam(para, paraLen, argv, &argc);
+        if (argc >= 1 && argv[0][0]) { characterName = (const char*)argv[0]; }
+        if (argc >= 2 && argv[1][0]) { eyesShape = (const char*)argv[1]; }
+        if (argc >= 3 && argv[2][0]) { hairColor = (const char*)argv[2]; }
+        if (argc >= 4 && argv[3][0]) { eyesColor = (const char*)argv[3]; }
+        if (argc >= 5 && argv[4][0]) { hairStyle = (const char*)argv[4]; }
+        if (argc >= 6 && argv[5][0]) { screenImageAsEmoji = atoi((char*)argv[5]) ? 1 : 0; }
+    }
+
+    char *json = (char*)malloc(1024);
+    if (json == NULL) {
+        printf("[LN][CHAR_CFG] OOM\n");
+        return ERRCODE_FAIL;
+    }
+    int n = snprintf(json, 1024,
+        "{\n"
+        "  \"deviceId\": \"%s\",\n"
+        "  \"timestamp\": %u,\n"
+        "  \"messageId\": \"at_msg\",\n"
+        "  \"command\": \"CharacterConfigChange\",\n"
+        "  \"data\": {\n"
+        "    \"characterId\": \"%s\",\n"
+        "    \"characterName\": \"%s\",\n"
+        "    \"characterAvatar\": {\n"
+        "      \"eyesShape\": \"%s\",\n"
+        "      \"hairColor\": \"%s\",\n"
+        "      \"eyesColor\": \"%s\",\n"
+        "      \"hairStyle\": \"%s\",\n"
+        "      \"screenImage\": \"%s\",\n"
+        "      \"screenImageAsEmoji\": %s\n"
+        "    }\n"
+        "  }\n"
+        "}\n",
+        id, 0U, characterId, characterName, eyesShape, hairColor, eyesColor, hairStyle, screenImage,
+        screenImageAsEmoji ? "true" : "false");
+    if (n <= 0 || n >= 1024) {
+        free(json);
+        printf("[LN][CHAR_CFG] build json failed\n");
+        return ERRCODE_FAIL;
+    }
+
+    ln_parse_character_config(json, n);
+    printf("\r\n[LN][CHAR_CFG] ok. name=%s eyesShape=%s hairColor=%s eyesColor=%s\n",
+           characterName, eyesShape, hairColor, eyesColor);
+
+    free(json);
+    return ERRCODE_SUCC;
+}
+
 static const at_cmd_table_t g_ln_api_cmd[] = {
     /* ----------------------------------- VAD ---------------------------------- */
     {(char *)"AT^VAD_INIT", vad_init, (char *)"初始化 VAD"},
@@ -1203,6 +1272,7 @@ static const at_cmd_table_t g_ln_api_cmd[] = {
     {(char *)"AT^LN_UI_OTA_PROGRESS", ln_ui_ota_progress_at, (char *)"推送OTA升级进度: AT^LN_UI_OTA_PROGRESS=100"},
     {(char *)"AT^LN_SCHEDULE_REMINDER", ln_schedule_reminder_at, (char *)"日程提醒: AT^LN_SCHEDULE_REMINDER[=scheduleName,reminderContent]"},
     {(char *)"AT^LN_SCHEDULE_REMINDER_CFG", ln_schedule_reminder_cfg_at, (char *)"日程提醒配置: AT^LN_SCHEDULE_REMINDER_CFG[=scheduleName]"},
+    {(char *)"AT^LN_CHARACTER_CFG", ln_character_config_change_at, (char *)"角色配置更改: AT^LN_CHARACTER_CFG[=name,eyesShape,hairColor,eyesColor,hairStyle,screenImageAsEmoji]"},
 };
 
 
index 0bc3bcf..188f302 100644 (file)
@@ -178,11 +178,6 @@ const char *uapi_ln_ota_get_saved_path(void)
 /* 获取录音音频路径 */
 const char *uapi_ln_get_audio_path(void)
 {
-    // 判断目录是否存在,不存在则创建
-    if (access(LN_DATA_PATH, F_OK) == -1)
-    {
-        uapi_create_folder(LN_DATA_PATH);
-    }
     const char *path = LN_DATA_PATH "/audio.mp3";
     return path;
 }
@@ -204,12 +199,6 @@ void uapi_ln_set_device_bound(int val)
     g_ln_is_device_bound = val;
 }
 
-/* 获取设备角色配置 */
-LnCharacterConfig uapi_ln_get_character_config(void)
-{
-    return g_ln_character_config;
-}
-
 /* 自适应申请HttpClient缓冲(保证header>=2048),失败则逐步扩容重试 */
 HTTPC_RESULT ln_http_prepare_auto(HttpClientData *clientData, int header_size, int resp_size)
 {
@@ -525,6 +514,10 @@ void ln_ws_process_message(const ws_data_msg_t *data_msg, ws_command_t type)
     switch (type) {
         case WS_CMD_CHARACTER_CHANGE: {
             printf("[WS] CharacterChange:角色切换\n");
+            if(json_str)
+            {
+                ln_parse_character_config(json_str, (int)strlen(json_str));
+            }
             break;
         }
         case WS_CMD_CHARACTER_CONFIG_CHANGE: {
@@ -1448,6 +1441,35 @@ void ln_ai_record_stop(void)
     }
 }
 
+/* 预创建运行期所需目录,避免因目录不存在导致文件创建失败 */
+static void ln_init_required_dirs(void)
+{
+    const char *dirs[] = {
+        LN_DATA_PATH,
+        LN_DATA_PATH "/token",  //存放token的
+        LN_DATA_PATH "/speech",
+        LN_DATA_PATH "/story",
+        LN_DATA_PATH "/character",
+        LN_DATA_PATH "/emoji",
+        LN_DATA_PATH "/story/choose",
+        LN_DATA_PATH "/story/random",
+    };
+    int count = (int)(sizeof(dirs) / sizeof(dirs[0]));
+    for (int i = 0; i < count; i++)
+    {
+        if (dirs[i] && dirs[i][0])
+        {
+            uapi_create_folder(dirs[i]);
+        }
+    }
+}
+
+//初始化一些配置信息
+static void ln_load_init_config(void)
+{
+    ln_read_character_config();
+}
+
 // 雷鸟http服务初始化
 errcode_t uapi_ln_http_server_init(void)
 {
@@ -1488,7 +1510,9 @@ errcode_t uapi_ln_http_server_init(void)
     //     /* 不中断HTTP服务,按需可返回失败 */
     // }
 
-    old_Start_AIVoice(LeiniaoDataCallback,NULL);
+    // old_Start_AIVoice(LeiniaoDataCallback,NULL);
+    ln_init_required_dirs();
+    ln_load_init_config();
 
     return ERRCODE_SUCC;
 }
index bb8c58f..c6bc20f 100644 (file)
@@ -52,10 +52,13 @@ typedef struct
 #define LN_EMOJI_REPORT_URL LN_BASE_URL LN_EMOJI_REPORT_PATH
 #define LN_EMOJI_REPORT_ALL_URL LN_BASE_URL LN_EMOJI_REPORT_ALL_PATH
 #define LN_SIGN_SECRET "vDdXVXgPUEUM6_piUjz5AEhMvYTSFzQdPaN-ogKnSus="
-/* OTA 包保存目录*/
-#define LN_OTA_SAVE_DIR "/user/update"
 /* 用户数据保存目录 */
-#define LN_DATA_PATH "/user/leiniao"
+#define LN_DATA_PATH "/user/userdata/leiniao"
+#define LN_TOKEN_FILE LN_DATA_PATH "/token/device_token.json" //token
+#define speech_file_path LN_DATA_PATH "/speech/speech.txt"
+#define story_choose_file_path LN_DATA_PATH "/story/choose.txt"
+#define story_random_file_path LN_DATA_PATH "/story/random.txt"
+#define LN_CHARACTER_CONFIG_FILE LN_DATA_PATH "/character/character_config.json"
 
 #define LN_CLIENT_PREPARE_SIZE 2 * 1024
 #define TEST_MP3_DOWNLOAD 0 // 测试下载mp3
@@ -235,6 +238,9 @@ uint32_t ln_token_is_expired(int *expired_out);
 uint32_t uapi_ln_refresh_device_token(void);
 uint32_t uapi_ln_device_get_user_id(LnDeviceBindStatus *out_status, char *out_user_id, uint32_t out_user_id_len);
 
+/* 从本地读取角色配置 */
+void ln_read_character_config(void);
+
 // 解析设备角色配置
 void ln_parse_character_config(const char *json_buf, int json_len);
 /* 解析哄睡模式开始 */
index c48cd0d..0b71a78 100644 (file)
@@ -1,6 +1,8 @@
 #include "ln_api.h"
 #include <stdio.h>
 #include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
 #include <cJSON.h>
 
 LnCharacterConfig g_ln_character_config = {0};
@@ -24,6 +26,93 @@ static inline void ln_assign_dup(char **dst, const char *src)
     *dst = newp;
 }
 
+/* 从本地读取角色配置 */
+void ln_read_character_config(void)
+{
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] read character config");
+    FILE *file = fopen(LN_CHARACTER_CONFIG_FILE, "rb");
+    if (!file)
+    {
+        printf("[DEVICE_CHARACTER] fopen failed\n");
+        return;
+    }
+    char json_buf[1024] = {0};
+    size_t n = fread(json_buf, 1, sizeof(json_buf) - 1, file);
+    if (n == 0)
+    {
+        printf("[DEVICE_CHARACTER] empty config file\n");
+        fclose(file);
+        return;
+    }
+    json_buf[n] = '\0';
+    ln_parse_character_config(json_buf, (int)n);
+    fclose(file);
+    printf("[DEVICE_CHARACTER] read character config success\n");
+}
+
+/* 保存角色配置到本地 */
+static void ln_save_character_config(const char *json_buf)
+{
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] save character config");
+    if (!json_buf) 
+    {
+        printf("[DEVICE_CHARACTER] json_buf is NULL\n");
+        return;
+    }
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "LN_CHARACTER_CONFIG_FILE:%s",LN_CHARACTER_CONFIG_FILE);
+    FILE *file = fopen(LN_CHARACTER_CONFIG_FILE, "wb+");
+    if (!file)
+    {
+        printf("[DEVICE_CHARACTER] fopen failed\n");
+        return;
+    }
+    fwrite(json_buf, 1, strlen(json_buf), file);
+    fclose(file);
+    printf("[DEVICE_CHARACTER] save character config success\n");
+}
+
+/* 清空角色配置 */
+static void ln_clear_character_config(void)
+{
+    if (g_ln_character_config.character_id)
+    {
+        free(g_ln_character_config.character_id);
+        g_ln_character_config.character_id = NULL;
+    }
+    if (g_ln_character_config.character_name)
+    {
+        free(g_ln_character_config.character_name);
+        g_ln_character_config.character_name = NULL;
+    }
+    if (g_ln_character_config.avatar_hair_color)
+    {
+        free(g_ln_character_config.avatar_hair_color);
+        g_ln_character_config.avatar_hair_color = NULL;
+    }
+    if (g_ln_character_config.avatar_eyes_color)
+    {
+        free(g_ln_character_config.avatar_eyes_color);
+        g_ln_character_config.avatar_eyes_color = NULL;
+    }
+    if (g_ln_character_config.avatar_hair_style)
+    {
+        free(g_ln_character_config.avatar_hair_style);
+        g_ln_character_config.avatar_hair_style = NULL;
+    }
+    if (g_ln_character_config.avatar_eyes_shape)
+    {
+        free(g_ln_character_config.avatar_eyes_shape);
+        g_ln_character_config.avatar_eyes_shape = NULL;
+    }
+    if (g_ln_character_config.avatar_screen_image)
+    {
+        free(g_ln_character_config.avatar_screen_image);
+        g_ln_character_config.avatar_screen_image = NULL;
+    }
+    g_ln_character_config.avatar_screen_image_as_emoji = 0;
+    g_ln_character_config = (LnCharacterConfig){0};
+}
+
 /* 解析设备角色配置 JSON 并保存到全局结构体 */
 void ln_parse_character_config(const char *json_buf, int json_len)
 {
@@ -115,6 +204,11 @@ void ln_parse_character_config(const char *json_buf, int json_len)
         g_ln_character_config.avatar_screen_image_as_emoji = 0;
     }
 
+    //将json体落库
+    ln_save_character_config(text);
+
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DEVICE_CHARACTER] push character config change notification");
+
     /* 打印解析结果 */
     printf("[DEVICE_CHARACTER] parsed:\n");
     printf("  character_id: %s\n", g_ln_character_config.character_id ? g_ln_character_config.character_id : "");
@@ -126,8 +220,18 @@ void ln_parse_character_config(const char *json_buf, int json_len)
     printf("  avatar_screen_image: %s\n", g_ln_character_config.avatar_screen_image ? g_ln_character_config.avatar_screen_image : "");
     printf("  avatar_screen_image_as_emoji: %d\n", g_ln_character_config.avatar_screen_image_as_emoji);
 
+    /* 推送角色配置更改通知 */
+    push_character_config_change_notification(g_ln_character_config.character_id,
+        g_ln_character_config.character_name,
+        g_ln_character_config.avatar_hair_color,
+        g_ln_character_config.avatar_eyes_color,
+        g_ln_character_config.avatar_hair_style,
+        g_ln_character_config.avatar_eyes_shape,
+        g_ln_character_config.avatar_screen_image_as_emoji);
+
     cJSON_Delete(root);
     free(text);
+    ln_clear_character_config();
 }
 
 // 获取设备角色信息
index d752e59..8c03ac2 100644 (file)
@@ -1,8 +1,6 @@
 #include "ln_api.h"
 #include <stdio.h>
 
-#define speech_file_path "/user/leiniao/speech/speech.txt"
-
 /* 读取整个文件到内存,返回缓冲区和长度;需调用者 free */
 static uint8_t *ln_read_file_all_speech_chat(const char *path, int *out_len)
 {
@@ -214,13 +212,6 @@ uint32_t ln_speech_chat_stream_http(const char *device_id,
         return ret;
     }
 
-    // 打开文件准备写入
-    const char *save_path_folder = "/user/leiniao/speech/";
-    // 判断目录是否存在,不存在则创建
-    if (access(save_path_folder, F_OK) == -1)
-    {
-        uapi_create_folder(save_path_folder);
-    }
     FILE *fd = fopen(speech_file_path, "w+");
     if (!fd)
     {
index 9856d94..3efd7c0 100644 (file)
@@ -1,8 +1,6 @@
 #include "ln_api.h"
 #include <stdio.h>
 
-#define story_choose_file_path "/user/leiniao/story/choose.txt"
-
 /* 读取整个文件到内存,返回缓冲区和长度;需调用者 free */
 static uint8_t *ln_read_file_all_story(const char *path, int *out_len)
 {
@@ -212,13 +210,6 @@ uint32_t ln_story_choose_http(const char *device_id,
         return ret;
     }
 
-    // 打开文件准备写入
-    const char *save_path_folder = "/user/leiniao/story/";
-    // 判断目录是否存在,不存在则创建
-    if (access(save_path_folder, F_OK) == -1)
-    {
-        uapi_create_folder(save_path_folder);
-    }
     FILE *fd = fopen(story_choose_file_path, "w+");
     if (!fd)
     {
index ee367ef..c068f32 100644 (file)
@@ -2,8 +2,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-#define story_random_file_path "/user/leiniao/story/random.txt"
-
 static void ln_parse_story_random_file(void)
 {
     printf("Starting to parse story choose file...\n");
@@ -138,13 +136,6 @@ uint32_t ln_story_random_http(const char *device_id,
         return ret;
     }
 
-    // 打开文件准备写入
-    const char *save_path_folder = "/user/leiniao/story/";
-    // 判断目录是否存在,不存在则创建
-    if (access(save_path_folder, F_OK) == -1)
-    {
-        uapi_create_folder(save_path_folder);
-    }
     FILE *fd = fopen(story_random_file_path, "w+");
     if (!fd)
     {
index 382f16a..6bc2d56 100644 (file)
@@ -10,9 +10,6 @@
 #include <sys/stat.h>
 #include <time.h>
 
-#define LN_TOKEN_DIR "/user/leiniao/token"
-#define LN_TOKEN_FILE "/user/leiniao/token/device_token.json"
-
 static void ln_to_hex_lower(const uint8_t *in, size_t in_len, char *out)
 {
     static const char hex[] = "0123456789abcdef";
@@ -47,17 +44,6 @@ static int ln_hmac_sha256_hex(const char *key, const char *msg, char *out_hex, s
     return 0;
 }
 
-/* 创建目录 */
-static void ln_mkdir_p(const char *path)
-{
-    if (!path)
-    {
-        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_mkdir_p: path is NULL");
-        return;
-    }
-    (void)mkdir(path, 0755);
-}
-
 /* 保存设备Token到本地 */
 static uint32_t ln_token_save_json(const char *token, uint64_t expire_timestamp)
 {
@@ -66,8 +52,6 @@ static uint32_t ln_token_save_json(const char *token, uint64_t expire_timestamp)
         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ln_token_save_json: token is NULL");
         return ERRCODE_FAIL;
     }
-    ln_mkdir_p(LN_DATA_PATH);
-    ln_mkdir_p(LN_TOKEN_DIR);
 
     /* 生成create_time字符串 */
     char create_time_str[32] = {0};
index fca5561..928abef 100644 (file)
@@ -79,6 +79,27 @@ void push_task_reminder_notification(const char *scheduleName)
     SendMsgToUIService(HAND_TASK_REMINDER_NOTIFY, 0,(void *)scheduleName, (int32_t)strlen(scheduleName));
 }
 
+// 推送角色配置更改通知(简洁载荷)
+void push_character_config_change_notification(const char* characterId,
+    const char *characterName,
+    const char *eyesShape,
+    const char *hairColor,
+    const char *eyesColor,
+    const char *hairStyle,
+    int screenImageAsEmoji)
+{
+    // 组装紧凑 payload: 使用分隔符拼装,UI 侧再拆分(或改用结构体)
+    char payload[512] = {0};
+    const char* id = characterId? characterId:"";
+    const char *n = characterName ? characterName : "";
+    const char *es = eyesShape ? eyesShape : "";
+    const char *hc = hairColor ? hairColor : "";
+    const char *ec = eyesColor ? eyesColor : "";
+    const char *hs = hairStyle ? hairStyle : "";
+    (void)snprintf(payload, sizeof(payload) - 1, "%s|%s|%s|%s|%s|%s|%d", id,n, es, hc, ec, hs, screenImageAsEmoji ? 1 : 0);
+
+    SendMsgToUIService(HAND_CHARACTER_CONFIG_CHANGE_NOTIFY, 0, (void *)payload, (int32_t)strlen(payload));
+}
 
 #ifdef __cplusplus
 };
index 6dd9763..7962ecb 100644 (file)
@@ -42,8 +42,18 @@ void push_ota_progress(int progress);
 void do_restore_factory_setting(void);
 // 推送日程提醒通知
 void push_schedule_reminder_notification(const char *title, const char *content);
+
 // 推送任务提醒
 void push_task_reminder_notification(const char *scheduleName);
+//推送角色(配置更改)/切换通知
+void push_character_config_change_notification(
+                                              const char* characterId,
+                                              const char *characterName,
+                                              const char *eyesShape,
+                                              const char *hairColor,
+                                              const char *eyesColor,
+                                              const char *hairStyle,
+                                              int screenImageAsEmoji);
 #ifdef __cplusplus
 }
 #endif