feat: working test
This commit is contained in:
464
prog/terminal.hpp
Normal file
464
prog/terminal.hpp
Normal file
@ -0,0 +1,464 @@
|
||||
#pragma once
|
||||
/*
|
||||
* Copyright (C) 2015-2019 Grégoire Passault <gregoire.passault@u-bordeaux.fr>
|
||||
* Copyright (C) 2019-2025 Adrien Boussicault <adrien.boussicault@labri.fr>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation, either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program. If not, see
|
||||
* <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <Arduino.h>
|
||||
#include <Preferences.h>
|
||||
#include "serial_print.hpp"
|
||||
|
||||
namespace terminal_n {
|
||||
|
||||
enum paramter_type {
|
||||
NONE_PARAMETER,
|
||||
FLOAT_PARAMETER,
|
||||
DOUBLE_PARAMETER,
|
||||
INT_PARAMETER,
|
||||
UINT_PARAMETER,
|
||||
LONGINT_PARAMETER,
|
||||
LONGUINT_PARAMETER,
|
||||
BOOL_PARAMETER
|
||||
};
|
||||
/**
|
||||
* To defined a new command, use the MACRO
|
||||
* TERMINAL_COMMAND(terminal, name, description)
|
||||
*
|
||||
* @param name : the name of the command, without space, without quotes
|
||||
* @param description : the description of the command, with quote
|
||||
*
|
||||
* use terminal_print() to print on output
|
||||
* use the variables arc and argv to get the string of given arguments
|
||||
*
|
||||
* If you declared the terminal in the following way, in another file :
|
||||
*
|
||||
* #include "terminal.hpp"
|
||||
* terminal_n::Terminal terminal("The terminal\n\r");
|
||||
*
|
||||
* you can add that command in other file :
|
||||
*
|
||||
* #include "terminal.hpp"
|
||||
* extern terminal_n::Terminal terminal;
|
||||
*
|
||||
* TERMINAL_COMMAND(terminal, hello, "Print a friendly warming welcoming message")
|
||||
* {
|
||||
* if (argc > 0) {
|
||||
* terminal.print("Hello ");
|
||||
* terminal.println(argv[0]);
|
||||
* terminal.print("Other params: ");
|
||||
* for (uint32_t i=1;i<argc;i++) {
|
||||
* terminal.print(argv[i]);
|
||||
* terminal.print(" ");
|
||||
* }
|
||||
* terminal.println("");
|
||||
* } else {
|
||||
* terminal.println("Hello world");
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*
|
||||
* WARNING: In order for the commands to be automatically registered with the
|
||||
* terminal at startup, the file containing the setup() and loop() functions
|
||||
* must be placed last in the list of files during the linking stage.
|
||||
*
|
||||
* In PlatformIO, to do this, simply begin the name of the file containing the
|
||||
* loop() and setup() functions with the letter z.
|
||||
*
|
||||
* For example: zzz_main.cpp.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
const constexpr int MAX_EEPROM_NAME_LENGTH = 15;
|
||||
|
||||
/**
|
||||
* Prototype of a terminal command
|
||||
*/
|
||||
typedef void terminal_command_fn(uint32_t argc, char *argv[]);
|
||||
|
||||
/**
|
||||
* A command definition for the terminal
|
||||
*/
|
||||
struct terminal_command
|
||||
{
|
||||
char *name;
|
||||
char *description;
|
||||
terminal_command_fn *command;
|
||||
paramter_type parameter;
|
||||
char *parameter_type;
|
||||
bool auto_save_after_command;
|
||||
volatile void* value_ptr;
|
||||
char eeprom_name[MAX_EEPROM_NAME_LENGTH+1];
|
||||
};
|
||||
|
||||
inline float atof(char *str){
|
||||
int32_t sign = (str[0]=='-') ? -1 : 1;
|
||||
float f = atoi(str);
|
||||
char *savePtr;
|
||||
strtok_r(str, ".", &savePtr);
|
||||
char *floatPart = strtok_r(NULL, ".", &savePtr);
|
||||
if (floatPart != NULL) {
|
||||
float divide = 1;
|
||||
for (char *tmp=floatPart; *tmp!='\0'; tmp++) {
|
||||
divide *= 10;
|
||||
}
|
||||
f += sign*atoi(floatPart)/divide;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
#ifndef PREFERENCES_NAMESPACE
|
||||
#define PREFERENCES_NAMESPACE "terminal"
|
||||
#endif
|
||||
#ifndef PREFERENCES_NVS_PART
|
||||
#define PREFERENCES_NVS_PARTITION "nvs"
|
||||
#endif
|
||||
#ifndef TERMINAL_BUFFER_SIZE
|
||||
/**
|
||||
* Maximum length of a command line
|
||||
* and its argument
|
||||
*/
|
||||
#define TERMINAL_BUFFER_SIZE 64
|
||||
#endif
|
||||
#ifndef TERMINAL_MAX_ARGUMENTS
|
||||
/**
|
||||
* Maximum number of command arguments
|
||||
*/
|
||||
#define TERMINAL_MAX_ARGUMENTS 12
|
||||
#endif
|
||||
#ifndef TERMINAL_MAX_COMMANDS
|
||||
/**
|
||||
* Maximum number of commands
|
||||
* which ca be registered
|
||||
*/
|
||||
#define TERMINAL_MAX_COMMANDS 100
|
||||
#endif
|
||||
|
||||
struct Terminal {
|
||||
const char* documentation;
|
||||
Preferences preferences;
|
||||
static const constexpr char* preferences_namespace = PREFERENCES_NAMESPACE;
|
||||
static const constexpr char* preferences_nvs_partition = PREFERENCES_NVS_PARTITION;
|
||||
|
||||
static constexpr const int terminal_buffer_size = TERMINAL_BUFFER_SIZE;
|
||||
static constexpr const int terminal_max_arguments = TERMINAL_MAX_ARGUMENTS;
|
||||
static constexpr const int terminal_max_commands = TERMINAL_MAX_COMMANDS;
|
||||
|
||||
char terminal_buffer[terminal_buffer_size] = {0};
|
||||
|
||||
HardwareSerial* io = 0;
|
||||
bool silent;
|
||||
bool disabled = false;
|
||||
bool terminal_last_ok = false;
|
||||
uint32_t terminal_last_pos = 0;
|
||||
uint32_t terminal_pos = 0;
|
||||
bool terminal_echo_mode = true;
|
||||
|
||||
uint32_t terminal_command_count = 0;
|
||||
const struct terminal_command *terminal_commands[terminal_max_commands];
|
||||
|
||||
static constexpr const char* PROMPT = "$ ";
|
||||
|
||||
Terminal(const char* documentation=""):
|
||||
documentation(documentation)
|
||||
{}
|
||||
|
||||
inline void write_char(char c){
|
||||
if(!silent){ io->write(c); }
|
||||
}
|
||||
inline void write(const char* buf, uint32_t len){
|
||||
if(!silent){ io->write((const uint8_t*)buf, len); }
|
||||
}
|
||||
template <typename T>
|
||||
inline void print(T v){
|
||||
if(!silent){ serial_print(io, v); }
|
||||
}
|
||||
template <typename T>
|
||||
inline void println(T v){
|
||||
if(!silent){ serial_println(io, v); }
|
||||
}
|
||||
template <typename T>
|
||||
inline void print_hex(T v){
|
||||
if(!silent){ serial_print_hex(io, v); }
|
||||
}
|
||||
template <typename T>
|
||||
inline void println_hex(T v){
|
||||
if(!silent){ serial_println_hex(io, v); }
|
||||
}
|
||||
inline bool terminal_has_io(){
|
||||
return io;
|
||||
}
|
||||
inline void print_hexa(uint8_t* buf, uint32_t n){
|
||||
uint8_t first=0, last=0;
|
||||
for( uint32_t i=0; i<n; i++ ){
|
||||
first = buf[i] >> 4;
|
||||
last = buf[i] & 0x0F;
|
||||
print( 'x' );
|
||||
print( static_cast<char>( (first < 10 ? '0' : 'A'-10) + first ) );
|
||||
print( static_cast<char>( (last < 10 ? '0' : 'A'-10) + last ) );
|
||||
}
|
||||
}
|
||||
inline void println_hexa(uint8_t* buf, uint32_t n){
|
||||
print_hexa(buf,1);
|
||||
println("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the terminal
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Enable the terminal
|
||||
*/
|
||||
void enable();
|
||||
|
||||
/**
|
||||
* Disable the terminal
|
||||
*/
|
||||
void disable();
|
||||
|
||||
void print_prompt();
|
||||
|
||||
void setup(HardwareSerial* serial);
|
||||
/**
|
||||
* Terminal update
|
||||
* Call this function in your main loop
|
||||
* to fetch serial port and handle terminal
|
||||
*/
|
||||
void update();
|
||||
|
||||
const terminal_command * find_command(
|
||||
char *command_name, uint32_t command_name_length
|
||||
);
|
||||
|
||||
bool terminal_execute(
|
||||
char *command_name, uint32_t command_name_length, uint32_t argc, char **argv
|
||||
);
|
||||
|
||||
void terminal_process();
|
||||
|
||||
bool have_to_be_saved_in_eeprom(const terminal_command *command);
|
||||
void check_eeprom_names();
|
||||
void save_value_to_eeprom(const terminal_command *command);
|
||||
bool save_config_to_eeprom(const char* eeprom_name);
|
||||
void save_config_to_eeprom();
|
||||
void load_config_from_eeprom(bool enable_print=true);
|
||||
bool load_config_from_eeprom(const char* eeprom_name, bool enable_print);
|
||||
void load_value_from_eeprom(
|
||||
const terminal_command *command, bool enable_print=false
|
||||
);
|
||||
void print_saved_parameter(const terminal_command *command);
|
||||
void print_value_from_eeprom(const struct terminal_command *command);
|
||||
void print_config_from_eeprom();
|
||||
bool print_config_from_eeprom(const char* eeprom_name);
|
||||
void reset_to_constructor_config();
|
||||
bool reset_to_constructor_config(const char* eeprom_name);
|
||||
|
||||
|
||||
/**
|
||||
* Registers a command
|
||||
*/
|
||||
void register_command(struct terminal_command *command);
|
||||
|
||||
|
||||
/**
|
||||
* Mute the terminal
|
||||
*/
|
||||
void terminal_silent(bool silent);
|
||||
void displayHelp(bool parameter);
|
||||
void terminal_params_show();
|
||||
|
||||
void echo_command(uint32_t argc, char *argv[]);
|
||||
void params_command(uint32_t argc, char *argv[]);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* ----------------------------------------------------------------------------
|
||||
* ----------------------------------------------------------------------------
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#define TERMINAL_COMMAND_INTERNAL(term, name, eeprom_name, description, parameter, parameterType, auto_save_after_command, value_ptr) terminal_n::terminal_command_fn terminal_command_ ## name; \
|
||||
\
|
||||
char terminal_command_name_ ## name [] = #name; \
|
||||
char terminal_command_description_ ## name [] = description; \
|
||||
\
|
||||
struct terminal_n::terminal_command terminal_command_definition_ ## name = { \
|
||||
terminal_command_name_ ## name , \
|
||||
terminal_command_description_ ## name , \
|
||||
terminal_command_ ## name, \
|
||||
parameter, \
|
||||
parameterType, \
|
||||
auto_save_after_command, \
|
||||
value_ptr, \
|
||||
eeprom_name \
|
||||
}; \
|
||||
\
|
||||
__attribute__((constructor)) \
|
||||
void terminal_command_init_ ## name () { \
|
||||
term.register_command(&terminal_command_definition_ ## name ); \
|
||||
} \
|
||||
\
|
||||
void terminal_command_ ## name (uint32_t argc, char *argv[])
|
||||
|
||||
#define TERMINAL_COMMAND(term, name, description) \
|
||||
TERMINAL_COMMAND_INTERNAL(term, name, "", description, terminal_n::NONE_PARAMETER, NULL, false, NULL)
|
||||
|
||||
|
||||
|
||||
#define TERMINAL_PARAMETER(term, name, eeprom_name, description, startValue, type, enum_type, conversion, auto_save_after_command, filter_function, finish_function) \
|
||||
volatile static type name = startValue; \
|
||||
char terminal_parameter_type_ ## name [] = #type; \
|
||||
\
|
||||
TERMINAL_COMMAND_INTERNAL(term, name, eeprom_name, description, enum_type, terminal_parameter_type_ ## name, auto_save_after_command, &name) \
|
||||
{ \
|
||||
type g; \
|
||||
if (argc) { \
|
||||
g = conversion(argv[0]); \
|
||||
if( filter_function(g, name) ){ \
|
||||
type old = name; \
|
||||
name = g; \
|
||||
finish_function(name, old); \
|
||||
} \
|
||||
} \
|
||||
term.print( #name ); \
|
||||
term.print("="); \
|
||||
term.println( name ); \
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline bool filter_nothing(T & new_value, T old_value){return true;}
|
||||
|
||||
template <typename T>
|
||||
inline void do_nothing(T new_value, T old_value){}
|
||||
|
||||
#define TERMINAL_PARAMETER_FLOAT(term, name, eeprom_name, description, startValue, auto_save_after_command, filter_function, finish_function) \
|
||||
TERMINAL_PARAMETER(term, name, eeprom_name, description, startValue, float, terminal_n::FLOAT_PARAMETER, terminal_n::atof, auto_save_after_command, filter_function, finish_function)
|
||||
|
||||
#define TERMINAL_PARAMETER_DOUBLE(term, name, eeprom_name, description, startValue, auto_save_after_command, filter_function, finish_function) \
|
||||
TERMINAL_PARAMETER(term, name, eeprom_name, description, startValue, double, terminal_n::DOUBLE_PARAMETER, terminal_n::atof, auto_save_after_command, filter_function, finish_function)
|
||||
|
||||
#define TERMINAL_PARAMETER_INT(term, name, eeprom_name, description, startValue, auto_save_after_command, filter_function, finish_function) \
|
||||
TERMINAL_PARAMETER(term, name, eeprom_name, description, startValue, int32_t, terminal_n::INT_PARAMETER, atoi, auto_save_after_command, filter_function, finish_function)
|
||||
|
||||
|
||||
#define TERMINAL_PARAMETER_UINT(term, name, eeprom_name, description, startValue, auto_save_after_command, filter_function, finish_function) \
|
||||
TERMINAL_PARAMETER(term, name, eeprom_name, description, startValue, uint32_t, terminal_n::UINT_PARAMETER, atoi, auto_save_after_command, filter_function, finish_function)
|
||||
|
||||
#define TERMINAL_PARAMETER_LONGINT(term, name, eeprom_name, description, startValue, auto_save_after_command, filter_function, finish_function) \
|
||||
TERMINAL_PARAMETER(term, name, eeprom_name, description, startValue, int64_t, terminal_n::LONGINT_PARAMETER, atoi, auto_save_after_command, filter_function, finish_function)
|
||||
|
||||
#define TERMINAL_PARAMETER_LONGUINT(term, name, eeprom_name, description, startValue, auto_save_after_command, filter_function, finish_function) \
|
||||
TERMINAL_PARAMETER(term, name, eeprom_name, description, startValue, uint64_t, terminal_n::LONGUINT_PARAMETER, atoi, auto_save_after_command, filter_function, finish_function)
|
||||
|
||||
#define TERMINAL_PARAMETER_BOOL(term, name, eeprom_name, description, startValue, auto_save_after_command, filter_function, finish_function) \
|
||||
TERMINAL_PARAMETER(term, name, eeprom_name, description, startValue, bool, terminal_n::BOOL_PARAMETER, (bool)atoi, auto_save_after_command, filter_function, finish_function)
|
||||
|
||||
#define TERMINAL_ADD_DEFAULT_COMMANDS(term) \
|
||||
TERMINAL_COMMAND(term, save_config, "Save the config to EEPROM (save_config [eeprom_name])") \
|
||||
{ \
|
||||
if(argc !=0 and argc != 1){ term.println("Invalid parameters"); return; } \
|
||||
if(argc == 0){ \
|
||||
term.save_config_to_eeprom(); \
|
||||
term.println("All is saved."); \
|
||||
}else{ \
|
||||
if( \
|
||||
term.save_config_to_eeprom(argv[0]) \
|
||||
){ \
|
||||
term.println("Saved.");\
|
||||
}else{ \
|
||||
term.println("Failed.");\
|
||||
}; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
TERMINAL_COMMAND(term, load_config, "Load config from EEPROM (load_config [eeprom_name])") \
|
||||
{ \
|
||||
bool print = true; \
|
||||
if(argc !=0 and argc != 1){ term.println("Invalid parameters"); return; } \
|
||||
if(argc == 0){ \
|
||||
term.load_config_from_eeprom(print); \
|
||||
}else{ \
|
||||
if( \
|
||||
! term.load_config_from_eeprom(argv[0], print) \
|
||||
){ \
|
||||
term.println("Failed.");\
|
||||
}; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
TERMINAL_COMMAND(term, reset_config, "Reset to constructor configuration (reset_config [eeprom_name])") \
|
||||
{ \
|
||||
if(argc !=0 and argc != 1){ term.println("Invalid parameters"); return; } \
|
||||
if(argc == 0){ \
|
||||
term.reset_to_constructor_config(); \
|
||||
term.println("All is reseted."); \
|
||||
}else{ \
|
||||
if( \
|
||||
term.reset_to_constructor_config(argv[0]) \
|
||||
){ \
|
||||
term.println("Reseted.");\
|
||||
}else{ \
|
||||
term.println("Failed.");\
|
||||
}; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
TERMINAL_COMMAND(term, print_config, "Print EEPROM configuration (print_config [eeprom_name])") \
|
||||
{ \
|
||||
if(argc !=0 and argc != 1){ term.println("Invalid parameters"); return; } \
|
||||
if(argc == 0){ \
|
||||
term.print_config_from_eeprom(); \
|
||||
}else{ \
|
||||
if( \
|
||||
! term.print_config_from_eeprom(argv[0]) \
|
||||
){ \
|
||||
term.println("Failed.");\
|
||||
}; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
TERMINAL_COMMAND(term, help, "Displays the help about commands") \
|
||||
{ \
|
||||
term.displayHelp(false); \
|
||||
} \
|
||||
\
|
||||
TERMINAL_COMMAND(term, params, "Displays the available parameters. Usage: params [show]") \
|
||||
{ \
|
||||
term.params_command(argc, argv); \
|
||||
} \
|
||||
\
|
||||
TERMINAL_COMMAND(term, echo, "Switch echo mode. Usage echo [on|off]") \
|
||||
{ \
|
||||
term.echo_command(argc, argv); \
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user