This commit is contained in:
Dmitry Borisenko
2020-11-15 03:09:21 +03:00
parent 3766a744f4
commit 6f45148d75
11 changed files with 1 additions and 1020 deletions

View File

@@ -1,72 +0,0 @@
#pragma once
#include <Arduino.h>
class CharBuffer : Print {
public:
CharBuffer(size_t size) : _capacity(size < 2 ? 2 : size), _write(0), _read(0) {
_pool = new char[_capacity + 1];
memset(_pool, 0, _capacity + 1);
}
CharBuffer(const CharBuffer &src) {
_capacity = src._capacity;
_write = src._write;
memcpy(_pool, src._pool, src._write);
}
CharBuffer(const char *str) : CharBuffer(strlen(str) + 1) {
write((const uint8_t *)str, strlen(str));
}
~CharBuffer() {
delete _pool;
}
void clear() {
memset(_pool, 0, _capacity);
_write = 0;
_read = 0;
}
size_t size() const { return _capacity; }
size_t free() const { return _capacity - _write - 2; }
size_t available() const { return _write; }
const char *c_str() {
if (_pool[_write] != '\x00')
_pool[_write] = '\x00';
return _pool;
}
size_t write(char ch) {
return write((uint8_t)ch);
};
size_t write(const uint8_t ch) {
size_t n = 0;
if (_write < (_capacity - 2)) {
_pool[_write++] = ch;
n = 1;
}
return n;
}
size_t write(const uint8_t *ptr, const size_t size) {
size_t n = 0;
while (n < size) {
uint8_t ch = ptr[n++];
if (!write(ch))
break;
}
return n;
}
protected:
char *_pool;
size_t _capacity;
size_t _write;
size_t _read;
};

View File

@@ -1,86 +0,0 @@
#pragma once
#include <Arduino.h>
template <typename T, size_t BUFFER_SIZE>
class CircularBuffer {
public:
CircularBuffer() : _head{0}, _tail{0}, _full{false} {}
~CircularBuffer() {}
void reset() {
_head = _tail = _full = 0;
}
bool empty() const {
return _head == _tail && !_full;
}
bool full() const {
return _full;
}
size_t size() const {
size_t res = 0;
if (!_full) {
if (_head < _tail)
res = BUFFER_SIZE + _head - _tail;
else
res = _head - _tail;
} else {
res = BUFFER_SIZE;
}
return res;
}
void push(const T &item) {
if (_full) {
_tail++;
if (_tail == BUFFER_SIZE)
_tail = 0;
}
_pool[_head++] = item;
if (_head == BUFFER_SIZE)
_head = 0;
if (_head == _tail)
_full = true;
}
bool pop(T &item) {
bool res = false;
if (!empty()) {
item = _pool[_tail++];
if (_tail == BUFFER_SIZE) _tail = 0;
_full = false;
res = true;
}
return res;
}
bool pop_back(T &item) {
bool res = false;
if (!empty()) {
item = _pool[--_head];
_full = false;
res = true;
}
return res;
}
bool peek(T &item) const {
bool res = false;
if (!empty()) {
item = _pool[_tail];
res = true;
}
return res;
}
private:
T _pool[BUFFER_SIZE];
size_t _head;
size_t _tail;
bool _full;
};

View File

@@ -1,45 +0,0 @@
#pragma once
#include <Arduino.h>
#include "Module/Terminal.h"
#include "Module/CircularBuffer.h"
#include "Module/Runner.h"
class CommandShell {
public:
CommandShell(Runner *runner);
void setTerm(Terminal *term);
Terminal *term();
void showGreetings(bool = true);
void showFarewell(bool = true);
void clearHistory();
void addHistory(const char *);
bool getHistoryInput(String &);
void setEditLine(const String &);
bool active();
void loop();
private:
size_t printGreetings(Print *);
size_t printFarewell(Print *);
size_t printPrompt(Print *);
void onOpen(Print *out);
void onClose(Print *out);
void onData(const char *);
void onHistory(Print *out);
bool getLastInput(String &);
private:
CircularBuffer<String, 4> _history;
Terminal *_term;
Runner *_runner;
String _path;
bool _active;
bool _greetings;
bool _farewell;
};

View File

