#ifndef ESP32DISPLAYTEST_LIB_COMMANDER_COMMANDER_H_ #define ESP32DISPLAYTEST_LIB_COMMANDER_COMMANDER_H_ #include #include #include #include #include #include #include class CmdString : public String { public: CmdString(const char *cstr = "") : String(cstr){} CmdString(const String& str) : String(str){} template bool ReadInto(T *out) const { static_assert(!std::is_same::value, "64-bit not supported"); static_assert(!std::is_same::value, "64-bit not supported"); static_assert(std::is_integral::value, "integer type required"); if (isEmpty()) return false; int64_t res; int i = 0; if (!std::is_unsigned::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::max() || res < std::numeric_limits::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, CommanderClient *)> handler_; std::vector commands_; bool shell_; String prompt_ = "> "; String welcome_message_ = ""; Command(const String &name, std::function, 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 cmdline, CommanderClient *client) const; void DefaultHandler(const std::vector &cmdline, CommanderClient *client) const; void DefaultHelpHandler(CommanderClient *client) const; public: Command *RegisterCommand(const String &name, const std::function &, 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 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 &, 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 cmd, CommanderClient *client) { } }; class CommanderClient : public Stream { protected: std::vector shell_stack_; bool in_command_ = false; String buffer_ = ""; std::vector 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 bool ReadLine(T *out) { static_assert(!std::is_same::value, "64-bit not supported"); static_assert(!std::is_same::value, "64-bit not supported"); static_assert(std::is_integral::value, "integer type required"); CmdString line = ReadLine(); if (line.isEmpty()) return false; int64_t res; int i = 0; if (!std::is_unsigned::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::max() || res < std::numeric_limits::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_