#pragma once #include #include #include #include #include #include "debug_auth.h" #include "service.h" #include "services/storage_service.h" #include "services/wifi_service.h" #include "settings.h" #include "ui/canvas.h" class NetworkClient; namespace roro { // The console over Wi-Fi, on TCP 2323 while Wi-Fi is Connected, in every build but only while it's // switched on in Settings (ADR 0010): off, nothing listens, and neither its task nor the console's // ring exists. A client answers a challenge with its token (debug_auth.h), then gets the recent // console backlog, every new console line, and runs the same commands as the serial port. One // client at a time. // // The socket lives on this Service's own task; commands are handed to the main loop (takeCommand), // which runs them where touching Apps and Services is safe. Their output reaches the client // through the console ring like everything else. class DebugConsole : public Service { public: static constexpr uint16_t kPort = 2323; DebugConsole(WifiService& wifi, StorageService& storage, Settings& settings); // The off-screen frame the UI composes into: `screenshot` sends it as it stands. void setFrame(Canvas& frame) { frame_ = &frame; } const char* name() const override { return "debug"; } void start() override { apply(); } // Follows the settings: starts or stops listening, and takes a changed token (which drops a client). void tick(uint32_t nowMs) override; // Switches the console on, making a token first if there's none (Q191). The settings are what // counts: this only writes them. static void switchOn(Settings& settings); static std::string freshToken(); // The next command line a client sent, for the main loop to run. bool takeCommand(std::string& line); // News for the user that the task can't publish itself (a pause after wrong tokens). bool takeAlert(std::string& text); bool on() const { return wanted_; } bool clientConnected() const { return connected_; } private: static void taskEntry(void* self); void apply(); void listen(); void serve(::NetworkClient& client); bool authenticate(::NetworkClient& client); // Binary commands, answered on this task: true if `line` was one. bool binaryCommand(::NetworkClient& client, const std::string& line); // Runs `job` on the storage task (where card access is safe) and waits for it. False if the // card isn't mounted, in which case the storage task never runs it. bool onStorage(std::function job); void get(::NetworkClient& client, const std::string& path); void put(::NetworkClient& client, const std::string& args); void screenshot(::NetworkClient& client); WifiService& wifi_; StorageService& storage_; Settings& settings_; Canvas* frame_ = nullptr; volatile TaskHandle_t task_ = nullptr; // null once the task has freed everything and gone SemaphoreHandle_t lock_; // commands_, token_, alert_ std::deque commands_; std::string token_, alert_; volatile uint32_t tokenSeq_ = 0; // changes with the token: an open connection ends volatile bool wanted_ = false; volatile bool connected_ = false; debug::AuthGate gate_; // the task's own }; } // namespace roro