@@ -1,68 +0,0 @@
#pragma once
#include "Module/CharBuffer.h"
class EditLine : public CharBuffer {
public:
EditLine(size_t size) : CharBuffer(size){};
char &operator[](size_t i) { return _pool[i]; }
char operator[](size_t i) const { return _pool[i]; }
EditLine &operator=(const EditLine &src) {
delete[] _pool;
_pool = new char[src._capacity];
memcpy(_pool, src._pool, src._capacity);
_read = src._read;
_write = src._write;
return *this;
}
void del() {
size_t i;
for (i = _write; i < _capacity; ++i)
_pool[i] = _pool[i + 1];
_pool[i] = '\x00';
}
bool backspace() {
bool res = false;
if (prev()) {
del();
res = true;
}
return res;
}
bool next() {
bool res = false;
if (_write < _capacity - 1) {
_write++;
res = true;
}
return res;
}
bool prev() {
bool res = false;
if (_write > 0) {
_write--;
res = true;
}
return res;
}
size_t home() {
size_t res = _write;
_write = 0;
return res;
}
size_t end() {
size_t n;
for (n = 0; n < _capacity - 1; ++n)
if (_pool[n] == '\x00') break;
return n;
}
};

View File

@@ -1,84 +0,0 @@
#pragma once
#include <Arduino.h>
enum ModuleState_t {
MOD_INIT,
MOD_INIT_FAILED,
MOD_INIT_COMPLETE,
MOD_START_FAILED,
MOD_ACTIVE
};
class Module {
protected:
virtual bool onInit() { return true; };
virtual void onEnd(){};
virtual bool onStart() { return true; }
virtual void onStop(){};
virtual void onLoop() = 0;
protected:
Print *_out;
public:
Module() : _state{MOD_INIT} {}
bool init(bool force = false) {
if (_state > MOD_INIT_COMPLETE) {
return true;
}
if (_state == MOD_INIT_FAILED && !force) {
return false;
}
_state = onInit() ? MOD_INIT_COMPLETE : MOD_INIT_FAILED;
return _state == MOD_INIT_COMPLETE;
}
bool start(bool force = false) {
if (_state == MOD_ACTIVE) {
return true;
}
if (_state == MOD_START_FAILED && !force) {
return false;
}
if (_state < MOD_INIT_COMPLETE) {
if (!init(force)) {
return false;
}
}
_state = onStart() ? MOD_ACTIVE : MOD_START_FAILED;
return _state == MOD_ACTIVE;
}
void stop() {
if (_state < MOD_ACTIVE) {
return;
}
onStop();
_state = MOD_INIT_COMPLETE;
};
void end() {
if (_state < MOD_INIT_FAILED) {
return;
}
onEnd();
_state = MOD_INIT;
};
void loop() {
if (_state == MOD_ACTIVE || start()) onLoop();
};
void setOutput(Print *p) { _out = p; }
ModuleState_t getState() {
return _state;
}
private:
ModuleState_t _state;
};

View File

@@ -1,17 +0,0 @@
#pragma once
#include <Print.h>
#include "Cmd.h"
class Runner {
public:
virtual void run(const char*, Print*);
};
class CmdRunner : public Runner {
public:
void run(const char* cmd, Print* out) override {
String cmdStr{cmd};
csvCmdExecute(cmdStr);
}
};

View File

@@ -1,51 +0,0 @@
#pragma once
#include "Global.h"
#include "Module/Module.h"
#include "Module/Terminal.h"
#include "Module/CommandShell.h"
#include <functional>
enum TelnetEvent_t {
TE_CONNECTED,
TE_DISCONNECTED
};
typedef std::function<void(TelnetEvent_t, WiFiClient*)> TelnetEventHandler;
class Telnet : public Module {
public:
Telnet(uint16_t port) : _port{port}, _lastConnected{false} {};
public:
void setEventHandler(TelnetEventHandler);
void sendData(const String&);
bool hasClient();
bool isShellActive();
void setCommandShell(CommandShell*);
protected:
bool onInit() override;
void onEnd() override;
bool onStart() override;
void onStop() override;
void onLoop() override;
private:
void onConnect();
void onDisconnect();
void onData();
void onOpen();
void onClose();
private:
TelnetEventHandler _eventHandler;
uint16_t _port;
bool _lastConnected;
WiFiClient _client;
WiFiServer* _server;
Terminal* _term;
CommandShell* _shell;
};

