including tftp based shell and sockets from different project
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* @file cliWrapper.hpp
|
||||
* @author Gustice
|
||||
* @brief Wrapping Command-Line-Interfaces with custom endpoints
|
||||
* @details Command-Line-Interface is based on the implementation of
|
||||
* https://github.com/funbiscuit/embedded-cli
|
||||
*
|
||||
* @copyright Copyright (c) 2024
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "embedded_cli.h"
|
||||
#include <esp_log.h>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include "utils.hpp"
|
||||
|
||||
/* Module configuration */
|
||||
static constexpr int CLI_RX_BUFFER_SIZE = 64;
|
||||
static constexpr int CLI_CMD_BUFFER_SIZE = 64; // to also accept URLs for Updates
|
||||
static constexpr int CLI_HISTORY_SIZE = 16;
|
||||
static constexpr int CLI_BINDING_COUNT = 8;
|
||||
static constexpr std::string_view CliWelcomeString{
|
||||
"** Smart-DMX-Bridge **\n"
|
||||
" send 'help' for help-page\n"
|
||||
" NOTE: Syslog-messages are sent to client of this session\n"};
|
||||
|
||||
/**
|
||||
* @brief Wrapper for c-implementation of CLI
|
||||
*/
|
||||
class Cli {
|
||||
public:
|
||||
/// @brief Type for callback arguments
|
||||
using CArgs = std::vector<std::string> &;
|
||||
/// @brief Type for bind callback
|
||||
using BindCb = std::function<void(CArgs)>;
|
||||
/// @brief Type for command callback
|
||||
using CommandCb = std::function<void(std::string, CArgs)>;
|
||||
|
||||
/// @brief Parameter structure for command-binding
|
||||
struct Binding {
|
||||
std::string_view name;
|
||||
std::string_view help_output;
|
||||
BindCb callback;
|
||||
};
|
||||
|
||||
/// @brief Constructor
|
||||
/// @param port Reference to serial port
|
||||
/// @param cmd command callback
|
||||
Cli(CharStream &port, CommandCb cmd)
|
||||
: _port(port), _commandCb(cmd) {
|
||||
EmbeddedCliConfig *config = embeddedCliDefaultConfig();
|
||||
// NOTE: Note setting cliBuffer allows Cli to allocate needed buffer in correct size
|
||||
// config->cliBuffer = cliBuffer;
|
||||
// config->cliBufferSize = CLI_BUFFER_SIZE;
|
||||
config->rxBufferSize = CLI_RX_BUFFER_SIZE;
|
||||
config->cmdBufferSize = CLI_CMD_BUFFER_SIZE;
|
||||
config->historyBufferSize = CLI_HISTORY_SIZE;
|
||||
config->maxBindingCount = CLI_BINDING_COUNT;
|
||||
cli = embeddedCliNew(config);
|
||||
|
||||
if (cli == NULL) {
|
||||
ESP_LOGE("CLI", "Cli was not created, requires %d bytes. Check sizes!",
|
||||
embeddedCliRequiredSize(config));
|
||||
return;
|
||||
}
|
||||
cli->onCommand = genericCallback;
|
||||
cli->writeChar = writeToken;
|
||||
// appContext is a user-end variable and is bound to current instance
|
||||
cli->appContext = this;
|
||||
}
|
||||
|
||||
/// @brief Destructor
|
||||
~Cli() {
|
||||
embeddedCliFree(cli);
|
||||
}
|
||||
|
||||
/// @brief Create command binding
|
||||
bool addBinding(Binding bind) {
|
||||
BindCb *cb = new BindCb{bind.callback};
|
||||
|
||||
CliCommandBinding cBinding{.name = bind.name.begin(),
|
||||
.help = bind.help_output.begin(),
|
||||
.tokenizeArgs = true,
|
||||
// NOTE: this hard reference only works because we only provide
|
||||
// an add-function without remove
|
||||
.context = (void *)(cb),
|
||||
.binding = boundCallback};
|
||||
return embeddedCliAddBinding(cli, cBinding);
|
||||
}
|
||||
|
||||
/// @brief Static method to generate c-compatible function-pointer for bound-command-callback
|
||||
/// @details - receives and wraps arguments in string,
|
||||
/// - determines object according to appContext
|
||||
/// - calls appropriate method of appropriate object
|
||||
static void boundCallback(EmbeddedCli *cli, char *args, void *context) {
|
||||
if (context == nullptr) {
|
||||
return;
|
||||
ESP_LOGE("CLI", "No context in callback available");
|
||||
}
|
||||
ESP_LOGD("CLI", "Received command:");
|
||||
|
||||
BindCb &cb = *((BindCb *)context);
|
||||
uint len = embeddedCliGetTokenCount(args);
|
||||
std::vector<std::string> sArgs;
|
||||
while (len-- > 0) {
|
||||
const char *const arg = embeddedCliGetToken(args, 1);
|
||||
ESP_LOGD("CLI", " arg: %s", arg);
|
||||
sArgs.emplace_back(arg);
|
||||
// shift position to end of this element to save time
|
||||
args += sArgs.back().size() + 1; // needs also to be shifted by null-delimiter
|
||||
}
|
||||
cb(sArgs);
|
||||
}
|
||||
|
||||
/// @brief Static method to generate c-compatible function-pointer for command-callback
|
||||
/// @details - receives and wraps arguments in string,
|
||||
/// - determines object according to appContext
|
||||
/// - calls command-callback of appropriate object
|
||||
static void genericCallback(EmbeddedCli *cli, CliCommand *command) {
|
||||
std::string name(command->name);
|
||||
ESP_LOGI("CLI", "Received generic command: %s", name.c_str());
|
||||
|
||||
embeddedCliTokenizeArgs(command->args);
|
||||
std::vector<std::string> sArgs;
|
||||
for (int i = 1; i <= embeddedCliGetTokenCount(command->args); ++i) {
|
||||
sArgs.emplace_back(embeddedCliGetToken(command->args, i));
|
||||
ESP_LOGI("CLI", " arg %d : %s", i, sArgs.back().c_str());
|
||||
}
|
||||
auto &commandCb = static_cast<Cli *>(cli->appContext)->_commandCb;
|
||||
commandCb(name, sArgs);
|
||||
}
|
||||
|
||||
/// @brief Process buffer and trigger commands
|
||||
void process() {
|
||||
auto r = _port.read();
|
||||
for (auto &&c : r) {
|
||||
embeddedCliReceiveChar(cli, c);
|
||||
}
|
||||
embeddedCliProcess(cli);
|
||||
}
|
||||
|
||||
/// @brief publicly exposed write method
|
||||
void write(std::string output) {
|
||||
_port.write(output);
|
||||
}
|
||||
|
||||
private:
|
||||
/// @brief Reference to hardware port
|
||||
CharStream &_port;
|
||||
/// @brief Callback for generic command
|
||||
CommandCb _commandCb;
|
||||
EmbeddedCli *cli = nullptr;
|
||||
|
||||
/// @brief Write callback
|
||||
static void writeToken(EmbeddedCli *embeddedCli, char c) {
|
||||
static_cast<Cli *>(embeddedCli->appContext)->_port.write(c);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user