From 4e56134dd9b1774d0e97841d2b977599659e3da9 Mon Sep 17 00:00:00 2001 From: Torrie Fischer Date: Mon, 11 Dec 2023 07:57:22 +0100 Subject: [PATCH] inputs: serial: implement a CLI --- src/inputs/Serial.cpp | 182 +++++++++++++++++++++++++++++++++++++----- src/inputs/Serial.h | 54 ++++++++++++- 2 files changed, 210 insertions(+), 26 deletions(-) diff --git a/src/inputs/Serial.cpp b/src/inputs/Serial.cpp index 2358665..bc30a66 100644 --- a/src/inputs/Serial.cpp +++ b/src/inputs/Serial.cpp @@ -1,33 +1,171 @@ #include "Serial.h" +#include "../Static.h" +#include +#include "../Config.h" +#include "../Sequencer.h" -InputEvent -Serial::read() +SerialInput::SerialInput() : InputSource("Serial"), + m_state(ParseState::Normal), + m_logPrinter(this) { - while (Serial.available() > 0) { - char nextChar = Serial.read(); - if (nextChar == '\n') { - doCommand(); - m_buf = ""; - } else { - m_buf += nextChar; - } - } } void -Serial::doCommand() { - if (command == "tasks") { - Serial.println("Tasks:"); - auto sched = MainLoop::instance()->scheduler; - for(auto task : sched.tasks) { - bool isFigment = task->isFigment(); - if (isFigment) { - Serial.println("F " + task->name); - } else { - Serial.println("T " + task->name); - } +SerialInput::redrawPrompt() +{ + if (m_canRedraw) { + Serial.print((char)8); + Serial.print((char)27); + Serial.print("[2K"); + Serial.print((char)8); + Serial.print((char)27); + Serial.print("[G"); + Serial.print('\r'); + Serial.print("> "); + Serial.print(m_buf); + } +} + +InputEvent +SerialInput::parseNormal(char nextChar) +{ + if (nextChar == 27) { + m_state = ParseState::EscapeSequence; + return InputEvent::None; + } + + if (nextChar == 13) { + redrawPrompt(); + Serial.println(); + if (m_buf.length() > 0) { + m_canRedraw = false; + doCommand(); + m_canRedraw = true; + m_history.insert(m_buf); + m_buf = ""; + } + m_historyOffset = 0; + redrawPrompt(); + return InputEvent{}; + } + + if (nextChar == 8) { + if (m_buf.length() > 0) { + m_buf.remove(m_buf.length() - 1, 1); + } + Serial.print((char)8); + Serial.print((char)27); + Serial.print("[K"); + redrawPrompt(); + return InputEvent::None; + } + + if (nextChar >= 32 && nextChar <= 126) { + m_buf += nextChar; + Serial.print(nextChar); + } + return InputEvent::None; +} + +InputEvent +SerialInput::parseEscape(char nextChar) +{ + if (nextChar == '[') { + m_state = ParseState::CSI; + } else { + m_state = ParseState::Normal; + } + return InputEvent::None; +} + +InputEvent +SerialInput::parseCSI(char nextChar) +{ + if (nextChar == 'A') { + if (m_historyOffset < m_history.size()) { + m_historyOffset += 1; + m_buf = m_history.peek(m_historyOffset); + redrawPrompt(); + } else { + Serial.print((char)7); + } + } else if (nextChar == 'B') { + if (m_historyOffset > 0) { + m_historyOffset -= 1; + m_buf = m_history.peek(m_historyOffset); + redrawPrompt(); + } else { + Serial.print((char)7); + } + } else { + Serial.print((char)7); + } + m_state = ParseState::Normal; + return InputEvent::None; +} + +InputEvent +SerialInput::read() +{ + while (Serial.available() > 0) { + char nextChar = Serial.read(); + InputEvent ret = InputEvent::None; + switch (m_state) { + case ParseState::Normal: + ret = parseNormal(nextChar);break; + case ParseState::EscapeSequence: + ret = parseEscape(nextChar);break; + case ParseState::CSI: + ret = parseCSI(nextChar);break; + } + if (ret != InputEvent::None) { + return ret; } } + return InputEvent::None; +} + +void +doHelp(Args& args, Print& out) +{ + out.println("Available commands:"); + auto sched = MainLoop::instance()->scheduler; + for(auto task : sched.tasks) { + for(auto &command : task->commands()) { + out.print(command.name); + out.print(" "); + } + } + out.println(); +} + +const std::vector serialCommands = { + {"help", doHelp} +}; + +const std::vector& +SerialInput::commands() const +{ + return serialCommands; +} + +void +SerialInput::doCommand() { + auto sched = MainLoop::instance()->scheduler; + Args args = Args(&m_buf); + const auto cmdName = args[0]; + + for(auto task : sched.tasks) { + for(auto &command : task->commands()) { + if (cmdName == command.name) { + command.func(args, m_logPrinter); + return; + } + } + } + + m_logPrinter.println("Unknown command"); + doHelp(args, m_logPrinter); } STATIC_ALLOC(SerialInput); diff --git a/src/inputs/Serial.h b/src/inputs/Serial.h index f6b7fee..6bbc286 100644 --- a/src/inputs/Serial.h +++ b/src/inputs/Serial.h @@ -3,14 +3,60 @@ class SerialInput: public InputSource { public: - void onStart() override { - //Serial.begin(); + SerialInput(); + InputEvent read() override; + + class LogPrinter : public Print { + private: + SerialInput* serial; + Ringbuf buf; + public: + LogPrinter(SerialInput* serial) : serial(serial) {}; + size_t write(uint8_t byte) { + if (byte == '\n') { + char c; + Serial.print('\r'); + while (buf.take(c)) { + Serial.write(c); + } + Serial.println(); + serial->redrawPrompt(); + } else { + buf.insert(byte); + } + return sizeof(byte); + } + }; + + void redrawPrompt(); + + Print* logPrinter() { + return &m_logPrinter; } - InputEvent read(); + //static SerialInput::Command *s_root; + LogPrinter* printer() { + return &m_logPrinter; + } + + const std::vector &commands() const override; private: + enum ParseState { + Normal, + EscapeSequence, + CSI + }; String m_buf; + ParseState m_state; + char m_escapeSeq[3]; void doCommand(); + LogPrinter m_logPrinter; + bool m_canRedraw = true; + Ringbuf m_history; + int m_historyOffset = 0; -} + InputEvent parseNormal(char nextChar); + InputEvent parseEscape(char nextChar); + InputEvent parseCSI(char nextChar); +};