View File

@@ -1,186 +0,0 @@
#pragma once
#include <Arduino.h>
#include "Module/EditLine.h"
#include <functional>
#define A_NORMAL 0x0000 // normal
#define A_UNDERLINE 0x0001 // underline
#define A_REVERSE 0x0002 // reverse
#define A_BLINK 0x0004 // blink
#define A_BOLD 0x0008 // bold
#define A_DIM 0x0010 // dim
#define A_STANDOUT A_BOLD // standout (same as bold)
#define F_BLACK 0x0100 // foreground black
#define F_RED 0x0200 // foreground red
#define F_GREEN 0x0300 // foreground green
#define F_BROWN 0x0400 // foreground brown
#define F_BLUE 0x0500 // foreground blue
#define F_MAGENTA 0x0600 // foreground magenta
#define F_CYAN 0x0700 // foreground cyan
#define F_WHITE 0x0800 // foreground white
#define F_YELLOW F_BROWN // some terminals show brown as yellow (with A_BOLD)
#define F_COLOR 0x0F00 // foreground mask
#define B_BLACK 0x1000 // background black
#define B_RED 0x2000 // background red
#define B_GREEN 0x3000 // background green
#define B_BROWN 0x4000 // background brown
#define B_BLUE 0x5000 // background blue
#define B_MAGENTA 0x6000 // background magenta
#define B_CYAN 0x7000 // background cyan
#define B_WHITE 0x8000 // background white
#define B_YELLOW B_BROWN // some terminals show brown as yellow (with A_BOLD)
#define B_COLOR 0xF000 // background mask
#define CHAR_NULL 0x00
#define CHAR_BEL 0x07
#define CHAR_BS 0x08
#define CHAR_SPACE 0x20
#define CHAR_TAB 0x09
#define CHAR_LF 0x0a
#define CHAR_CR 0x0d
#define CHR_ZERO 0x30
#define KEY_DEL 0x7f
#define KEY_DOWN 0x80
#define KEY_UP 0x81
#define KEY_LEFT 0x82
#define KEY_RIGHT 0x83
#define KEY_HOME 0x84
#define KEY_INS 0x86
#define KEY_PAGE_DOWN 0x87
#define KEY_PAGE_UP 0x88
#define KEY_END 0x89
#define CHAR_LT 0x8b
#define CHAR_CSI 0x9b
#define CHAR_ESC 0x1b
#define CHAR_BIN 0xFF
#define ESC_CURSOR_HOME "\x1b[H"
#define ESC_SAVE_CURSOR "\x1b[s"
#define ESC_UNSAVE_CURSOR "\x1b[u"
#define ESC_SAVE_CURSOR_AND_ATTRS "\x1b[7"
#define ESC_RESTORE_CURSOR_AND_ATTRS "\x1b[8"
#define ESC_CLEAR "\x1b[2J"
#define ESC_CLEAR_BOTTOM "\x1b[J"
#define ESC_CLEAR_EOL "\x1b[0K"
#define ESC_CURSOR_UP "\x1b[1A"
#define ESC_CURSOR_DOWN "\x1b[1B"
#define ESC_CURSOR_FORWARD "\x1b[1C"
#define ESC_CURSOR_BACKWARD "\x1b[1D"
#define SEQ_CSI PSTR("\033[") // code introducer
#define SEQ_LOAD_G1 PSTR("\033)0") // load G1 character set
#define SEQ_CLEAR PSTR("\033[2J") // clear screen
#define SEQ_ATTRSET PSTR("\033[0") // set attributes, e.g. "\033[0;7;1m"
#define SEQ_ATTRSET_BOLD PSTR(";1") // bold
#define SEQ_ATTRSET_DIM PSTR(";2") // dim
#define SEQ_ATTRSET_FCOLOR PSTR(";3") // forground color
#define SEQ_ATTRSET_UNDERLINE PSTR(";4") // underline
#define SEQ_ATTRSET_BCOLOR PSTR(";4") // background color
#define SEQ_ATTRSET_BLINK PSTR(";5") // blink
#define SEQ_ATTRSET_REVERSE PSTR(";7") // reverse
enum TerminalEventEnum {
EVENT_OPEN,
EVENT_CLOSE,
EVENT_TAB
};
enum SpecialKeyEnum { SPEC_KEY_UP,
SPEC_KEY_TAB,
SPEC_KEY_ENTER,
SPEC_KEY_ESC };
typedef std::function<bool(SpecialKeyEnum key)> SpecialKeyPressedEvent;
typedef std::function<void(TerminalEventEnum, Stream *)> TerminalEventHandler;
typedef std::function<void(const char *)> TerminalInputEventHandler;
enum EOLType_t { CRLF,
LFCR,
LF,
CR };
enum State { ST_INACTIVE,
ST_NORMAL,
ST_ESC_SEQ,
ST_CTRL_SEQ };
class Terminal : public Print {
public:
Terminal(Stream *stream = nullptr);
void setStream(Stream *stream);
void setEOL(EOLType_t code);
void enableControlCodes(bool enabled = true);
void enableEcho(bool enabled = true);
void enableColors(bool enabled = true);
void setOnEvent(TerminalEventHandler);
void setOnSpecKeyPress(SpecialKeyPressedEvent);
void setOnReadLine(TerminalInputEventHandler);
bool setLine(const uint8_t *bytes, size_t size);
CharBuffer &getLine();
void backsp();
void clear();
void clear_line();
size_t println(const char *str);
size_t println(void);
size_t write_P(PGM_P str);
size_t write(uint8_t c);
size_t write(const uint8_t *buf, size_t size);
void writeByDigit(uint8_t i);
bool available();
void loop();
void start();
void quit();
void initscr();
void attrset(uint16_t attr);
private:
void move(uint8_t y, uint8_t x);
TerminalEventHandler eventHandler_;
TerminalInputEventHandler inputHandler_;
uint8_t attr = 0xff;
uint8_t curY = 0xff;
uint8_t curX = 0xff;
unsigned long _lastReceived = 0;
State state;
Stream *_stream;
EditLine _line;
char _cc_buf[32] = {0};
size_t _cc_pos = 0;
bool _color = false;
bool _controlCodes = false;
bool _echo = false;
EOLType_t _eol = CRLF;
struct ControlCode {
const char *cc;
const char ch;
};
ControlCode keyMap[10] = {
{"G", KEY_HOME}, // 71 Home key
{"H", KEY_UP}, // 72 Up arrow
{"I", KEY_PAGE_UP}, // 73 PageUp
{"K", KEY_LEFT}, // 75 Left arrow
{"M", KEY_RIGHT}, // 77 Right arrow
{"O", KEY_END}, // 79 End key
{"P", KEY_DOWN}, // 80 Down arrow
{"Q", KEY_PAGE_DOWN}, // 81 PageDown
{"R", KEY_INS}, // 82 Insert
{"S", KEY_DEL}, // 83 Delete
};
};

