1 /*
2 * Copyright (C) 2015-2020 Alibaba Group Holding Limited
3 */
4
5 #include <aos/errno.h>
6 #include <aos/kernel.h>
7 #include <stdarg.h>
8 #include <stddef.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11
12 #include "amp_task.h"
13 #include "aos/init.h"
14 #include "aos/vfs.h"
15 #include "aos_hal_uart.h"
16 #include "board.h"
17 #include "board_mgr.h"
18 #include "cJSON.h"
19 #include "mpstdinport.h"
20 #include "netmgr.h"
21 #include "py/compile.h"
22 #include "py/gc.h"
23 #include "py/mperrno.h"
24 #include "py/mphal.h"
25 #include "py/repl.h"
26 #include "py/runtime.h"
27 #include "py/stackctrl.h"
28 #include "py_defines.h"
29 #include "shared/runtime/pyexec.h"
30 #include "sys/stat.h"
31 #include "ulog/ulog.h"
32
33 #if AOS_COMP_CLI
34 #include "aos/cli.h"
35 #endif
36 #include "haas_main.h"
37 #include "miniunz.h"
38
39 #define LOG_TAG "haas_main"
40
41 static int8_t *stack_top = NULL;
42 static bool isPythonRunning = false;
43
44 #if MICROPY_ENABLE_GC
45 static int8_t *heap = NULL;
46 #ifndef MICROPY_GC_HEAP_SIZE
47 #define MICROPY_GC_HEAP_SIZE (1024 * 64)
48 #endif
49 #endif
50
alloc_mpy_thread_args(int32_t argc,int8_t ** argv)51 static mpy_thread_args *alloc_mpy_thread_args(int32_t argc, int8_t **argv)
52 {
53 mpy_thread_args *temp_args = (mpy_thread_args *)calloc(1, sizeof(mpy_thread_args));
54 if (temp_args == NULL) {
55 LOGE(LOG_TAG, "%s;malloc mpy_thread_args failed\n", __func__);
56 return NULL;
57 }
58
59 if (argc == 1) {
60 temp_args->is_repl_mode = true;
61 } else {
62 temp_args->is_repl_mode = false;
63 }
64 temp_args->argc = argc;
65
66 temp_args->argv = (int8_t **)calloc(1, sizeof(int8_t *) * temp_args->argc);
67 if (temp_args->argv == NULL) {
68 LOGE(LOG_TAG, "%s:temp_args->argv alloc memory failed\n", __func__);
69 free(temp_args);
70 return NULL;
71 }
72
73 for (int32_t i = 0; i < argc; i++) {
74 temp_args->argv[i] = strdup(argv[i]);
75 }
76
77 return temp_args;
78 }
79
free_mpy_thread_args(mpy_thread_args * args)80 static int32_t free_mpy_thread_args(mpy_thread_args *args)
81 {
82 if (args == NULL) {
83 LOGE(LOG_TAG, "args is illegal\n");
84 return -1;
85 }
86
87 for (int32_t i = 0; i < args->argc; i++) {
88 if (args->argv[i] != NULL) {
89 free(args->argv[i]);
90 }
91 }
92
93 free(args->argv);
94 free(args);
95 return 0;
96 }
97
is_file_exist(const int8_t * path)98 static bool is_file_exist(const int8_t *path)
99 {
100 if (path == NULL) {
101 LOGE(LOG_TAG, "File path null\n");
102 return false;
103 }
104
105 FILE *fd = fopen(path, "r");
106 if (fd != NULL) {
107 fclose(fd);
108 return true;
109 }
110
111 return false;
112 }
113
is_mainpy_exist()114 static uint8_t *is_mainpy_exist()
115 {
116 /* check whether main/pyamp/main.py */
117 FILE *fd = fopen(AMP_PY_ENTRY_DEFAULE, "r");
118 if (fd != NULL) {
119 printf(" ==== python execute from %s ====\n", AMP_PY_ENTRY_DEFAULE);
120 fclose(fd);
121 return AMP_PY_ENTRY_DEFAULE;
122 }
123
124 fd = fopen(AMP_PY_ENTRY_BAK, "r");
125 if (fd != NULL) {
126 printf(" ==== python execute from %s ====\n", AMP_PY_ENTRY_BAK);
127 fclose(fd);
128 return AMP_PY_ENTRY_BAK;
129 }
130
131 return NULL;
132 }
133
net_init()134 static void net_init()
135 {
136 #define WIFI_DEV_PATH "/dev/wifi0"
137
138 int8_t *wificonf = MP_FS_ROOT_DIR "/wifi.conf";
139 if (is_file_exist(wificonf) == true) {
140 event_service_init(NULL);
141 netmgr_service_init(NULL);
142 netmgr_set_auto_reconnect(0, true);
143 netmgr_wifi_set_auto_save_ap(true);
144
145 netmgr_add_dev(WIFI_DEV_PATH);
146 netmgr_hdl_t hdl = netmgr_get_dev(WIFI_DEV_PATH);
147 } else {
148 // need init event service because of eth net
149 event_service_init(NULL);
150 }
151 }
152
153 #include "extmod/vfs.h"
154 #include "extmod/vfs_posix.h"
155 // Try to mount the data on "/data" and chdir to it for the boot-up directory.
mount_fs(int8_t * mount_point_str)156 static int32_t mount_fs(int8_t *mount_point_str)
157 {
158 mp_obj_t mount_point = mp_obj_new_str(mount_point_str, strlen(mount_point_str));
159 mp_obj_t bdev = mp_type_vfs_posix.make_new(&mp_type_vfs_posix, 0, 0, NULL);
160 int32_t ret = mp_vfs_mount_and_chdir_protected(bdev, mount_point);
161 if (ret != 0) {
162 printf("mount_fs failed with mount_point: %s\n", mount_point_str);
163 return -MP_ENOENT;
164 }
165 return 0;
166 }
167
py_engine_mainloop(void * p)168 static void py_engine_mainloop(void *p)
169 {
170 mpy_thread_args *args = (mpy_thread_args *)p;
171 if (args == NULL) {
172 LOGE(LOG_TAG, "%s:args is illegal\n", __func__);
173 return;
174 }
175
176 int32_t ret = mpy_init(args);
177 if (ret) {
178 LOGE(LOG_TAG, "%s:mpy_init failed, ret=%d\n", __func__, ret);
179 } else {
180 /* Suspend CLI to make sure REPL use UART exclusively */
181 aos_cli_suspend();
182
183 isPythonRunning = true;
184 mpy_run(args->argc, args->argv);
185
186 /* Resume CLI after REPL */
187 aos_cli_resume();
188 }
189
190 free_mpy_thread_args(args);
191 mpy_deinit();
192 isPythonRunning = false;
193 }
194
py_engine_task(void * p)195 static void py_engine_task(void *p)
196 {
197 py_task_init();
198 while (1) {
199 /* loop for asynchronous operation */
200 if (py_task_yield(200) == 1) {
201 printf("pyengine task yield exit! \r\n");
202 break;
203 }
204 }
205 aos_task_exit(0);
206 }
207
208 #if PY_BUILD_CHANNEL
network_func(void * argv)209 static void network_func(void *argv)
210 {
211 while (!aos_get_network_status()) {
212 aos_msleep(1000);
213 }
214 py_app_management_center_init();
215 aos_task_exit(0);
216 return;
217 }
218 #endif
219
python_entry(int32_t argc,int8_t ** argv)220 static void python_entry(int32_t argc, int8_t **argv)
221 {
222 if (isPythonRunning == true) {
223 printf(" **************************************************** \r\n");
224 printf(" ** Python is running, cannot start another engine ** \r\n");
225 printf(" **************************************************** \r\n");
226
227 return;
228 }
229
230 printf(" Welcome to MicroPython \n");
231
232 aos_task_t engine_entry = NULL;
233 mpy_thread_args *args = alloc_mpy_thread_args(argc, argv);
234
235 int32_t ret =
236 aos_task_new_ext(&engine_entry, "py_engine", py_engine_mainloop, args, 1024 * 20, AOS_DEFAULT_APP_PRI);
237 if (ret != 0) {
238 LOGE(LOG_TAG, "py_engine_mainloop task creat failed!");
239 return;
240 }
241 }
242
get_logLevel()243 static aos_log_level_t get_logLevel()
244 {
245 int32_t ret = -1;
246 int8_t logLevelStr[16] = { 0 };
247 int8_t *data_root_path = AMP_FS_ROOT_DIR "/board.json";
248 int8_t *board_json_path = MP_FS_ROOT_DIR "/python-apps/driver/board.json";
249
250 FILE *json_fd = fopen(data_root_path, "r");
251 if (json_fd != NULL) {
252 fclose(json_fd);
253 board_json_path = data_root_path;
254 }
255
256 int8_t *json_buff = board_get_json_buff(board_json_path);
257 if (NULL == json_buff) {
258 goto debugLevel_kv; /* get debugLevel from kv */
259 }
260
261 cJSON *root = cJSON_Parse(json_buff);
262 if (NULL == root) {
263 goto debugLevel_kv; /* get debugLevel from kv */
264 }
265
266 cJSON *debug = cJSON_GetObjectItem(root, APP_CONFIG_DEBUG);
267 if (debug != NULL) {
268 if (!cJSON_IsString(debug)) {
269 goto debugLevel_kv;
270 } else {
271 strcpy(logLevelStr, debug->valuestring);
272 goto debugLevel_parser;
273 }
274 }
275
276 debugLevel_kv:
277 if (json_buff != NULL) {
278 aos_free(json_buff);
279 }
280
281 int32_t len = sizeof(logLevelStr);
282 ret = aos_kv_get(APP_CONFIG_DEBUG, logLevelStr, &len);
283 if (ret != 0) {
284 uint8_t *set_value = "ERROR";
285 aos_kv_set(APP_CONFIG_DEBUG, set_value, strlen(set_value), 1);
286 strcpy(logLevelStr, set_value);
287 }
288
289 debugLevel_parser:
290 if (strcmp(logLevelStr, "DEBUG") == 0) {
291 return AOS_LL_DEBUG;
292 } else if (strcmp(logLevelStr, "INFO") == 0) {
293 return AOS_LL_INFO;
294 } else if (strcmp(logLevelStr, "WARN") == 0) {
295 return AOS_LL_WARN;
296 } else if (strcmp(logLevelStr, "ERROR") == 0) {
297 return AOS_LL_ERROR;
298 } else if (strcmp(logLevelStr, "FATAL") == 0) {
299 return AOS_LL_FATAL;
300 } else if (strcmp(logLevelStr, "NONE") == 0) {
301 return AOS_LL_NONE;
302 }
303 return AOS_LL_ERROR;
304 }
305
haas_main(int32_t argc,int8_t ** argv)306 void haas_main(int32_t argc, int8_t **argv)
307 {
308 int32_t ret = -1;
309
310 #if PY_BUILD_BOOT
311 aos_cli_suspend();
312 pyamp_boot_main();
313 aos_cli_resume();
314 #endif
315
316 #if PY_BUILD_KV
317 /* kv module init */
318 ret = kv_init();
319 if (ret != 0) {
320 LOGE(LOG_TAG, "kv init failed!");
321 return;
322 }
323 #endif
324
325 /* ulog module init */
326 ulog_init();
327 aos_log_level_t log_level = get_logLevel();
328 aos_set_log_level(log_level);
329
330 /* net init */
331 net_init();
332
333 aos_task_t engine_task;
334 ret = aos_task_new_ext(&engine_task, "py_engine_task", py_engine_task, NULL, 1024 * 8, AOS_DEFAULT_APP_PRI);
335 if (ret != 0) {
336 LOGE(LOG_TAG, "pyengine task creat failed!");
337 return;
338 }
339
340 #if PY_BUILD_CHANNEL
341 aos_task_t network_task;
342 /* network start */
343 ret = aos_task_new_ext(&network_task, "mpy_network", network_func, NULL, 1024 * 4, AOS_DEFAULT_APP_PRI);
344 if (ret != 0) {
345 LOGE(LOG_TAG, "network task creat failed!");
346 return ret;
347 }
348 #endif
349
350 /* Check whether we have main.py to execute */
351 uint8_t *path = is_mainpy_exist();
352 if (path != NULL) {
353 uint8_t *argv[2] = { "python", path };
354 python_entry(2, &argv);
355 }
356 }
357
handle_unzip_cmd(int32_t argc,int8_t ** argv)358 static void handle_unzip_cmd(int32_t argc, int8_t **argv)
359 {
360 int8_t *zippath;
361 int8_t *destpath;
362 int32_t ret = 0;
363 if (argc < 2) {
364 LOGE(LOG_TAG, "Error params,Usage: unzip /data/src.zip /sdcard \r\n");
365 return;
366 }
367
368 if (argc == 2) {
369 destpath = "/data";
370 } else {
371 destpath = argv[2];
372 }
373 zippath = argv[1];
374 ret = miniUnzip(zippath, destpath);
375 if (ret) {
376 LOGE(LOG_TAG, "unzip failed ,errno is %d \r\n", ret);
377 } else {
378 LOGD(LOG_TAG, "unzip succeed \r\n");
379 }
380 }
381
382 #if MICROPY_ENABLE_COMPILER
do_str(const int8_t * src,mp_parse_input_kind_t input_kind)383 void do_str(const int8_t *src, mp_parse_input_kind_t input_kind)
384 {
385 nlr_buf_t nlr;
386 if (nlr_push(&nlr) == 0) {
387 mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0);
388 qstr source_name = lex->source_name;
389 mp_parse_tree_t parse_tree = mp_parse(lex, input_kind);
390 mp_obj_t module_fun = mp_compile(&parse_tree, source_name, true);
391 mp_call_function_0(module_fun);
392 nlr_pop();
393 } else {
394 // uncaught exception
395 mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val);
396 }
397 }
398 #endif
399
mpy_init(mpy_thread_args * args)400 int32_t mpy_init(mpy_thread_args *args)
401 {
402 int32_t ret = -1;
403 uint32_t stack_size = 1024;
404
405 mp_stdin_init(MP_REPL_UART_PORT, MP_REPL_UART_BAUDRATE);
406
407 #if MICROPY_PY_THREAD
408 void *stack_addr = mp_sal_get_stack_addr();
409 stack_size = mp_sal_get_stack_size();
410 mp_thread_init(stack_addr, stack_size);
411 #endif
412
413 mp_stack_set_top(stack_top);
414
415 // adjust the stack_size to provide room to recover from hitting the
416 // limit
417 #if MICROPY_STACK_CHECK
418 mp_stack_ctrl_init();
419 mp_stack_set_limit(stack_size * sizeof(cpu_stack_t) - 1024);
420 #endif
421
422 #if MICROPY_ENABLE_GC
423 heap = (int8_t *)malloc(MICROPY_GC_HEAP_SIZE);
424 if (NULL == heap) {
425 LOGE(LOG_TAG, "mpy_init: heap alloc fail!\r\n");
426 return -1;
427 }
428 gc_init(heap, heap + MICROPY_GC_HEAP_SIZE);
429 #endif
430
431 mp_init();
432
433 #if MICROPY_VFS_POSIX
434 {
435 // Mount the host FS at the root of our internal VFS
436 ret = mount_fs("/");
437 if (ret != 0) {
438 printf(" !!!!!!!! %s, %d, faild to mount fs !!!!!!!!\n", __func__, __LINE__);
439 }
440 MP_STATE_VM(vfs_cur) = MP_STATE_VM(vfs_mount_table);
441 }
442 #endif
443
444 /*set default mp_sys_path*/
445 mp_obj_list_init(mp_sys_path, 0);
446 mp_obj_list_append(mp_sys_path,
447 MP_OBJ_NEW_QSTR(MP_QSTR_)); // current dir (or base dir of the script)
448
449 int8_t *path = MP_FS_ROOT_DIR "/pyamp";
450 mp_obj_list_append(mp_sys_path, mp_obj_new_str_via_qstr(path, strlen(path)));
451
452 path = MP_FS_ROOT_DIR "/lib/micropython";
453 mp_obj_list_append(mp_sys_path, mp_obj_new_str_via_qstr(path, strlen(path)));
454
455 path = MP_FS_ROOT_DIR "/lib/micropython/uasyncio";
456 mp_obj_list_append(mp_sys_path, mp_obj_new_str_via_qstr(path, strlen(path)));
457
458 path = MP_FS_EXT_ROOT_DIR "/lib/micropython";
459 mp_obj_list_append(mp_sys_path, mp_obj_new_str_via_qstr(path, strlen(path)));
460
461 mp_obj_list_init(mp_sys_argv, 0);
462
463 return 0;
464 }
465
mpy_deinit(void)466 int32_t mpy_deinit(void)
467 {
468 // need relese python run mem
469 #if MICROPY_ENABLE_GC
470 if (NULL != heap) {
471 free(heap);
472 heap == NULL;
473 }
474 #endif
475
476 mp_stdin_deinit();
477 }
478
set_sys_argv(int8_t * argv[],int32_t argc,int32_t start_arg)479 static void set_sys_argv(int8_t *argv[], int32_t argc, int32_t start_arg)
480 {
481 for (int32_t i = start_arg; i < argc; i++) {
482 mp_obj_list_append(mp_sys_argv, MP_OBJ_NEW_QSTR(qstr_from_str(argv[i])));
483 }
484 }
485
is_download_mode(uint32_t wait_time_ms)486 int32_t is_download_mode(uint32_t wait_time_ms)
487 {
488 int32_t ret = 0;
489 int32_t c = 0;
490 int64_t now_time = 0;
491 int64_t begin_time = aos_now_ms();
492
493 do {
494 int32_t c = ringbuf_get(&stdin_ringbuf);
495 if (c == '\x03') {
496 ret = 1;
497 break;
498 }
499 now_time = aos_now_ms();
500 uint32_t time = now_time - begin_time;
501 if (time >= wait_time_ms) {
502 ret = 0;
503 break;
504 }
505 pyamp_boot_delay(1);
506 } while (1);
507
508 return ret;
509 }
510
mpy_run(int32_t argc,int8_t * argv[])511 int32_t mpy_run(int32_t argc, int8_t *argv[])
512 {
513 int32_t mode = 0;
514 #if 0
515 mode = is_download_mode(300); /* Only available on HaaS506 */
516 #endif
517
518 if (argc >= 2 && mode == 0) {
519 int8_t *filename = argv[1];
520 int8_t filepath[256] = { 0 };
521
522 if (filename[0] != '/') {
523 getcwd(filepath, sizeof(filepath));
524 strcat(filepath, "/");
525 strcat(filepath, filename);
526 } else {
527 strcpy(filepath, filename);
528 }
529
530 int8_t *p = strrchr(filepath, '/');
531 size_t len = 0;
532 mp_obj_t *path_items = NULL;
533 mp_obj_list_get(mp_sys_path, &len, &path_items);
534
535 if (p >= filepath) {
536 path_items[0] = mp_obj_new_str_via_qstr(filepath, p - filepath);
537 } else {
538 path_items[0] = mp_obj_new_str_via_qstr(filepath, filepath - p);
539 }
540
541 set_sys_argv(argv, argc, 1);
542
543 if (filepath != NULL) {
544 pyexec_file(filepath);
545 }
546 }
547 {
548 for (;;) {
549 if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
550 if (pyexec_raw_repl() != 0) {
551 break;
552 }
553 } else {
554 if (pyexec_friendly_repl() != 0) {
555 break;
556 }
557 }
558 }
559 }
560 mp_deinit();
561 return 0;
562 }
563
mp_lexer_new_from_file(const char * filename)564 MP_WEAK mp_lexer_t *mp_lexer_new_from_file(const char *filename)
565 {
566 mp_raise_OSError(MP_ENOENT);
567 }
568
nlr_jump_fail(void * val)569 void nlr_jump_fail(void *val)
570 {
571 while (1)
572 ;
573 }
574
__fatal_error(const int8_t * msg)575 void NORETURN __fatal_error(const int8_t *msg)
576 {
577 while (1)
578 ;
579 }
580
581 #ifndef NDEBUG
__assert_func(const char * file,int line,const char * func,const char * expr)582 void MP_WEAK __assert_func(const char *file, int line, const char *func, const char *expr)
583 {
584 printf("assert:%s:%d:%s: %s\n", file, line, func, expr);
585 mp_raise_msg(&mp_type_AssertionError, MP_ERROR_TEXT("C-level assert"));
586 }
587 #endif
588
589 #ifdef AOS_COMP_CLI
590 #if BOARD_HAAS700
591 #include <console.h>
592 #endif
593 /* reg args: fun, cmd, description*/
594 ALIOS_CLI_CMD_REGISTER(python_entry, python, start micropython)
595 ALIOS_CLI_CMD_REGISTER(handle_unzip_cmd, unzip, start unzip)
596 #endif
597