From beb7fee65b26221faa8fdff25f4319bcef020ad3 Mon Sep 17 00:00:00 2001 From: Gijs Noorlander Date: Wed, 4 Sep 2019 02:23:31 +0200 Subject: [PATCH] [Cleanup] Move structs to separate .h/.cpp files --- src/DataStructs/CRCStruct.h | 1 + src/DataStructs/ControllerSettingsStruct.cpp | 3 +- src/DataStructs/ControllerSettingsStruct.h | 29 +- src/DataStructs/DeviceStruct.h | 66 + src/DataStructs/ESPEasyDefaults.h | 5 - src/DataStructs/ESPEasyLimits.h | 65 +- src/DataStructs/ExtraTaskSettingsStruct.cpp | 73 ++ src/DataStructs/ExtraTaskSettingsStruct.h | 42 + src/DataStructs/LogStruct.cpp | 102 ++ src/DataStructs/LogStruct.h | 55 + src/DataStructs/NodeStruct.h | 29 + .../NotificationSettingsStruct.cpp | 26 + src/DataStructs/NotificationSettingsStruct.h | 34 + src/DataStructs/NotificationStruct.h | 19 + src/DataStructs/PortStatusStruct.h | 30 + src/DataStructs/ProtocolStruct.h | 35 + src/DataStructs/RTCStruct.h | 36 + src/DataStructs/SecurityStruct.cpp | 32 + src/DataStructs/SecurityStruct.h | 29 +- src/DataStructs/SettingsStruct.h | 2 + src/DataStructs/SystemTimerStruct.h | 28 + src/DataStructs/TimingStats.cpp | 213 ++++ src/DataStructs/TimingStats.h | 93 ++ src/DelayQueueElements.h | 643 ++++++++++ src/ESPEasy-Globals.h | 1109 +---------------- src/ESPEasy.ino | 20 + src/ESPEasyStorage.ino | 25 + src/ESPEasy_Log.h | 54 + src/ESPEasy_Log.ino | 185 +++ src/ESPEasy_buildinfo.h | 31 + src/ESPEasy_common.cpp | 3 + src/ESPEasy_common.h | 2 + src/ESPEasy_fdwdecl.h | 33 +- src/ESPEasy_packed_raw_data.cpp | 91 ++ src/ESPEasy_packed_raw_data.h | 83 ++ src/ESPEasy_plugindefs.h | 81 ++ src/Misc.ino | 182 --- src/Scheduler.ino | 2 +- src/_CPlugin_Helper.cpp | 422 +++++++ src/_CPlugin_Helper.h | 1020 +-------------- src/_CPlugin_LoRa_TTN_helper.ino | 1 + src/_P026_Sysinfo.ino | 2 + src/_P082_GPS.ino | 1 + src/_Plugin_Helper.cpp | 103 ++ src/_Plugin_Helper.h | 96 +- 45 files changed, 2864 insertions(+), 2372 deletions(-) create mode 100644 src/DataStructs/DeviceStruct.h create mode 100644 src/DataStructs/ExtraTaskSettingsStruct.cpp create mode 100644 src/DataStructs/ExtraTaskSettingsStruct.h create mode 100644 src/DataStructs/LogStruct.cpp create mode 100644 src/DataStructs/LogStruct.h create mode 100644 src/DataStructs/NodeStruct.h create mode 100644 src/DataStructs/NotificationSettingsStruct.cpp create mode 100644 src/DataStructs/NotificationSettingsStruct.h create mode 100644 src/DataStructs/NotificationStruct.h create mode 100644 src/DataStructs/PortStatusStruct.h create mode 100644 src/DataStructs/ProtocolStruct.h create mode 100644 src/DataStructs/RTCStruct.h create mode 100644 src/DataStructs/SecurityStruct.cpp create mode 100644 src/DataStructs/SystemTimerStruct.h create mode 100644 src/DataStructs/TimingStats.cpp create mode 100644 src/DataStructs/TimingStats.h create mode 100644 src/DelayQueueElements.h create mode 100644 src/ESPEasy_Log.h create mode 100644 src/ESPEasy_Log.ino create mode 100644 src/ESPEasy_buildinfo.h create mode 100644 src/ESPEasy_packed_raw_data.cpp create mode 100644 src/ESPEasy_packed_raw_data.h create mode 100644 src/ESPEasy_plugindefs.h create mode 100644 src/_CPlugin_Helper.cpp create mode 100644 src/_Plugin_Helper.cpp diff --git a/src/DataStructs/CRCStruct.h b/src/DataStructs/CRCStruct.h index c7d663ff0..9b58f60cb 100644 --- a/src/DataStructs/CRCStruct.h +++ b/src/DataStructs/CRCStruct.h @@ -14,4 +14,5 @@ struct CRCStruct{ uint32_t numberOfCRCBytes=0; }; + #endif // DATASTRUCTS_CRCSTRUCT_H \ No newline at end of file diff --git a/src/DataStructs/ControllerSettingsStruct.cpp b/src/DataStructs/ControllerSettingsStruct.cpp index 58c9c98ad..d70e741d7 100644 --- a/src/DataStructs/ControllerSettingsStruct.cpp +++ b/src/DataStructs/ControllerSettingsStruct.cpp @@ -1,6 +1,7 @@ +#include "DataStructs/ControllerSettingsStruct.h" + #include "ESPEasy_common.h" #include "ESPEasy_fdwdecl.h" -#include "DataStructs/ControllerSettingsStruct.h" #include "DataStructs/ESPEasyLimits.h" #include "DataStructs/ESPEasyDefaults.h" diff --git a/src/DataStructs/ControllerSettingsStruct.h b/src/DataStructs/ControllerSettingsStruct.h index 8fef2c5a0..eea83ce4a 100644 --- a/src/DataStructs/ControllerSettingsStruct.h +++ b/src/DataStructs/ControllerSettingsStruct.h @@ -5,6 +5,8 @@ * ControllerSettingsStruct definition \*********************************************************************************************/ #include +#include // For std::shared_ptr + class IPAddress; class WiFiClient; class WiFiUDP; @@ -24,6 +26,31 @@ class WiFiUDP; #define CONTROLLER_CLIENTTIMEOUT_DFLT 100 + +// ******************************************************************************** +// IDs of controller settings, used to generate web forms +// ******************************************************************************** + +#define CONTROLLER_USE_DNS 1 +#define CONTROLLER_HOSTNAME 2 +#define CONTROLLER_IP 3 +#define CONTROLLER_PORT 4 +#define CONTROLLER_USER 5 +#define CONTROLLER_PASS 6 +#define CONTROLLER_MIN_SEND_INTERVAL 7 +#define CONTROLLER_MAX_QUEUE_DEPTH 8 +#define CONTROLLER_MAX_RETRIES 9 +#define CONTROLLER_FULL_QUEUE_ACTION 10 +#define CONTROLLER_CHECK_REPLY 12 +#define CONTROLLER_SUBSCRIBE 13 +#define CONTROLLER_PUBLISH 14 +#define CONTROLLER_LWT_TOPIC 15 +#define CONTROLLER_LWT_CONNECT_MESSAGE 16 +#define CONTROLLER_LWT_DISCONNECT_MESSAGE 17 +#define CONTROLLER_TIMEOUT 18 +#define CONTROLLER_SAMPLE_SET_INITIATOR 19 +#define CONTROLLER_ENABLED 20 // Keep this as last, is used to loop over all parameters + struct ControllerSettingsStruct { ControllerSettingsStruct(); @@ -74,5 +101,5 @@ private: typedef std::shared_ptr ControllerSettingsStruct_ptr_type; #define MakeControllerSettings(T) ControllerSettingsStruct_ptr_type ControllerSettingsStruct_ptr(new ControllerSettingsStruct());\ ControllerSettingsStruct& T = *ControllerSettingsStruct_ptr; - + #endif // DATASTRUCTS_CONTROLLERSETTINGSSTRUCT_H \ No newline at end of file diff --git a/src/DataStructs/DeviceStruct.h b/src/DataStructs/DeviceStruct.h new file mode 100644 index 000000000..c1ad426cf --- /dev/null +++ b/src/DataStructs/DeviceStruct.h @@ -0,0 +1,66 @@ +#ifndef DATASTRUCTS_DEVICESTRUCTS_H +#define DATASTRUCTS_DEVICESTRUCTS_H + + +#include +#include + +#define DEVICE_TYPE_SINGLE 1 // connected through 1 datapin +#define DEVICE_TYPE_DUAL 2 // connected through 2 datapins +#define DEVICE_TYPE_TRIPLE 3 // connected through 3 datapins +#define DEVICE_TYPE_ANALOG 10 // AIN/tout pin +#define DEVICE_TYPE_I2C 20 // connected through I2C +#define DEVICE_TYPE_DUMMY 99 // Dummy device, has no physical connection + +// Used for VType +#define SENSOR_TYPE_NONE 0 +#define SENSOR_TYPE_SINGLE 1 +#define SENSOR_TYPE_TEMP_HUM 2 +#define SENSOR_TYPE_TEMP_BARO 3 +#define SENSOR_TYPE_TEMP_HUM_BARO 4 +#define SENSOR_TYPE_DUAL 5 +#define SENSOR_TYPE_TRIPLE 6 +#define SENSOR_TYPE_QUAD 7 +#define SENSOR_TYPE_TEMP_EMPTY_BARO 8 +#define SENSOR_TYPE_SWITCH 10 +#define SENSOR_TYPE_DIMMER 11 +#define SENSOR_TYPE_LONG 20 +#define SENSOR_TYPE_WIND 21 +#define SENSOR_TYPE_STRING 22 + +/*********************************************************************************************\ + * DeviceStruct +\*********************************************************************************************/ +struct DeviceStruct +{ + DeviceStruct() : + Number(0), Type(0), VType(SENSOR_TYPE_NONE), Ports(0), ValueCount(0), + PullUpOption(false), InverseLogicOption(false), FormulaOption(false), + Custom(false), SendDataOption(false), GlobalSyncOption(false), + TimerOption(false), TimerOptional(false), DecimalsOnly(false) {} + + bool connectedToGPIOpins() { + return (Type >= DEVICE_TYPE_SINGLE && Type <= DEVICE_TYPE_TRIPLE); + } + + + byte Number; // Plugin ID number. (PLUGIN_ID_xxx) + byte Type; // How the device is connected. e.g. DEVICE_TYPE_SINGLE => connected through 1 datapin + byte VType; // Type of value the plugin will return, used only for Domoticz + byte Ports; // Port to use when device has multiple I/O pins (N.B. not used much) + byte ValueCount; // The number of output values of a plugin. The value should match the number of keys PLUGIN_VALUENAME1_xxx + bool PullUpOption : 1; // Allow to set internal pull-up resistors. + bool InverseLogicOption : 1; // Allow to invert the boolean state (e.g. a switch) + bool FormulaOption : 1; // Allow to enter a formula to convert values during read. (not possible with Custom enabled) + bool Custom : 1; + bool SendDataOption : 1; // Allow to send data to a controller. + bool GlobalSyncOption : 1; // No longer used. Was used for ESPeasy values sync between nodes + bool TimerOption : 1; // Allow to set the "Interval" timer for the plugin. + bool TimerOptional : 1; // When taskdevice timer is not set and not optional, use default "Interval" delay (Settings.Delay) + bool DecimalsOnly : 1; // Allow to set the number of decimals (otherwise treated a 0 decimals) +}; +typedef std::vector DeviceVector; +DeviceVector Device; + + +#endif // DATASTRUCTS_DEVICESTRUCTS_H \ No newline at end of file diff --git a/src/DataStructs/ESPEasyDefaults.h b/src/DataStructs/ESPEasyDefaults.h index dc17cc30a..af35f125f 100644 --- a/src/DataStructs/ESPEasyDefaults.h +++ b/src/DataStructs/ESPEasyDefaults.h @@ -183,11 +183,6 @@ #define DEFAULT_USE_DST false // (true|false) Use Daily Time Saving #endif -#define LOG_TO_SERIAL 1 -#define LOG_TO_SYSLOG 2 -#define LOG_TO_WEBLOG 3 -#define LOG_TO_SDCARD 4 - #ifndef DEFAULT_SYSLOG_IP #define DEFAULT_SYSLOG_IP "" // Syslog IP Address #endif diff --git a/src/DataStructs/ESPEasyLimits.h b/src/DataStructs/ESPEasyLimits.h index b7dd0f7a4..bd822a7b4 100644 --- a/src/DataStructs/ESPEasyLimits.h +++ b/src/DataStructs/ESPEasyLimits.h @@ -1,15 +1,11 @@ #ifndef DATASTRUCTS_ESPEASY_LIMITS_H #define DATASTRUCTS_ESPEASY_LIMITS_H -#if defined(PLUGIN_BUILD_TESTING) || defined(PLUGIN_BUILD_DEV) - #define DEVICES_MAX 95 -#else - #ifdef ESP32 - #define DEVICES_MAX 85 - #else - #define DEVICES_MAX 60 - #endif -#endif +// *********************************************************************** +// * These limits have direct impact on the settings files +// * Do not change them! +// * Else settings files will no longer be compatible with official builds +// *********************************************************************** #if defined(ESP8266) #define TASKS_MAX 12 // max 12! @@ -23,25 +19,68 @@ #define CONTROLLER_MAX 3 // max 4! #define NOTIFICATION_MAX 3 // max 4! #define VARS_PER_TASK 4 -#define PLUGIN_MAX DEVICES_MAX #define PLUGIN_CONFIGVAR_MAX 8 #define PLUGIN_CONFIGFLOATVAR_MAX 4 #define PLUGIN_CONFIGLONGVAR_MAX 4 #define PLUGIN_EXTRACONFIGVAR_MAX 16 +#define NAME_FORMULA_LENGTH_MAX 40 + + + +// *********************************************************************** +// * The next limits affect memory usage +// *********************************************************************** + +#if defined(PLUGIN_BUILD_TESTING) || defined(PLUGIN_BUILD_DEV) + #define DEVICES_MAX 95 +#else + #ifdef ESP32 + #define DEVICES_MAX 85 + #else + #define DEVICES_MAX 60 + #endif +#endif + +#define PLUGIN_MAX DEVICES_MAX #define CPLUGIN_MAX 20 #define NPLUGIN_MAX 4 + #define UNIT_MAX 254 // unit 255 = broadcast +#define CUSTOM_VARS_MAX 16 + + + +// *********************************************************************** +// * Limits regarding Rules +// *********************************************************************** + #define RULES_TIMER_MAX 8 //#define PINSTATE_TABLE_MAX 32 #define RULES_MAX_SIZE 2048 #define RULES_MAX_NESTING_LEVEL 3 #define RULESETS_MAX 4 #define RULES_BUFFER_SIZE 64 -#define NAME_FORMULA_LENGTH_MAX 40 -#define RULES_IF_MAX_NESTING_LEVEL 4 -#define CUSTOM_VARS_MAX 16 +#define RULES_IF_MAX_NESTING_LEVEL 4 + + +#define INPUT_COMMAND_SIZE 240 // Affects maximum command length in rules and other commands +// FIXME TD-er: INPUT_COMMAND_SIZE is also used in commands where simply a check for valid parameter is needed +// and some may need less memory. (which is stack allocated) + + +// *********************************************************************** +// * Other operational limits +// *********************************************************************** + +#define MAX_FLASHWRITES_PER_DAY 100 // per 24 hour window #define UDP_PACKETSIZE_MAX 2048 +#define TIMER_GRATUITOUS_ARP_MAX 5000 + +#define UNIT_NUMBER_MAX 9999 // Stored in Settings.Unit +#define DOMOTICZ_MAX_IDX 999999999 // Looks like it is an unsigned int, so could be up to 4 bln. + + diff --git a/src/DataStructs/ExtraTaskSettingsStruct.cpp b/src/DataStructs/ExtraTaskSettingsStruct.cpp new file mode 100644 index 000000000..a18af8a47 --- /dev/null +++ b/src/DataStructs/ExtraTaskSettingsStruct.cpp @@ -0,0 +1,73 @@ +#include "DataStructs/ExtraTaskSettingsStruct.h" + +#include "ESPEasy_common.h" + + ExtraTaskSettingsStruct::ExtraTaskSettingsStruct() : TaskIndex(TASKS_MAX) { + clear(); + } + + void ExtraTaskSettingsStruct::clear() { + TaskIndex = TASKS_MAX; + ZERO_FILL(TaskDeviceName); + for (byte i = 0; i < VARS_PER_TASK; ++i) { + TaskDeviceValueDecimals[i] = 2; + ZERO_FILL(TaskDeviceFormula[i]); + ZERO_FILL(TaskDeviceValueNames[i]); + } + for (byte i = 0; i < PLUGIN_EXTRACONFIGVAR_MAX; ++i) { + TaskDevicePluginConfigLong[i] = 0; + TaskDevicePluginConfig[i] = 0; + } + } + + void ExtraTaskSettingsStruct::validate() { + ZERO_TERMINATE(TaskDeviceName); + for (byte i = 0; i < VARS_PER_TASK; ++i) { + ZERO_TERMINATE(TaskDeviceFormula[i]); + ZERO_TERMINATE(TaskDeviceValueNames[i]); + } + } + + bool ExtraTaskSettingsStruct::checkUniqueValueNames() const { + for (int i = 0; i < (VARS_PER_TASK - 1); ++i) { + for (int j = i; j < VARS_PER_TASK; ++j) { + if (i != j && TaskDeviceValueNames[i][0] != 0) { + if (strcasecmp(TaskDeviceValueNames[i], TaskDeviceValueNames[j]) == 0) + return false; + } + } + } + return true; + } + + void ExtraTaskSettingsStruct::clearUnusedValueNames(byte usedVars) { + for (byte i = usedVars; i < VARS_PER_TASK; ++i) { + TaskDeviceValueDecimals[i] = 2; + ZERO_FILL(TaskDeviceFormula[i]); + ZERO_FILL(TaskDeviceValueNames[i]); + } + } + + bool ExtraTaskSettingsStruct::checkInvalidCharInNames(const char* name) const { + int pos = 0; + while (*(name+pos) != 0) { + switch (*(name+pos)) { + case ',': + case ' ': + case '#': + case '[': + case ']': + return false; + } + ++pos; + } + return true; + } + + bool ExtraTaskSettingsStruct::checkInvalidCharInNames() const { + if (!checkInvalidCharInNames(&TaskDeviceName[0])) return false; + for (int i = 0; i < (VARS_PER_TASK - 1); ++i) { + if (!checkInvalidCharInNames(&TaskDeviceValueNames[i][0])) return false; + } + return true; + } diff --git a/src/DataStructs/ExtraTaskSettingsStruct.h b/src/DataStructs/ExtraTaskSettingsStruct.h new file mode 100644 index 000000000..a28eb2d3b --- /dev/null +++ b/src/DataStructs/ExtraTaskSettingsStruct.h @@ -0,0 +1,42 @@ +#ifndef DATASTRUCTS_EXTRATASKSETTINGSSTRUCT_H +#define DATASTRUCTS_EXTRATASKSETTINGSSTRUCT_H + +/*********************************************************************************************\ + * ExtraTaskSettingsStruct +\*********************************************************************************************/ + +#include + +#include "DataStructs/ESPEasyLimits.h" + +// This is only used by some plugins to store extra settings like formula descriptions. +// These settings can only be active for one plugin, meaning they have to be loaded +// over and over again from flash when another active plugin uses these values. +//FIXME @TD-er: Should think of another mechanism to make this more efficient. +struct ExtraTaskSettingsStruct +{ + ExtraTaskSettingsStruct(); + + void clear(); + + void validate(); + + bool checkUniqueValueNames() const; + + void clearUnusedValueNames(byte usedVars); + + bool checkInvalidCharInNames(const char* name) const; + + bool checkInvalidCharInNames() const; + + byte TaskIndex; // Always < TASKS_MAX + char TaskDeviceName[NAME_FORMULA_LENGTH_MAX + 1]; + char TaskDeviceFormula[VARS_PER_TASK][NAME_FORMULA_LENGTH_MAX + 1]; + char TaskDeviceValueNames[VARS_PER_TASK][NAME_FORMULA_LENGTH_MAX + 1]; + long TaskDevicePluginConfigLong[PLUGIN_EXTRACONFIGVAR_MAX]; + byte TaskDeviceValueDecimals[VARS_PER_TASK]; + int16_t TaskDevicePluginConfig[PLUGIN_EXTRACONFIGVAR_MAX]; +}; + + +#endif // DATASTRUCTS_EXTRATASKSETTINGSSTRUCT_H \ No newline at end of file diff --git a/src/DataStructs/LogStruct.cpp b/src/DataStructs/LogStruct.cpp new file mode 100644 index 000000000..2068f9978 --- /dev/null +++ b/src/DataStructs/LogStruct.cpp @@ -0,0 +1,102 @@ +#include "DataStructs/LogStruct.h" +#include "ESPEasy_fdwdecl.h" + + + LogStruct::LogStruct() : write_idx(0), read_idx(0), lastReadTimeStamp(0) { + for (int i = 0; i < LOG_STRUCT_MESSAGE_LINES; ++i) { + timeStamp[i] = 0; + log_level[i] = 0; + } + } + + void LogStruct::add(const byte loglevel, const char *line) { + write_idx = (write_idx + 1) % LOG_STRUCT_MESSAGE_LINES; + if (write_idx == read_idx) { + // Buffer full, move read_idx to overwrite oldest entry. + read_idx = (read_idx + 1) % LOG_STRUCT_MESSAGE_LINES; + } + timeStamp[write_idx] = millis(); + log_level[write_idx] = loglevel; + unsigned linelength = strlen(line); + if (linelength > LOG_STRUCT_MESSAGE_SIZE-1) + linelength = LOG_STRUCT_MESSAGE_SIZE-1; + Message[write_idx] = ""; + Message[write_idx].reserve(linelength); + for (unsigned i = 0; i < linelength; ++i) { + Message[write_idx] += *(line + i); + } + } + + // Read the next item and append it to the given string. + // Returns whether new lines are available. + bool LogStruct::get(String& output, const String& lineEnd) { + lastReadTimeStamp = millis(); + if (!isEmpty()) { + read_idx = (read_idx + 1) % LOG_STRUCT_MESSAGE_LINES; + output += formatLine(read_idx, lineEnd); + } + return !isEmpty(); + } + + String LogStruct::get_logjson_formatted(bool& logLinesAvailable, unsigned long& timestamp) { + lastReadTimeStamp = millis(); + logLinesAvailable = false; + if (isEmpty()) { + return ""; + } + read_idx = (read_idx + 1) % LOG_STRUCT_MESSAGE_LINES; + timestamp = timeStamp[read_idx]; + String output = logjson_formatLine(read_idx); + if (isEmpty()) return output; + output += ",\n"; + logLinesAvailable = true; + return output; + } + + bool LogStruct::isEmpty() { + return (write_idx == read_idx); + } + + bool LogStruct::logActiveRead() { + clearExpiredEntries(); + return timePassedSince(lastReadTimeStamp) < LOG_BUFFER_EXPIRE; + } + + String LogStruct::formatLine(int index, const String& lineEnd) { + String output; + output += timeStamp[index]; + output += " : "; + output += Message[index]; + output += lineEnd; + return output; + } + + String LogStruct::logjson_formatLine(int index) { + String output; + output.reserve(LOG_STRUCT_MESSAGE_SIZE + 64); + output = "{"; + output += to_json_object_value("timestamp", String(timeStamp[index])); + output += ",\n"; + output += to_json_object_value("text", Message[index]); + output += ",\n"; + output += to_json_object_value("level", String(log_level[index])); + output += "}"; + return output; + } + + void LogStruct::clearExpiredEntries() { + if (isEmpty()) { + return; + } + if (timePassedSince(lastReadTimeStamp) > LOG_BUFFER_EXPIRE) { + // Clear the entire log. + // If web log is the only log active, it will not be checked again until it is read. + for (read_idx = 0; read_idx < LOG_STRUCT_MESSAGE_LINES; ++read_idx) { + Message[read_idx] = String(); // Free also the reserved memory. + timeStamp[read_idx] = 0; + log_level[read_idx] = 0; + } + read_idx = 0; + write_idx = 0; + } + } diff --git a/src/DataStructs/LogStruct.h b/src/DataStructs/LogStruct.h new file mode 100644 index 000000000..6ba8cd36e --- /dev/null +++ b/src/DataStructs/LogStruct.h @@ -0,0 +1,55 @@ +#ifndef DATASTRUCTS_LOGSTRUCT_H +#define DATASTRUCTS_LOGSTRUCT_H + +#include + +/*********************************************************************************************\ + * LogStruct +\*********************************************************************************************/ +#define LOG_STRUCT_MESSAGE_SIZE 128 +#ifdef ESP32 + #define LOG_STRUCT_MESSAGE_LINES 30 + #define LOG_BUFFER_EXPIRE 30000 // Time after which a buffered log item is considered expired. +#else + #if defined(PLUGIN_BUILD_TESTING) || defined(PLUGIN_BUILD_DEV) + #define LOG_STRUCT_MESSAGE_LINES 10 + #else + #define LOG_STRUCT_MESSAGE_LINES 15 + #endif + #define LOG_BUFFER_EXPIRE 5000 // Time after which a buffered log item is considered expired. +#endif + +struct LogStruct { + LogStruct(); + + void add(const byte loglevel, const char *line); + + // Read the next item and append it to the given string. + // Returns whether new lines are available. + bool get(String& output, const String& lineEnd); + + String get_logjson_formatted(bool& logLinesAvailable, unsigned long& timestamp); + + bool isEmpty(); + + bool logActiveRead(); + + private: + String formatLine(int index, const String& lineEnd); + + String logjson_formatLine(int index); + + void clearExpiredEntries(); + + String Message[LOG_STRUCT_MESSAGE_LINES]; + unsigned long timeStamp[LOG_STRUCT_MESSAGE_LINES]; + int write_idx; + int read_idx; + unsigned long lastReadTimeStamp; + byte log_level[LOG_STRUCT_MESSAGE_LINES]; + +}; + + + +#endif // DATASTRUCTS_LOGSTRUCT_H \ No newline at end of file diff --git a/src/DataStructs/NodeStruct.h b/src/DataStructs/NodeStruct.h new file mode 100644 index 000000000..67b4d0654 --- /dev/null +++ b/src/DataStructs/NodeStruct.h @@ -0,0 +1,29 @@ +#ifndef DATASTRUCTS_NODESTRUCT_H +#define DATASTRUCTS_NODESTRUCT_H + +#include "ESPEasy_common.h" +#include +#include + +/*********************************************************************************************\ +* NodeStruct +\*********************************************************************************************/ +struct NodeStruct +{ + NodeStruct() : + build(0), age(0), nodeType(0) + { + for (byte i = 0; i < 4; ++i) { ip[i] = 0; } + } + + String nodeName; + IPAddress ip; + uint16_t build; + byte age; + byte nodeType; +}; +typedef std::map NodesMap; +NodesMap Nodes; + + +#endif // DATASTRUCTS_NODESTRUCT_H diff --git a/src/DataStructs/NotificationSettingsStruct.cpp b/src/DataStructs/NotificationSettingsStruct.cpp new file mode 100644 index 000000000..31ee2821a --- /dev/null +++ b/src/DataStructs/NotificationSettingsStruct.cpp @@ -0,0 +1,26 @@ +#include "DataStructs/NotificationSettingsStruct.h" + +#include "ESPEasy_common.h" + + +NotificationSettingsStruct::NotificationSettingsStruct() : Port(0), Pin1(0), Pin2(0) { + ZERO_FILL(Server); + ZERO_FILL(Domain); + ZERO_FILL(Sender); + ZERO_FILL(Receiver); + ZERO_FILL(Subject); + ZERO_FILL(Body); + ZERO_FILL(User); + ZERO_FILL(Pass); + } + + void NotificationSettingsStruct::validate() { + ZERO_TERMINATE(Server); + ZERO_TERMINATE(Domain); + ZERO_TERMINATE(Sender); + ZERO_TERMINATE(Receiver); + ZERO_TERMINATE(Subject); + ZERO_TERMINATE(Body); + ZERO_TERMINATE(User); + ZERO_TERMINATE(Pass); + } \ No newline at end of file diff --git a/src/DataStructs/NotificationSettingsStruct.h b/src/DataStructs/NotificationSettingsStruct.h new file mode 100644 index 000000000..afc9090a2 --- /dev/null +++ b/src/DataStructs/NotificationSettingsStruct.h @@ -0,0 +1,34 @@ +#ifndef DATASTRUCTS_NOTIFICATIONSETTINGSSTRUCT_H +#define DATASTRUCTS_NOTIFICATIONSETTINGSSTRUCT_H + +#include +#include // For std::shared_ptr + +/*********************************************************************************************\ + * NotificationSettingsStruct +\*********************************************************************************************/ +struct NotificationSettingsStruct +{ + NotificationSettingsStruct(); + + void validate(); + + char Server[65]; + unsigned int Port; + char Domain[65]; + char Sender[65]; + char Receiver[65]; + char Subject[129]; + char Body[513]; + byte Pin1; + byte Pin2; + char User[49]; + char Pass[33]; + //its safe to extend this struct, up to 4096 bytes, default values in config are 0 +}; + +typedef std::shared_ptr NotificationSettingsStruct_ptr_type; +#define MakeNotificationSettings(T) NotificationSettingsStruct_ptr_type NotificationSettingsStruct_ptr(new NotificationSettingsStruct());\ + NotificationSettingsStruct& T = *NotificationSettingsStruct_ptr; + +#endif // DATASTRUCTS_NOTIFICATIONSETTINGSSTRUCT_H \ No newline at end of file diff --git a/src/DataStructs/NotificationStruct.h b/src/DataStructs/NotificationStruct.h new file mode 100644 index 000000000..59894b1e0 --- /dev/null +++ b/src/DataStructs/NotificationStruct.h @@ -0,0 +1,19 @@ +#ifndef DATASTRUCTS_NOTIFICATIONSTRUCT_H +#define DATASTRUCTS_NOTIFICATIONSTRUCT_H + + +/*********************************************************************************************\ +* NotificationStruct +\*********************************************************************************************/ +struct NotificationStruct +{ + NotificationStruct() : + Number(0), usesMessaging(false), usesGPIO(0) {} + + byte Number; + boolean usesMessaging; + byte usesGPIO; +}; + + +#endif // DATASTRUCTS_NOTIFICATIONSTRUCT_H diff --git a/src/DataStructs/PortStatusStruct.h b/src/DataStructs/PortStatusStruct.h new file mode 100644 index 000000000..cc2f42e91 --- /dev/null +++ b/src/DataStructs/PortStatusStruct.h @@ -0,0 +1,30 @@ +#ifndef DATASTRUCTS_PORTSTATUSSTRUCT_H +#define DATASTRUCTS_PORTSTATUSSTRUCT_H + +#include "ESPEasy_common.h" +#include + +struct portStatusStruct { + portStatusStruct() : state(-1), output(-1), command(0), init(0), mode(0), task(0), monitor(0), forceMonitor(0), forceEvent(0), previousTask( + -1), x(-1) {} + + int8_t state : 2; // -1,0,1 + int8_t output : 2; // -1,0,1 + int8_t command : 2; // 0,1 + int8_t init : 2; // 0,1 + + uint8_t mode : 3; // 6 current values (max. 8) + uint8_t task : 2; // 0-3 (max. 4) + uint8_t monitor : 1; // 0,1 + uint8_t forceMonitor : 1; // 0,1 + uint8_t forceEvent : 1; // 0,1 + + int8_t previousTask : 8; + + int8_t x; // used to synchronize the Plugin_prt vector index (x) with the PLUGIN_ID +}; + +std::map globalMapPortStatus; + + +#endif // DATASTRUCTS_PORTSTATUSSTRUCT_H diff --git a/src/DataStructs/ProtocolStruct.h b/src/DataStructs/ProtocolStruct.h new file mode 100644 index 000000000..5eb5c4278 --- /dev/null +++ b/src/DataStructs/ProtocolStruct.h @@ -0,0 +1,35 @@ +#ifndef DATASTRUCTS_PROTOCOLSTRUCT_H +#define DATASTRUCTS_PROTOCOLSTRUCT_H + +#include "ESPEasy_common.h" +#include + +/*********************************************************************************************\ +* ProtocolStruct +\*********************************************************************************************/ +struct ProtocolStruct +{ + ProtocolStruct() : + defaultPort(0), Number(0), usesMQTT(false), usesAccount(false), usesPassword(false), + usesTemplate(false), usesID(false), Custom(false), usesHost(true), usesPort(true), + usesQueue(true), usesSampleSets(false) {} + + uint16_t defaultPort; + byte Number; + bool usesMQTT : 1; + bool usesAccount : 1; + bool usesPassword : 1; + bool usesTemplate : 1; // When set, the protocol will pre-load some templates like default MQTT topics + bool usesID : 1; // Whether a controller supports sending an IDX value sent along with plugin data + bool Custom : 1; // When set, the controller has to define all parameters on the controller setup page + bool usesHost : 1; + bool usesPort : 1; + bool usesQueue : 1; + bool usesSampleSets : 1; +}; + +typedef std::vector ProtocolVector; +ProtocolVector Protocol; + + +#endif // DATASTRUCTS_PROTOCOLSTRUCT_H diff --git a/src/DataStructs/RTCStruct.h b/src/DataStructs/RTCStruct.h new file mode 100644 index 000000000..eff8bee44 --- /dev/null +++ b/src/DataStructs/RTCStruct.h @@ -0,0 +1,36 @@ +#ifndef DATASTRUCTS_RTC_STRUCTS_H +#define DATASTRUCTS_RTC_STRUCTS_H + +#include "ESPEasy_common.h" + +// this offsets are in blocks, bytes = blocks * 4 +#define RTC_BASE_STRUCT 64 +#define RTC_BASE_USERVAR 74 +#define RTC_BASE_CACHE 124 + +#define RTC_CACHE_DATA_SIZE 240 +#define CACHE_FILE_MAX_SIZE 24000 + +/*********************************************************************************************\ + * RTCStruct +\*********************************************************************************************/ +//max 40 bytes: ( 74 - 64 ) * 4 +struct RTCStruct +{ + RTCStruct() : ID1(0), ID2(0), unused1(false), factoryResetCounter(0), + deepSleepState(0), bootFailedCount(0), flashDayCounter(0), + flashCounter(0), bootCounter(0), lastMixedSchedulerId(0) {} + byte ID1; + byte ID2; + boolean unused1; + byte factoryResetCounter; + byte deepSleepState; + byte bootFailedCount; + byte flashDayCounter; + unsigned long flashCounter; + unsigned long bootCounter; + unsigned long lastMixedSchedulerId; +}; + + +#endif // DATASTRUCTS_RTC_STRUCTS_H diff --git a/src/DataStructs/SecurityStruct.cpp b/src/DataStructs/SecurityStruct.cpp new file mode 100644 index 000000000..9c35cbcbc --- /dev/null +++ b/src/DataStructs/SecurityStruct.cpp @@ -0,0 +1,32 @@ +#include "DataStructs/SecurityStruct.h" + +#include "ESPEasy_common.h" +#include "DataStructs/ESPEasyLimits.h" + +SecurityStruct::SecurityStruct() { + ZERO_FILL(WifiSSID); + ZERO_FILL(WifiKey); + ZERO_FILL(WifiSSID2); + ZERO_FILL(WifiKey2); + ZERO_FILL(WifiAPKey); + + for (byte i = 0; i < CONTROLLER_MAX; ++i) { + ZERO_FILL(ControllerUser[i]); + ZERO_FILL(ControllerPassword[i]); + } + ZERO_FILL(Password); +} + +void SecurityStruct::validate() { + ZERO_TERMINATE(WifiSSID); + ZERO_TERMINATE(WifiKey); + ZERO_TERMINATE(WifiSSID2); + ZERO_TERMINATE(WifiKey2); + ZERO_TERMINATE(WifiAPKey); + + for (byte i = 0; i < CONTROLLER_MAX; ++i) { + ZERO_TERMINATE(ControllerUser[i]); + ZERO_TERMINATE(ControllerPassword[i]); + } + ZERO_TERMINATE(Password); +} diff --git a/src/DataStructs/SecurityStruct.h b/src/DataStructs/SecurityStruct.h index 09a7eaec2..ab374a94f 100644 --- a/src/DataStructs/SecurityStruct.h +++ b/src/DataStructs/SecurityStruct.h @@ -1,37 +1,17 @@ #ifndef DATASTRUCTS_SECURITYSTRUCT_H #define DATASTRUCTS_SECURITYSTRUCT_H +#include "ESPEasy_common.h" +#include "DataStructs/ESPEasyLimits.h" /*********************************************************************************************\ * SecurityStruct \*********************************************************************************************/ struct SecurityStruct { - SecurityStruct() { - ZERO_FILL(WifiSSID); - ZERO_FILL(WifiKey); - ZERO_FILL(WifiSSID2); - ZERO_FILL(WifiKey2); - ZERO_FILL(WifiAPKey); - for (byte i = 0; i < CONTROLLER_MAX; ++i) { - ZERO_FILL(ControllerUser[i]); - ZERO_FILL(ControllerPassword[i]); - } - ZERO_FILL(Password); - } + SecurityStruct(); - void validate() { - ZERO_TERMINATE(WifiSSID); - ZERO_TERMINATE(WifiKey); - ZERO_TERMINATE(WifiSSID2); - ZERO_TERMINATE(WifiKey2); - ZERO_TERMINATE(WifiAPKey); - for (byte i = 0; i < CONTROLLER_MAX; ++i) { - ZERO_TERMINATE(ControllerUser[i]); - ZERO_TERMINATE(ControllerPassword[i]); - } - ZERO_TERMINATE(Password); - } + void validate(); char WifiSSID[32]; char WifiKey[64]; @@ -50,4 +30,5 @@ struct SecurityStruct uint8_t md5[16] = {0}; }; + #endif // DATASTRUCTS_SECURITYSTRUCT_H \ No newline at end of file diff --git a/src/DataStructs/SettingsStruct.h b/src/DataStructs/SettingsStruct.h index 0b31d023a..5e7900c17 100644 --- a/src/DataStructs/SettingsStruct.h +++ b/src/DataStructs/SettingsStruct.h @@ -160,4 +160,6 @@ SettingsStruct* SettingsStruct_ptr = new SettingsStruct; SettingsStruct& Settings = *SettingsStruct_ptr; */ + + #endif // DATASTRUCTS_SETTINGSSTRUCT_H \ No newline at end of file diff --git a/src/DataStructs/SystemTimerStruct.h b/src/DataStructs/SystemTimerStruct.h new file mode 100644 index 000000000..aff79e34c --- /dev/null +++ b/src/DataStructs/SystemTimerStruct.h @@ -0,0 +1,28 @@ +#ifndef DATASTRUCTS_SYSTEMTIMERSTRUCT_H +#define DATASTRUCTS_SYSTEMTIMERSTRUCT_H + +#include "ESPEasy_common.h" +#include + +/*********************************************************************************************\ + * systemTimerStruct +\*********************************************************************************************/ +struct systemTimerStruct +{ + systemTimerStruct() : + timer(0), Par1(0), Par2(0), Par3(0), Par4(0), Par5(0), TaskIndex(-1), plugin(0) {} + + unsigned long timer; + int Par1; + int Par2; + int Par3; + int Par4; + int Par5; + int16_t TaskIndex; + byte plugin; +}; +std::map systemTimers; + + + +#endif // DATASTRUCTS_SYSTEMTIMERSTRUCT_H \ No newline at end of file diff --git a/src/DataStructs/TimingStats.cpp b/src/DataStructs/TimingStats.cpp new file mode 100644 index 000000000..4dd054c33 --- /dev/null +++ b/src/DataStructs/TimingStats.cpp @@ -0,0 +1,213 @@ +#include "DataStructs/TimingStats.h" +#include "ESPEasy_common.h" +#include "ESPEasy_plugindefs.h" +#include "_CPlugin_Helper.h" + +TimingStats::TimingStats() : _timeTotal(0.0), _count(0), _maxVal(0), _minVal(4294967295) {} + +void TimingStats::add(unsigned long time) { + _timeTotal += time; + ++_count; + + if (time > _maxVal) { _maxVal = time; } + + if (time < _minVal) { _minVal = time; } +} + +void TimingStats::reset() { + _timeTotal = 0.0; + _count = 0; + _maxVal = 0; + _minVal = 4294967295; +} + +bool TimingStats::isEmpty() const { + return _count == 0; +} + +float TimingStats::getAvg() const { + if (_count == 0) { return 0.0; } + return _timeTotal / _count; +} + +unsigned int TimingStats::getMinMax(unsigned long& minVal, unsigned long& maxVal) const { + if (_count == 0) { + minVal = 0; + maxVal = 0; + return 0; + } + minVal = _minVal; + maxVal = _maxVal; + return _count; +} + +bool TimingStats::thresholdExceeded(unsigned long threshold) const { + if (_count == 0) { + return false; + } + return _maxVal > threshold; +} + +/********************************************************************************************\ + Functions used for displaying timing stats + \*********************************************************************************************/ +String getPluginFunctionName(int function) { + switch (function) { + case PLUGIN_INIT_ALL: return F("INIT_ALL"); + case PLUGIN_INIT: return F("INIT"); + case PLUGIN_READ: return F("READ"); + case PLUGIN_ONCE_A_SECOND: return F("ONCE_A_SECOND"); + case PLUGIN_TEN_PER_SECOND: return F("TEN_PER_SECOND"); + case PLUGIN_DEVICE_ADD: return F("DEVICE_ADD"); + case PLUGIN_EVENTLIST_ADD: return F("EVENTLIST_ADD"); + case PLUGIN_WEBFORM_SAVE: return F("WEBFORM_SAVE"); + case PLUGIN_WEBFORM_LOAD: return F("WEBFORM_LOAD"); + case PLUGIN_WEBFORM_SHOW_VALUES: return F("WEBFORM_SHOW_VALUES"); + case PLUGIN_GET_DEVICENAME: return F("GET_DEVICENAME"); + case PLUGIN_GET_DEVICEVALUENAMES: return F("GET_DEVICEVALUENAMES"); + case PLUGIN_WRITE: return F("WRITE"); + case PLUGIN_EVENT_OUT: return F("EVENT_OUT"); + case PLUGIN_WEBFORM_SHOW_CONFIG: return F("WEBFORM_SHOW_CONFIG"); + case PLUGIN_SERIAL_IN: return F("SERIAL_IN"); + case PLUGIN_UDP_IN: return F("UDP_IN"); + case PLUGIN_CLOCK_IN: return F("CLOCK_IN"); + case PLUGIN_TIMER_IN: return F("TIMER_IN"); + case PLUGIN_FIFTY_PER_SECOND: return F("FIFTY_PER_SECOND"); + case PLUGIN_SET_CONFIG: return F("SET_CONFIG"); + case PLUGIN_GET_DEVICEGPIONAMES: return F("GET_DEVICEGPIONAMES"); + case PLUGIN_EXIT: return F("EXIT"); + case PLUGIN_GET_CONFIG: return F("GET_CONFIG"); + case PLUGIN_UNCONDITIONAL_POLL: return F("UNCONDITIONAL_POLL"); + case PLUGIN_REQUEST: return F("REQUEST"); + } + return getUnknownString(); +} + +bool mustLogFunction(int function) { + switch (function) { + case PLUGIN_INIT_ALL: return false; + case PLUGIN_INIT: return false; + case PLUGIN_READ: return true; + case PLUGIN_ONCE_A_SECOND: return true; + case PLUGIN_TEN_PER_SECOND: return true; + case PLUGIN_DEVICE_ADD: return false; + case PLUGIN_EVENTLIST_ADD: return false; + case PLUGIN_WEBFORM_SAVE: return false; + case PLUGIN_WEBFORM_LOAD: return false; + case PLUGIN_WEBFORM_SHOW_VALUES: return false; + case PLUGIN_GET_DEVICENAME: return false; + case PLUGIN_GET_DEVICEVALUENAMES: return false; + case PLUGIN_WRITE: return true; + case PLUGIN_EVENT_OUT: return true; + case PLUGIN_WEBFORM_SHOW_CONFIG: return false; + case PLUGIN_SERIAL_IN: return true; + case PLUGIN_UDP_IN: return true; + case PLUGIN_CLOCK_IN: return false; + case PLUGIN_TIMER_IN: return true; + case PLUGIN_FIFTY_PER_SECOND: return true; + case PLUGIN_SET_CONFIG: return false; + case PLUGIN_GET_DEVICEGPIONAMES: return false; + case PLUGIN_EXIT: return false; + case PLUGIN_GET_CONFIG: return false; + case PLUGIN_UNCONDITIONAL_POLL: return false; + case PLUGIN_REQUEST: return true; + } + return false; +} + +String getCPluginCFunctionName(int function) { + switch (function) { + case CPLUGIN_PROTOCOL_ADD: return F("CPLUGIN_PROTOCOL_ADD"); + case CPLUGIN_PROTOCOL_TEMPLATE: return F("CPLUGIN_PROTOCOL_TEMPLATE"); + case CPLUGIN_PROTOCOL_SEND: return F("CPLUGIN_PROTOCOL_SEND"); + case CPLUGIN_PROTOCOL_RECV: return F("CPLUGIN_PROTOCOL_RECV"); + case CPLUGIN_GET_DEVICENAME: return F("CPLUGIN_GET_DEVICENAME"); + case CPLUGIN_WEBFORM_SAVE: return F("CPLUGIN_WEBFORM_SAVE"); + case CPLUGIN_WEBFORM_LOAD: return F("CPLUGIN_WEBFORM_LOAD"); + case CPLUGIN_GET_PROTOCOL_DISPLAY_NAME: return F("CPLUGIN_GET_PROTOCOL_DISPLAY_NAME"); + case CPLUGIN_TASK_CHANGE_NOTIFICATION: return F("CPLUGIN_TASK_CHANGE_NOTIFICATION"); + case CPLUGIN_INIT: return F("CPLUGIN_INIT"); + case CPLUGIN_UDP_IN: return F("CPLUGIN_UDP_IN"); + } + return getUnknownString(); +} + +bool mustLogCFunction(int function) { + switch (function) { + case CPLUGIN_PROTOCOL_ADD: return false; + case CPLUGIN_PROTOCOL_TEMPLATE: return false; + case CPLUGIN_PROTOCOL_SEND: return true; + case CPLUGIN_PROTOCOL_RECV: return true; + case CPLUGIN_GET_DEVICENAME: return false; + case CPLUGIN_WEBFORM_SAVE: return false; + case CPLUGIN_WEBFORM_LOAD: return false; + case CPLUGIN_GET_PROTOCOL_DISPLAY_NAME: return false; + case CPLUGIN_TASK_CHANGE_NOTIFICATION: return false; + case CPLUGIN_INIT: return false; + case CPLUGIN_UDP_IN: return true; + } + return false; +} + +String getMiscStatsName(int stat) { + switch (stat) { + case LOADFILE_STATS: return F("Load File"); + case SAVEFILE_STATS: return F("Save File"); + case LOOP_STATS: return F("Loop"); + case PLUGIN_CALL_50PS: return F("Plugin call 50 p/s"); + case PLUGIN_CALL_10PS: return F("Plugin call 10 p/s"); + case PLUGIN_CALL_10PSU: return F("Plugin call 10 p/s U"); + case PLUGIN_CALL_1PS: return F("Plugin call 1 p/s"); + case SENSOR_SEND_TASK: return F("SensorSendTask()"); + case SEND_DATA_STATS: return F("sendData()"); + case COMPUTE_FORMULA_STATS: return F("Compute formula"); + case PROC_SYS_TIMER: return F("proc_system_timer()"); + case SET_NEW_TIMER: return F("setNewTimerAt()"); + case TIME_DIFF_COMPUTE: return F("timeDiff()"); + case MQTT_DELAY_QUEUE: return F("Delay queue MQTT"); + case TRY_CONNECT_HOST_TCP: return F("try_connect_host() (TCP)"); + case TRY_CONNECT_HOST_UDP: return F("try_connect_host() (UDP)"); + case HOST_BY_NAME_STATS: return F("hostByName()"); + case CONNECT_CLIENT_STATS: return F("connectClient()"); + case LOAD_CUSTOM_TASK_STATS: return F("LoadCustomTaskSettings()"); + case WIFI_ISCONNECTED_STATS: return F("WiFi.isConnected()"); + case WIFI_NOTCONNECTED_STATS: return F("WiFi.isConnected() (fail)"); + case LOAD_TASK_SETTINGS: return F("LoadTaskSettings()"); + case TRY_OPEN_FILE: return F("TryOpenFile()"); + case SPIFFS_GC_SUCCESS: return F("SPIFFS GC success"); + case SPIFFS_GC_FAIL: return F("SPIFFS GC fail"); + case RULES_PROCESSING: return F("rulesProcessing()"); + case GRAT_ARP_STATS: return F("sendGratuitousARP()"); + case BACKGROUND_TASKS: return F("backgroundtasks()"); + case HANDLE_SCHEDULER_IDLE: return F("handle_schedule() idle"); + case HANDLE_SCHEDULER_TASK: return F("handle_schedule() task"); + case C001_DELAY_QUEUE: + case C002_DELAY_QUEUE: + case C003_DELAY_QUEUE: + case C004_DELAY_QUEUE: + case C005_DELAY_QUEUE: + case C006_DELAY_QUEUE: + case C007_DELAY_QUEUE: + case C008_DELAY_QUEUE: + case C009_DELAY_QUEUE: + case C010_DELAY_QUEUE: + case C011_DELAY_QUEUE: + case C012_DELAY_QUEUE: + case C013_DELAY_QUEUE: + case C014_DELAY_QUEUE: + case C015_DELAY_QUEUE: + case C016_DELAY_QUEUE: + case C017_DELAY_QUEUE: + case C018_DELAY_QUEUE: + case C019_DELAY_QUEUE: + case C020_DELAY_QUEUE: + { + String result; + result.reserve(16); + result = F("Delay queue "); + result += get_formatted_Controller_number(static_cast(stat - C001_DELAY_QUEUE + 1)); + return result; + } + } + return getUnknownString(); +} diff --git a/src/DataStructs/TimingStats.h b/src/DataStructs/TimingStats.h new file mode 100644 index 000000000..59cc67ba7 --- /dev/null +++ b/src/DataStructs/TimingStats.h @@ -0,0 +1,93 @@ +#ifndef DATASTRUCTS_TIMINGSTATS_H +#define DATASTRUCTS_TIMINGSTATS_H + +#include +#include + + +/*********************************************************************************************\ +* TimingStats +\*********************************************************************************************/ + + +#define LOADFILE_STATS 0 +#define SAVEFILE_STATS 1 +#define LOOP_STATS 2 +#define PLUGIN_CALL_50PS 3 +#define PLUGIN_CALL_10PS 4 +#define PLUGIN_CALL_10PSU 5 +#define PLUGIN_CALL_1PS 6 +#define SENSOR_SEND_TASK 7 +#define SEND_DATA_STATS 8 +#define COMPUTE_FORMULA_STATS 9 +#define PROC_SYS_TIMER 10 +#define SET_NEW_TIMER 11 +#define TIME_DIFF_COMPUTE 12 +#define MQTT_DELAY_QUEUE 13 +#define C001_DELAY_QUEUE 14 +#define C002_DELAY_QUEUE 15 +#define C003_DELAY_QUEUE 16 +#define C004_DELAY_QUEUE 17 +#define C005_DELAY_QUEUE 18 +#define C006_DELAY_QUEUE 19 +#define C007_DELAY_QUEUE 20 +#define C008_DELAY_QUEUE 21 +#define C009_DELAY_QUEUE 22 +#define C010_DELAY_QUEUE 23 +#define C011_DELAY_QUEUE 24 +#define C012_DELAY_QUEUE 25 +#define C013_DELAY_QUEUE 26 +#define C014_DELAY_QUEUE 27 +#define C015_DELAY_QUEUE 28 +#define C016_DELAY_QUEUE 29 +#define C017_DELAY_QUEUE 30 +#define C018_DELAY_QUEUE 31 +#define C019_DELAY_QUEUE 32 +#define C020_DELAY_QUEUE 33 +#define TRY_CONNECT_HOST_TCP 34 +#define TRY_CONNECT_HOST_UDP 35 +#define HOST_BY_NAME_STATS 36 +#define CONNECT_CLIENT_STATS 37 +#define LOAD_CUSTOM_TASK_STATS 38 +#define WIFI_ISCONNECTED_STATS 39 +#define WIFI_NOTCONNECTED_STATS 40 +#define LOAD_TASK_SETTINGS 41 +#define TRY_OPEN_FILE 42 +#define SPIFFS_GC_SUCCESS 43 +#define SPIFFS_GC_FAIL 44 +#define RULES_PROCESSING 45 +#define GRAT_ARP_STATS 46 +#define BACKGROUND_TASKS 47 +#define HANDLE_SCHEDULER_IDLE 48 +#define HANDLE_SCHEDULER_TASK 49 + +class TimingStats { +public: + + TimingStats(); + + void add(unsigned long time); + void reset(); + bool isEmpty() const; + float getAvg() const; + unsigned int getMinMax(unsigned long& minVal, + unsigned long& maxVal) const; + bool thresholdExceeded(unsigned long threshold) const; + +private: + + float _timeTotal; + unsigned int _count; + unsigned long _maxVal; + unsigned long _minVal; +}; + + +String getPluginFunctionName(int function); +bool mustLogFunction(int function); +String getCPluginCFunctionName(int function); +bool mustLogCFunction(int function); +String getMiscStatsName(int stat); + + +#endif // DATASTRUCTS_TIMINGSTATS_H diff --git a/src/DelayQueueElements.h b/src/DelayQueueElements.h new file mode 100644 index 000000000..8001fc6c5 --- /dev/null +++ b/src/DelayQueueElements.h @@ -0,0 +1,643 @@ +#ifndef DELAY_QUEUE_ELEMENTS_H +#define DELAY_QUEUE_ELEMENTS_H + +#include "DataStructs/ControllerSettingsStruct.h" +#include "ESPEasy_fdwdecl.h" + +// These element classes should be defined as class, to be used as template. + +/*********************************************************************************************\ +* MQTT_queue_element for all MQTT base controllers +\*********************************************************************************************/ +class MQTT_queue_element { +public: + + MQTT_queue_element() : controller_idx(0), _retained(false) {} + + MQTT_queue_element(int ctrl_idx, + const String& topic, const String& payload, boolean retained) : + controller_idx(ctrl_idx), _topic(topic), _payload(payload), _retained(retained) + {} + + size_t getSize() const { + return sizeof(this) + _topic.length() + _payload.length(); + } + + int controller_idx; + String _topic; + String _payload; + boolean _retained; +}; + +/*********************************************************************************************\ +* Simple queue element, only storing controller index and some String +\*********************************************************************************************/ +class simple_queue_element_string_only { +public: + + simple_queue_element_string_only() : controller_idx(0) {} + + simple_queue_element_string_only(int ctrl_idx, const String& req) : + controller_idx(ctrl_idx), txt(req) {} + + size_t getSize() const { + return sizeof(this) + txt.length(); + } + + int controller_idx; + String txt; +}; + + +//#ifdef USES_C001 +/*********************************************************************************************\ +* C001_queue_element for queueing requests for C001. +\*********************************************************************************************/ +#define C001_queue_element simple_queue_element_string_only +//#endif //USES_C001 + +//#ifdef USES_C003 +/*********************************************************************************************\ +* C003_queue_element for queueing requests for C003 Nodo Telnet. +\*********************************************************************************************/ +#define C003_queue_element simple_queue_element_string_only +//#endif //USES_C003 + +//#ifdef USES_C004 +/*********************************************************************************************\ +* C004_queue_element for queueing requests for C004 ThingSpeak. +* Typical use case for Thingspeak is to only send values every N seconds/minutes. +* So we just store everything needed to recreate the event when the time is ready. +\*********************************************************************************************/ +class C004_queue_element { +public: + + C004_queue_element() : controller_idx(0), TaskIndex(0), idx(0), sensorType(0) {} + + C004_queue_element(const struct EventStruct *event) : + controller_idx(event->ControllerIndex), + TaskIndex(event->TaskIndex), + idx(event->idx), + sensorType(event->sensorType) { + if (sensorType == SENSOR_TYPE_STRING) { + txt = event->String2; + } + } + + size_t getSize() const { + return sizeof(this) + txt.length(); + } + + int controller_idx; + byte TaskIndex; + int idx; + byte sensorType; + String txt; +}; +//#endif //USES_C004 + +//#ifdef USES_C007 +/*********************************************************************************************\ +* C007_queue_element for queueing requests for C007 Emoncms +\*********************************************************************************************/ +class C007_queue_element { +public: + + C007_queue_element() : controller_idx(0), TaskIndex(0), idx(0), sensorType(0) {} + + C007_queue_element(const struct EventStruct *event) : + controller_idx(event->ControllerIndex), + TaskIndex(event->TaskIndex), + idx(event->idx), + sensorType(event->sensorType) {} + + size_t getSize() const { + return sizeof(this); + } + + int controller_idx; + byte TaskIndex; + int idx; + byte sensorType; +}; +//#endif //USES_C007 + +/*********************************************************************************************\ +* Base class for controllers that only send a single value per request and thus needs to +* keep track of the number of values already sent. +\*********************************************************************************************/ +class queue_element_single_value_base { +public: + + queue_element_single_value_base() : controller_idx(0), TaskIndex(0), idx(0), valuesSent(0) {} + + queue_element_single_value_base(const struct EventStruct *event, byte value_count) : + controller_idx(event->ControllerIndex), + TaskIndex(event->TaskIndex), + idx(event->idx), + valuesSent(0), + valueCount(value_count) {} + + bool checkDone(bool succesfull) const { + if (succesfull) { ++valuesSent; } + return valuesSent >= valueCount || valuesSent >= VARS_PER_TASK; + } + + size_t getSize() const { + size_t total = sizeof(this); + + for (int i = 0; i < VARS_PER_TASK; ++i) { + total += txt[i].length(); + } + return total; + } + + String txt[VARS_PER_TASK]; + int controller_idx; + byte TaskIndex; + int idx; + mutable byte valuesSent; // Value must be set by const function checkDone() + byte valueCount; +}; + + +//#ifdef USES_C008 +/*********************************************************************************************\ +* C008_queue_element for queueing requests for 008: Generic HTTP +* Using queue_element_single_value_base +\*********************************************************************************************/ +#define C008_queue_element queue_element_single_value_base +//#endif //USES_C008 + +//#ifdef USES_C009 +/*********************************************************************************************\ +* C009_queue_element for queueing requests for C009: FHEM HTTP. +\*********************************************************************************************/ +class C009_queue_element { +public: + + C009_queue_element() : controller_idx(0), TaskIndex(0), idx(0), sensorType(0) {} + + C009_queue_element(const struct EventStruct *event) : + controller_idx(event->ControllerIndex), + TaskIndex(event->TaskIndex), + idx(event->idx), + sensorType(event->sensorType) {} + + size_t getSize() const { + size_t total = sizeof(this); + + for (int i = 0; i < VARS_PER_TASK; ++i) { + total += txt[i].length(); + } + return total; + } + + String txt[VARS_PER_TASK]; + int controller_idx; + byte TaskIndex; + int idx; + byte sensorType; +}; +//#endif //USES_C009 + + +//#ifdef USES_C010 +/*********************************************************************************************\ +* C010_queue_element for queueing requests for 010: Generic UDP +* Using queue_element_single_value_base +\*********************************************************************************************/ +#define C010_queue_element queue_element_single_value_base +//#endif //USES_C010 + +//#ifdef USES_C011 +/*********************************************************************************************\ +* C011_queue_element for queueing requests for 011: Generic HTTP Advanced +\*********************************************************************************************/ +#define C011_queue_element simple_queue_element_string_only +//#endif //USES_C011 + +//#ifdef USES_C012 +/*********************************************************************************************\ +* C012_queue_element for queueing requests for 012: Blynk +* Using queue_element_single_value_base +\*********************************************************************************************/ +#define C012_queue_element queue_element_single_value_base +//#endif //USES_C012 + +//#ifdef USES_C015 +/*********************************************************************************************\ +* C015_queue_element for queueing requests for 015: Blynk +* Using queue_element_single_value_base +\*********************************************************************************************/ + +// #define C015_queue_element queue_element_single_value_base +class C015_queue_element { +public: + + C015_queue_element() : controller_idx(0), TaskIndex(0), idx(0), valuesSent(0) {} + + C015_queue_element(const struct EventStruct *event, byte value_count) : + controller_idx(event->ControllerIndex), + TaskIndex(event->TaskIndex), + idx(event->idx), + valuesSent(0), + valueCount(value_count) {} + + bool checkDone(bool succesfull) const { + if (succesfull) { ++valuesSent; } + return valuesSent >= valueCount || valuesSent >= VARS_PER_TASK; + } + + size_t getSize() const { + size_t total = sizeof(this); + + for (int i = 0; i < VARS_PER_TASK; ++i) { + total += txt[i].length(); + } + return total; + } + + String txt[VARS_PER_TASK]; + int vPin[VARS_PER_TASK] = {0}; + int controller_idx; + byte TaskIndex; + int idx; + mutable byte valuesSent; // Value must be set by const function checkDone() + byte valueCount; +}; +//#endif //USES_C015 + +//#ifdef USES_C016 +/*********************************************************************************************\ +* C016_queue_element for queueing requests for C016: Cached HTTP. +\*********************************************************************************************/ +class C016_queue_element { +public: + + C016_queue_element() : timestamp(0), controller_idx(0), TaskIndex(0), sensorType(0) {} + + C016_queue_element(const struct EventStruct *event, byte value_count, unsigned long unixTime) : + timestamp(unixTime), + controller_idx(event->ControllerIndex), + TaskIndex(event->TaskIndex), + sensorType(event->sensorType), + valueCount(value_count) + { + const byte BaseVarIndex = TaskIndex * VARS_PER_TASK; + + for (byte i = 0; i < VARS_PER_TASK; ++i) { + if (i < value_count) { + values[i] = UserVar[BaseVarIndex + i]; + } else { + values[i] = 0.0; + } + } + } + + size_t getSize() const { + return sizeof(this); + } + + float values[VARS_PER_TASK]; + unsigned long timestamp; // Unix timestamp + byte controller_idx; + byte TaskIndex; + byte sensorType; + byte valueCount; +}; + +//#endif //USES_C016 + +//#ifdef USES_C017 +/*********************************************************************************************\ +* C017_queue_element for queueing requests for C017: Zabbix Trapper Protocol. +\*********************************************************************************************/ +class C017_queue_element { +public: + + C017_queue_element() : controller_idx(0), TaskIndex(0), idx(0), sensorType(0) {} + + C017_queue_element(const struct EventStruct *event) : + controller_idx(event->ControllerIndex), + TaskIndex(event->TaskIndex), + idx(event->idx), + sensorType(event->sensorType) {} + + size_t getSize() const { + size_t total = sizeof(this); + + for (int i = 0; i < VARS_PER_TASK; ++i) { + total += txt[i].length(); + } + return total; + } + + String txt[VARS_PER_TASK]; + int controller_idx; + byte TaskIndex; + int idx; + byte sensorType; +}; +//#endif //USES_C017 + +//#ifdef USES_C018 +/*********************************************************************************************\ +* C018_queue_element for queueing requests for C018: TTN/RN2483 +\*********************************************************************************************/ + +#ifdef USES_PACKED_RAW_DATA +String getPackedFromPlugin(struct EventStruct *event, uint8_t sampleSetCount); +#endif // USES_PACKED_RAW_DATA + + +class C018_queue_element { +public: + + C018_queue_element() {} + + C018_queue_element(struct EventStruct *event, uint8_t sampleSetCount) : + controller_idx(event->ControllerIndex) + { + #ifdef USES_PACKED_RAW_DATA + packed = getPackedFromPlugin(event, sampleSetCount); + #endif // USES_PACKED_RAW_DATA + } + + size_t getSize() const { + return sizeof(this) + packed.length(); + } + + int controller_idx = 0; + String packed; +}; + +//#endif //USES_C018 + + + + +/*********************************************************************************************\ +* ControllerDelayHandlerStruct +\*********************************************************************************************/ +template +struct ControllerDelayHandlerStruct { + ControllerDelayHandlerStruct() : + lastSend(0), + minTimeBetweenMessages(CONTROLLER_DELAY_QUEUE_DELAY_DFLT), + max_queue_depth(CONTROLLER_DELAY_QUEUE_DEPTH_DFLT), + attempt(0), + max_retries(CONTROLLER_DELAY_QUEUE_RETRY_DFLT), + delete_oldest(false), + must_check_reply(false) {} + + void configureControllerSettings(const ControllerSettingsStruct& settings) { + minTimeBetweenMessages = settings.MinimalTimeBetweenMessages; + max_queue_depth = settings.MaxQueueDepth; + max_retries = settings.MaxRetry; + delete_oldest = settings.DeleteOldest; + must_check_reply = settings.MustCheckReply; + + // Set some sound limits when not configured + if (max_queue_depth == 0) { max_queue_depth = CONTROLLER_DELAY_QUEUE_DEPTH_DFLT; } + + if (max_retries == 0) { max_retries = CONTROLLER_DELAY_QUEUE_RETRY_DFLT; } + + if (minTimeBetweenMessages == 0) { minTimeBetweenMessages = CONTROLLER_DELAY_QUEUE_DELAY_DFLT; } + + // No less than 10 msec between messages. + if (minTimeBetweenMessages < 10) { minTimeBetweenMessages = 10; } + } + + bool queueFull(const T& element) const { + if (sendQueue.size() >= max_queue_depth) { return true; } + + // Number of elements is not exceeding the limit, check memory + int freeHeap = ESP.getFreeHeap(); + + if (freeHeap > 5000) { return false; // Memory is not an issue. + } +#ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + String log = "Controller-"; + log += element.controller_idx + 1; + log += " : Memory used: "; + log += getQueueMemorySize(); + log += " bytes "; + log += sendQueue.size(); + log += " items "; + log += freeHeap; + log += " free"; + addLog(LOG_LEVEL_DEBUG, log); + } +#endif // ifndef BUILD_NO_DEBUG + return true; + } + + // Try to add to the queue, if permitted by "delete_oldest" + // Return false when no item was added. + bool addToQueue(const T& element) { + if (delete_oldest) { + // Force add to the queue. + // If max buffer is reached, the oldest in the queue (first to be served) will be removed. + while (queueFull(element)) { + sendQueue.pop_front(); + } + sendQueue.emplace_back(element); + return true; + } + + if (!queueFull(element)) { + sendQueue.emplace_back(element); + return true; + } +#ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + String log = get_formatted_Controller_number(element.controller_idx); + log += " : queue full"; + addLog(LOG_LEVEL_DEBUG, log); + } +#endif // ifndef BUILD_NO_DEBUG + return false; + } + + // Get the next element. + // Remove front element when max_retries is reached. + T* getNext() { + if (sendQueue.empty()) { return NULL; } + + if (attempt > max_retries) { + sendQueue.pop_front(); + attempt = 0; + + if (sendQueue.empty()) { return NULL; } + } + return &sendQueue.front(); + } + + // Mark as processed and return time to schedule for next process. + // Return 0 when nothing to process. + // @param remove_from_queue indicates whether the elements should be removed from the queue. + unsigned long markProcessed(bool remove_from_queue) { + if (sendQueue.empty()) { return 0; } + + if (remove_from_queue) { + sendQueue.pop_front(); + attempt = 0; + } else { + ++attempt; + } + lastSend = millis(); + return getNextScheduleTime(); + } + + unsigned long getNextScheduleTime() const { + if (sendQueue.empty()) { return 0; } + unsigned long nextTime = lastSend + minTimeBetweenMessages; + + if (timePassedSince(nextTime) > 0) { + nextTime = millis(); + } + + if (nextTime == 0) { nextTime = 1; // Just to make sure it will be executed + } + return nextTime; + } + + size_t getQueueMemorySize() const { + size_t totalSize = 0; + + for (auto it = sendQueue.begin(); it != sendQueue.end(); ++it) { + totalSize += it->getSize(); + } + return totalSize; + } + + std::list sendQueue; + unsigned long lastSend; + unsigned int minTimeBetweenMessages; + byte max_queue_depth; + byte attempt; + byte max_retries; + bool delete_oldest; + bool must_check_reply; +}; + +ControllerDelayHandlerStruct MQTTDelayHandler; + +// Uncrustify must not be used on macros, so turn it off. +// *INDENT-OFF* + + +// This macro defines the code needed to create the 'process_c##NNN####M##_delay_queue()' +// function and all needed objects and forward declarations. +// It is a macro to prevent common typo errors. +// This function will perform the (re)scheduling and mark if it is processed (and can be removed) +// The controller itself must implement the 'do_process_c004_delay_queue' function to actually +// send the data. +// Its return value must state whether it can be marked 'Processed'. +// N.B. some controllers only can send one value per iteration, so a returned "false" can mean it +// was still successful. The controller should keep track of the last value sent +// in the element stored in the queue. +#define DEFINE_Cxxx_DELAY_QUEUE_MACRO(NNN, M) \ + ControllerDelayHandlerStructC##NNN####M##_DelayHandler; \ + bool do_process_c##NNN####M##_delay_queue(int controller_number, \ + const C##NNN####M##_queue_element & element, \ + ControllerSettingsStruct & ControllerSettings); \ + void process_c##NNN####M##_delay_queue() { \ + C##NNN####M##_queue_element *element(C##NNN####M##_DelayHandler.getNext()); \ + if (element == NULL) return; \ + MakeControllerSettings (ControllerSettings); \ + LoadControllerSettings(element->controller_idx, ControllerSettings); \ + C##NNN####M##_DelayHandler.configureControllerSettings(ControllerSettings); \ + if (!WiFiConnected(10)) { \ + scheduleNextDelayQueue(TIMER_C##NNN####M##_DELAY_QUEUE, C##NNN####M##_DelayHandler.getNextScheduleTime()); \ + return; \ + } \ + START_TIMER; \ + C##NNN####M##_DelayHandler.markProcessed(do_process_c##NNN####M##_delay_queue(M, *element, ControllerSettings)); \ + STOP_TIMER(C##NNN####M##_DELAY_QUEUE); \ + scheduleNextDelayQueue(TIMER_C##NNN####M##_DELAY_QUEUE, C##NNN####M##_DelayHandler.getNextScheduleTime()); \ + } + +// Define the function wrappers to handle the calling to Cxxx_DelayHandler etc. +// If someone knows how to add leading zeros in macros, please be my guest :) +#ifdef USES_C001 +DEFINE_Cxxx_DELAY_QUEUE_MACRO(00, 1) +#endif // ifdef USES_C001 +#ifdef USES_C003 +DEFINE_Cxxx_DELAY_QUEUE_MACRO(00, 3) +#endif // ifdef USES_C003 +#ifdef USES_C004 +DEFINE_Cxxx_DELAY_QUEUE_MACRO(00, 4) +#endif // ifdef USES_C004 +#ifdef USES_C007 +DEFINE_Cxxx_DELAY_QUEUE_MACRO(00, 7) +#endif // ifdef USES_C007 +#ifdef USES_C008 +DEFINE_Cxxx_DELAY_QUEUE_MACRO(00, 8) +#endif // ifdef USES_C008 +#ifdef USES_C009 +DEFINE_Cxxx_DELAY_QUEUE_MACRO(00, 9) +#endif // ifdef USES_C009 +#ifdef USES_C010 +DEFINE_Cxxx_DELAY_QUEUE_MACRO(0, 10) +#endif // ifdef USES_C010 +#ifdef USES_C011 +DEFINE_Cxxx_DELAY_QUEUE_MACRO(0, 11) +#endif // ifdef USES_C011 +#ifdef USES_C012 +DEFINE_Cxxx_DELAY_QUEUE_MACRO(0, 12) +#endif // ifdef USES_C012 + +/* + #ifdef USES_C013 + DEFINE_Cxxx_DELAY_QUEUE_MACRO(0, 13) + #endif + */ + +/* + #ifdef USES_C014 + DEFINE_Cxxx_DELAY_QUEUE_MACRO(0, 14) + #endif + */ +#ifdef USES_C015 +DEFINE_Cxxx_DELAY_QUEUE_MACRO(0, 15) +#endif // ifdef USES_C015 + +#ifdef USES_C016 +DEFINE_Cxxx_DELAY_QUEUE_MACRO(0, 16) +#endif // ifdef USES_C016 + + +#ifdef USES_C017 +DEFINE_Cxxx_DELAY_QUEUE_MACRO(0, 17) +#endif // ifdef USES_C017 + +#ifdef USES_C018 +DEFINE_Cxxx_DELAY_QUEUE_MACRO(0, 18) +#endif + + +/* + #ifdef USES_C019 + DEFINE_Cxxx_DELAY_QUEUE_MACRO(0, 19) + #endif + */ + +/* + #ifdef USES_C020 + DEFINE_Cxxx_DELAY_QUEUE_MACRO(0, 20) + #endif + */ + + +// When extending this, also extend in Scheduler.ino: +// void process_interval_timer(unsigned long id, unsigned long lasttimer) + +// Uncrustify must not be used on macros, but we're now done, so turn Uncrustify on again. +// *INDENT-ON* + +#endif // DELAY_QUEUE_ELEMENTS_H \ No newline at end of file diff --git a/src/ESPEasy-Globals.h b/src/ESPEasy-Globals.h index b474c3ae9..eaae0bb88 100644 --- a/src/ESPEasy-Globals.h +++ b/src/ESPEasy-Globals.h @@ -14,8 +14,11 @@ #include "ESPEasy_fdwdecl.h" #include "DataStructs/ESPEasyLimits.h" +#include "ESPEasy_plugindefs.h" -#include +#include "ESPEasy_buildinfo.h" + +//#include // ******************************************************************************** // Check struct sizes at compile time @@ -91,31 +94,7 @@ void check_size() { // ******************************************************************************** // DO NOT CHANGE ANYTHING BELOW THIS LINE // ******************************************************************************** -#define ESP_PROJECT_PID 2016110801L -#if defined(ESP8266) - #define VERSION 2 // config file version (not ESPEasy version). increase if you make incompatible changes to config system. -#endif -#if defined(ESP32) - #define VERSION 3 // Change in config.dat mapping needs a full reset -#endif - -#define BUILD 20103 // git version 2.1.03 -#if defined(ESP8266) - #define BUILD_NOTES " - Mega" -#endif -#if defined(ESP32) - #define BUILD_NOTES " - Mega32" -#endif - -#ifndef BUILD_GIT -#define BUILD_GIT "(custom)" -#endif - -#define MAX_FLASHWRITES_PER_DAY 100 // per 24 hour window -#define INPUT_COMMAND_SIZE 240 // Affects maximum command length in rules and other commands -// FIXME TD-er: INPUT_COMMAND_SIZE is also used in commands where simply a check for valid parameter is needed -// and some may need less memory. (which is stack allocated) #define NODE_TYPE_ID_ESP_EASY_STD 1 #define NODE_TYPE_ID_RPI_EASY_STD 5 // https://github.com/enesbcs/rpieasy @@ -151,102 +130,9 @@ void check_size() { #define TIMER_C020_DELAY_QUEUE 25 #define TIMING_STATS_THRESHOLD 100000 -#define TIMER_GRATUITOUS_ARP_MAX 5000 -#define PLUGIN_INIT_ALL 1 -#define PLUGIN_INIT 2 -#define PLUGIN_READ 3 // This call can yield new data (when success = true) and then send to controllers -#define PLUGIN_ONCE_A_SECOND 4 // Called once a second -#define PLUGIN_TEN_PER_SECOND 5 // Called 10x per second (typical for checking new data instead of waiting) -#define PLUGIN_DEVICE_ADD 6 // Called at boot for letting a plugin adding itself to list of available plugins/devices -#define PLUGIN_EVENTLIST_ADD 7 -#define PLUGIN_WEBFORM_SAVE 8 // Call from web interface to save settings -#define PLUGIN_WEBFORM_LOAD 9 // Call from web interface for presenting settings and status of plugin -#define PLUGIN_WEBFORM_SHOW_VALUES 10 // Call from devices overview page to format values in HTML -#define PLUGIN_GET_DEVICENAME 11 -#define PLUGIN_GET_DEVICEVALUENAMES 12 -#define PLUGIN_WRITE 13 -#define PLUGIN_EVENT_OUT 14 -#define PLUGIN_WEBFORM_SHOW_CONFIG 15 -#define PLUGIN_SERIAL_IN 16 -#define PLUGIN_UDP_IN 17 -#define PLUGIN_CLOCK_IN 18 -#define PLUGIN_TIMER_IN 19 -#define PLUGIN_FIFTY_PER_SECOND 20 -#define PLUGIN_SET_CONFIG 21 -#define PLUGIN_GET_DEVICEGPIONAMES 22 -#define PLUGIN_EXIT 23 -#define PLUGIN_GET_CONFIG 24 -#define PLUGIN_UNCONDITIONAL_POLL 25 -#define PLUGIN_REQUEST 26 -#define PLUGIN_TIME_CHANGE 27 -#define PLUGIN_MONITOR 28 -#define PLUGIN_SET_DEFAULTS 29 -#define PLUGIN_GET_PACKED_RAW_DATA 30 // Return all data in a compact binary format specific for that plugin. - // Needs USES_PACKED_RAW_DATA - - -// Make sure the CPLUGIN_* does not overlap PLUGIN_* -#define CPLUGIN_PROTOCOL_ADD 41 // Called at boot for letting a controller adding itself to list of available controllers -#define CPLUGIN_PROTOCOL_TEMPLATE 42 -#define CPLUGIN_PROTOCOL_SEND 43 -#define CPLUGIN_PROTOCOL_RECV 44 -#define CPLUGIN_GET_DEVICENAME 45 -#define CPLUGIN_WEBFORM_SAVE 46 -#define CPLUGIN_WEBFORM_LOAD 47 -#define CPLUGIN_GET_PROTOCOL_DISPLAY_NAME 48 -#define CPLUGIN_TASK_CHANGE_NOTIFICATION 49 -#define CPLUGIN_INIT 50 -#define CPLUGIN_UDP_IN 51 -#define CPLUGIN_FLUSH 52 // Force offloading data stored in buffers, called before sleep/reboot - -// new messages for autodiscover controller plugins (experimental) i.e. C014 -#define CPLUGIN_GOT_CONNECTED 53 // call after connected to mqtt server to publich device autodicover features -#define CPLUGIN_GOT_INVALID 54 // should be called before major changes i.e. changing the device name to clean up data on the controller. !ToDo -#define CPLUGIN_INTERVAL 55 // call every interval loop -#define CPLUGIN_ACKNOWLEDGE 56 // call for sending acknowledges !ToDo done by direct function call in PluginCall() for now. - -#define CPLUGIN_WEBFORM_SHOW_HOST_CONFIG 57 // Used for showing host information for the controller. - -#define CONTROLLER_USE_DNS 1 -#define CONTROLLER_HOSTNAME 2 -#define CONTROLLER_IP 3 -#define CONTROLLER_PORT 4 -#define CONTROLLER_USER 5 -#define CONTROLLER_PASS 6 -#define CONTROLLER_MIN_SEND_INTERVAL 7 -#define CONTROLLER_MAX_QUEUE_DEPTH 8 -#define CONTROLLER_MAX_RETRIES 9 -#define CONTROLLER_FULL_QUEUE_ACTION 10 -#define CONTROLLER_CHECK_REPLY 12 -#define CONTROLLER_SUBSCRIBE 13 -#define CONTROLLER_PUBLISH 14 -#define CONTROLLER_LWT_TOPIC 15 -#define CONTROLLER_LWT_CONNECT_MESSAGE 16 -#define CONTROLLER_LWT_DISCONNECT_MESSAGE 17 -#define CONTROLLER_TIMEOUT 18 -#define CONTROLLER_SAMPLE_SET_INITIATOR 19 -#define CONTROLLER_ENABLED 20 // Keep this as last, is used to loop over all parameters - -#define NPLUGIN_PROTOCOL_ADD 1 -#define NPLUGIN_GET_DEVICENAME 2 -#define NPLUGIN_WEBFORM_SAVE 3 -#define NPLUGIN_WEBFORM_LOAD 4 -#define NPLUGIN_WRITE 5 -#define NPLUGIN_NOTIFY 6 -#define NPLUGIN_NOT_FOUND 255 - - -#define LOG_LEVEL_NONE 0 -#define LOG_LEVEL_ERROR 1 -#define LOG_LEVEL_INFO 2 -#define LOG_LEVEL_DEBUG 3 -#define LOG_LEVEL_DEBUG_MORE 4 -#define LOG_LEVEL_DEBUG_DEV 9 // use for testing/debugging only, not for regular use -#define LOG_LEVEL_NRELEMENTS 5 // Update this and getLogLevelDisplayString() when new log levels are added - #define CMD_REBOOT 89 #define CMD_WIFI_DISCONNECT 135 @@ -263,30 +149,7 @@ void check_size() { #define SEARCH_PIN_STATE true #define NO_SEARCH_PIN_STATE false -#define DEVICE_TYPE_SINGLE 1 // connected through 1 datapin -#define DEVICE_TYPE_DUAL 2 // connected through 2 datapins -#define DEVICE_TYPE_TRIPLE 3 // connected through 3 datapins -#define DEVICE_TYPE_ANALOG 10 // AIN/tout pin -#define DEVICE_TYPE_I2C 20 // connected through I2C -#define DEVICE_TYPE_DUMMY 99 // Dummy device, has no physical connection -#define SENSOR_TYPE_NONE 0 -#define SENSOR_TYPE_SINGLE 1 -#define SENSOR_TYPE_TEMP_HUM 2 -#define SENSOR_TYPE_TEMP_BARO 3 -#define SENSOR_TYPE_TEMP_HUM_BARO 4 -#define SENSOR_TYPE_DUAL 5 -#define SENSOR_TYPE_TRIPLE 6 -#define SENSOR_TYPE_QUAD 7 -#define SENSOR_TYPE_TEMP_EMPTY_BARO 8 -#define SENSOR_TYPE_SWITCH 10 -#define SENSOR_TYPE_DIMMER 11 -#define SENSOR_TYPE_LONG 20 -#define SENSOR_TYPE_WIND 21 -#define SENSOR_TYPE_STRING 22 - -#define UNIT_NUMBER_MAX 9999 // Stored in Settings.Unit -#define DOMOTICZ_MAX_IDX 999999999 // Looks like it is an unsigned int, so could be up to 4 bln. #define VALUE_SOURCE_SYSTEM 1 #define VALUE_SOURCE_SERIAL 2 @@ -300,28 +163,6 @@ void check_size() { #define BOOT_CAUSE_DEEP_SLEEP 2 #define BOOT_CAUSE_EXT_WD 10 -#define DAT_TASKS_DISTANCE 2048 // DAT_TASKS_SIZE + DAT_TASKS_CUSTOM_SIZE -#define DAT_TASKS_SIZE 1024 -#define DAT_TASKS_CUSTOM_OFFSET 1024 // Equal to DAT_TASKS_SIZE -#define DAT_TASKS_CUSTOM_SIZE 1024 -#define DAT_CUSTOM_CONTROLLER_SIZE 1024 -#define DAT_CONTROLLER_SIZE 1024 -#define DAT_NOTIFICATION_SIZE 1024 - -#define DAT_BASIC_SETTINGS_SIZE 4096 - -#if defined(ESP8266) - #define DAT_OFFSET_TASKS 4096 // each task = 2k, (1024 basic + 1024 bytes custom), 12 max - #define DAT_OFFSET_CONTROLLER 28672 // each controller = 1k, 4 max - #define DAT_OFFSET_CUSTOM_CONTROLLER 32768 // each custom controller config = 1k, 4 max. - #define CONFIG_FILE_SIZE 65536 -#endif -#if defined(ESP32) - #define DAT_OFFSET_CONTROLLER 8192 // each controller = 1k, 4 max - #define DAT_OFFSET_CUSTOM_CONTROLLER 12288 // each custom controller config = 1k, 4 max. - #define DAT_OFFSET_TASKS 32768 // each task = 2k, (1024 basic + 1024 bytes custom), 32 max - #define CONFIG_FILE_SIZE 131072 -#endif /* @@ -334,6 +175,9 @@ void check_size() { #include "Custom.h" #endif +#include +#include + #include "DataStructs/ESPEasyLimits.h" #include "DataStructs/ESPEasy_EventStruct.h" @@ -344,13 +188,59 @@ void check_size() { #include "DataStructs/DeviceModel.h" #include "DataStructs/FactoryDefaultPref.h" #include "DataStructs/ControllerSettingsStruct.h" +#include "DataStructs/NotificationSettingsStruct.h" +#include "DataStructs/ExtraTaskSettingsStruct.h" +#include "DataStructs/DeviceStruct.h" +#include "DataStructs/TimingStats.h" +#include "DataStructs/LogStruct.h" +#include "DataStructs/ProtocolStruct.h" +#include "DataStructs/NotificationStruct.h" +#include "DataStructs/NodeStruct.h" +#include "DataStructs/SystemTimerStruct.h" +#include "DataStructs/RTCStruct.h" +#include "DataStructs/PortStatusStruct.h" + CRCStruct CRCValues; SecurityStruct SecuritySettings; SettingsStruct Settings; ResetFactoryDefaultPreference_struct ResetFactoryDefaultPreference; +ExtraTaskSettingsStruct ExtraTaskSettings; +LogStruct Logging; +NotificationStruct Notification[NPLUGIN_MAX]; +RTCStruct RTC; + + +std::map pluginStats; +std::map controllerStats; +std::map miscStats; +unsigned long timingstats_last_reset = 0; + + +#define START_TIMER const unsigned statisticsTimerStart(micros()); +#define STOP_TIMER_TASK(T, F) \ + if (mustLogFunction(F)) pluginStats[T * 256 + F].add(usecPassedSince(statisticsTimerStart)); +#define STOP_TIMER_CONTROLLER(T, F) \ + if (mustLogCFunction(F)) controllerStats[T * 256 + F].add(usecPassedSince(statisticsTimerStart)); + +// #define STOP_TIMER_LOADFILE miscStats[LOADFILE_STATS].add(usecPassedSince(statisticsTimerStart)); +#define STOP_TIMER(L) miscStats[L].add(usecPassedSince(statisticsTimerStart)); + + +unsigned long connectionFailures = 0; +byte highest_active_log_level = 0; +bool log_to_serial_disabled = false; + +boolean (*Plugin_ptr[PLUGIN_MAX])(byte, struct EventStruct*, String&); + +std::vector Plugin_id; +std::vector Task_id_to_Plugin_id; + + + #include "define_plugin_sets.h" +#include "ESPEasy_Log.h" #include "WebStaticData.h" #include "ESPEasyTimeTypes.h" #include "StringProviderTypes.h" @@ -359,8 +249,6 @@ ResetFactoryDefaultPreference_struct ResetFactoryDefaultPreference; #include "WebServer_fwddecl.h" #include "I2CTypes.h" #include -#include -#include #define FS_NO_GLOBALS @@ -517,11 +405,6 @@ bool statusNTPInitialized = false; // udp protocol stuff (syslog, global sync, node info list, ntp time) WiFiUDP portUDP; -class TimingStats; - - - - /*********************************************************************************************\ * Custom Variables for usage in rules and http. @@ -530,7 +413,9 @@ class TimingStats; * let,1,10 * if %v1%=10 do ... \*********************************************************************************************/ -float customFloatVar[CUSTOM_VARS_MAX]; +float customFloatVar[CUSTOM_VARS_MAX]; + +float UserVar[VARS_PER_TASK * TASKS_MAX]; @@ -538,387 +423,16 @@ float customFloatVar[CUSTOM_VARS_MAX]; -/*********************************************************************************************\ - * NotificationSettingsStruct -\*********************************************************************************************/ -struct NotificationSettingsStruct -{ - NotificationSettingsStruct() : Port(0), Pin1(0), Pin2(0) { - ZERO_FILL(Server); - ZERO_FILL(Domain); - ZERO_FILL(Sender); - ZERO_FILL(Receiver); - ZERO_FILL(Subject); - ZERO_FILL(Body); - ZERO_FILL(User); - ZERO_FILL(Pass); - } - - void validate() { - ZERO_TERMINATE(Server); - ZERO_TERMINATE(Domain); - ZERO_TERMINATE(Sender); - ZERO_TERMINATE(Receiver); - ZERO_TERMINATE(Subject); - ZERO_TERMINATE(Body); - ZERO_TERMINATE(User); - ZERO_TERMINATE(Pass); - } - - char Server[65]; - unsigned int Port; - char Domain[65]; - char Sender[65]; - char Receiver[65]; - char Subject[129]; - char Body[513]; - byte Pin1; - byte Pin2; - char User[49]; - char Pass[33]; - //its safe to extend this struct, up to 4096 bytes, default values in config are 0 -}; - -typedef std::shared_ptr NotificationSettingsStruct_ptr_type; -#define MakeNotificationSettings(T) NotificationSettingsStruct_ptr_type NotificationSettingsStruct_ptr(new NotificationSettingsStruct());\ - NotificationSettingsStruct& T = *NotificationSettingsStruct_ptr; - - -/*********************************************************************************************\ - * ExtraTaskSettingsStruct -\*********************************************************************************************/ - -// This is only used by some plugins to store extra settings like formula descriptions. -// These settings can only be active for one plugin, meaning they have to be loaded -// over and over again from flash when another active plugin uses these values. -//FIXME @TD-er: Should think of another mechanism to make this more efficient. -struct ExtraTaskSettingsStruct -{ - ExtraTaskSettingsStruct() : TaskIndex(TASKS_MAX) { - clear(); - } - - void clear() { - TaskIndex = TASKS_MAX; - ZERO_FILL(TaskDeviceName); - for (byte i = 0; i < VARS_PER_TASK; ++i) { - TaskDeviceValueDecimals[i] = 2; - ZERO_FILL(TaskDeviceFormula[i]); - ZERO_FILL(TaskDeviceValueNames[i]); - } - for (byte i = 0; i < PLUGIN_EXTRACONFIGVAR_MAX; ++i) { - TaskDevicePluginConfigLong[i] = 0; - TaskDevicePluginConfig[i] = 0; - } - } - - void validate() { - ZERO_TERMINATE(TaskDeviceName); - for (byte i = 0; i < VARS_PER_TASK; ++i) { - ZERO_TERMINATE(TaskDeviceFormula[i]); - ZERO_TERMINATE(TaskDeviceValueNames[i]); - } - } - - bool checkUniqueValueNames() { - for (int i = 0; i < (VARS_PER_TASK - 1); ++i) { - for (int j = i; j < VARS_PER_TASK; ++j) { - if (i != j && TaskDeviceValueNames[i][0] != 0) { - if (strcasecmp(TaskDeviceValueNames[i], TaskDeviceValueNames[j]) == 0) - return false; - } - } - } - return true; - } - - void clearUnusedValueNames(byte usedVars) { - for (byte i = usedVars; i < VARS_PER_TASK; ++i) { - TaskDeviceValueDecimals[i] = 2; - ZERO_FILL(TaskDeviceFormula[i]); - ZERO_FILL(TaskDeviceValueNames[i]); - } - } - - bool checkInvalidCharInNames(const char* name) { - int pos = 0; - while (*(name+pos) != 0) { - switch (*(name+pos)) { - case ',': - case ' ': - case '#': - case '[': - case ']': - return false; - } - ++pos; - } - return true; - } - - bool checkInvalidCharInNames() { - if (!checkInvalidCharInNames(&TaskDeviceName[0])) return false; - for (int i = 0; i < (VARS_PER_TASK - 1); ++i) { - if (!checkInvalidCharInNames(&TaskDeviceValueNames[i][0])) return false; - } - return true; - } - - byte TaskIndex; // Always < TASKS_MAX - char TaskDeviceName[NAME_FORMULA_LENGTH_MAX + 1]; - char TaskDeviceFormula[VARS_PER_TASK][NAME_FORMULA_LENGTH_MAX + 1]; - char TaskDeviceValueNames[VARS_PER_TASK][NAME_FORMULA_LENGTH_MAX + 1]; - long TaskDevicePluginConfigLong[PLUGIN_EXTRACONFIGVAR_MAX]; - byte TaskDeviceValueDecimals[VARS_PER_TASK]; - int16_t TaskDevicePluginConfig[PLUGIN_EXTRACONFIGVAR_MAX]; -} ExtraTaskSettings; /*********************************************************************************************\ - * LogStruct + * Buffer for outputting logs via serial port. \*********************************************************************************************/ -#define LOG_STRUCT_MESSAGE_SIZE 128 -#ifdef ESP32 - #define LOG_STRUCT_MESSAGE_LINES 30 - #define LOG_BUFFER_EXPIRE 30000 // Time after which a buffered log item is considered expired. -#else - #if defined(PLUGIN_BUILD_TESTING) || defined(PLUGIN_BUILD_DEV) - #define LOG_STRUCT_MESSAGE_LINES 10 - #else - #define LOG_STRUCT_MESSAGE_LINES 15 - #endif - #define LOG_BUFFER_EXPIRE 5000 // Time after which a buffered log item is considered expired. -#endif - -struct LogStruct { - LogStruct() : write_idx(0), read_idx(0), lastReadTimeStamp(0) { - for (int i = 0; i < LOG_STRUCT_MESSAGE_LINES; ++i) { - timeStamp[i] = 0; - log_level[i] = 0; - } - } - - void add(const byte loglevel, const char *line) { - write_idx = (write_idx + 1) % LOG_STRUCT_MESSAGE_LINES; - if (write_idx == read_idx) { - // Buffer full, move read_idx to overwrite oldest entry. - read_idx = (read_idx + 1) % LOG_STRUCT_MESSAGE_LINES; - } - timeStamp[write_idx] = millis(); - log_level[write_idx] = loglevel; - unsigned linelength = strlen(line); - if (linelength > LOG_STRUCT_MESSAGE_SIZE-1) - linelength = LOG_STRUCT_MESSAGE_SIZE-1; - Message[write_idx] = ""; - Message[write_idx].reserve(linelength); - for (unsigned i = 0; i < linelength; ++i) { - Message[write_idx] += *(line + i); - } - } - - // Read the next item and append it to the given string. - // Returns whether new lines are available. - bool get(String& output, const String& lineEnd) { - lastReadTimeStamp = millis(); - if (!isEmpty()) { - read_idx = (read_idx + 1) % LOG_STRUCT_MESSAGE_LINES; - output += formatLine(read_idx, lineEnd); - } - return !isEmpty(); - } - - String get_logjson_formatted(bool& logLinesAvailable, unsigned long& timestamp) { - lastReadTimeStamp = millis(); - logLinesAvailable = false; - if (isEmpty()) { - return ""; - } - read_idx = (read_idx + 1) % LOG_STRUCT_MESSAGE_LINES; - timestamp = timeStamp[read_idx]; - String output = logjson_formatLine(read_idx); - if (isEmpty()) return output; - output += ",\n"; - logLinesAvailable = true; - return output; - } - - bool isEmpty() { - return (write_idx == read_idx); - } - - bool logActiveRead() { - clearExpiredEntries(); - return timePassedSince(lastReadTimeStamp) < LOG_BUFFER_EXPIRE; - } - - private: - String formatLine(int index, const String& lineEnd) { - String output; - output += timeStamp[index]; - output += " : "; - output += Message[index]; - output += lineEnd; - return output; - } - - String logjson_formatLine(int index) { - String output; - output.reserve(LOG_STRUCT_MESSAGE_SIZE + 64); - output = "{"; - output += to_json_object_value("timestamp", String(timeStamp[index])); - output += ",\n"; - output += to_json_object_value("text", Message[index]); - output += ",\n"; - output += to_json_object_value("level", String(log_level[index])); - output += "}"; - return output; - } - - void clearExpiredEntries() { - if (isEmpty()) { - return; - } - if (timePassedSince(lastReadTimeStamp) > LOG_BUFFER_EXPIRE) { - // Clear the entire log. - // If web log is the only log active, it will not be checked again until it is read. - for (read_idx = 0; read_idx < LOG_STRUCT_MESSAGE_LINES; ++read_idx) { - Message[read_idx] = String(); // Free also the reserved memory. - timeStamp[read_idx] = 0; - log_level[read_idx] = 0; - } - read_idx = 0; - write_idx = 0; - } - } - - String Message[LOG_STRUCT_MESSAGE_LINES]; - unsigned long timeStamp[LOG_STRUCT_MESSAGE_LINES]; - int write_idx; - int read_idx; - unsigned long lastReadTimeStamp; - byte log_level[LOG_STRUCT_MESSAGE_LINES]; - -} Logging; - std::deque serialWriteBuffer; -byte highest_active_log_level = 0; -bool log_to_serial_disabled = false; -// Do this in a template to prevent casting to String when not needed. -#define addLog(L,S) if (loglevelActiveFor(L)) { addToLog(L,S); } - -/*********************************************************************************************\ - * DeviceStruct -\*********************************************************************************************/ -struct DeviceStruct -{ - DeviceStruct() : - Number(0), Type(0), VType(0), Ports(0), ValueCount(0), - PullUpOption(false), InverseLogicOption(false), FormulaOption(false), - Custom(false), SendDataOption(false), GlobalSyncOption(false), - TimerOption(false), TimerOptional(false), DecimalsOnly(false) {} - - bool connectedToGPIOpins() { - return (Type >= DEVICE_TYPE_SINGLE && Type <= DEVICE_TYPE_TRIPLE); - } - byte Number; // Plugin ID number. (PLUGIN_ID_xxx) - byte Type; // How the device is connected. e.g. DEVICE_TYPE_SINGLE => connected through 1 datapin - byte VType; // Type of value the plugin will return, used only for Domoticz - byte Ports; // Port to use when device has multiple I/O pins (N.B. not used much) - byte ValueCount; // The number of output values of a plugin. The value should match the number of keys PLUGIN_VALUENAME1_xxx - bool PullUpOption : 1; // Allow to set internal pull-up resistors. - bool InverseLogicOption : 1; // Allow to invert the boolean state (e.g. a switch) - bool FormulaOption : 1; // Allow to enter a formula to convert values during read. (not possible with Custom enabled) - bool Custom : 1; - bool SendDataOption : 1; // Allow to send data to a controller. - bool GlobalSyncOption : 1; // No longer used. Was used for ESPeasy values sync between nodes - bool TimerOption : 1; // Allow to set the "Interval" timer for the plugin. - bool TimerOptional : 1; // When taskdevice timer is not set and not optional, use default "Interval" delay (Settings.Delay) - bool DecimalsOnly; // Allow to set the number of decimals (otherwise treated a 0 decimals) -}; -typedef std::vector DeviceVector; -DeviceVector Device; - - -/*********************************************************************************************\ - * ProtocolStruct -\*********************************************************************************************/ -struct ProtocolStruct -{ - ProtocolStruct() : - defaultPort(0), Number(0), usesMQTT(false), usesAccount(false), usesPassword(false), - usesTemplate(false), usesID(false), Custom(false), usesHost(true), usesPort(true), - usesQueue(true), usesSampleSets(false) {} - uint16_t defaultPort; - byte Number; - bool usesMQTT : 1; - bool usesAccount : 1; - bool usesPassword : 1; - bool usesTemplate : 1; // When set, the protocol will pre-load some templates like default MQTT topics - bool usesID : 1; // Whether a controller supports sending an IDX value sent along with plugin data - bool Custom : 1; // When set, the controller has to define all parameters on the controller setup page - bool usesHost : 1; - bool usesPort : 1; - bool usesQueue : 1; - bool usesSampleSets : 1; -}; -typedef std::vector ProtocolVector; -ProtocolVector Protocol; - - -/*********************************************************************************************\ - * NotificationStruct -\*********************************************************************************************/ -struct NotificationStruct -{ - NotificationStruct() : - Number(0), usesMessaging(false), usesGPIO(0) {} - byte Number; - boolean usesMessaging; - byte usesGPIO; -} Notification[NPLUGIN_MAX]; - - -/*********************************************************************************************\ - * NodeStruct -\*********************************************************************************************/ -struct NodeStruct -{ - NodeStruct() : - build(0), age(0), nodeType(0) - { - for (byte i = 0; i < 4; ++i) ip[i] = 0; - } - String nodeName; - IPAddress ip; - uint16_t build; - byte age; - byte nodeType; -}; -typedef std::map NodesMap; -NodesMap Nodes; - -/*********************************************************************************************\ - * systemTimerStruct -\*********************************************************************************************/ -struct systemTimerStruct -{ - systemTimerStruct() : - timer(0), Par1(0), Par2(0), Par3(0), Par4(0), Par5(0), TaskIndex(-1), plugin(0) {} - - unsigned long timer; - int Par1; - int Par2; - int Par3; - int Par4; - int Par5; - int16_t TaskIndex; - byte plugin; -}; -std::map systemTimers; enum gpio_direction { gpio_input, @@ -926,6 +440,7 @@ enum gpio_direction { gpio_bidirectional }; + /*********************************************************************************************\ * pinStatesStruct \*********************************************************************************************/ @@ -940,34 +455,6 @@ struct pinStatesStruct } pinStates[PINSTATE_TABLE_MAX]; */ -// this offsets are in blocks, bytes = blocks * 4 -#define RTC_BASE_STRUCT 64 -#define RTC_BASE_USERVAR 74 -#define RTC_BASE_CACHE 124 - -#define RTC_CACHE_DATA_SIZE 240 -#define CACHE_FILE_MAX_SIZE 24000 - -/*********************************************************************************************\ - * RTCStruct -\*********************************************************************************************/ -//max 40 bytes: ( 74 - 64 ) * 4 -struct RTCStruct -{ - RTCStruct() : ID1(0), ID2(0), unused1(false), factoryResetCounter(0), - deepSleepState(0), bootFailedCount(0), flashDayCounter(0), - flashCounter(0), bootCounter(0), lastMixedSchedulerId(0) {} - byte ID1; - byte ID2; - boolean unused1; - byte factoryResetCounter; - byte deepSleepState; - byte bootFailedCount; - byte flashDayCounter; - unsigned long flashCounter; - unsigned long bootCounter; - unsigned long lastMixedSchedulerId; -} RTC; int deviceCount = -1; int protocolCount = -1; @@ -977,8 +464,6 @@ boolean printToWeb = false; String printWebString = ""; boolean printToWebJSON = false; -float UserVar[VARS_PER_TASK * TASKS_MAX]; - /********************************************************************************************\ RTC_cache_struct \*********************************************************************************************/ @@ -1014,7 +499,6 @@ unsigned long timermqtt_interval = 250; unsigned long lastSend = 0; unsigned long lastWeb = 0; byte cmd_within_mainloop = 0; -unsigned long connectionFailures = 0; unsigned long wdcounter = 0; unsigned long timerAPoff = 0; // Timer to check whether the AP mode should be disabled (0 = disabled) unsigned long timerAPstart = 0; // Timer to start AP mode, started when no valid network is detected. @@ -1028,9 +512,6 @@ float vcc = -1.0; boolean WebLoggedIn = false; int WebLoggedInTimer = 300; -boolean (*Plugin_ptr[PLUGIN_MAX])(byte, struct EventStruct*, String&); -std::vector Plugin_id; -std::vector Task_id_to_Plugin_id; bool (*CPlugin_ptr[CPLUGIN_MAX])(byte, struct EventStruct*, String&); byte CPlugin_id[CPLUGIN_MAX]; @@ -1165,60 +646,6 @@ boolean UseRTOSMultitasking = false; // void (*MainLoopCall_ptr)(void); //FIXME TD-er: No idea what this does. -/*********************************************************************************************\ - * TimingStats -\*********************************************************************************************/ -class TimingStats { - public: - TimingStats() : _timeTotal(0.0), _count(0), _maxVal(0), _minVal(4294967295) {} - - void add(unsigned long time) { - _timeTotal += time; - ++_count; - if (time > _maxVal) _maxVal = time; - if (time < _minVal) _minVal = time; - } - - void reset() { - _timeTotal = 0.0; - _count = 0; - _maxVal = 0; - _minVal = 4294967295; - } - - bool isEmpty() const { - return _count == 0; - } - - float getAvg() const { - if (_count == 0) return 0.0; - return _timeTotal / _count; - } - - unsigned int getMinMax(unsigned long& minVal, unsigned long& maxVal) const { - if (_count == 0) { - minVal = 0; - maxVal = 0; - return 0; - } - minVal = _minVal; - maxVal = _maxVal; - return _count; - } - - bool thresholdExceeded(unsigned long threshold) const { - if (_count == 0) { - return false; - } - return _maxVal > threshold; - } - - private: - float _timeTotal; - unsigned int _count; - unsigned long _maxVal; - unsigned long _minVal; -}; /* String getLogLine(const TimingStats& stats) { @@ -1239,254 +666,14 @@ String getLogLine(const TimingStats& stats) { } */ -String getPluginFunctionName(int function) { - switch(function) { - case PLUGIN_INIT_ALL: return F("INIT_ALL"); - case PLUGIN_INIT: return F("INIT"); - case PLUGIN_READ: return F("READ"); - case PLUGIN_ONCE_A_SECOND: return F("ONCE_A_SECOND"); - case PLUGIN_TEN_PER_SECOND: return F("TEN_PER_SECOND"); - case PLUGIN_DEVICE_ADD: return F("DEVICE_ADD"); - case PLUGIN_EVENTLIST_ADD: return F("EVENTLIST_ADD"); - case PLUGIN_WEBFORM_SAVE: return F("WEBFORM_SAVE"); - case PLUGIN_WEBFORM_LOAD: return F("WEBFORM_LOAD"); - case PLUGIN_WEBFORM_SHOW_VALUES: return F("WEBFORM_SHOW_VALUES"); - case PLUGIN_GET_DEVICENAME: return F("GET_DEVICENAME"); - case PLUGIN_GET_DEVICEVALUENAMES: return F("GET_DEVICEVALUENAMES"); - case PLUGIN_WRITE: return F("WRITE"); - case PLUGIN_EVENT_OUT: return F("EVENT_OUT"); - case PLUGIN_WEBFORM_SHOW_CONFIG: return F("WEBFORM_SHOW_CONFIG"); - case PLUGIN_SERIAL_IN: return F("SERIAL_IN"); - case PLUGIN_UDP_IN: return F("UDP_IN"); - case PLUGIN_CLOCK_IN: return F("CLOCK_IN"); - case PLUGIN_TIMER_IN: return F("TIMER_IN"); - case PLUGIN_FIFTY_PER_SECOND: return F("FIFTY_PER_SECOND"); - case PLUGIN_SET_CONFIG: return F("SET_CONFIG"); - case PLUGIN_GET_DEVICEGPIONAMES: return F("GET_DEVICEGPIONAMES"); - case PLUGIN_EXIT: return F("EXIT"); - case PLUGIN_GET_CONFIG: return F("GET_CONFIG"); - case PLUGIN_UNCONDITIONAL_POLL: return F("UNCONDITIONAL_POLL"); - case PLUGIN_REQUEST: return F("REQUEST"); - } - return getUnknownString(); -} - -bool mustLogFunction(int function) { - switch(function) { - case PLUGIN_INIT_ALL: return false; - case PLUGIN_INIT: return false; - case PLUGIN_READ: return true; - case PLUGIN_ONCE_A_SECOND: return true; - case PLUGIN_TEN_PER_SECOND: return true; - case PLUGIN_DEVICE_ADD: return false; - case PLUGIN_EVENTLIST_ADD: return false; - case PLUGIN_WEBFORM_SAVE: return false; - case PLUGIN_WEBFORM_LOAD: return false; - case PLUGIN_WEBFORM_SHOW_VALUES: return false; - case PLUGIN_GET_DEVICENAME: return false; - case PLUGIN_GET_DEVICEVALUENAMES: return false; - case PLUGIN_WRITE: return true; - case PLUGIN_EVENT_OUT: return true; - case PLUGIN_WEBFORM_SHOW_CONFIG: return false; - case PLUGIN_SERIAL_IN: return true; - case PLUGIN_UDP_IN: return true; - case PLUGIN_CLOCK_IN: return false; - case PLUGIN_TIMER_IN: return true; - case PLUGIN_FIFTY_PER_SECOND: return true; - case PLUGIN_SET_CONFIG: return false; - case PLUGIN_GET_DEVICEGPIONAMES: return false; - case PLUGIN_EXIT: return false; - case PLUGIN_GET_CONFIG: return false; - case PLUGIN_UNCONDITIONAL_POLL: return false; - case PLUGIN_REQUEST: return true; - } - return false; -} - -String getCPluginCFunctionName(int function) { - switch(function) { - case CPLUGIN_PROTOCOL_ADD: return F("CPLUGIN_PROTOCOL_ADD"); - case CPLUGIN_PROTOCOL_TEMPLATE: return F("CPLUGIN_PROTOCOL_TEMPLATE"); - case CPLUGIN_PROTOCOL_SEND: return F("CPLUGIN_PROTOCOL_SEND"); - case CPLUGIN_PROTOCOL_RECV: return F("CPLUGIN_PROTOCOL_RECV"); - case CPLUGIN_GET_DEVICENAME: return F("CPLUGIN_GET_DEVICENAME"); - case CPLUGIN_WEBFORM_SAVE: return F("CPLUGIN_WEBFORM_SAVE"); - case CPLUGIN_WEBFORM_LOAD: return F("CPLUGIN_WEBFORM_LOAD"); - case CPLUGIN_GET_PROTOCOL_DISPLAY_NAME: return F("CPLUGIN_GET_PROTOCOL_DISPLAY_NAME"); - case CPLUGIN_TASK_CHANGE_NOTIFICATION: return F("CPLUGIN_TASK_CHANGE_NOTIFICATION"); - case CPLUGIN_INIT: return F("CPLUGIN_INIT"); - case CPLUGIN_UDP_IN: return F("CPLUGIN_UDP_IN"); - } - return getUnknownString(); -} - -bool mustLogCFunction(int function) { - switch(function) { - case CPLUGIN_PROTOCOL_ADD: return false; - case CPLUGIN_PROTOCOL_TEMPLATE: return false; - case CPLUGIN_PROTOCOL_SEND: return true; - case CPLUGIN_PROTOCOL_RECV: return true; - case CPLUGIN_GET_DEVICENAME: return false; - case CPLUGIN_WEBFORM_SAVE: return false; - case CPLUGIN_WEBFORM_LOAD: return false; - case CPLUGIN_GET_PROTOCOL_DISPLAY_NAME: return false; - case CPLUGIN_TASK_CHANGE_NOTIFICATION: return false; - case CPLUGIN_INIT: return false; - case CPLUGIN_UDP_IN: return true; - } - return false; -} - -std::map pluginStats; -std::map controllerStats; -std::map miscStats; -unsigned long timingstats_last_reset = 0; - -#define LOADFILE_STATS 0 -#define SAVEFILE_STATS 1 -#define LOOP_STATS 2 -#define PLUGIN_CALL_50PS 3 -#define PLUGIN_CALL_10PS 4 -#define PLUGIN_CALL_10PSU 5 -#define PLUGIN_CALL_1PS 6 -#define SENSOR_SEND_TASK 7 -#define SEND_DATA_STATS 8 -#define COMPUTE_FORMULA_STATS 9 -#define PROC_SYS_TIMER 10 -#define SET_NEW_TIMER 11 -#define TIME_DIFF_COMPUTE 12 -#define MQTT_DELAY_QUEUE 13 -#define C001_DELAY_QUEUE 14 -#define C002_DELAY_QUEUE 15 -#define C003_DELAY_QUEUE 16 -#define C004_DELAY_QUEUE 17 -#define C005_DELAY_QUEUE 18 -#define C006_DELAY_QUEUE 19 -#define C007_DELAY_QUEUE 20 -#define C008_DELAY_QUEUE 21 -#define C009_DELAY_QUEUE 22 -#define C010_DELAY_QUEUE 23 -#define C011_DELAY_QUEUE 24 -#define C012_DELAY_QUEUE 25 -#define C013_DELAY_QUEUE 26 -#define C014_DELAY_QUEUE 27 -#define C015_DELAY_QUEUE 28 -#define C016_DELAY_QUEUE 29 -#define C017_DELAY_QUEUE 30 -#define C018_DELAY_QUEUE 31 -#define C019_DELAY_QUEUE 32 -#define C020_DELAY_QUEUE 33 -#define TRY_CONNECT_HOST_TCP 34 -#define TRY_CONNECT_HOST_UDP 35 -#define HOST_BY_NAME_STATS 36 -#define CONNECT_CLIENT_STATS 37 -#define LOAD_CUSTOM_TASK_STATS 38 -#define WIFI_ISCONNECTED_STATS 39 -#define WIFI_NOTCONNECTED_STATS 40 -#define LOAD_TASK_SETTINGS 41 -#define TRY_OPEN_FILE 42 -#define SPIFFS_GC_SUCCESS 43 -#define SPIFFS_GC_FAIL 44 -#define RULES_PROCESSING 45 -#define GRAT_ARP_STATS 46 -#define BACKGROUND_TASKS 47 -#define HANDLE_SCHEDULER_IDLE 48 -#define HANDLE_SCHEDULER_TASK 49 -#define START_TIMER const unsigned statisticsTimerStart(micros()); -#define STOP_TIMER_TASK(T,F) if (mustLogFunction(F)) pluginStats[T*256 + F].add(usecPassedSince(statisticsTimerStart)); -#define STOP_TIMER_CONTROLLER(T,F) if (mustLogCFunction(F)) controllerStats[T*256 + F].add(usecPassedSince(statisticsTimerStart)); -//#define STOP_TIMER_LOADFILE miscStats[LOADFILE_STATS].add(usecPassedSince(statisticsTimerStart)); -#define STOP_TIMER(L) miscStats[L].add(usecPassedSince(statisticsTimerStart)); -String getMiscStatsName(int stat) { - switch (stat) { - case LOADFILE_STATS: return F("Load File"); - case SAVEFILE_STATS: return F("Save File"); - case LOOP_STATS: return F("Loop"); - case PLUGIN_CALL_50PS: return F("Plugin call 50 p/s"); - case PLUGIN_CALL_10PS: return F("Plugin call 10 p/s"); - case PLUGIN_CALL_10PSU: return F("Plugin call 10 p/s U"); - case PLUGIN_CALL_1PS: return F("Plugin call 1 p/s"); - case SENSOR_SEND_TASK: return F("SensorSendTask()"); - case SEND_DATA_STATS: return F("sendData()"); - case COMPUTE_FORMULA_STATS: return F("Compute formula"); - case PROC_SYS_TIMER: return F("proc_system_timer()"); - case SET_NEW_TIMER: return F("setNewTimerAt()"); - case TIME_DIFF_COMPUTE: return F("timeDiff()"); - case MQTT_DELAY_QUEUE: return F("Delay queue MQTT"); - case TRY_CONNECT_HOST_TCP: return F("try_connect_host() (TCP)"); - case TRY_CONNECT_HOST_UDP: return F("try_connect_host() (UDP)"); - case HOST_BY_NAME_STATS: return F("hostByName()"); - case CONNECT_CLIENT_STATS: return F("connectClient()"); - case LOAD_CUSTOM_TASK_STATS: return F("LoadCustomTaskSettings()"); - case WIFI_ISCONNECTED_STATS: return F("WiFi.isConnected()"); - case WIFI_NOTCONNECTED_STATS: return F("WiFi.isConnected() (fail)"); - case LOAD_TASK_SETTINGS: return F("LoadTaskSettings()"); - case TRY_OPEN_FILE: return F("TryOpenFile()"); - case SPIFFS_GC_SUCCESS: return F("SPIFFS GC success"); - case SPIFFS_GC_FAIL: return F("SPIFFS GC fail"); - case RULES_PROCESSING: return F("rulesProcessing()"); - case GRAT_ARP_STATS: return F("sendGratuitousARP()"); - case BACKGROUND_TASKS: return F("backgroundtasks()"); - case HANDLE_SCHEDULER_IDLE: return F("handle_schedule() idle"); - case HANDLE_SCHEDULER_TASK: return F("handle_schedule() task"); - case C001_DELAY_QUEUE: - case C002_DELAY_QUEUE: - case C003_DELAY_QUEUE: - case C004_DELAY_QUEUE: - case C005_DELAY_QUEUE: - case C006_DELAY_QUEUE: - case C007_DELAY_QUEUE: - case C008_DELAY_QUEUE: - case C009_DELAY_QUEUE: - case C010_DELAY_QUEUE: - case C011_DELAY_QUEUE: - case C012_DELAY_QUEUE: - case C013_DELAY_QUEUE: - case C014_DELAY_QUEUE: - case C015_DELAY_QUEUE: - case C016_DELAY_QUEUE: - case C017_DELAY_QUEUE: - case C018_DELAY_QUEUE: - case C019_DELAY_QUEUE: - case C020_DELAY_QUEUE: - { - String result; - result.reserve(16); - result = F("Delay queue "); - result += get_formatted_Controller_number(static_cast(stat - C001_DELAY_QUEUE + 1)); - return result; - } - } - return getUnknownString(); -} -struct portStatusStruct { - portStatusStruct() : state(-1), output(-1), command(0), init(0), mode(0), task(0), monitor(0), forceMonitor(0), forceEvent(0), previousTask(-1), x(-1) {} - - int8_t state : 2; //-1,0,1 - int8_t output : 2; //-1,0,1 - int8_t command : 2; //0,1 - int8_t init : 2; //0,1 - - uint8_t mode : 3; //6 current values (max. 8) - uint8_t task : 2; //0-3 (max. 4) - uint8_t monitor : 1; //0,1 - uint8_t forceMonitor : 1; //0,1 - uint8_t forceEvent : 1; //0,1 - - int8_t previousTask : 8; - - int8_t x; //used to synchronize the Plugin_prt vector index (x) with the PLUGIN_ID -}; - -std::map globalMapPortStatus; @@ -1582,178 +769,6 @@ void addPredefinedRules(const GpioFactorySettingsStruct& gpio_settings); -/* ####################################################################################################### -# Supported units of measure as output type for sensor values -####################################################################################################### */ -struct UnitOfMeasure { - enum uom_t { - latitude, - longitude, - altitude, - speed, - hdop, - snr_dBHz, - }; -}; - -#ifdef USES_PACKED_RAW_DATA - -// Data types used in packed encoder. -// p_uint16_1e2 means it is a 16 bit unsigned int, but multiplied by 100 first. -// This allows to store 2 decimals of a floating point value in 8 bits, ranging from 0.00 ... 2.55 -// For example p_int24_1e6 is a 24-bit signed value, ideal to store a GPS coordinate -// with 6 decimals using only 3 bytes instead of 4 a normal float would use. -// -// PackedData_uintX_1eY = 0x11XY (X= #bytes, Y=exponent) -// PackedData_intX_1eY = 0x12XY (X= #bytes, Y=exponent) -typedef uint32_t PackedData_enum; -#define PackedData_uint8 0x1110 -#define PackedData_uint16 0x1120 -#define PackedData_uint24 0x1130 -#define PackedData_uint32 0x1140 -#define PackedData_uint8_1e3 0x1113 -#define PackedData_uint8_1e2 0x1112 -#define PackedData_uint8_1e1 0x1111 -#define PackedData_uint16_1e5 0x1125 -#define PackedData_uint16_1e4 0x1124 -#define PackedData_uint16_1e3 0x1123 -#define PackedData_uint16_1e2 0x1122 -#define PackedData_uint16_1e1 0x1121 -#define PackedData_uint24_1e6 0x1136 -#define PackedData_uint24_1e5 0x1135 -#define PackedData_uint24_1e4 0x1134 -#define PackedData_uint24_1e3 0x1133 -#define PackedData_uint24_1e2 0x1132 -#define PackedData_uint24_1e1 0x1131 -#define PackedData_uint32_1e6 0x1146 -#define PackedData_uint32_1e5 0x1145 -#define PackedData_uint32_1e4 0x1144 -#define PackedData_uint32_1e3 0x1143 -#define PackedData_uint32_1e2 0x1142 -#define PackedData_uint32_1e1 0x1141 -#define PackedData_int8 0x1210 -#define PackedData_int16 0x1220 -#define PackedData_int24 0x1230 -#define PackedData_int32 0x1240 -#define PackedData_int8_1e3 0x1213 -#define PackedData_int8_1e2 0x1212 -#define PackedData_int8_1e1 0x1211 -#define PackedData_int16_1e5 0x1225 -#define PackedData_int16_1e4 0x1224 -#define PackedData_int16_1e3 0x1223 -#define PackedData_int16_1e2 0x1222 -#define PackedData_int16_1e1 0x1221 -#define PackedData_int24_1e6 0x1236 -#define PackedData_int24_1e5 0x1235 -#define PackedData_int24_1e4 0x1234 -#define PackedData_int24_1e3 0x1233 -#define PackedData_int24_1e2 0x1232 -#define PackedData_int24_1e1 0x1231 -#define PackedData_int32_1e6 0x1246 -#define PackedData_int32_1e5 0x1245 -#define PackedData_int32_1e4 0x1244 -#define PackedData_int32_1e3 0x1243 -#define PackedData_int32_1e2 0x1242 -#define PackedData_int32_1e1 0x1241 -#define PackedData_pluginid 1 -#define PackedData_latLng 2 -#define PackedData_hdop 3 -#define PackedData_altitude 4 -#define PackedData_vcc 5 -#define PackedData_pct_8 6 - -uint8_t getPackedDataTypeSize(PackedData_enum dtype, float& factor, float& offset) { - offset = 0; - factor = 1; - if (dtype > 0x1000 && dtype < 0x12FF) { - const uint8_t exponent = dtype & 0xF; - switch(exponent) { - case 0: factor = 1; break; - case 1: factor = 1e1; break; - case 2: factor = 1e2; break; - case 3: factor = 1e3; break; - case 4: factor = 1e4; break; - case 5: factor = 1e5; break; - case 6: factor = 1e6; break; - } - const uint8_t size = (dtype >> 8) & 0xF; - return size; - } - switch (dtype) { - case PackedData_pluginid: factor = 1; return 1; - case PackedData_latLng: factor = 46600; return 3; // 2^23 / 180 - case PackedData_hdop: factor = 10; return 1; - case PackedData_altitude: factor = 4; offset = 1000; return 2; // -1000 .. 15383.75 meter - case PackedData_vcc: factor = 41.83; offset = 1; return 1; // -1 .. 5.12V - case PackedData_pct_8: factor = 2.56; return 1; // 0 .. 100% - default: - break; - } - - // Unknown type - factor = 1; - return 0; -} - -void LoRa_uintToBytes(uint64_t value, uint8_t byteSize, byte *data, uint8_t& cursor) { - // Clip values to upper limit - const uint64_t upperlimit = (1 << (8*byteSize)) - 1; - if (value > upperlimit) { value = upperlimit; } - for (uint8_t x = 0; x < byteSize; x++) { - byte next = 0; - if (sizeof(value) > x) { - next = static_cast((value >> (x * 8)) & 0xFF); - } - data[cursor] = next; - ++cursor; - } -} - -void LoRa_intToBytes(int64_t value, uint8_t byteSize, byte *data, uint8_t& cursor) { - // Clip values to lower limit - const int64_t lowerlimit = (1 << ((8*byteSize) - 1)) * -1; - if (value < lowerlimit) { value = lowerlimit; } - if (value < 0) { - value += (1 << (8*byteSize)); - } - LoRa_uintToBytes(value, byteSize, data, cursor); -} - -String LoRa_base16Encode(byte *data, size_t size) { - String output; - output.reserve(size * 2); - char buffer[3]; - for (unsigned i=0; i= LOG_LEVEL_DEBUG_MORE) { + Serial.setDebugOutput(true); + } +#endif + } + max_lvl = _max(max_lvl, Settings.SyslogLevel); + if (Logging.logActiveRead()) { + max_lvl = _max(max_lvl, Settings.WebLogLevel); + } +#ifdef FEATURE_SD + max_lvl = _max(max_lvl, Settings.SDLogLevel); +#endif + highest_active_log_level = max_lvl; +} + +bool loglevelActiveFor(byte logLevel) { + return loglevelActive(logLevel, highest_active_log_level); +} + +byte getSerialLogLevel() { + if (log_to_serial_disabled || !Settings.UseSerial) return 0; + if (wifiStatus != ESPEASY_WIFI_SERVICES_INITIALIZED){ + if (Settings.SerialLogLevel < LOG_LEVEL_INFO) { + return LOG_LEVEL_INFO; + } + } + return Settings.SerialLogLevel; +} + +byte getWebLogLevel() { + byte logLevelSettings = 0; + if (Logging.logActiveRead()) { + logLevelSettings = Settings.WebLogLevel; + } else { + if (Settings.WebLogLevel != 0) { + updateLogLevelCache(); + } + } + return logLevelSettings; +} + +bool loglevelActiveFor(byte destination, byte logLevel) { + byte logLevelSettings = 0; + switch (destination) { + case LOG_TO_SERIAL: { + logLevelSettings = getSerialLogLevel(); + break; + } + case LOG_TO_SYSLOG: { + logLevelSettings = Settings.SyslogLevel; + break; + } + case LOG_TO_WEBLOG: { + logLevelSettings = getWebLogLevel(); + break; + } + case LOG_TO_SDCARD: { + #ifdef FEATURE_SD + logLevelSettings = Settings.SDLogLevel; + #endif + break; + } + default: + return false; + } + return loglevelActive(logLevel, logLevelSettings); +} + + +bool loglevelActive(byte logLevel, byte logLevelSettings) { + return (logLevel <= logLevelSettings); +} + +void addToLog(byte logLevel, const char *line) +{ + if (loglevelActiveFor(LOG_TO_SERIAL, logLevel)) { + addToSerialBuffer(String(millis()).c_str()); + addToSerialBuffer(" : "); + addToSerialBuffer(line); + addNewlineToSerialBuffer(); + } + if (loglevelActiveFor(LOG_TO_SYSLOG, logLevel)) { + syslog(logLevel, line); + } + if (loglevelActiveFor(LOG_TO_WEBLOG, logLevel)) { + Logging.add(logLevel, line); + } + +#ifdef FEATURE_SD + if (loglevelActiveFor(LOG_TO_SDCARD, logLevel)) { + File logFile = SD.open("log.dat", FILE_WRITE); + if (logFile) + logFile.println(line); + logFile.close(); + } +#endif +} diff --git a/src/ESPEasy_buildinfo.h b/src/ESPEasy_buildinfo.h new file mode 100644 index 000000000..2ddfbd4fc --- /dev/null +++ b/src/ESPEasy_buildinfo.h @@ -0,0 +1,31 @@ +#ifndef ESPEASY_BUILD_INFO_H +#define ESPEASY_BUILD_INFO_H + + +// ******************************************************************************** +// DO NOT CHANGE ANYTHING BELOW THIS LINE +// ******************************************************************************** +#define ESP_PROJECT_PID 2016110801L + +#if defined(ESP8266) + # define VERSION 2 // config file version (not ESPEasy version). increase if you make incompatible changes to + // config system. +#endif // if defined(ESP8266) +#if defined(ESP32) + # define VERSION 3 // Change in config.dat mapping needs a full reset +#endif // if defined(ESP32) + +#define BUILD 20103 // git version 2.1.03 +#if defined(ESP8266) + # define BUILD_NOTES " - Mega" +#endif // if defined(ESP8266) +#if defined(ESP32) + # define BUILD_NOTES " - Mega32" +#endif // if defined(ESP32) + +#ifndef BUILD_GIT +# define BUILD_GIT "(custom)" +#endif // ifndef BUILD_GIT + + +#endif // ESPEASY_BUILD_INFO_H diff --git a/src/ESPEasy_common.cpp b/src/ESPEasy_common.cpp index 2b4ee815b..6778f4169 100644 --- a/src/ESPEasy_common.cpp +++ b/src/ESPEasy_common.cpp @@ -1,5 +1,8 @@ #include "ESPEasy_common.h" + +String getUnknownString() { return F("Unknown"); } + /*********************************************************************************************\ Bitwise operators \*********************************************************************************************/ diff --git a/src/ESPEasy_common.h b/src/ESPEasy_common.h index 4ef624e03..2c532d243 100644 --- a/src/ESPEasy_common.h +++ b/src/ESPEasy_common.h @@ -15,6 +15,8 @@ namespace std #define ZERO_TERMINATE(S) S[sizeof(S) - 1] = 0 +String getUnknownString(); + /*********************************************************************************************\ Bitwise operators \*********************************************************************************************/ diff --git a/src/ESPEasy_fdwdecl.h b/src/ESPEasy_fdwdecl.h index c09fb5cb1..bcd09fcdf 100644 --- a/src/ESPEasy_fdwdecl.h +++ b/src/ESPEasy_fdwdecl.h @@ -20,22 +20,35 @@ #include +struct SettingsStruct; +struct SecurityStruct; +struct CRCStruct; + +// Forward declaration to give access to global member variables +SettingsStruct& getSettings(); +SecurityStruct& getSecuritySettings(); +CRCStruct& getCRCValues(); +unsigned long& getConnectionFailures(); +byte& getHighestActiveLogLevel(); +int getPluginId_from_TaskIndex(byte taskIndex); + -// Forward declaration struct ControllerSettingsStruct; String getUnknownString(); void scheduleNextDelayQueue(unsigned long id, unsigned long nextTime); String LoadControllerSettings(int ControllerIndex, ControllerSettingsStruct& controller_settings); String get_formatted_Controller_number(int controller_index); -bool loglevelActiveFor(byte logLevel); -void addToLog(byte loglevel, const String& string); -void addToLog(byte logLevel, const __FlashStringHelper* flashString); void statusLED(boolean traffic); void backgroundtasks(); uint32_t getCurrentFreeStack(); uint32_t getFreeStackWatermark(); bool canYield(); + +boolean timeOutReached(unsigned long timer); +long timePassedSince(unsigned long timestamp); +long usecPassedSince(unsigned long timestamp); + void serialHelper_getGpioNames(struct EventStruct *event, bool rxOptional=false, bool txOptional=false); fs::File tryOpenFile(const String& fname, const String& mode); @@ -76,17 +89,6 @@ void rulesProcessing(String& event); void setIntervalTimer(unsigned long id); byte getProtocolIndex(byte Number); -#ifdef USES_PACKED_RAW_DATA -// Forward declarations PackedData related functions -typedef uint32_t PackedData_enum; -uint8_t getPackedDataTypeSize(PackedData_enum dtype, float& factor, float& offset); -void LoRa_uintToBytes(uint64_t value, uint8_t byteSize, byte *data, uint8_t& cursor); -String LoRa_base16Encode(byte *data, size_t size); -String LoRa_addInt(uint64_t value, PackedData_enum datatype); -static String LoRa_addFloat(float value, PackedData_enum datatype); -//String getPackedFromPlugin(struct EventStruct *event, uint8_t sampleSetCount); -#endif // USES_PACKED_RAW_DATA - #ifdef USES_MQTT //void runPeriodicalMQTT(); //void updateMQTTclient_connected(); @@ -99,4 +101,5 @@ bool MQTTCheck(int controller_idx); void schedule_all_tasks_using_MQTT_controller(); #endif + #endif // ESPEASY_FWD_DECL_H \ No newline at end of file diff --git a/src/ESPEasy_packed_raw_data.cpp b/src/ESPEasy_packed_raw_data.cpp new file mode 100644 index 000000000..417863fe5 --- /dev/null +++ b/src/ESPEasy_packed_raw_data.cpp @@ -0,0 +1,91 @@ +#include "ESPEasy_packed_raw_data.h" + + +uint8_t getPackedDataTypeSize(PackedData_enum dtype, float& factor, float& offset) { + offset = 0; + factor = 1; + if (dtype > 0x1000 && dtype < 0x12FF) { + const uint8_t exponent = dtype & 0xF; + switch(exponent) { + case 0: factor = 1; break; + case 1: factor = 1e1; break; + case 2: factor = 1e2; break; + case 3: factor = 1e3; break; + case 4: factor = 1e4; break; + case 5: factor = 1e5; break; + case 6: factor = 1e6; break; + } + const uint8_t size = (dtype >> 8) & 0xF; + return size; + } + switch (dtype) { + case PackedData_pluginid: factor = 1; return 1; + case PackedData_latLng: factor = 46600; return 3; // 2^23 / 180 + case PackedData_hdop: factor = 10; return 1; + case PackedData_altitude: factor = 4; offset = 1000; return 2; // -1000 .. 15383.75 meter + case PackedData_vcc: factor = 41.83; offset = 1; return 1; // -1 .. 5.12V + case PackedData_pct_8: factor = 2.56; return 1; // 0 .. 100% + default: + break; + } + + // Unknown type + factor = 1; + return 0; +} + +void LoRa_uintToBytes(uint64_t value, uint8_t byteSize, byte *data, uint8_t& cursor) { + // Clip values to upper limit + const uint64_t upperlimit = (1 << (8*byteSize)) - 1; + if (value > upperlimit) { value = upperlimit; } + for (uint8_t x = 0; x < byteSize; x++) { + byte next = 0; + if (sizeof(value) > x) { + next = static_cast((value >> (x * 8)) & 0xFF); + } + data[cursor] = next; + ++cursor; + } +} + +void LoRa_intToBytes(int64_t value, uint8_t byteSize, byte *data, uint8_t& cursor) { + // Clip values to lower limit + const int64_t lowerlimit = (1 << ((8*byteSize) - 1)) * -1; + if (value < lowerlimit) { value = lowerlimit; } + if (value < 0) { + value += (1 << (8*byteSize)); + } + LoRa_uintToBytes(value, byteSize, data, cursor); +} + +String LoRa_base16Encode(byte *data, size_t size) { + String output; + output.reserve(size * 2); + char buffer[3]; + for (unsigned i=0; i= LOG_LEVEL_DEBUG_MORE) { - Serial.setDebugOutput(true); - } -#endif - } - max_lvl = _max(max_lvl, Settings.SyslogLevel); - if (Logging.logActiveRead()) { - max_lvl = _max(max_lvl, Settings.WebLogLevel); - } -#ifdef FEATURE_SD - max_lvl = _max(max_lvl, Settings.SDLogLevel); -#endif - highest_active_log_level = max_lvl; -} - -bool loglevelActiveFor(byte logLevel) { - return loglevelActive(logLevel, highest_active_log_level); -} - -byte getSerialLogLevel() { - if (log_to_serial_disabled || !Settings.UseSerial) return 0; - if (wifiStatus != ESPEASY_WIFI_SERVICES_INITIALIZED){ - if (Settings.SerialLogLevel < LOG_LEVEL_INFO) { - return LOG_LEVEL_INFO; - } - } - return Settings.SerialLogLevel; -} - -byte getWebLogLevel() { - byte logLevelSettings = 0; - if (Logging.logActiveRead()) { - logLevelSettings = Settings.WebLogLevel; - } else { - if (Settings.WebLogLevel != 0) { - updateLogLevelCache(); - } - } - return logLevelSettings; -} - -boolean loglevelActiveFor(byte destination, byte logLevel) { - byte logLevelSettings = 0; - switch (destination) { - case LOG_TO_SERIAL: { - logLevelSettings = getSerialLogLevel(); - break; - } - case LOG_TO_SYSLOG: { - logLevelSettings = Settings.SyslogLevel; - break; - } - case LOG_TO_WEBLOG: { - logLevelSettings = getWebLogLevel(); - break; - } - case LOG_TO_SDCARD: { - #ifdef FEATURE_SD - logLevelSettings = Settings.SDLogLevel; - #endif - break; - } - default: - return false; - } - return loglevelActive(logLevel, logLevelSettings); -} - - -boolean loglevelActive(byte logLevel, byte logLevelSettings) { - return (logLevel <= logLevelSettings); -} - -void addToLog(byte logLevel, const char *line) -{ - if (loglevelActiveFor(LOG_TO_SERIAL, logLevel)) { - addToSerialBuffer(String(millis()).c_str()); - addToSerialBuffer(" : "); - addToSerialBuffer(line); - addNewlineToSerialBuffer(); - } - if (loglevelActiveFor(LOG_TO_SYSLOG, logLevel)) { - syslog(logLevel, line); - } - if (loglevelActiveFor(LOG_TO_WEBLOG, logLevel)) { - Logging.add(logLevel, line); - } - -#ifdef FEATURE_SD - if (loglevelActiveFor(LOG_TO_SDCARD, logLevel)) { - File logFile = SD.open("log.dat", FILE_WRITE); - if (logFile) - logFile.println(line); - logFile.close(); - } -#endif -} /********************************************************************************************\ diff --git a/src/Scheduler.ino b/src/Scheduler.ino index a9d424a49..2293d0c89 100644 --- a/src/Scheduler.ino +++ b/src/Scheduler.ino @@ -334,7 +334,7 @@ void process_interval_timer(unsigned long id, unsigned long lasttimer) { #endif */ - // When extending this, also extend in _CPlugin_Helper.h + // When extending this, also extend in DelayQueueElements.h // Look for DEFINE_Cxxx_DELAY_QUEUE_MACRO } } diff --git a/src/_CPlugin_Helper.cpp b/src/_CPlugin_Helper.cpp new file mode 100644 index 000000000..5bb77f59e --- /dev/null +++ b/src/_CPlugin_Helper.cpp @@ -0,0 +1,422 @@ +#include "_CPlugin_Helper.h" + +#include "ESPEasy_fdwdecl.h" +#include "ESPEasy_Log.h" +#include "ESPEasy_buildinfo.h" + +#include "DataStructs/SecurityStruct.h" +#include "DataStructs/CRCStruct.h" +#include "DataStructs/SettingsStruct.h" + +#include "DataStructs/ControllerSettingsStruct.h" +#include "DataStructs/ESPEasyLimits.h" +#include "DataStructs/TimingStats.h" + +#include +#include +#include + +bool safeReadStringUntil(Stream & input, + String & str, + char terminator, + unsigned int maxSize, + unsigned int timeout) +{ + int c; + const unsigned long start = millis(); + const unsigned long timer = start + timeout; + unsigned long backgroundtasks_timer = start + 10; + + str = ""; + + do { + // read character + if (input.available()) { + c = input.read(); + + if (c >= 0) { + // found terminator, we're ok + if (c == terminator) { + return true; + } + + // found character, add to string + else { + str += char(c); + + // string at max size? + if (str.length() >= maxSize) { + addLog(LOG_LEVEL_ERROR, F("Not enough bufferspace to read all input data!")); + return false; + } + } + } + + // We must run the backgroundtasks every now and then. + if (timeOutReached(backgroundtasks_timer)) { + backgroundtasks_timer += 10; + backgroundtasks(); + } else { + delay(0); + } + } else { + delay(0); + } + } while (!timeOutReached(timer)); + + addLog(LOG_LEVEL_ERROR, F("Timeout while reading input data!")); + return false; +} + +bool valid_controller_number(int controller_number) { + if (controller_number < 0) { return false; } + return true; + + // return getProtocolIndex(controller_number) <= protocolCount; +} + +String get_formatted_Controller_number(int controller_number) { + if (!valid_controller_number(controller_number)) { + return F("C---"); + } + String result = F("C"); + + if (controller_number < 100) { result += '0'; } + + if (controller_number < 10) { result += '0'; } + result += controller_number; + return result; +} + + +String get_auth_header(const String& user, const String& pass) { + String authHeader = ""; + + if (user.length() != 0 && pass.length() != 0) { + base64 encoder; + String auth = user; + auth += ":"; + auth += pass; + authHeader = F("Authorization: Basic "); + authHeader += encoder.encode(auth); + authHeader += F(" \r\n"); + } + return authHeader; +} + +String get_auth_header(int controller_index) { + String authHeader = ""; + + if (controller_index < CONTROLLER_MAX) { + if ((getSecuritySettings().ControllerUser[controller_index][0] != 0) && + (getSecuritySettings().ControllerPassword[controller_index][0] != 0)) + { + authHeader = get_auth_header( + String(getSecuritySettings().ControllerUser[controller_index]), + String(getSecuritySettings().ControllerPassword[controller_index])); + } + } else { + addLog(LOG_LEVEL_ERROR, F("Invalid controller index")); + } + return authHeader; +} + +String get_user_agent_request_header_field() { + static unsigned int agent_size = 20; + String request; + + request.reserve(agent_size); + request = F("User-Agent: "); + request += F("ESP Easy/"); + request += BUILD; + request += '/'; + request += String(getCRCValues().compileDate); + request += ' '; + request += String(getCRCValues().compileTime); + request += "\r\n"; + agent_size = request.length(); + return request; +} + +String do_create_http_request( + const String& hostportString, + const String& method, const String& uri, + const String& auth_header, const String& additional_options, + int content_length) { + int estimated_size = hostportString.length() + method.length() + + uri.length() + auth_header.length() + + additional_options.length() + + 42; + + if (content_length >= 0) { estimated_size += 25; } + String request; + request.reserve(estimated_size); + request += method; + request += ' '; + + if (!uri.startsWith("/")) { request += '/'; } + request += uri; + request += F(" HTTP/1.1"); + request += "\r\n"; + + if (content_length >= 0) { + request += F("Content-Length: "); + request += content_length; + request += "\r\n"; + } + request += F("Host: "); + request += hostportString; + request += "\r\n"; + request += auth_header; + request += additional_options; + request += get_user_agent_request_header_field(); + request += F("Connection: close\r\n"); + request += "\r\n"; +#ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_DEBUG, request); +#endif // ifndef BUILD_NO_DEBUG + return request; +} + +String do_create_http_request( + const String& hostportString, + const String& method, const String& uri) { + return do_create_http_request(hostportString, method, uri, + "", // auth_header + "", // additional_options + -1 // content_length + ); +} + +String do_create_http_request( + int controller_number, ControllerSettingsStruct& ControllerSettings, + const String& method, const String& uri, + int content_length) { + const bool defaultport = ControllerSettings.Port == 0 || ControllerSettings.Port == 80; + + return do_create_http_request( + defaultport ? ControllerSettings.getHost() : ControllerSettings.getHostPortString(), + method, + uri, + "", // auth_header + "", // additional_options + content_length); +} + +String create_http_request_auth( + int controller_number, int controller_index, ControllerSettingsStruct& ControllerSettings, + const String& method, const String& uri, + int content_length) { + const bool defaultport = ControllerSettings.Port == 0 || ControllerSettings.Port == 80; + + return do_create_http_request( + defaultport ? ControllerSettings.getHost() : ControllerSettings.getHostPortString(), + method, + uri, + get_auth_header(controller_index), + "", // additional_options + content_length); +} + +String create_http_get_request(int controller_number, ControllerSettingsStruct& ControllerSettings, + const String& uri) { + return do_create_http_request(controller_number, ControllerSettings, F("GET"), uri, -1); +} + +String create_http_request_auth(int controller_number, int controller_index, ControllerSettingsStruct& ControllerSettings, + const String& method, const String& uri) { + return create_http_request_auth(controller_number, controller_index, ControllerSettings, method, uri, -1); +} + +#ifndef BUILD_NO_DEBUG +void log_connecting_to(const String& prefix, int controller_number, ControllerSettingsStruct& ControllerSettings) { + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + String log = prefix; + log += get_formatted_Controller_number(controller_number); + log += F(" connecting to "); + log += ControllerSettings.getHostPortString(); + addLog(LOG_LEVEL_DEBUG, log); + } +} + +#endif // ifndef BUILD_NO_DEBUG + +void log_connecting_fail(const String& prefix, int controller_number, ControllerSettingsStruct& ControllerSettings) { + if (loglevelActiveFor(LOG_LEVEL_ERROR)) { + String log = prefix; + log += get_formatted_Controller_number(controller_number); + log += F(" connection failed ("); + log += getConnectionFailures(); + log += F("/"); + log += getSettings().ConnectionFailuresThreshold; + log += F(")"); + addLog(LOG_LEVEL_ERROR, log); + } +} + +bool count_connection_results(bool success, const String& prefix, int controller_number, ControllerSettingsStruct& ControllerSettings) { + if (!success) + { + getConnectionFailures()++; + log_connecting_fail(prefix, controller_number, ControllerSettings); + return false; + } + statusLED(true); + + if (getConnectionFailures()) { + getConnectionFailures()--; + } + return true; +} + +bool try_connect_host(int controller_number, WiFiUDP& client, ControllerSettingsStruct& ControllerSettings) { + //START_TIMER; // FIXME TD-er Timingstats macros currently not callable from .cpp file + if (!WiFiConnected()) return false; + client.setTimeout(ControllerSettings.ClientTimeout); +#ifndef BUILD_NO_DEBUG + log_connecting_to(F("UDP : "), controller_number, ControllerSettings); +#endif // ifndef BUILD_NO_DEBUG + bool success = ControllerSettings.beginPacket(client) != 0; + const bool result = count_connection_results( + success, + F("UDP : "), controller_number, ControllerSettings); + //STOP_TIMER(TRY_CONNECT_HOST_UDP); + return result; +} + +bool try_connect_host(int controller_number, WiFiClient& client, ControllerSettingsStruct& ControllerSettings) { + //START_TIMER; // FIXME TD-er Timingstats macros currently not callable from .cpp file + if (!WiFiConnected()) return false; + + // Use WiFiClient class to create TCP connections + client.setTimeout(ControllerSettings.ClientTimeout); +#ifndef BUILD_NO_DEBUG + log_connecting_to(F("HTTP : "), controller_number, ControllerSettings); +#endif // ifndef BUILD_NO_DEBUG + bool success = ControllerSettings.connectToHost(client); + const bool result = count_connection_results( + success, + F("HTTP : "), controller_number, ControllerSettings); + //STOP_TIMER(TRY_CONNECT_HOST_TCP); + return result; +} + +// Use "client.available() || client.connected()" to read all lines from slow servers. +// See: https://github.com/esp8266/Arduino/pull/5113 +// https://github.com/esp8266/Arduino/pull/1829 +bool client_available(WiFiClient& client) { + delay(0); + return client.available() || client.connected(); +} + +bool send_via_http(const String& logIdentifier, WiFiClient& client, const String& postStr, bool must_check_reply) { + bool success = !must_check_reply; + + // This will send the request to the server + byte written = client.print(postStr); + + // as of 2018/11/01 the print function only returns one byte (upd to 256 chars sent). However if the string sent can be longer than this + // therefore we calculate modulo 256. + // see discussion here https://github.com/letscontrolit/ESPEasy/pull/1979 + // and implementation here + // https://github.com/esp8266/Arduino/blob/561426c0c77e9d05708f2c4bf2a956d3552a3706/libraries/ESP8266WiFi/src/include/ClientContext.h#L437-L467 + // this needs to be adjusted if the WiFiClient.print method changes. + if (written != (postStr.length() % 256)) { + if (loglevelActiveFor(LOG_LEVEL_ERROR)) { + String log = F("HTTP : "); + log += logIdentifier; + log += F(" Error: could not write to client ("); + log += written; + log += "/"; + log += postStr.length(); + log += ")"; + addLog(LOG_LEVEL_ERROR, log); + } + success = false; + } +#ifndef BUILD_NO_DEBUG + else { + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + String log = F("HTTP : "); + log += logIdentifier; + log += F(" written to client ("); + log += written; + log += "/"; + log += postStr.length(); + log += ")"; + addLog(LOG_LEVEL_DEBUG, log); + } + } +#endif // ifndef BUILD_NO_DEBUG + + if (must_check_reply) { + unsigned long timer = millis() + 200; + + while (!client_available(client)) { + if (timeOutReached(timer)) { return false; } + delay(1); + } + + // Read all the lines of the reply from server and print them to Serial + while (client_available(client) && !success) { + // String line = client.readStringUntil('\n'); + String line; + safeReadStringUntil(client, line, '\n'); + +#ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) { + if (line.length() > 80) { + addLog(LOG_LEVEL_DEBUG_MORE, line.substring(0, 80)); + } else { + addLog(LOG_LEVEL_DEBUG_MORE, line); + } + } +#endif // ifndef BUILD_NO_DEBUG + + if (line.startsWith(F("HTTP/1.1 2"))) + { + success = true; + + // Leave this debug info in the build, regardless of the + // BUILD_NO_DEBUG flags. + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + String log = F("HTTP : "); + log += logIdentifier; + log += F(" Success! "); + log += line; + addLog(LOG_LEVEL_DEBUG, log); + } + } else if (line.startsWith(F("HTTP/1.1 4"))) { + if (loglevelActiveFor(LOG_LEVEL_ERROR)) { + String log = F("HTTP : "); + log += logIdentifier; + log += F(" Error: "); + log += line; + addLog(LOG_LEVEL_ERROR, log); + } +#ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_DEBUG_MORE, postStr); +#endif // ifndef BUILD_NO_DEBUG + } + delay(0); + } + } +#ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + String log = F("HTTP : "); + log += logIdentifier; + log += F(" closing connection"); + addLog(LOG_LEVEL_DEBUG, log); + } +#endif // ifndef BUILD_NO_DEBUG + + client.flush(); + client.stop(); + return success; +} + +bool send_via_http(int controller_number, WiFiClient& client, const String& postStr, bool must_check_reply) { + return send_via_http(get_formatted_Controller_number(controller_number), client, postStr, must_check_reply); +} \ No newline at end of file diff --git a/src/_CPlugin_Helper.h b/src/_CPlugin_Helper.h index 1911447ff..3eb64b8b6 100644 --- a/src/_CPlugin_Helper.h +++ b/src/_CPlugin_Helper.h @@ -1,642 +1,11 @@ #ifndef CPLUGIN_HELPER_H #define CPLUGIN_HELPER_H CPLUGIN_HELPER_H -#include "DataStructs/ControllerSettingsStruct.h" -#include "ESPEasy_fdwdecl.h" - -// These element classes should be defined as class, to be used as template. - -/*********************************************************************************************\ -* MQTT_queue_element for all MQTT base controllers -\*********************************************************************************************/ -class MQTT_queue_element { -public: - - MQTT_queue_element() : controller_idx(0), _retained(false) {} - - MQTT_queue_element(int ctrl_idx, - const String& topic, const String& payload, boolean retained) : - controller_idx(ctrl_idx), _topic(topic), _payload(payload), _retained(retained) - {} - - size_t getSize() const { - return sizeof(this) + _topic.length() + _payload.length(); - } - - int controller_idx; - String _topic; - String _payload; - boolean _retained; -}; - -/*********************************************************************************************\ -* Simple queue element, only storing controller index and some String -\*********************************************************************************************/ -class simple_queue_element_string_only { -public: - - simple_queue_element_string_only() : controller_idx(0) {} - - simple_queue_element_string_only(int ctrl_idx, const String& req) : - controller_idx(ctrl_idx), txt(req) {} - - size_t getSize() const { - return sizeof(this) + txt.length(); - } - - int controller_idx; - String txt; -}; - - -//#ifdef USES_C001 -/*********************************************************************************************\ -* C001_queue_element for queueing requests for C001. -\*********************************************************************************************/ -#define C001_queue_element simple_queue_element_string_only -//#endif //USES_C001 - -//#ifdef USES_C003 -/*********************************************************************************************\ -* C003_queue_element for queueing requests for C003 Nodo Telnet. -\*********************************************************************************************/ -#define C003_queue_element simple_queue_element_string_only -//#endif //USES_C003 - -//#ifdef USES_C004 -/*********************************************************************************************\ -* C004_queue_element for queueing requests for C004 ThingSpeak. -* Typical use case for Thingspeak is to only send values every N seconds/minutes. -* So we just store everything needed to recreate the event when the time is ready. -\*********************************************************************************************/ -class C004_queue_element { -public: - - C004_queue_element() : controller_idx(0), TaskIndex(0), idx(0), sensorType(0) {} - - C004_queue_element(const struct EventStruct *event) : - controller_idx(event->ControllerIndex), - TaskIndex(event->TaskIndex), - idx(event->idx), - sensorType(event->sensorType) { - if (sensorType == SENSOR_TYPE_STRING) { - txt = event->String2; - } - } - - size_t getSize() const { - return sizeof(this) + txt.length(); - } - - int controller_idx; - byte TaskIndex; - int idx; - byte sensorType; - String txt; -}; -//#endif //USES_C004 - -//#ifdef USES_C007 -/*********************************************************************************************\ -* C007_queue_element for queueing requests for C007 Emoncms -\*********************************************************************************************/ -class C007_queue_element { -public: - - C007_queue_element() : controller_idx(0), TaskIndex(0), idx(0), sensorType(0) {} - - C007_queue_element(const struct EventStruct *event) : - controller_idx(event->ControllerIndex), - TaskIndex(event->TaskIndex), - idx(event->idx), - sensorType(event->sensorType) {} - - size_t getSize() const { - return sizeof(this); - } - - int controller_idx; - byte TaskIndex; - int idx; - byte sensorType; -}; -//#endif //USES_C007 - -/*********************************************************************************************\ -* Base class for controllers that only send a single value per request and thus needs to -* keep track of the number of values already sent. -\*********************************************************************************************/ -class queue_element_single_value_base { -public: - - queue_element_single_value_base() : controller_idx(0), TaskIndex(0), idx(0), valuesSent(0) {} - - queue_element_single_value_base(const struct EventStruct *event, byte value_count) : - controller_idx(event->ControllerIndex), - TaskIndex(event->TaskIndex), - idx(event->idx), - valuesSent(0), - valueCount(value_count) {} - - bool checkDone(bool succesfull) const { - if (succesfull) { ++valuesSent; } - return valuesSent >= valueCount || valuesSent >= VARS_PER_TASK; - } - - size_t getSize() const { - size_t total = sizeof(this); - - for (int i = 0; i < VARS_PER_TASK; ++i) { - total += txt[i].length(); - } - return total; - } - - String txt[VARS_PER_TASK]; - int controller_idx; - byte TaskIndex; - int idx; - mutable byte valuesSent; // Value must be set by const function checkDone() - byte valueCount; -}; - - -//#ifdef USES_C008 -/*********************************************************************************************\ -* C008_queue_element for queueing requests for 008: Generic HTTP -* Using queue_element_single_value_base -\*********************************************************************************************/ -#define C008_queue_element queue_element_single_value_base -//#endif //USES_C008 - -//#ifdef USES_C009 -/*********************************************************************************************\ -* C009_queue_element for queueing requests for C009: FHEM HTTP. -\*********************************************************************************************/ -class C009_queue_element { -public: - - C009_queue_element() : controller_idx(0), TaskIndex(0), idx(0), sensorType(0) {} - - C009_queue_element(const struct EventStruct *event) : - controller_idx(event->ControllerIndex), - TaskIndex(event->TaskIndex), - idx(event->idx), - sensorType(event->sensorType) {} - - size_t getSize() const { - size_t total = sizeof(this); - - for (int i = 0; i < VARS_PER_TASK; ++i) { - total += txt[i].length(); - } - return total; - } - - String txt[VARS_PER_TASK]; - int controller_idx; - byte TaskIndex; - int idx; - byte sensorType; -}; -//#endif //USES_C009 - - -//#ifdef USES_C010 -/*********************************************************************************************\ -* C010_queue_element for queueing requests for 010: Generic UDP -* Using queue_element_single_value_base -\*********************************************************************************************/ -#define C010_queue_element queue_element_single_value_base -//#endif //USES_C010 - -//#ifdef USES_C011 -/*********************************************************************************************\ -* C011_queue_element for queueing requests for 011: Generic HTTP Advanced -\*********************************************************************************************/ -#define C011_queue_element simple_queue_element_string_only -//#endif //USES_C011 - -//#ifdef USES_C012 -/*********************************************************************************************\ -* C012_queue_element for queueing requests for 012: Blynk -* Using queue_element_single_value_base -\*********************************************************************************************/ -#define C012_queue_element queue_element_single_value_base -//#endif //USES_C012 - -//#ifdef USES_C015 -/*********************************************************************************************\ -* C015_queue_element for queueing requests for 015: Blynk -* Using queue_element_single_value_base -\*********************************************************************************************/ - -// #define C015_queue_element queue_element_single_value_base -class C015_queue_element { -public: - - C015_queue_element() : controller_idx(0), TaskIndex(0), idx(0), valuesSent(0) {} - - C015_queue_element(const struct EventStruct *event, byte value_count) : - controller_idx(event->ControllerIndex), - TaskIndex(event->TaskIndex), - idx(event->idx), - valuesSent(0), - valueCount(value_count) {} - - bool checkDone(bool succesfull) const { - if (succesfull) { ++valuesSent; } - return valuesSent >= valueCount || valuesSent >= VARS_PER_TASK; - } - - size_t getSize() const { - size_t total = sizeof(this); - - for (int i = 0; i < VARS_PER_TASK; ++i) { - total += txt[i].length(); - } - return total; - } - - String txt[VARS_PER_TASK]; - int vPin[VARS_PER_TASK] = {0}; - int controller_idx; - byte TaskIndex; - int idx; - mutable byte valuesSent; // Value must be set by const function checkDone() - byte valueCount; -}; -//#endif //USES_C015 - -//#ifdef USES_C016 -/*********************************************************************************************\ -* C016_queue_element for queueing requests for C016: Cached HTTP. -\*********************************************************************************************/ -class C016_queue_element { -public: - - C016_queue_element() : timestamp(0), controller_idx(0), TaskIndex(0), sensorType(0) {} - - C016_queue_element(const struct EventStruct *event, byte value_count, unsigned long unixTime) : - timestamp(unixTime), - controller_idx(event->ControllerIndex), - TaskIndex(event->TaskIndex), - sensorType(event->sensorType), - valueCount(value_count) - { - const byte BaseVarIndex = TaskIndex * VARS_PER_TASK; - - for (byte i = 0; i < VARS_PER_TASK; ++i) { - if (i < value_count) { - values[i] = UserVar[BaseVarIndex + i]; - } else { - values[i] = 0.0; - } - } - } - - size_t getSize() const { - return sizeof(this); - } - - float values[VARS_PER_TASK]; - unsigned long timestamp; // Unix timestamp - byte controller_idx; - byte TaskIndex; - byte sensorType; - byte valueCount; -}; - -//#endif //USES_C016 - -//#ifdef USES_C017 -/*********************************************************************************************\ -* C017_queue_element for queueing requests for C017: Zabbix Trapper Protocol. -\*********************************************************************************************/ -class C017_queue_element { -public: - - C017_queue_element() : controller_idx(0), TaskIndex(0), idx(0), sensorType(0) {} - - C017_queue_element(const struct EventStruct *event) : - controller_idx(event->ControllerIndex), - TaskIndex(event->TaskIndex), - idx(event->idx), - sensorType(event->sensorType) {} - - size_t getSize() const { - size_t total = sizeof(this); - - for (int i = 0; i < VARS_PER_TASK; ++i) { - total += txt[i].length(); - } - return total; - } - - String txt[VARS_PER_TASK]; - int controller_idx; - byte TaskIndex; - int idx; - byte sensorType; -}; -//#endif //USES_C017 - -//#ifdef USES_C018 -/*********************************************************************************************\ -* C018_queue_element for queueing requests for C018: TTN/RN2483 -\*********************************************************************************************/ - -#ifdef USES_PACKED_RAW_DATA -String getPackedFromPlugin(struct EventStruct *event, uint8_t sampleSetCount); -#endif // USES_PACKED_RAW_DATA - - -class C018_queue_element { -public: - - C018_queue_element() {} - - C018_queue_element(struct EventStruct *event, uint8_t sampleSetCount) : - controller_idx(event->ControllerIndex) - { - #ifdef USES_PACKED_RAW_DATA - packed = getPackedFromPlugin(event, sampleSetCount); - #endif // USES_PACKED_RAW_DATA - } - - size_t getSize() const { - return sizeof(this) + packed.length(); - } - - int controller_idx = 0; - String packed; -}; - -//#endif //USES_C018 - -/*********************************************************************************************\ -* ControllerDelayHandlerStruct -\*********************************************************************************************/ -template -struct ControllerDelayHandlerStruct { - ControllerDelayHandlerStruct() : - lastSend(0), - minTimeBetweenMessages(CONTROLLER_DELAY_QUEUE_DELAY_DFLT), - max_queue_depth(CONTROLLER_DELAY_QUEUE_DEPTH_DFLT), - attempt(0), - max_retries(CONTROLLER_DELAY_QUEUE_RETRY_DFLT), - delete_oldest(false), - must_check_reply(false) {} - - void configureControllerSettings(const ControllerSettingsStruct& settings) { - minTimeBetweenMessages = settings.MinimalTimeBetweenMessages; - max_queue_depth = settings.MaxQueueDepth; - max_retries = settings.MaxRetry; - delete_oldest = settings.DeleteOldest; - must_check_reply = settings.MustCheckReply; - - // Set some sound limits when not configured - if (max_queue_depth == 0) { max_queue_depth = CONTROLLER_DELAY_QUEUE_DEPTH_DFLT; } - - if (max_retries == 0) { max_retries = CONTROLLER_DELAY_QUEUE_RETRY_DFLT; } - - if (minTimeBetweenMessages == 0) { minTimeBetweenMessages = CONTROLLER_DELAY_QUEUE_DELAY_DFLT; } - - // No less than 10 msec between messages. - if (minTimeBetweenMessages < 10) { minTimeBetweenMessages = 10; } - } - - bool queueFull(const T& element) const { - if (sendQueue.size() >= max_queue_depth) { return true; } - - // Number of elements is not exceeding the limit, check memory - int freeHeap = ESP.getFreeHeap(); - - if (freeHeap > 5000) { return false; // Memory is not an issue. - } -#ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - String log = "Controller-"; - log += element.controller_idx + 1; - log += " : Memory used: "; - log += getQueueMemorySize(); - log += " bytes "; - log += sendQueue.size(); - log += " items "; - log += freeHeap; - log += " free"; - addLog(LOG_LEVEL_DEBUG, log); - } -#endif // ifndef BUILD_NO_DEBUG - return true; - } - - // Try to add to the queue, if permitted by "delete_oldest" - // Return false when no item was added. - bool addToQueue(const T& element) { - if (delete_oldest) { - // Force add to the queue. - // If max buffer is reached, the oldest in the queue (first to be served) will be removed. - while (queueFull(element)) { - sendQueue.pop_front(); - } - sendQueue.emplace_back(element); - return true; - } - - if (!queueFull(element)) { - sendQueue.emplace_back(element); - return true; - } -#ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - String log = get_formatted_Controller_number(element.controller_idx); - log += " : queue full"; - addLog(LOG_LEVEL_DEBUG, log); - } -#endif // ifndef BUILD_NO_DEBUG - return false; - } - - // Get the next element. - // Remove front element when max_retries is reached. - T* getNext() { - if (sendQueue.empty()) { return NULL; } - - if (attempt > max_retries) { - sendQueue.pop_front(); - attempt = 0; - - if (sendQueue.empty()) { return NULL; } - } - return &sendQueue.front(); - } - - // Mark as processed and return time to schedule for next process. - // Return 0 when nothing to process. - // @param remove_from_queue indicates whether the elements should be removed from the queue. - unsigned long markProcessed(bool remove_from_queue) { - if (sendQueue.empty()) { return 0; } - - if (remove_from_queue) { - sendQueue.pop_front(); - attempt = 0; - } else { - ++attempt; - } - lastSend = millis(); - return getNextScheduleTime(); - } - - unsigned long getNextScheduleTime() const { - if (sendQueue.empty()) { return 0; } - unsigned long nextTime = lastSend + minTimeBetweenMessages; - - if (timePassedSince(nextTime) > 0) { - nextTime = millis(); - } - - if (nextTime == 0) { nextTime = 1; // Just to make sure it will be executed - } - return nextTime; - } - - size_t getQueueMemorySize() const { - size_t totalSize = 0; - - for (auto it = sendQueue.begin(); it != sendQueue.end(); ++it) { - totalSize += it->getSize(); - } - return totalSize; - } - - std::list sendQueue; - unsigned long lastSend; - unsigned int minTimeBetweenMessages; - byte max_queue_depth; - byte attempt; - byte max_retries; - bool delete_oldest; - bool must_check_reply; -}; - -ControllerDelayHandlerStruct MQTTDelayHandler; - -// Uncrustify must not be used on macros, so turn it off. -// *INDENT-OFF* - - -// This macro defines the code needed to create the 'process_c##NNN####M##_delay_queue()' -// function and all needed objects and forward declarations. -// It is a macro to prevent common typo errors. -// This function will perform the (re)scheduling and mark if it is processed (and can be removed) -// The controller itself must implement the 'do_process_c004_delay_queue' function to actually -// send the data. -// Its return value must state whether it can be marked 'Processed'. -// N.B. some controllers only can send one value per iteration, so a returned "false" can mean it -// was still successful. The controller should keep track of the last value sent -// in the element stored in the queue. -#define DEFINE_Cxxx_DELAY_QUEUE_MACRO(NNN, M) \ - ControllerDelayHandlerStructC##NNN####M##_DelayHandler; \ - bool do_process_c##NNN####M##_delay_queue(int controller_number, \ - const C##NNN####M##_queue_element & element, \ - ControllerSettingsStruct & ControllerSettings); \ - void process_c##NNN####M##_delay_queue() { \ - C##NNN####M##_queue_element *element(C##NNN####M##_DelayHandler.getNext()); \ - if (element == NULL) return; \ - MakeControllerSettings (ControllerSettings); \ - LoadControllerSettings(element->controller_idx, ControllerSettings); \ - C##NNN####M##_DelayHandler.configureControllerSettings(ControllerSettings); \ - if (!WiFiConnected(10)) { \ - scheduleNextDelayQueue(TIMER_C##NNN####M##_DELAY_QUEUE, C##NNN####M##_DelayHandler.getNextScheduleTime()); \ - return; \ - } \ - START_TIMER; \ - C##NNN####M##_DelayHandler.markProcessed(do_process_c##NNN####M##_delay_queue(M, *element, ControllerSettings)); \ - STOP_TIMER(C##NNN####M##_DELAY_QUEUE); \ - scheduleNextDelayQueue(TIMER_C##NNN####M##_DELAY_QUEUE, C##NNN####M##_DelayHandler.getNextScheduleTime()); \ - } - -// Define the function wrappers to handle the calling to Cxxx_DelayHandler etc. -// If someone knows how to add leading zeros in macros, please be my guest :) -#ifdef USES_C001 -DEFINE_Cxxx_DELAY_QUEUE_MACRO(00, 1) -#endif // ifdef USES_C001 -#ifdef USES_C003 -DEFINE_Cxxx_DELAY_QUEUE_MACRO(00, 3) -#endif // ifdef USES_C003 -#ifdef USES_C004 -DEFINE_Cxxx_DELAY_QUEUE_MACRO(00, 4) -#endif // ifdef USES_C004 -#ifdef USES_C007 -DEFINE_Cxxx_DELAY_QUEUE_MACRO(00, 7) -#endif // ifdef USES_C007 -#ifdef USES_C008 -DEFINE_Cxxx_DELAY_QUEUE_MACRO(00, 8) -#endif // ifdef USES_C008 -#ifdef USES_C009 -DEFINE_Cxxx_DELAY_QUEUE_MACRO(00, 9) -#endif // ifdef USES_C009 -#ifdef USES_C010 -DEFINE_Cxxx_DELAY_QUEUE_MACRO(0, 10) -#endif // ifdef USES_C010 -#ifdef USES_C011 -DEFINE_Cxxx_DELAY_QUEUE_MACRO(0, 11) -#endif // ifdef USES_C011 -#ifdef USES_C012 -DEFINE_Cxxx_DELAY_QUEUE_MACRO(0, 12) -#endif // ifdef USES_C012 - -/* - #ifdef USES_C013 - DEFINE_Cxxx_DELAY_QUEUE_MACRO(0, 13) - #endif - */ - -/* - #ifdef USES_C014 - DEFINE_Cxxx_DELAY_QUEUE_MACRO(0, 14) - #endif - */ -#ifdef USES_C015 -DEFINE_Cxxx_DELAY_QUEUE_MACRO(0, 15) -#endif // ifdef USES_C015 - -#ifdef USES_C016 -DEFINE_Cxxx_DELAY_QUEUE_MACRO(0, 16) -#endif // ifdef USES_C016 - - -#ifdef USES_C017 -DEFINE_Cxxx_DELAY_QUEUE_MACRO(0, 17) -#endif // ifdef USES_C017 - -#ifdef USES_C018 -DEFINE_Cxxx_DELAY_QUEUE_MACRO(0, 18) -#endif - - -/* - #ifdef USES_C019 - DEFINE_Cxxx_DELAY_QUEUE_MACRO(0, 19) - #endif - */ - -/* - #ifdef USES_C020 - DEFINE_Cxxx_DELAY_QUEUE_MACRO(0, 20) - #endif - */ - - -// When extending this, also extend in Scheduler.ino: -// void process_interval_timer(unsigned long id, unsigned long lasttimer) - -// Uncrustify must not be used on macros, but we're now done, so turn Uncrustify on again. -// *INDENT-ON* +#include +struct ControllerSettingsStruct; +class WiFiUDP; +class WiFiClient; /*********************************************************************************************\ * Helper functions used in a number of controllers @@ -645,405 +14,64 @@ bool safeReadStringUntil(Stream & input, String & str, char terminator, unsigned int maxSize = 1024, - unsigned int timeout = 1000) -{ - int c; - const unsigned long start = millis(); - const unsigned long timer = start + timeout; - unsigned long backgroundtasks_timer = start + 10; + unsigned int timeout = 1000); - str = ""; +bool valid_controller_number(int controller_number); - do { - // read character - if (input.available()) { - c = input.read(); +String get_formatted_Controller_number(int controller_number); - if (c >= 0) { - // found terminator, we're ok - if (c == terminator) { - return true; - } +String get_auth_header(const String& user, const String& pass); - // found character, add to string - else { - str += char(c); +String get_auth_header(int controller_index); - // string at max size? - if (str.length() >= maxSize) { - addLog(LOG_LEVEL_ERROR, F("Not enough bufferspace to read all input data!")); - return false; - } - } - } - - // We must run the backgroundtasks every now and then. - if (timeOutReached(backgroundtasks_timer)) { - backgroundtasks_timer += 10; - backgroundtasks(); - } else { - delay(0); - } - } else { - delay(0); - } - } while (!timeOutReached(timer)); - - addLog(LOG_LEVEL_ERROR, F("Timeout while reading input data!")); - return false; -} - -bool valid_controller_number(int controller_number) { - if (controller_number < 0) { return false; } - return true; - - // return getProtocolIndex(controller_number) <= protocolCount; -} - -String get_formatted_Controller_number(int controller_number) { - if (!valid_controller_number(controller_number)) { - return F("C---"); - } - String result = F("C"); - - if (controller_number < 100) { result += '0'; } - - if (controller_number < 10) { result += '0'; } - result += controller_number; - return result; -} - - -String get_auth_header(const String& user, const String& pass) { - String authHeader = ""; - - if (user.length() != 0 && pass.length() != 0) { - base64 encoder; - String auth = user; - auth += ":"; - auth += pass; - authHeader = F("Authorization: Basic "); - authHeader += encoder.encode(auth); - authHeader += F(" \r\n"); - } - return authHeader; -} - -String get_auth_header(int controller_index) { - String authHeader = ""; - - if (controller_index < CONTROLLER_MAX) { - if ((SecuritySettings.ControllerUser[controller_index][0] != 0) && - (SecuritySettings.ControllerPassword[controller_index][0] != 0)) - { - authHeader = get_auth_header( - String(SecuritySettings.ControllerUser[controller_index]), - String(SecuritySettings.ControllerPassword[controller_index])); - } - } else { - addLog(LOG_LEVEL_ERROR, F("Invalid controller index")); - } - return authHeader; -} - -String get_user_agent_request_header_field() { - static unsigned int agent_size = 20; - String request; - - request.reserve(agent_size); - request = F("User-Agent: "); - request += F("ESP Easy/"); - request += BUILD; - request += '/'; - request += String(CRCValues.compileDate); - request += ' '; - request += String(CRCValues.compileTime); - request += "\r\n"; - agent_size = request.length(); - return request; -} +String get_user_agent_request_header_field(); String do_create_http_request( const String& hostportString, const String& method, const String& uri, const String& auth_header, const String& additional_options, - int content_length) { - int estimated_size = hostportString.length() + method.length() - + uri.length() + auth_header.length() - + additional_options.length() - + 42; - - if (content_length >= 0) { estimated_size += 25; } - String request; - request.reserve(estimated_size); - request += method; - request += ' '; - - if (!uri.startsWith("/")) { request += '/'; } - request += uri; - request += F(" HTTP/1.1"); - request += "\r\n"; - - if (content_length >= 0) { - request += F("Content-Length: "); - request += content_length; - request += "\r\n"; - } - request += F("Host: "); - request += hostportString; - request += "\r\n"; - request += auth_header; - request += additional_options; - request += get_user_agent_request_header_field(); - request += F("Connection: close\r\n"); - request += "\r\n"; -#ifndef BUILD_NO_DEBUG - addLog(LOG_LEVEL_DEBUG, request); -#endif // ifndef BUILD_NO_DEBUG - return request; -} + int content_length); String do_create_http_request( const String& hostportString, - const String& method, const String& uri) { - return do_create_http_request(hostportString, method, uri, - "", // auth_header - "", // additional_options - -1 // content_length - ); -} + const String& method, const String& uri); String do_create_http_request( int controller_number, ControllerSettingsStruct& ControllerSettings, const String& method, const String& uri, - int content_length) { - const bool defaultport = ControllerSettings.Port == 0 || ControllerSettings.Port == 80; - - return do_create_http_request( - defaultport ? ControllerSettings.getHost() : ControllerSettings.getHostPortString(), - method, - uri, - "", // auth_header - "", // additional_options - content_length); -} + int content_length); String create_http_request_auth( int controller_number, int controller_index, ControllerSettingsStruct& ControllerSettings, const String& method, const String& uri, - int content_length) { - const bool defaultport = ControllerSettings.Port == 0 || ControllerSettings.Port == 80; - - return do_create_http_request( - defaultport ? ControllerSettings.getHost() : ControllerSettings.getHostPortString(), - method, - uri, - get_auth_header(controller_index), - "", // additional_options - content_length); -} + int content_length); String create_http_get_request(int controller_number, ControllerSettingsStruct& ControllerSettings, - const String& uri) { - return do_create_http_request(controller_number, ControllerSettings, F("GET"), uri, -1); -} + const String& uri); String create_http_request_auth(int controller_number, int controller_index, ControllerSettingsStruct& ControllerSettings, - const String& method, const String& uri) { - return create_http_request_auth(controller_number, controller_index, ControllerSettings, method, uri, -1); -} + const String& method, const String& uri); #ifndef BUILD_NO_DEBUG -void log_connecting_to(const String& prefix, int controller_number, ControllerSettingsStruct& ControllerSettings) { - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - String log = prefix; - log += get_formatted_Controller_number(controller_number); - log += F(" connecting to "); - log += ControllerSettings.getHostPortString(); - addLog(LOG_LEVEL_DEBUG, log); - } -} - +void log_connecting_to(const String& prefix, int controller_number, ControllerSettingsStruct& ControllerSettings); #endif // ifndef BUILD_NO_DEBUG -void log_connecting_fail(const String& prefix, int controller_number, ControllerSettingsStruct& ControllerSettings) { - if (loglevelActiveFor(LOG_LEVEL_ERROR)) { - String log = prefix; - log += get_formatted_Controller_number(controller_number); - log += F(" connection failed ("); - log += connectionFailures; - log += F("/"); - log += Settings.ConnectionFailuresThreshold; - log += F(")"); - addLog(LOG_LEVEL_ERROR, log); - } -} +void log_connecting_fail(const String& prefix, int controller_number, ControllerSettingsStruct& ControllerSettings); -bool count_connection_results(bool success, const String& prefix, int controller_number, ControllerSettingsStruct& ControllerSettings) { - if (!success) - { - connectionFailures++; - log_connecting_fail(prefix, controller_number, ControllerSettings); - return false; - } - statusLED(true); +bool count_connection_results(bool success, const String& prefix, int controller_number, ControllerSettingsStruct& ControllerSettings); - if (connectionFailures) { - connectionFailures--; - } - return true; -} +bool try_connect_host(int controller_number, WiFiUDP& client, ControllerSettingsStruct& ControllerSettings); -bool try_connect_host(int controller_number, WiFiUDP& client, ControllerSettingsStruct& ControllerSettings) { - START_TIMER; - if (!WiFiConnected()) return false; - client.setTimeout(ControllerSettings.ClientTimeout); -#ifndef BUILD_NO_DEBUG - log_connecting_to(F("UDP : "), controller_number, ControllerSettings); -#endif // ifndef BUILD_NO_DEBUG - bool success = ControllerSettings.beginPacket(client) != 0; - const bool result = count_connection_results( - success, - F("UDP : "), controller_number, ControllerSettings); - STOP_TIMER(TRY_CONNECT_HOST_UDP); - return result; -} - -bool try_connect_host(int controller_number, WiFiClient& client, ControllerSettingsStruct& ControllerSettings) { - START_TIMER; - if (!WiFiConnected()) return false; - - // Use WiFiClient class to create TCP connections - client.setTimeout(ControllerSettings.ClientTimeout); -#ifndef BUILD_NO_DEBUG - log_connecting_to(F("HTTP : "), controller_number, ControllerSettings); -#endif // ifndef BUILD_NO_DEBUG - bool success = ControllerSettings.connectToHost(client); - const bool result = count_connection_results( - success, - F("HTTP : "), controller_number, ControllerSettings); - STOP_TIMER(TRY_CONNECT_HOST_TCP); - return result; -} +bool try_connect_host(int controller_number, WiFiClient& client, ControllerSettingsStruct& ControllerSettings); // Use "client.available() || client.connected()" to read all lines from slow servers. // See: https://github.com/esp8266/Arduino/pull/5113 // https://github.com/esp8266/Arduino/pull/1829 -bool client_available(WiFiClient& client) { - delay(0); - return client.available() || client.connected(); -} +bool client_available(WiFiClient& client); -bool send_via_http(const String& logIdentifier, WiFiClient& client, const String& postStr, bool must_check_reply) { - bool success = !must_check_reply; +bool send_via_http(const String& logIdentifier, WiFiClient& client, const String& postStr, bool must_check_reply); - // This will send the request to the server - byte written = client.print(postStr); +bool send_via_http(int controller_number, WiFiClient& client, const String& postStr, bool must_check_reply); - // as of 2018/11/01 the print function only returns one byte (upd to 256 chars sent). However if the string sent can be longer than this - // therefore we calculate modulo 256. - // see discussion here https://github.com/letscontrolit/ESPEasy/pull/1979 - // and implementation here - // https://github.com/esp8266/Arduino/blob/561426c0c77e9d05708f2c4bf2a956d3552a3706/libraries/ESP8266WiFi/src/include/ClientContext.h#L437-L467 - // this needs to be adjusted if the WiFiClient.print method changes. - if (written != (postStr.length() % 256)) { - if (loglevelActiveFor(LOG_LEVEL_ERROR)) { - String log = F("HTTP : "); - log += logIdentifier; - log += F(" Error: could not write to client ("); - log += written; - log += "/"; - log += postStr.length(); - log += ")"; - addLog(LOG_LEVEL_ERROR, log); - } - success = false; - } -#ifndef BUILD_NO_DEBUG - else { - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - String log = F("HTTP : "); - log += logIdentifier; - log += F(" written to client ("); - log += written; - log += "/"; - log += postStr.length(); - log += ")"; - addLog(LOG_LEVEL_DEBUG, log); - } - } -#endif // ifndef BUILD_NO_DEBUG - - if (must_check_reply) { - unsigned long timer = millis() + 200; - - while (!client_available(client)) { - if (timeOutReached(timer)) { return false; } - delay(1); - } - - // Read all the lines of the reply from server and print them to Serial - while (client_available(client) && !success) { - // String line = client.readStringUntil('\n'); - String line; - safeReadStringUntil(client, line, '\n'); - -#ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) { - if (line.length() > 80) { - addLog(LOG_LEVEL_DEBUG_MORE, line.substring(0, 80)); - } else { - addLog(LOG_LEVEL_DEBUG_MORE, line); - } - } -#endif // ifndef BUILD_NO_DEBUG - - if (line.startsWith(F("HTTP/1.1 2"))) - { - success = true; - - // Leave this debug info in the build, regardless of the - // BUILD_NO_DEBUG flags. - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - String log = F("HTTP : "); - log += logIdentifier; - log += F(" Success! "); - log += line; - addLog(LOG_LEVEL_DEBUG, log); - } - } else if (line.startsWith(F("HTTP/1.1 4"))) { - if (loglevelActiveFor(LOG_LEVEL_ERROR)) { - String log = F("HTTP : "); - log += logIdentifier; - log += F(" Error: "); - log += line; - addLog(LOG_LEVEL_ERROR, log); - } -#ifndef BUILD_NO_DEBUG - addLog(LOG_LEVEL_DEBUG_MORE, postStr); -#endif // ifndef BUILD_NO_DEBUG - } - delay(0); - } - } -#ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - String log = F("HTTP : "); - log += logIdentifier; - log += F(" closing connection"); - addLog(LOG_LEVEL_DEBUG, log); - } -#endif // ifndef BUILD_NO_DEBUG - - client.flush(); - client.stop(); - return success; -} - -bool send_via_http(int controller_number, WiFiClient& client, const String& postStr, bool must_check_reply) { - return send_via_http(get_formatted_Controller_number(controller_number), client, postStr, must_check_reply); -} #endif // CPLUGIN_HELPER_H diff --git a/src/_CPlugin_LoRa_TTN_helper.ino b/src/_CPlugin_LoRa_TTN_helper.ino index 505ea2c82..a56cafe2f 100644 --- a/src/_CPlugin_LoRa_TTN_helper.ino +++ b/src/_CPlugin_LoRa_TTN_helper.ino @@ -4,6 +4,7 @@ #if defined(USES_PACKED_RAW_DATA) +#include "ESPEasy_packed_raw_data.h" String getPackedFromPlugin(struct EventStruct *event, uint8_t sampleSetCount) diff --git a/src/_P026_Sysinfo.ino b/src/_P026_Sysinfo.ino index d4ae5497e..85b04088c 100644 --- a/src/_P026_Sysinfo.ino +++ b/src/_P026_Sysinfo.ino @@ -4,6 +4,8 @@ //####################################################################################################### +#include "ESPEasy_packed_raw_data.h" + #define PLUGIN_026 #define PLUGIN_ID_026 26 #define PLUGIN_NAME_026 "Generic - System Info" diff --git a/src/_P082_GPS.ino b/src/_P082_GPS.ino index f7410b644..11615de49 100644 --- a/src/_P082_GPS.ino +++ b/src/_P082_GPS.ino @@ -12,6 +12,7 @@ #include #include +#include "ESPEasy_packed_raw_data.h" #define PLUGIN_082 #define PLUGIN_ID_082 82 diff --git a/src/_Plugin_Helper.cpp b/src/_Plugin_Helper.cpp new file mode 100644 index 000000000..5da2fc6a5 --- /dev/null +++ b/src/_Plugin_Helper.cpp @@ -0,0 +1,103 @@ +#include "_Plugin_Helper.h" + +#include "ESPEasy_common.h" +#include "ESPEasy_fdwdecl.h" + +#include "DataStructs/ESPEasyLimits.h" +#include "DataStructs/SettingsStruct.h" + +PluginTaskData_base *Plugin_task_data[TASKS_MAX] = { NULL, }; + +String PCONFIG_LABEL(int n) { + if (n < PLUGIN_CONFIGVAR_MAX) { + String result = F("pconf_"); + result += n; + return result; + } + return F("error"); +} + + +void resetPluginTaskData() { + for (byte i = 0; i < TASKS_MAX; ++i) { + Plugin_task_data[i] = nullptr; + } +} + +void clearPluginTaskData(byte taskIndex) { + if (taskIndex < TASKS_MAX) { + if (Plugin_task_data[taskIndex] != nullptr) { + delete Plugin_task_data[taskIndex]; + Plugin_task_data[taskIndex] = nullptr; + } + } +} + +void initPluginTaskData(byte taskIndex, PluginTaskData_base *data) { + clearPluginTaskData(taskIndex); + + if ((taskIndex < TASKS_MAX) && getSettings().TaskDeviceEnabled[taskIndex]) { + Plugin_task_data[taskIndex] = data; + Plugin_task_data[taskIndex]->_taskdata_plugin_id = getPluginId_from_TaskIndex(taskIndex); + } +} + +PluginTaskData_base* getPluginTaskData(byte taskIndex) { + if (taskIndex >= TASKS_MAX) { + return nullptr; + } + + if ((Plugin_task_data[taskIndex] != nullptr) && (Plugin_task_data[taskIndex]->_taskdata_plugin_id == getPluginId_from_TaskIndex(taskIndex))) { + return Plugin_task_data[taskIndex]; + } + return nullptr; +} + +bool pluginTaskData_initialized(byte taskIndex) { + // FIXME TD-er: Must check for type also. + if (taskIndex < TASKS_MAX) { + return Plugin_task_data[taskIndex] != nullptr; + } + return false; +} + +String getPluginCustomArgName(int varNr) { + String argName = F("plugin_custom_arg"); + argName += varNr + 1; + return argName; +} + +// 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) +String pluginWebformShowValue(byte taskIndex, byte varNr, const String& label, const String& value, bool addTrailingBreak) { + String result; + size_t length = 96 + label.length() + value.length(); + String breakStr = F("
"); + if (addTrailingBreak) { + length += breakStr.length(); + } + result.reserve(length); + if (varNr > 0) { + result += breakStr; + } + result += F("
TaskIndex]) #endif // ifndef CONFIG_PORT -String PCONFIG_LABEL(int n) { - if (n < PLUGIN_CONFIGVAR_MAX) { - String result = "pconf_"; - result += n; - return result; - } - return "error"; -} +String PCONFIG_LABEL(int n); // ============================================== // Data used by instances of plugins. @@ -56,94 +52,28 @@ struct PluginTaskData_base { int _taskdata_plugin_id = -1; }; -PluginTaskData_base *Plugin_task_data[TASKS_MAX] = { NULL, }; -void resetPluginTaskData() { - for (byte i = 0; i < TASKS_MAX; ++i) { - Plugin_task_data[i] = nullptr; - } -} -void clearPluginTaskData(byte taskIndex) { - if (taskIndex < TASKS_MAX) { - if (Plugin_task_data[taskIndex] != nullptr) { - delete Plugin_task_data[taskIndex]; - Plugin_task_data[taskIndex] = nullptr; - } - } -} +void resetPluginTaskData(); -void initPluginTaskData(byte taskIndex, PluginTaskData_base *data) { - clearPluginTaskData(taskIndex); +void clearPluginTaskData(byte taskIndex); - if ((taskIndex < TASKS_MAX) && Settings.TaskDeviceEnabled[taskIndex]) { - Plugin_task_data[taskIndex] = data; - Plugin_task_data[taskIndex]->_taskdata_plugin_id = Task_id_to_Plugin_id[taskIndex]; - } -} +void initPluginTaskData(byte taskIndex, PluginTaskData_base *data); -PluginTaskData_base* getPluginTaskData(byte taskIndex) { - if (taskIndex >= TASKS_MAX) { - return nullptr; - } +PluginTaskData_base* getPluginTaskData(byte taskIndex); - if ((Plugin_task_data[taskIndex] != nullptr) && (Plugin_task_data[taskIndex]->_taskdata_plugin_id == Task_id_to_Plugin_id[taskIndex])) { - return Plugin_task_data[taskIndex]; - } - return nullptr; -} +bool pluginTaskData_initialized(byte taskIndex); -bool pluginTaskData_initialized(byte taskIndex) { - // FIXME TD-er: Must check for type also. - if (taskIndex < TASKS_MAX) { - return Plugin_task_data[taskIndex] != nullptr; - } - return false; -} - -String getPluginCustomArgName(int varNr) { - String argName = F("plugin_custom_arg"); - argName += varNr + 1; - return argName; -} +String getPluginCustomArgName(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) -String pluginWebformShowValue(byte taskIndex, byte varNr, const String& label, const String& value, bool addTrailingBreak) { - String result; - size_t length = 96 + label.length() + value.length(); - String breakStr = F("
"); - if (addTrailingBreak) { - length += breakStr.length(); - } - result.reserve(length); - if (varNr > 0) { - result += breakStr; - } - result += F("