diff --git a/src/_Plugin_Helper.cpp b/src/_Plugin_Helper.cpp index 3e8000d34..6547641d1 100644 --- a/src/_Plugin_Helper.cpp +++ b/src/_Plugin_Helper.cpp @@ -7,16 +7,11 @@ #include "src/DataStructs/SettingsStruct.h" #include "src/DataStructs/TimingStats.h" #include "src/Globals/Cache.h" -# if FEATURE_STRING_VARIABLES -#include "src/Globals/Device.h" -# endif // #if FEATURE_STRING_VARIABLES -#include "src/Globals/Plugins.h" #include "src/Globals/Settings.h" -# if FEATURE_TASKVALUE_UNIT_OF_MEASURE -#include "src/Helpers/ESPEasy_UnitOfMeasure.h" -#endif #include "src/Helpers/Misc.h" #include "src/Helpers/StringParser.h" +#include "src/Helpers/TaskValuesWriterHelper.h" + PluginTaskData_base *Plugin_task_data[TASKS_MAX] = {}; @@ -223,92 +218,24 @@ bool pluginTaskData_initialized(taskIndex_t taskIndex) { (Plugin_task_data[taskIndex]->_taskdata_pluginID == Settings.getPluginID_for_task(taskIndex)); } -pluginWebformShowValue_struct::pluginWebformShowValue_struct(struct EventStruct *e) -: event(e) { - if (e) { - deviceIndex = getDeviceIndex_from_TaskIndex(event->TaskIndex); - valueCount = getValueCountForTask(event->TaskIndex); - } -} - -void pluginWebformShowValue_struct::clear() -{ - valName.clear(); - valName_id.clear(); - value.clear(); - value_id.clear(); -# if FEATURE_TASKVALUE_UNIT_OF_MEASURE - uom.clear(); -#endif -# if FEATURE_STRING_VARIABLES - hasPresentation = false; - presentation.clear(); -#endif - nrDecimals=0; -} - -bool pluginWebformShowValue_struct::initRegularTaskValue(uint8_t varNr) -{ - if (!validDeviceIndex(deviceIndex)) return false; - - if (varNr >= valueCount) return false; - - valueNumber = varNr; - - // Make sure we can re-use the same struct for multiple task values - clear(); - - // Need to produce the 'regular' task values -# if FEATURE_TASKVALUE_UNIT_OF_MEASURE - if (Settings.ShowUnitOfMeasureOnDevicesPage()) { - const uint8_t uomIndex = Cache.getTaskVarUnitOfMeasure(event->TaskIndex, varNr); - - if ((uomIndex != 0)) { - uom = toUnitOfMeasureName(uomIndex); - } - } -# endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE - - nrDecimals = Cache.getTaskDeviceValueDecimals(event->TaskIndex, varNr); - - value = formatUserVarNoCheck(event, varNr); - - # if FEATURE_STRING_VARIABLES - const DeviceStruct& device = Device[deviceIndex]; - bool hasPresentation = false; - - if (!device.HideDerivedValues) { - presentation = formatUserVarForPresentation(event, varNr, hasPresentation, value, deviceIndex); - } - # endif // if FEATURE_STRING_VARIABLES - valName = Cache.getTaskDeviceValueName(event->TaskIndex, varNr); - setID(varNr); - - return true; -} - -void pluginWebformShowValue_struct::setID(uint8_t varNr) -{ - valueNumber = varNr; - const String id_postfix = strformat(F("%u_%u"), event->TaskIndex, varNr); - valName_id = concat(F("valuename_"), id_postfix); - value_id = concat(F("value_"), id_postfix); -# if FEATURE_TASKVALUE_UNIT_OF_MEASURE - if (!uom.isEmpty()) - uom_id = concat(F("uom_"), id_postfix); -#endif -# if FEATURE_STRING_VARIABLES - if (!presentation.isEmpty()) - presentation_id = concat(F("pres_"), id_postfix); -#endif -} void pluginWebformShowValue( - struct EventStruct *event, - const String &label, - const String &value, - bool addTrailingBreak) + TaskValuesWriterHelper *data) { + if (!data || !data->event) return; + if (data->valueNumber > 0) { + addHtmlDiv(F("div_br")); + } + String value; + value = data->value; + if (data->hasPresentation) value = data->presentation; + else if (!data->uom.isEmpty()) + value += concat(' ', data->uom); + + pluginWebformShowValue( + data->valName, data->valName_id, + data->value, data->value_id, + data->addTrailingBreak); } diff --git a/src/_Plugin_Helper.h b/src/_Plugin_Helper.h index b1152a832..6fec78b58 100644 --- a/src/_Plugin_Helper.h +++ b/src/_Plugin_Helper.h @@ -10,7 +10,6 @@ #include "src/DataStructs/PinMode.h" #include "src/DataStructs/PluginTaskData_base.h" -#include "src/DataTypes/DeviceIndex.h" #include "src/DataTypes/ESPEasy_plugin_functions.h" #include "src/ESPEasyCore/Controller.h" @@ -162,6 +161,8 @@ int8_t & do_PIN(DEBUG_PCONFIG_RANGE_CHECK_args_decl); # define CONFIG_PORT (Settings.TaskDevicePort[event->TaskIndex]) #endif // ifndef CONFIG_PORT +// Forward declaration to prevent cyclical dependencies +struct TaskValuesWriterHelper; extern PluginTaskData_base *Plugin_task_data[TASKS_MAX]; @@ -193,42 +194,11 @@ String getPluginCustomArgName(const __FlashStringHelper *label, int getFormItemIntCustomArgName(int varNr); -// Helper function to create formatted custom values for display in the devices overview page. -// When called from PLUGIN_WEBFORM_SHOW_VALUES, the last item should add a traling div_br class -// if the regular values should also be displayed. -// The call to PLUGIN_WEBFORM_SHOW_VALUES should only return success = true when no regular values should be displayed -// Note that the varNr of the custom values should not conflict with the existing variable numbers (e.g. start at VARS_PER_TASK) + + void pluginWebformShowValue( - struct EventStruct *event, - bool addTrailingBreak = false); + TaskValuesWriterHelper *data); -struct pluginWebformShowValue_struct { - pluginWebformShowValue_struct(struct EventStruct *event); - - void clear(); - - bool initRegularTaskValue(uint8_t varNr); - - void setID(uint8_t varNr); - - - - EventStruct * const event = nullptr; - String valName, valName_id, value, value_id; -# if FEATURE_TASKVALUE_UNIT_OF_MEASURE - String uom, uom_id; -#endif -# if FEATURE_STRING_VARIABLES - String presentation, presentation_id; - bool hasPresentation = false; -#endif - - uint8_t nrDecimals{}; - uint8_t valueCount{}; - uint8_t valueNumber{}; - deviceIndex_t deviceIndex = INVALID_DEVICE_INDEX; - bool addTrailingBreak = false; -}; void pluginWebformShowValue( struct EventStruct *event, diff --git a/src/src/Helpers/KeyValueWriter.h b/src/src/Helpers/KeyValueWriter.h index 7735bc4be..bb7e09733 100644 --- a/src/src/Helpers/KeyValueWriter.h +++ b/src/src/Helpers/KeyValueWriter.h @@ -156,5 +156,7 @@ protected: bool _allowFormatOverrides = true; + bool _tabularFormat = true; + }; // class KeyValueWriter diff --git a/src/src/Helpers/TaskValuesWriterHelper.cpp b/src/src/Helpers/TaskValuesWriterHelper.cpp new file mode 100644 index 000000000..a01f2293a --- /dev/null +++ b/src/src/Helpers/TaskValuesWriterHelper.cpp @@ -0,0 +1,192 @@ +#include "../Helpers/TaskValuesWriterHelper.h" + +#include "../../ESPEasy_common.h" +#include "../../_Plugin_Helper.h" + +#if FEATURE_STRING_VARIABLES +# include "../Globals/Device.h" +#endif // #if FEATURE_STRING_VARIABLES +#include "../Globals/Plugins.h" +#if FEATURE_TASKVALUE_UNIT_OF_MEASURE +# include "../Helpers/ESPEasy_UnitOfMeasure.h" +#endif +#include "../WebServer/JSON.h" + +TaskValuesWriterHelper::TaskValuesWriterHelper(struct EventStruct *e) + : event(e) { + if (e) { + deviceIndex = getDeviceIndex_from_TaskIndex(event->TaskIndex); + valueCount = getValueCountForTask(event->TaskIndex); + } +} + +void TaskValuesWriterHelper::clear() +{ + valName.clear(); + valName_id.clear(); + value.clear(); + value_id.clear(); +#if FEATURE_TASKVALUE_UNIT_OF_MEASURE + uom.clear(); +#endif +#if FEATURE_STRING_VARIABLES + hasPresentation = false; + presentation.clear(); +#endif // if FEATURE_STRING_VARIABLES + nrDecimals = 0; +} + +void TaskValuesWriterHelper::writeRegularTaskValues() +{ + for (uint8_t x = 0; x < valueCount; x++) + { + if (initRegularTaskValue(x)) { + write(); + } + } +} + +#if FEATURE_STRING_VARIABLES + +void TaskValuesWriterHelper::writeDerivedTaskValues() +{ + if (!validDeviceIndex(deviceIndex)) { return; } + const DeviceStruct& device = Device[deviceIndex]; + + int varNr = VARS_PER_TASK; + + if (Settings.ShowDerivedTaskValues(event->TaskIndex)) { + String taskName = getTaskDeviceName(event->TaskIndex); + taskName.toLowerCase(); + String postfix; + const String search = getDerivedValueSearchAndPostfix(taskName, postfix); + + auto it = customStringVar.begin(); + + while (it != customStringVar.end()) { + if (it->first.startsWith(search) && it->first.endsWith(postfix)) { + clear(); + valName = it->first.substring(search.length(), it->first.indexOf('-')); + String vType; + const String vname2 = getDerivedValueNameUomAndVType(taskName, valName, uom, vType); + + if (!vname2.isEmpty()) { + valName = vname2; + } + + if (!it->second.isEmpty()) { + value = it->second; + + // FIXME TD-er: Why these differences between JSON and Web? + if (event->kvWriter) + { + stripEscapeCharacters(value); + value = parseTemplate(value); + nrDecimals = 255; // FIXME Use the minimal number of decimals needed + presentation = + formatUserVarForPresentation( + event, + INVALID_TASKVAR_INDEX, + hasPresentation, + value, + deviceIndex, + valName); + } else { + value = parseTemplateAndCalculate(value); + presentation = getCustomStringVar(strformat( + F(TASK_VALUE_PRESENTATION_PREFIX_TEMPLATE), + taskName.c_str(), + valName.c_str())); + + if (!presentation.isEmpty()) { + stripEscapeCharacters(presentation); + presentation.replace(F("%value%"), value); + presentation = parseTemplate(presentation); + } + } + + setID(varNr); + write(); + ++varNr; + } + } + // FIXME TD-er: Search for "compareTo(search)" in the code to find lots of nearly duplicate code + else if (it->first.substring(0, search.length()).compareTo(search) > 0) { + break; + } + ++it; + } + } +} + +#endif // if FEATURE_STRING_VARIABLES + +bool TaskValuesWriterHelper::initRegularTaskValue(uint8_t varNr) +{ + if (!validDeviceIndex(deviceIndex)) { return false; } + + if (varNr >= valueCount) { return false; } + + valueNumber = varNr; + + // Make sure we can re-use the same struct for multiple task values + clear(); + + // Need to produce the 'regular' task values +#if FEATURE_TASKVALUE_UNIT_OF_MEASURE + + if (Settings.ShowUnitOfMeasureOnDevicesPage()) { + const uint8_t uomIndex = Cache.getTaskVarUnitOfMeasure(event->TaskIndex, varNr); + + if ((uomIndex != 0)) { + uom = toUnitOfMeasureName(uomIndex); + } + } +#endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE + + nrDecimals = Cache.getTaskDeviceValueDecimals(event->TaskIndex, varNr); + + value = formatUserVarNoCheck(event, varNr); + + #if FEATURE_STRING_VARIABLES + const DeviceStruct& device = Device[deviceIndex]; + bool hasPresentation = false; + + if (!device.HideDerivedValues) { + presentation = formatUserVarForPresentation(event, varNr, hasPresentation, value, deviceIndex); + } + #endif // if FEATURE_STRING_VARIABLES + valName = Cache.getTaskDeviceValueName(event->TaskIndex, varNr); + setID(varNr); + + return true; +} + +void TaskValuesWriterHelper::setID(uint8_t varNr) +{ + if (!validDeviceIndex(deviceIndex)) { return; } + valueNumber = varNr; + const String id_postfix = strformat(F("%u_%u"), event->TaskIndex, varNr); + valName_id = concat(F("valuename_"), id_postfix); + value_id = concat(F("value_"), id_postfix); +#if FEATURE_TASKVALUE_UNIT_OF_MEASURE + + if (!uom.isEmpty()) { + uom_id = concat(F("uom_"), id_postfix); + } +#endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE +#if FEATURE_STRING_VARIABLES + + if (hasPresentation) { + presentation_id = concat(F("pres_"), id_postfix); + } +#endif // if FEATURE_STRING_VARIABLES +} + +void TaskValuesWriterHelper::write() +{ + if (!validDeviceIndex(deviceIndex)) { return; } + + if (event->kvWriter) { handle_json_stream_task_value_data(this); } + else { pluginWebformShowValue(this); } +} diff --git a/src/src/Helpers/TaskValuesWriterHelper.h b/src/src/Helpers/TaskValuesWriterHelper.h new file mode 100644 index 000000000..a04b98283 --- /dev/null +++ b/src/src/Helpers/TaskValuesWriterHelper.h @@ -0,0 +1,45 @@ +#pragma once + +#include "../../ESPEasy_common.h" + +#include "../DataTypes/DeviceIndex.h" + +// Helper function to create formatted custom values for display in the devices overview page. +// When called from PLUGIN_WEBFORM_SHOW_VALUES, the last item should add a traling div_br class +// if the regular values should also be displayed. +// The call to PLUGIN_WEBFORM_SHOW_VALUES should only return success = true when no regular values should be displayed +// Note that the varNr of the custom values should not conflict with the existing variable numbers (e.g. start at VARS_PER_TASK) +struct TaskValuesWriterHelper { + TaskValuesWriterHelper(struct EventStruct *event); + + void clear(); + + void writeRegularTaskValues(); +#if FEATURE_STRING_VARIABLES + void writeDerivedTaskValues(); +#endif + + bool initRegularTaskValue(uint8_t varNr); + + void setID(uint8_t varNr); + + void write(); + + + EventStruct * const event = nullptr; + String valName, valName_id, value, value_id; +#if FEATURE_TASKVALUE_UNIT_OF_MEASURE + String uom, uom_id; +#endif +#if FEATURE_STRING_VARIABLES + String presentation, presentation_id; + bool hasPresentation = false; +#endif // if FEATURE_STRING_VARIABLES + + uint8_t nrDecimals{}; + uint8_t valueCount{}; + uint8_t valueNumber{}; + deviceIndex_t deviceIndex = INVALID_DEVICE_INDEX; + bool addTrailingBreak = false; + +}; diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index 6bc07f0a3..236eb03d6 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -34,6 +34,7 @@ # include "../Helpers/SPI_Helper.h" # include "../Helpers/StringConverter.h" # include "../Helpers/StringGenerator_GPIO.h" +# include "../Helpers/TaskValuesWriterHelper.h" # include "../../_Plugin_Helper.h" @@ -859,99 +860,12 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) { if (validPluginID_fullcheck(pid)) { - const uint8_t valueCount = getValueCountForTask(x); - - for (uint8_t varNr = 0; varNr < valueCount; varNr++) - { - # if FEATURE_TASKVALUE_UNIT_OF_MEASURE - const uint8_t uomIndex = Cache.getTaskVarUnitOfMeasure(x, varNr); - String uom; - - if ((uomIndex != 0) && Settings.ShowUnitOfMeasureOnDevicesPage()) { - uom = concat(' ', toUnitOfMeasureName(uomIndex)); - } - # endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE - const String value = formatUserVarNoCheck(&TempEvent, varNr); - # if FEATURE_STRING_VARIABLES - bool hasPresentation = false; - String presentation; - - if (!device.HideDerivedValues) { - presentation = formatUserVarForPresentation(&TempEvent, varNr, hasPresentation, value, DeviceIndex); - } - # endif // if FEATURE_STRING_VARIABLES - pluginWebformShowValue( - x, - varNr, - Cache.getTaskDeviceValueName(x, varNr), - # if FEATURE_STRING_VARIABLES - hasPresentation ? presentation : - # endif // if FEATURE_STRING_VARIABLES - # if FEATURE_TASKVALUE_UNIT_OF_MEASURE - concat(value, uom) - # else // if FEATURE_TASKVALUE_UNIT_OF_MEASURE - value - # endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE - ); - } + TaskValuesWriterHelper data(&TempEvent); + data.writeRegularTaskValues(); +# if FEATURE_STRING_VARIABLES + data.writeDerivedTaskValues(); +#endif } - - # if FEATURE_STRING_VARIABLES - - if (!device.HideDerivedValues) { - int varNr = VARS_PER_TASK; - - if (Settings.ShowDerivedTaskValues(x)) { - String taskName = getTaskDeviceName(x); - taskName.toLowerCase(); - String postfix; - const String search = getDerivedValueSearchAndPostfix(taskName, postfix); - - auto it = customStringVar.begin(); - - while (it != customStringVar.end()) { - if (it->first.startsWith(search) && it->first.endsWith(postfix)) { - String valueName = it->first.substring(search.length(), it->first.indexOf('-')); - String uom; - String vType; - const String vname2 = getDerivedValueNameUomAndVType(taskName, valueName, uom, vType); - - if (!vname2.isEmpty()) { - valueName = vname2; - } - - if (!it->second.isEmpty()) { - String value(it->second); - value = parseTemplateAndCalculate(value); - String presentation = getCustomStringVar(strformat(F(TASK_VALUE_PRESENTATION_PREFIX_TEMPLATE), - taskName.c_str(), - valueName.c_str())); - - if (!uom.isEmpty()) { - value = strformat(F("%s %s"), value.c_str(), uom.c_str()); - } - - if (!presentation.isEmpty()) { - stripEscapeCharacters(presentation); - presentation.replace(F("%value%"), value); - value = parseTemplate(presentation); - } - pluginWebformShowValue( - x, - varNr, - valueName, - value); - ++varNr; - } - } - else if (it->first.substring(0, search.length()).compareTo(search) > 0) { - break; - } - ++it; - } - } - } - # endif // if FEATURE_STRING_VARIABLES } } } diff --git a/src/src/WebServer/JSON.cpp b/src/src/WebServer/JSON.cpp index fb8cfd9d4..30ab16876 100644 --- a/src/src/WebServer/JSON.cpp +++ b/src/src/WebServer/JSON.cpp @@ -3,6 +3,7 @@ #include "../WebServer/JSON.h" #include "../WebServer/Markup_Forms.h" +#include "../DataStructs/KeyValueStruct.h" #include "../DataStructs/TimingStats.h" #include "../Globals/Cache.h" @@ -19,6 +20,7 @@ #include "../Helpers/StringConverter.h" #include "../Helpers/StringProvider.h" #include "../Helpers/StringGenerator_System.h" +#include "../Helpers/TaskValuesWriterHelper.h" #include "../../ESPEasy/net/Helpers/NW_info_writer.h" @@ -486,67 +488,12 @@ void handle_json() struct EventStruct TempEvent(TaskIndex); TempEvent.kvWriter = taskValueWriter.get(); + TaskValuesWriterHelper data(&TempEvent); // FIXME tonhuisman: HasFormatUserVar is not really compatible with Derived Values... - for (uint8_t x = 0; x < valueCount; x++) - { - pluginWebformShowValue_struct data(&TempEvent); - data.initRegularTaskValue(x); - handle_json_stream_task_value_data(&data); - } - -#if FEATURE_STRING_VARIABLES - - if (Settings.ShowDerivedTaskValues(TaskIndex)) { - int varNr = VARS_PER_TASK; - String taskName = getTaskDeviceName(TaskIndex); - taskName.toLowerCase(); - String postfix; - const String search = getDerivedValueSearchAndPostfix(taskName, postfix); - - auto it = customStringVar.begin(); - - while (it != customStringVar.end()) { - if (it->first.startsWith(search) && it->first.endsWith(postfix)) { - String valueName = it->first.substring(search.length(), it->first.indexOf('-')); - String uom; - String vType; - const String vname2 = getDerivedValueNameUomAndVType(taskName, valueName, uom, vType); - - if (!vname2.isEmpty()) { - valueName = vname2; - } - - if (!it->second.isEmpty()) { - String value(it->second); - stripEscapeCharacters(value); - value = parseTemplate(value); - uint8_t nrDecimals = 255; // FIXME Use the minimal number of decimals needed - bool hasPresentation; - const String presentation = - formatUserVarForPresentation(&TempEvent, - INVALID_TASKVAR_INDEX, - hasPresentation, - value, - DeviceIndex, - valueName); - - handle_json_stream_task_value_data(taskValueWriter.get(), - varNr + 1, - valueName, - nrDecimals, - value, - presentation, - uom); - ++varNr; - } - } - else if (it->first.substring(0, search.length()).compareTo(search) > 0) { - break; - } - ++it; - } - } -#endif // if FEATURE_STRING_VARIABLES + data.writeRegularTaskValues(); +# if FEATURE_STRING_VARIABLES + data.writeDerivedTaskValues(); +#endif } } @@ -656,7 +603,7 @@ void handle_json() STOP_TIMER(HANDLE_SERVING_WEBPAGE_JSON); } -void handle_json_stream_task_value_data(pluginWebformShowValue_struct* data) +void handle_json_stream_task_value_data(TaskValuesWriterHelper* data) { if (!data || !data->event || !data->event->kvWriter) return; diff --git a/src/src/WebServer/JSON.h b/src/src/WebServer/JSON.h index 54384db53..635cd5042 100644 --- a/src/src/WebServer/JSON.h +++ b/src/src/WebServer/JSON.h @@ -5,7 +5,7 @@ #include "../WebServer/common.h" class KeyValueWriter; -struct pluginWebformShowValue_struct; +struct TaskValuesWriterHelper; // ******************************************************************************** // Web Interface get CSV value from task @@ -20,7 +20,7 @@ void handle_csvval(); // ******************************************************************************** void handle_json(); -void handle_json_stream_task_value_data(pluginWebformShowValue_struct* data); +void handle_json_stream_task_value_data(TaskValuesWriterHelper* data); void handle_json_stream_task_value_data(KeyValueWriter* parent, uint16_t valueNumber,