mirror of
https://github.com/IoTManagerProject/IoTManager.git
synced 2026-03-31 04:19:15 +03:00
Merge pull request #185 from biveraxe/ver4dev
Сценарии(вложенные ифы, режимы) и мелкие правки
This commit is contained in:
@@ -12,6 +12,15 @@ class ExprAST {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class IoTScenario {
|
class IoTScenario {
|
||||||
|
int mode = 0; // режим работы:
|
||||||
|
// 0 - весь сценарий хранится в FS, читаем посимвольно из файла. Максимальная экономии памяти, но медленно
|
||||||
|
// 1 - весь сценарий подгружается в String, читаем посимвольно из строки
|
||||||
|
// 2 - сценарий конвертируется в структуру классов за один проход и выполняется только в памяти. Максимальная
|
||||||
|
// скорость, но на 8266 при средних сценариях не хватает памяти
|
||||||
|
|
||||||
|
String strFromFile;
|
||||||
|
int charCount;
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// Lexer (Лексический анализатор)
|
// Lexer (Лексический анализатор)
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
@@ -47,16 +56,16 @@ class IoTScenario {
|
|||||||
/// identifierexpr
|
/// identifierexpr
|
||||||
/// ::= identifier
|
/// ::= identifier
|
||||||
/// ::= identifier '(' expression* ')'
|
/// ::= identifier '(' expression* ')'
|
||||||
ExprAST *ParseIdentifierExpr(String *IDNames);
|
ExprAST *ParseIdentifierExpr(String *IDNames, bool callFromCondition);
|
||||||
|
|
||||||
/// numberexpr ::= number
|
/// numberexpr ::= number
|
||||||
ExprAST *ParseNumberExpr();
|
ExprAST *ParseNumberExpr();
|
||||||
|
|
||||||
/// parenexpr ::= '(' expression ')'
|
/// parenexpr ::= '(' expression ')'
|
||||||
ExprAST *ParseParenExpr();
|
ExprAST *ParseParenExpr(String *IDNames, bool callFromCondition);
|
||||||
|
|
||||||
/// bracketsexpr ::= '{' expression '}'
|
/// bracketsexpr ::= '{' expression '}'
|
||||||
ExprAST *ParseBracketsExpr();
|
ExprAST *ParseBracketsExpr(String *IDNames, bool callFromCondition);
|
||||||
|
|
||||||
/// quotesexpr ::= '"' expression '"'
|
/// quotesexpr ::= '"' expression '"'
|
||||||
ExprAST *ParseQuotesExpr();
|
ExprAST *ParseQuotesExpr();
|
||||||
@@ -68,16 +77,16 @@ class IoTScenario {
|
|||||||
/// ::= identifierexpr
|
/// ::= identifierexpr
|
||||||
/// ::= numberexpr
|
/// ::= numberexpr
|
||||||
/// ::= parenexpr
|
/// ::= parenexpr
|
||||||
ExprAST *ParsePrimary(String *IDNames);
|
ExprAST *ParsePrimary(String *IDNames, bool callFromCondition);
|
||||||
|
|
||||||
/// binoprhs
|
/// binoprhs
|
||||||
/// ::= ('+' primary)*
|
/// ::= ('+' primary)*
|
||||||
ExprAST *ParseBinOpRHS(int ExprPrec, ExprAST *LHS, String *IDNames);
|
ExprAST *ParseBinOpRHS(int ExprPrec, ExprAST *LHS, String *IDNames, bool callFromCondition);
|
||||||
|
|
||||||
/// expression
|
/// expression
|
||||||
/// ::= primary binoprhs
|
/// ::= primary binoprhs
|
||||||
///
|
///
|
||||||
ExprAST *ParseExpression(String *IDNames);
|
ExprAST *ParseExpression(String *IDNames, bool callFromCondition);
|
||||||
|
|
||||||
int getLastChar();
|
int getLastChar();
|
||||||
fs::File file;
|
fs::File file;
|
||||||
|
|||||||
@@ -98,6 +98,8 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length)
|
|||||||
strFromFile.replace("{\"scen\":\"", "");
|
strFromFile.replace("{\"scen\":\"", "");
|
||||||
strFromFile.replace("\\n", "\n");
|
strFromFile.replace("\\n", "\n");
|
||||||
strFromFile.replace("\\\"", "\"");
|
strFromFile.replace("\\\"", "\"");
|
||||||
|
strFromFile.replace(";", " ");
|
||||||
|
strFromFile.replace("\\t", " ");
|
||||||
strFromFile.remove(strFromFile.length() - 2, 2);
|
strFromFile.remove(strFromFile.length() - 2, 2);
|
||||||
}
|
}
|
||||||
myfile.close();
|
myfile.close();
|
||||||
|
|||||||
@@ -586,11 +586,22 @@ class BracketsExprAST : public ExprAST {
|
|||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
int IoTScenario::getLastChar() {
|
int IoTScenario::getLastChar() {
|
||||||
|
if (mode == 0) {
|
||||||
if (file) {
|
if (file) {
|
||||||
LastChar = file.read();
|
LastChar = file.read();
|
||||||
if (LastChar == 10) curLine++;
|
if (LastChar == 10) curLine++;
|
||||||
return LastChar;
|
return LastChar;
|
||||||
} else return EOF;
|
} else return EOF;
|
||||||
|
} else if (mode == 1) {
|
||||||
|
if (charCount < strFromFile.length()) {
|
||||||
|
LastChar = strFromFile.charAt(charCount);
|
||||||
|
//Serial.printf("%d, ", LastChar);
|
||||||
|
if (LastChar == 10) curLine++;
|
||||||
|
charCount++;
|
||||||
|
return LastChar;
|
||||||
|
} else return EOF;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// gettok - Возвращает следующий токен из стандартного потока ввода.
|
/// gettok - Возвращает следующий токен из стандартного потока ввода.
|
||||||
@@ -728,7 +739,7 @@ ExprAST *IoTScenario::Error(const char *Str) {
|
|||||||
/// identifierexpr
|
/// identifierexpr
|
||||||
/// ::= identifier
|
/// ::= identifier
|
||||||
/// ::= identifier '(' expression* ')'
|
/// ::= identifier '(' expression* ')'
|
||||||
ExprAST *IoTScenario::ParseIdentifierExpr(String *IDNames) {
|
ExprAST *IoTScenario::ParseIdentifierExpr(String *IDNames, bool callFromCondition) {
|
||||||
String IdName = IdentifierStr;
|
String IdName = IdentifierStr;
|
||||||
String Cmd = "";
|
String Cmd = "";
|
||||||
IoTItem *tmpItem = findIoTItem(IdName);
|
IoTItem *tmpItem = findIoTItem(IdName);
|
||||||
@@ -754,7 +765,7 @@ ExprAST *IoTScenario::ParseIdentifierExpr(String *IDNames) {
|
|||||||
std::vector<ExprAST *> Args;
|
std::vector<ExprAST *> Args;
|
||||||
if (CurTok != ')') {
|
if (CurTok != ')') {
|
||||||
while (1) {
|
while (1) {
|
||||||
ExprAST *Arg = ParseExpression(IDNames);
|
ExprAST *Arg = ParseExpression(IDNames, callFromCondition);
|
||||||
if (!Arg) return nullptr;
|
if (!Arg) return nullptr;
|
||||||
Args.push_back(Arg);
|
Args.push_back(Arg);
|
||||||
|
|
||||||
@@ -783,9 +794,9 @@ ExprAST *IoTScenario::ParseNumberExpr() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// parenexpr ::= '(' expression ')'
|
/// parenexpr ::= '(' expression ')'
|
||||||
ExprAST *IoTScenario::ParseParenExpr() {
|
ExprAST *IoTScenario::ParseParenExpr(String *IDNames, bool callFromCondition) {
|
||||||
getNextToken(); // получаем (.
|
getNextToken(); // получаем (.
|
||||||
ExprAST *V = ParseExpression(nullptr);
|
ExprAST *V = ParseExpression(IDNames, callFromCondition);
|
||||||
if (!V) return nullptr;
|
if (!V) return nullptr;
|
||||||
|
|
||||||
if (CurTok != ')')
|
if (CurTok != ')')
|
||||||
@@ -795,28 +806,31 @@ ExprAST *IoTScenario::ParseParenExpr() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// bracketsexpr ::= '{' expression '}'
|
/// bracketsexpr ::= '{' expression '}'
|
||||||
ExprAST *IoTScenario::ParseBracketsExpr() {
|
ExprAST *IoTScenario::ParseBracketsExpr(String *IDNames, bool callFromCondition) {
|
||||||
getNextToken(); // получаем {.
|
getNextToken(); // получаем {.
|
||||||
std::vector<ExprAST *> bracketsList;
|
std::vector<ExprAST *> bracketsList;
|
||||||
if (CurTok != '}') {
|
|
||||||
while (1) {
|
while (CurTok != '}') {
|
||||||
ExprAST *Expr = ParseExpression(nullptr);
|
ExprAST *Expr = ParseExpression(IDNames, callFromCondition);
|
||||||
if (!Expr) return nullptr;
|
if (!Expr) return nullptr;
|
||||||
bracketsList.push_back(Expr);
|
bracketsList.push_back(Expr);
|
||||||
|
|
||||||
if (CurTok != ';')
|
//if (CurTok == '}') break;
|
||||||
return Error("Expected ';' in operation list");
|
|
||||||
int ttok = getNextToken();
|
|
||||||
if (!ttok) {
|
|
||||||
Error("Expected '}'");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CurTok == '}') break;
|
//Serial.printf("ParseBracketsExpr CurTok = %d \n", CurTok);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//if (CurTok != ';')
|
||||||
|
// return Error("Expected ';' in operation list");
|
||||||
|
|
||||||
|
//int ttok = getNextToken();
|
||||||
|
if (CurTok == tok_eof) {
|
||||||
|
return Error("Expected '}'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//getNextToken(); // получаем }.
|
getNextToken(); // получаем }.
|
||||||
return new BracketsExprAST(bracketsList);
|
return new BracketsExprAST(bracketsList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -833,14 +847,14 @@ ExprAST *IoTScenario::ParseIfExpr(String *IDNames) {
|
|||||||
getNextToken(); // Получаем if.
|
getNextToken(); // Получаем if.
|
||||||
|
|
||||||
// условие.
|
// условие.
|
||||||
ExprAST *Cond = ParseExpression(IDNames);
|
ExprAST *Cond = ParseExpression(IDNames, true);
|
||||||
if (!Cond) return nullptr;
|
if (!Cond) return nullptr;
|
||||||
|
|
||||||
if (CurTok != tok_then)
|
if (CurTok != tok_then)
|
||||||
return Error("expected then");
|
return Error("expected then");
|
||||||
getNextToken(); // Получаем then
|
getNextToken(); // Получаем then
|
||||||
|
|
||||||
ExprAST *Then = ParseExpression(nullptr);
|
ExprAST *Then = ParseExpression(IDNames, false);
|
||||||
if (!Then) return nullptr;
|
if (!Then) return nullptr;
|
||||||
|
|
||||||
// if (CurTok != tok_else)
|
// if (CurTok != tok_else)
|
||||||
@@ -848,7 +862,8 @@ ExprAST *IoTScenario::ParseIfExpr(String *IDNames) {
|
|||||||
ExprAST *Else = nullptr;
|
ExprAST *Else = nullptr;
|
||||||
if (CurTok == tok_else) {
|
if (CurTok == tok_else) {
|
||||||
getNextToken();
|
getNextToken();
|
||||||
Else = ParseExpression(nullptr);
|
Else = ParseExpression(IDNames, false);
|
||||||
|
if (!Else) return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new IfExprAST(Cond, Then, Else, IDNames);
|
return new IfExprAST(Cond, Then, Else, IDNames);
|
||||||
@@ -858,24 +873,24 @@ ExprAST *IoTScenario::ParseIfExpr(String *IDNames) {
|
|||||||
/// ::= identifierexpr
|
/// ::= identifierexpr
|
||||||
/// ::= numberexpr
|
/// ::= numberexpr
|
||||||
/// ::= parenexpr
|
/// ::= parenexpr
|
||||||
ExprAST *IoTScenario::ParsePrimary(String *IDNames) {
|
ExprAST *IoTScenario::ParsePrimary(String *IDNames, bool callFromCondition) {
|
||||||
switch (CurTok) {
|
switch (CurTok) {
|
||||||
default:
|
default:
|
||||||
Serial.println(CurTok);
|
Serial.println(CurTok);
|
||||||
return Error("unknown token when expecting an expression");
|
return Error("unknown token when expecting an expression");
|
||||||
case tok_identifier: {
|
case tok_identifier: {
|
||||||
if (IDNames) {
|
if (callFromCondition && IDNames) {
|
||||||
String tmpstr = *IDNames;
|
String tmpstr = *IDNames;
|
||||||
*IDNames = tmpstr + " " + IdentifierStr + " ";
|
*IDNames = tmpstr + " " + IdentifierStr + " ";
|
||||||
}
|
}
|
||||||
return ParseIdentifierExpr(IDNames);
|
return ParseIdentifierExpr(IDNames, callFromCondition);
|
||||||
}
|
}
|
||||||
case tok_number:
|
case tok_number:
|
||||||
return ParseNumberExpr();
|
return ParseNumberExpr();
|
||||||
case '(':
|
case '(':
|
||||||
return ParseParenExpr();
|
return ParseParenExpr(IDNames, callFromCondition);
|
||||||
case '{':
|
case '{':
|
||||||
return ParseBracketsExpr();
|
return ParseBracketsExpr(IDNames, callFromCondition);
|
||||||
case tok_string:
|
case tok_string:
|
||||||
return ParseQuotesExpr();
|
return ParseQuotesExpr();
|
||||||
case tok_if:
|
case tok_if:
|
||||||
@@ -885,7 +900,7 @@ ExprAST *IoTScenario::ParsePrimary(String *IDNames) {
|
|||||||
|
|
||||||
/// binoprhs
|
/// binoprhs
|
||||||
/// ::= ('+' primary)*
|
/// ::= ('+' primary)*
|
||||||
ExprAST *IoTScenario::ParseBinOpRHS(int ExprPrec, ExprAST *LHS, String *IDNames) {
|
ExprAST *IoTScenario::ParseBinOpRHS(int ExprPrec, ExprAST *LHS, String *IDNames, bool callFromCondition) {
|
||||||
// Если это бинарный оператор, получаем его приоритет
|
// Если это бинарный оператор, получаем его приоритет
|
||||||
while (1) {
|
while (1) {
|
||||||
int TokPrec = GetTokPrecedence();
|
int TokPrec = GetTokPrecedence();
|
||||||
@@ -900,14 +915,14 @@ ExprAST *IoTScenario::ParseBinOpRHS(int ExprPrec, ExprAST *LHS, String *IDNames)
|
|||||||
getNextToken(); // eat binop
|
getNextToken(); // eat binop
|
||||||
|
|
||||||
// Разобрать первичное выражение после бинарного оператора
|
// Разобрать первичное выражение после бинарного оператора
|
||||||
ExprAST *RHS = ParsePrimary(IDNames);
|
ExprAST *RHS = ParsePrimary(IDNames, callFromCondition);
|
||||||
if (!RHS) return nullptr;
|
if (!RHS) return nullptr;
|
||||||
|
|
||||||
// Если BinOp связан с RHS меньшим приоритетом, чем оператор после RHS,
|
// Если BinOp связан с RHS меньшим приоритетом, чем оператор после RHS,
|
||||||
// то берём часть вместе с RHS как LHS.
|
// то берём часть вместе с RHS как LHS.
|
||||||
int NextPrec = GetTokPrecedence();
|
int NextPrec = GetTokPrecedence();
|
||||||
if (TokPrec < NextPrec) {
|
if (TokPrec < NextPrec) {
|
||||||
RHS = ParseBinOpRHS(TokPrec + 1, RHS, IDNames);
|
RHS = ParseBinOpRHS(TokPrec + 1, RHS, IDNames, callFromCondition);
|
||||||
if (RHS == nullptr) return nullptr;
|
if (RHS == nullptr) return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -919,29 +934,45 @@ ExprAST *IoTScenario::ParseBinOpRHS(int ExprPrec, ExprAST *LHS, String *IDNames)
|
|||||||
/// expression
|
/// expression
|
||||||
/// ::= primary binoprhs
|
/// ::= primary binoprhs
|
||||||
///
|
///
|
||||||
ExprAST *IoTScenario::ParseExpression(String *IDNames) {
|
ExprAST *IoTScenario::ParseExpression(String *IDNames, bool callFromCondition) {
|
||||||
ExprAST *LHS = ParsePrimary(IDNames);
|
ExprAST *LHS = ParsePrimary(IDNames, callFromCondition);
|
||||||
if (!LHS) return nullptr;
|
if (!LHS) return nullptr;
|
||||||
return ParseBinOpRHS(0, LHS, IDNames);
|
return ParseBinOpRHS(0, LHS, IDNames, callFromCondition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void IoTScenario::loadScenario(String fileName) { // подготавливаем контекст для чтения и интерпретации файла
|
void IoTScenario::loadScenario(String fileName) { // подготавливаем контекст для чтения и интерпретации файла
|
||||||
|
if (mode == 0) {
|
||||||
if (file) file.close();
|
if (file) file.close();
|
||||||
file = FileFS.open(fileName, "r");
|
file = FileFS.open(fileName.c_str(), "r");
|
||||||
}
|
|
||||||
|
|
||||||
void IoTScenario::exec(String eventIdName) { // посимвольно считываем и сразу интерпретируем сценарий в дерево AST
|
|
||||||
if (!file) {
|
if (!file) {
|
||||||
Error("Open file scenario error");
|
Error("Open file scenario error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else if (mode == 1) {
|
||||||
|
file = FileFS.open(fileName.c_str(), "r");
|
||||||
|
if (!file) {
|
||||||
|
Error("Open file scenario error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
strFromFile = file.readString();
|
||||||
|
Serial.printf("strFromFile: %s, %s\n", strFromFile.c_str(), fileName.c_str());
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IoTScenario::exec(String eventIdName) { // посимвольно считываем и сразу интерпретируем сценарий в дерево AST
|
||||||
|
if (mode == 0 && !file) return;
|
||||||
|
|
||||||
LastChar = 0;
|
LastChar = 0;
|
||||||
CurTok = 0;
|
CurTok = 0;
|
||||||
file.seek(0);
|
|
||||||
curLine = 1;
|
curLine = 1;
|
||||||
|
charCount = 0;
|
||||||
|
|
||||||
while ((getNextToken()) != EOF) {
|
if (mode == 0) file.seek(0);
|
||||||
|
|
||||||
|
if (mode < 2) {
|
||||||
|
while (CurTok != EOF) {
|
||||||
switch (CurTok) {
|
switch (CurTok) {
|
||||||
case tok_if: {
|
case tok_if: {
|
||||||
IDNames = ""; // сбрасываем накопитель встречающихся идентификаторов в условии
|
IDNames = ""; // сбрасываем накопитель встречающихся идентификаторов в условии
|
||||||
@@ -956,6 +987,8 @@ void IoTScenario::exec(String eventIdName) { // посимвольно счит
|
|||||||
}
|
}
|
||||||
delete tmpAST;
|
delete tmpAST;
|
||||||
break;}
|
break;}
|
||||||
|
default: getNextToken(); break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,11 +27,33 @@
|
|||||||
"moduleDesc": "Добавляет инструмент таймеров обратного отсчета для организации периодичных операций или логических конструкций. Часто используется как вспомогательный элемент для автоматизации.",
|
"moduleDesc": "Добавляет инструмент таймеров обратного отсчета для организации периодичных операций или логических конструкций. Часто используется как вспомогательный элемент для автоматизации.",
|
||||||
"propInfo": {
|
"propInfo": {
|
||||||
"int": "Задает размер в секундах одного шага(тика) таймера.",
|
"int": "Задает размер в секундах одного шага(тика) таймера.",
|
||||||
"countDown": "Начальное значение таймера, с которого начинается обратный отсчет.",
|
"countDown": "Начальное значение таймера, с которого начинается обратный отсчет. countDown=0 - бесконечный счет (имеет смысл при ticker=1, иначе таймер будет выключен), countDown=-1 - отключает таймер совсем (используется для запуска системы с выключенным таймером)",
|
||||||
"ticker": "Генерировать(1) или нет(0) события при каждом тике таймера.",
|
"ticker": "Генерировать(1) или нет(0) события при каждом тике таймера.",
|
||||||
"repeat": "Сбрасывать(1) или нет(0) таймер в начальное состояние при достижении нуля.",
|
"repeat": "Сбрасывать(1) или нет(0) таймер в начальное состояние при достижении нуля.",
|
||||||
"needSave": "Требуется сохранять(1) или нет(0) состояние в энерго независимую память. Функция находится в разработке."
|
"needSave": "Требуется сохранять(1) или нет(0) состояние в энерго независимую память. Функция находится в разработке."
|
||||||
|
},
|
||||||
|
"funcInfo": [
|
||||||
|
{
|
||||||
|
"name": "stop",
|
||||||
|
"descr": "Поставить процесс на паузу, при этом не будет событий и не будет уменьшаться счетчик.",
|
||||||
|
"params": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "reset",
|
||||||
|
"descr": "Вернуть таймер к началу, установленному в countDown.",
|
||||||
|
"params": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "continue",
|
||||||
|
"descr": "Продолжить выполнение с момента остановки.",
|
||||||
|
"params": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "int",
|
||||||
|
"descr": "Можно изменить шаг тиков.",
|
||||||
|
"params": []
|
||||||
}
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"defActive": true,
|
"defActive": true,
|
||||||
"devices": {
|
"devices": {
|
||||||
|
|||||||
Reference in New Issue
Block a user