开发TinyEngine本地扩展模块的技术要领 - Shaofa/AliOS-Things-Certification-Manual GitHub Wiki
前面介绍了如何使用C/C++快速开发一个TinyEngine的本地扩展模块,那么在开发过程中要注意哪些东西以及有哪些技术要领?本文将详细阐述。
回调和事件处理程序本质上并无区别,只是在不同情况下,不同的叫法 回调函数:程序(JS)费时的操作或函数需要回调通知 (异步通知) 事件处理:程序(JS)中的任务处理一般需要使用事件通知到JS任务
- Javascript Addon 也叫Native module Javascript 的本地扩展模块,函数或对象,一般是用C语言实现的模块,在JS 层可以调用该模块,函数,或对象;
说明:具体实现可以参考 module_wdg.c、module_timers.c 等实现。
耗时操作应该在一个new task中运行,通过aos_schedule_call把结果处理函数nextTick_cb(C调用javascript函数)放到JavaScript主任务上下文去执行。
if(arg0 && symbol_is_function(arg0)){
BE_ASYNC_S* async = (BE_ASYNC_S*)aos_malloc(sizeof(BE_ASYNC_S));
async->func = arg0;
async->param_count = 2;
async->params = (be_jse_symbol_t**) aos_malloc( sizeof(be_jse_symbol_t*) * async->param_count);
async->params[0] = new_int_symbol(count++);
async->params[1] = new_int_symbol(count++);
LOGW("WDG"," async = %p ", async);
LOGW("WDG"," async->func = %p ", async->func);
LOGW("WDG"," async->param_count = %d", async->param_count);
LOGW("WDG"," async->params = %p ", async->params);
ret = aos_schedule_call(nextTick_cb, async);
if( ret >= 0 ) {
// success
INC_SYMBL_REF(async->func);
}else {
// 出错
LOGW("WDG", "fatal error");
}
}
- 一般来说,本地扩展对象和函数是让上层 JS 应用调用 C 的函数,但是有时需要JSE 能够提供 C 反向调用JS函数,如事件通知,回调函数; C 调用 Javascript 函数,一般采用 be_jse_execute_func 函数,be_jse_execute_func 函数形式如下:
bool be_jse_execute_func(be_jse_executor_ctx_t *executor, be_jse_symbol_t *func, int argCount, be_jse_symbol_t **argPtr);
其中: 在C层调用JS函数时: func 等于NULL时, argPtr[]会自动 symbol_unlock func执行结束之后,argPtr[]会自动 symbol_unlock 若JS层函数参数数目与argCount不匹配时,argPtr[]会自动 symbol_unlock
示例如下:
static void nextTick_cb(void *arg)
{
BE_ASYNC_S* async = (BE_ASYNC_S*)arg;
int i;
be_jse_execute_func(bone_engine_get_executor(), async->func, async->param_count, async->params);
DEC_SYMBL_REF(async->func);
if(async->params)
aos_free(async->params);
aos_free(async);
}
注意事件:
1.事件通知:
如何需要多次事件通知, func不能释放,可以参考module_timers.c
2.BE_ASYNC_S* async
使用完之后需要释放
示例:
int i;
be_jse_symbol_t *arr = new_symbol(BE_SYM_ARRAY);
for (i=0; i<5; i++) {
be_jse_symbol_t *val = new_str_symbol("abcd");
be_jse_symbol_t *idx = new_named_symbol(new_int_symbol(i), val);
symbol_unlock(val);
add_symbol_node(arr, idx);
symbol_unlock(idx);
}
示例:
be_jse_symbol_t *obj = new_symbol(BE_SYM_OBJECT);
be_jse_symbol_t *name = new_str_symbol("IoT");
be_jse_symbol_t *val = add_symbol_node_name(obj, name, "name");
symbol_unlock(name);
symbol_unlock(val);
-
根据JSON字符串构造对象或数组
be_jse_symbol_t *new_json_symbol(char* json_str, size_t json_str_len)
建议使用AOS系统中的LOG输出系统
定义在.c文件的第一个.h文件之前
#define CONFIG_LOGMACRO_DETAILS
使用
LOGD LOGW LOGE
例如程序出现crash的panic信息如下:
kernel panic,err 1200!
assertion "0" failed: file "platform/mcu/esp32/aos/soc_impl.c", line 35, function: soc_err_proc
abort() was called at PC 0x4013826b on core 0
Backtrace: 0x40088af0:0x3fffe4a0 0x40088bef:0x3fffe4c0 0x4013826b:0x3fffe4e0 0x4008b2c1:0x3fffe510 0x400859bd:0x3fffe530 0x40084706:0x3fffe550 0x4008b985:0x3fffe570 0x4008b962:0x3fffe590 0x400862cc:0x3fffe5b0 0x4008675f:0x3fffe5d0 0x40086bc8:0x3fffe5f0 0x400d3bee:0x3fffe610 0x400d3e82:0x3fffe640 0x400d26fb:0x3fffe660 0x40089f4e:0x3fffe6b0 0x401152a9:0x3fffe6e0 0x4011deec:0x3fffe700
解决方法:找到对应固件的elf文件,使用gdb调试
1. gdb加载对应的elf文件
./xtensa-esp32-elf-gdb.exe ./[email protected]
2. 查看Backtrace中地址对应的函数
(gdb) info symbol 0x4008675f
krhino_sem_take + 135 in section .iram0.text
(gdb) info symbol 0x40086bc8
espos_sem_take + 12 in section .iram0.text
