diff --git a/include/classes/IoTScenario.h b/include/classes/IoTScenario.h index ec0dc516..f212bb8a 100644 --- a/include/classes/IoTScenario.h +++ b/include/classes/IoTScenario.h @@ -12,6 +12,15 @@ class ExprAST { }; class IoTScenario { + int mode = 0; // режим работы: + // 0 - весь сценарий хранится в FS, читаем посимвольно из файла. Максимальная экономии памяти, но медленно + // 1 - весь сценарий подгружается в String, читаем посимвольно из строки + // 2 - сценарий конвертируется в структуру классов за один проход и выполняется только в памяти. Максимальная + // скорость, но на 8266 при средних сценариях не хватает памяти + + String strFromFile; + int charCount; + //===----------------------------------------------------------------------===// // Lexer (Лексический анализатор) //===----------------------------------------------------------------------===// @@ -47,16 +56,16 @@ class IoTScenario { /// identifierexpr /// ::= identifier /// ::= identifier '(' expression* ')' - ExprAST *ParseIdentifierExpr(String *IDNames); + ExprAST *ParseIdentifierExpr(String *IDNames, bool callFromCondition); /// numberexpr ::= number ExprAST *ParseNumberExpr(); /// parenexpr ::= '(' expression ')' - ExprAST *ParseParenExpr(); + ExprAST *ParseParenExpr(String *IDNames, bool callFromCondition); /// bracketsexpr ::= '{' expression '}' - ExprAST *ParseBracketsExpr(); + ExprAST *ParseBracketsExpr(String *IDNames, bool callFromCondition); /// quotesexpr ::= '"' expression '"' ExprAST *ParseQuotesExpr(); @@ -68,16 +77,16 @@ class IoTScenario { /// ::= identifierexpr /// ::= numberexpr /// ::= parenexpr - ExprAST *ParsePrimary(String *IDNames); + ExprAST *ParsePrimary(String *IDNames, bool callFromCondition); /// binoprhs /// ::= ('+' primary)* - ExprAST *ParseBinOpRHS(int ExprPrec, ExprAST *LHS, String *IDNames); + ExprAST *ParseBinOpRHS(int ExprPrec, ExprAST *LHS, String *IDNames, bool callFromCondition); /// expression /// ::= primary binoprhs /// - ExprAST *ParseExpression(String *IDNames); + ExprAST *ParseExpression(String *IDNames, bool callFromCondition); int getLastChar(); fs::File file; diff --git a/src/WsServer.cpp b/src/WsServer.cpp index 5d3b0ba1..98b60aba 100644 --- a/src/WsServer.cpp +++ b/src/WsServer.cpp @@ -98,6 +98,8 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) strFromFile.replace("{\"scen\":\"", ""); strFromFile.replace("\\n", "\n"); strFromFile.replace("\\\"", "\""); + strFromFile.replace(";", " "); + strFromFile.replace("\\t", " "); strFromFile.remove(strFromFile.length() - 2, 2); } myfile.close(); diff --git a/src/classes/IoTScenario.cpp b/src/classes/IoTScenario.cpp index 4e9b9284..e66d7a64 100644 --- a/src/classes/IoTScenario.cpp +++ b/src/classes/IoTScenario.cpp @@ -586,11 +586,22 @@ class BracketsExprAST : public ExprAST { //===----------------------------------------------------------------------===// int IoTScenario::getLastChar() { - if (file) { - LastChar = file.read(); - if (LastChar == 10) curLine++; - return LastChar; - } else return EOF; + if (mode == 0) { + if (file) { + LastChar = file.read(); + if (LastChar == 10) curLine++; + return LastChar; + } 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 - Возвращает следующий токен из стандартного потока ввода. @@ -728,7 +739,7 @@ ExprAST *IoTScenario::Error(const char *Str) { /// identifierexpr /// ::= identifier /// ::= identifier '(' expression* ')' -ExprAST *IoTScenario::ParseIdentifierExpr(String *IDNames) { +ExprAST *IoTScenario::ParseIdentifierExpr(String *IDNames, bool callFromCondition) { String IdName = IdentifierStr; String Cmd = ""; IoTItem *tmpItem = findIoTItem(IdName); @@ -754,7 +765,7 @@ ExprAST *IoTScenario::ParseIdentifierExpr(String *IDNames) { std::vector Args; if (CurTok != ')') { while (1) { - ExprAST *Arg = ParseExpression(IDNames); + ExprAST *Arg = ParseExpression(IDNames, callFromCondition); if (!Arg) return nullptr; Args.push_back(Arg); @@ -783,9 +794,9 @@ ExprAST *IoTScenario::ParseNumberExpr() { } /// parenexpr ::= '(' expression ')' -ExprAST *IoTScenario::ParseParenExpr() { +ExprAST *IoTScenario::ParseParenExpr(String *IDNames, bool callFromCondition) { getNextToken(); // получаем (. - ExprAST *V = ParseExpression(nullptr); + ExprAST *V = ParseExpression(IDNames, callFromCondition); if (!V) return nullptr; if (CurTok != ')') @@ -795,28 +806,31 @@ ExprAST *IoTScenario::ParseParenExpr() { } /// bracketsexpr ::= '{' expression '}' -ExprAST *IoTScenario::ParseBracketsExpr() { +ExprAST *IoTScenario::ParseBracketsExpr(String *IDNames, bool callFromCondition) { getNextToken(); // получаем {. std::vector bracketsList; - if (CurTok != '}') { - while (1) { - ExprAST *Expr = ParseExpression(nullptr); + + while (CurTok != '}') { + ExprAST *Expr = ParseExpression(IDNames, callFromCondition); if (!Expr) return nullptr; bracketsList.push_back(Expr); + + //if (CurTok == '}') break; - if (CurTok != ';') - return Error("Expected ';' in operation list"); - int ttok = getNextToken(); - if (!ttok) { - Error("Expected '}'"); - break; - } + //Serial.printf("ParseBracketsExpr CurTok = %d \n", CurTok); - if (CurTok == '}') break; + + + //if (CurTok != ';') + // return Error("Expected ';' in operation list"); + + //int ttok = getNextToken(); + if (CurTok == tok_eof) { + return Error("Expected '}'"); + } } - } - //getNextToken(); // получаем }. + getNextToken(); // получаем }. return new BracketsExprAST(bracketsList); } @@ -833,14 +847,14 @@ ExprAST *IoTScenario::ParseIfExpr(String *IDNames) { getNextToken(); // Получаем if. // условие. - ExprAST *Cond = ParseExpression(IDNames); + ExprAST *Cond = ParseExpression(IDNames, true); if (!Cond) return nullptr; if (CurTok != tok_then) return Error("expected then"); getNextToken(); // Получаем then - ExprAST *Then = ParseExpression(nullptr); + ExprAST *Then = ParseExpression(IDNames, false); if (!Then) return nullptr; // if (CurTok != tok_else) @@ -848,7 +862,8 @@ ExprAST *IoTScenario::ParseIfExpr(String *IDNames) { ExprAST *Else = nullptr; if (CurTok == tok_else) { getNextToken(); - Else = ParseExpression(nullptr); + Else = ParseExpression(IDNames, false); + if (!Else) return nullptr; } return new IfExprAST(Cond, Then, Else, IDNames); @@ -858,24 +873,24 @@ ExprAST *IoTScenario::ParseIfExpr(String *IDNames) { /// ::= identifierexpr /// ::= numberexpr /// ::= parenexpr -ExprAST *IoTScenario::ParsePrimary(String *IDNames) { +ExprAST *IoTScenario::ParsePrimary(String *IDNames, bool callFromCondition) { switch (CurTok) { default: Serial.println(CurTok); return Error("unknown token when expecting an expression"); case tok_identifier: { - if (IDNames) { + if (callFromCondition && IDNames) { String tmpstr = *IDNames; *IDNames = tmpstr + " " + IdentifierStr + " "; } - return ParseIdentifierExpr(IDNames); + return ParseIdentifierExpr(IDNames, callFromCondition); } case tok_number: return ParseNumberExpr(); case '(': - return ParseParenExpr(); + return ParseParenExpr(IDNames, callFromCondition); case '{': - return ParseBracketsExpr(); + return ParseBracketsExpr(IDNames, callFromCondition); case tok_string: return ParseQuotesExpr(); case tok_if: @@ -885,7 +900,7 @@ ExprAST *IoTScenario::ParsePrimary(String *IDNames) { /// binoprhs /// ::= ('+' primary)* -ExprAST *IoTScenario::ParseBinOpRHS(int ExprPrec, ExprAST *LHS, String *IDNames) { +ExprAST *IoTScenario::ParseBinOpRHS(int ExprPrec, ExprAST *LHS, String *IDNames, bool callFromCondition) { // Если это бинарный оператор, получаем его приоритет while (1) { int TokPrec = GetTokPrecedence(); @@ -900,14 +915,14 @@ ExprAST *IoTScenario::ParseBinOpRHS(int ExprPrec, ExprAST *LHS, String *IDNames) getNextToken(); // eat binop // Разобрать первичное выражение после бинарного оператора - ExprAST *RHS = ParsePrimary(IDNames); + ExprAST *RHS = ParsePrimary(IDNames, callFromCondition); if (!RHS) return nullptr; // Если BinOp связан с RHS меньшим приоритетом, чем оператор после RHS, // то берём часть вместе с RHS как LHS. int NextPrec = GetTokPrecedence(); if (TokPrec < NextPrec) { - RHS = ParseBinOpRHS(TokPrec + 1, RHS, IDNames); + RHS = ParseBinOpRHS(TokPrec + 1, RHS, IDNames, callFromCondition); if (RHS == nullptr) return nullptr; } @@ -919,43 +934,61 @@ ExprAST *IoTScenario::ParseBinOpRHS(int ExprPrec, ExprAST *LHS, String *IDNames) /// expression /// ::= primary binoprhs /// -ExprAST *IoTScenario::ParseExpression(String *IDNames) { - ExprAST *LHS = ParsePrimary(IDNames); +ExprAST *IoTScenario::ParseExpression(String *IDNames, bool callFromCondition) { + ExprAST *LHS = ParsePrimary(IDNames, callFromCondition); if (!LHS) return nullptr; - return ParseBinOpRHS(0, LHS, IDNames); + return ParseBinOpRHS(0, LHS, IDNames, callFromCondition); } void IoTScenario::loadScenario(String fileName) { // подготавливаем контекст для чтения и интерпретации файла - if (file) file.close(); - file = FileFS.open(fileName, "r"); + if (mode == 0) { + if (file) file.close(); + file = FileFS.open(fileName.c_str(), "r"); + if (!file) { + Error("Open file scenario error"); + 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 (!file) { - Error("Open file scenario error"); - return; - } + if (mode == 0 && !file) return; + LastChar = 0; CurTok = 0; - file.seek(0); curLine = 1; - - while ((getNextToken()) != EOF) { - switch (CurTok) { - case tok_if: { - IDNames = ""; // сбрасываем накопитель встречающихся идентификаторов в условии - ExprAST *tmpAST = ParseIfExpr(&IDNames); - if (!tmpAST) { - Error("IF Expr wrong."); - break; - } + charCount = 0; - if (tmpAST->hasEventIdName(eventIdName)) { - tmpAST->exec(); - } - delete tmpAST; - break;} + if (mode == 0) file.seek(0); + + if (mode < 2) { + while (CurTok != EOF) { + switch (CurTok) { + case tok_if: { + IDNames = ""; // сбрасываем накопитель встречающихся идентификаторов в условии + ExprAST *tmpAST = ParseIfExpr(&IDNames); + if (!tmpAST) { + Error("IF Expr wrong."); + break; + } + + if (tmpAST->hasEventIdName(eventIdName)) { + tmpAST->exec(); + } + delete tmpAST; + break;} + default: getNextToken(); break; + } } } } diff --git a/src/modules/virtual/Timer/modinfo.json b/src/modules/virtual/Timer/modinfo.json index f2256166..6d4bb7b4 100644 --- a/src/modules/virtual/Timer/modinfo.json +++ b/src/modules/virtual/Timer/modinfo.json @@ -27,11 +27,33 @@ "moduleDesc": "Добавляет инструмент таймеров обратного отсчета для организации периодичных операций или логических конструкций. Часто используется как вспомогательный элемент для автоматизации.", "propInfo": { "int": "Задает размер в секундах одного шага(тика) таймера.", - "countDown": "Начальное значение таймера, с которого начинается обратный отсчет.", + "countDown": "Начальное значение таймера, с которого начинается обратный отсчет. countDown=0 - бесконечный счет (имеет смысл при ticker=1, иначе таймер будет выключен), countDown=-1 - отключает таймер совсем (используется для запуска системы с выключенным таймером)", "ticker": "Генерировать(1) или нет(0) события при каждом тике таймера.", "repeat": "Сбрасывать(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, "devices": {