diff --git a/src/_P010_BH1750.ino b/src/_P010_BH1750.ino index 3825251e9..2c80aa923 100644 --- a/src/_P010_BH1750.ino +++ b/src/_P010_BH1750.ino @@ -34,6 +34,7 @@ boolean Plugin_010(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].SendDataOption = true; Device[deviceCount].TimerOption = true; Device[deviceCount].GlobalSyncOption = true; + Device[deviceCount].PluginStats = true; break; } @@ -94,6 +95,12 @@ boolean Plugin_010(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { + #ifdef USES_PLUGIN_STATS + if (ExtraTaskSettings.anyEnabledPluginStats()) { + initPluginTaskData(event->TaskIndex, new (std::nothrow) _StatsOnly_data_struct()); + } + #endif + success = true; break; } diff --git a/src/_P013_HCSR04.ino b/src/_P013_HCSR04.ino index 389560f4d..26d907cd0 100644 --- a/src/_P013_HCSR04.ino +++ b/src/_P013_HCSR04.ino @@ -55,6 +55,7 @@ boolean Plugin_013(uint8_t function, struct EventStruct *even Device[deviceCount].SendDataOption = true; Device[deviceCount].TimerOption = true; Device[deviceCount].GlobalSyncOption = true; + Device[deviceCount].PluginStats = true; break; } @@ -162,6 +163,12 @@ boolean Plugin_013(uint8_t function, struct EventStruct *even case PLUGIN_INIT: { + #ifdef USES_PLUGIN_STATS + if (ExtraTaskSettings.anyEnabledPluginStats()) { + initPluginTaskData(event->TaskIndex, new (std::nothrow) _StatsOnly_data_struct()); + } + #endif + int16_t max_distance = PCONFIG(2); int16_t measuringUnit = PCONFIG(3); int16_t filterType = PCONFIG(4); diff --git a/src/_P026_Sysinfo.ino b/src/_P026_Sysinfo.ino index 0a2717987..7a5a3796a 100644 --- a/src/_P026_Sysinfo.ino +++ b/src/_P026_Sysinfo.ino @@ -9,6 +9,7 @@ #include "src/ESPEasyCore/ESPEasyNetwork.h" #include "src/Globals/ESPEasyWiFiEvent.h" #include "src/Helpers/Memory.h" + #include "ESPEasy-Globals.h" #define PLUGIN_026 @@ -153,6 +154,12 @@ boolean Plugin_026(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { + #ifdef USES_PLUGIN_STATS + if (ExtraTaskSettings.anyEnabledPluginStats()) { + initPluginTaskData(event->TaskIndex, new (std::nothrow) _StatsOnly_data_struct()); + } + #endif + success = true; break; } diff --git a/src/_P047_i2c-soil-moisture-sensor.ino b/src/_P047_i2c-soil-moisture-sensor.ino index 710bd036a..6a239f3e6 100644 --- a/src/_P047_i2c-soil-moisture-sensor.ino +++ b/src/_P047_i2c-soil-moisture-sensor.ino @@ -60,6 +60,7 @@ boolean Plugin_047(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].SendDataOption = true; Device[deviceCount].TimerOption = true; Device[deviceCount].GlobalSyncOption = true; + Device[deviceCount].PluginStats = true; break; } @@ -123,6 +124,12 @@ boolean Plugin_047(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { + #ifdef USES_PLUGIN_STATS + if (ExtraTaskSettings.anyEnabledPluginStats()) { + initPluginTaskData(event->TaskIndex, new (std::nothrow) _StatsOnly_data_struct()); + } + #endif + success = true; break; } diff --git a/src/_P051_AM2320.ino b/src/_P051_AM2320.ino index 197e26cbe..638340f76 100644 --- a/src/_P051_AM2320.ino +++ b/src/_P051_AM2320.ino @@ -41,6 +41,7 @@ boolean Plugin_051(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].SendDataOption = true; Device[deviceCount].TimerOption = true; Device[deviceCount].GlobalSyncOption = true; + Device[deviceCount].PluginStats = true; break; } @@ -77,6 +78,12 @@ boolean Plugin_051(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { + #ifdef USES_PLUGIN_STATS + if (ExtraTaskSettings.anyEnabledPluginStats()) { + initPluginTaskData(event->TaskIndex, new (std::nothrow) _StatsOnly_data_struct()); + } + #endif + success = true; break; } diff --git a/src/_P056_SDS011-Dust.ino b/src/_P056_SDS011-Dust.ino index a92c3843c..77554cd3d 100644 --- a/src/_P056_SDS011-Dust.ino +++ b/src/_P056_SDS011-Dust.ino @@ -46,6 +46,7 @@ boolean Plugin_056(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].TimerOption = true; Device[deviceCount].TimerOptional = false; Device[deviceCount].GlobalSyncOption = true; + Device[deviceCount].PluginStats = true; break; } @@ -108,6 +109,12 @@ boolean Plugin_056(uint8_t function, struct EventStruct *event, String& string) if (Plugin_056_SDS) { delete Plugin_056_SDS; } + #ifdef USES_PLUGIN_STATS + if (ExtraTaskSettings.anyEnabledPluginStats()) { + initPluginTaskData(event->TaskIndex, new (std::nothrow) _StatsOnly_data_struct()); + } + #endif + const int16_t serial_rx = CONFIG_PIN1; const int16_t serial_tx = CONFIG_PIN2; const ESPEasySerialPort port = static_cast(CONFIG_PORT); diff --git a/src/_P066_VEML6040.ino b/src/_P066_VEML6040.ino index 62e0d88f5..758d76b25 100644 --- a/src/_P066_VEML6040.ino +++ b/src/_P066_VEML6040.ino @@ -44,6 +44,7 @@ boolean Plugin_066(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].TimerOption = true; Device[deviceCount].TimerOptional = false; Device[deviceCount].GlobalSyncOption = true; + Device[deviceCount].PluginStats = true; break; } @@ -111,6 +112,12 @@ boolean Plugin_066(uint8_t function, struct EventStruct *event, String& string) { VEML6040_Init(PCONFIG(1)); + #ifdef USES_PLUGIN_STATS + if (ExtraTaskSettings.anyEnabledPluginStats()) { + initPluginTaskData(event->TaskIndex, new (std::nothrow) _StatsOnly_data_struct()); + } + #endif + success = true; break; } diff --git a/src/_P067_HX711_Load_Cell.ino b/src/_P067_HX711_Load_Cell.ino index 48ab9dc28..8aefd07d6 100644 --- a/src/_P067_HX711_Load_Cell.ino +++ b/src/_P067_HX711_Load_Cell.ino @@ -160,6 +160,7 @@ boolean Plugin_067(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].TimerOption = true; Device[deviceCount].TimerOptional = false; Device[deviceCount].GlobalSyncOption = true; + Device[deviceCount].PluginStats = true; break; } @@ -308,6 +309,12 @@ boolean Plugin_067(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { + #ifdef USES_PLUGIN_STATS + if (ExtraTaskSettings.anyEnabledPluginStats()) { + initPluginTaskData(event->TaskIndex, new (std::nothrow) _StatsOnly_data_struct()); + } + #endif + int16_t pinSCL = CONFIG_PIN1; int16_t pinDOUT = CONFIG_PIN2; diff --git a/src/_P072_HDC1080.ino b/src/_P072_HDC1080.ino index 72011e691..31c483fdf 100644 --- a/src/_P072_HDC1080.ino +++ b/src/_P072_HDC1080.ino @@ -34,6 +34,7 @@ boolean Plugin_072(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].SendDataOption = true; Device[deviceCount].TimerOption = true; Device[deviceCount].GlobalSyncOption = true; + Device[deviceCount].PluginStats = true; break; } @@ -58,6 +59,12 @@ boolean Plugin_072(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { + #ifdef USES_PLUGIN_STATS + if (ExtraTaskSettings.anyEnabledPluginStats()) { + initPluginTaskData(event->TaskIndex, new (std::nothrow) _StatsOnly_data_struct()); + } + #endif + success = true; break; } diff --git a/src/_P076_HLW8012.ino b/src/_P076_HLW8012.ino index 61594d1e0..b9351f101 100644 --- a/src/_P076_HLW8012.ino +++ b/src/_P076_HLW8012.ino @@ -125,6 +125,7 @@ boolean Plugin_076(uint8_t function, struct EventStruct *event, String &string) Device[deviceCount].SendDataOption = true; Device[deviceCount].TimerOption = true; Device[deviceCount].GlobalSyncOption = false; + Device[deviceCount].PluginStats = true; break; } @@ -433,6 +434,13 @@ boolean Plugin_076(uint8_t function, struct EventStruct *event, String &string) // Library expects an interrupt on both edges attachInterrupt(CF1_PIN, p076_hlw8012_cf1_interrupt, cf1_trigger); attachInterrupt(CF_PIN, p076_hlw8012_cf_interrupt, cf_trigger); + + #ifdef USES_PLUGIN_STATS + if (ExtraTaskSettings.anyEnabledPluginStats()) { + initPluginTaskData(event->TaskIndex, new (std::nothrow) _StatsOnly_data_struct()); + } + #endif + success = true; } } diff --git a/src/_P084_VEML6070.ino b/src/_P084_VEML6070.ino index 55321a6a4..1c2617881 100644 --- a/src/_P084_VEML6070.ino +++ b/src/_P084_VEML6070.ino @@ -52,6 +52,7 @@ boolean Plugin_084(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].TimerOption = true; Device[deviceCount].TimerOptional = false; Device[deviceCount].GlobalSyncOption = true; + Device[deviceCount].PluginStats = true; break; } @@ -98,6 +99,12 @@ boolean Plugin_084(uint8_t function, struct EventStruct *event, String& string) if (!status) { addLog(LOG_LEVEL_INFO, F("VEML6070: Not available!")); + } else { + #ifdef USES_PLUGIN_STATS + if (ExtraTaskSettings.anyEnabledPluginStats()) { + initPluginTaskData(event->TaskIndex, new (std::nothrow) _StatsOnly_data_struct()); + } + #endif } success = status; diff --git a/src/_Plugin_Helper.cpp b/src/_Plugin_Helper.cpp index 98b8f4b17..9446a35a2 100644 --- a/src/_Plugin_Helper.cpp +++ b/src/_Plugin_Helper.cpp @@ -60,8 +60,11 @@ void initPluginTaskData(taskIndex_t taskIndex, PluginTaskData_base *data) { #ifdef USES_PLUGIN_STATS const uint8_t valueCount = getValueCountForTask(taskIndex); - for (size_t i = 0; i < VARS_PER_TASK && i < valueCount; ++i) { - Plugin_task_data[taskIndex]->initPluginStats(i); + LoadTaskSettings(taskIndex); + for (size_t i = 0; i < valueCount; ++i) { + if (ExtraTaskSettings.enabledPluginStats(i)) { + Plugin_task_data[taskIndex]->initPluginStats(i); + } } #endif } else if (data != nullptr) { diff --git a/src/_Plugin_Helper.h b/src/_Plugin_Helper.h index 704936f20..32a9e9ff4 100644 --- a/src/_Plugin_Helper.h +++ b/src/_Plugin_Helper.h @@ -44,6 +44,10 @@ #include "src/Helpers/_Plugin_SensorTypeHelper.h" #include "src/Helpers/_Plugin_Helper_serial.h" +#ifdef USES_PLUGIN_STATS +#include "src/PluginStructs/_StatsOnly_data_struct.h" +#endif + #include "src/WebServer/Chart_JS.h" #include "src/WebServer/HTML_wrappers.h" #include "src/WebServer/Markup.h" diff --git a/src/src/DataStructs/ExtraTaskSettingsStruct.cpp b/src/src/DataStructs/ExtraTaskSettingsStruct.cpp index a513b593b..1c39f0583 100644 --- a/src/src/DataStructs/ExtraTaskSettingsStruct.cpp +++ b/src/src/DataStructs/ExtraTaskSettingsStruct.cpp @@ -12,14 +12,7 @@ void ExtraTaskSettingsStruct::clear() { TaskIndex = INVALID_TASK_INDEX; ZERO_FILL(TaskDeviceName); - for (uint8_t i = 0; i < VARS_PER_TASK; ++i) { - TaskDeviceValueDecimals[i] = 2; - ZERO_FILL(TaskDeviceFormula[i]); - ZERO_FILL(TaskDeviceValueNames[i]); - setIgnoreRangeCheck(i); - TaskDeviceErrorValue[i] = 0.0f; - VariousBits[i] = 0u; - } + clearUnusedValueNames(0); for (uint8_t i = 0; i < PLUGIN_EXTRACONFIGVAR_MAX; ++i) { TaskDevicePluginConfigLong[i] = 0; @@ -70,11 +63,12 @@ bool ExtraTaskSettingsStruct::checkUniqueValueNames() const { void ExtraTaskSettingsStruct::clearUnusedValueNames(uint8_t usedVars) { for (uint8_t i = usedVars; i < VARS_PER_TASK; ++i) { - TaskDeviceValueDecimals[i] = 2; ZERO_FILL(TaskDeviceFormula[i]); ZERO_FILL(TaskDeviceValueNames[i]); + TaskDeviceValueDecimals[i] = 2; setIgnoreRangeCheck(i); TaskDeviceErrorValue[i] = 0.0f; + VariousBits[i] = 0; } } @@ -182,7 +176,7 @@ float ExtraTaskSettingsStruct::checkAllowedRange(taskVarIndex_t taskVarIndex, co return value; } -bool ExtraTaskSettingsStruct::enablePluginStats(taskVarIndex_t taskVarIndex) const +bool ExtraTaskSettingsStruct::enabledPluginStats(taskVarIndex_t taskVarIndex) const { if (!validTaskVarIndex(taskVarIndex)) { return false; } return bitRead(VariousBits[taskVarIndex], 0); @@ -195,10 +189,10 @@ void ExtraTaskSettingsStruct::enablePluginStats(taskVarIndex_t taskVarIndex, boo } } -bool ExtraTaskSettingsStruct::anyEnablePluginStats() const +bool ExtraTaskSettingsStruct::anyEnabledPluginStats() const { for (uint8_t i = 0; i < VARS_PER_TASK; ++i) { - if (enablePluginStats(i)) return true; + if (enabledPluginStats(i)) return true; } return false; } diff --git a/src/src/DataStructs/ExtraTaskSettingsStruct.h b/src/src/DataStructs/ExtraTaskSettingsStruct.h index d12daa832..a50e4d7c9 100644 --- a/src/src/DataStructs/ExtraTaskSettingsStruct.h +++ b/src/src/DataStructs/ExtraTaskSettingsStruct.h @@ -45,9 +45,9 @@ struct ExtraTaskSettingsStruct bool valueInAllowedRange(taskVarIndex_t taskVarIndex, const float & value) const; - bool enablePluginStats(taskVarIndex_t taskVarIndex) const; + bool enabledPluginStats(taskVarIndex_t taskVarIndex) const; void enablePluginStats(taskVarIndex_t taskVarIndex, bool enabled); - bool anyEnablePluginStats() const; + bool anyEnabledPluginStats() const; taskIndex_t TaskIndex; // Always < TASKS_MAX or INVALID_TASK_INDEX char TaskDeviceName[NAME_FORMULA_LENGTH_MAX + 1]; diff --git a/src/src/DataStructs/PluginStats.cpp b/src/src/DataStructs/PluginStats.cpp index e2e9e2b0c..b39c78bb3 100644 --- a/src/src/DataStructs/PluginStats.cpp +++ b/src/src/DataStructs/PluginStats.cpp @@ -109,8 +109,10 @@ bool PluginStats::plugin_get_config_value_base(struct EventStruct *event, String bool PluginStats::webformLoad_show_stats(struct EventStruct *event) const { bool somethingAdded = false; - if (webformLoad_show_avg(event)) somethingAdded = true; - if (webformLoad_show_peaks(event)) somethingAdded = true; + + if (webformLoad_show_avg(event)) { somethingAdded = true; } + + if (webformLoad_show_peaks(event)) { somethingAdded = true; } if (somethingAdded) { addFormSeparator(4); @@ -130,7 +132,7 @@ bool PluginStats::webformLoad_show_avg(struct EventStruct *event) const return true; } return false; -} +} bool PluginStats::webformLoad_show_peaks(struct EventStruct *event) const { @@ -142,8 +144,7 @@ bool PluginStats::webformLoad_show_peaks(struct EventStruct *event) const return true; } return false; -} - +} # ifdef USES_CHART_JS void PluginStats::plot_ChartJS_dataset() const @@ -168,4 +169,201 @@ void PluginStats::plot_ChartJS_dataset() const } # endif // ifdef USES_CHART_JS -#endif // ifdef USES_PLUGIN_STATS + +PluginStats_array::PluginStats_array() +{ + for (size_t i = 0; i < VARS_PER_TASK; ++i) { + _plugin_stats[i] = nullptr; + } +} + +PluginStats_array::~PluginStats_array() +{ + for (size_t i = 0; i < VARS_PER_TASK; ++i) { + if (_plugin_stats[i] != nullptr) { + delete _plugin_stats[i]; + _plugin_stats[i] = nullptr; + } + } +} + +void PluginStats_array::initPluginStats(taskVarIndex_t taskVarIndex) +{ + if (taskVarIndex < VARS_PER_TASK) { + delete _plugin_stats[taskVarIndex]; + _plugin_stats[taskVarIndex] = nullptr; + + if (ExtraTaskSettings.enabledPluginStats(taskVarIndex)) { + _plugin_stats[taskVarIndex] = new (std::nothrow) PluginStats( + ExtraTaskSettings.TaskDeviceValueDecimals[taskVarIndex], + ExtraTaskSettings.TaskDeviceErrorValue[taskVarIndex]); + + if (_plugin_stats[taskVarIndex] != nullptr) { + _plugin_stats[taskVarIndex]->setLabel(ExtraTaskSettings.TaskDeviceValueNames[taskVarIndex]); + # ifdef USES_CHART_JS + const __FlashStringHelper *colors[] = { F("#A52422"), F("#BEA57D"), F("#EFF2C0"), F("#A4BAB7") }; + _plugin_stats[taskVarIndex]->_ChartJS_dataset_config.color = colors[taskVarIndex]; + # endif // ifdef USES_CHART_JS + } + } + } +} + +void PluginStats_array::clearPluginStats(taskVarIndex_t taskVarIndex) +{ + if (taskVarIndex < VARS_PER_TASK) { + if (_plugin_stats[taskVarIndex] != nullptr) { + delete _plugin_stats[taskVarIndex]; + _plugin_stats[taskVarIndex] = nullptr; + } + } +} + +bool PluginStats_array::hasStats() const +{ + for (size_t i = 0; i < VARS_PER_TASK; ++i) { + if (_plugin_stats[i] != nullptr) { return true; } + } + return false; +} + +bool PluginStats_array::hasPeaks() const +{ + for (size_t i = 0; i < VARS_PER_TASK; ++i) { + if ((_plugin_stats[i] != nullptr) && _plugin_stats[i]->hasPeaks()) { + return true; + } + } + return false; +} + +uint8_t PluginStats_array::nrSamplesPresent() const +{ + for (size_t i = 0; i < VARS_PER_TASK; ++i) { + if (_plugin_stats[i] != nullptr) { + return _plugin_stats[i]->getNrSamples(); + } + } + return 0; +} + +void PluginStats_array::pushPluginStatsValues(struct EventStruct *event, bool trackPeaks) +{ + for (size_t i = 0; i < VARS_PER_TASK; ++i) { + if (_plugin_stats[i] != nullptr) { + _plugin_stats[i]->push(UserVar[event->BaseVarIndex + i]); + + if (trackPeaks) { + _plugin_stats[i]->trackPeak(UserVar[event->BaseVarIndex + i]); + } + } + } +} + +bool PluginStats_array::plugin_get_config_value_base(struct EventStruct *event, + String & string) const +{ + // Full value name is something like "taskvaluename.avg" + const String fullValueName = parseString(string, 1); + const String valueName = parseString(fullValueName, 1, '.'); + + for (uint8_t i = 0; i < VARS_PER_TASK; i++) + { + if (_plugin_stats[i] != nullptr) { + // Check case insensitive, since the user entered value name can have any case. + if (valueName.equalsIgnoreCase(ExtraTaskSettings.TaskDeviceValueNames[i])) + { + return _plugin_stats[i]->plugin_get_config_value_base(event, string); + } + } + } + return false; +} + +bool PluginStats_array::plugin_write_base(struct EventStruct *event, const String& string) +{ + bool success = false; + const String cmd = parseString(string, 1); // command + + const bool resetPeaks = cmd.equals(F("resetpeaks")); // Command: "taskname.resetPeaks" + const bool clearSamples = cmd.equals(F("clearsamples")); // Command: "taskname.clearSamples" + + if (resetPeaks || clearSamples) { + success = true; + + for (size_t i = 0; i < VARS_PER_TASK; ++i) { + if (_plugin_stats[i] != nullptr) { + if (resetPeaks) { + _plugin_stats[i]->resetPeaks(); + } + + if (clearSamples) { + _plugin_stats[i]->clearSamples(); + } + } + } + } + return success; +} + +bool PluginStats_array::webformLoad_show_stats(struct EventStruct *event) const +{ + bool somethingAdded = false; + + for (size_t i = 0; i < VARS_PER_TASK; ++i) { + if (_plugin_stats[i] != nullptr) { + if (_plugin_stats[i]->webformLoad_show_stats(event)) { somethingAdded = true; } + } + } + return somethingAdded; +} + +# ifdef USES_CHART_JS +void PluginStats_array::plot_ChartJS() const +{ + const uint8_t nrSamples = nrSamplesPresent(); + + if (nrSamples == 0) { return; } + + // Chart Header + add_ChartJS_chart_header(F("line"), F("TaskStatsChart"), F(""), 500, 500); + + // Add labels + for (size_t i = 0; i < nrSamples; ++i) { + if (i != 0) { + addHtml(','); + } + addHtmlInt(i); + } + addHtml(F("],datasets: [")); + + + // Data sets + for (size_t i = 0; i < VARS_PER_TASK; ++i) { + if (_plugin_stats[i] != nullptr) { + _plugin_stats[i]->plot_ChartJS_dataset(); + } + } + add_ChartJS_chart_footer(); +} + +# endif // ifdef USES_CHART_JS + + +PluginStats * PluginStats_array::getPluginStats(taskVarIndex_t taskVarIndex) const +{ + if ((taskVarIndex < VARS_PER_TASK)) { + return _plugin_stats[taskVarIndex]; + } + return nullptr; +} + +PluginStats * PluginStats_array::getPluginStats(taskVarIndex_t taskVarIndex) +{ + if ((taskVarIndex < VARS_PER_TASK)) { + return _plugin_stats[taskVarIndex]; + } + return nullptr; +} + +#endif // ifdef USES_PLUGIN_STATS diff --git a/src/src/DataStructs/PluginStats.h b/src/src/DataStructs/PluginStats.h index 248e0cd6d..1c4cb75c3 100644 --- a/src/src/DataStructs/PluginStats.h +++ b/src/src/DataStructs/PluginStats.h @@ -75,13 +75,13 @@ public: // Support task value notation to 'get' statistics // Notations like [taskname#taskvalue.avg] can then be used to compute the average over a number of samples. - bool plugin_get_config_value_base(struct EventStruct *event, - String & string) const; + bool plugin_get_config_value_base(struct EventStruct *event, + String & string) const; - bool webformLoad_show_stats(struct EventStruct *event) const; + bool webformLoad_show_stats(struct EventStruct *event) const; - bool webformLoad_show_avg(struct EventStruct *event) const; - bool webformLoad_show_peaks(struct EventStruct *event) const; + bool webformLoad_show_avg(struct EventStruct *event) const; + bool webformLoad_show_peaks(struct EventStruct *event) const; const String& getLabel() const { @@ -129,5 +129,45 @@ private: uint8_t _nrDecimals = 3u; }; + +class PluginStats_array { +public: + + PluginStats_array(); + ~PluginStats_array(); + + void initPluginStats(taskVarIndex_t taskVarIndex); + void clearPluginStats(taskVarIndex_t taskVarIndex); + + bool hasStats() const; + bool hasPeaks() const; + + uint8_t nrSamplesPresent() const; + + void pushPluginStatsValues(struct EventStruct *event, + bool trackPeaks); + + bool plugin_get_config_value_base(struct EventStruct *event, + String & string) const; + + bool plugin_write_base(struct EventStruct *event, + const String & string); + + bool webformLoad_show_stats(struct EventStruct *event) const; + +# ifdef USES_CHART_JS + void plot_ChartJS() const; +# endif // ifdef USES_CHART_JS + + + PluginStats* getPluginStats(taskVarIndex_t taskVarIndex) const; + + PluginStats* getPluginStats(taskVarIndex_t taskVarIndex); + +private: + + PluginStats *_plugin_stats[VARS_PER_TASK] = { nullptr, }; +}; + #endif // ifdef USES_PLUGIN_STATS #endif // ifndef HELPERS_PLUGINSTATS_H diff --git a/src/src/DataStructs/PluginTaskData_base.cpp b/src/src/DataStructs/PluginTaskData_base.cpp index 53c298910..973520bc7 100644 --- a/src/src/DataStructs/PluginTaskData_base.cpp +++ b/src/src/DataStructs/PluginTaskData_base.cpp @@ -10,25 +10,17 @@ #include "../WebServer/Chart_JS.h" #include "../WebServer/HTML_wrappers.h" - -PluginTaskData_base::PluginTaskData_base() { +PluginTaskData_base::PluginTaskData_base() + : _taskdata_pluginID(INVALID_PLUGIN_ID) #ifdef USES_PLUGIN_STATS - - for (size_t i = 0; i < VARS_PER_TASK; ++i) { - _plugin_stats[i] = nullptr; - } + , _plugin_stats_array(nullptr) #endif // ifdef USES_PLUGIN_STATS -} +{} PluginTaskData_base::~PluginTaskData_base() { #ifdef USES_PLUGIN_STATS - - for (size_t i = 0; i < VARS_PER_TASK; ++i) { - if (_plugin_stats[i] != nullptr) { - delete _plugin_stats[i]; - _plugin_stats[i] = nullptr; - } - } + delete _plugin_stats_array; + _plugin_stats_array = nullptr; #endif // ifdef USES_PLUGIN_STATS } @@ -36,10 +28,8 @@ bool PluginTaskData_base::hasPluginStats() const { #ifdef USES_PLUGIN_STATS - for (size_t i = 0; i < VARS_PER_TASK; ++i) { - if (_plugin_stats[i] != nullptr) { - return true; - } + if (_plugin_stats_array != nullptr) { + return _plugin_stats_array->hasStats(); } #endif // ifdef USES_PLUGIN_STATS return false; @@ -49,10 +39,8 @@ bool PluginTaskData_base::hasPeaks() const { #ifdef USES_PLUGIN_STATS - for (size_t i = 0; i < VARS_PER_TASK; ++i) { - if ((_plugin_stats[i] != nullptr) && _plugin_stats[i]->hasPeaks()) { - return true; - } + if (_plugin_stats_array != nullptr) { + return _plugin_stats_array->hasPeaks(); } #endif // ifdef USES_PLUGIN_STATS return false; @@ -62,10 +50,8 @@ uint8_t PluginTaskData_base::nrSamplesPresent() const { #ifdef USES_PLUGIN_STATS - for (size_t i = 0; i < VARS_PER_TASK; ++i) { - if (_plugin_stats[i] != nullptr) { - return _plugin_stats[i]->getNrSamples(); - } + if (_plugin_stats_array != nullptr) { + return _plugin_stats_array->nrSamplesPresent(); } #endif // ifdef USES_PLUGIN_STATS return 0; @@ -75,30 +61,24 @@ uint8_t PluginTaskData_base::nrSamplesPresent() const void PluginTaskData_base::initPluginStats(taskVarIndex_t taskVarIndex) { if (taskVarIndex < VARS_PER_TASK) { - clearPluginStats(taskVarIndex); + if (_plugin_stats_array == nullptr) { + _plugin_stats_array = new (std::nothrow) PluginStats_array(); + } - if (ExtraTaskSettings.enablePluginStats(taskVarIndex)) { - _plugin_stats[taskVarIndex] = new PluginStats( - ExtraTaskSettings.TaskDeviceValueDecimals[taskVarIndex], - ExtraTaskSettings.TaskDeviceErrorValue[taskVarIndex]); - - if (_plugin_stats[taskVarIndex] != nullptr) { - _plugin_stats[taskVarIndex]->setLabel(ExtraTaskSettings.TaskDeviceValueNames[taskVarIndex]); - # ifdef USES_CHART_JS - const __FlashStringHelper *colors[] = { F("#A52422"), F("#BEA57D"), F("#EFF2C0"), F("#A4BAB7") }; - _plugin_stats[taskVarIndex]->_ChartJS_dataset_config.color = colors[taskVarIndex]; - # endif // ifdef USES_CHART_JS - } + if (_plugin_stats_array != nullptr) { + _plugin_stats_array->initPluginStats(taskVarIndex); } } } void PluginTaskData_base::clearPluginStats(taskVarIndex_t taskVarIndex) { - if (taskVarIndex < VARS_PER_TASK) { - if (_plugin_stats[taskVarIndex] != nullptr) { - delete _plugin_stats[taskVarIndex]; - _plugin_stats[taskVarIndex] = nullptr; + if ((taskVarIndex < VARS_PER_TASK) && _plugin_stats_array) { + _plugin_stats_array->clearPluginStats(taskVarIndex); + + if (!_plugin_stats_array->hasStats()) { + delete _plugin_stats_array; + _plugin_stats_array = nullptr; } } } @@ -108,14 +88,8 @@ void PluginTaskData_base::pushPluginStatsValues(struct EventStruct *event, bool { #ifdef USES_PLUGIN_STATS - for (size_t i = 0; i < VARS_PER_TASK; ++i) { - if (_plugin_stats[i] != nullptr) { - _plugin_stats[i]->push(UserVar[event->BaseVarIndex + i]); - - if (trackPeaks) { - _plugin_stats[i]->trackPeak(UserVar[event->BaseVarIndex + i]); - } - } + if (_plugin_stats_array != nullptr) { + _plugin_stats_array->pushPluginStatsValues(event, trackPeaks); } #endif // ifdef USES_PLUGIN_STATS } @@ -125,19 +99,8 @@ bool PluginTaskData_base::plugin_get_config_value_base(struct EventStruct *event { #ifdef USES_PLUGIN_STATS - // Full value name is something like "taskvaluename.avg" - const String fullValueName = parseString(string, 1); - const String valueName = parseString(fullValueName, 1, '.'); - - for (uint8_t valueNr = 0; valueNr < VARS_PER_TASK; valueNr++) - { - if (_plugin_stats[valueNr] != nullptr) { - // Check case insensitive, since the user entered value name can have any case. - if (valueName.equalsIgnoreCase(ExtraTaskSettings.TaskDeviceValueNames[valueNr])) - { - return _plugin_stats[valueNr]->plugin_get_config_value_base(event, string); - } - } + if (_plugin_stats_array != nullptr) { + return _plugin_stats_array->plugin_get_config_value_base(event, string); } #endif // ifdef USES_PLUGIN_STATS return false; @@ -146,77 +109,47 @@ bool PluginTaskData_base::plugin_get_config_value_base(struct EventStruct *event bool PluginTaskData_base::plugin_write_base(struct EventStruct *event, const String& string) { #ifdef USES_PLUGIN_STATS - bool success = false; - const String cmd = parseString(string, 1); // command - - const bool resetPeaks = cmd.equals(F("resetpeaks")); // Command: "taskname.resetPeaks" - const bool clearSamples = cmd.equals(F("clearsamples")); // Command: "taskname.clearSamples" - - if (resetPeaks || clearSamples) { - success = true; - - for (size_t i = 0; i < VARS_PER_TASK; ++i) { - if (_plugin_stats[i] != nullptr) { - if (resetPeaks) { - _plugin_stats[i]->resetPeaks(); - } - - if (clearSamples) { - _plugin_stats[i]->clearSamples(); - } - } - } + if (_plugin_stats_array != nullptr) { + return _plugin_stats_array->plugin_write_base(event, string); } - - return success; -#else // ifdef USES_PLUGIN_STATS - return false; #endif // ifdef USES_PLUGIN_STATS + return false; } #ifdef USES_PLUGIN_STATS bool PluginTaskData_base::webformLoad_show_stats(struct EventStruct *event) const { - bool somethingAdded = false; - - for (size_t i = 0; i < VARS_PER_TASK; ++i) { - if (_plugin_stats[i] != nullptr) { - if (_plugin_stats[i]->webformLoad_show_stats(event)) { somethingAdded = true; } - } + if (_plugin_stats_array != nullptr) { + return _plugin_stats_array->webformLoad_show_stats(event); } - return somethingAdded; + return false; } # ifdef USES_CHART_JS void PluginTaskData_base::plot_ChartJS() const { - const uint8_t nrSamples = nrSamplesPresent(); - - if (nrSamples == 0) { return; } - - // Chart Header - add_ChartJS_chart_header(F("line"), F("TaskStatsChart"), F(""), 500, 500); - - // Add labels - for (size_t i = 0; i < nrSamples; ++i) { - if (i != 0) { - addHtml(','); - } - addHtmlInt(i); + if (_plugin_stats_array != nullptr) { + _plugin_stats_array->plot_ChartJS(); } - addHtml(F("],datasets: [")); - - - // Data sets - for (size_t i = 0; i < VARS_PER_TASK; ++i) { - if (_plugin_stats[i] != nullptr) { - _plugin_stats[i]->plot_ChartJS_dataset(); - } - } - add_ChartJS_chart_footer(); } # endif // ifdef USES_CHART_JS -#endif // ifdef USES_PLUGIN_STATS +PluginStats * PluginTaskData_base::getPluginStats(taskVarIndex_t taskVarIndex) const +{ + if (_plugin_stats_array != nullptr) { + return _plugin_stats_array->getPluginStats(taskVarIndex); + } + return nullptr; +} + +PluginStats * PluginTaskData_base::getPluginStats(taskVarIndex_t taskVarIndex) +{ + if (_plugin_stats_array != nullptr) { + return _plugin_stats_array->getPluginStats(taskVarIndex); + } + return nullptr; +} + +#endif // ifdef USES_PLUGIN_STATS diff --git a/src/src/DataStructs/PluginTaskData_base.h b/src/src/DataStructs/PluginTaskData_base.h index 45b13dc75..e40798e6c 100644 --- a/src/src/DataStructs/PluginTaskData_base.h +++ b/src/src/DataStructs/PluginTaskData_base.h @@ -55,8 +55,14 @@ struct PluginTaskData_base { pluginID_t _taskdata_pluginID = INVALID_PLUGIN_ID; #ifdef USES_PLUGIN_STATS + PluginStats* getPluginStats(taskVarIndex_t taskVarIndex) const; + + PluginStats* getPluginStats(taskVarIndex_t taskVarIndex); + +private: + // Array of pointers to PluginStats. One per task value. - PluginStats *_plugin_stats[VARS_PER_TASK] = { nullptr, }; + PluginStats_array *_plugin_stats_array = nullptr; #endif // ifdef USES_PLUGIN_STATS }; diff --git a/src/src/Globals/Plugins.cpp b/src/src/Globals/Plugins.cpp index ca2a3cf21..cca5b0f41 100644 --- a/src/src/Globals/Plugins.cpp +++ b/src/src/Globals/Plugins.cpp @@ -626,20 +626,12 @@ bool PluginCall(uint8_t Function, struct EventStruct *event, String& str) Plugin_ptr[DeviceIndex](PLUGIN_INIT_VALUE_RANGES, event, str); // Initialize value range(s) } - bool retval = Plugin_ptr[DeviceIndex](Function, event, str); - - #ifdef USES_PLUGIN_STATS - if (Function == PLUGIN_INIT && Device[DeviceIndex].PluginStats) { - PluginTaskData_base *taskData = getPluginTaskData(event->TaskIndex); - if (taskData == nullptr) { - // Plugin apparently does not have PluginTaskData. - // Create Plugin Task data if it has "Stats" checked. - if (ExtraTaskSettings.anyEnablePluginStats()) { - initPluginTaskData(event->TaskIndex, new (std::nothrow) PluginTaskData_base()); - } - } + if (Function == PLUGIN_INIT) { + // Make sure any task data is actually cleared. + clearPluginTaskData(event->TaskIndex); } - #endif + + bool retval = Plugin_ptr[DeviceIndex](Function, event, str); if (Function == PLUGIN_READ) { if (!retval) { diff --git a/src/src/PluginStructs/P002_data_struct.cpp b/src/src/PluginStructs/P002_data_struct.cpp index d8b08925d..55344a700 100644 --- a/src/src/PluginStructs/P002_data_struct.cpp +++ b/src/src/PluginStructs/P002_data_struct.cpp @@ -82,8 +82,8 @@ void P002_data_struct::webformLoad(struct EventStruct *event) # ifdef USES_PLUGIN_STATS - if (_plugin_stats[0] != nullptr) { - _plugin_stats[0]->trackPeak(raw_value); + if (getPluginStats(0) != nullptr) { + getPluginStats(0)->trackPeak(raw_value); } # endif // ifdef USES_PLUGIN_STATS @@ -254,12 +254,12 @@ bool P002_data_struct::webformLoad_show_stats(struct EventStruct *event) { bool somethingAdded = false; - if (_plugin_stats[0] != nullptr) { - if (_plugin_stats[0]->webformLoad_show_avg(event)) { somethingAdded = true; } + if (getPluginStats(0) != nullptr) { + if (getPluginStats(0)->webformLoad_show_avg(event)) { somethingAdded = true; } - if (_plugin_stats[0]->hasPeaks()) { - formatADC_statistics(F("ADC Peak Low"), _plugin_stats[0]->getPeakLow(), true); - formatADC_statistics(F("ADC Peak High"), _plugin_stats[0]->getPeakHigh(), true); + if (getPluginStats(0)->hasPeaks()) { + formatADC_statistics(F("ADC Peak Low"), getPluginStats(0)->getPeakLow(), true); + formatADC_statistics(F("ADC Peak High"), getPluginStats(0)->getPeakHigh(), true); somethingAdded = true; } } @@ -689,8 +689,8 @@ void P002_data_struct::takeSample() # ifdef USES_PLUGIN_STATS - if (_plugin_stats[0] != nullptr) { - _plugin_stats[0]->trackPeak(raw); + if (getPluginStats(0) != nullptr) { + getPluginStats(0)->trackPeak(raw); } # endif // ifdef USES_PLUGIN_STATS # ifdef ESP32 @@ -747,8 +747,8 @@ bool P002_data_struct::getValue(float& float_value, raw_value = espeasy_analogRead(_pin_analogRead); # ifdef USES_PLUGIN_STATS - if (_plugin_stats[0] != nullptr) { - _plugin_stats[0]->trackPeak(raw_value); + if (getPluginStats(0) != nullptr) { + getPluginStats(0)->trackPeak(raw_value); } # endif // ifdef USES_PLUGIN_STATS # ifdef ESP32 diff --git a/src/src/PluginStructs/_StatsOnly_data_struct.cpp b/src/src/PluginStructs/_StatsOnly_data_struct.cpp new file mode 100644 index 000000000..9e573fef0 --- /dev/null +++ b/src/src/PluginStructs/_StatsOnly_data_struct.cpp @@ -0,0 +1,11 @@ +#include "../PluginStructs/_StatsOnly_data_struct.h" + + +#ifdef USES_PLUGIN_STATS + +_StatsOnly_data_struct::_StatsOnly_data_struct() +{ + +} + +#endif \ No newline at end of file diff --git a/src/src/PluginStructs/_StatsOnly_data_struct.h b/src/src/PluginStructs/_StatsOnly_data_struct.h new file mode 100644 index 000000000..27e84124c --- /dev/null +++ b/src/src/PluginStructs/_StatsOnly_data_struct.h @@ -0,0 +1,15 @@ +#ifndef PLUGINSTRUCTS__STATSONLY_DATA_STRUCT_H +#define PLUGINSTRUCTS__STATSONLY_DATA_STRUCT_H + +#include "../../_Plugin_Helper.h" + +#ifdef USES_PLUGIN_STATS + +// Dummy class to be able to use the PluginStats for plugins that don't (yet) have their own PluginTaskData. +struct _StatsOnly_data_struct : public PluginTaskData_base { + _StatsOnly_data_struct(); + +}; + +#endif +#endif \ No newline at end of file diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index 5f5fb4a1b..84eae8921 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -371,6 +371,7 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task strncpy_webserver_arg(ExtraTaskSettings.TaskDeviceValueNames[varNr], String(F("TDVN")) + (varNr + 1)); ExtraTaskSettings.enablePluginStats(varNr, isFormItemChecked(String(F("TDS")) + (varNr + 1))); } + ExtraTaskSettings.clearUnusedValueNames(valueCount); // allow the plugin to save plugin-specific form settings. { @@ -1382,7 +1383,7 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde html_TD(); String id = F("TDS"); // ="taskdevicestats" id += (varNr + 1); - addCheckBox(id, ExtraTaskSettings.enablePluginStats(varNr)); + addCheckBox(id, ExtraTaskSettings.enabledPluginStats(varNr)); } if (Device[DeviceIndex].configurableDecimals())