From c9d0106007b259974e30dfa1b546a253c7a2a2ab Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Thu, 18 Jan 2024 16:32:28 +0100 Subject: [PATCH] Removed max number of 30 backlog entries --- CHANGELOG.md | 7 +- RELEASENOTES.md | 4 +- lib/default/LinkedList-1.2.3/LinkedList.h | 78 +--- .../LinkedList-1.2.3/LinkedList_with_sort.h | 419 ++++++++++++++++++ tasmota/include/tasmota.h | 1 - tasmota/my_user_config.h | 1 + tasmota/tasmota.ino | 24 +- tasmota/tasmota_support/support_command.ino | 25 +- .../tasmota_xdrv_driver/xdrv_07_domoticz.ino | 2 +- tasmota/tasmota_xdrv_driver/xdrv_10_rules.ino | 4 +- 10 files changed, 438 insertions(+), 127 deletions(-) create mode 100644 lib/default/LinkedList-1.2.3/LinkedList_with_sort.h diff --git a/CHANGELOG.md b/CHANGELOG.md index ff7287194..5ec363e55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,8 +13,8 @@ All notable changes to this project will be documented in this file. - ESP32 MI BLE support for Xiaomi LYWSD02MMC (#20381) - LVGL option to add `lv.keyboard` extra widget (#20496) - GUI sensor separators (#20495) -- Command ``TimedPower [,ON|OFF|TOGGLE|BLINK]`` executes ``Power [ON|OFF|TOGGLE|BLINK] `` and after executes ``Power [OFF|ON|TOGGLE|OFF]`` -- Berry add solidification of strings longer than 255 bytes +- Command ``TimedPower [,ON|OFF|TOGGLE|BLINK]`` executes ``Power [ON|OFF|TOGGLE|BLINK] `` and after executes ``Power [OFF|ON|TOGGLE|BLINK_OFF]`` +- Berry solidification of strings longer than 255 bytes (#20529) ### Breaking Changed @@ -30,9 +30,10 @@ All notable changes to this project will be documented in this file. - Zigbee ramdom crash in main page (#20481) - Web file upload response on upload error (#20340) - ESP32 shutter exception 6 (divide by zero) on ``ShutterMode 4`` (#20524) +- GPIOViewer exception 3 ### Removed - +- Max number of 30 backlog entries ## [13.3.0.2] 20240111 ### Added diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 9d1d594a4..8180640b4 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -146,6 +146,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm - Berry provide lightweight options for `tasmota.wifi/eth/memory/rtc` [#20448](https://github.com/arendst/Tasmota/issues/20448) - Berry `tasmota.webcolor` [#20454](https://github.com/arendst/Tasmota/issues/20454) - Berry `debug.caller` [#20470](https://github.com/arendst/Tasmota/issues/20470) +- Berry solidification of strings longer than 255 bytes [#20529](https://github.com/arendst/Tasmota/issues/20529) - LVGL `lv.str_arr` [#20480](https://github.com/arendst/Tasmota/issues/20480) - LVGL option to add `lv.keyboard` extra widget [#20496](https://github.com/arendst/Tasmota/issues/20496) - HASPmota `haspmota.page_show()` to change page [#20333](https://github.com/arendst/Tasmota/issues/20333) @@ -179,4 +180,5 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm - Berry claiming UART0 if needed [#20324](https://github.com/arendst/Tasmota/issues/20324) - Matter Contact sensor was not triggering any update [#20232](https://github.com/arendst/Tasmota/issues/20232) -### Removed \ No newline at end of file +### Removed +- Max number of 30 backlog entries diff --git a/lib/default/LinkedList-1.2.3/LinkedList.h b/lib/default/LinkedList-1.2.3/LinkedList.h index 64321cf01..4453126de 100644 --- a/lib/default/LinkedList-1.2.3/LinkedList.h +++ b/lib/default/LinkedList-1.2.3/LinkedList.h @@ -7,6 +7,8 @@ Created by Ivan Seidel Gomes, March, 2013. Released into the public domain. + + 20240118 - Removed sort functions not used by Tasmota (@arendst) */ @@ -39,8 +41,6 @@ protected: ListNode* getNode(int index); - ListNode* findEndOfSortedString(ListNode *p, int (*cmp)(T &, T &)); - public: LinkedList(); LinkedList(int sizeIndex, T _t); //initiate list size and default value @@ -96,11 +96,6 @@ public: */ virtual void clear(); - /* - Sort the list, given a comparison function - */ - virtual void sort(int (*cmp)(T &, T &)); - // add support to array brakets [] operator inline T& operator[](int index); inline T& operator[](size_t& i) { return this->get(i); } @@ -347,73 +342,4 @@ void LinkedList::clear(){ shift(); } -template -void LinkedList::sort(int (*cmp)(T &, T &)){ - if(_size < 2) return; // trivial case; - - for(;;) { - - ListNode **joinPoint = &root; - - while(*joinPoint) { - ListNode *a = *joinPoint; - ListNode *a_end = findEndOfSortedString(a, cmp); - - if(!a_end->next ) { - if(joinPoint == &root) { - last = a_end; - isCached = false; - return; - } - else { - break; - } - } - - ListNode *b = a_end->next; - ListNode *b_end = findEndOfSortedString(b, cmp); - - ListNode *tail = b_end->next; - - a_end->next = NULL; - b_end->next = NULL; - - while(a && b) { - if(cmp(a->data, b->data) <= 0) { - *joinPoint = a; - joinPoint = &a->next; - a = a->next; - } - else { - *joinPoint = b; - joinPoint = &b->next; - b = b->next; - } - } - - if(a) { - *joinPoint = a; - while(a->next) a = a->next; - a->next = tail; - joinPoint = &a->next; - } - else { - *joinPoint = b; - while(b->next) b = b->next; - b->next = tail; - joinPoint = &b->next; - } - } - } -} - -template -ListNode* LinkedList::findEndOfSortedString(ListNode *p, int (*cmp)(T &, T &)) { - while(p->next && cmp(p->data, p->next->data) <= 0) { - p = p->next; - } - - return p; -} - #endif diff --git a/lib/default/LinkedList-1.2.3/LinkedList_with_sort.h b/lib/default/LinkedList-1.2.3/LinkedList_with_sort.h new file mode 100644 index 000000000..64321cf01 --- /dev/null +++ b/lib/default/LinkedList-1.2.3/LinkedList_with_sort.h @@ -0,0 +1,419 @@ +/* + 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 + +template +struct ListNode +{ + T data; + ListNode *next; +}; + +template +class LinkedList{ + +protected: + int _size; + ListNode *root; + ListNode *last; + + // Helps "get" method, by saving last position + ListNode *lastNodeGot; + int lastIndexGot; + // isCached should be set to FALSE + // everytime the list suffer changes + bool isCached; + + ListNode* getNode(int index); + + ListNode* findEndOfSortedString(ListNode *p, int (*cmp)(T &, T &)); + +public: + LinkedList(); + LinkedList(int sizeIndex, T _t); //initiate list size and default value + ~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; + */ + 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(); + + /* + Sort the list, given a comparison function + */ + virtual void sort(int (*cmp)(T &, T &)); + + // add support to array brakets [] operator + inline T& operator[](int index); + inline T& operator[](size_t& i) { return this->get(i); } + inline const T& operator[](const size_t& i) const { return this->get(i); } + +}; + +// Initialize LinkedList with false values +template +LinkedList::LinkedList() +{ + root=NULL; + last=NULL; + _size=0; + + lastNodeGot = root; + lastIndexGot = 0; + isCached = false; +} + +// Clear Nodes and free Memory +template +LinkedList::~LinkedList() +{ + ListNode* tmp; + while(root!=NULL) + { + tmp=root; + root=root->next; + delete tmp; + } + last = NULL; + _size=0; + isCached = false; +} + +/* + Actualy "logic" coding +*/ + +template +ListNode* LinkedList::getNode(int index){ + + int _pos = 0; + ListNode* 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 NULL; +} + +template +int LinkedList::size(){ + return _size; +} + +template +LinkedList::LinkedList(int sizeIndex, T _t){ + for (int i = 0; i < sizeIndex; i++){ + add(_t); + } +} + +template +bool LinkedList::add(int index, T _t){ + + if(index >= _size) + return add(_t); + + if(index == 0) + return unshift(_t); + + ListNode *tmp = new ListNode(), + *_prev = getNode(index-1); + tmp->data = _t; + tmp->next = _prev->next; + _prev->next = tmp; + + _size++; + isCached = false; + + return true; +} + +template +bool LinkedList::add(T _t){ + + ListNode *tmp = new ListNode(); + 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 +bool LinkedList::unshift(T _t){ + + if(_size == 0) + return add(_t); + + ListNode *tmp = new ListNode(); + tmp->next = root; + tmp->data = _t; + root = tmp; + + _size++; + isCached = false; + + return true; +} + + +template +T& LinkedList::operator[](int index) { + return getNode(index)->data; +} + +template +bool LinkedList::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 +T LinkedList::pop(){ + if(_size <= 0) + return T(); + + isCached = false; + + if(_size >= 2){ + ListNode *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 +T LinkedList::shift(){ + if(_size <= 0) + return T(); + + if(_size > 1){ + ListNode *_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 +T LinkedList::remove(int index){ + if (index < 0 || index >= _size) + { + return T(); + } + + if(index == 0) + return shift(); + + if (index == _size-1) + { + return pop(); + } + + ListNode *tmp = getNode(index - 1); + ListNode *toDelete = tmp->next; + T ret = toDelete->data; + tmp->next = tmp->next->next; + delete(toDelete); + _size--; + isCached = false; + return ret; +} + + +template +T LinkedList::get(int index){ + ListNode *tmp = getNode(index); + + return (tmp ? tmp->data : T()); +} + +template +void LinkedList::clear(){ + while(size() > 0) + shift(); +} + +template +void LinkedList::sort(int (*cmp)(T &, T &)){ + if(_size < 2) return; // trivial case; + + for(;;) { + + ListNode **joinPoint = &root; + + while(*joinPoint) { + ListNode *a = *joinPoint; + ListNode *a_end = findEndOfSortedString(a, cmp); + + if(!a_end->next ) { + if(joinPoint == &root) { + last = a_end; + isCached = false; + return; + } + else { + break; + } + } + + ListNode *b = a_end->next; + ListNode *b_end = findEndOfSortedString(b, cmp); + + ListNode *tail = b_end->next; + + a_end->next = NULL; + b_end->next = NULL; + + while(a && b) { + if(cmp(a->data, b->data) <= 0) { + *joinPoint = a; + joinPoint = &a->next; + a = a->next; + } + else { + *joinPoint = b; + joinPoint = &b->next; + b = b->next; + } + } + + if(a) { + *joinPoint = a; + while(a->next) a = a->next; + a->next = tail; + joinPoint = &a->next; + } + else { + *joinPoint = b; + while(b->next) b = b->next; + b->next = tail; + joinPoint = &b->next; + } + } + } +} + +template +ListNode* LinkedList::findEndOfSortedString(ListNode *p, int (*cmp)(T &, T &)) { + while(p->next && cmp(p->data, p->next->data) <= 0) { + p = p->next; + } + + return p; +} + +#endif diff --git a/tasmota/include/tasmota.h b/tasmota/include/tasmota.h index b3fa0cbe4..3685fbc7e 100644 --- a/tasmota/include/tasmota.h +++ b/tasmota/include/tasmota.h @@ -220,7 +220,6 @@ const uint16_t MAX_LOGSZ = 700; // Max number of characters in log l const uint8_t SENSOR_MAX_MISS = 5; // Max number of missed sensor reads before deciding it's offline -const uint8_t MAX_BACKLOG = 30; // Max number of commands in backlog const uint32_t MIN_BACKLOG_DELAY = 200; // Minimal backlog delay in mSeconds const uint8_t MAX_TIMED_CMND = 16; // Max number of timed commands diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index d74edd0cb..b58adf14a 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -497,6 +497,7 @@ // -- Rules or Script ---------------------------- // Select none or only one of the below defines USE_RULES or USE_SCRIPT #define USE_RULES // Add support for rules (+8k code) +// #define SUPPORT_MQTT_EVENT // Support trigger event with MQTT subscriptions (+3k5 code) // #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 USER_RULE1 "" // Add rule1 data saved at initial firmware load or when command reset is executed diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index e7d4c455c..91a2c0b4f 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -411,11 +411,6 @@ struct TasmotaGlobal_t { uint8_t pwm_dimmer_led_bri; // Adjusted brightness LED level #endif // USE_PWM_DIMMER -#ifndef SUPPORT_IF_STATEMENT - uint8_t backlog_index; // Command backlog index - uint8_t backlog_pointer; // Command backlog pointer - String backlog[MAX_BACKLOG]; // Command backlog buffer -#endif tTimedCmnd timed_cmnd[MAX_TIMED_CMND]; // Timed command buffer #ifdef MQTT_DATA_STRING @@ -438,19 +433,15 @@ struct TasmotaGlobal_t { #endif // PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED #ifdef USE_BERRY - bool berry_fast_loop_enabled = false; // is Berry fast loop enabled, i.e. control is passed at each loop iteration + bool berry_fast_loop_enabled = false; // is Berry fast loop enabled, i.e. control is passed at each loop iteration #endif // USE_BERRY } TasmotaGlobal; TSettings* Settings = nullptr; -#ifdef SUPPORT_IF_STATEMENT - #include - LinkedList backlog; // Command backlog implemented with LinkedList - #define BACKLOG_EMPTY (backlog.size() == 0) -#else - #define BACKLOG_EMPTY (TasmotaGlobal.backlog_pointer == TasmotaGlobal.backlog_index) -#endif +#include +LinkedList backlog; // Command backlog implemented with LinkedList +#define BACKLOG_EMPTY (backlog.size() == 0) /*********************************************************************************************\ * Main @@ -794,14 +785,7 @@ void BacklogLoop(void) { bool nodelay_detected = false; String cmd; do { -#ifdef SUPPORT_IF_STATEMENT cmd = backlog.shift(); -#else - cmd = TasmotaGlobal.backlog[TasmotaGlobal.backlog_pointer]; - TasmotaGlobal.backlog[TasmotaGlobal.backlog_pointer] = (const char*) nullptr; // Force deallocation of the String internal memory - TasmotaGlobal.backlog_pointer++; - if (TasmotaGlobal.backlog_pointer >= MAX_BACKLOG) { TasmotaGlobal.backlog_pointer = 0; } -#endif nodelay_detected = !strncasecmp_P(cmd.c_str(), PSTR(D_CMND_NODELAY), strlen(D_CMND_NODELAY)); if (nodelay_detected) { nodelay = true; } } while (!BACKLOG_EMPTY && nodelay_detected); diff --git a/tasmota/tasmota_support/support_command.ino b/tasmota/tasmota_support/support_command.ino index 398fd11cd..8e6158b00 100644 --- a/tasmota/tasmota_support/support_command.ino +++ b/tasmota/tasmota_support/support_command.ino @@ -580,14 +580,7 @@ void CmndBacklog(void) { } char *blcommand = strtok(XdrvMailbox.data, ";"); -#ifdef SUPPORT_IF_STATEMENT - while ((blcommand != nullptr) && (backlog.size() < MAX_BACKLOG)) -#else - uint32_t bl_pointer = (!TasmotaGlobal.backlog_pointer) ? MAX_BACKLOG -1 : TasmotaGlobal.backlog_pointer; - bl_pointer--; - while ((blcommand != nullptr) && (TasmotaGlobal.backlog_index != bl_pointer)) -#endif - { + while (blcommand != nullptr) { // Ignore semicolon (; = end of single command) between brackets {} char *next = strchr(blcommand, '\0') +1; // Prepare for next ; while ((next != nullptr) && (ChrCount(blcommand, "{") != ChrCount(blcommand, "}"))) { // Check for valid {} pair @@ -606,17 +599,7 @@ void CmndBacklog(void) { } // Do not allow command Reset in backlog if ((*blcommand != '\0') && (strncasecmp_P(blcommand, PSTR(D_CMND_RESET), strlen(D_CMND_RESET)) != 0)) { -#ifdef SUPPORT_IF_STATEMENT - if (backlog.size() < MAX_BACKLOG) { - backlog.add(blcommand); - } -#else - TasmotaGlobal.backlog[TasmotaGlobal.backlog_index] = blcommand; - TasmotaGlobal.backlog_index++; - if (TasmotaGlobal.backlog_index >= MAX_BACKLOG) { - TasmotaGlobal.backlog_index = 0; - } -#endif + backlog.add(blcommand); } blcommand = strtok(nullptr, ";"); } @@ -625,11 +608,7 @@ void CmndBacklog(void) { TasmotaGlobal.backlog_timer = millis(); } else { bool blflag = BACKLOG_EMPTY; -#ifdef SUPPORT_IF_STATEMENT backlog.clear(); -#else - TasmotaGlobal.backlog_pointer = TasmotaGlobal.backlog_index; -#endif ResponseCmndChar(blflag ? PSTR(D_JSON_EMPTY) : PSTR(D_JSON_ABORTED)); } } diff --git a/tasmota/tasmota_xdrv_driver/xdrv_07_domoticz.ino b/tasmota/tasmota_xdrv_driver/xdrv_07_domoticz.ino index 4d4c54102..68bccfff7 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_07_domoticz.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_07_domoticz.ino @@ -657,7 +657,7 @@ void DomoticzSaveSettings(void) { snprintf_P(arg_idx, sizeof(arg_idx), PSTR("l%d"), i -1); cmnd += AddWebCommand(cmnd2, arg_idx, PSTR("0")); } - ExecuteWebCommand((char*)cmnd.c_str()); // Note: beware of max number of commands in backlog currently 30 (MAX_BACKLOG) + ExecuteWebCommand((char*)cmnd.c_str()); } #endif // USE_WEBSERVER diff --git a/tasmota/tasmota_xdrv_driver/xdrv_10_rules.ino b/tasmota/tasmota_xdrv_driver/xdrv_10_rules.ino index 13389b082..81f6ab23d 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_10_rules.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_10_rules.ino @@ -2066,7 +2066,7 @@ void ExecuteCommandBlock(const char * commands, int len) sCurrentCommand.trim(); /* if (sCurrentCommand.length() > 0 - && backlog.size() < MAX_BACKLOG && !TasmotaGlobal.backlog_mutex) + && !TasmotaGlobal.backlog_mutex) { //Insert into backlog TasmotaGlobal.backlog_mutex = true; @@ -2075,7 +2075,7 @@ void ExecuteCommandBlock(const char * commands, int len) insertPosition++; } */ - if ((sCurrentCommand.length() > 0) && (backlog.size() < MAX_BACKLOG)) { + if (sCurrentCommand.length() > 0) { //Insert into backlog backlog.add(insertPosition, sCurrentCommand); insertPosition++;