Merge pull request #6386 from laurentdong/SupportIfUpdated

Support if updated
This commit is contained in:
Theo Arends 2019-09-09 17:43:03 +02:00 committed by GitHub
commit b150decb92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 608 additions and 48 deletions

View File

@ -302,6 +302,7 @@
//#define USE_SCRIPT_FATFS 4 // Script: Add FAT FileSystem Support
// #define USE_EXPRESSION // Add support for expression evaluation in rules (+3k2 code, +64 bytes mem)
// #define SUPPORT_IF_STATEMENT // Add support for IF statement in rules (+4k2 code, -332 bytes mem)
// #define SUPPORT_MQTT_EVENT // Support trigger event with MQTT subscriptions (+3k5 code)
// -- Optional modules ----------------------------

View File

@ -120,8 +120,6 @@ int16_t save_data_counter; // Counter and flag for config save
RulesBitfield rules_flag; // Rule state flags (16 bits)
uint8_t state_250mS = 0; // State 250msecond per second flag
uint8_t latching_relay_pulse = 0; // Latching relay pulse timer
uint8_t backlog_index = 0; // Command backlog index
uint8_t backlog_pointer = 0; // Command backlog pointer
uint8_t sleep; // Current copy of Settings.sleep
uint8_t blinkspeed = 1; // LED blink rate
uint8_t pin[GPIO_MAX]; // Possible pin configurations
@ -166,7 +164,16 @@ char serial_in_buffer[INPUT_BUFFER_SIZE]; // Receive buffer
char mqtt_data[MESSZ]; // MQTT publish buffer and web page ajax buffer
char log_data[LOGSZ]; // Logging
char web_log[WEB_LOG_SIZE] = {'\0'}; // Web log buffer
String backlog[MAX_BACKLOG]; // Command backlog
#ifdef SUPPORT_IF_STATEMENT
#include <LinkedList.h>
LinkedList<String> backlog; // Command backlog implemented with LinkedList
#define BACKLOG_EMPTY (backlog.size() == 0)
#else
uint8_t backlog_index = 0; // Command backlog index
uint8_t backlog_pointer = 0; // Command backlog pointer
String backlog[MAX_BACKLOG]; // Command backlog buffer
#define BACKLOG_EMPTY (backlog_pointer == backlog_index)
#endif
/********************************************************************************************/
@ -862,12 +869,16 @@ void Every100mSeconds(void)
// Backlog
if (TimeReached(backlog_delay)) {
if ((backlog_pointer != backlog_index) && !backlog_mutex) {
if (!BACKLOG_EMPTY && !backlog_mutex) {
backlog_mutex = true;
#ifdef SUPPORT_IF_STATEMENT
ExecuteCommand((char*)backlog.shift().c_str(), SRC_BACKLOG);
#else
ExecuteCommand((char*)backlog[backlog_pointer].c_str(), SRC_BACKLOG);
backlog_mutex = false;
backlog_pointer++;
if (backlog_pointer >= MAX_BACKLOG) { backlog_pointer = 0; }
#endif
backlog_mutex = false;
}
}
}
@ -928,7 +939,7 @@ void Every250mSeconds(void)
case 0: // Every x.0 second
PerformEverySecond();
if (ota_state_flag && (backlog_pointer == backlog_index)) {
if (ota_state_flag && BACKLOG_EMPTY) {
ota_state_flag--;
if (2 == ota_state_flag) {
ota_url = Settings.ota_url;
@ -999,7 +1010,7 @@ void Every250mSeconds(void)
if (MidnightNow()) {
XsnsCall(FUNC_SAVE_AT_MIDNIGHT);
}
if (save_data_counter && (backlog_pointer == backlog_index)) {
if (save_data_counter && BACKLOG_EMPTY) {
save_data_counter--;
if (save_data_counter <= 0) {
if (Settings.flag.save_state) {
@ -1019,7 +1030,7 @@ void Every250mSeconds(void)
save_data_counter = Settings.save_data;
}
}
if (restart_flag && (backlog_pointer == backlog_index)) {
if (restart_flag && BACKLOG_EMPTY) {
if ((214 == restart_flag) || (215 == restart_flag) || (216 == restart_flag)) {
char storage_wifi[sizeof(Settings.sta_ssid) +
sizeof(Settings.sta_pwd)];

View File

@ -215,10 +215,17 @@ void CommandHandler(char* topic, uint8_t* data, uint32_t data_len)
void CmndBacklog(void)
{
if (XdrvMailbox.data_len) {
#ifdef SUPPORT_IF_STATEMENT
char *blcommand = strtok(XdrvMailbox.data, ";");
while ((blcommand != nullptr) && (backlog.size() < MAX_BACKLOG))
#else
uint32_t bl_pointer = (!backlog_pointer) ? MAX_BACKLOG -1 : backlog_pointer;
bl_pointer--;
char *blcommand = strtok(XdrvMailbox.data, ";");
while ((blcommand != nullptr) && (backlog_index != bl_pointer)) {
while ((blcommand != nullptr) && (backlog_index != bl_pointer))
#endif
{
while(true) {
blcommand = Trim(blcommand);
if (!strncasecmp_P(blcommand, PSTR(D_CMND_BACKLOG), strlen(D_CMND_BACKLOG))) {
@ -228,17 +235,27 @@ void CmndBacklog(void)
}
}
if (*blcommand != '\0') {
#ifdef SUPPORT_IF_STATEMENT
if (backlog.size() < MAX_BACKLOG) {
backlog.add(blcommand);
}
#else
backlog[backlog_index] = String(blcommand);
backlog_index++;
if (backlog_index >= MAX_BACKLOG) backlog_index = 0;
#endif
}
blcommand = strtok(nullptr, ";");
}
// ResponseCmndChar(D_JSON_APPENDED);
mqtt_data[0] = '\0';
} else {
bool blflag = (backlog_pointer == backlog_index);
bool blflag = BACKLOG_EMPTY;
#ifdef SUPPORT_IF_STATEMENT
backlog.clear();
#else
backlog_pointer = backlog_index;
#endif
ResponseCmndChar(blflag ? D_JSON_EMPTY : D_JSON_ABORTED);
}
}

View File

@ -78,6 +78,7 @@
#define D_CMND_CALC_RESOLUTION "CalcRes"
#define D_CMND_SUBSCRIBE "Subscribe"
#define D_CMND_UNSUBSCRIBE "Unsubscribe"
#define D_CMND_IF "If"
#define D_JSON_INITIATED "Initiated"
@ -106,6 +107,16 @@ const char kCompareOperators[] PROGMEM = "=\0>\0<\0|\0==!=>=<=";
const uint8_t kExpressionOperatorsPriorities[] PROGMEM = {1, 1, 2, 2, 3, 4};
#define MAX_EXPRESSION_OPERATOR_PRIORITY 4
#define LOGIC_OPERATOR_AND 1
#define LOGIC_OPERATOR_OR 2
#define IF_BLOCK_INVALID -1
#define IF_BLOCK_ANY 0
#define IF_BLOCK_ELSEIF 1
#define IF_BLOCK_ELSE 2
#define IF_BLOCK_ENDIF 3
#endif // USE_EXPRESSION
const char kRulesCommands[] PROGMEM = "|" // No prefix
@ -113,6 +124,9 @@ const char kRulesCommands[] PROGMEM = "|" // No prefix
D_CMND_ADD "|" D_CMND_SUB "|" D_CMND_MULT "|" D_CMND_SCALE "|" D_CMND_CALC_RESOLUTION
#ifdef SUPPORT_MQTT_EVENT
"|" D_CMND_SUBSCRIBE "|" D_CMND_UNSUBSCRIBE
#endif
#ifdef SUPPORT_IF_STATEMENT
"|" D_CMND_IF
#endif
;
@ -121,6 +135,9 @@ void (* const RulesCommand[])(void) PROGMEM = {
&CmndAddition, &CmndSubtract, &CmndMultiply, &CmndScale, &CmndCalcResolution
#ifdef SUPPORT_MQTT_EVENT
, &CmndSubscribe, &CmndUnsubscribe
#endif
#ifdef SUPPORT_IF_STATEMENT
, &CmndIf
#endif
};
@ -183,22 +200,13 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule)
rule_task = rule.substring(5, pos); // "INA219" or "SYSTEM"
}
String rule_name = rule.substring(pos +1); // "CURRENT>0.100" or "BOOT" or "%var1%" or "MINUTE|5"
char compare_operator[3];
int8_t compare = COMPARE_OPERATOR_NONE;
for (int32_t i = MAXIMUM_COMPARE_OPERATOR; i >= 0; i--) {
snprintf_P(compare_operator, sizeof(compare_operator), kCompareOperators + (i *2));
if ((pos = rule_name.indexOf(compare_operator)) > 0) {
compare = i;
break;
}
}
String rule_expr = rule.substring(pos +1); // "CURRENT>0.100" or "BOOT" or "%var1%" or "MINUTE|5"
String rule_name, rule_param;
int8_t compareOperator = parseCompareExpression(rule_expr, rule_name, rule_param); //Parse the compare expression.Return operator and the left, right part of expression
char rule_svalue[CMDSZ] = { 0 };
float rule_value = 0;
if (compare != COMPARE_OPERATOR_NONE) {
String rule_param = rule_name.substring(pos + strlen(compare_operator));
if (compareOperator != COMPARE_OPERATOR_NONE) {
for (uint32_t i = 0; i < MAX_RULE_VARS; i++) {
snprintf_P(stemp, sizeof(stemp), PSTR("%%VAR%d%%"), i +1);
if (rule_param.startsWith(stemp)) {
@ -244,7 +252,6 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule)
} else {
rule_value = CharToFloat((char*)rule_svalue); // 0.1 - This saves 9k code over toFLoat()!
}
rule_name = rule_name.substring(0, pos); // "CURRENT"
}
// Step2: Search rule_task and rule_name
@ -268,7 +275,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule)
value = CharToFloat((char*)str_value);
int int_value = int(value);
int int_rule_value = int(rule_value);
switch (compare) {
switch (compareOperator) {
case COMPARE_OPERATOR_EXACT_DIVISION:
match = (int_rule_value && (int_value % int_rule_value) == 0);
break;
@ -313,6 +320,40 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule)
return match;
}
/********************************************************************************************/
/*
* Parse a comparison expression.
* Get 3 parts - left expression, compare operator and right expression.
* Input:
* expr - A comparison expression like VAR1 >= MEM1 + 10
* leftExpr - Used to accept returned left parts of expression
* rightExpr - Used to accept returned right parts of expression
* Output:
* leftExpr - Left parts of expression
* rightExpr - Right parts of expression
* Return:
* compare operator
* COMPARE_OPERATOR_NONE - failed
*/
int8_t parseCompareExpression(String &expr, String &leftExpr, String &rightExpr)
{
char compare_operator[3];
int8_t compare = COMPARE_OPERATOR_NONE;
int position;
for (int8_t i = MAXIMUM_COMPARE_OPERATOR; i >= 0; i--) {
snprintf_P(compare_operator, sizeof(compare_operator), kCompareOperators + (i *2));
if ((position = expr.indexOf(compare_operator)) > 0) {
compare = i;
leftExpr = expr.substring(0, position);
leftExpr.trim();
rightExpr = expr.substring(position + strlen(compare_operator));
rightExpr.trim();
break;
}
}
return compare;
}
/*******************************************************************************************/
bool RuleSetProcess(uint8_t rule_set, String &event_saved)
@ -389,7 +430,10 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved)
// Response_P(S_JSON_COMMAND_SVALUE, D_CMND_RULE, D_JSON_INITIATED);
// MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_RULE));
#ifdef SUPPORT_IF_STATEMENT
char *pCmd = command;
RulesPreprocessCommand(pCmd); //Do pre-process for IF statement
#endif
ExecuteCommand(command, SRC_RULE);
serviced = true;
if (stop_all_rules) { return serviced; } // If BREAK was used, Stop execution of this rule set
@ -810,6 +854,43 @@ void CmndUnsubscribe(void)
#endif // SUPPORT_MQTT_EVENT
#ifdef USE_EXPRESSION
/********************************************************************************************/
/*
* Looking for matched bracket - ")"
* Search buffer from current loction, skip all nested bracket pairs, find the matched close bracket.
* Input:
* pStart - Point to a char buffer start with "("
* Output:
* N/A
* Return:
* position of matched close bracket
*/
char * findClosureBracket(char * pStart)
{
char * pointer = pStart + 1;
//Look for the matched closure parenthesis.")"
bool bFindClosures = false;
uint8_t matchClosures = 1;
while (*pointer)
{
if (*pointer == ')') {
matchClosures--;
if (matchClosures == 0) {
bFindClosures = true;
break;
}
} else if (*pointer == '(') {
matchClosures++;
}
pointer++;
}
if (bFindClosures) {
return pointer;
} else {
return NULL;
}
}
/********************************************************************************************/
/*
* Parse a number value
@ -933,28 +1014,10 @@ bool findNextObjectValue(char * &pointer, float &value)
bSucceed = findNextVariableValue(pointer, value);
break;
} else if (*pointer == '(') { //It is a sub expression bracketed with ()
pointer++;
char * sub_exp_start = pointer; //Find out the sub expression between a pair of parenthesis. "()"
unsigned int sub_exp_len = 0;
//Look for the matched closure parenthesis.")"
bool bFindClosures = false;
uint8_t matchClosures = 1;
while (*pointer)
{
if (*pointer == ')') {
matchClosures--;
if (matchClosures == 0) {
sub_exp_len = pointer - sub_exp_start;
bFindClosures = true;
break;
}
} else if (*pointer == '(') {
matchClosures++;
}
pointer++;
}
if (bFindClosures) {
value = evaluateExpression(sub_exp_start, sub_exp_len);
char * closureBracket = findClosureBracket(pointer); //Get the position of closure bracket ")"
if (closureBracket != NULL) {
value = evaluateExpression(pointer+1, closureBracket - pointer - 2);
pointer = closureBracket + 1;
bSucceed = true;
}
break;
@ -1113,6 +1176,474 @@ float evaluateExpression(const char * expression, unsigned int len)
}
#endif // USE_EXPRESSION
#ifdef SUPPORT_IF_STATEMENT
void CmndIf()
{
if (XdrvMailbox.data_len > 0) {
char parameters[XdrvMailbox.data_len+1];
memcpy(parameters, XdrvMailbox.data, XdrvMailbox.data_len);
parameters[XdrvMailbox.data_len] = '\0';
ProcessIfStatement(parameters);
}
}
/********************************************************************************************/
/*
* Evaluate a comparison expression.
* Get the logic value of expression, true or false
* Input:
* expression - A comparison expression like VAR1 >= MEM1 + 10
* len - Length of expression
* Output:
* N/A
* Return:
* logic value of comparison expression
*/
bool evaluateComparisonExpression(const char *expression, int len)
{
bool bResult = true;
char expbuf[len + 1];
memcpy(expbuf, expression, len);
expbuf[len] = '\0';
String compare_expression = expbuf;
String leftExpr, rightExpr;
int8_t compareOp = parseCompareExpression(compare_expression, leftExpr, rightExpr);
double leftValue = evaluateExpression(leftExpr.c_str(), leftExpr.length());
double rightValue = evaluateExpression(rightExpr.c_str(), rightExpr.length());
switch (compareOp) {
case COMPARE_OPERATOR_EXACT_DIVISION:
bResult = (rightValue != 0 && leftValue == int(leftValue)
&& rightValue == int(rightValue) && (int(leftValue) % int(rightValue)) == 0);
break;
case COMPARE_OPERATOR_EQUAL:
bResult = leftExpr.equalsIgnoreCase(rightExpr); // Compare strings - this also works for hexadecimals
break;
case COMPARE_OPERATOR_BIGGER:
bResult = (leftValue > rightValue);
break;
case COMPARE_OPERATOR_SMALLER:
bResult = (leftValue < rightValue);
break;
case COMPARE_OPERATOR_NUMBER_EQUAL:
bResult = (leftValue == rightValue);
break;
case COMPARE_OPERATOR_NOT_EQUAL:
bResult = (leftValue != rightValue);
break;
case COMPARE_OPERATOR_BIGGER_EQUAL:
bResult = (leftValue >= rightValue);
break;
case COMPARE_OPERATOR_SMALLER_EQUAL:
bResult = (leftValue <= rightValue);
break;
}
return bResult;
}
/********************************************************************************************/
/*
* Looking for a logical operator, either "AND" or "OR"
* A logical operator is expected at this moment. If we find something else, this function will fail.
* Input:
* pointer - Point to a char buffer
* op - Used to accpet the logical operator type
* Output:
* Pointer - pointer will forward to next character after the logical operator.
* op - The logical operator type we found
* Return:
* true - succeed
* false - failed
*/
bool findNextLogicOperator(char * &pointer, int8_t &op)
{
bool bSucceed = false;
while (*pointer && isspace(*pointer)) {
//Skip spaces
pointer++;
}
if (*pointer) {
if (strncasecmp_P(pointer, PSTR("AND "), 4) == 0) {
op = LOGIC_OPERATOR_AND;
pointer += 4;
bSucceed = true;
} else if (strncasecmp_P(pointer, PSTR("OR "), 3) == 0) {
op = LOGIC_OPERATOR_OR;
pointer += 3;
bSucceed = true;
}
}
return bSucceed;
}
/********************************************************************************************/
/*
* Find next logical object and get its value
* A logical object could be:
* - A comparison expression.
* - A logical expression bracketed with a pair of parenthesis.
* Input:
* pointer - A char pointer point to a start of logical object
* value - Used to accept the result value
* Output:
* pointer - Pointer forward to next character after the object
* value - boolean type, the value of the logical object.
* Return:
* true - succeed
* false - failed
*/
bool findNextLogicObjectValue(char * &pointer, bool &value)
{
bool bSucceed = false;
while (*pointer && isspace(*pointer)) {
//Skip leading spaces
pointer++;
}
char * pExpr = pointer;
while (*pointer) {
if (isalpha(*pointer)
&& (strncasecmp_P(pointer, PSTR("AND "), 4) == 0
|| strncasecmp_P(pointer, PSTR("OR "), 3) == 0))
{ //We have a logic operator, should stop
value = evaluateComparisonExpression(pExpr, pointer - pExpr);
bSucceed = true;
break;
} else if (*pointer == '(') { //It is a sub expression bracketed with ()
char * closureBracket = findClosureBracket(pointer); //Get the position of closure bracket ")"
if (closureBracket != NULL) {
value = evaluateLogicalExpression(pointer+1, closureBracket - pointer - 2);
pointer = closureBracket + 1;
bSucceed = true;
}
break;
}
pointer++;
}
if (!bSucceed && pointer > pExpr) {
//The whole buffer is an comparison expression
value = evaluateComparisonExpression(pExpr, pointer - pExpr);
bSucceed = true;
}
return bSucceed;
}
/********************************************************************************************/
/*
* Evaluate a logical expression
* Logic expression is constructed with multiple comparison expressions and logical
* operators between them. For example: Mem1==0 AND (time > sunrise + 60).
* Parenthesis are allowed to change the priority of logical operators.
* Input:
* expression - A logical expression
* len - Length of the expression
* Output:
* N/A
* Return:
* boolean - the value of logical expression
*/
bool evaluateLogicalExpression(const char * expression, int len)
{
bool bResult = false;
//Make a copy first
char expbuff[len + 1];
memcpy(expbuff, expression, len);
expbuff[len] = '\0';
//snprintf_P(log_data, sizeof(log_data), PSTR("EvalLogic: |%s|"), expbuff);
//AddLog(LOG_LEVEL_DEBUG);
char * pointer = expbuff;
LinkedList<bool> values;
LinkedList<int8_t> logicOperators;
//Find first comparison expression
bool bValue;
if (findNextLogicObjectValue(pointer, bValue)) {
values.add(bValue);
} else {
return false;
}
int8_t op;
while (*pointer) {
if (findNextLogicOperator(pointer, op)
&& (*pointer) && findNextLogicObjectValue(pointer, bValue))
{
logicOperators.add(op);
values.add(bValue);
} else {
break;
}
}
//Calculate all "AND" first
int index = 0;
while (index < logicOperators.size()) {
if (logicOperators.get(index) == LOGIC_OPERATOR_AND) {
values.set(index, values.get(index) && values.get(index+1));
values.remove(index + 1);
logicOperators.remove(index);
} else {
index++;
}
}
//Then, calculate all "OR"
index = 0;
while (index < logicOperators.size()) {
if (logicOperators.get(index) == LOGIC_OPERATOR_OR) {
values.set(index, values.get(index) || values.get(index+1));
values.remove(index + 1);
logicOperators.remove(index);
} else {
index++;
}
}
return values.get(0);
}
/********************************************************************************************/
/*
* This function search in a buffer to find out an IF block start from current position
* Note: All the tokens found during the searching will be changed to NULL terminated string.
* Please make a copy before call this function if you still need it.
* Input:
* pointer - Point to a NULL end string buffer with the commands
* lenWord - Accept the length of block end word
* block_type - The block type you are looking for.
* Output:
* pointer - pointer point to the end of if block.
* lenWord - The length of block end word ("ENDIF", "ELSEIF", "ELSE")
* Return:
* The block type we find.
* IF_BLOCK_INVALID - Failed.
*/
int8_t findIfBlock(char * &pointer, int &lenWord, int8_t block_type)
{
int8_t foundBlock = IF_BLOCK_INVALID;
//First break into words delimited by space or ";"
const char * word;
while (*pointer) {
if (!isalpha(*pointer)) {
pointer++;
continue;
}
word = pointer;
while (*pointer && isalpha(*pointer)) {
pointer++;
}
lenWord = pointer - word;
if (2 == lenWord && 0 == strncasecmp_P(word, PSTR("IF"), 2)) {
//if we find a new "IF" that means this is nested if block
//Try to finish this nested if block
if (findIfBlock(pointer, lenWord, IF_BLOCK_ENDIF) != IF_BLOCK_ENDIF) {
//If failed, we done.
break;
}
} else if ( (IF_BLOCK_ENDIF == block_type || IF_BLOCK_ANY == block_type)
&& (5 == lenWord) && (0 == strncasecmp_P(word, PSTR("ENDIF"), 5)))
{
//Find an "ENDIF"
foundBlock = IF_BLOCK_ENDIF;
break;
} else if ( (IF_BLOCK_ELSEIF == block_type || IF_BLOCK_ANY == block_type)
&& (6 == lenWord) && (0 == strncasecmp_P(word, PSTR("ELSEIF"), 6)))
{
//Find an "ELSEIF"
foundBlock = IF_BLOCK_ELSEIF;
break;
} else if ( (IF_BLOCK_ELSE == block_type || IF_BLOCK_ANY == block_type)
&& (4 == lenWord) && (0 == strncasecmp_P(word, PSTR("ELSE"), 4)))
{
//Find an "ELSE"
foundBlock = IF_BLOCK_ELSE;
break;
}
}
return foundBlock;
}
/********************************************************************************************/
/*
* This function is used to execute a commands block in if statement when one of the condition is true.
* Input:
* commands - A char buffer include (but not limited) the commands block need to execute
* len - Length of the commands block
* Output:
N/A
* Return:
* void
*/
void ExecuteCommandBlock(const char * commands, int len)
{
char cmdbuff[len + 1]; //apply enough space
memcpy(cmdbuff, commands, len);
cmdbuff[len] = '\0';
//snprintf_P(log_data, sizeof(log_data), PSTR("ExecCmd: |%s|"), cmdbuff);
//AddLog(LOG_LEVEL_DEBUG);
char oneCommand[len + 1]; //To put one command
int insertPosition = 0; //When insert into backlog, we should do it by 0, 1, 2 ...
char * pos = cmdbuff;
int lenEndBlock = 0;
while (*pos) {
if (isspace(*pos) || '\x1e' == *pos || ';' == *pos) {
pos++;
continue;
}
if (strncasecmp_P(pos, PSTR("BACKLOG "), 8) == 0) {
//Skip "BACKLOG " and set not first command flag. So all followed command will be send to backlog
pos += 8;
continue;
}
if (strncasecmp_P(pos, PSTR("IF "), 3) == 0) {
//Has a nested IF statement
//Find the matched ENDIF
char *pEndif = pos + 3; //Skip "IF "
if (IF_BLOCK_ENDIF != findIfBlock(pEndif, lenEndBlock, IF_BLOCK_ENDIF)) {
//Cannot find matched endif, stop execution.
break;
}
//We has the whole IF statement, copy to oneCommand
memcpy(oneCommand, pos, pEndif - pos);
oneCommand[pEndif - pos] = '\0';
pos = pEndif;
} else { //Normal command
//Looking for the command end single - '\x1e'
char *pEndOfCommand = strpbrk(pos, "\x1e;");
if (NULL == pEndOfCommand) {
pEndOfCommand = pos + strlen(pos);
}
memcpy(oneCommand, pos, pEndOfCommand - pos);
oneCommand[pEndOfCommand - pos] = '\0';
pos = pEndOfCommand;
}
//Start to process current command we found
//Going to insert the command into backlog
String sCurrentCommand = oneCommand;
sCurrentCommand.trim();
if (sCurrentCommand.length() > 0
&& backlog.size() < MAX_BACKLOG && !backlog_mutex)
{
//Insert into backlog
backlog_mutex = true;
backlog.add(insertPosition, sCurrentCommand);
backlog_mutex = false;
insertPosition++;
}
}
return;
}
/********************************************************************************************/
/*
* Execute IF statement. This is the place to run a "IF ..." command.
* Input:
* statements - The IF statement we are going to process
* Output:
N/A
* Return:
* void
*/
void ProcessIfStatement(const char* statements)
{
String conditionExpression;
int len = strlen(statements);
char statbuff[len + 1];
memcpy(statbuff, statements, len + 1);
char *pos = statbuff;
int lenEndBlock = 0;
while (true) { //Each loop process one IF (or ELSEIF) block
//Find and test the condition expression followed the IF or ELSEIF
//Search for the open bracket first
while (*pos && *pos != '(') {
pos++;
}
if (NULL == *pos) break;
char * posEnd = findClosureBracket(pos);
if (true == evaluateLogicalExpression(pos + 1, posEnd - (pos + 1))) {
//Looking for matched "ELSEIF", "ELSE" or "ENDIF", then Execute this block
char * cmdBlockStart = posEnd + 1;
char * cmdBlockEnd = cmdBlockStart;
int8_t nextBlock = findIfBlock(cmdBlockEnd, lenEndBlock, IF_BLOCK_ANY);
if (IF_BLOCK_INVALID == nextBlock) {
//Failed
break;
}
ExecuteCommandBlock(cmdBlockStart, cmdBlockEnd - cmdBlockStart - lenEndBlock);
pos = cmdBlockEnd;
break;
} else { //Does not match the IF condition, going to check elseif and else
pos = posEnd + 1;
int8_t nextBlock = findIfBlock(pos, lenEndBlock, IF_BLOCK_ANY);
if (IF_BLOCK_ELSEIF == nextBlock) {
//Continue process next ELSEIF block like IF
continue;
} else if (IF_BLOCK_ELSE == nextBlock) {
//Looking for matched "ENDIF" then execute this block
char * cmdBlockEnd = pos;
int8_t nextBlock = findIfBlock(cmdBlockEnd, lenEndBlock, IF_BLOCK_ENDIF);
if (IF_BLOCK_ENDIF != nextBlock) {
//Failed
break;
}
ExecuteCommandBlock(pos, cmdBlockEnd - pos - lenEndBlock);
break;
} else { // IF_BLOCK_ENDIF == nextBlock
//We done
break;
}
}
}
}
/********************************************************************************************/
/*
* This function is called in Rules event handler to process any command between DO ... ENDON (BREAK)
* - Do escape (convert ";" into "\x1e") for all IF statements.
* Input:
* commands - The commands block need to execute
* Output:
N/A
* Return:
* void
*/
void RulesPreprocessCommand(char *pCommands)
{
char * cmd = pCommands;
int lenEndBlock = 0;
while (*cmd) {
//Skip all ";" and space between two commands
if (';' == *cmd || isspace(*cmd)) {
cmd++;
}
else if (strncasecmp_P(cmd, PSTR("IF "), 3) == 0) { //found IF block
//We are going to look for matched "ENDIF"
char * pIfStart = cmd;
char * pIfEnd = pIfStart + 3; //Skip "IF "
//int pIfStart = cmd - command; //"IF" statement block start at position (relative to command start)
if (IF_BLOCK_ENDIF == findIfBlock(pIfEnd, lenEndBlock, IF_BLOCK_ENDIF)) {
//Found the ENDIF
cmd = pIfEnd; //Will continue process from here
//Escapte from ";" to "\x1e".
//By remove all ";" in IF statement block, we can prevent backlog command cut the whole block as multiple commands
while (pIfStart < pIfEnd) {
if (';' == *pIfStart)
*pIfStart = '\x1e';
pIfStart++;
}
}
else { //Did not find the matched ENDIF, stop processing
break;
}
}
else { //Other commands, skip it
while (*cmd && ';' != *cmd) {
cmd++;
}
}
}
return;
}
#endif //SUPPORT_IF_STATEMENT
/*********************************************************************************************\
* Commands
\*********************************************************************************************/