From 84beb9b23e3cd1b9eb42a50038cefc2941b2c4db Mon Sep 17 00:00:00 2001 From: Nikolay Minaylov Date: Mon, 25 Mar 2024 19:35:38 +0300 Subject: [PATCH] JS CLI command (#3539) * js command * made the js command exit when there's an error * JS CLI: moved to js_app * JS: abortable cli invocations * JS: less debug logging in console logs, fix storage descriptor leak in cli Co-authored-by: Milk-Cool Co-authored-by: hedger Co-authored-by: Aleksandr Kutuzov --- applications/system/application.fam | 1 + applications/system/js_app/application.fam | 7 ++ applications/system/js_app/js_app.c | 85 +++++++++++++++++++++- applications/system/js_app/js_modules.c | 5 +- applications/system/js_app/js_thread.c | 4 +- 5 files changed, 98 insertions(+), 4 deletions(-) diff --git a/applications/system/application.fam b/applications/system/application.fam index 2d4dfcbb8..095ca1ab2 100644 --- a/applications/system/application.fam +++ b/applications/system/application.fam @@ -6,6 +6,7 @@ App( "updater_app", "storage_move_to_sd", "js_app", + "js_app_start", # "archive", ], ) diff --git a/applications/system/js_app/application.fam b/applications/system/js_app/application.fam index 5716234dc..114bec55f 100644 --- a/applications/system/js_app/application.fam +++ b/applications/system/js_app/application.fam @@ -8,6 +8,13 @@ App( order=0, ) +App( + appid="js_app_start", + apptype=FlipperAppType.STARTUP, + entry_point="js_app_on_system_start", + order=160, +) + App( appid="js_dialog", apptype=FlipperAppType.PLUGIN, diff --git a/applications/system/js_app/js_app.c b/applications/system/js_app/js_app.c index e99cc3249..b38903312 100644 --- a/applications/system/js_app/js_app.c +++ b/applications/system/js_app/js_app.c @@ -4,6 +4,7 @@ #include "js_app_i.h" #include #include +#include #define TAG "JS app" @@ -128,4 +129,86 @@ int32_t js_app(void* arg) { js_app_free(app); return 0; -} //-V773 \ No newline at end of file +} //-V773 + +typedef struct { + Cli* cli; + FuriSemaphore* exit_sem; +} JsCliContext; + +static void js_cli_print(JsCliContext* ctx, const char* msg) { + cli_write(ctx->cli, (uint8_t*)msg, strlen(msg)); +} + +static void js_cli_exit(JsCliContext* ctx) { + furi_check(furi_semaphore_release(ctx->exit_sem) == FuriStatusOk); +} + +static void js_cli_callback(JsThreadEvent event, const char* msg, void* context) { + JsCliContext* ctx = context; + switch(event) { + case JsThreadEventError: + js_cli_print(ctx, "---- ERROR ----\r\n"); + js_cli_print(ctx, msg); + js_cli_print(ctx, "\r\n"); + break; + case JsThreadEventErrorTrace: + js_cli_print(ctx, "Trace:\r\n"); + js_cli_print(ctx, msg); + js_cli_print(ctx, "\r\n"); + + js_cli_exit(ctx); // Exit when an error occurs + break; + case JsThreadEventPrint: + js_cli_print(ctx, msg); + js_cli_print(ctx, "\r\n"); + break; + case JsThreadEventDone: + js_cli_print(ctx, "Script done!\r\n"); + + js_cli_exit(ctx); + break; + } +} + +void js_cli_execute(Cli* cli, FuriString* args, void* context) { + UNUSED(context); + + const char* path = furi_string_get_cstr(args); + Storage* storage = furi_record_open(RECORD_STORAGE); + + do { + if(furi_string_size(args) == 0) { + printf("Usage:\r\njs \r\n"); + break; + } + + if(!storage_file_exists(storage, path)) { + printf("Can not open file %s\r\n", path); + break; + } + + JsCliContext ctx = {.cli = cli}; + ctx.exit_sem = furi_semaphore_alloc(1, 0); + + printf("Running script %s, press CTRL+C to stop\r\n", path); + JsThread* js_thread = js_thread_run(path, js_cli_callback, &ctx); + + while(furi_semaphore_acquire(ctx.exit_sem, 100) != FuriStatusOk) { + if(cli_cmd_interrupt_received(cli)) break; + } + + js_thread_stop(js_thread); + furi_semaphore_free(ctx.exit_sem); + } while(false); + + furi_record_close(RECORD_STORAGE); +} + +void js_app_on_system_start(void) { +#ifdef SRV_CLI + Cli* cli = furi_record_open(RECORD_CLI); + cli_add_command(cli, "js", CliCommandFlagDefault, js_cli_execute, NULL); + furi_record_close(RECORD_CLI); +#endif +} diff --git a/applications/system/js_app/js_modules.c b/applications/system/js_app/js_modules.c index 0e2d6bf25..fa5332884 100644 --- a/applications/system/js_app/js_modules.c +++ b/applications/system/js_app/js_modules.c @@ -5,6 +5,9 @@ #define TAG "JS modules" +// Absolute path is used to make possible plugin load from CLI +#define MODULES_PATH "/ext/apps_data/js_app/plugins" + typedef struct { JsModeConstructor create; JsModeDestructor destroy; @@ -81,7 +84,7 @@ mjs_val_t js_module_require(JsModules* modules, const char* name, size_t name_le // External module load if(!module_found) { FuriString* module_path = furi_string_alloc(); - furi_string_printf(module_path, "%s/js_%s.fal", APP_DATA_PATH("plugins"), name); + furi_string_printf(module_path, "%s/js_%s.fal", MODULES_PATH, name); FURI_LOG_I(TAG, "Loading external module %s", furi_string_get_cstr(module_path)); do { uint32_t plugin_cnt_last = plugin_manager_get_count(modules->plugin_manager); diff --git a/applications/system/js_app/js_thread.c b/applications/system/js_app/js_thread.c index 5ca365404..759d63b0e 100644 --- a/applications/system/js_app/js_thread.c +++ b/applications/system/js_app/js_thread.c @@ -44,12 +44,12 @@ static void js_print(struct mjs* mjs) { FuriString* msg_str = furi_string_alloc(); js_str_print(msg_str, mjs); - printf("%s\r\n", furi_string_get_cstr(msg_str)); - JsThread* worker = mjs_get_context(mjs); furi_assert(worker); if(worker->app_callback) { worker->app_callback(JsThreadEventPrint, furi_string_get_cstr(msg_str), worker->context); + } else { + FURI_LOG_D(TAG, "%s\r\n", furi_string_get_cstr(msg_str)); } furi_string_free(msg_str);