From 8c32a74086bf304d9ea99c98ef0a8c3f7ffc0332 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Tue, 17 Dec 2024 14:01:22 -0600 Subject: [PATCH] ux: Introduce progress indicator Not providing any user feedback during flashing is not very user friendly, in particular when, in some situations, it can take minutes to flash very large files. Introduce a progress bar for the flashing and patching steps to provide such feedback to the user, when stdout is determined to be a terminal of sufficient size (i.e. tools programmatically invoking qdl should see no difference). Signed-off-by: Bjorn Andersson --- firehose.c | 4 ++- patch.c | 12 ++++++++ qdl.c | 2 ++ qdl.h | 2 ++ ux.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 107 insertions(+), 1 deletion(-) diff --git a/firehose.c b/firehose.c index cf53c83..0fcdfdd 100644 --- a/firehose.c +++ b/firehose.c @@ -438,6 +438,8 @@ static int firehose_program(struct qdl_device *qdl, struct program *program, int } left -= chunk_size; + + ux_progress("%s", num_sectors - left, num_sectors, program->label); } t = time(NULL) - t0; @@ -565,7 +567,7 @@ static int firehose_apply_patch(struct qdl_device *qdl, struct patch *patch) xmlDoc *doc; int ret; - ux_info("applying patch \"%s\"\n", patch->what); + ux_debug("applying patch \"%s\"\n", patch->what); doc = xmlNewDoc((xmlChar*)"1.0"); root = xmlNewNode(NULL, (xmlChar*)"data"); diff --git a/patch.c b/patch.c index b345f42..0ca4c41 100644 --- a/patch.c +++ b/patch.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "patch.h" #include "qdl.h" @@ -100,8 +101,15 @@ int patch_load(const char *patch_file) int patch_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct patch *patch)) { struct patch *patch; + unsigned int count = 0; + unsigned int idx = 0; int ret; + for (patch = patches; patch; patch = patch->next) { + if (!strcmp(patch->filename, "DISK")) + count++; + } + for (patch = patches; patch; patch = patch->next) { if (strcmp(patch->filename, "DISK")) continue; @@ -109,8 +117,12 @@ int patch_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, s ret = apply(qdl, patch); if (ret) return ret; + + ux_progress("Applying patches", idx++, count); } + ux_info("%d patches applied\n", idx); + return 0; } diff --git a/qdl.c b/qdl.c index 10ba0dc..69a0098 100644 --- a/qdl.c +++ b/qdl.c @@ -170,6 +170,8 @@ int main(int argc, char **argv) return 1; } + ux_init(); + prog_mbn = argv[optind++]; do { diff --git a/qdl.h b/qdl.h index 1446504..669c96f 100644 --- a/qdl.h +++ b/qdl.h @@ -39,10 +39,12 @@ void print_hex_dump(const char *prefix, const void *buf, size_t len); unsigned attr_as_unsigned(xmlNode *node, const char *attr, int *errors); const char *attr_as_string(xmlNode *node, const char *attr, int *errors); +void ux_init(void); void ux_err(const char *fmt, ...); void ux_info(const char *fmt, ...); void ux_log(const char *fmt, ...); void ux_debug(const char *fmt, ...); +void ux_progress(const char *fmt, unsigned int value, unsigned int size, ...); extern bool qdl_debug; diff --git a/ux.c b/ux.c index 6e861ff..c12552f 100644 --- a/ux.c +++ b/ux.c @@ -1,7 +1,21 @@ #include +#include +#include +#include #include "qdl.h" +#define MIN(x, y) ((x) < (y) ? (x) : (y)) + +#define UX_PROGRESS_REFRESH_RATE 10 +#define UX_PROGRESS_SIZE_MAX 120 + +static const char * const progress_hashes = "########################################################################################################################"; +static const char * const progress_dashes = "------------------------------------------------------------------------------------------------------------------------"; + +static unsigned int ux_width; +static unsigned int ux_cur_line_length; + /* * Levels of output: * @@ -11,10 +25,33 @@ * debug: protocol logs */ +/* Clear ux_cur_line_length characters of the progress bar from the screen */ +static void ux_clear_line(void) +{ + if (!ux_cur_line_length) + return; + + printf("%*s\r", ux_cur_line_length, ""); + fflush(stdout); + ux_cur_line_length = 0; +} + +void ux_init(void) +{ + struct winsize w; + int ret; + + ret = ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + if (!ret) + ux_width = MIN(w.ws_col, UX_PROGRESS_SIZE_MAX); +} + void ux_err(const char *fmt, ...) { va_list ap; + ux_clear_line(); + va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); @@ -25,6 +62,8 @@ void ux_info(const char *fmt, ...) { va_list ap; + ux_clear_line(); + va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); @@ -38,6 +77,8 @@ void ux_log(const char *fmt, ...) if (!qdl_debug) return; + ux_clear_line(); + va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); @@ -51,8 +92,55 @@ void ux_debug(const char *fmt, ...) if (!qdl_debug) return; + ux_clear_line(); + va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); fflush(stdout); } + +void ux_progress(const char *fmt, unsigned int value, unsigned int max, ...) +{ + static struct timeval last_progress_update; + unsigned long elapsed_us; + unsigned int bar_length; + unsigned int bars; + unsigned int dashes; + struct timeval now; + char task_name[32]; + float percent; + va_list ap; + + /* Don't print progress is window is too narrow, or if stdout is redirected */ + if (ux_width < 30) + return; + + /* Avoid updating the console more than UX_PROGRESS_REFRESH_RATE per second */ + if (last_progress_update.tv_sec) { + gettimeofday(&now, NULL); + elapsed_us = (now.tv_sec - last_progress_update.tv_sec) * 1000000 + + (now.tv_usec - last_progress_update.tv_usec); + + if (elapsed_us < (1000000 / UX_PROGRESS_REFRESH_RATE)) + return; + } + + va_start(ap, max); + vsnprintf(task_name, sizeof(task_name), fmt, ap); + va_end(ap); + + bar_length = ux_width - (20 + 4 + 6); + percent = (float)value / max; + bars = percent * bar_length; + dashes = bar_length - bars; + + printf("%-20.20s [%.*s%.*s] %1.2f%%%n\r", task_name, + bars, progress_hashes, + dashes, progress_dashes, + percent * 100, + &ux_cur_line_length); + fflush(stdout); + + gettimeofday(&last_progress_update, NULL); +}