inputs: serial: implement a CLI
This commit is contained in:
parent
ac94c4be0c
commit
4e56134dd9
@ -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);
|
||||||
|
@ -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);
|
||||||
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user