ADI console menu

Revision:
1:dcc17e5a913f
Parent:
0:47b30a147723
Child:
2:6a97882f6144
--- a/adi_console_menu.c	Fri Feb 28 20:04:52 2020 +0000
+++ b/adi_console_menu.c	Sat Feb 29 04:11:15 2020 +0000
@@ -0,0 +1,317 @@
+/*!
+ *****************************************************************************
+  @file:  adi_console_menu.c
+
+  @brief: A simple console menu manager handler
+
+  @details: A way to define using arrays of structs a set of menus that can
+            be displayed to a user, easily, with all user interaction handled
+            by the library, leaving only the implementation of the menu actions
+            to be done by the library user.
+ -----------------------------------------------------------------------------
+ Copyright (c) 2019, 2020 Analog Devices, Inc.
+ All rights reserved.
+
+ This software is proprietary to Analog Devices, Inc. and its licensors.
+ By using this software you agree to the terms of the associated
+ Analog Devices Software License Agreement.
+
+*****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdbool.h>
+#include <assert.h>
+
+#include "adi_console_menu.h"
+
+
+#define DIV_STRING "\t=================================================="
+
+/*!
+ * @brief      displays the text of a console menu
+ *
+ * @details
+ */
+static void adi_display_console_menu(const console_menu * menu)
+{
+	adi_clear_console();
+
+	// call headerItem to allow display of other content
+	if (menu->headerItem != NULL) {
+		menu->headerItem();
+		printf(DIV_STRING EOL);
+	}
+
+	/*
+	 * Display the menu title and  menuItems
+	 * The shortcutKey is used to display '[A]' before the dispayText
+	 */
+	printf("\t%s" EOL "\t", menu->title);
+	// show an underline to distinguish title from item
+	for (uint8_t i = 0; i < strlen(menu->title); i++) {
+		putchar('-');
+	}
+	// Extend underline past end of string, and then new line
+	printf("--" EOL);
+
+	// If the shortcutKey is not unique, first found is used
+	for (uint8_t i = 0; i < menu->itemCount; i ++) {
+		if (menu->items[i].shortcutKey == '\00') {
+			// No shortcut key defined, but display item text if available
+			printf("\t%s" EOL, menu->items[i].text);
+		} else {
+			printf("\t[%c] %s" EOL, toupper(menu->items[i].shortcutKey),
+			       menu->items[i].text);
+		}
+	}
+	if (menu->enableEscapeKey) {
+		printf(EOL "\t[ESC] Exit Menu" EOL);
+	}
+
+	printf(EOL "\tPlease make a selection." EOL);
+
+	// call footerItem to allow display of other content
+	if (menu->footerItem != NULL) {
+		printf(DIV_STRING EOL);
+		menu->footerItem();
+	}
+}
+
+
+/*!
+ * @brief      Display a consoleMenu and handle User interaction
+ *
+ * @details    This displays the menuItems defined by the console menu, and
+ *              handles all user interaction for the menu.
+ */
+int32_t adi_do_console_menu(const console_menu * menu)
+{
+	int32_t itemSelected = MENU_ESCAPED;
+	bool enableKeyScan = true;
+
+	adi_display_console_menu(menu);
+
+	/*
+	 *  Loop waiting for valid user input. menuItem index is returned if
+	 *  user presses a valid menu option.
+	 */
+	do {
+		char keyPressed = toupper(getchar());
+
+		if (menu->enableEscapeKey) {
+			if (keyPressed == ESCAPE_KEY_CODE) {
+				itemSelected = MENU_ESCAPED;
+				enableKeyScan = false;
+				break;
+			}
+		}
+
+		for (uint8_t i = 0; i < menu->itemCount; i ++) {
+			if (toupper(menu->items[i].shortcutKey) == keyPressed) {
+				itemSelected = i;
+
+				// If the menuAction function pointer is not NULL, call the action
+				if (menu->items[i].action != NULL) {
+					switch (menu->items[i].action(menu->items[i].id)) {
+					case MENU_DONE: {
+						enableKeyScan = false;
+						break;
+					}
+					case MENU_CONTINUE:
+					default: {
+						enableKeyScan = true;
+						adi_display_console_menu(menu);
+						break;
+					}
+					}
+				}
+				break;
+			}
+		}
+	} while (enableKeyScan);
+
+	return (itemSelected);
+}
+
+
+/*!
+ * @brief      Clears the standard input buffer (typically
+ *             needed after scanf function)
+ *
+ * @details
+ */
+static void flushf(void)
+{
+	// flush the standard input (i.e. clear the input buffer)
+	while((getchar()) != '\n');
+}
+
+
+/*!
+ * @brief      Reads a decimal string from the user
+ *
+ * @param      input_len max number of character to accept from the user
+ *
+ * @return      The integer value entered
+ *
+ * @details    Allows a user to type in number, echoing back to the user,
+ *             up to input_len chars
+ *
+ *  @note      Only positive integer numbers are supported currently
+ */
+int32_t adi_get_decimal_int(uint8_t input_len)
+{
+	char buf[20] = {0};
+	uint8_t buf_index = 0;
+	char ch;
+	bool loop = true;
+
+	assert(input_len < 19);
+
+	do  {
+		ch = getchar();
+		if (isdigit(ch) && buf_index < (input_len)) {
+			//  echo and store it as buf not full
+			buf[buf_index++] = ch;
+			putchar(ch);
+		}
+		if ((ch == '\x7F') && (buf_index > 0)) {
+			//backspace and at least 1 char in buffer
+			buf[buf_index--] = '\x00';
+			putchar(ch);
+		}
+		if ((ch == '\x0D') || (ch == '\x0A')) {
+			// return key pressed, all done, null terminate string
+			buf[buf_index] = '\x00';
+			loop = false;
+		}
+	} while(loop);
+
+	flushf();
+	return atoi(buf);
+}
+
+/*!
+ * @brief      Reads a hexadecimal number from the user
+ *
+ * @param      input_len max number of character to accept from the user
+ *
+ * @return     The integer value entered
+ *
+* @details     Allows a user to type in a hexnumber, echoing back to the user,
+ *             up to input_len chars
+ */
+uint32_t adi_get_hex_integer(uint8_t input_len)
+{
+	char buf[9] = {0};
+	uint8_t buf_index = 0;
+	char ch;
+	bool loop = true;
+
+	assert(input_len < 8);
+
+	do  {
+		ch = getchar();
+		if (isxdigit(ch) && buf_index < (input_len)) {
+			//  echo and store it as buf not full
+			buf[buf_index++] = ch;
+			putchar(ch);
+		}
+		if ((ch == '\x7F') && (buf_index > 0)) {
+			//backspace and at least 1 char in buffer
+			buf[buf_index--] = '\x00';
+			putchar(ch);
+		}
+		if ((ch == '\x0D') || (ch == '\x0A')) {
+			// return key pressed, all done, null terminate string
+			buf[buf_index] = '\x00';
+			loop = false;
+		}
+	} while(loop);
+
+	flushf();
+	return strtol(buf, NULL, 16);
+}
+
+
+/*!
+ * @brief      Reads a floating string from the user
+ *
+ * @param      input_len max number of character to accept from the user
+ *
+ * @return      The float value entered
+ *
+ * @details    Allows a user to type in number, echoing back to the user,
+ *             up to input_len chars
+ *
+ *  @note      Only positive floating point numbers are supported currently
+ */
+float adi_get_decimal_float(uint8_t input_len)
+{
+	char buf[20] = { 0 };
+	uint8_t buf_index = 0;
+	char ch;
+	bool loop = true;
+
+	assert(input_len < 19);
+
+	do {
+		ch = getchar();
+		if ((isdigit(ch) || (ch == '.')) && buf_index < (input_len)) {
+			//  echo and store it as buf not full
+			buf[buf_index++] = ch;
+			putchar(ch);
+		}
+		if ((ch == '\x7F') && (buf_index > 0)) {
+			//backspace and at least 1 char in buffer
+			buf[buf_index--] = '\x00';
+			putchar(ch);
+		}
+		if ((ch == '\x0D') || (ch == '\x0A')) {
+			// return key pressed, all done, null terminate string
+			buf[buf_index] = '\x00';
+			loop = false;
+		}
+	} while (loop);
+
+	flushf();
+	return atof(buf);
+}
+
+
+/*!
+ * @brief      Clears the console terminal
+ *
+ * @details    Clears the console terminal using VT100 escape code, or can be changed to
+ *             output blank lines if serial link doesn't support VT100.
+ */
+void adi_clear_console(void)
+{
+	/*
+	 * clear console and move cursor to home location, followed by move to home location.
+	 *  Dedicated call to move home is because sometimes first move home doesn't work
+	 *  \r\n required to flush the uart buffer.
+	 */
+	printf("\x1B[2J\x1B[H\r\n");
+
+	/*
+	 * if VT100 is not supported, this can be enabled instead, but menu display may not work well
+	 */
+//    for (uint8_t = 0; i < 100; i++)
+//      printf("\r\n\r");
+}
+
+
+/*!
+ * @brief      waits for any key to be pressed, and displays a prompt to the user
+ *
+ * @details
+ */
+void adi_press_any_key_to_continue(void)
+{
+	printf("\r\nPress any key to continue...\r\n");
+	getchar();
+}