esp32displaytest/lib/Commander/Commander.h
2022-03-12 12:33:52 +01:00

319 lines
6.7 KiB
C++

#ifndef ESP32DISPLAYTEST_LIB_COMMANDER_COMMANDER_H_
#define ESP32DISPLAYTEST_LIB_COMMANDER_COMMANDER_H_
#include <functional>
#include <utility>
#include <vector>
#include <WString.h>
#include <WiFiServer.h>
#include <AsyncTCP.h>
#include <Stream.h>
class CmdString : public String {
public:
CmdString(const char *cstr = "") : String(cstr){}
CmdString(const String& str) : String(str){}
template<typename T>
bool ReadInto(T *out) const {
static_assert(!std::is_same<T, int64_t>::value, "64-bit not supported");
static_assert(!std::is_same<T, uint64_t>::value, "64-bit not supported");
static_assert(std::is_integral<T>::value, "integer type required");
if (isEmpty()) return false;
int64_t res;
int i = 0;
if (!std::is_unsigned<T>::value) {
bool negative = false;
if (this->charAt(0) == '-') {
i = 1;
negative = true;
if (length() < 2) return false;
}
char ch = this->charAt(i);
if (ch >= '0' && ch <= '9') {
res = ch - '0';
if (negative) {
res = -res;
}
} else {
return false;
};
i += 1;
}
for (; i < length(); i++) {
char ch = this->charAt(i);
if (ch >= '0' && ch <= '9') {
uint8_t digit = ch - '0';
res *= 10;
res += digit;
if (res > std::numeric_limits<T>::max() || res < std::numeric_limits<T>::min()) {
return false;
}
} else {
return false;
}
}
*out = res;
return true;
}
};
class Commander;
class Command;
class CommanderClient;
class SerialCommanderClient;
class WifiCommanderClient;
class Command {
friend class CommanderClient;
protected:
String name_;
String description_;
std::function<void(std::vector<CmdString>, CommanderClient *)> handler_;
std::vector<Command> commands_;
bool shell_;
String prompt_ = "> ";
String welcome_message_ = "";
Command(const String &name, std::function<void(std::vector<CmdString>, CommanderClient *)> handler, bool shell)
: handler_(std::move(handler)), shell_(shell) {
int space_index = name.indexOf(' ');
if (space_index == -1) {
name_ = name;
description_ = "";
} else {
name_ = name.substring(0, space_index);
description_ = name.substring(space_index);
}
}
const Command* Dispatch(std::vector<CmdString> cmdline, CommanderClient *client) const;
void DefaultHandler(const std::vector<CmdString> &cmdline, CommanderClient *client) const;
void DefaultHelpHandler(CommanderClient *client) const;
public:
Command *RegisterCommand(const String &name,
const std::function<void(const std::vector<CmdString> &,
CommanderClient *)> &handler);
Command *RegisterCommandWithShell(const String &name);
void SetPrompt(const String &prompt);
void SetWelcomeMessage(const String &message);
};
class Commander : public Command {
friend CommanderClient;
private:
bool enable_wifi_ = false;
bool enable_serial_ = false;
uint16_t port_ = 0;
AsyncServer *tcp_server_{};
std::vector<WifiCommanderClient> wifi_clients_;
SerialCommanderClient *serial_client_{};
bool running_ = false;
static Commander *serial_instance_;
TaskHandle_t task_handle_{};
public:
Commander() : Command("", Commander::DefaultFallbackHandler, false) {}
using Command::RegisterCommand;
using Command::RegisterCommandWithShell;
void EnableSerial();
void EnableWifi(uint16_t port);
void SetFallbackHandler(const std::function<void(const std::vector<CmdString> &,
CommanderClient *)> &handler);
void Begin();
void Stop();
private:
void WifiOnClient(AsyncClient *client);
void SerialReceive();
[[noreturn]] void Task();
static void SerialReceiveHandler();
static void WifiOnClientHandler(void *thisarg, AsyncClient *client);
[[noreturn]] static void TaskFn(void *thisarg);
static void DefaultFallbackHandler(std::vector<CmdString> cmd, CommanderClient *client) {
}
};
class CommanderClient : public Stream {
protected:
std::vector<const Command *> shell_stack_;
bool in_command_ = false;
String buffer_ = "";
std::vector<CmdString> current_line_;
bool in_quotes_ = false;
bool in_escape_ = false;
bool previous_was_cr_ = false;
TaskHandle_t task_handle_ = nullptr;
void BufferAppend(const String &str);
void BufferAppend(char ch);
void DipatchCurrentCommandLine();
void ProcessCommandLine();
const String &GetPrompt();
const String &GetWelcomeMessage();
virtual void Task() = 0;
static void TaskFn(void *thisarg) {
((CommanderClient *)thisarg)->Task();
}
public:
explicit CommanderClient(Commander *commander) {
shell_stack_.push_back(commander);
}
virtual void Begin() {
xTaskCreate(CommanderClient::TaskFn, "commander client", 10000, this, 1, &this->task_handle_);
}
CmdString ReadLine();
template<typename T>
bool ReadLine(T *out) {
static_assert(!std::is_same<T, int64_t>::value, "64-bit not supported");
static_assert(!std::is_same<T, uint64_t>::value, "64-bit not supported");
static_assert(std::is_integral<T>::value, "integer type required");
CmdString line = ReadLine();
if (line.isEmpty()) return false;
int64_t res;
int i = 0;
if (!std::is_unsigned<T>::value) {
bool negative = false;
if (line[0] == '-') {
i = 1;
negative = true;
if (line.length() < 2) return false;
}
char ch = line[i];
if (ch >= '0' && ch <= '9') {
res = ch - '0';
if (negative) {
res = -res;
}
} else {
return false;
};
i += 1;
}
for (; i < line.length(); i++) {
char ch = line[i];
if (ch >= '0' && ch <= '9') {
uint8_t digit = ch - '0';
res *= 10;
res += digit;
if (res > std::numeric_limits<T>::max() || res < std::numeric_limits<T>::min()) {
return false;
}
} else {
return false;
}
}
*out = res;
return true;
}
};
class SerialCommanderClient : public CommanderClient {
static SerialCommanderClient *single_instance_;
public:
explicit SerialCommanderClient(Commander *commander);
void OnReceive();
size_t write(uint8_t byte) override;
int available() override;
int read() override;
int peek() override;
void flush() override;
void Begin() override {
CommanderClient::Begin();
Serial.onReceive(SerialCommanderClient::OnReceiveHandler);
}
[[noreturn]] void Task() override;
static void OnReceiveHandler() {
single_instance_->OnReceive();
}
};
class WifiCommanderClient : public CommanderClient {
AsyncClient *async_client_;
public:
explicit WifiCommanderClient(Commander *commander, AsyncClient *client);
void OnData(void *data, size_t len);
size_t write(uint8_t byte) override;
int available() override;
int read() override;
int peek() override;
void flush() override;
void Task() {};
private:
static void OnDataHandler(void *thisarg, AsyncClient *, void *data, size_t len);
};
#endif //ESP32DISPLAYTEST_LIB_COMMANDER_COMMANDER_H_