mirror of https://github.com/arendst/Tasmota.git
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:
parent
e779c7bdca
commit
4b4b3709ad
|
@ -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.
|
|
@ -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
|
|
@ -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)
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
@ -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)
|
||||
#######################################
|
|
@ -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": "*"
|
||||
}
|
|
@ -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=*
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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]);
|
||||
|
|
Loading…
Reference in New Issue