2023-02-18 15:27:24 +00:00
|
|
|
#include "Serial.h"
|
2023-12-11 06:57:22 +00:00
|
|
|
#include "../Static.h"
|
|
|
|
#include <LittleFS.h>
|
|
|
|
#include "../Config.h"
|
|
|
|
#include "../Sequencer.h"
|
|
|
|
|
|
|
|
SerialInput::SerialInput() : InputSource("Serial"),
|
|
|
|
m_state(ParseState::Normal),
|
|
|
|
m_logPrinter(this)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2023-02-18 15:27:24 +00:00
|
|
|
|
|
|
|
InputEvent
|
2023-12-11 06:57:22 +00:00
|
|
|
SerialInput::parseNormal(char nextChar)
|
2023-02-18 15:27:24 +00:00
|
|
|
{
|
2023-12-11 06:57:22 +00:00
|
|
|
if (nextChar == 27) {
|
|
|
|
m_state = ParseState::EscapeSequence;
|
|
|
|
return InputEvent::None;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nextChar == 13) {
|
|
|
|
redrawPrompt();
|
|
|
|
Serial.println();
|
|
|
|
if (m_buf.length() > 0) {
|
|
|
|
m_canRedraw = false;
|
2023-02-18 15:27:24 +00:00
|
|
|
doCommand();
|
2023-12-11 06:57:22 +00:00
|
|
|
m_canRedraw = true;
|
|
|
|
m_history.insert(m_buf);
|
2023-02-18 15:27:24 +00:00
|
|
|
m_buf = "";
|
2023-12-11 06:57:22 +00:00
|
|
|
}
|
|
|
|
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();
|
2023-02-18 15:27:24 +00:00
|
|
|
} else {
|
2023-12-11 06:57:22 +00:00
|
|
|
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;
|
2023-02-18 15:27:24 +00:00
|
|
|
}
|
|
|
|
}
|
2023-12-11 06:57:22 +00:00
|
|
|
return InputEvent::None;
|
2023-02-18 15:27:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2023-12-11 06:57:22 +00:00
|
|
|
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;
|
|
|
|
}
|
2023-02-18 15:27:24 +00:00
|
|
|
}
|
|
|
|
}
|
2023-12-11 06:57:22 +00:00
|
|
|
|
|
|
|
m_logPrinter.println("Unknown command");
|
|
|
|
doHelp(args, m_logPrinter);
|
2023-02-18 15:27:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
STATIC_ALLOC(SerialInput);
|
|
|
|
STATIC_TASK(SerialInput);
|