View File

@@ -4,7 +4,7 @@
#include "items/vButtonOut.h"
//
#include "Global.h"
#include "Module/Terminal.h"
void loopCmdAdd(const String& cmdStr) {
orderBuf += cmdStr;
@@ -27,8 +27,6 @@ void csvCmdExecute(String& cmdStr) {
while (cmdStr.length()) {
String buf = selectToMarker(cmdStr, "\n");
buf = deleteBeforeDelimiter(buf, " "); //отсечка чекбокса
count++;
@@ -94,8 +92,6 @@ void csvCmdExecute(String& cmdStr) {
sCmd.addCommand(order.c_str(), impuls);
}
sCmd.readStr(buf);
}
cmdStr = deleteBeforeDelimiter(cmdStr, "\n");

View File

@@ -1,86 +0,0 @@
#include "Module/Telnet.h"
bool Telnet::onInit() {
_server = new WiFiServer(_port);
_term = new Terminal();
_term->enableControlCodes();
_term->enableEcho(false);
_term->setStream(&_client);
return true;
}
void Telnet::onEnd() {
delete _server;
}
bool Telnet::onStart() {
_server->begin();
_server->setNoDelay(true);
return true;
}
void Telnet::onStop() {
if (hasClient()) {
_client.stop();
}
_server->stop();
}
bool Telnet::hasClient() { return _client.connected(); }
void Telnet::sendData(const String& data) {
if (hasClient()) {
_client.write(data.c_str());
}
}
void Telnet::setCommandShell(CommandShell* shell) {
_shell = shell;
_shell->setTerm(_term);
}
void Telnet::setEventHandler(TelnetEventHandler h) { _eventHandler = h; }
void Telnet::onLoop() {
if (_server->hasClient()) {
if (!_client) {
_client = _server->available();
} else {
if (!_client.connected()) {
_server->stop();
_client = _server->available();
} else {
WiFiClient rejected;
rejected = _server->available();
rejected.stop();
}
}
}
if (_lastConnected != hasClient()) {
_lastConnected = hasClient();
if (_lastConnected) {
onConnect();
} else {
onDisconnect();
}
}
if (hasClient() && _shell != nullptr) _shell->loop();
}
bool Telnet::isShellActive() {
return _shell->active();
}
void Telnet::onConnect() {
if (_eventHandler) {
_eventHandler(TE_CONNECTED, &_client);
}
}
void Telnet::onDisconnect() {
if (_eventHandler) {
_eventHandler(TE_DISCONNECTED, nullptr);
}
}

View File

@@ -1,320 +0,0 @@
#include "Module/Terminal.h"
#include "Utils/TimeUtils.h"
#define INPUT_MAX_LENGHT 255
Terminal::Terminal(Stream *stream) : _stream{stream},
_line(INPUT_MAX_LENGHT),
_cc_pos(0),
_color(false),
_controlCodes(false),
_echo(false),
_eol(CRLF) { state = ST_NORMAL; };
void Terminal::setStream(Stream *stream) {
_stream = stream;
}
void Terminal::setOnReadLine(TerminalInputEventHandler h) { inputHandler_ = h; }
void Terminal::setOnEvent(TerminalEventHandler h) { eventHandler_ = h; }
bool Terminal::available() {
return _stream != nullptr ? _stream->available() : false;
}
void Terminal::setEOL(EOLType_t eol) {
_eol = eol;
}
void Terminal::enableEcho(bool enabled) {
_echo = enabled;
}
void Terminal::enableColors(bool enabled) {
_color = enabled;
}
void Terminal::enableControlCodes(bool enabled) {
_controlCodes = enabled;
}
void Terminal::quit() {}
void Terminal::loop() {
if (_stream == nullptr || !_stream->available()) return;
byte moveX = 0;
byte moveY = 0;
char c = _stream->read();
_lastReceived = millis();
if (state == ST_INACTIVE) {
// wait for CR
if (c == CHAR_CR) {
if (eventHandler_) {
eventHandler_(EVENT_OPEN, _stream);
state = ST_NORMAL;
}
}
// or ignore all other
return;
}
if (c == CHAR_LF || c == CHAR_NULL || c == CHAR_BIN)
return;
// Esc
if (c == CHAR_ESC || c == 195) {
state = ST_ESC_SEQ;
_cc_pos = 0;
for (size_t i = 0; i < 2; ++i) {
bool timeout = false;
while (!_stream->available() &&
!(timeout = millis_since(_lastReceived) > 10)) {
delay(0);
}
if (timeout) {
state = ST_NORMAL;
break;
}
_lastReceived = millis();
c = _stream->read();
_cc_buf[_cc_pos] = c;
if ((c == '[') || ((c >= 'A' && c <= 'Z') || c == '~')) {
_cc_pos++;
_cc_buf[++_cc_pos] = '\x00';
}
}
uint8_t i;
for (i = 0; i < 10; ++i) {
if (strcmp(_cc_buf, keyMap[i].cc) == 0) {
c = keyMap[i].ch;
state = ST_NORMAL;
}
}
}
if (state == ST_ESC_SEQ) {
state = ST_NORMAL;
return;
}
// WHEN NORMAL
if (state == ST_NORMAL) {
if (c == CHAR_ESC) {
if (!_line.available()) {
// QUIT
state = ST_INACTIVE;
if (eventHandler_)
eventHandler_(EVENT_CLOSE, _stream);
} else {
// CLEAR
_line.clear();
if (_controlCodes) {
clear_line();
} else {
println();
}
}
return;
}
switch (c) {
case CHAR_CR:
println();
if (inputHandler_)
inputHandler_(_line.c_str());
_line.clear();
moveY++;
break;
case CHAR_TAB:
if (eventHandler_)
eventHandler_(EVENT_TAB, _stream);
return;
case KEY_LEFT:
if (_line.prev())
moveX--;
break;
case KEY_RIGHT:
if (_line.next())
moveX++;
break;
case KEY_HOME:
moveX = -1 * _line.home();
break;
case KEY_END:
moveX = _line.end();
break;
case CHAR_BS:
case KEY_DEL:
if (_line.backspace()) {
backsp();
moveX--;
}
break;
default:
// printable ascii 7bit or printable 8bit ISO8859
if ((c & '\x7F') >= 32 && (c & '\x7F') < 127)
if (_line.write(c)) {
if (_echo) write(c);
moveX++;
}
break;
}
// if (controlCodesEnabled)
// move(startY + moveY, startX + moveX);
}
}
bool Terminal::setLine(const uint8_t *ptr, size_t size) {
_line.clear();
if (_line.write(ptr, size))
print(_line.c_str());
return true;
}
CharBuffer &Terminal::getLine() { return _line; }
void Terminal::start() {
if (_controlCodes) initscr();
println();
}
void Terminal::initscr() {
write_P(SEQ_LOAD_G1);
attrset(A_NORMAL);
move(0, 0);
clear();
}
void Terminal::attrset(const uint16_t attr) {
uint8_t i;
if (attr != this->attr) {
this->write_P(SEQ_ATTRSET);
i = (attr & F_COLOR) >> 8;
if (i >= 1 && i <= 8) {
this->write_P(SEQ_ATTRSET_FCOLOR);
this->write(i - 1 + '0');
}
i = (attr & B_COLOR) >> 12;
if (i >= 1 && i <= 8) {
this->write_P(SEQ_ATTRSET_BCOLOR);
this->write(i - 1 + '0');
}
if (attr & A_REVERSE)
this->write_P(SEQ_ATTRSET_REVERSE);
if (attr & A_UNDERLINE)
this->write_P(SEQ_ATTRSET_UNDERLINE);
if (attr & A_BLINK)
this->write_P(SEQ_ATTRSET_BLINK);
if (attr & A_BOLD)
this->write_P(SEQ_ATTRSET_BOLD);
if (attr & A_DIM)
this->write_P(SEQ_ATTRSET_DIM);
this->write('m');
this->attr = attr;
}
}
void Terminal::clear() { write_P(SEQ_CLEAR); }
void Terminal::clear_line() {
write(CHAR_CR);
write_P(ESC_CLEAR_EOL);
}
void Terminal::move(uint8_t y, uint8_t x) {
write_P(SEQ_CSI);
writeByDigit(y + 1);
write(';');
writeByDigit(x + 1);
write('H');
curY = y;
curX = x;
}
void Terminal::writeByDigit(uint8_t i) {
uint8_t ii;
if (i >= 10) {
if (i >= 100) {
ii = i / 100;
write(ii + '0');
i -= 100 * ii;
}
ii = i / 10;
write(ii + '0');
i -= 10 * ii;
}
write(i + '0');
}
void Terminal::backsp() {
write(CHAR_BS);
write(CHAR_SPACE);
write(CHAR_BS);
}
size_t Terminal::println(const char *str) {
size_t n = print(str);
return n += println();
}
size_t Terminal::println(void) {
size_t n = 0;
switch (_eol) {
case CRLF:
n += write(CHAR_CR);
n += write(CHAR_LF);
break;
case LF:
n += write(CHAR_LF);
break;
case LFCR:
n += write(CHAR_LF);
n += write(CHAR_CR);
break;
case CR:
n += write(CHAR_CR);
break;
}
return n;
}
size_t Terminal::write(uint8_t ch) {
size_t n = 0;
if (_stream)
n = _stream->write(ch);
return n;
}
size_t Terminal::write_P(PGM_P str) {
uint8_t ch;
size_t n = 0;
while ((ch = pgm_read_byte(str + n)) != '\x0') {
_stream->write(ch);
n++;
}
return n;
}
size_t Terminal::write(const uint8_t *buf, size_t size) {
size_t n = 0;
while (size--) {
if (_stream->write(*buf++))
n++;
else
break;
}
return n;
}