diff --git a/.gitignore b/.gitignore index 704a67ba47..8ce82c5784 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,7 @@ lib/readme.txt src/Custom.h /ESPEasy test/output_export.cpp +.vscode +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json diff --git a/src/ESPEasy-Globals.h b/src/ESPEasy-Globals.h index 351595ede6..388f959c82 100644 --- a/src/ESPEasy-Globals.h +++ b/src/ESPEasy-Globals.h @@ -325,6 +325,8 @@ #define RULESETS_MAX 4 #define RULES_BUFFER_SIZE 64 #define NAME_FORMULA_LENGTH_MAX 40 +#define RULES_IF_MAX_NESTING_LEVEL 4 + #define UDP_PACKETSIZE_MAX 2048 #define PIN_MODE_UNDEFINED 0 diff --git a/src/Misc.ino b/src/Misc.ino index 560d487f3f..bf9c1bee16 100644 --- a/src/Misc.ino +++ b/src/Misc.ino @@ -2136,10 +2136,10 @@ String rulesProcessingFile(String fileName, String& event) bool match = false; bool codeBlock = false; bool isCommand = false; - bool conditional = false; - bool condition = false; - bool ifBranche = false; - bool ifBrancheJustMatch = false; + bool condition[RULES_IF_MAX_NESTING_LEVEL]; + bool ifBranche[RULES_IF_MAX_NESTING_LEVEL]; + byte ifBlock = 0; + byte fakeIfBlock = 0; byte buf[RULES_BUFFER_SIZE]; int len = 0; @@ -2162,8 +2162,8 @@ String rulesProcessingFile(String fileName, String& event) parseCompleteNonCommentLine( line, event, log, match, codeBlock, isCommand, - conditional, condition, - ifBranche, ifBrancheJustMatch); + condition, ifBranche, + ifBlock, fakeIfBlock); backgroundtasks(); } @@ -2184,10 +2184,11 @@ void parseCompleteNonCommentLine( bool& match, bool& codeBlock, bool& isCommand, - bool& conditional, - bool& condition, - bool& ifBranche, - bool& ifBrancheJustMatch) { + bool condition[], + bool ifBranche[], + byte& ifBlock, + byte& fakeIfBlock) +{ isCommand = true; // Strip comments @@ -2199,6 +2200,23 @@ void parseCompleteNonCommentLine( // only parse [xxx#yyy] if we have a matching ruleblock or need to eval the "on" (no codeBlock) // This to avoid waisting CPU time... line = parseTemplate(line, line.length()); + + if (match && !fakeIfBlock) { + // substitution of %eventvalue% is made here so it can be used on if statement too + if (event.charAt(0) == '!') + { + line.replace(F("%eventvalue%"), event); // substitute %eventvalue% with literal event string if starting with '!' + } + else + { + int equalsPos = event.indexOf("="); + if (equalsPos > 0) + { + String tmpString = event.substring(equalsPos + 1); + line.replace(F("%eventvalue%"), tmpString); // substitute %eventvalue% with the actual value from the event + } + } + } } line.trim(); @@ -2213,6 +2231,8 @@ void parseCompleteNonCommentLine( { if (line.startsWith(F("on "))) { + ifBlock = 0; + fakeIfBlock = 0; line = line.substring(3); int split = line.indexOf(F(" do")); if (split != -1) @@ -2249,6 +2269,8 @@ void parseCompleteNonCommentLine( isCommand = false; codeBlock = false; match = false; + ifBlock = 0; + fakeIfBlock = 0; } if (Settings.SerialLogLevel == LOG_LEVEL_DEBUG_DEV){ @@ -2265,8 +2287,8 @@ void parseCompleteNonCommentLine( processMatchedRule( lcAction, action, event, log, match, codeBlock, isCommand, - conditional, condition, - ifBranche, ifBrancheJustMatch); + condition, ifBranche, + ifBlock, fakeIfBlock); } } @@ -2275,70 +2297,101 @@ void processMatchedRule( bool& match, bool& codeBlock, bool& isCommand, - bool& conditional, - bool& condition, - bool& ifBranche, - bool& ifBrancheJustMatch) + bool condition[], + bool ifBranche[], + byte& ifBlock, + byte& fakeIfBlock) { - int split = lcAction.indexOf(F("if ")); // check for optional "if" condition - if (!lcAction.startsWith(F("elseif "))) { - if (split != -1) - { // There is some 'if ' in the string. - conditional = true; - String check = lcAction.substring(split + 3); - - log = F("[if "); - log += check; - log += F("]="); - condition = ifBrancheJustMatch == false && conditionMatchExtended(check); - if(condition == true) + if (fakeIfBlock) + isCommand = false; + else if (ifBlock) + if (condition[ifBlock-1] != ifBranche[ifBlock-1]) + isCommand = false; + int split = lcAction.indexOf(F("elseif ")); // check for optional "elseif" condition + if (split != -1) + { + isCommand = false; + if (ifBlock && !fakeIfBlock) + { + if (ifBranche[ifBlock-1]) { - ifBrancheJustMatch = true; + if (condition[ifBlock-1]) + ifBranche[ifBlock-1] = false; + else + { + String check = lcAction.substring(split + 7); + log = F("Lev."); + log += String(ifBlock); + log += F(": [elseif "); + log += check; + log += "]="; + condition[ifBlock-1] = conditionMatchExtended(check); + log += toString(condition[ifBlock-1]); + addLog(LOG_LEVEL_DEBUG, log); + } } - ifBranche = true; - isCommand = false; - log += toString(condition); - addLog(LOG_LEVEL_DEBUG, log); } } else - { // Starts with 'elseif ' - String check = lcAction.substring(7); - log = F("[elseif "); - log += check; - log += "]="; - condition = ifBrancheJustMatch == false && conditionMatchExtended(check); - if(condition == true) + { + split = lcAction.indexOf(F("if ")); // check for optional "if" condition + if (split != -1) { - ifBrancheJustMatch = true; + if (ifBlock < RULES_IF_MAX_NESTING_LEVEL) + { + if (isCommand) + { + ifBlock++; + String check = lcAction.substring(split + 3); + log = F("Lev."); + log += String(ifBlock); + log += F(": [if "); + log += check; + log += F("]="); + condition[ifBlock-1] = conditionMatchExtended(check); + ifBranche[ifBlock-1] = true; + log += toString(condition[ifBlock-1]); + addLog(LOG_LEVEL_DEBUG, log); + } + else + fakeIfBlock++; + } + else + { + fakeIfBlock++; + log = F("Lev."); + log += String(ifBlock); + log = F(": Error: IF Nesting level exceeded!"); + addLog(LOG_LEVEL_ERROR, log); + } + isCommand = false; } - ifBranche = true; - isCommand = false; - log += toString(condition); - addLog(LOG_LEVEL_DEBUG, log); } - if (lcAction == "else") // in case of an "else" block of actions, set ifBranche to false + if ((lcAction == F("else")) && !fakeIfBlock) // in case of an "else" block of actions, set ifBranche to false { - ifBranche = false; + ifBranche[ifBlock-1] = false; isCommand = false; if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - String log = F("else = "); - log += toString(conditional && (condition == ifBranche)); + log = F("Lev."); + log += String(ifBlock); + log += F(": [else]="); + log += toString(condition[ifBlock-1] == ifBranche[ifBlock-1]); addLog(LOG_LEVEL_DEBUG, log); } } - if (lcAction == "endif") // conditional block ends here + if (lcAction == F("endif")) // conditional block ends here { - conditional = false; + if (fakeIfBlock) + fakeIfBlock--; + else if (ifBlock) + ifBlock--; isCommand = false; - ifBranche = false; - ifBrancheJustMatch = false; } // process the action if it's a command and unconditional, or conditional and the condition matches the if or else block. - if (isCommand && ((!conditional) || (conditional && (condition == ifBranche)))) + if (isCommand) { if (event.charAt(0) == '!') { @@ -2415,19 +2468,21 @@ boolean ruleMatch(String& event, String& rule) // Special handling of literal string events, they should start with '!' if (event.charAt(0) == '!') { - int pos = rule.indexOf('#'); - if (pos == -1) // no # sign in rule, use 'wildcard' match on event 'source' - { - tmpEvent = event.substring(0,rule.length()); - tmpRule = rule; - } - - pos = rule.indexOf('*'); + int pos = rule.indexOf('*'); if (pos != -1) // a * sign in rule, so use a'wildcard' match on message { tmpEvent = event.substring(0,pos-1); tmpRule = rule.substring(0,pos-1); } + else + { + pos = rule.indexOf('#'); + if (pos == -1) // no # sign in rule, use 'wildcard' match on event 'source' + { + tmpEvent = event.substring(0,rule.length()); + tmpRule = rule; + } + } if (tmpEvent.equalsIgnoreCase(tmpRule)) return true;