Skip to content

Commit

Permalink
feature(console): add user context support
Browse files Browse the repository at this point in the history
current implementation implicitly forces the developer to use global variables
to enter its context during the command invocation.

this implementation adds the option to add a user context with minimum API
implication and no breakage.

when running via repl the context is available in the repl config, when
running directly from esp_context a new method is introduced.

Fields added:
   esp_console_cmd_t::func_context    - (*)(int argc, char **argv, void *context)
   esp_console_repl_config_t::context - void*

Functions added:

   esp_err_t esp_console_run_context(const char *cmdline, void *context, int *cmd_ret)

Signed-off-by: Alon Bar-Lev <alon.barlev@gmail.com>
  • Loading branch information
alonbl committed Oct 27, 2023
1 parent 8fc8f3f commit 0147a20
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 10 deletions.
20 changes: 16 additions & 4 deletions components/console/commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ typedef struct cmd_item_ {
* May be NULL.
*/
char *hint;
esp_console_cmd_func_t func; //!< pointer to the command handler
void *argtable; //!< optional pointer to arg table
SLIST_ENTRY(cmd_item_) next; //!< next command in the list
esp_console_cmd_func_t func; //!< pointer to the command handler
esp_console_cmd_func_context_t func_context; //!< pointer to the command handler
void *argtable; //!< optional pointer to arg table
SLIST_ENTRY(cmd_item_) next; //!< next command in the list
} cmd_item_t;

/** linked list of command structures */
Expand Down Expand Up @@ -130,6 +131,7 @@ esp_err_t esp_console_cmd_register(const esp_console_cmd_t *cmd)
}
item->argtable = cmd->argtable;
item->func = cmd->func;
item->func_context = cmd->func_context;
cmd_item_t *last = SLIST_FIRST(&s_cmd_list);
if (last == NULL) {
SLIST_INSERT_HEAD(&s_cmd_list, item, next);
Expand Down Expand Up @@ -189,6 +191,11 @@ static const cmd_item_t *find_command_by_name(const char *name)
}

esp_err_t esp_console_run(const char *cmdline, int *cmd_ret)
{
return esp_console_run_context(NULL, cmdline, cmd_ret);
}

esp_err_t esp_console_run_context(void *context, const char *cmdline, int *cmd_ret)
{
if (s_tmp_line_buf == NULL) {
return ESP_ERR_INVALID_STATE;
Expand All @@ -210,7 +217,12 @@ esp_err_t esp_console_run(const char *cmdline, int *cmd_ret)
free(argv);
return ESP_ERR_NOT_FOUND;
}
*cmd_ret = (*cmd->func)(argc, argv);
if (cmd->func) {
*cmd_ret = (*cmd->func)(argc, argv);
}
if (cmd->func_context) {
*cmd_ret = (*cmd->func_context)(context, argc, argv);
}
free(argv);
return ESP_OK;
}
Expand Down
28 changes: 28 additions & 0 deletions components/console/esp_console.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ typedef struct {
uint32_t task_priority; //!< repl task priority
const char *prompt; //!< prompt (NULL represents default: "esp> ")
size_t max_cmdline_length; //!< maximum length of a command line. If 0, default value will be used
void *context; //!< user context
} esp_console_repl_config_t;

/**
Expand Down Expand Up @@ -156,6 +157,15 @@ esp_err_t esp_console_deinit(void);
*/
typedef int (*esp_console_cmd_func_t)(int argc, char **argv);

/**
* @brief Console command main function
* @param context a user context given at invocation.
* @param argc number of arguments
* @param argv array with argc entries, each pointing to a zero-terminated string argument
* @return console command return code, 0 indicates "success"
*/
typedef int (*esp_console_cmd_func_context_t)(void *context, int argc, char **argv);

