Loggy_McLogger/Core/Src/console.c

444 lines
9.5 KiB
C

/**
*********************************************************************
*
* @file console.c
* @brief
*
* @date 2024-04-14 19:12:40
* @author CT
*
* @details
*
*************************************************************************
**/
/*
Operational Concept:
Sending enter displays the current menu
Simple 1 char command interface
*/
#include <stdint.h>
#include <stdbool.h>
#include "console.h"
#include "NVmem.h"
#include "version.h"
#include "usbd_cdc_if.h"
#define CONSOLE_BUF_SIZE (256u)
typedef struct config_data
{
int32_t val_1;
int32_t val_2;
int32_t val_3;
} config_data_t;
typedef union config_data_u
{
config_data_t config;
uint8_t data[12];
} config_data_u_t;
static cbuf_handle_t m_console_input;
static cbuf_handle_t m_console_output;
static uint8_t m_console_input_buf[CONSOLE_BUF_SIZE] = {0};
static uint8_t m_console_output_buf[CONSOLE_BUF_SIZE] = {0};
static uint8_t tx_buf[CONSOLE_BUF_SIZE];
static uint8_t m_command_buf[16] = {0};
static uint8_t m_command_len = 0;
static bool m_command_ready = false;
static bool m_echo_console = false;
// static int32_t submenu_value_one = 0;
// static int32_t submenu_value_two = 0;
// static int32_t submenu_value_three = 0;
config_data_u_t configuration_data;
enum console_menu_state{main_menu, submenu_1, submenu_2, submenu_3, save_menu, version_menu};
enum console_menu_state console_menu = main_menu;
static void process_incoming(void);
static void process_outgoing(void);
static void process_command(void);
static int console_send(uint8_t* buf, uint32_t len);
static void menu_state_machine(void);
static int parse_input(int32_t* data);
static int32_t read_line(uint8_t data);
static void print_main_menu(void);
static void print_submenu_one(void);
static void print_submenu_two(void);
static void print_submenu_three(void);
static void print_saved_menu(int32_t val);
static void set_console_reset(void);
static void set_console_red(void);
static void set_console_green(void);
static void reset_console(void);
/* PUBLIC FUNCTIONS */
void console_init(void)
{
m_console_input = circular_buf_init(m_console_input_buf, CONSOLE_BUF_SIZE);
m_console_output = circular_buf_init(m_console_output_buf, CONSOLE_BUF_SIZE);
// for this demonstratior, load example values from nvmem
NVmem_read(configuration_data.data, 0, 12);
}
void console_service(void)
{
// if there is received data waiting
process_incoming();
// if there is data waiting to be transmitted
process_outgoing();
// Process command
process_command();
}
/// @brief Attempts to push one byte into the console input buffer
/// @param data byte to be pushed to input
/// @return 0 on success, -1 on failure
int console_push(uint8_t data)
{
int ret = 0;
ret = circular_buf_try_put(m_console_input, data);
return (ret);
}
/* PRIVATE FUNCTIONS */
void process_incoming(void)
{
if (!circular_buf_empty(m_console_input))
{
uint8_t data;
while (!circular_buf_empty(m_console_input))
{
circular_buf_get(m_console_input, &data);
m_command_ready = read_line(data);
if (m_echo_console)
{
circular_buf_try_put(m_console_output, data);
}
}
}
}
void process_outgoing(void)
{
if(!circular_buf_empty(m_console_output))
{
size_t tx_len = circular_buf_size(m_console_output);
for (uint32_t i = 0; i < tx_len; i++)
{
circular_buf_get(m_console_output, &tx_buf[i]);
}
// Note: directly interacts with interface
CDC_Transmit_FS(tx_buf, tx_len);
}
}
void process_command(void)
{
if(m_command_ready)
{
m_command_ready = false;
// for(uint32_t i = 0; i < m_command_len; i++)
// {
// circular_buf_put(m_console_output, m_command_buf[i]);
// }
menu_state_machine();
}
}
int console_send(uint8_t* buf, uint32_t len)
{
int ret = 0;
for (uint32_t i = 0; i < len; i++)
{
ret |= circular_buf_try_put(m_console_output, buf[i]);
}
return (ret);
}
/// @brief Reads in a line terminated by \\n
/// @param data
/// @return 1 if EOL found, 0 otherwise
int32_t read_line(uint8_t data)
{
int32_t ret = 0;
static uint8_t m_command_buf_index = 0;
// if EOL, return on and set length
if((data == '\n') || (data == '\r'))
{
m_command_buf[m_command_buf_index++] = '\n';
// set length of command for use within rest of module
m_command_len = m_command_buf_index;
// reset index
m_command_buf_index = 0;
ret = 1;
}
else if (data == 0x1B) // ESCAPE
{
m_command_len = 1;
m_command_buf[0] = 0x1B;
// reset index
m_command_buf_index = 0;
ret = 1;
}
else
{
if (m_command_buf_index < 16)
{
m_command_buf[m_command_buf_index++] = data;
}
else
{
m_command_buf_index = 0;
}
}
return (ret);
}
// Called when a complete line has been received
void menu_state_machine(void)
{
int32_t success = 0;
switch (console_menu)
{
case main_menu:
if (m_command_buf[0] == '1')
{
print_submenu_one();
console_menu = submenu_1;
}
else if (m_command_buf[0] == '2')
{
print_submenu_two();
console_menu = submenu_2;
}
else if (m_command_buf[0] == '3')
{
print_submenu_three();
console_menu = submenu_3;
}
else if (m_command_buf[0] == 'S')
{
success = NVmem_write_immediate(configuration_data.data, 0, 12);
print_saved_menu(success);
// need to call service because we are blocking all normal execution here
process_outgoing();
// Delay for user to read result
HAL_Delay(3000);
console_menu = main_menu;
print_main_menu();
}
else// if (m_command_buf[0] == '\n')
{
print_main_menu();
}
break;
case submenu_1:
if (m_command_buf[0] == 0x1B) // ESCAPE
{
console_menu = main_menu;
print_main_menu();
}
else
{
// Take input and immediately return to main menu
parse_input(&configuration_data.config.val_1);
console_menu = main_menu;
print_main_menu();
}
break;
case submenu_2:
if (m_command_buf[0] == 0x1B) // ESCAPE
{
console_menu = main_menu;
print_main_menu();
}
else
{
parse_input(&configuration_data.config.val_2);
console_menu = main_menu;
print_main_menu();
}
break;
case submenu_3:
if (m_command_buf[0] == 0x1B) // ESCAPE
{
console_menu = main_menu;
print_main_menu();
}
else
{
parse_input(&configuration_data.config.val_3);
console_menu = main_menu;
print_main_menu();
}
break;
case save_menu:
break;
default:
break;
}
}
/// @brief Parses line for integer
/// @param data parsed out integer
/// @return 1 on success, otherwise fail...kinda
int parse_input(int32_t* data)
{
// *data = atoi((char*)m_command_buf);
int ret = sscanf((char*)m_command_buf, "%ld", data);
return (ret);
}
// Also acts as initialization for state
void print_main_menu(void)
{
char buf[128];
m_echo_console = true;
reset_console();
set_console_red();
uint32_t len = snprintf(buf, 128, "Main Menu:\r\n[1]Option 1: \"%li\"\r\n[2]Option 2: \"%li\"\r\n[3]Option 3: \"%li\"\r\n[S]ave\r\nVersion: %s\r\nEnter Selection: ",
configuration_data.config.val_1, configuration_data.config.val_2, configuration_data.config.val_3, VERSION_STR);
console_send((uint8_t*)buf, len);
}
void print_submenu_one(void)
{
char buf[128];
m_echo_console = true;
reset_console();
set_console_reset();
uint32_t len = snprintf(buf, 128, "Submenu 1:\r\n");
console_send((uint8_t*)buf, len);
}
void print_submenu_two(void)
{
char buf[128];
m_echo_console = true;
reset_console();
set_console_reset();
uint32_t len = snprintf(buf, 128, "Submenu 2:\r\n");
console_send((uint8_t*)buf, len);
}
void print_submenu_three(void)
{
char buf[128];
m_echo_console = true;
reset_console();
set_console_reset();
uint32_t len = snprintf(buf, 128, "Submenu 3:\r\n");
console_send((uint8_t*)buf, len);
}
void print_saved_menu(int32_t val)
{
char buf[128];
uint32_t len;
reset_console();
set_console_green();
if (0 == val)
{
len = snprintf(buf, 128, "SAVED!!1!");
}
else
{
len = snprintf(buf, 128, "SAVE FAILED!");
}
console_send((uint8_t*)buf, len);
}
void set_console_reset(void)
{
char buf[16] = {0};
uint32_t len = snprintf(buf, 16, "\x1b[1;0m");
console_send((uint8_t*)buf, len);
}
void set_console_red(void)
{
char buf[16] = {0};
uint32_t len = snprintf(buf, 16, "\x1b[1;31m");
console_send((uint8_t*)buf, len);
}
void set_console_green(void)
{
char buf[16] = {0};
uint32_t len = snprintf(buf, 16, "\x1b[1;32m");
console_send((uint8_t*)buf, len);
}
void reset_console(void)
{
char buf[16] = {0};
uint32_t len = snprintf(buf, 16, "\x1b[2J\x1b[H");
console_send((uint8_t*)buf, len);
}