diff --git a/misc/CacheController/ParseCacheReaderBulkUpload.py b/misc/CacheController/ParseCacheReaderBulkUpload.py index 2832eb7ca8..1416844759 100644 --- a/misc/CacheController/ParseCacheReaderBulkUpload.py +++ b/misc/CacheController/ParseCacheReaderBulkUpload.py @@ -1,108 +1,110 @@ -import csv -import ctypes -import datetime - - - -def isNewSampleSet(data): - # Each sample on a new CSV line as is done by dump8.htm - return True - - # We trigger on the event of the GPS to flush all values - # Thus consider this the start of a new row - #return data.pluginID == 82 # GPS plugin - #return data.TaskIndex == 1 - - -def decode(bytestream): - - # struct C016_binary_element - # { - # float values[VARS_PER_TASK]{}; - # unsigned long _timestamp{}; // Unix timestamp - # taskIndex_t TaskIndex{INVALID_TASK_INDEX}; - # pluginID_t pluginID{INVALID_PLUGIN_ID}; - # Sensor_VType sensorType{Sensor_VType::SENSOR_TYPE_NONE}; - # uint8_t valueCount{}; - # }; - - class C016_binary_element(ctypes.Structure): - _fields_ = ( - ('val1', ctypes.c_float), - ('val2', ctypes.c_float), - ('val3', ctypes.c_float), - ('val4', ctypes.c_float), - ('timestamp', ctypes.c_ulong), - ('TaskIndex', ctypes.c_uint8), - ('pluginID', ctypes.c_uint8), - ('sensorType', ctypes.c_uint8), - ('valueCount', ctypes.c_uint8) - ) - - def __str__(self): - return "{}: {{{}}}".format(self.__class__.__name__, - ", ".join(["{}: {}".format(field[0], - getattr(self, - field[0])) - for field in self._fields_])) - - ex1 = C016_binary_element.from_buffer_copy(bytestream) - return ex1 - - -def processFile(filename): - # Some example header, used for testing - # Should use the output sent by cachereader.sendtaskinfo command - header = "UNIX timestamp;UTC timestamp;task index;plugin ID;pms5003#cnt1.0;pms5003#cnt2.5;pms5003#cnt5.0;pms5003#cnt10;sunrise#co2;sunrise#T;sunrise#;sunrise#;bme280#Temperature;bme280#Humidity;bme280#Pressure;bme280#;bat#Analog;bat#;bat#;bat#;gps#long;gps#lat;gps#alt;gps#spd;batBackup#Analog;batBackup#;batBackup#;batBackup#;cachereader#FileNr;cachereader#FilePos;cachereader#;cachereader#;ADXL345#Pitch;ADXL345#Roll;ADXL345#Z;ADXL345#X;sysinfo#uptime;sysinfo#freeheap;sysinfo#rssi;sysinfo#load;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;bme2#Temperature;bme2#Humidity;bme2#Pressure;bme2#;pms1#pm1_0;pms1#pm2_5;pms1#pm10;pms1#cnt5_0;pms2#cnt0_3;pms2#cnt0_5;pms2#cnt1_0;pms2#cnt2_5;CO2#co2;CO2#temp;CO2#co2_prev;CO2#temp_prev;analog#carBat;analog#backupBat;analog#;analog#;gps2#hdop;gps2#satvis;gps2#sattracked;gps2#chksumfail" - csvheader = header.split(';') - nrcols = len(csvheader) - fileIn = open(filename, "r") - - firstline = True - - with open('decoded.csv', 'w', newline='') as fileOut: - writer = csv.writer(fileOut) - writer.writerow(csvheader) - - row_out = [0] * nrcols - - for line in fileIn: - samples = line.split(';') - length = len(samples) - - # Skip the first 2 elements on the line - # filenr and filepos - i = 2 - - while i < length: - if len(samples[i]) == 48: - data = decode(bytes.fromhex(samples[i])) - if data.TaskIndex < 32: # and data.timestamp > 1674485061: - if isNewSampleSet(data): - if firstline: - firstline = False - else: - writer.writerow(row_out) - - baseIndex = data.TaskIndex * 4 + 4 - row_out[baseIndex + 0] = data.val1 - row_out[baseIndex + 1] = data.val2 - row_out[baseIndex + 2] = data.val3 - row_out[baseIndex + 3] = data.val4 - - row_out[0] = data.timestamp - dt = datetime.datetime.fromtimestamp(data.timestamp) - row_out[1] = dt - row_out[2] = data.TaskIndex - row_out[3] = data.pluginID - - print("{}: {}".format(samples[i], data)) - i += 1 - - writer.writerow(row_out) - fileOut.close() - - -if __name__ == '__main__': - processFile("upload_18.csv") - +import csv +import ctypes +import datetime + + + +def isNewSampleSet(data): + # Each sample on a new CSV line as is done by dump8.htm + return True + + # We trigger on the event of the GPS to flush all values + # Thus consider this the start of a new row + #return data.pluginID == 82 # GPS plugin + #return data.TaskIndex == 1 + + +def decode(bytestream): + + # struct C016_binary_element + # { + # float values[VARS_PER_TASK]{}; + # unsigned long _timestamp{}; // Unix timestamp + # taskIndex_t TaskIndex{INVALID_TASK_INDEX}; + # pluginID_t pluginID{INVALID_PLUGIN_ID}; + # Sensor_VType sensorType{Sensor_VType::SENSOR_TYPE_NONE}; + # uint8_t valueCount{}; + # }; + + # FIXME TD-er: Must fix fetching pluginID > 255. See C016_binary_element + + class C016_binary_element(ctypes.Structure): + _fields_ = ( + ('val1', ctypes.c_float), + ('val2', ctypes.c_float), + ('val3', ctypes.c_float), + ('val4', ctypes.c_float), + ('timestamp', ctypes.c_ulong), + ('TaskIndex', ctypes.c_uint8), + ('pluginID', ctypes.c_uint8), + ('sensorType', ctypes.c_uint8), + ('valueCount', ctypes.c_uint8) + ) + + def __str__(self): + return "{}: {{{}}}".format(self.__class__.__name__, + ", ".join(["{}: {}".format(field[0], + getattr(self, + field[0])) + for field in self._fields_])) + + ex1 = C016_binary_element.from_buffer_copy(bytestream) + return ex1 + + +def processFile(filename): + # Some example header, used for testing + # Should use the output sent by cachereader.sendtaskinfo command + header = "UNIX timestamp;UTC timestamp;task index;plugin ID;pms5003#cnt1.0;pms5003#cnt2.5;pms5003#cnt5.0;pms5003#cnt10;sunrise#co2;sunrise#T;sunrise#;sunrise#;bme280#Temperature;bme280#Humidity;bme280#Pressure;bme280#;bat#Analog;bat#;bat#;bat#;gps#long;gps#lat;gps#alt;gps#spd;batBackup#Analog;batBackup#;batBackup#;batBackup#;cachereader#FileNr;cachereader#FilePos;cachereader#;cachereader#;ADXL345#Pitch;ADXL345#Roll;ADXL345#Z;ADXL345#X;sysinfo#uptime;sysinfo#freeheap;sysinfo#rssi;sysinfo#load;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;#;bme2#Temperature;bme2#Humidity;bme2#Pressure;bme2#;pms1#pm1_0;pms1#pm2_5;pms1#pm10;pms1#cnt5_0;pms2#cnt0_3;pms2#cnt0_5;pms2#cnt1_0;pms2#cnt2_5;CO2#co2;CO2#temp;CO2#co2_prev;CO2#temp_prev;analog#carBat;analog#backupBat;analog#;analog#;gps2#hdop;gps2#satvis;gps2#sattracked;gps2#chksumfail" + csvheader = header.split(';') + nrcols = len(csvheader) + fileIn = open(filename, "r") + + firstline = True + + with open('decoded.csv', 'w', newline='') as fileOut: + writer = csv.writer(fileOut) + writer.writerow(csvheader) + + row_out = [0] * nrcols + + for line in fileIn: + samples = line.split(';') + length = len(samples) + + # Skip the first 2 elements on the line + # filenr and filepos + i = 2 + + while i < length: + if len(samples[i]) == 48: + data = decode(bytes.fromhex(samples[i])) + if data.TaskIndex < 32: # and data.timestamp > 1674485061: + if isNewSampleSet(data): + if firstline: + firstline = False + else: + writer.writerow(row_out) + + baseIndex = data.TaskIndex * 4 + 4 + row_out[baseIndex + 0] = data.val1 + row_out[baseIndex + 1] = data.val2 + row_out[baseIndex + 2] = data.val3 + row_out[baseIndex + 3] = data.val4 + + row_out[0] = data.timestamp + dt = datetime.datetime.fromtimestamp(data.timestamp) + row_out[1] = dt + row_out[2] = data.TaskIndex + row_out[3] = data.pluginID + + print("{}: {}".format(samples[i], data)) + i += 1 + + writer.writerow(row_out) + fileOut.close() + + +if __name__ == '__main__': + processFile("upload_18.csv") + diff --git a/src/_C013.cpp b/src/_C013.cpp index 3e33bb2f8b..fe99df4ec8 100644 --- a/src/_C013.cpp +++ b/src/_C013.cpp @@ -124,7 +124,7 @@ void C013_SendUDPTaskInfo(uint8_t destUnit, uint8_t sourceTaskIndex, uint8_t des infoReply.sourceUnit = Settings.Unit; infoReply.sourceTaskIndex = sourceTaskIndex; infoReply.destTaskIndex = destTaskIndex; - infoReply.deviceNumber = pluginID; + infoReply.setPluginID(pluginID); infoReply.destUnit = destUnit; if (destUnit == 0) @@ -149,7 +149,7 @@ void C013_SendUDPTaskData(struct EventStruct *event, uint8_t destUnit, uint8_t d dataReply.sourceUnit = Settings.Unit; dataReply.sourceTaskIndex = event->TaskIndex; dataReply.destTaskIndex = destTaskIndex; - dataReply.deviceNumber = Settings.getPluginID_for_task(event->TaskIndex); + dataReply.setPluginID(Settings.getPluginID_for_task(event->TaskIndex)); // FIXME TD-er: We should check for sensorType and pluginID on both sides. // For example sending different sensor type data from one dummy to another is probably not going to work well @@ -264,7 +264,7 @@ void C013_Receive(struct EventStruct *event) { const pluginID_t currentPluginID = Settings.getPluginID_for_task(infoReply.destTaskIndex); bool mustUpdateCurrentTask = false; - if (currentPluginID == infoReply.deviceNumber) { + if (currentPluginID == infoReply.getPluginID()) { // Check to see if task already is set to receive from this host if ((Settings.TaskDeviceDataFeed[infoReply.destTaskIndex] == infoReply.sourceUnit) && Settings.TaskDeviceEnabled[infoReply.destTaskIndex]) { @@ -273,10 +273,11 @@ void C013_Receive(struct EventStruct *event) { } if ((mustUpdateCurrentTask || !validPluginID_fullcheck(currentPluginID)) && - supportedPluginID(infoReply.deviceNumber)) + supportedPluginID(infoReply.getPluginID())) { taskClear(infoReply.destTaskIndex, false); - Settings.TaskDeviceNumber[infoReply.destTaskIndex] = infoReply.deviceNumber.value; + // FIXME TD-er: Must find some extra bits to extend the pluginID beyond 255 + Settings.setPluginID_for_task(infoReply.destTaskIndex, infoReply.getPluginID()); Settings.TaskDeviceDataFeed[infoReply.destTaskIndex] = infoReply.sourceUnit; // remote feed store unit nr sending the data if (mustUpdateCurrentTask) { @@ -285,7 +286,7 @@ void C013_Receive(struct EventStruct *event) { constexpr pluginID_t DUMMY_PLUGIN_ID{ 33 }; - if ((infoReply.deviceNumber == DUMMY_PLUGIN_ID) && (infoReply.sensorType != Sensor_VType::SENSOR_TYPE_NONE)) { + if ((infoReply.getPluginID() == DUMMY_PLUGIN_ID) && (infoReply.sensorType != Sensor_VType::SENSOR_TYPE_NONE)) { // Received a dummy device and the sensor type is actually set Settings.TaskDevicePluginConfig[infoReply.destTaskIndex][0] = static_cast(infoReply.sensorType); } @@ -372,7 +373,7 @@ void C013_Receive(struct EventStruct *event) { if (loglevelActiveFor(LOG_LEVEL_ERROR)) { String log = concat(F("P2P data : PluginID mismatch for task "), dataReply.destTaskIndex + 1); log += concat(F(" from unit "), dataReply.sourceUnit); - log += concat(F(" remote: "), dataReply.deviceNumber.value); + log += concat(F(" remote: "), dataReply.getPluginID().value); log += concat(F(" local: "), Settings.getPluginID_for_task(dataReply.destTaskIndex).value); addLogMove(LOG_LEVEL_ERROR, log); } diff --git a/src/_P082_GPS.ino b/src/_P082_GPS.ino index 21d3c1a875..f16fe564c3 100644 --- a/src/_P082_GPS.ino +++ b/src/_P082_GPS.ino @@ -50,6 +50,11 @@ boolean Plugin_082(uint8_t function, struct EventStruct *event, String& string) dev.TimerOption = true; dev.PluginStats = true; dev.CustomVTypeVar = true; +# if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + + // Allow to use double type for improved accuracy + dev.HasFormatUserVar = true; +# endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE break; } @@ -68,7 +73,7 @@ boolean Plugin_082(uint8_t function, struct EventStruct *event, String& string) switch (choice) { case P082_query::P082_QUERY_LONG: case P082_query::P082_QUERY_LAT: - ExtraTaskSettings.TaskDeviceValueDecimals[i] = 6; + ExtraTaskSettings.TaskDeviceValueDecimals[i] = P082_MAX_NR_DECIMALS; break; default: ExtraTaskSettings.TaskDeviceValueDecimals[i] = 2; @@ -133,6 +138,37 @@ boolean Plugin_082(uint8_t function, struct EventStruct *event, String& string) break; } +# if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + case PLUGIN_FORMAT_USERVAR: + { + P082_data_struct *P082_data = + static_cast(getPluginTaskData(event->TaskIndex)); + + if ((nullptr != P082_data) && P082_data->isInitialized()) { + if (event->idx < P082_NR_OUTPUT_VALUES) { + const uint8_t pconfigIndex = event->idx + P082_QUERY1_CONFIG_POS; + const P082_query query = static_cast(PCONFIG(pconfigIndex)); + + if ((query == P082_query::P082_QUERY_LONG) || + (query == P082_query::P082_QUERY_LAT)) + { + const uint8_t nrDecimals = Cache.getTaskDeviceValueDecimals(event->TaskIndex, event->idx); + + if (nrDecimals > 5) { + const ESPEASY_RULES_FLOAT_TYPE value = P082_data->_cache[static_cast(query)]; + + if (!isnan(value)) { + string = doubleToString(value, nrDecimals); + success = true; + } + } + } + } + } + break; + } +# endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + case PLUGIN_GET_CONFIG_VALUE: { P082_data_struct *P082_data = @@ -142,19 +178,22 @@ boolean Plugin_082(uint8_t function, struct EventStruct *event, String& string) const P082_query query = Plugin_082_from_valuename(string); if (query != P082_query::P082_NR_OUTPUT_OPTIONS) { - const float value = P082_data->_cache[static_cast(query)]; - int nrDecimals = 2; + const ESPEASY_RULES_FLOAT_TYPE value = P082_data->_cache[static_cast(query)]; + int nrDecimals = 2; if ((query == P082_query::P082_QUERY_LONG) || (query == P082_query::P082_QUERY_LAT)) { - nrDecimals = 6; + nrDecimals = P082_MAX_NR_DECIMALS; } else if ((query == P082_query::P082_QUERY_SATVIS) || (query == P082_query::P082_QUERY_SATUSE) || (query == P082_query::P082_QUERY_FIXQ) || (query == P082_query::P082_QUERY_CHKSUM_FAIL)) { nrDecimals = 0; } - - string = toString(value, nrDecimals); +# if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + string = doubleToString(value, nrDecimals); +# else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + string = toString(value, nrDecimals); +# endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE success = true; } } @@ -417,8 +456,8 @@ boolean Plugin_082(uint8_t function, struct EventStruct *event, String& string) if (curFixStatus) { if (P082_data->gps->location.isUpdated()) { - const float lng = P082_data->gps->location.lng(); - const float lat = P082_data->gps->location.lat(); + const ESPEASY_RULES_FLOAT_TYPE lng = P082_data->gps->location.lng(); + const ESPEASY_RULES_FLOAT_TYPE lat = P082_data->gps->location.lat(); P082_setOutputValue(event, static_cast(P082_query::P082_QUERY_LONG), lng); P082_setOutputValue(event, static_cast(P082_query::P082_QUERY_LAT), lat); @@ -584,7 +623,7 @@ bool P082_referencePointSet(struct EventStruct *event) { && (P082_LAT_REF < 0.1f) && (P082_LAT_REF > -0.1f)); } -void P082_setOutputValue(struct EventStruct *event, uint8_t outputType, float value) { +void P082_setOutputValue(struct EventStruct *event, uint8_t outputType, ESPEASY_RULES_FLOAT_TYPE value) { P082_data_struct *P082_data = static_cast(getPluginTaskData(event->TaskIndex)); @@ -755,6 +794,7 @@ void P082_html_show_stats(struct EventStruct *event) { P082_data->gps->passedChecksum(), P082_data->gps->failedChecksum(), P082_data->gps->invalidData())); + # ifndef BUILD_NO_DEBUG /* diff --git a/src/src/ControllerQueue/C016_queue_element.cpp b/src/src/ControllerQueue/C016_queue_element.cpp index d21222ee39..104a705a4c 100644 --- a/src/src/ControllerQueue/C016_queue_element.cpp +++ b/src/src/ControllerQueue/C016_queue_element.cpp @@ -89,12 +89,12 @@ C016_binary_element C016_queue_element::getBinary() const { element.unixTime = unixTime; element.TaskIndex = _taskIndex; element.sensorType = sensorType; - element.valueCount = valueCount; + element.setValueCount(valueCount); element.values = values; // It makes no sense to keep the controller index when storing it. // re-purpose it to store the pluginID - element.pluginID = getPluginID_from_TaskIndex(_taskIndex); + element.setPluginID(getPluginID_from_TaskIndex(_taskIndex)); return element; } diff --git a/src/src/ControllerQueue/C016_queue_element.h b/src/src/ControllerQueue/C016_queue_element.h index 5c35d3df11..448408968b 100644 --- a/src/src/ControllerQueue/C016_queue_element.h +++ b/src/src/ControllerQueue/C016_queue_element.h @@ -9,6 +9,7 @@ # include "../CustomBuild/ESPEasyLimits.h" # include "../DataTypes/ControllerIndex.h" # include "../DataTypes/TaskValues_Data.h" +# include "../DataStructs/C016_binary_element.h" # include "../DataStructs/DeviceStruct.h" # include "../DataStructs/UnitMessageCount.h" # include "../Globals/Plugins.h" @@ -16,17 +17,6 @@ struct EventStruct; -// The binary format to store the samples using the Cache Controller -// Do NOT change order of members! -struct C016_binary_element { - TaskValues_Data_t values{}; - unsigned long unixTime{}; - taskIndex_t TaskIndex{ INVALID_TASK_INDEX }; - pluginID_t pluginID{ INVALID_PLUGIN_ID }; - Sensor_VType sensorType{ Sensor_VType::SENSOR_TYPE_NONE }; - uint8_t valueCount{}; -}; - /*********************************************************************************************\ * C016_queue_element for queueing requests for C016: Cached HTTP. diff --git a/src/src/CustomBuild/ESPEasyLimits.h b/src/src/CustomBuild/ESPEasyLimits.h index 73b5ecdeb8..1234c4b60a 100644 --- a/src/src/CustomBuild/ESPEasyLimits.h +++ b/src/src/CustomBuild/ESPEasyLimits.h @@ -74,22 +74,14 @@ // *********************************************************************** // * The next limits affect memory usage // *********************************************************************** -#ifndef DEVICES_MAX - // TODO TD-er: This should be set automatically by counting the number of included plugins. - # ifdef ESP32 - # define DEVICES_MAX 175 - #else - #if defined(PLUGIN_BUILD_COLLECTION) || defined(PLUGIN_BUILD_DEV) - # define DEVICES_MAX 95 - # else - # define DEVICES_MAX 60 - # endif - #endif -#endif #ifndef DEVICE_INDEX_MAX +#if FEATURE_SUPPORT_OVER_255_PLUGINS + #define DEVICE_INDEX_MAX 4095 // Limit determined by C016_binary_element +#else #define DEVICE_INDEX_MAX 255 #endif +#endif #ifndef PLUGIN_MAX #define PLUGIN_MAX 255 #endif diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 75413d8f15..d444304cf1 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -3809,4 +3809,12 @@ To create/register a plugin, you have to : #endif +#ifndef FEATURE_SUPPORT_OVER_255_PLUGINS +#ifdef ESP32 +#define FEATURE_SUPPORT_OVER_255_PLUGINS 1 +#else +#define FEATURE_SUPPORT_OVER_255_PLUGINS 0 +#endif +#endif + #endif // CUSTOMBUILD_DEFINE_PLUGIN_SETS_H \ No newline at end of file diff --git a/src/src/DataStructs/C013_p2p_SensorDataStruct.cpp b/src/src/DataStructs/C013_p2p_SensorDataStruct.cpp index 881a378236..c8eeba563f 100644 --- a/src/src/DataStructs/C013_p2p_SensorDataStruct.cpp +++ b/src/src/DataStructs/C013_p2p_SensorDataStruct.cpp @@ -75,8 +75,8 @@ bool C013_SensorDataStruct::setData(const uint8_t *data, size_t size) } if (size <= 24) { - deviceNumber = INVALID_PLUGIN_ID; - sensorType = Sensor_VType::SENSOR_TYPE_NONE; + setPluginID(INVALID_PLUGIN_ID); + sensorType = Sensor_VType::SENSOR_TYPE_NONE; if (sourceNode != nullptr) { sourceNodeBuild = sourceNode->build; @@ -97,6 +97,8 @@ bool C013_SensorDataStruct::setData(const uint8_t *data, size_t size) bool C013_SensorDataStruct::matchesPluginID(pluginID_t pluginID) const { + const pluginID_t deviceNumber = getPluginID(); + if ((deviceNumber.value == 255) || !validPluginID(deviceNumber) || !validPluginID(pluginID)) { // Was never set, so probably received data from older node. return true; @@ -106,11 +108,46 @@ bool C013_SensorDataStruct::matchesPluginID(pluginID_t pluginID) const bool C013_SensorDataStruct::matchesSensorType(Sensor_VType sensor_type) const { - if ((deviceNumber.value == 255) || (sensorType == Sensor_VType::SENSOR_TYPE_NONE)) { + if ((getPluginID().value == 255) || (sensorType == Sensor_VType::SENSOR_TYPE_NONE)) { // Was never set, so probably received data from older node. return true; } return sensorType == sensor_type; } +pluginID_t C013_SensorDataStruct::getPluginID() const +{ +# if FEATURE_SUPPORT_OVER_255_PLUGINS + + // Support for pluginID > 255 added in build 20240708 (build ID 20890) + if ((deviceNumber_lsb == 0) && + (deviceNumber_msb != 0) && + (sourceNodeBuild >= 20890)) { + return pluginID_t::toPluginID(deviceNumber_msb); + } +# endif // if FEATURE_SUPPORT_OVER_255_PLUGINS + return pluginID_t::toPluginID(deviceNumber_lsb); +} + +void C013_SensorDataStruct::setPluginID(pluginID_t pluginID) +{ + deviceNumber_lsb = 0u; + deviceNumber_msb = 0u; + + if (validPluginID(pluginID)) { +# if FEATURE_SUPPORT_OVER_255_PLUGINS + + if (pluginID.value <= 255) { + // Compatible with older builds + deviceNumber_lsb = (pluginID.value & 0xFF); + } else { + // Store in new 16bit member and set deviceNumber_lsb to invalid for older builds + deviceNumber_msb = pluginID.value; + } +# else // if FEATURE_SUPPORT_OVER_255_PLUGINS + deviceNumber_lsb = pluginID.value; +# endif // if FEATURE_SUPPORT_OVER_255_PLUGINS + } +} + #endif // ifdef USES_C013 diff --git a/src/src/DataStructs/C013_p2p_SensorDataStruct.h b/src/src/DataStructs/C013_p2p_SensorDataStruct.h index 6c7ffebe6e..1cac6118a2 100644 --- a/src/src/DataStructs/C013_p2p_SensorDataStruct.h +++ b/src/src/DataStructs/C013_p2p_SensorDataStruct.h @@ -19,14 +19,17 @@ struct __attribute__((__packed__)) C013_SensorDataStruct { C013_SensorDataStruct() = default; - bool setData(const uint8_t *data, - size_t size); + bool setData(const uint8_t *data, + size_t size); - bool prepareForSend(); + bool prepareForSend(); - bool matchesPluginID(pluginID_t pluginID) const; + bool matchesPluginID(pluginID_t pluginID) const; - bool matchesSensorType(Sensor_VType sensor_type) const; + bool matchesSensorType(Sensor_VType sensor_type) const; + + pluginID_t getPluginID() const; + void setPluginID(pluginID_t pluginID); uint8_t header = 255; uint8_t ID = 5; @@ -39,19 +42,27 @@ struct __attribute__((__packed__)) C013_SensorDataStruct // See: // https://github.com/letscontrolit/ESPEasy/commit/cf791527eeaf31ca98b07c45c1b64e2561a7b041#diff-86b42dd78398b103e272503f05f55ee0870ae5fb907d713c2505d63279bb0321 // Thus should not be checked - pluginID_t deviceNumber = INVALID_PLUGIN_ID; - Sensor_VType sensorType = Sensor_VType::SENSOR_TYPE_NONE; + + // PluginID least significant byte, to remain compatible with older builds + // Will be set to 0 (= INVALID_PLUGIN_ID) when a plugin ID > 255 is used + uint8_t deviceNumber_lsb = INVALID_PLUGIN_ID.value; + Sensor_VType sensorType = Sensor_VType::SENSOR_TYPE_NONE; uint8_t taskValues_Data[sizeof(TaskValues_Data_t)]{}; // Extra info added on 20240619 (build ID 20871) ShortChecksumType checksum; - uint16_t sourceNodeBuild = 0; - uint16_t timestamp_frac = 0; - uint32_t timestamp_sec = 0; + uint16_t sourceNodeBuild{}; + uint16_t timestamp_frac{}; + uint32_t timestamp_sec{}; - // Optional IDX value to allow receiving remote + // Optional IDX value to allow receiving remote // feed data on a different task index as is used on the sender node. - uint32_t IDX = 0; + uint32_t IDX{}; + + // Used for PluginID with values > 255. + // This way older builds only "see" invalid pluginID + // Added in build 20240708 (build ID 20890) + uint16_t deviceNumber_msb{}; }; #endif // ifdef USES_C013 diff --git a/src/src/DataStructs/C013_p2p_SensorInfoStruct.cpp b/src/src/DataStructs/C013_p2p_SensorInfoStruct.cpp index d215bd07d5..b99240f3ff 100644 --- a/src/src/DataStructs/C013_p2p_SensorInfoStruct.cpp +++ b/src/src/DataStructs/C013_p2p_SensorInfoStruct.cpp @@ -17,7 +17,7 @@ bool C013_SensorInfoStruct::prepareForSend(size_t& sizeToSend) { if (!(validTaskIndex(sourceTaskIndex) && validTaskIndex(destTaskIndex) && - validPluginID(deviceNumber))) { + validPluginID(getPluginID()))) { return false; } @@ -47,13 +47,13 @@ bool C013_SensorInfoStruct::prepareForSend(size_t& sizeToSend) TaskDeviceErrorValue[x] = ExtraTaskSettings.TaskDeviceErrorValue[x]; VariousBits[x] = ExtraTaskSettings.VariousBits[x]; -/* - ZERO_FILL(TaskDeviceFormula[x]); + /* + ZERO_FILL(TaskDeviceFormula[x]); - if (ExtraTaskSettings.TaskDeviceFormula[x][0] != 0) { - safe_strncpy(TaskDeviceFormula[x], ExtraTaskSettings.TaskDeviceFormula[x], sizeof(TaskDeviceFormula[x])); - } -*/ + if (ExtraTaskSettings.TaskDeviceFormula[x][0] != 0) { + safe_strncpy(TaskDeviceFormula[x], ExtraTaskSettings.TaskDeviceFormula[x], sizeof(TaskDeviceFormula[x])); + } + */ } for (uint8_t x = 0; x < PLUGIN_CONFIGVAR_MAX; ++x) { @@ -121,8 +121,8 @@ bool C013_SensorInfoStruct::setData(const uint8_t *data, size_t size) } if (size <= 138) { - deviceNumber = INVALID_PLUGIN_ID; - sensorType = Sensor_VType::SENSOR_TYPE_NONE; + setPluginID(INVALID_PLUGIN_ID); + sensorType = Sensor_VType::SENSOR_TYPE_NONE; NodeStruct *sourceNode = Nodes.getNode(data[2]); // sourceUnit @@ -141,7 +141,42 @@ bool C013_SensorInfoStruct::setData(const uint8_t *data, size_t size) return validTaskIndex(sourceTaskIndex) && validTaskIndex(destTaskIndex) && - validPluginID(deviceNumber); + validPluginID(getPluginID()); +} + +pluginID_t C013_SensorInfoStruct::getPluginID() const +{ +# if FEATURE_SUPPORT_OVER_255_PLUGINS + + // Support for pluginID > 255 added in build 20240708 (build ID 20890) + if ((deviceNumber_lsb == 0) && + (deviceNumber_msb != 0) && + (sourceNodeBuild >= 20890)) { + return pluginID_t::toPluginID(deviceNumber_msb); + } +# endif // if FEATURE_SUPPORT_OVER_255_PLUGINS + return pluginID_t::toPluginID(deviceNumber_lsb); +} + +void C013_SensorInfoStruct::setPluginID(pluginID_t pluginID) +{ + deviceNumber_lsb = 0u; + deviceNumber_msb = 0u; + + if (validPluginID(pluginID)) { +# if FEATURE_SUPPORT_OVER_255_PLUGINS + + if (pluginID.value <= 255) { + // Compatible with older builds + deviceNumber_lsb = (pluginID.value & 0xFF); + } else { + // Store in new 16bit member and set deviceNumber_lsb to invalid for older builds + deviceNumber_msb = pluginID.value; + } +# else // if FEATURE_SUPPORT_OVER_255_PLUGINS + deviceNumber_lsb = pluginID.value; +# endif // if FEATURE_SUPPORT_OVER_255_PLUGINS + } } #endif // ifdef USES_C013 diff --git a/src/src/DataStructs/C013_p2p_SensorInfoStruct.h b/src/src/DataStructs/C013_p2p_SensorInfoStruct.h index 7f4650698e..26705d07ae 100644 --- a/src/src/DataStructs/C013_p2p_SensorInfoStruct.h +++ b/src/src/DataStructs/C013_p2p_SensorInfoStruct.h @@ -19,18 +19,25 @@ struct __attribute__((__packed__)) C013_SensorInfoStruct { C013_SensorInfoStruct() = default; - bool setData(const uint8_t *data, - size_t size); - - bool prepareForSend(size_t& sizeToSend); - - uint8_t header = 255; - uint8_t ID = 3; - uint8_t sourceUnit = 0; - uint8_t destUnit = 0; - taskIndex_t sourceTaskIndex = INVALID_TASK_INDEX; - taskIndex_t destTaskIndex = INVALID_TASK_INDEX; - pluginID_t deviceNumber = INVALID_PLUGIN_ID; + bool setData(const uint8_t *data, + size_t size); + + bool prepareForSend(size_t& sizeToSend); + + pluginID_t getPluginID() const; + void setPluginID(pluginID_t pluginID); + + + uint8_t header = 255; + uint8_t ID = 3; + uint8_t sourceUnit = 0; + uint8_t destUnit = 0; + taskIndex_t sourceTaskIndex = INVALID_TASK_INDEX; + taskIndex_t destTaskIndex = INVALID_TASK_INDEX; + + // PluginID least significant byte, to remain compatible with older builds + // Will be set to 0 (= INVALID_PLUGIN_ID) when a plugin ID > 255 is used + uint8_t deviceNumber_lsb = INVALID_PLUGIN_ID.value; char taskName[26]{}; char ValueNames[VARS_PER_TASK][26]{}; Sensor_VType sensorType = Sensor_VType::SENSOR_TYPE_NONE; @@ -39,9 +46,9 @@ struct __attribute__((__packed__)) C013_SensorInfoStruct ShortChecksumType checksum; uint16_t sourceNodeBuild = 0; - // Optional IDX value to allow receiving remote + // Optional IDX value to allow receiving remote // feed data on a different task index as is used on the sender node. - uint32_t IDX = 0; + uint32_t IDX = 0; // Settings PCONFIG values int16_t TaskDevicePluginConfig[PLUGIN_CONFIGVAR_MAX]{}; @@ -54,9 +61,14 @@ struct __attribute__((__packed__)) C013_SensorInfoStruct float TaskDeviceMinValue[VARS_PER_TASK]{}; float TaskDeviceMaxValue[VARS_PER_TASK]{}; + // Used for PluginID with values > 255. + // This way older builds only "see" invalid pluginID + // Support for pluginID > 255 added in build 20240708 (build ID 20890) + uint16_t deviceNumber_msb{}; + // Put these as last as they are most likely to be empty // FIXME TD-er: Sending formula over is not working well on the receiving end. -// char TaskDeviceFormula[VARS_PER_TASK][NAME_FORMULA_LENGTH_MAX + 1]{}; + // char TaskDeviceFormula[VARS_PER_TASK][NAME_FORMULA_LENGTH_MAX + 1]{}; }; diff --git a/src/src/DataStructs/C016_binary_element.cpp b/src/src/DataStructs/C016_binary_element.cpp new file mode 100644 index 0000000000..63ed763891 --- /dev/null +++ b/src/src/DataStructs/C016_binary_element.cpp @@ -0,0 +1,34 @@ +#include "../DataStructs/C016_binary_element.h" + +#ifdef USES_C016 + +pluginID_t C016_binary_element::getPluginID() const +{ + const uint16_t value = + (static_cast(valueCount_and_pluginID_msb & 0xF0) << 4) | + pluginID_lsb; + + return pluginID_t::toPluginID(value); +} + +void C016_binary_element::setPluginID(pluginID_t pluginID) +{ + pluginID_lsb = (pluginID.value & 0xFF); + valueCount_and_pluginID_msb = + (valueCount_and_pluginID_msb & 0x0F) | + ((pluginID.value >> 4) & 0xF0); +} + +uint8_t C016_binary_element::getValueCount() const +{ + return valueCount_and_pluginID_msb & 0x0F; +} + +void C016_binary_element::setValueCount(uint8_t valueCount) +{ + valueCount_and_pluginID_msb = + (valueCount_and_pluginID_msb & 0xF0) | + (valueCount & 0x0F); +} + +#endif // ifdef USES_C016 diff --git a/src/src/DataStructs/C016_binary_element.h b/src/src/DataStructs/C016_binary_element.h new file mode 100644 index 0000000000..aff7c75568 --- /dev/null +++ b/src/src/DataStructs/C016_binary_element.h @@ -0,0 +1,33 @@ +#ifndef DATASTRUCTS_C016_BINARY_ELEMENT_H +#define DATASTRUCTS_C016_BINARY_ELEMENT_H + +#include "../../ESPEasy_common.h" +#ifdef USES_C016 + +#include "../DataTypes/PluginID.h" +#include "../DataTypes/TaskValues_Data.h" +#include "../DataTypes/TaskIndex.h" +#include "../DataTypes/SensorVType.h" + +// The binary format to store the samples using the Cache Controller +// Do NOT change order of members! +struct C016_binary_element { + + pluginID_t getPluginID() const; + void setPluginID(pluginID_t pluginID); + + uint8_t getValueCount() const; + void setValueCount(uint8_t valueCount); + + + TaskValues_Data_t values{}; + unsigned long unixTime{}; + taskIndex_t TaskIndex{ INVALID_TASK_INDEX }; + uint8_t pluginID_lsb{ 0u }; // INVALID_PLUGIN_ID.value + Sensor_VType sensorType{ Sensor_VType::SENSOR_TYPE_NONE }; + uint8_t valueCount_and_pluginID_msb{}; // msb 4 bits for pluginID, lsb 4 bits for valueCount +}; + +#endif + +#endif \ No newline at end of file diff --git a/src/src/DataStructs/DeviceStruct.h b/src/src/DataStructs/DeviceStruct.h index 09385a420a..ffaf953215 100644 --- a/src/src/DataStructs/DeviceStruct.h +++ b/src/src/DataStructs/DeviceStruct.h @@ -129,7 +129,7 @@ struct DeviceStruct }; }; - uint8_t Number; // Plugin ID number. (PLUGIN_ID_xxx) + PLUGINID_BASE_TYPE Number; // Plugin ID number. (PLUGIN_ID_xxx) uint8_t Type; // How the device is connected. e.g. DEVICE_TYPE_SINGLE => connected through 1 datapin Sensor_VType VType; // Type of value the plugin will return. e.g. SENSOR_TYPE_STRING uint8_t Ports; // Port to use when device has multiple I/O pins (N.B. not used much) diff --git a/src/src/DataStructs/ESPEasyControllerCache_CSV_dumper.cpp b/src/src/DataStructs/ESPEasyControllerCache_CSV_dumper.cpp index 6b80e0c3e8..b25270315a 100644 --- a/src/src/DataStructs/ESPEasyControllerCache_CSV_dumper.cpp +++ b/src/src/DataStructs/ESPEasyControllerCache_CSV_dumper.cpp @@ -159,7 +159,7 @@ bool ESPEasyControllerCache_CSV_dumper::createCSVLine() _outputLine.line += _separator; _outputLine.line += _element.TaskIndex; _outputLine.line += _separator; - _outputLine.line += _element.pluginID.value; + _outputLine.line += _element.getPluginID().value; } lastTimestamp = static_cast(_element.unixTime); @@ -199,7 +199,7 @@ bool ESPEasyControllerCache_CSV_dumper::createCSVLine() if (!_pluginID_str.isEmpty()) { _pluginID_str += '/'; } _taskIndex_str += _element.TaskIndex; - _pluginID_str += _element.pluginID.value; + _pluginID_str += _element.getPluginID().value; } ++csv_values_left; } diff --git a/src/src/DataStructs/PluginStats.cpp b/src/src/DataStructs/PluginStats.cpp index c836af50ae..946c634e68 100644 --- a/src/src/DataStructs/PluginStats.cpp +++ b/src/src/DataStructs/PluginStats.cpp @@ -24,9 +24,12 @@ PluginStats::PluginStats(uint8_t nrDecimals, float errorValue) : else { _samples = new (ptr) PluginStatsBuffer_t(); } +# if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + _offset = 0.0f; +# endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE _errorValueIsNaN = isnan(_errorValue); - _minValue = std::numeric_limits::max(); - _maxValue = std::numeric_limits::lowest(); + _minValue = std::numeric_limits::max(); + _maxValue = std::numeric_limits::lowest(); _minValueTimestamp = 0; _maxValueTimestamp = 0; } @@ -58,51 +61,55 @@ void PluginStats::processTimeSet(const double& time_offset) } } -bool PluginStats::push(float value) +bool PluginStats::push(ESPEASY_RULES_FLOAT_TYPE value) { if (_samples == nullptr) { return false; } +# if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + + if (_samples->isEmpty() && usableValue(value)) { + // When the first value isn't usable, we keep the offset at 0 and thus loose accuracy + _offset = value; + } + return _samples->push(value - _offset); +# else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE return _samples->push(value); +# endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE } -bool PluginStats::matchesLastTwoEntries(float value) const +bool PluginStats::matchesLastTwoEntries(ESPEASY_RULES_FLOAT_TYPE value) const { const size_t nrSamples = getNrSamples(); if (nrSamples < 2) { return false; } - const float last = (*_samples)[nrSamples - 1]; - const float beforeLast = (*_samples)[nrSamples - 2]; +# if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + return matchesLastTwoEntries(doubleToString(value, _nrDecimals)); +# else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + return matchesLastTwoEntries(toString(value, _nrDecimals)); +# endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE +} + +bool PluginStats::matchesLastTwoEntries(const String& value_str) const +{ + const size_t nrSamples = getNrSamples(); + + if (nrSamples < 2) { return false; } - const String value_str = toString(value, _nrDecimals); + const ESPEASY_RULES_FLOAT_TYPE last = get(nrSamples - 1); + const ESPEASY_RULES_FLOAT_TYPE beforeLast = get(nrSamples - 2); +# if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + return + doubleToString(last, _nrDecimals).equals(value_str) && + doubleToString(beforeLast, _nrDecimals).equals(value_str); +# else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE return toString(last, _nrDecimals).equals(value_str) && toString(beforeLast, _nrDecimals).equals(value_str); - - - /* - const bool value_valid = isValidFloat(value); - const bool last_valid = isValidFloat(last); - - if (value_valid != last_valid) { - return false; - } - const bool beforeLast_valid = isValidFloat(beforeLast); - - if (value_valid != beforeLast_valid) { - return false; - } - - if (value_valid) { - return - approximatelyEqual(value, last) && - approximatelyEqual(value, beforeLast); - } - return true; - */ +# endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE } -void PluginStats::trackPeak(float value, int64_t timestamp) +void PluginStats::trackPeak(ESPEASY_RULES_FLOAT_TYPE value, int64_t timestamp) { if ((value > _maxValue) || (value < _minValue)) { if (timestamp == 0) { @@ -124,8 +131,8 @@ void PluginStats::trackPeak(float value, int64_t timestamp) void PluginStats::resetPeaks() { - _minValue = std::numeric_limits::max(); - _maxValue = std::numeric_limits::lowest(); + _minValue = std::numeric_limits::max(); + _maxValue = std::numeric_limits::lowest(); _minValueTimestamp = 0; _maxValueTimestamp = 0; } @@ -141,16 +148,16 @@ size_t PluginStats::getNrSamples() const { return _samples->size(); } -float PluginStats::getSampleAvg() const { +ESPEASY_RULES_FLOAT_TYPE PluginStats::getSampleAvg() const { return getSampleAvg(getNrSamples()); } -float PluginStats::getSampleAvg(PluginStatsBuffer_t::index_t lastNrSamples) const +ESPEASY_RULES_FLOAT_TYPE PluginStats::getSampleAvg(PluginStatsBuffer_t::index_t lastNrSamples) const { const size_t nrSamples = getNrSamples(); if (nrSamples == 0) { return _errorValue; } - float sum = 0.0f; + ESPEASY_RULES_FLOAT_TYPE sum{}; PluginStatsBuffer_t::index_t i = 0; @@ -160,7 +167,7 @@ float PluginStats::getSampleAvg(PluginStatsBuffer_t::index_t lastNrSamples) cons PluginStatsBuffer_t::index_t samplesUsed = 0; for (; i < nrSamples; ++i) { - const float sample((*_samples)[i]); + const ESPEASY_RULES_FLOAT_TYPE sample(get(i)); if (usableValue(sample)) { ++samplesUsed; @@ -172,7 +179,7 @@ float PluginStats::getSampleAvg(PluginStatsBuffer_t::index_t lastNrSamples) cons return sum / samplesUsed; } -float PluginStats::getSampleAvg_time(PluginStatsBuffer_t::index_t lastNrSamples, uint64_t& totalDuration_usec) const +ESPEASY_RULES_FLOAT_TYPE PluginStats::getSampleAvg_time(PluginStatsBuffer_t::index_t lastNrSamples, uint64_t& totalDuration_usec) const { const size_t nrSamples = getNrSamples(); @@ -188,13 +195,13 @@ float PluginStats::getSampleAvg_time(PluginStatsBuffer_t::index_t lastNrSamples, i = nrSamples - lastNrSamples; } - int64_t lastTimestamp = 0; - float lastValue = 0.0f; - bool lastValueUsable = false; - float sum = 0.0f; + int64_t lastTimestamp = 0; + ESPEASY_RULES_FLOAT_TYPE lastValue{}; + bool lastValueUsable = false; + ESPEASY_RULES_FLOAT_TYPE sum{}; for (; i < nrSamples; ++i) { - const float sample((*_samples)[i]); + const ESPEASY_RULES_FLOAT_TYPE sample(get(i)); const int64_t curTimestamp = (*_plugin_stats_timestamps)[i]; const bool curValueUsable = usableValue(sample); @@ -203,7 +210,7 @@ float PluginStats::getSampleAvg_time(PluginStatsBuffer_t::index_t lastNrSamples, if (curValueUsable) { // Old and new value usable, take average of this period. - sum += ((lastValue + sample) / 2.0f) * duration_usec; + sum += ((lastValue + sample) / 2) * duration_usec; } else { // New value is not usable, so just add the last value for the duration. sum += lastValue * duration_usec; @@ -220,11 +227,11 @@ float PluginStats::getSampleAvg_time(PluginStatsBuffer_t::index_t lastNrSamples, return sum / totalDuration_usec; } -float PluginStats::getSampleStdDev(PluginStatsBuffer_t::index_t lastNrSamples) const +ESPEASY_RULES_FLOAT_TYPE PluginStats::getSampleStdDev(PluginStatsBuffer_t::index_t lastNrSamples) const { const size_t nrSamples = getNrSamples(); - float variance = 0.0f; - const float average = getSampleAvg(lastNrSamples); + ESPEASY_RULES_FLOAT_TYPE variance{}; + const ESPEASY_RULES_FLOAT_TYPE average = getSampleAvg(lastNrSamples); if (!usableValue(average)) { return 0.0f; } @@ -236,11 +243,11 @@ float PluginStats::getSampleStdDev(PluginStatsBuffer_t::index_t lastNrSamples) c PluginStatsBuffer_t::index_t samplesUsed = 0; for (; i < nrSamples; ++i) { - const float sample((*_samples)[i]); + const ESPEASY_RULES_FLOAT_TYPE sample(get(i)); if (usableValue(sample)) { ++samplesUsed; - const float diff = sample - average; + const ESPEASY_RULES_FLOAT_TYPE diff = sample - average; variance += diff * diff; } } @@ -248,10 +255,14 @@ float PluginStats::getSampleStdDev(PluginStatsBuffer_t::index_t lastNrSamples) c if (samplesUsed < 2) { return 0.0f; } variance /= samplesUsed; +# if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + return sqrt(variance); +# else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE return sqrtf(variance); +# endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE } -float PluginStats::getSampleExtreme(PluginStatsBuffer_t::index_t lastNrSamples, bool getMax) const +ESPEASY_RULES_FLOAT_TYPE PluginStats::getSampleExtreme(PluginStatsBuffer_t::index_t lastNrSamples, bool getMax) const { const size_t nrSamples = getNrSamples(); @@ -265,10 +276,10 @@ float PluginStats::getSampleExtreme(PluginStatsBuffer_t::index_t lastNrSamples, bool changed = false; - float res = getMax ? INT_MIN : INT_MAX; + ESPEASY_RULES_FLOAT_TYPE res = getMax ? INT_MIN : INT_MAX; for (; i < nrSamples; ++i) { - const float sample((*_samples)[i]); + const ESPEASY_RULES_FLOAT_TYPE sample(get(i)); if (usableValue(sample)) { if ((getMax && (sample > res)) || @@ -284,7 +295,7 @@ float PluginStats::getSampleExtreme(PluginStatsBuffer_t::index_t lastNrSamples, return res; } -float PluginStats::getSample(int lastNrSamples) const +ESPEASY_RULES_FLOAT_TYPE PluginStats::getSample(int lastNrSamples) const { const size_t nrSamples = getNrSamples(); @@ -299,16 +310,28 @@ float PluginStats::getSample(int lastNrSamples) const } if (i < nrSamples) { - return (*_samples)[i]; + return get(i); } return _errorValue; } -float PluginStats::operator[](PluginStatsBuffer_t::index_t index) const +ESPEASY_RULES_FLOAT_TYPE PluginStats::operator[](PluginStatsBuffer_t::index_t index) const +{ + return get(index); +} + +ESPEASY_RULES_FLOAT_TYPE PluginStats::get(PluginStatsBuffer_t::index_t index) const { const size_t nrSamples = getNrSamples(); - if (index < nrSamples) { return (*_samples)[index]; } + if (index < nrSamples) { +# if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + ESPEASY_RULES_FLOAT_TYPE res = _offset + (*_samples)[index]; + return res; +# else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + return (*_samples)[index]; +# endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + } return _errorValue; } diff --git a/src/src/DataStructs/PluginStats.h b/src/src/DataStructs/PluginStats.h index 2a320eb809..38164c04c9 100644 --- a/src/src/DataStructs/PluginStats.h +++ b/src/src/DataStructs/PluginStats.h @@ -35,23 +35,24 @@ class PluginStats { // Add a sample to the _sample buffer // This does not also track peaks as the peaks could be raw sensor data and the samples processed data. - bool push(float value); + bool push(ESPEASY_RULES_FLOAT_TYPE value); // When only updating the timestamp of the last entry, we should look at the last - bool matchesLastTwoEntries(float value) const; + bool matchesLastTwoEntries(ESPEASY_RULES_FLOAT_TYPE value) const; + bool matchesLastTwoEntries(const String& value_str) const; // Keep track of peaks. // Use this for sensors that need to take several samples before actually output a task value. // For example the ADC with oversampling - void trackPeak(float value, int64_t timestamp = 0u); + void trackPeak(ESPEASY_RULES_FLOAT_TYPE value, int64_t timestamp = 0u); // Get lowest recorded value since reset - float getPeakLow() const { + ESPEASY_RULES_FLOAT_TYPE getPeakLow() const { return hasPeaks() ? _minValue : _errorValue; } // Get highest recorded value since reset - float getPeakHigh() const { + ESPEASY_RULES_FLOAT_TYPE getPeakHigh() const { return hasPeaks() ? _maxValue : _errorValue; } @@ -75,41 +76,43 @@ class PluginStats { size_t getNrSamples() const; // Compute average over all stored values - float getSampleAvg() const; + ESPEASY_RULES_FLOAT_TYPE getSampleAvg() const; // Compute average over last N stored values - float getSampleAvg(PluginStatsBuffer_t::index_t lastNrSamples) const; + ESPEASY_RULES_FLOAT_TYPE getSampleAvg(PluginStatsBuffer_t::index_t lastNrSamples) const; // Compute the standard deviation over all stored values - float getSampleStdDev() const { + ESPEASY_RULES_FLOAT_TYPE getSampleStdDev() const { return getSampleStdDev(getNrSamples()); } // Compute average over all stored values, taking timestamp into account. // Returns average per second. - float getSampleAvg_time(uint64_t& totalDuration_usec) const { + ESPEASY_RULES_FLOAT_TYPE getSampleAvg_time(uint64_t& totalDuration_usec) const { return getSampleAvg_time(getNrSamples(), totalDuration_usec); } // Compute average over last N stored values, taking timestamp into account. // Returns average per second. - float getSampleAvg_time(PluginStatsBuffer_t::index_t lastNrSamples, + ESPEASY_RULES_FLOAT_TYPE getSampleAvg_time(PluginStatsBuffer_t::index_t lastNrSamples, uint64_t & totalDuration_usec) const; // Compute the standard deviation over last N stored values - float getSampleStdDev(PluginStatsBuffer_t::index_t lastNrSamples) const; + ESPEASY_RULES_FLOAT_TYPE getSampleStdDev(PluginStatsBuffer_t::index_t lastNrSamples) const; // Compute min/max over last N stored values - float getSampleExtreme(PluginStatsBuffer_t::index_t lastNrSamples, + ESPEASY_RULES_FLOAT_TYPE getSampleExtreme(PluginStatsBuffer_t::index_t lastNrSamples, bool getMax) const; // Compute sample stored values - float getSample(int lastNrSamples) const; + ESPEASY_RULES_FLOAT_TYPE getSample(int lastNrSamples) const; - float operator[](PluginStatsBuffer_t::index_t index) const; + ESPEASY_RULES_FLOAT_TYPE operator[](PluginStatsBuffer_t::index_t index) const; private: + ESPEASY_RULES_FLOAT_TYPE get(PluginStatsBuffer_t::index_t index) const; + static bool matchedCommand(const String & command, const __FlashStringHelper *cmd_match, int & nrSamples); @@ -177,15 +180,19 @@ class PluginStats { private: + // TD-er: Keep this comparing against float type bool usableValue(float value) const; - float _minValue; - float _maxValue; + ESPEASY_RULES_FLOAT_TYPE _minValue; + ESPEASY_RULES_FLOAT_TYPE _maxValue; int64_t _minValueTimestamp; int64_t _maxValueTimestamp; PluginStatsBuffer_t *_samples = nullptr; - float _errorValue; +#if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + ESPEASY_RULES_FLOAT_TYPE _offset{}; +#endif + float _errorValue; // TD-er: Keep this errorvalue as float type bool _errorValueIsNaN; uint8_t _nrDecimals = 3u; diff --git a/src/src/DataStructs/PluginStats_array.cpp b/src/src/DataStructs/PluginStats_array.cpp index 14a7d4782d..bd9c79c927 100644 --- a/src/src/DataStructs/PluginStats_array.cpp +++ b/src/src/DataStructs/PluginStats_array.cpp @@ -202,9 +202,7 @@ void PluginStats_array::pushPluginStatsValues( while (isSame && i < valueCount) { if (_plugin_stats[i] != nullptr) { - const float value = UserVar.getAsDouble(event->TaskIndex, i, sensorType); - - if (!_plugin_stats[i]->matchesLastTwoEntries(value)) { + if (!_plugin_stats[i]->matchesLastTwoEntries(UserVar.getAsString(event->TaskIndex, i, sensorType))) { isSame = false; } } @@ -223,7 +221,7 @@ void PluginStats_array::pushPluginStatsValues( for (size_t i = 0; i < valueCount; ++i) { if (_plugin_stats[i] != nullptr) { - const float value = UserVar.getAsDouble(event->TaskIndex, i, sensorType); + const ESPEASY_RULES_FLOAT_TYPE value = UserVar.getAsDouble(event->TaskIndex, i, sensorType); _plugin_stats[i]->push(value); if (trackPeaks) { diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index 8c2a31699e..2d2d577722 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -365,6 +365,7 @@ class SettingsStruct_tmpl void setWiFi_TX_power(float dBm); pluginID_t getPluginID_for_task(taskIndex_t taskIndex) const; + void setPluginID_for_task(taskIndex_t taskIndex, pluginID_t pluginID); void forceSave() { memset(md5, 0, 16); } @@ -432,8 +433,8 @@ class SettingsStruct_tmpl // FIXME TD-er: Must change to cpluginID_t, but then also another check must be added since changing the pluginID_t will also render settings incompatible uint8_t Protocol[CONTROLLER_MAX] = {0}; uint8_t Notification[NOTIFICATION_MAX] = {0}; //notifications, point to a NPLUGIN id - // FIXME TD-er: Must change to pluginID_t, but then also another check must be added since changing the pluginID_t will also render settings incompatible - uint8_t TaskDeviceNumber[N_TASKS] = {0}; // The "plugin number" set at as task (e.g. 4 for P004_dallas) + // PluginID least significant byte, to remain compatible with older builds + uint8_t TaskDeviceNumber_lsb[N_TASKS] = {0}; // The "plugin number" set at as task (e.g. 4 for P004_dallas) int8_t Pin_i2c2_sda = DEFAULT_PIN_I2C2_SDA; // From here, storage borrowed from OLD_TaskDeviceID array int8_t Pin_i2c2_scl = DEFAULT_PIN_I2C2_SCL; int8_t Pin_i2c3_sda = DEFAULT_PIN_I2C3_SDA; diff --git a/src/src/DataStructs/UserVarStruct.cpp b/src/src/DataStructs/UserVarStruct.cpp index ee579afe34..7738005cc2 100644 --- a/src/src/DataStructs/UserVarStruct.cpp +++ b/src/src/DataStructs/UserVarStruct.cpp @@ -4,15 +4,16 @@ #include "../DataStructs/TimingStats.h" #include "../ESPEasyCore/ESPEasy_Log.h" #include "../Globals/Cache.h" +#include "../Globals/Device.h" #include "../Globals/Plugins.h" #include "../Globals/RulesCalculate.h" #include "../Helpers/_Plugin_SensorTypeHelper.h" #include "../Helpers/CRC_functions.h" +#include "../Helpers/Numerical.h" #include "../Helpers/StringConverter.h" #include "../Helpers/StringParser.h" - void UserVarStruct::clear() { for (size_t i = 0; i < TASKS_MAX; ++i) { @@ -40,7 +41,7 @@ float UserVarStruct::operator[](unsigned int index) const static float errorvalue = NAN; #ifndef LIMIT_BUILD_SIZE addLog(LOG_LEVEL_ERROR, F("UserVar index out of range")); -#endif +#endif // ifndef LIMIT_BUILD_SIZE return errorvalue; } } @@ -253,6 +254,27 @@ ESPEASY_RULES_FLOAT_TYPE UserVarStruct::getAsDouble(taskIndex_t taskIndex, Sensor_VType sensorType, bool raw) const { +#if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + + if (raw || !Cache.hasFormula(taskIndex, varNr)) { + const deviceIndex_t DeviceIndex = getDeviceIndex_from_TaskIndex(taskIndex); + + if (Device[DeviceIndex].HasFormatUserVar) { + // First try to format using the plugin specific formatting. + String formattedUserVar; + EventStruct tempEvent(taskIndex); + tempEvent.idx = varNr; + + if (PluginCall(PLUGIN_FORMAT_USERVAR, &tempEvent, formattedUserVar)) { + ESPEASY_RULES_FLOAT_TYPE value{}; + + if (validDoubleFromString(formattedUserVar, value)) { + return value; + } + } + } + } +#endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE const TaskValues_Data_t *data = getRawOrComputed(taskIndex, varNr, sensorType, raw); if (data != nullptr) { @@ -263,6 +285,20 @@ ESPEASY_RULES_FLOAT_TYPE UserVarStruct::getAsDouble(taskIndex_t taskIndex, String UserVarStruct::getAsString(taskIndex_t taskIndex, taskVarIndex_t varNr, Sensor_VType sensorType, uint8_t nrDecimals, bool raw) const { + if (raw || !Cache.hasFormula(taskIndex, varNr)) { + const deviceIndex_t DeviceIndex = getDeviceIndex_from_TaskIndex(taskIndex); + + if (Device[DeviceIndex].HasFormatUserVar) { + // First try to format using the plugin specific formatting. + String formattedUserVar; + EventStruct tempEvent(taskIndex); + tempEvent.idx = varNr; + + if (PluginCall(PLUGIN_FORMAT_USERVAR, &tempEvent, formattedUserVar)) { + return formattedUserVar; + } + } + } const TaskValues_Data_t *data = getRawOrComputed(taskIndex, varNr, sensorType, raw); if (data != nullptr) { @@ -339,7 +375,7 @@ void UserVarStruct::clear_computed(taskIndex_t taskIndex) const uint16_t key = makeWord(taskIndex, varNr); #ifndef LIMIT_BUILD_SIZE { - auto it = _preprocessedFormula.find(key); + auto it = _preprocessedFormula.find(key); if (it != _preprocessedFormula.end()) { _preprocessedFormula.erase(it); @@ -347,7 +383,7 @@ void UserVarStruct::clear_computed(taskIndex_t taskIndex) } #endif // ifndef LIMIT_BUILD_SIZE { - auto it = _prevValue.find(key); + auto it = _prevValue.find(key); if (it != _prevValue.end()) { _prevValue.erase(it); @@ -359,6 +395,7 @@ void UserVarStruct::clear_computed(taskIndex_t taskIndex) void UserVarStruct::markPluginRead(taskIndex_t taskIndex) { struct EventStruct TempEvent(taskIndex); + for (taskVarIndex_t varNr = 0; validTaskVarIndex(varNr); ++varNr) { if (Cache.hasFormula_with_prevValue(taskIndex, varNr)) { const uint16_t key = makeWord(taskIndex, varNr); @@ -446,14 +483,15 @@ bool UserVarStruct::applyFormula(taskIndex_t taskIndex, if (formula_has_prevvalue) { const String prev_str = getPreviousValue(taskIndex, varNr, sensorType); formula.replace(F("%pvalue%"), prev_str.isEmpty() ? value : prev_str); + /* - addLog(LOG_LEVEL_INFO, - strformat( - F("pvalue: %s, value: %s, formula: %s"), - prev_str.c_str(), + addLog(LOG_LEVEL_INFO, + strformat( + F("pvalue: %s, value: %s, formula: %s"), + prev_str.c_str(), value.c_str(), formula.c_str())); - */ + */ } ESPEASY_RULES_FLOAT_TYPE result{}; @@ -515,7 +553,7 @@ String UserVarStruct::getPreprocessedFormula(taskIndex_t taskIndex, taskVarIndex if (it == _preprocessedFormula.end()) { _preprocessedFormula.emplace( std::make_pair( - key, + key, RulesCalculate_t::preProces(Cache.getTaskDeviceFormula(taskIndex, varNr)) )); } diff --git a/src/src/DataStructs_templ/SettingsStruct.cpp b/src/src/DataStructs_templ/SettingsStruct.cpp index 060b702784..9cb675644d 100644 --- a/src/src/DataStructs_templ/SettingsStruct.cpp +++ b/src/src/DataStructs_templ/SettingsStruct.cpp @@ -714,7 +714,7 @@ void SettingsStruct_tmpl::clearTask(taskIndex_t task) { TaskDeviceID[i][task] = 0u; TaskDeviceSendData[i][task] = false; } - TaskDeviceNumber[task] = 0u; //.setInvalid(); + setPluginID_for_task(task, INVALID_PLUGIN_ID); //.setInvalid(); OLD_TaskDeviceID[task] = 0u; // UNUSED: this can be removed TaskDevicePin1[task] = -1; TaskDevicePin2[task] = -1; @@ -1241,7 +1241,8 @@ void SettingsStruct_tmpl::setWiFi_TX_power(float dBm) { template pluginID_t SettingsStruct_tmpl::getPluginID_for_task(taskIndex_t taskIndex) const { if (validTaskIndex(taskIndex)) { - const uint8_t tdn = TaskDeviceNumber[taskIndex]; + // FIXME TD-er: Must find a few more unused bits to store larger pluginIDs + const uint8_t tdn = TaskDeviceNumber_lsb[taskIndex]; if (tdn > 0) { return pluginID_t::toPluginID(tdn); } @@ -1249,4 +1250,13 @@ pluginID_t SettingsStruct_tmpl::getPluginID_for_task(taskIndex_t taskIn return INVALID_PLUGIN_ID; } +template +void SettingsStruct_tmpl::setPluginID_for_task(taskIndex_t taskIndex, pluginID_t pluginID) +{ + if (validTaskIndex(taskIndex) && validPluginID(pluginID)) { + // FIXME TD-er: Must find a few more unused bits to store larger pluginIDs + TaskDeviceNumber_lsb[taskIndex] = static_cast(pluginID.value); + } +} + #endif // ifndef DATASTRUCTS_SETTINGSSTRUCT_CPP diff --git a/src/src/DataTypes/DeviceIndex.h b/src/src/DataTypes/DeviceIndex.h index 1c01457e0d..32d04f9c6d 100644 --- a/src/src/DataTypes/DeviceIndex.h +++ b/src/src/DataTypes/DeviceIndex.h @@ -1,66 +1,74 @@ -#ifndef DATATYPES_DEVICEINDEX_H -#define DATATYPES_DEVICEINDEX_H - -#include "../../ESPEasy_common.h" - -#include "../CustomBuild/ESPEasyLimits.h" - -struct deviceIndex_t { - deviceIndex_t() = default; - - deviceIndex_t(const deviceIndex_t& other) - { - value = other.value; - } - - static deviceIndex_t toDeviceIndex(unsigned other) - { - deviceIndex_t res; - res = other; - return res; - } - - // TD-er: Do not add constructor with int as argument, - // as this makes it impossible for the compiler to typecheck its use. - // deviceIndex_t(int other); - - deviceIndex_t& operator=(unsigned other) - { - value = (other < DEVICE_INDEX_MAX) ? other : DEVICE_INDEX_MAX; - return *this; - } - - - deviceIndex_t& operator=(const deviceIndex_t& other) - { - value = other.value; - return *this; - } - - // TD-er: Using operator unsigned() makes it impossible for the compiler to check for types. - // However, since the Device[] array is accessed using a deviceIndex_t, we need this operator unsigned() - // on ESP8266. - // On ESP32 we have a strongly typed DeviceVector class and thus we can properly check per operator. - #ifndef ESP8266 - bool operator<(unsigned other) const { return value < other; } - bool operator!=(unsigned other) const { return value != other; } - bool operator!=(const deviceIndex_t& other) const { return value != other.value; } - - #else // ifndef ESP8266 - operator unsigned() const { return value; } - #endif // ifndef ESP8266 - - deviceIndex_t& operator++() - { - // pre-increment, ++a - ++value; - return *this; - } - - uint8_t value{}; // Init this to 0, so we can easily iterate over it. -}; - - -extern deviceIndex_t INVALID_DEVICE_INDEX; - -#endif // ifndef DATATYPES_DEVICEINDEX_H +#ifndef DATATYPES_DEVICEINDEX_H +#define DATATYPES_DEVICEINDEX_H + +#include "../../ESPEasy_common.h" + +#include "../CustomBuild/ESPEasyLimits.h" + +#if FEATURE_SUPPORT_OVER_255_PLUGINS +#define DEVICEINDEX_BASE_TYPE uint16_t +#else +#define DEVICEINDEX_BASE_TYPE uint8_t +#endif + +struct deviceIndex_t { + deviceIndex_t() = default; + + deviceIndex_t(const deviceIndex_t& other) + { + value = other.value; + } + + static deviceIndex_t toDeviceIndex(unsigned other) + { + deviceIndex_t res; + res = other; + return res; + } + + // TD-er: Do not add constructor with int as argument, + // as this makes it impossible for the compiler to typecheck its use. + // deviceIndex_t(int other); + + deviceIndex_t& operator=(unsigned other) + { + value = (other < DEVICE_INDEX_MAX) ? other : DEVICE_INDEX_MAX; + return *this; + } + + + deviceIndex_t& operator=(const deviceIndex_t& other) + { + value = other.value; + return *this; + } + + // TD-er: Using operator unsigned() makes it impossible for the compiler to check for types. + // However, since the Device[] array is accessed using a deviceIndex_t, we need this operator unsigned() + // on ESP8266. + // On ESP32 we have a strongly typed DeviceVector class and thus we can properly check per operator. + #ifndef ESP8266 + bool operator<(unsigned other) const { return value < other; } + bool operator!=(unsigned other) const { return value != other; } + bool operator!=(const deviceIndex_t& other) const { return value != other.value; } + + #else // ifndef ESP8266 + operator unsigned() const { return value; } + #endif // ifndef ESP8266 + + deviceIndex_t& operator++() + { + // pre-increment, ++a + ++value; + return *this; + } + + // FIXME TD-er: This should also be set to 16 bit if we support > 255 plugins, + // we should also support builds with > 255 plugins included + DEVICEINDEX_BASE_TYPE value{}; // Init this to 0, so we can easily iterate over it. +}; + + +extern deviceIndex_t INVALID_DEVICE_INDEX; + +#endif // ifndef DATATYPES_DEVICEINDEX_H diff --git a/src/src/DataTypes/PluginID.h b/src/src/DataTypes/PluginID.h index 8e9b909da5..bde82f83f4 100644 --- a/src/src/DataTypes/PluginID.h +++ b/src/src/DataTypes/PluginID.h @@ -1,54 +1,63 @@ -#ifndef DATATYPES_PLUGINID_H -#define DATATYPES_PLUGINID_H - -#include "../../ESPEasy_common.h" - -struct __attribute__((__packed__)) pluginID_t { - pluginID_t() = default; - - pluginID_t(const pluginID_t& other) - { - value = other.value; - } - - constexpr explicit pluginID_t(uint8_t id) : value(id) {} - - - static pluginID_t toPluginID(unsigned other) - { - pluginID_t res; - - if (other <= 255) { res.value = other; } - - return res; - } - - pluginID_t& operator=(const pluginID_t& other) - { - value = other.value; - return *this; - } - - bool operator==(const pluginID_t& other) const - { - return this->value == other.value; - } - - bool operator!=(const pluginID_t& other) const - { - return this->value != other.value; - } - - void setInvalid() { - value = 0; - } - - String toDisplayString() const; - - uint8_t value{}; -}; - - -extern const pluginID_t INVALID_PLUGIN_ID; - -#endif // ifndef DATATYPES_PLUGINID_H +#ifndef DATATYPES_PLUGINID_H +#define DATATYPES_PLUGINID_H + +#include "../../ESPEasy_common.h" + +#if FEATURE_SUPPORT_OVER_255_PLUGINS +#define PLUGINID_BASE_TYPE uint16_t +#else +#define PLUGINID_BASE_TYPE uint8_t +#endif + + + +struct __attribute__((__packed__)) pluginID_t { + pluginID_t() = default; + + pluginID_t(const pluginID_t& other) + { + value = other.value; + } + + constexpr explicit pluginID_t(uint8_t id) : value(id) {} + + + static pluginID_t toPluginID(unsigned other) + { + pluginID_t res; + + if (other <= 255) { res.value = other; } + + return res; + } + + pluginID_t& operator=(const pluginID_t& other) + { + value = other.value; + return *this; + } + + bool operator==(const pluginID_t& other) const + { + return this->value == other.value; + } + + bool operator!=(const pluginID_t& other) const + { + return this->value != other.value; + } + + void setInvalid() { + value = 0; + } + + String toDisplayString() const; + + // Max value = 4095, due to how the bits are stored in C016_binary_element + PLUGINID_BASE_TYPE value{}; +}; + +// Invalid PluginID value = 0 +extern const pluginID_t INVALID_PLUGIN_ID; + +#endif // ifndef DATATYPES_PLUGINID_H diff --git a/src/src/Globals/C016_ControllerCache.cpp b/src/src/Globals/C016_ControllerCache.cpp index e27c4b6916..49cc7f5d6a 100644 --- a/src/src/Globals/C016_ControllerCache.cpp +++ b/src/src/Globals/C016_ControllerCache.cpp @@ -1,64 +1,64 @@ -#include "../Globals/C016_ControllerCache.h" - -#ifdef USES_C016 - - -# include "../ControllerQueue/C016_queue_element.h" - -ControllerCache_struct ControllerCache; - -void C016_flush() { - ControllerCache.flush(); -} - -bool C016_CacheInitialized() { - return ControllerCache.isInitialized(); -} - -String C016_getCacheFileName(int& fileNr, bool& islast) { - return ControllerCache.getNextCacheFileName(fileNr, islast); -} - -bool C016_deleteOldestCacheBlock() { - return ControllerCache.deleteOldestCacheBlock(); -} - -bool C016_deleteAllCacheBlocks() { - return ControllerCache.deleteAllCacheBlocks(); -} - -bool C016_getTaskSample(C016_binary_element& element) { - return ControllerCache.peek((uint8_t *)&element, sizeof(element)); -} - -struct EventStruct C016_getTaskSample( - unsigned long& timestamp, - uint8_t & valueCount, - float & val1, - float & val2, - float & val3, - float & val4) -{ - C016_binary_element element; - - if (!ControllerCache.peek((uint8_t *)&element, sizeof(element))) { - return EventStruct(); - } - - timestamp = element.unixTime; - valueCount = element.valueCount; - val1 = element.values.getFloat(0); - val2 = element.values.getFloat(1); - val3 = element.values.getFloat(2); - val4 = element.values.getFloat(3); - - EventStruct event(element.TaskIndex); - - // FIXME TD-er: Is this needed? - // event.ControllerIndex = element._controller_idx; - event.sensorType = element.sensorType; - - return event; -} - -#endif // ifdef USES_C016 +#include "../Globals/C016_ControllerCache.h" + +#ifdef USES_C016 + + +# include "../ControllerQueue/C016_queue_element.h" + +ControllerCache_struct ControllerCache; + +void C016_flush() { + ControllerCache.flush(); +} + +bool C016_CacheInitialized() { + return ControllerCache.isInitialized(); +} + +String C016_getCacheFileName(int& fileNr, bool& islast) { + return ControllerCache.getNextCacheFileName(fileNr, islast); +} + +bool C016_deleteOldestCacheBlock() { + return ControllerCache.deleteOldestCacheBlock(); +} + +bool C016_deleteAllCacheBlocks() { + return ControllerCache.deleteAllCacheBlocks(); +} + +bool C016_getTaskSample(C016_binary_element& element) { + return ControllerCache.peek((uint8_t *)&element, sizeof(element)); +} + +struct EventStruct C016_getTaskSample( + unsigned long& timestamp, + uint8_t & valueCount, + float & val1, + float & val2, + float & val3, + float & val4) +{ + C016_binary_element element; + + if (!ControllerCache.peek((uint8_t *)&element, sizeof(element))) { + return EventStruct(); + } + + timestamp = element.unixTime; + valueCount = element.getValueCount(); + val1 = element.values.getFloat(0); + val2 = element.values.getFloat(1); + val3 = element.values.getFloat(2); + val4 = element.values.getFloat(3); + + EventStruct event(element.TaskIndex); + + // FIXME TD-er: Is this needed? + // event.ControllerIndex = element._controller_idx; + event.sensorType = element.sensorType; + + return event; +} + +#endif // ifdef USES_C016 diff --git a/src/src/Helpers/ESPEasy_checks.cpp b/src/src/Helpers/ESPEasy_checks.cpp index f2ab659359..41d681abfb 100644 --- a/src/src/Helpers/ESPEasy_checks.cpp +++ b/src/src/Helpers/ESPEasy_checks.cpp @@ -119,8 +119,16 @@ void run_compiletime_checks() { const unsigned int LogStructSize = ((13u + 20 * LOG_STRUCT_MESSAGE_LINES) + 3) & ~3; #endif check_size(); // Is not stored +#if FEATURE_SUPPORT_OVER_255_PLUGINS + check_size(); // Is not stored +#else check_size(); // Is not stored +#endif + #if FEATURE_MQTT_TLS + check_size(); + #else check_size(); + #endif #if FEATURE_NOTIFIER check_size(); #endif // if FEATURE_NOTIFIER @@ -132,12 +140,16 @@ void run_compiletime_checks() { #endif check_size(); check_size(); - check_size(); +#if FEATURE_SUPPORT_OVER_255_PLUGINS + check_size(); // Is not stored +#else + check_size(); // Is not stored +#endif check_size(); check_size(); #ifdef USES_C013 - check_size(); - check_size(); + check_size(); + check_size(); #endif #ifdef USES_C016 check_size(); @@ -182,7 +194,7 @@ void run_compiletime_checks() { static_assert(192u == offsetof(SettingsStruct, Protocol), ""); static_assert(195u == offsetof(SettingsStruct, Notification), "CONTROLLER_MAX has changed?"); - static_assert(198u == offsetof(SettingsStruct, TaskDeviceNumber), "NOTIFICATION_MAX has changed?"); + static_assert(198u == offsetof(SettingsStruct, TaskDeviceNumber_lsb), "NOTIFICATION_MAX has changed?"); // All settings related to N_TASKS static_assert((228 + TASKS_MAX) == offsetof(SettingsStruct, OLD_TaskDeviceID), ""); // 32-bit alignment, so offset of 2 bytes. diff --git a/src/src/Helpers/Hardware.cpp b/src/src/Helpers/Hardware.cpp index b8f41a32ed..2f90c76799 100644 --- a/src/src/Helpers/Hardware.cpp +++ b/src/src/Helpers/Hardware.cpp @@ -960,7 +960,7 @@ void setTaskDevice_to_TaskIndex(pluginID_t taskdevicenumber, taskIndex_t taskInd taskClear(taskIndex, false); // clear settings, but do not save ClearCustomTaskSettings(taskIndex); - Settings.TaskDeviceNumber[taskIndex] = taskdevicenumber.value; + Settings.setPluginID_for_task(taskIndex, taskdevicenumber); // Settings.getPluginID_for_task(taskIndex) = taskdevicenumber; diff --git a/src/src/Helpers/StringConverter.cpp b/src/src/Helpers/StringConverter.cpp index 27311893d1..d3991f2d35 100644 --- a/src/src/Helpers/StringConverter.cpp +++ b/src/src/Helpers/StringConverter.cpp @@ -453,13 +453,12 @@ String doFormatUserVar(struct EventStruct *event, uint8_t rel_index, bool mustCh if (Device[DeviceIndex].HasFormatUserVar) { // First try to format using the plugin specific formatting. - String result; + String formattedUserVar; EventStruct tempEvent; tempEvent.deep_copy(event); tempEvent.idx = rel_index; - PluginCall(PLUGIN_FORMAT_USERVAR, &tempEvent, result); - if (result.length() > 0) { - return result; + if (PluginCall(PLUGIN_FORMAT_USERVAR, &tempEvent, formattedUserVar)) { + return formattedUserVar; } } diff --git a/src/src/Helpers/_Plugin_init.cpp b/src/src/Helpers/_Plugin_init.cpp index 9365b8d794..9e30728347 100644 --- a/src/src/Helpers/_Plugin_init.cpp +++ b/src/src/Helpers/_Plugin_init.cpp @@ -16,7 +16,7 @@ // Vector to match a "DeviceIndex" to a plugin ID. -constexpr /*pluginID_t*/ uint8_t DeviceIndex_to_Plugin_id[] PROGMEM = +constexpr PLUGINID_BASE_TYPE DeviceIndex_to_Plugin_id[] PROGMEM = { #ifdef USES_P001 1, @@ -2174,7 +2174,12 @@ pluginID_t getPluginID_from_DeviceIndex(deviceIndex_t deviceIndex) { if (validDeviceIndex_init(deviceIndex)) { + #if FEATURE_SUPPORT_OVER_255_PLUGINS + // FIXME TD-er: Check if this uses the correct pointer arithmic. + return pluginID_t::toPluginID(pgm_read_word(DeviceIndex_to_Plugin_id + deviceIndex.value)); + #else return pluginID_t::toPluginID(pgm_read_byte(DeviceIndex_to_Plugin_id + deviceIndex.value)); + #endif } return INVALID_PLUGIN_ID; } diff --git a/src/src/PluginStructs/P082_data_struct.cpp b/src/src/PluginStructs/P082_data_struct.cpp index c51b682bf7..f69207c925 100644 --- a/src/src/PluginStructs/P082_data_struct.cpp +++ b/src/src/PluginStructs/P082_data_struct.cpp @@ -89,7 +89,7 @@ void P082_software_pps::setSentenceType( const uint64_t endOfLine_received_usec = getMicros64() - bytesToUsec(bytesAvailableInSerialBuffer); const int64_t sentence_duration = timeDiff64(_cur_start_sentence_usec, endOfLine_received_usec); -// if (usecPassedSince(_cur_start_sentence_usec) < 1000000ll) { + // if (usecPassedSince(_cur_start_sentence_usec) < 1000000ll) { // Assume a NMEA sentence cannot be over 80 bytes // Apply some tolerance, thus check for duration to receive 120 bytes @@ -144,16 +144,16 @@ uint64_t P082_software_pps::bytesToUsec(uint32_t bytes) const return duration_usec; } -#ifndef BUILD_NO_DEBUG +# ifndef BUILD_NO_DEBUG String P082_software_pps::getStats() const { String res; constexpr uint32_t nrelements = NR_ELEMENTS(_second_frac_in_usec); + for (size_t i = 0; i < nrelements; ++i) { uint32_t value{}; _second_frac_in_usec[i].peek(value); { - switch (i) { case TinyGPSPlus::GPS_SENTENCE_GPGGA: res += F("GGA"); break; case TinyGPSPlus::GPS_SENTENCE_GPRMC: res += F("RMC"); break; @@ -162,16 +162,16 @@ String P082_software_pps::getStats() const case TinyGPSPlus::GPS_SENTENCE_GPGLL: res += F("GLL"); break; case TinyGPSPlus::GPS_SENTENCE_GPTXT: res += F("TXT"); break; default: - res += F("---"); - break; + res += F("---"); + break; } res += strformat(F(": %06d (%d)
"), value, _second_frac_in_usec[i].getCount()); } } - return res; + return res; } -#endif +# endif // ifndef BUILD_NO_DEBUG P082_data_struct::P082_data_struct() : gps(nullptr), easySerial(nullptr) { @@ -352,6 +352,7 @@ bool P082_data_struct::loop() { available = easySerial->available(); _softwarePPS.addStartOfSentence(available); } + if (available == 0) { available = easySerial->available(); } @@ -772,12 +773,13 @@ void P082_data_struct::webformLoad_show_position_scatterplot(struct EventStruct # endif // if FEATURE_CHART_JS # endif // if FEATURE_PLUGIN_STATS -#ifndef BUILD_NO_DEBUG +# ifndef BUILD_NO_DEBUG String P082_data_struct::getPPSStats() const { return _softwarePPS.getStats(); } -#endif + +# endif // ifndef BUILD_NO_DEBUG -#endif // ifdef USES_P082 +#endif // ifdef USES_P082 diff --git a/src/src/PluginStructs/P082_data_struct.h b/src/src/PluginStructs/P082_data_struct.h index 8222aaa51b..001f5e04b7 100644 --- a/src/src/PluginStructs/P082_data_struct.h +++ b/src/src/PluginStructs/P082_data_struct.h @@ -47,6 +47,12 @@ # define P082_QUERY3_DFLT P082_query::P082_QUERY_ALT # define P082_QUERY4_DFLT P082_query::P082_QUERY_SPD +#if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE +#define P082_MAX_NR_DECIMALS 9 +#else +#define P082_MAX_NR_DECIMALS 6 +#endif + enum class P082_query : uint8_t { P082_QUERY_LONG = 0, @@ -255,7 +261,7 @@ struct P082_data_struct : public PluginTaskData_base { String _currentSentence; # endif // ifdef P082_SEND_GPS_TO_LOG - float _cache[static_cast(P082_query::P082_NR_OUTPUT_OPTIONS)]{}; + ESPEASY_RULES_FLOAT_TYPE _cache[static_cast(P082_query::P082_NR_OUTPUT_OPTIONS)]{}; OversamplingHelper_oversampling_gps_time_offset_usec; diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index 35c3b0435d..bfb42a562f 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -275,7 +275,7 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task unsigned long taskdevicetimer = getFormItemInt(F("TDT"), 0); - Settings.TaskDeviceNumber[taskIndex] = taskdevicenumber.value; + Settings.setPluginID_for_task(taskIndex, taskdevicenumber); if (device.Type == DEVICE_TYPE_I2C) { uint8_t flags = 0; @@ -1763,7 +1763,12 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde { html_TD(); const String id = getPluginCustomArgName(F("TDVD"), varNr); // ="taskdevicevaluedecimals" - addNumericBox(id, Cache.getTaskDeviceValueDecimals(taskIndex, varNr), 0, 6); +#if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + constexpr int max_nr_decimals = 9; +#else + constexpr int max_nr_decimals = 6; +#endif + addNumericBox(id, Cache.getTaskDeviceValueDecimals(taskIndex, varNr), 0, max_nr_decimals); } # if FEATURE_PLUGIN_STATS