/**
* @brief Console command description
*/
Expand All @@ -181,6 +191,10 @@ typedef struct {
* Pointer to a function which implements the command.
*/
esp_console_cmd_func_t func;
/**
* Pointer to a context aware function which implements the command.
*/
esp_console_cmd_func_context_t func_context;
/**
* Array or structure of pointers to arg_xxx structures, may be NULL.
* Used to generate hint text if 'hint' is set to NULL.
Expand Down Expand Up @@ -213,6 +227,20 @@ esp_err_t esp_console_cmd_register(const esp_console_cmd_t *cmd);
*/
esp_err_t esp_console_run(const char *cmdline, int *cmd_ret);

/**
* @brief Run command line with context
* @param context a user context.
* @param cmdline command line (command name followed by a number of arguments)
* @param[out] cmd_ret return code from the command (set if command was run)
* @return
* - ESP_OK, if command was run
* - ESP_ERR_INVALID_ARG, if the command line is empty, or only contained
* whitespace
* - ESP_ERR_NOT_FOUND, if command with given name wasn't registered
* - ESP_ERR_INVALID_STATE, if esp_console_init wasn't called
*/
esp_err_t esp_console_run_context(void *context, const char *cmdline, int *cmd_ret);

/**
* @brief Split command line into arguments in place
* @verbatim
Expand Down
15 changes: 9 additions & 6 deletions components/console/esp_console_repl.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ typedef struct {
const char *history_save_path;
TaskHandle_t task_hdl; // REPL task handle
size_t max_cmdline_length; // Maximum length of a command line. If 0, default value will be used.
void *context; // User context
} esp_console_repl_com_t;

typedef struct {
Expand All @@ -56,7 +57,7 @@ static esp_err_t esp_console_repl_usb_cdc_delete(esp_console_repl_t *repl);
#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
static esp_err_t esp_console_repl_usb_serial_jtag_delete(esp_console_repl_t *repl);
#endif //CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
static esp_err_t esp_console_common_init(size_t max_cmdline_length, esp_console_repl_com_t *repl_com);
static esp_err_t esp_console_common_init(void *context, size_t max_cmdline_length, esp_console_repl_com_t *repl_com);
static esp_err_t esp_console_setup_prompt(const char *prompt, esp_console_repl_com_t *repl_com);
static esp_err_t esp_console_setup_history(const char *history_path, uint32_t max_history_len, esp_console_repl_com_t *repl_com);

Expand Down Expand Up @@ -86,7 +87,7 @@ esp_err_t esp_console_new_repl_usb_cdc(const esp_console_dev_usb_cdc_config_t *d
fcntl(fileno(stdin), F_SETFL, 0);

// initialize console, common part
ret = esp_console_common_init(repl_config->max_cmdline_length, &cdc_repl->repl_com);
ret = esp_console_common_init(repl_config->context, repl_config->max_cmdline_length, &cdc_repl->repl_com);
if (ret != ESP_OK) {
goto _exit;
}
Expand Down Expand Up @@ -160,7 +161,7 @@ esp_err_t esp_console_new_repl_usb_serial_jtag(const esp_console_dev_usb_serial_
}

// initialize console, common part
ret = esp_console_common_init(repl_config->max_cmdline_length, &usb_serial_jtag_repl->repl_com);
ret = esp_console_common_init(repl_config->context, repl_config->max_cmdline_length, &usb_serial_jtag_repl->repl_com);
if (ret != ESP_OK) {
goto _exit;
}
Expand Down Expand Up @@ -264,7 +265,7 @@ esp_err_t esp_console_new_repl_uart(const esp_console_dev_uart_config_t *dev_con
esp_vfs_dev_uart_use_driver(dev_config->channel);

// initialize console, common part
ret = esp_console_common_init(repl_config->max_cmdline_length, &uart_repl->repl_com);
ret = esp_console_common_init(repl_config->context, repl_config->max_cmdline_length, &uart_repl->repl_com);
if (ret != ESP_OK) {
goto _exit;
}
Expand Down Expand Up @@ -369,7 +370,7 @@ static esp_err_t esp_console_setup_history(const char *history_path, uint32_t ma
return ret;
}

static esp_err_t esp_console_common_init(size_t max_cmdline_length, esp_console_repl_com_t *repl_com)
static esp_err_t esp_console_common_init(void *context, size_t max_cmdline_length, esp_console_repl_com_t *repl_com)
{
esp_err_t ret = ESP_OK;
/* Initialize the console */
Expand All @@ -381,6 +382,8 @@ static esp_err_t esp_console_common_init(size_t max_cmdline_length, esp_console_
repl_com->max_cmdline_length = max_cmdline_length;
}

repl_com->context = context;

#if CONFIG_LOG_COLORS
console_config.hint_color = atoi(LOG_COLOR_CYAN);
#else
Expand Down Expand Up @@ -531,7 +534,7 @@ static void esp_console_repl_task(void *args)

/* Try to run the command */
int ret;
esp_err_t err = esp_console_run(line, &ret);
esp_err_t err = esp_console_run_context(repl_com->context, line, &ret);
if (err == ESP_ERR_NOT_FOUND) {
printf("Unrecognized command\n");
} else if (err == ESP_ERR_INVALID_ARG) {
Expand Down
35 changes: 35 additions & 0 deletions components/console/test_apps/console/main/test_console.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,38 @@ TEST_CASE("esp console init/deinit test, minimal config", "[console]")
TEST_ESP_OK(esp_console_cmd_register(&cmd));
TEST_ESP_OK(esp_console_deinit());
}

static const char *context_test_context = "context";

/* handle 'quit' command */
static int do_cmd_quit_with_context(void *context, int argc, char **argv)
{
printf("ByeBye %s\r\n", (char *)context);
s_repl->del(s_repl);

linenoiseHistoryFree(); // Free up memory

return context != context_test_context;
}

static esp_console_cmd_t s_quit_cmd_with_context = {
.command = "quit",
.help = "Quit REPL environment",
.func_context = &do_cmd_quit_with_context
};

// Enter "quit" to exit REPL environment
/* Marked as ignore since it cannot run as a normal unity test case
ran separately in test_console_repl */
TEST_CASE("esp console repl test with context", "[console][ignore]")
{
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
repl_config.context = (void *)context_test_context;
esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
TEST_ESP_OK(esp_console_new_repl_uart(&uart_config, &repl_config, &s_repl));

TEST_ESP_OK(esp_console_cmd_register(&s_quit_cmd_with_context));

TEST_ESP_OK(esp_console_start_repl(s_repl));
vTaskDelay(pdMS_TO_TICKS(2000));
}

0 comments on commit 0147a20

Please sign in to comment.