Introduce Expression in Rules

Support use an expression as paramter in some rule commands, include Var<x>, Mem<x> and Ruletimer<x>.
Expression is constructed by constants (float number), variables (var<x>, mem<x>, Time, Uptime, Sunrise, Sunset),  operators and round brackets.
Currently support 6 operators, order by priority from high to low:
^  (power)
% (modulo)
*, /
+, -
Commands examples:
Var1 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + uptime / (2 + MEM2)
Ruletimer4 Time - Sunrise + MEM2/2
This commit is contained in:
Laurent 2019-02-12 21:46:42 -05:00
parent e779c7bdca
commit 4b4b3709ad
12 changed files with 1042 additions and 2 deletions

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Ivan Seidel
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,325 @@
/*
LinkedList.h - V1.1 - Generic LinkedList implementation
Works better with FIFO, because LIFO will need to
search the entire List to find the last one;
For instructions, go to https://github.com/ivanseidel/LinkedList
Created by Ivan Seidel Gomes, March, 2013.
Released into the public domain.
*/
#ifndef LinkedList_h
#define LinkedList_h
#include <stddef.h>
template<class T>
struct ListNode
{
T data;
ListNode<T> *next;
};
template <typename T>
class LinkedList{
protected:
int _size;
ListNode<T> *root;
ListNode<T> *last;
// Helps "get" method, by saving last position
ListNode<T> *lastNodeGot;
int lastIndexGot;
// isCached should be set to FALSE
// everytime the list suffer changes
bool isCached;
ListNode<T>* getNode(int index);
public:
LinkedList();
~LinkedList();
/*
Returns current size of LinkedList
*/
virtual int size();
/*
Adds a T object in the specified index;
Unlink and link the LinkedList correcly;
Increment _size
*/
virtual bool add(int index, T);
/*
Adds a T object in the end of the LinkedList;
Increment _size;
*/
virtual bool add(T);
/*
Adds a T object in the start of the LinkedList;
Increment _size;
*/
virtual bool unshift(T);
/*
Set the object at index, with T;
Increment _size;
*/
virtual bool set(int index, T);
/*
Remove object at index;
If index is not reachable, returns false;
else, decrement _size
*/
virtual T remove(int index);
/*
Remove last object;
*/
virtual T pop();
/*
Remove first object;
*/
virtual T shift();
/*
Get the index'th element on the list;
Return Element if accessible,
else, return false;
*/
virtual T get(int index);
/*
Clear the entire array
*/
virtual void clear();
};
// Initialize LinkedList with false values
template<typename T>
LinkedList<T>::LinkedList()
{
root=NULL;
last=NULL;
_size=0;
lastNodeGot = root;
lastIndexGot = 0;
isCached = false;
}
// Clear Nodes and free Memory
template<typename T>
LinkedList<T>::~LinkedList()
{
ListNode<T>* tmp;
while(root!=NULL)
{
tmp=root;
root=root->next;
delete tmp;
}
last = NULL;
_size=0;
isCached = false;
}
/*
Actualy "logic" coding
*/
template<typename T>
ListNode<T>* LinkedList<T>::getNode(int index){
int _pos = 0;
ListNode<T>* current = root;
// Check if the node trying to get is
// immediatly AFTER the previous got one
if(isCached && lastIndexGot <= index){
_pos = lastIndexGot;
current = lastNodeGot;
}
while(_pos < index && current){
current = current->next;
_pos++;
}
// Check if the object index got is the same as the required
if(_pos == index){
isCached = true;
lastIndexGot = index;
lastNodeGot = current;
return current;
}
return false;
}
template<typename T>
int LinkedList<T>::size(){
return _size;
}
template<typename T>
bool LinkedList<T>::add(int index, T _t){
if(index >= _size)
return add(_t);
if(index == 0)
return unshift(_t);
ListNode<T> *tmp = new ListNode<T>(),
*_prev = getNode(index-1);
tmp->data = _t;
tmp->next = _prev->next;
_prev->next = tmp;
_size++;
isCached = false;
return true;
}
template<typename T>
bool LinkedList<T>::add(T _t){
ListNode<T> *tmp = new ListNode<T>();
tmp->data = _t;
tmp->next = NULL;
if(root){
// Already have elements inserted
last->next = tmp;
last = tmp;
}else{
// First element being inserted
root = tmp;
last = tmp;
}
_size++;
isCached = false;
return true;
}
template<typename T>
bool LinkedList<T>::unshift(T _t){
if(_size == 0)
return add(_t);
ListNode<T> *tmp = new ListNode<T>();
tmp->next = root;
tmp->data = _t;
root = tmp;
_size++;
isCached = false;
return true;
}
template<typename T>
bool LinkedList<T>::set(int index, T _t){
// Check if index position is in bounds
if(index < 0 || index >= _size)
return false;
getNode(index)->data = _t;
return true;
}
template<typename T>
T LinkedList<T>::pop(){
if(_size <= 0)
return T();
isCached = false;
if(_size >= 2){
ListNode<T> *tmp = getNode(_size - 2);
T ret = tmp->next->data;
delete(tmp->next);
tmp->next = NULL;
last = tmp;
_size--;
return ret;
}else{
// Only one element left on the list
T ret = root->data;
delete(root);
root = NULL;
last = NULL;
_size = 0;
return ret;
}
}
template<typename T>
T LinkedList<T>::shift(){
if(_size <= 0)
return T();
if(_size > 1){
ListNode<T> *_next = root->next;
T ret = root->data;
delete(root);
root = _next;
_size --;
isCached = false;
return ret;
}else{
// Only one left, then pop()
return pop();
}
}
template<typename T>
T LinkedList<T>::remove(int index){
if (index < 0 || index >= _size)
{
return T();
}
if(index == 0)
return shift();
if (index == _size-1)
{
return pop();
}
ListNode<T> *tmp = getNode(index - 1);
ListNode<T> *toDelete = tmp->next;
T ret = toDelete->data;
tmp->next = tmp->next->next;
delete(toDelete);
_size--;
isCached = false;
return ret;
}
template<typename T>
T LinkedList<T>::get(int index){
ListNode<T> *tmp = getNode(index);
return (tmp ? tmp->data : T());
}
template<typename T>
void LinkedList<T>::clear(){
while(size() > 0)
shift();
}
#endif

