From 82c1e7143102c51c28290af84c71f006639650e2 Mon Sep 17 00:00:00 2001 From: Anna Antonenko Date: Wed, 25 Dec 2024 06:34:32 +0400 Subject: [PATCH] feat: commands in fals --- applications/main/application.fam | 4 +- applications/main/onewire/onewire_cli.c | 10 +- applications/services/bt/application.fam | 2 +- applications/services/cli/application.fam | 18 +- applications/services/cli/cli.c | 51 +++++- applications/services/cli/cli.h | 18 +- applications/services/cli/cli_commands.c | 16 +- applications/services/cli/cli_commands.h | 31 +++- applications/services/cli/cli_i.h | 5 +- applications/services/cli/cli_shell.c | 100 +++++++---- applications/services/cli/cli_shell.h | 3 +- .../services/cli/commands/hello_world.c | 10 ++ applications/services/cli/commands/neofetch.c | 159 ++++++++++++++++++ applications/services/power/application.fam | 2 +- applications/services/rpc/application.fam | 2 +- 15 files changed, 379 insertions(+), 52 deletions(-) create mode 100644 applications/services/cli/commands/hello_world.c create mode 100644 applications/services/cli/commands/neofetch.c diff --git a/applications/main/application.fam b/applications/main/application.fam index c488ae7d46b..4d316233731 100644 --- a/applications/main/application.fam +++ b/applications/main/application.fam @@ -21,9 +21,9 @@ App( name="On start hooks", apptype=FlipperAppType.METAPACKAGE, provides=[ - "cli_start", + "cli", "ibutton_start", - # "onewire_start", + "onewire_start", "subghz_start", "infrared_start", "lfrfid_start", diff --git a/applications/main/onewire/onewire_cli.c b/applications/main/onewire/onewire_cli.c index af3d4e8035c..e61b8c3064d 100644 --- a/applications/main/onewire/onewire_cli.c +++ b/applications/main/onewire/onewire_cli.c @@ -6,7 +6,7 @@ #include -static void onewire_cli(Cli* cli, FuriString* args, void* context); +static void onewire_cli(PipeSide* pipe, FuriString* args, void* context); void onewire_on_system_start(void) { #ifdef SRV_CLI @@ -23,8 +23,8 @@ static void onewire_cli_print_usage(void) { printf("onewire search\r\n"); } -static void onewire_cli_search(Cli* cli) { - UNUSED(cli); +static void onewire_cli_search(PipeSide* pipe) { + UNUSED(pipe); OneWireHost* onewire = onewire_host_alloc(&gpio_ibutton); uint8_t address[8]; bool done = false; @@ -53,7 +53,7 @@ static void onewire_cli_search(Cli* cli) { onewire_host_free(onewire); } -void onewire_cli(Cli* cli, FuriString* args, void* context) { +void onewire_cli(PipeSide* pipe, FuriString* args, void* context) { UNUSED(context); FuriString* cmd; cmd = furi_string_alloc(); @@ -65,7 +65,7 @@ void onewire_cli(Cli* cli, FuriString* args, void* context) { } if(furi_string_cmp_str(cmd, "search") == 0) { - onewire_cli_search(cli); + onewire_cli_search(pipe); } furi_string_free(cmd); diff --git a/applications/services/bt/application.fam b/applications/services/bt/application.fam index 60627756fc2..2d2840e3a5d 100644 --- a/applications/services/bt/application.fam +++ b/applications/services/bt/application.fam @@ -5,7 +5,7 @@ App( entry_point="bt_srv", cdefines=["SRV_BT"], requires=[ - "cli_start", + "cli", "dialogs", ], provides=[ diff --git a/applications/services/cli/application.fam b/applications/services/cli/application.fam index d0c20ad4ac9..578f0410dce 100644 --- a/applications/services/cli/application.fam +++ b/applications/services/cli/application.fam @@ -1,5 +1,5 @@ App( - appid="cli_start", + appid="cli", apptype=FlipperAppType.STARTUP, entry_point="cli_on_system_start", cdefines=["SRV_CLI"], @@ -23,3 +23,19 @@ App( sdk_headers=["cli_vcp.h"], sources=["cli_vcp.c"], ) + +App( + appid="cli_hello_world", + apptype=FlipperAppType.PLUGIN, + entry_point="cli_hello_world_ep", + requires=["cli"], + sources=["commands/hello_world.c"], +) + +App( + appid="cli_neofetch", + apptype=FlipperAppType.PLUGIN, + entry_point="cli_neofetch_ep", + requires=["cli"], + sources=["commands/neofetch.c"], +) diff --git a/applications/services/cli/cli.c b/applications/services/cli/cli.c index a344eb2ea9f..3999ef80142 100644 --- a/applications/services/cli/cli.c +++ b/applications/services/cli/cli.c @@ -4,6 +4,8 @@ #include "cli_ansi.h" #include +#define TAG "cli" + struct Cli { CliCommandTree_t commands; FuriMutex* mutex; @@ -32,10 +34,10 @@ void cli_add_command( furi_check(furi_string_search_char(name_str, ' ') == FURI_STRING_FAILURE); CliCommand command = { - .name = name, .context = context, .execute_callback = callback, .flags = flags, + .stack_depth = CLI_BUILTIN_COMMAND_STACK_SIZE, }; furi_check(furi_mutex_acquire(cli->mutex, FuriWaitForever) == FuriStatusOk); @@ -74,6 +76,53 @@ bool cli_get_command(Cli* cli, FuriString* command, CliCommand* result) { return !!data; } +void cli_enumerate_external_commands(Cli* cli) { + furi_check(cli); + furi_check(furi_mutex_acquire(cli->mutex, FuriWaitForever) == FuriStatusOk); + FURI_LOG_D(TAG, "Enumerating external commands"); + + // remove external commands + CliCommandTree_t internal_cmds; + CliCommandTree_init(internal_cmds); + for + M_EACH(item, cli->commands, CliCommandTree_t) { + if(!(item->value_ptr->flags & CliCommandFlagExternal)) + CliCommandTree_set_at(internal_cmds, *item->key_ptr, *item->value_ptr); + } + CliCommandTree_move(cli->commands, internal_cmds); + + // iterate over files in plugin directory + Storage* storage = furi_record_open(RECORD_STORAGE); + File* plogin_dir = storage_file_alloc(storage); + + if(storage_dir_open(plogin_dir, CLI_COMMANDS_PATH)) { + char plugin_filename[64]; + FuriString* plugin_name = furi_string_alloc(); + + while(storage_dir_read(plogin_dir, NULL, plugin_filename, sizeof(plugin_filename))) { + FURI_LOG_T(TAG, "Plugin: %s", plugin_filename); + furi_string_set_str(plugin_name, plugin_filename); + furi_string_replace_all_str(plugin_name, ".fal", ""); + furi_string_replace_at(plugin_name, 0, 4, ""); // remove "cli_" in the beginning + CliCommand command = { + .context = NULL, + .execute_callback = NULL, + .flags = CliCommandFlagExternal, + }; + CliCommandTree_set_at(cli->commands, plugin_name, command); + } + + furi_string_free(plugin_name); + } + + storage_dir_close(plogin_dir); + storage_file_free(plogin_dir); + furi_record_close(RECORD_STORAGE); + + FURI_LOG_D(TAG, "Finished enumerating external commands"); + furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk); +} + void cli_lock_commands(Cli* cli) { furi_assert(cli); furi_check(furi_mutex_acquire(cli->mutex, FuriWaitForever) == FuriStatusOk); diff --git a/applications/services/cli/cli.h b/applications/services/cli/cli.h index 166194181f5..61524104b0e 100644 --- a/applications/services/cli/cli.h +++ b/applications/services/cli/cli.h @@ -20,14 +20,18 @@ typedef enum { CliCommandFlagParallelUnsafe = (1 << 0), /**< Unsafe to run in parallel with other apps */ CliCommandFlagInsomniaSafe = (1 << 1), /**< Safe to run with insomnia mode on */ CliCommandFlagDontAttachStdio = (1 << 2), /**< Do no attach I/O pipe to thread stdio */ + + // internal flags (do not set them yourselves!) + + CliCommandFlagExternal = (1 << 3), /**< The command comes from a .fal file */ } CliCommandFlag; /** Cli type anonymous structure */ typedef struct Cli Cli; /** - * @brief CLI execution callbackpointer. Implement this interface and use - * `add_cli_command` or `cli_add_command_ex`. + * @brief CLI execution callback pointer. Implement this interface and use + * `add_cli_command`. * * This callback will be called from a separate thread spawned just for your * command. The pipe will be installed as the thread's stdio, so you can use @@ -44,8 +48,7 @@ typedef struct Cli Cli; typedef void (*CliExecuteCallback)(PipeSide* pipe, FuriString* args, void* context); /** - * @brief Registers a command with the CLI. Provides less options than - * `cli_add_command_ex` + * @brief Registers a command with the CLI. * * @param [in] cli Pointer to CLI instance * @param [in] name Command name @@ -68,6 +71,13 @@ void cli_add_command( */ void cli_delete_command(Cli* cli, const char* name); +/** + * @brief Reloads the list of externally available commands + * + * @param [in] cli pointer to cli instance + */ +void cli_enumerate_external_commands(Cli* cli); + /** * @brief Detects if Ctrl+C has been pressed or session has been terminated * diff --git a/applications/services/cli/cli_commands.c b/applications/services/cli/cli_commands.c index d595a16a4f0..7aee94e2f0d 100644 --- a/applications/services/cli/cli_commands.c +++ b/applications/services/cli/cli_commands.c @@ -60,7 +60,7 @@ void cli_command_help(PipeSide* pipe, FuriString* args, void* context) { UNUSED(pipe); UNUSED(args); UNUSED(context); - printf("Built-in shell commands:" ANSI_FG_GREEN); + printf("Available commands:" ANSI_FG_GREEN); // count non-hidden commands Cli* cli = furi_record_open(RECORD_CLI); @@ -91,6 +91,8 @@ void cli_command_help(PipeSide* pipe, FuriString* args, void* context) { } } + printf(ANSI_RESET + "\r\nIf you just added a new command and can't see it above, run `reload_ext_cmds`"); printf(ANSI_RESET "\r\nFind out more: https://docs.flipper.net/development/cli"); cli_unlock_commands(cli); @@ -512,10 +514,22 @@ void cli_command_i2c(PipeSide* pipe, FuriString* args, void* context) { furi_hal_i2c_release(&furi_hal_i2c_handle_external); } +void cli_command_reload_external(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); + UNUSED(args); + UNUSED(context); + Cli* cli = furi_record_open(RECORD_CLI); + cli_enumerate_external_commands(cli); + furi_record_close(RECORD_CLI); + printf("OK!"); +} + void cli_commands_init(Cli* cli) { cli_add_command(cli, "!", CliCommandFlagDefault, cli_command_info, (void*)true); cli_add_command(cli, "info", CliCommandFlagDefault, cli_command_info, NULL); cli_add_command(cli, "device_info", CliCommandFlagDefault, cli_command_info, (void*)true); + cli_add_command( + cli, "reload_ext_cmds", CliCommandFlagDefault, cli_command_reload_external, NULL); cli_add_command(cli, "?", CliCommandFlagDefault, cli_command_help, NULL); cli_add_command(cli, "help", CliCommandFlagDefault, cli_command_help, NULL); diff --git a/applications/services/cli/cli_commands.h b/applications/services/cli/cli_commands.h index 184eeb3739e..77d9930aff2 100644 --- a/applications/services/cli/cli_commands.h +++ b/applications/services/cli/cli_commands.h @@ -1,5 +1,34 @@ #pragma once -#include "cli_i.h" +#include "cli.h" +#include void cli_commands_init(Cli* cli); + +#define PLUGIN_APP_ID "cli" +#define PLUGIN_API_VERSION 1 + +typedef struct { + char* name; + CliExecuteCallback execute_callback; + CliCommandFlag flags; + size_t stack_depth; +} CliCommandDescriptor; + +#define CLI_COMMAND_INTERFACE(name, execute_callback, flags, stack_depth) \ + static const CliCommandDescriptor cli_##name##_desc = { \ + #name, \ + &execute_callback, \ + flags, \ + stack_depth, \ + }; \ + \ + static const FlipperAppPluginDescriptor plugin_descriptor = { \ + .appid = PLUGIN_APP_ID, \ + .ep_api_version = PLUGIN_API_VERSION, \ + .entry_point = &cli_##name##_desc, \ + }; \ + \ + const FlipperAppPluginDescriptor* cli_##name##_ep(void) { \ + return &plugin_descriptor; \ + } diff --git a/applications/services/cli/cli_i.h b/applications/services/cli/cli_i.h index fd565f6dc38..ec7867f4fe2 100644 --- a/applications/services/cli/cli_i.h +++ b/applications/services/cli/cli_i.h @@ -13,11 +13,14 @@ extern "C" { #endif +#define CLI_BUILTIN_COMMAND_STACK_SIZE (3 * 1024U) +#define CLI_COMMANDS_PATH "/ext/apps_data/cli/plugins" + typedef struct { - const char* name; // #include #include #include #include +#include +#include #define TAG "CliShell" @@ -62,46 +65,80 @@ static void cli_shell_execute_command(CliShell* cli_shell, FuriString* command) FuriString* args = furi_string_alloc_set(command); furi_string_right(args, space + 1); - // find handler + PluginManager* plugin_manager = NULL; + Loader* loader = NULL; CliCommand command_data; - if(!cli_get_command(cli_shell->cli, command_name, &command_data)) { - printf( - ANSI_FG_RED "could not find command `%s`" ANSI_RESET, - furi_string_get_cstr(command_name)); - return; - } - // lock loader - Loader* loader = furi_record_open(RECORD_LOADER); - if(command_data.flags & CliCommandFlagParallelUnsafe) { - bool success = loader_lock(loader); - if(!success) { - printf(ANSI_FG_RED - "this command cannot be run while an application is open" ANSI_RESET); - return; + do { + // find handler + if(!cli_get_command(cli_shell->cli, command_name, &command_data)) { + printf( + ANSI_FG_RED "could not find command `%s`, try `help`" ANSI_RESET, + furi_string_get_cstr(command_name)); + break; } - } - // run command in separate thread - CliCommandThreadData thread_data = { - .command = &command_data, - .pipe = cli_shell->pipe, - .args = args, - }; - FuriThread* thread = furi_thread_alloc_ex( - furi_string_get_cstr(command_name), - CLI_COMMAND_STACK_SIZE, - cli_command_thread, - &thread_data); - furi_thread_start(thread); - furi_thread_join(thread); - furi_thread_free(thread); + // load external command + if(command_data.flags & CliCommandFlagExternal) { + plugin_manager = + plugin_manager_alloc(PLUGIN_APP_ID, PLUGIN_API_VERSION, firmware_api_interface); + FuriString* path = furi_string_alloc_printf( + "%s/cli_%s.fal", CLI_COMMANDS_PATH, furi_string_get_cstr(command_name)); + uint32_t plugin_cnt_last = plugin_manager_get_count(plugin_manager); + PluginManagerError error = + plugin_manager_load_single(plugin_manager, furi_string_get_cstr(path)); + furi_string_free(path); + + if(error != PluginManagerErrorNone) { + printf(ANSI_FG_RED "failed to load external command" ANSI_RESET); + break; + } + + const CliCommandDescriptor* plugin = + plugin_manager_get_ep(plugin_manager, plugin_cnt_last); + furi_assert(plugin); + furi_check(furi_string_cmp_str(command_name, plugin->name) == 0); + command_data.execute_callback = plugin->execute_callback; + command_data.flags = plugin->flags | CliCommandFlagExternal; + command_data.stack_depth = plugin->stack_depth; + } + + // lock loader + if(command_data.flags & CliCommandFlagParallelUnsafe) { + loader = furi_record_open(RECORD_LOADER); + bool success = loader_lock(loader); + if(!success) { + printf(ANSI_FG_RED + "this command cannot be run while an application is open" ANSI_RESET); + break; + } + } + + // run command in separate thread + CliCommandThreadData thread_data = { + .command = &command_data, + .pipe = cli_shell->pipe, + .args = args, + }; + FuriThread* thread = furi_thread_alloc_ex( + furi_string_get_cstr(command_name), + command_data.stack_depth, + cli_command_thread, + &thread_data); + furi_thread_start(thread); + furi_thread_join(thread); + furi_thread_free(thread); + } while(0); furi_string_free(command_name); furi_string_free(args); // unlock loader - if(command_data.flags & CliCommandFlagParallelUnsafe) loader_unlock(loader); + if(loader) loader_unlock(loader); + furi_record_close(RECORD_LOADER); + + // unload external command + if(plugin_manager) plugin_manager_free(plugin_manager); } static size_t cli_shell_prompt_length(CliShell* cli_shell) { @@ -731,6 +768,7 @@ static int32_t cli_shell_thread(void* context) { CliShell* cli_shell = cli_shell_alloc(pipe); FURI_LOG_D(TAG, "Started"); + cli_enumerate_external_commands(cli_shell->cli); cli_shell_motd(); cli_shell_prompt(cli_shell); furi_event_loop_run(cli_shell->event_loop); diff --git a/applications/services/cli/cli_shell.h b/applications/services/cli/cli_shell.h index 2737017deb9..fdcabab8bff 100644 --- a/applications/services/cli/cli_shell.h +++ b/applications/services/cli/cli_shell.h @@ -7,8 +7,7 @@ extern "C" { #endif -#define CLI_SHELL_STACK_SIZE (1 * 1024U) -#define CLI_COMMAND_STACK_SIZE (3 * 1024U) +#define CLI_SHELL_STACK_SIZE (1 * 1024U) FuriThread* cli_shell_start(PipeSide* pipe); diff --git a/applications/services/cli/commands/hello_world.c b/applications/services/cli/commands/hello_world.c new file mode 100644 index 00000000000..81be97298fb --- /dev/null +++ b/applications/services/cli/commands/hello_world.c @@ -0,0 +1,10 @@ +#include "../cli_commands.h" + +static void execute(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); + UNUSED(args); + UNUSED(context); + puts("Hello, World!"); +} + +CLI_COMMAND_INTERFACE(hello_world, execute, CliCommandFlagDefault, 768); diff --git a/applications/services/cli/commands/neofetch.c b/applications/services/cli/commands/neofetch.c new file mode 100644 index 00000000000..e652212eb7d --- /dev/null +++ b/applications/services/cli/commands/neofetch.c @@ -0,0 +1,159 @@ +#include "../cli_commands.h" +#include +#include +#include +#include +#include + +static void execute(PipeSide* pipe, FuriString* args, void* context) { + UNUSED(pipe); + UNUSED(args); + UNUSED(context); + + static const char* const neofetch_logo[] = { + " _.-------.._ -,", + " .-\"```\"--..,,_/ /`-, -, \\ ", + " .:\" /:/ /'\\ \\ ,_..., `. | |", + " / ,----/:/ /`\\ _\\~`_-\"` _;", + " ' / /`\"\"\"'\\ \\ \\.~`_-' ,-\"'/ ", + " | | | 0 | | .-' ,/` /", + " | ,..\\ \\ ,.-\"` ,/` /", + "; : `/`\"\"\\` ,/--==,/-----,", + "| `-...| -.___-Z:_______J...---;", + ": ` _-'", + }; +#define NEOFETCH_COLOR ANSI_FLIPPER_BRAND_ORANGE + + // Determine logo parameters + size_t logo_height = COUNT_OF(neofetch_logo), logo_width = 0; + for(size_t i = 0; i < logo_height; i++) + logo_width = MAX(logo_width, strlen(neofetch_logo[i])); + logo_width += 4; // space between logo and info + + // Format hostname delimiter + const size_t size_of_hostname = 4 + strlen(furi_hal_version_get_name_ptr()); + char delimiter[64]; + memset(delimiter, '-', size_of_hostname); + delimiter[size_of_hostname] = '\0'; + + // Get heap info + size_t heap_total = memmgr_get_total_heap(); + size_t heap_used = heap_total - memmgr_get_free_heap(); + uint16_t heap_percent = (100 * heap_used) / heap_total; + + // Get storage info + Storage* storage = furi_record_open(RECORD_STORAGE); + uint64_t ext_total, ext_free, ext_used, ext_percent; + storage_common_fs_info(storage, "/ext", &ext_total, &ext_free); + ext_used = ext_total - ext_free; + ext_percent = (100 * ext_used) / ext_total; + ext_used /= 1024 * 1024; + ext_total /= 1024 * 1024; + furi_record_close(RECORD_STORAGE); + + // Get battery info + uint16_t charge_percent = furi_hal_power_get_pct(); + const char* charge_state; + if(furi_hal_power_is_charging()) { + if((charge_percent < 100) && (!furi_hal_power_is_charging_done())) { + charge_state = "charging"; + } else { + charge_state = "charged"; + } + } else { + charge_state = "discharging"; + } + + // Get misc info + uint32_t uptime = furi_get_tick() / furi_kernel_get_tick_frequency(); + const Version* version = version_get(); + uint16_t major, minor; + furi_hal_info_get_api_version(&major, &minor); + + // Print ASCII art with info + const size_t info_height = 16; + for(size_t i = 0; i < MAX(logo_height, info_height); i++) { + printf(NEOFETCH_COLOR "%-*s", logo_width, (i < logo_height) ? neofetch_logo[i] : ""); + switch(i) { + case 0: // you@ + printf("you" ANSI_RESET "@" NEOFETCH_COLOR "%s", furi_hal_version_get_name_ptr()); + break; + case 1: // delimiter + printf(ANSI_RESET "%s", delimiter); + break; + case 2: // OS: FURI (SDK .) + printf( + "OS" ANSI_RESET ": FURI %s %s %s %s (SDK %hu.%hu)", + version_get_version(version), + version_get_gitbranch(version), + version_get_version(version), + version_get_githash(version), + major, + minor); + break; + case 3: // Host: + printf( + "Host" ANSI_RESET ": %s %s", + furi_hal_version_get_model_code(), + furi_hal_version_get_device_name_ptr()); + break; + case 4: // Kernel: FreeRTOS .. + printf( + "Kernel" ANSI_RESET ": FreeRTOS %d.%d.%d", + tskKERNEL_VERSION_MAJOR, + tskKERNEL_VERSION_MINOR, + tskKERNEL_VERSION_BUILD); + break; + case 5: // Uptime: ?h?m?s + printf( + "Uptime" ANSI_RESET ": %luh%lum%lus", + uptime / 60 / 60, + uptime / 60 % 60, + uptime % 60); + break; + case 6: // ST7567 128x64 @ 1 bpp in 1.4" + printf("Display" ANSI_RESET ": ST7567 128x64 @ 1 bpp in 1.4\""); + break; + case 7: // DE: GuiSrv + printf("DE" ANSI_RESET ": GuiSrv"); + break; + case 8: // Shell: CliSrv + printf("Shell" ANSI_RESET ": CliShell"); + break; + case 9: // CPU: STM32WB55RG @ 64 MHz + printf("CPU" ANSI_RESET ": STM32WB55RG @ 64 MHz"); + break; + case 10: // Memory: / B (??%) + printf( + "Memory" ANSI_RESET ": %zu / %zu B (%hu%%)", heap_used, heap_total, heap_percent); + break; + case 11: // Disk (/ext): / MiB (??%) + printf( + "Disk (/ext)" ANSI_RESET ": %llu / %llu MiB (%llu%%)", + ext_used, + ext_total, + ext_percent); + break; + case 12: // Battery: ??% () + printf("Battery" ANSI_RESET ": %hu%% (%s)" ANSI_RESET, charge_percent, charge_state); + break; + case 13: // empty space + break; + case 14: // Colors (line 1) + for(size_t j = 30; j <= 37; j++) + printf("\e[%dm███", j); + break; + case 15: // Colors (line 2) + for(size_t j = 90; j <= 97; j++) + printf("\e[%dm███", j); + break; + default: + break; + } + printf("\r\n"); + } + printf(ANSI_RESET); +#undef NEOFETCH_COLOR +} + +CLI_COMMAND_INTERFACE(neofetch, execute, CliCommandFlagDefault, 2048); diff --git a/applications/services/power/application.fam b/applications/services/power/application.fam index 059c570c40d..f14d88c5426 100644 --- a/applications/services/power/application.fam +++ b/applications/services/power/application.fam @@ -6,7 +6,7 @@ App( cdefines=["SRV_POWER"], requires=[ "gui", - "cli_start", + "cli", ], provides=[ "power_settings", diff --git a/applications/services/rpc/application.fam b/applications/services/rpc/application.fam index c8e26e0446f..7c0b6813369 100644 --- a/applications/services/rpc/application.fam +++ b/applications/services/rpc/application.fam @@ -3,7 +3,7 @@ App( apptype=FlipperAppType.STARTUP, entry_point="rpc_on_system_start", cdefines=["SRV_RPC"], - requires=["cli_start"], + requires=["cli"], order=10, sdk_headers=["rpc_app.h"], )