inputs: serial: implement a CLI

This commit is contained in:
Torrie Fischer 2023-12-11 07:57:22 +01:00
parent ac94c4be0c
commit 4e56134dd9
2 changed files with 210 additions and 26 deletions

View File

@ -1,33 +1,171 @@
#include "Serial.h" #include "Serial.h"
#include "../Static.h"
#include <LittleFS.h>
#include "../Config.h"
#include "../Sequencer.h"
InputEvent SerialInput::SerialInput() : InputSource("Serial"),
Serial::read() 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 void
Serial::doCommand() { SerialInput::redrawPrompt()
if (command == "tasks") { {
Serial.println("Tasks:"); if (m_canRedraw) {
auto sched = MainLoop::instance()->scheduler; Serial.print((char)8);
for(auto task : sched.tasks) { Serial.print((char)27);
bool isFigment = task->isFigment(); Serial.print("[2K");
if (isFigment) { Serial.print((char)8);
Serial.println("F " + task->name); Serial.print((char)27);
} else { Serial.print("[G");
Serial.println("T " + task->name); 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<Command> serialCommands = {
{"help", doHelp}
};
const std::vector<Command>&
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); STATIC_ALLOC(SerialInput);

View File

@ -3,14 +3,60 @@
class SerialInput: public InputSource { class SerialInput: public InputSource {
public: public:
void onStart() override { SerialInput();
//Serial.begin(); InputEvent read() override;
class LogPrinter : public Print {
private:
SerialInput* serial;
Ringbuf<char, 512> 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<Command> &commands() const override;
private: private:
enum ParseState {
Normal,
EscapeSequence,
CSI
};
String m_buf; String m_buf;
ParseState m_state;
char m_escapeSeq[3];
void doCommand(); void doCommand();
LogPrinter m_logPrinter;
bool m_canRedraw = true;
Ringbuf<String, 5> m_history;
int m_historyOffset = 0;
} InputEvent parseNormal(char nextChar);
InputEvent parseEscape(char nextChar);
InputEvent parseCSI(char nextChar);
};