View File

@ -0,0 +1,171 @@
# LinkedList
This library was developed targeting **`Arduino`** applications. However, works just great with any C++.
Implementing a buffer for objects takes time. If we are not in the mood, we just create an `array[1000]` with enough size.
The objective of this library is to create a pattern for projects.
If you need to use a List of: `int`, `float`, `objects`, `Lists` or `Wales`. **This is what you are looking for.**
With a simple but powerful caching algorithm, you can get subsequent objects much faster than usual. Tested without any problems with Lists bigger than 2000 members.
## Installation
1. [Download](https://github.com/ivanseidel/LinkedList/archive/master.zip) the Latest release from gitHub.
2. Unzip and modify the Folder name to "LinkedList" (Remove the '-version')
3. Paste the modified folder on your Library folder (On your `Libraries` folder inside Sketchbooks or Arduino software).
4. Reopen the Arduino software.
**If you are here, because another Library requires this class, just don't waste time reading bellow. Install and ready.**
-------------------------
## Getting started
### The `LinkedList` class
In case you don't know what a LinkedList is and what it's used for, take a quick look at [Wikipedia::LinkedList](https://en.wikipedia.org/wiki/Linked_list) before continuing.
#### To declare a LinkedList object
```c++
// Instantiate a LinkedList that will hold 'integer'
LinkedList<int> myLinkedList = LinkedList<int>();
// Or just this
LinkedList<int> myLinkedList;
// But if you are instantiating a pointer LinkedList...
LinkedList<int> *myLinkedList = new LinkedList<int>();
// If you want a LinkedList with any other type such as 'MyClass'
// Make sure you call delete(MyClass) when you remove!
LinkedList<MyClass> *myLinkedList = new LinkedList<MyClass>();
```
#### Getting the size of the linked list
```c++
// To get the size of a linked list, make use of the size() method
int theSize = myList.size();
// Notice that if it's pointer to the linked list, you should use -> instead
int theSize = myList->size();
```
#### Adding elements
```c++
// add(obj) method will insert at the END of the list
myList.add(myObject);
// add(index, obj) method will try to insert the object at the specified index
myList.add(0, myObject); // Add at the beginning
myList.add(3, myObject); // Add at index 3
// unshift(obj) method will insert the object at the beginning
myList.unshift(myObject);
```
#### Getting elements
```c++
// get(index) will return the element at index
// (notice that the start element is 0, not 1)
// Get the FIRST element
myObject = myList.get(0);
// Get the third element
myObject = myList.get(2);
// Get the LAST element
myObject = myList.get(myList.size() - 1);
```
#### Changing elements
```c++
// set(index, obj) method will change the object at index to obj
// Change the first element to myObject
myList.set(0, myObject);
// Change the third element to myObject
myList.set(2, myObject);
// Change the LAST element of the list
myList.set(myList.size() - 1, myObject);
```
#### Removing elements
```c++
// remove(index) will remove and return the element at index
// Remove the first object
myList.remove(0);
// Get and Delete the third element
myDeletedObject = myList.remove(2);
// pop() will remove and return the LAST element
myDeletedObject = myList.pop();
// shift() will remove and return the FIRST element
myDeletedObject = myList.shift();
// clear() will erase the entire list, leaving it with 0 elements
// NOTE: Clear wont DELETE/FREE memory from Pointers, if you
// are using Classes/Poiners, manualy delete and free those.
myList.clear();
```
------------------------
## Library Reference
### `ListNode` struct
- `T` `ListNode::data` - The object data
- `ListNode<T>` `*next` - Pointer to the next Node
### `LinkedList` class
**`boolean` methods returns if succeeded**
- `LinkedList<T>::LinkedList()` - Constructor.
- `LinkedList<T>::~LinkedList()` - Destructor. Clear Nodes to minimize memory. Does not free pointer memory.
- `int` `LinkedList<T>::size()` - Returns the current size of the list.
- `bool` `LinkedList<T>::add(T)` - Add element T at the END of the list.
- `bool` `LinkedList<T>::add(int index, T)` - Add element T at `index` of the list.
- `bool` `LinkedList<T>::unshift(T)` - Add element T at the BEGINNING of the list.
- `bool` `LinkedList<T>::set(int index, T)` - Set the element at `index` to T.
- `T` `LinkedList<T>::remove(int index)` - Remove element at `index`. Return the removed element. Does not free pointer memory
- `T` `LinkedList<T>::pop()` - Remove the LAST element. Return the removed element.
- `T` `LinkedList<T>::shift()` - Remove the FIRST element. Return the removed element.
- `T` `LinkedList<T>::get(int index)` - Return the element at `index`.
- `void` `LinkedList<T>::clear()` - Removes all elements. Does not free pointer memory.
- **protected** `int` `LinkedList<T>::_size` - Holds the cached size of the list.
- **protected** `ListNode<T>` `LinkedList<T>::*root` - Holds the root node of the list.
- **protected** `ListNode<T>` `LinkedList<T>::*last` - Holds the last node of the list.
- **protected** `ListNode<T>*` `LinkedList<T>::getNode(int index)` - Returns the `index` node of the list.
### Version History
* `1.1 (2013-07-20)`: Cache implemented. Getting subsequent objects is now O(N). Before, O(N^2).
* `1.0 (2013-07-20)`: Original release
![LinkedList](https://d2weczhvl823v0.cloudfront.net/ivanseidel/LinkedList/trend.png)

View File

@ -0,0 +1,81 @@
/*
LinkedList Example
Link: http://github.com/ivanseidel/LinkedList
Example Created by
Tom Stewart, github.com/tastewar
Edited by:
Ivan Seidel, github.com/ivanseidel
*/
#include <LinkedList.h>
// Let's define a new class
class Animal {
public:
char *name;
bool isMammal;
};
char catname[]="kitty";
char dogname[]="doggie";
char emuname[]="emu";
LinkedList<Animal*> myAnimalList = LinkedList<Animal*>();
void setup()
{
Serial.begin(9600);
Serial.println("Hello!" );
// Create a Cat
Animal *cat = new Animal();
cat->name = catname;
cat->isMammal = true;
// Create a dog
Animal *dog = new Animal();
dog->name = dogname;
dog->isMammal = true;
// Create a emu
Animal *emu = new Animal();
emu->name = emuname;
emu->isMammal = false; // just an example; no offense to pig lovers
// Add animals to list
myAnimalList.add(cat);
myAnimalList.add(emu);
myAnimalList.add(dog);
}
void loop() {
Serial.print("There are ");
Serial.print(myAnimalList.size());
Serial.print(" animals in the list. The mammals are: ");
int current = 0;
Animal *animal;
for(int i = 0; i < myAnimalList.size(); i++){
// Get animal from list
animal = myAnimalList.get(i);
// If its a mammal, then print it's name
if(animal->isMammal){
// Avoid printing spacer on the first element
if(current++)
Serial.print(", ");
// Print animal name
Serial.print(animal->name);
}
}
Serial.println(".");
while (true); // nothing else to do, loop forever
}

View File

@ -0,0 +1,58 @@
/*
LinkedList Example
Link: http://github.com/ivanseidel/LinkedList
Example Created by
Tom Stewart, github.com/tastewar
Edited by:
Ivan Seidel, github.com/ivanseidel
*/
#include <LinkedList.h>
LinkedList<int> myList = LinkedList<int>();
void setup()
{
Serial.begin(9600);
Serial.println("Hello!");
// Add some stuff to the list
int k = -240,
l = 123,
m = -2,
n = 222;
myList.add(n);
myList.add(0);
myList.add(l);
myList.add(17);
myList.add(k);
myList.add(m);
}
void loop() {
int listSize = myList.size();
Serial.print("There are ");
Serial.print(listSize);
Serial.print(" integers in the list. The negative ones are: ");
// Print Negative numbers
for (int h = 0; h < listSize; h++) {
// Get value from list
int val = myList.get(h);
// If the value is negative, print it
if (val < 0) {
Serial.print(" ");
Serial.print(val);
}
}
while (true); // nothing else to do, loop forever
}

View File

@ -0,0 +1,28 @@
#######################################
# Syntax Coloring
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
LinkedList KEYWORD1
ListNode KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
size KEYWORD2
add KEYWORD2
unshift KEYWORD2
set KEYWORD2
remove KEYWORD2
pop KEYWORD2
shift KEYWORD2
get KEYWORD2
clear KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################

View File

@ -0,0 +1,12 @@
{
"name": "LinkedList",
"keywords": "pattern",
"description": "A fully implemented LinkedList (int, float, objects, Lists or Wales) made to work with Arduino projects",
"repository":
{
"type": "git",
"url": "https://github.com/ivanseidel/LinkedList.git"
},
"frameworks": "arduino",
"platforms": "*"
}

View File

@ -0,0 +1,9 @@
name=LinkedList
version=1.2.3
author=Ivan Seidel <ivanseidel@gmail.com>
maintainer=Ivan Seidel <ivanseidel@gmail.com>
sentence=A fully implemented LinkedList made to work with Arduino projects
paragraph=The objective of this library is to create a pattern for projects. If you need to use a List of: int, float, objects, Lists or Wales. This is what you are looking for.
category=Data Processing
url=https://github.com/ivanseidel/LinkedList
architectures=*

View File

@ -278,6 +278,9 @@
// -- Rules ---------------------------------------
#define USE_RULES // Add support for rules (+4k4 code)
#ifdef USE_RULES
#define USE_EXPRESSION // Add support for expression evaluation in rules (+3k1 code, +28 bytes mem)
#endif
// -- Internal Analog input -----------------------
#define USE_ADC_VCC // Display Vcc in Power status. Disable for use as Analog input on selected devices

View File

@ -64,7 +64,9 @@
#ifdef USE_SPI
#include <SPI.h> // SPI support, TFT
#endif // USE_SPI
#ifdef USE_EXPRESSION
#include <LinkedList.h> // Import LinkedList library
#endif
// Structs
#include "settings.h"

View File

@ -154,7 +154,7 @@ char* subStr(char* dest, char* str, const char *delim, int index)
return sub;
}
double CharToDouble(char *str)
double CharToDouble(const char *str)
{
// simple ascii to double, because atof or strtod are too large
char strbuf[24];

View File

@ -90,6 +90,19 @@
#define MAXIMUM_COMPARE_OPERATOR COMPARE_OPERATOR_SMALLER_EQUAL
const char kCompareOperators[] PROGMEM = "=\0>\0<\0|\0==!=>=<=";
#ifdef USE_EXPRESSION
const char kExpressionOperators[] PROGMEM = "+-*/%^";
#define EXPRESSION_OPERATOR_ADD 0
#define EXPRESSION_OPERATOR_SUBTRACT 1
#define EXPRESSION_OPERATOR_MULTIPLY 2
#define EXPRESSION_OPERATOR_DIVIDEDBY 3
#define EXPRESSION_OPERATOR_MODULO 4
#define EXPRESSION_OPERATOR_POWER 5
const uint8_t kExpressionOperatorsPriorities[] PROGMEM = {1, 1, 2, 2, 3, 4};
#define MAX_EXPRESSION_OPERATOR_PRIORITY 4
#endif //USE_EXPRESSION
enum RulesCommands { CMND_RULE, CMND_RULETIMER, CMND_EVENT, CMND_VAR, CMND_MEM, CMND_ADD, CMND_SUB, CMND_MULT, CMND_SCALE, CMND_CALC_RESOLUTION };
const char kRulesCommands[] PROGMEM = D_CMND_RULE "|" D_CMND_RULETIMER "|" D_CMND_EVENT "|" D_CMND_VAR "|" D_CMND_MEM "|" D_CMND_ADD "|" D_CMND_SUB "|" D_CMND_MULT "|" D_CMND_SCALE "|" D_CMND_CALC_RESOLUTION ;
@ -562,6 +575,310 @@ void RulesTeleperiod(void)
rules_teleperiod = 0;
}
#ifdef USE_EXPRESSION
/********************************************************************************************/
/*
* Parse a number value
* Input:
* pNumber - A char pointer point to a digit started string (guaranteed)
* value - Reference a double variable used to accept the result
* Output:
* pNumber - Pointer forward to next character after the number
* value - double type, the result value
* Return:
* true - succeed
* false - failed
*/
bool findNextNumber(char * &pNumber, double &value)
{
bool bSucceed = false;
String sNumber = "";
while (*pNumber) {
if (isdigit(*pNumber) || (*pNumber == '.')) {
sNumber += *pNumber;
pNumber++;
} else {
break;
}
}
if (sNumber.length() > 0) {
value = CharToDouble(sNumber.c_str());
bSucceed = true;
}
return bSucceed;
}
/********************************************************************************************/
/*
* Parse a variable (like VAR1, MEM3) and get its value (double type)
* Input:
* pVarname - A char pointer point to a variable name string
* value - Reference a double variable used to accept the result
* Output:
* pVarname - Pointer forward to next character after the variable
* value - double type, the result value
* Return:
* true - succeed
* false - failed
*/
bool findNextVariableValue(char * &pVarname, double &value)
{
bool succeed = false;
value = 0;
String sVarName = "";
while (*pVarname) {
if (isalpha(*pVarname) || isdigit(*pVarname)) {
sVarName.concat(*pVarname);
pVarname++;
} else {
break;
}
}
sVarName.toUpperCase();
if (sVarName.startsWith("VAR")) {
int index = sVarName.substring(3).toInt();
if (index > 0 && index <= MAX_RULE_VARS) {
value = CharToDouble(vars[index -1]);
succeed = true;
}
} else if (sVarName.startsWith("MEM")) {
int index = sVarName.substring(3).toInt();
if (index > 0 && index <= MAX_RULE_MEMS) {
value = CharToDouble(Settings.mems[index -1]);
succeed = true;
}
} else if (sVarName.equals("TIME")) {
value = GetMinutesPastMidnight();
succeed = true;
} else if (sVarName.equals("UPTIME")) {
value = GetMinutesUptime();
succeed = true;
#if defined(USE_TIMERS) && defined(USE_SUNRISE)
} else if (sVarName.equals("SUNRISE")) {
value = GetSunMinutes(0);
succeed = true;
} else if (sVarName.equals("SUNSET")) {
value = GetSunMinutes(1);
succeed = true;
#endif
}
return succeed;
}
/********************************************************************************************/
/*
* Find next object in expression and evaluate it
* An object could be:
* - A float number start with a digit, like 0.787
* - A variable name, like VAR1, MEM3
* - An expression enclosed with a pair of round brackets, (.....)
* Input:
* pointer - A char pointer point to a place of the expression string
* value - Reference a double variable used to accept the result
* Output:
* pointer - Pointer forward to next character after next object
* value - double type, the result value
* Return:
* true - succeed
* false - failed
*/
bool findNextObjectValue(char * &pointer, double &value)
{
bool bSucceed = false;
while (*pointer)
{
if (isspace(*pointer)) { //Skip leading spaces
pointer++;
continue;
}
if (isdigit(*pointer)) { //This object is a number
bSucceed = findNextNumber(pointer, value);
break;
} else if (isalpha(*pointer)) { //Should be a variable like VAR12, MEM1
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);
bSucceed = true;
}
break;
} else { //No number, no variable, no expression, then invalid object.
break;
}
}
return bSucceed;
}
/********************************************************************************************/
/*
* Find next operator in expression
* An operator could be: +, - , * , / , %, ^
* Input:
* pointer - A char pointer point to a place of the expression string
* op - Reference to a variable used to accept the result
* Output:
* pointer - Pointer forward to next character after next operator
* op - The operator. 0, 1, 2, 3, 4, 5
* Return:
* true - succeed
* false - failed
*/
bool findNextOperator(char * &pointer, int8_t &op)
{
bool bSucceed = false;
while (*pointer)
{
if (isspace(*pointer)) { //Skip leading spaces
pointer++;
continue;
}
if (char *pch = strchr(kExpressionOperators, *pointer)) { //If it is an operator
op = (int8_t)(pch - kExpressionOperators);
pointer++;
bSucceed = true;
}
break;
}
return bSucceed;
}
/********************************************************************************************/
/*
* Calculate a simple expression composed by 2 value and 1 operator, like 2 * 3
* Input:
* pointer - A char pointer point to a place of the expression string
* value - Reference a double variable used to accept the result
* Output:
* pointer - Pointer forward to next character after next object
* value - double type, the result value
* Return:
* true - succeed
* false - failed
*/
double calculateTwoValues(double v1, double v2, uint8_t op)
{
switch (op)
{
case EXPRESSION_OPERATOR_ADD:
return v1 + v2;
case EXPRESSION_OPERATOR_SUBTRACT:
return v1 - v2;
case EXPRESSION_OPERATOR_MULTIPLY:
return v1 * v2;
case EXPRESSION_OPERATOR_DIVIDEDBY:
return (0 == v2) ? 0 : (v1 / v2);
case EXPRESSION_OPERATOR_MODULO:
return (0 == v2) ? 0 : (int(v1) % int(v2));
case EXPRESSION_OPERATOR_POWER:
return FastPrecisePow(v1, v2);
}
return 0;
}
/********************************************************************************************/
/*
* Parse and evaluate an expression.
* For example: "10 * ( MEM2 + 1) / 2"
* Right now, only support operators listed here: (order by priority)
* Priority 4: ^ (power)
* Priority 3: % (modulo, always get integer result)
* Priority 2: *, /
* Priority 1: +, -
* Input:
* expression - The expression to be evaluated
* len - Length of the expression
* Return:
* double - result.
* 0 - if the expression is invalid
* An example:
* MEM1 = 3, MEM2 = 6, VAR2 = 15, VAR10 = 80
* At beginning, the expression might be complicated like: 3.14 * (MEM1 * (10 + VAR2 ^2) - 100) % 10 + VAR10 / (2 + MEM2)
* We are going to scan the whole expression, evaluate each object.
* Finally we will have a value list:.
* Order Object Value
* 0 3.14 3.14
* 1 (MEM1 * (10 + VAR2 ^2) - 100) 605
* 2 10 10
* 3 VAR10 80
* 4 (2 + MEM2) 8
* And an operator list:
* Order Operator Priority
* 0 * 2
* 1 % 3
* 2 + 1
* 3 / 2
*/
double evaluateExpression(const char * expression, unsigned int len)
{
char expbuf[len + 1];
memcpy(expbuf, expression, len);
expbuf[len] = '\0';
char * scan_pointer = expbuf;
LinkedList<double> object_values;
LinkedList<int8_t> operators;
int8_t op;
double va;
//Find and add the value of first object
if (findNextObjectValue(scan_pointer, va)) {
object_values.add(va);
} else {
return 0;
}
while (*scan_pointer)
{
if (findNextOperator(scan_pointer, op)
&& *scan_pointer
&& findNextObjectValue(scan_pointer, va))
{
operators.add(op);
object_values.add(va);
} else {
//No operator followed or no more object after this operator, we done.
break;
}
}
//Going to evaluate the whole expression
//Calculate by order of operator priorities. Looking for all operators with specified priority (from High to Low)
for (int8_t priority = MAX_EXPRESSION_OPERATOR_PRIORITY; priority>0; priority--) {
int index = 0;
while (index < operators.size()) {
if (priority == kExpressionOperatorsPriorities[(operators.get(index))]) { //need to calculate the operator first
//get current object value and remove the next object with current operator
va = calculateTwoValues(object_values.get(index), object_values.remove(index + 1), operators.remove(index));
//Replace the current value with the result
object_values.set(index, va);
} else {
index++;
}
}
}
return object_values.get(0);
}
#endif //USE_EXPRESSION
bool RulesCommand(void)
{
char command[CMDSZ];
@ -620,7 +937,12 @@ bool RulesCommand(void)
}
else if ((CMND_RULETIMER == command_code) && (index > 0) && (index <= MAX_RULE_TIMERS)) {
if (XdrvMailbox.data_len > 0) {
#ifdef USE_EXPRESSION
double timer_set = evaluateExpression(XdrvMailbox.data, XdrvMailbox.data_len);
rules_timer[index -1] = (timer_set > 0) ? millis() + (1000 * timer_set) : 0;
#else
rules_timer[index -1] = (XdrvMailbox.payload > 0) ? millis() + (1000 * XdrvMailbox.payload) : 0;
#endif //USE_EXPRESSION
}
mqtt_data[0] = '\0';
for (uint8_t i = 0; i < MAX_RULE_TIMERS; i++) {
@ -636,14 +958,22 @@ bool RulesCommand(void)
}
else if ((CMND_VAR == command_code) && (index > 0) && (index <= MAX_RULE_VARS)) {
if (XdrvMailbox.data_len > 0) {
#ifdef USE_EXPRESSION
dtostrfd(evaluateExpression(XdrvMailbox.data, XdrvMailbox.data_len), Settings.flag2.calc_resolution, vars[index -1]);
#else
strlcpy(vars[index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(vars[index -1]));
#endif //USE_EXPRESSION
bitSet(vars_event, index -1);
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, vars[index -1]);
}
else if ((CMND_MEM == command_code) && (index > 0) && (index <= MAX_RULE_MEMS)) {
if (XdrvMailbox.data_len > 0) {
#ifdef USE_EXPRESSION
dtostrfd(evaluateExpression(XdrvMailbox.data, XdrvMailbox.data_len), Settings.flag2.calc_resolution, Settings.mems[index -1]);
#else
strlcpy(Settings.mems[index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(Settings.mems[index -1]));
#endif //USE_EXPRESSION
bitSet(mems_event, index -1);
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.mems[index -1]);