From 30bf631326e670b907f859e4a8b84dd9e4cdb9f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Qu=E1=BB=91c=20H=C3=B9ng?= Date: Sun, 30 Jun 2024 22:31:05 +0700 Subject: [PATCH] Bump version to v1.3.3 * Add api write object, array * Add Modbus action * Fix modbus tcp * Add function support ERaString * Some refactor... --- library.json | 2 +- library.properties | 2 +- linux/main.py | 6 +- src/ERa/ERaApi.hpp | 111 +++++++++--- src/ERa/ERaApiDef.hpp | 2 + src/ERa/ERaApiHandler.hpp | 7 + src/ERa/ERaConfig.hpp | 13 +- src/ERa/ERaData.hpp | 207 +++++++++++++++++++---- src/ERa/ERaDebug.hpp | 14 ++ src/ERa/ERaParam.hpp | 97 +++++++++-- src/ERa/ERaProperty.hpp | 165 ++++++++++++++---- src/ERa/ERaProtocol.hpp | 45 +++-- src/ERa/ERaReport.cpp | 4 +- src/ERa/ERaTimer.cpp | 4 +- src/ERa/ERaVersion.hpp | 8 +- src/ERa/types/WrapperArray.hpp | 49 ++++++ src/ERa/types/WrapperBase.hpp | 80 +++++---- src/ERa/types/WrapperNumber.hpp | 2 +- src/ERa/types/WrapperObject.hpp | 16 +- src/ERa/types/WrapperString.hpp | 56 +++++- src/ERa/types/WrapperTypes.hpp | 1 + src/Modbus/ERaDefineModbus.hpp | 17 +- src/Modbus/ERaModbus.hpp | 244 +++++++++++++++++++-------- src/Modbus/ERaModbusAction.hpp | 144 ++++++++++++++++ src/Modbus/ERaModbusConfig.hpp | 4 + src/Modbus/ERaModbusSlave.hpp | 4 +- src/Modbus/ERaModbusTransp.hpp | 5 +- src/Modbus/ERaParse.hpp | 177 ++++++++++++++++++- src/PnP/ERaPnPArduino.hpp | 10 +- src/PnP/ERaPnPEsp32.hpp | 9 +- src/PnP/ERaPnPEsp8266.hpp | 9 +- src/PnP/ERaPnPTiny.hpp | 7 +- src/Utility/ERaUtility.cpp | 116 +++++++++++++ src/Utility/ERaUtility.hpp | 5 + src/Utility/ERacJSON.cpp | 12 ++ src/Utility/ERacJSON.hpp | 1 + src/Utility/cJSON.cpp | 25 ++- src/Utility/cJSON.hpp | 2 +- src/Widgets/ERaWidgetTerminalBox.hpp | 19 ++- 39 files changed, 1434 insertions(+), 267 deletions(-) create mode 100644 src/ERa/types/WrapperArray.hpp create mode 100644 src/Modbus/ERaModbusAction.hpp diff --git a/library.json b/library.json index a17e26b..74b4fa7 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "ERa", - "version": "1.3.2", + "version": "1.3.3", "description": "E-Ra by EoH. An IoT Market Enabler! It supports WiFi, Ethernet, Zigbee, Modbus, Serial. Works with boards like Arduino, ESP8266, ESP32, STM32, Raspberry Pi...", "keywords": "ERa, E-Ra, esp8266, esp32, stm32, raspberry-pi, http, mqtt, zigbee, modbus, sensors, control, device, smartphone, mobile, app, web, cloud, communication, protocol, iot, wifi, ethernet, serial", "authors": diff --git a/library.properties b/library.properties index 15cb6d7..299eec4 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=ERa -version=1.3.2 +version=1.3.3 author=EoH Ltd license=MIT maintainer=EoH Ltd diff --git a/linux/main.py b/linux/main.py index c87e27b..cfabec2 100644 --- a/linux/main.py +++ b/linux/main.py @@ -19,9 +19,9 @@ ERA_MAJOR = 1 ERA_MINOR = 3 -ERA_PATCH = 2 -ERA_VERSION = "1.3.2" -ERA_FIRMWARE_VERSION = "1.3.2" +ERA_PATCH = 3 +ERA_VERSION = "1.3.3" +ERA_FIRMWARE_VERSION = "1.3.3" BUTTON_GPIO = 16 diff --git a/src/ERa/ERaApi.hpp b/src/ERa/ERaApi.hpp index d605eb0..5dc577c 100644 --- a/src/ERa/ERaApi.hpp +++ b/src/ERa/ERaApi.hpp @@ -77,9 +77,9 @@ class ERaApi {} template - void virtualWrite(int pin, T value, bool send = false) { + void virtualWrite(int pin, const T& value, bool send = false) { #if defined(ERA_VIRTUAL_WRITE_LEGACY) - this->virtualWriteSingle(pin, value); + this->virtualWriteSingle(pin, value, false); ERA_FORCE_UNUSED(send); #else Property::virtualWriteProperty(pin, value, send); @@ -87,32 +87,50 @@ class ERaApi } template - void virtualWrite(int pin, T value, Args... tail) { + void virtualWrite(int pin, const T& value, const Args&... tail) { this->virtualWriteMulti(pin, value, tail...); } - void virtualWriteObject(const char* value) { + template + void virtualObject(int pin, const Args&... tail) { + ERaDataJson data; + data.add_multi(tail...); +#if defined(ERA_VIRTUAL_WRITE_LEGACY) + this->virtualWriteSingle(pin, data, ERA_API_JSON); +#else + Property::virtualObjectProperty(pin, data, false); +#endif + } + + template + void virtualArray(int pin, const Args&... tail) { + ERaDataJson data; + data.array_multi(tail...); +#if defined(ERA_VIRTUAL_WRITE_LEGACY) + this->virtualWriteSingle(pin, data, ERA_API_JSON); +#else + Property::virtualArrayProperty(pin, data, false); +#endif + } + + void virtualObject(const char* value) { ERaDataJson data(value); - this->virtualWriteObject(data); + this->virtualObject(data); } - void virtualWriteObject(cJSON* value) { + void virtualObject(cJSON* value) { ERaDataJson data(value); - this->virtualWriteObject(data); + this->virtualObject(data); } - void virtualWriteObject(ERaDataJson& value) { - ERaRsp_t rsp; - rsp.type = ERaTypeWriteT::ERA_WRITE_VIRTUAL_PIN_MULTI; - rsp.retained = ERA_MQTT_PUBLISH_RETAINED; - rsp.id = 0; - rsp.param = 0; - this->thisProto().sendCommand(rsp, &value); + void virtualObject(ERaDataJson& value) { + this->virtualWriteMultiReal(value, false); } void digitalWrite(int pin, bool value) { ERaRsp_t rsp; rsp.type = ERaTypeWriteT::ERA_WRITE_DIGITAL_PIN; + rsp.json = false; rsp.retained = ERA_MQTT_PUBLISH_RETAINED; rsp.id = pin; rsp.param = value; @@ -122,6 +140,7 @@ class ERaApi void analogWrite(int pin, int value) { ERaRsp_t rsp; rsp.type = ERaTypeWriteT::ERA_WRITE_ANALOG_PIN; + rsp.json = false; rsp.retained = ERA_MQTT_PUBLISH_RETAINED; rsp.id = pin; rsp.param = value; @@ -131,6 +150,7 @@ class ERaApi void pwmWrite(int pin, int value) { ERaRsp_t rsp; rsp.type = ERaTypeWriteT::ERA_WRITE_PWM_PIN; + rsp.json = false; rsp.retained = ERA_MQTT_PUBLISH_RETAINED; rsp.id = pin; rsp.param = value; @@ -140,6 +160,7 @@ class ERaApi void pinWrite(int pin, int value) { ERaRsp_t rsp; rsp.type = ERaTypeWriteT::ERA_WRITE_PIN; + rsp.json = false; rsp.retained = ERA_MQTT_PUBLISH_RETAINED; rsp.id = pin; rsp.param = value; @@ -147,9 +168,10 @@ class ERaApi } template - void configIdWrite(ERaInt_t configId, T value) { + void configIdWrite(ERaInt_t configId, const T& value) { ERaRsp_t rsp; rsp.type = ERaTypeWriteT::ERA_WRITE_CONFIG_ID; + rsp.json = false; rsp.retained = ERA_MQTT_PUBLISH_RETAINED; rsp.id = configId; rsp.param = value; @@ -157,7 +179,7 @@ class ERaApi } template - void configIdWrite(ERaInt_t configId, T value, Args... tail) { + void configIdWrite(ERaInt_t configId, const T& value, const Args&... tail) { this->configIdMultiWrite(configId, value, tail...); } @@ -178,6 +200,7 @@ class ERaApi #else ERaRsp_t rsp; rsp.type = ERaTypeWriteT::ERA_WRITE_SPECIFIC_DATA; + rsp.json = false; rsp.retained = retained; rsp.id = id; rsp.param = value; @@ -194,6 +217,7 @@ class ERaApi #else ERaRsp_t rsp; rsp.type = ERaTypeWriteT::ERA_WRITE_SPECIFIC_DATA; + rsp.json = false; rsp.retained = retained; rsp.id = id; rsp.param = value; @@ -426,6 +450,7 @@ class ERaApi #else ERaRsp_t rsp; rsp.type = ERaTypeWriteT::ERA_WRITE_MODBUS_DATA; + rsp.json = false; rsp.retained = ERA_MQTT_PUBLISH_RETAINED; rsp.id = 0; rsp.param = 0; @@ -443,6 +468,7 @@ class ERaApi #else ERaRsp_t rsp; rsp.type = ERaTypeWriteT::ERA_WRITE_ZIGBEE_DATA; + rsp.json = false; rsp.retained = retained; rsp.id = id; rsp.param = value; @@ -550,9 +576,10 @@ class ERaApi private: template - void virtualWriteSingle(int pin, T value) { + void virtualWriteSingle(int pin, const T& value, bool json = false) { ERaRsp_t rsp; rsp.type = ERaTypeWriteT::ERA_WRITE_VIRTUAL_PIN; + rsp.json = json; rsp.retained = ERA_MQTT_PUBLISH_RETAINED; rsp.id = pin; rsp.param = value; @@ -560,23 +587,29 @@ class ERaApi } template - void virtualWriteMulti(Args... tail) { - ERaRsp_t rsp; + void virtualWriteMulti(const Args&... tail) { ERaDataJson data; data.add_multi(tail...); + this->virtualWriteMultiReal(data, false); + } + + void virtualWriteMultiReal(ERaDataJson& value, bool json = false) { + ERaRsp_t rsp; rsp.type = ERaTypeWriteT::ERA_WRITE_VIRTUAL_PIN_MULTI; + rsp.json = json; rsp.retained = ERA_MQTT_PUBLISH_RETAINED; rsp.id = 0; rsp.param = 0; - this->thisProto().sendCommand(rsp, &data); + this->thisProto().sendCommand(rsp, &value); } template - void configIdMultiWrite(Args... tail) { + void configIdMultiWrite(const Args&... tail) { ERaRsp_t rsp; ERaDataJson data; data.add_multi(tail...); rsp.type = ERaTypeWriteT::ERA_WRITE_CONFIG_ID_MULTI; + rsp.json = false; rsp.retained = ERA_MQTT_PUBLISH_RETAINED; rsp.id = 0; rsp.param = data.getObject(); @@ -761,8 +794,10 @@ void ERaApi::processVirtualPinRequest(const ERaDataBuff& arrayTopi ERaParam param(data); uint8_t pin = ERA_DECODE_PIN_NAME(str); cJSON* item = cJSON_GetObjectItem(root, "value"); - if (cJSON_IsNumber(item) || - cJSON_IsBool(item)) { + if (cJSON_IsBool(item)) { + param.add(item->valueint); + } + else if (cJSON_IsNumber(item)) { param.add(item->valuedouble); } else if (cJSON_IsString(item)) { @@ -771,8 +806,17 @@ void ERaApi::processVirtualPinRequest(const ERaDataBuff& arrayTopi else if (item != nullptr) { param.add(item); } + + if (cJSON_IsObject(item) || + cJSON_IsArray(item)) { + data.detachObject(); + data.setObject(item, false); + } + this->callERaWriteHandler(pin, param); + data.detachObject(); + cJSON_Delete(root); root = nullptr; item = nullptr; } @@ -1155,9 +1199,10 @@ template inline void ERaApi::callERaProHandler(const char* deviceId, const cJSON* const root) { char id[65] {0}; - ERaParam param; + ERaDataJson data; cJSON* current = nullptr; for (current = root->child; current != nullptr && current->string != nullptr; current = current->next) { + ERaParam param; ClearArray(id); FormatString(id, "%s:%s", deviceId, current->string); if (cJSON_IsBool(current)) { @@ -1166,8 +1211,14 @@ void ERaApi::callERaProHandler(const char* deviceId, const cJSON* else if (cJSON_IsNumber(current)) { param = current->valuedouble; } - else if (cJSON_IsString(current)){ - param = current->valuestring; + else if (cJSON_IsString(current)) { + param.add_static(current->valuestring); + } + else if (cJSON_IsObject(current) || + cJSON_IsArray(current)) { + data.setObject(current, false); + param = current; + param = data; } else { continue; @@ -1175,6 +1226,7 @@ void ERaApi::callERaProHandler(const char* deviceId, const cJSON* #if !defined(ERA_ABBR) Property::handler(id, param); #endif + data.detachObject(); } } @@ -1220,6 +1272,7 @@ void ERaApi::callERaProHandler(const char* deviceId, const cJSON* ERaEvent_t event; event.type = ERaTypeWriteT::ERA_WRITE_MODBUS_DATA; event.specific = false; + event.json = false; event.retained = ERA_MQTT_PUBLISH_RETAINED; event.id = nullptr; event.data = value; @@ -1236,6 +1289,7 @@ void ERaApi::callERaProHandler(const char* deviceId, const cJSON* ERaRsp_t rsp; ERaDataBuff* data = (ERaDataBuff*)event.data; rsp.type = ERaTypeWriteT::ERA_WRITE_MODBUS_DATA; + rsp.json = false; rsp.retained = ERA_MQTT_PUBLISH_RETAINED; rsp.id = 0; rsp.param = 0; @@ -1255,6 +1309,7 @@ void ERaApi::callERaProHandler(const char* deviceId, const cJSON* ERaEvent_t event; event.type = ERaTypeWriteT::ERA_WRITE_ZIGBEE_DATA; event.specific = specific; + event.json = false; event.retained = retained; if (!specific) { event.id = (void*)id; @@ -1277,6 +1332,7 @@ void ERaApi::callERaProHandler(const char* deviceId, const cJSON* ERaRsp_t rsp; rsp.type = ERaTypeWriteT::ERA_WRITE_ZIGBEE_DATA; + rsp.json = event.json; rsp.retained = event.retained; rsp.id.add_static((char*)event.id); if (!event.specific) { @@ -1307,6 +1363,7 @@ void ERaApi::callERaProHandler(const char* deviceId, const cJSON* ERaEvent_t event; event.type = ERaTypeWriteT::ERA_WRITE_SPECIFIC_DATA; event.specific = specific; + event.json = false; event.retained = retained; if (!specific) { event.id = (void*)id; @@ -1330,6 +1387,7 @@ void ERaApi::callERaProHandler(const char* deviceId, const cJSON* ERaEvent_t event; event.type = ERaTypeWriteT::ERA_WRITE_SPECIFIC_DATA; event.specific = specific; + event.json = false; event.retained = retained; if (!specific) { event.id = (void*)id; @@ -1352,6 +1410,7 @@ void ERaApi::callERaProHandler(const char* deviceId, const cJSON* ERaRsp_t rsp; rsp.type = ERaTypeWriteT::ERA_WRITE_SPECIFIC_DATA; + rsp.json = false; rsp.retained = event.retained; rsp.id.add_static((char*)event.id); if (!event.specific) { diff --git a/src/ERa/ERaApiDef.hpp b/src/ERa/ERaApiDef.hpp index d4a47a6..29d25d1 100644 --- a/src/ERa/ERaApiDef.hpp +++ b/src/ERa/ERaApiDef.hpp @@ -88,6 +88,7 @@ enum ERaTypeWriteT { typedef struct __ERaRsp_t { uint8_t type; + bool json; bool retained; ERaParam id; ERaParam param; @@ -96,6 +97,7 @@ typedef struct __ERaRsp_t { typedef struct __ERaEvent_t { uint8_t type; bool specific; + bool json; bool retained; void* id; void* data; diff --git a/src/ERa/ERaApiHandler.hpp b/src/ERa/ERaApiHandler.hpp index bdec46e..af7d462 100644 --- a/src/ERa/ERaApiHandler.hpp +++ b/src/ERa/ERaApiHandler.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -129,6 +130,12 @@ class ERaApiHandler this->ERaTm.run(); } + virtual bool afterNetwork() { + return (ERaState::is(StateT::STATE_RUNNING) || + ERaState::is(StateT::STATE_CONNECTED) || + ERaState::is(StateT::STATE_CONNECTING_CLOUD)); + } + virtual bool connected() = 0; protected: diff --git a/src/ERa/ERaConfig.hpp b/src/ERa/ERaConfig.hpp index c539294..5f2f4a4 100644 --- a/src/ERa/ERaConfig.hpp +++ b/src/ERa/ERaConfig.hpp @@ -111,7 +111,7 @@ #if defined(DEFAULT_INFO_PUBLISH_RETAINED) #define ERA_INFO_PUBLISH_RETAINED DEFAULT_INFO_PUBLISH_RETAINED #else - #define ERA_INFO_PUBLISH_RETAINED false + #define ERA_INFO_PUBLISH_RETAINED ERA_MQTT_PUBLISH_RETAINED #endif #if defined(DEFAULT_SOCKET_TIMEOUT) @@ -148,6 +148,10 @@ #define LIMIT_CONNECT_BROKER_MQTT 5 #endif +#if !defined(ERA_API_JSON) + #define ERA_API_JSON false +#endif + #define INFO_ID "id" #define INFO_BOARD "board" #define INFO_MODEL "model" @@ -178,9 +182,10 @@ #define INFO_MB_DATA "data" #define INFO_MB_ACK "ack" #define INFO_MB_SCAN "scan" -#define INFO_MB_READ_FAIL "modbus_fail" -#define INFO_MB_WRITE_FAIL "modbus_write_fail" -#define INFO_MB_TOTAL "modbus_total" +#define INFO_MB_FAIL_READ "modbus_fail" +#define INFO_MB_FAIL_WRITE "modbus_fail_write" +#define INFO_MB_TOTAL_READ "modbus_total" +#define INFO_MB_TOTAL_WRITE "modbus_total_write" #define INFO_MB_CHIP_TEMPERATURE "chip_temperature" #define INFO_MB_TEMPERATURE "temperature" #define INFO_MB_VOLTAGE "voltage" diff --git a/src/ERa/ERaData.hpp b/src/ERa/ERaData.hpp index 7622e76..7bdb06e 100644 --- a/src/ERa/ERaData.hpp +++ b/src/ERa/ERaData.hpp @@ -248,12 +248,12 @@ class ERaDataBuff #endif template - void add_multi(const T last) { + void add_multi(const T& last) { this->add(last); } template - void add_multi(const T head, Args... tail) { + void add_multi(const T& head, const Args&... tail) { this->add(head); this->add_multi(tail...); } @@ -841,12 +841,14 @@ class ERaDataJson typedef cJSON* (CJSON_STDCALL* AddDouble)(cJSON* const object, const char* const name, const double number, int decimal); typedef cJSON* (CJSON_STDCALL* AddString)(cJSON* const object, const char* const name, const char* const string); typedef cJSON_bool (CJSON_STDCALL* AddItem)(cJSON* object, const char* name, cJSON* item); + typedef cJSON_bool (CJSON_STDCALL* AddArray)(cJSON* array, cJSON* item); typedef struct __DataHooks { AddBool addBool; AddNumber addNumber; AddDouble addDouble; AddString addString; AddItem addItem; + AddArray addArray; } DataHooks; public: @@ -1119,7 +1121,7 @@ class ERaDataJson } template - iterator& operator = (T value) { + iterator& operator = (const T& value) { cJSON_SetNumber(this->parent, this->item, value); return (*this); } @@ -1167,7 +1169,7 @@ class ERaDataJson } template - bool operator == (T value) const { + bool operator == (const T& value) const { if (!this->isNumber()) { return false; } @@ -1213,7 +1215,7 @@ class ERaDataJson } template - bool operator != (T value) const { + bool operator != (const T& value) const { return !(operator == (value)); } @@ -1250,7 +1252,7 @@ class ERaDataJson } template - T operator | (T value) const { + T operator | (const T& value) const { if (!this->isNumber()) { return value; } @@ -1315,6 +1317,7 @@ class ERaDataJson ERaDataJson(cJSON* json = nullptr) : ptr(nullptr) , root(json) + , sendJS(false) {} ERaDataJson(const char* str) : ERaDataJson(cJSON_Parse(str)) @@ -1322,13 +1325,13 @@ class ERaDataJson ERaDataJson(const ERaDataJson& json) : ptr(nullptr) , root(nullptr) + , sendJS(false) { (*this) = json; } ~ERaDataJson() { - this->clear(); - this->clearObject(); + this->reset(); } const cJSON* getObject() const { @@ -1341,14 +1344,37 @@ class ERaDataJson return it; } - void setObject(cJSON* const it) { + cJSON* duplicateObject() const { + return cJSON_Duplicate(this->root, true); + } + + void setObject(cJSON* const it, bool rst = true) { + if (rst) { + this->reset(); + } this->root = it; } + void copyObject(cJSON* const it, bool rst = true) { + this->setObject(cJSON_Duplicate(it, true), rst); + } + void parse(const char* str) { + this->reset(); + this->root = cJSON_Parse(str); + } + + void reset() { this->clear(); this->clearObject(); - this->root = cJSON_Parse(str); + } + + void setSendJSON(bool enable) { + this->sendJS = enable; + } + + bool isSendJSON() const { + return this->sendJS; } const char* c_str() { @@ -1413,7 +1439,7 @@ class ERaDataJson } template - void set(const char* name, T value); + void set(const char* name, const T& value); void set(const char* name, bool value); void set(const char* name, float value); void set(const char* name, double value); @@ -1421,50 +1447,78 @@ class ERaDataJson void set(const char* name, const char* value); template - void add(const char* name, T value); + void add(const char* name, const T& value); void add(const char* name, bool value); void add(const char* name, float value); void add(const char* name, double value); void add(const char* name, char* value); void add(const char* name, const char* value); void add(const char* name, cJSON* value); - void add(const char* name, ERaDataJson& value); - void add(const char* name, ERaParam& value); + void add(const char* name, const ERaDataJson& value); + void add(const char* name, const ERaParam& value); + void add(const char* name, const ERaString& value); #if defined(ERA_HAS_PROGMEM) void add(const char* name, const __FlashStringHelper* value); #endif template - void add_multi(const T value) { + void add_multi(const T& value) { (void)value; } template - void add_multi(const char* name, const T last) { + void add_multi(const char* name, const T& last) { this->add(name, last); } template - void add_multi(const char* name, const T head, Args... tail) { + void add_multi(const char* name, const T& head, const Args&... tail) { this->add(name, head); this->add_multi(tail...); } template - void add_multi(ERaInt_t id, const T last) { + void add_multi(ERaInt_t id, const T& last) { char name[2 + 8 * sizeof(ERaInt_t)] {0}; snprintf(name, sizeof(name), ERA_INT_FORMAT, id); this->add_multi(name, last); } template - void add_multi(ERaInt_t id, const T head, Args... tail) { + void add_multi(ERaInt_t id, const T& head, const Args&... tail) { char name[2 + 8 * sizeof(ERaInt_t)] {0}; snprintf(name, sizeof(name), ERA_INT_FORMAT, id); this->add_multi(name, head, tail...); } + template + void add(const T& value); + void add(bool value); + void add(float value); + void add(double value); + void add(char* value); + void add(const char* value); + void add(cJSON* value); + void add(const ERaDataJson& value); + void add(const ERaParam& value); + void add(const ERaString& value); + +#if defined(ERA_HAS_PROGMEM) + void add(const __FlashStringHelper* value); +#endif + + template + void array_multi(const T& last) { + this->add(last); + } + + template + void array_multi(const T& head, const Args&... tail) { + this->add(head); + this->array_multi(tail...); + } + bool containsKey(const char* key) const; cJSON* detach(size_t index); @@ -1507,6 +1561,7 @@ class ERaDataJson bool operator != (nullptr_t) const; ERaDataJson& operator = (const char* value); + ERaDataJson& operator = (const cJSON* value); ERaDataJson& operator = (const ERaDataJson& value); ERaDataJson& operator = (nullptr_t); @@ -1563,13 +1618,35 @@ class ERaDataJson return cJSON_AddItemToObject(object, name, item); } + cJSON_bool addArray(cJSON* array, cJSON* item) { + return cJSON_AddItemToArray(array, item); + } + + cJSON_bool addBoolArray(cJSON* array, const cJSON_bool boolean) { + return this->addArray(array, cJSON_CreateBool(boolean)); + } + + cJSON_bool addNumberArray(cJSON* array, const double number) { + return this->addArray(array, cJSON_CreateNumber(number)); + } + + cJSON_bool addDoubleArray(cJSON* array, const double number, int decimal) { + return this->addArray(array, cJSON_CreateNumberWithDecimalToObject(number, decimal)); + } + + cJSON_bool addStringArray(cJSON* array, const char* const string) { + return this->addArray(array, cJSON_CreateString(string)); + } + char* ptr; cJSON* root; + + bool sendJS; }; template inline -void ERaDataJson::set(const char* name, T value) { +void ERaDataJson::set(const char* name, const T& value) { this->create(); this->setNumber(this->root, name, value); } @@ -1606,7 +1683,7 @@ void ERaDataJson::set(const char* name, const char* value) { template inline -void ERaDataJson::add(const char* name, T value) { +void ERaDataJson::add(const char* name, const T& value) { this->create(); this->addNumber(this->root, name, value); } @@ -1648,8 +1725,8 @@ void ERaDataJson::add(const char* name, cJSON* value) { } inline -void ERaDataJson::add(const char* name, ERaDataJson& value) { - this->add(name, value.detachObject()); +void ERaDataJson::add(const char* name, const ERaDataJson& value) { + this->add(name, value.duplicateObject()); } #if defined(ERA_HAS_PROGMEM) @@ -1668,6 +1745,70 @@ void ERaDataJson::add(const char* name, ERaDataJson& value) { #endif +template +inline +void ERaDataJson::add(const T& value) { + this->createArray(); + this->addNumberArray(this->root, value); +} + +inline +void ERaDataJson::add(bool value) { + this->createArray(); + this->addBoolArray(this->root, value); +} + +inline +void ERaDataJson::add(float value) { + this->createArray(); + this->addDoubleArray(this->root, value, 2); +} + +inline +void ERaDataJson::add(double value) { + this->createArray(); + this->addDoubleArray(this->root, value, 5); +} + +inline +void ERaDataJson::add(char* value) { + this->createArray(); + this->addStringArray(this->root, value); +} + +inline +void ERaDataJson::add(const char* value) { + this->createArray(); + this->addStringArray(this->root, value); +} + +inline +void ERaDataJson::add(cJSON* value) { + this->createArray(); + this->addArray(this->root, value); +} + +inline +void ERaDataJson::add(const ERaDataJson& value) { + this->add(value.duplicateObject()); +} + +#if defined(ERA_HAS_PROGMEM) + + inline + void ERaDataJson::add(const __FlashStringHelper* value) { + if (value == nullptr) { + return; + } + PGM_P p = reinterpret_cast(value); + size_t size = strlen_P(p); + char str[size + 1] {0}; + memcpy_P(str, p, size); + this->add(str); + } + +#endif + inline bool ERaDataJson::containsKey(const char* key) const { return (cJSON_GetObjectItem(this->root, key) != nullptr); @@ -1801,27 +1942,35 @@ bool ERaDataJson::operator != (nullptr_t) const { inline ERaDataJson& ERaDataJson::operator = (const char* value) { - this->clear(); - this->clearObject(); + this->reset(); this->root = cJSON_Parse(value); return (*this); } +inline +ERaDataJson& ERaDataJson::operator = (const cJSON* value) { + if (this->root == value) { + return (*this); + } + this->reset(); + this->root = cJSON_Duplicate(value, true); + return (*this); +} + inline ERaDataJson& ERaDataJson::operator = (const ERaDataJson& value) { if (this == &value) { return (*this); } - this->clear(); - this->clearObject(); + this->reset(); + this->sendJS = value.sendJS; this->root = cJSON_Duplicate(value.getObject(), true); return (*this); } inline ERaDataJson& ERaDataJson::operator = (nullptr_t) { - this->clear(); - this->clearObject(); + this->reset(); return (*this); } diff --git a/src/ERa/ERaDebug.hpp b/src/ERa/ERaDebug.hpp index 50a7457..a05ce36 100644 --- a/src/ERa/ERaDebug.hpp +++ b/src/ERa/ERaDebug.hpp @@ -332,4 +332,18 @@ ERA_UNUSED void ERaLogHex(const char ERA_UNUSED *title, const uint8_t ERA_UNUSED #endif } +template +inline void ERaHexDump(S& serial, const void* data, size_t size) { + static constexpr char alphabet[] { "0123456789ABCDEF" }; + const uint8_t* buffer = (const uint8_t*)data; + char hex[3] {0}; + for (size_t i = 0; i < size; ++i) { + hex[0] = alphabet[(buffer[i] >> 4) & 0xF]; + hex[1] = alphabet[(buffer[i]) & 0xF]; + serial.print(hex); + serial.print(" "); + } + serial.println(); +} + #endif /* INC_ERA_DEBUG_HPP_ */ diff --git a/src/ERa/ERaParam.hpp b/src/ERa/ERaParam.hpp index f64b0d5..79feb80 100644 --- a/src/ERa/ERaParam.hpp +++ b/src/ERa/ERaParam.hpp @@ -29,7 +29,7 @@ class ERaParam , valueobject(nullptr) {} template - ERaParam(T value) + ERaParam(const T& value) : type(0) , valueint(0) , valuedouble(0) @@ -38,7 +38,7 @@ class ERaParam { this->add(value); } - ERaParam(ERaDataJson& value) + ERaParam(const ERaDataJson& value) : type(0) , valueint(0) , valuedouble(0) @@ -87,6 +87,10 @@ class ERaParam return this->getType(ERaParamTypeT::ERA_PARAM_TYPE_OBJECT); } + bool hasNonObject() const { + return (this->isNumber() || this->isString()); + } + ERaInt_t getInt() const { return this->valueint; } @@ -211,11 +215,11 @@ class ERaParam } template - void add(T value) { + void add(const T& value) { this->addParam(value); } - void add(ERaDataJson& value) { + void add(const ERaDataJson& value) { this->addParam(value); } @@ -246,12 +250,12 @@ class ERaParam } template - ERaParam& operator = (T value) { + ERaParam& operator = (const T& value) { this->addParam(value); return (*this); } - ERaParam& operator = (ERaDataJson& value) { + ERaParam& operator = (const ERaDataJson& value) { this->addParam(value); return (*this); } @@ -267,7 +271,7 @@ class ERaParam } template - bool operator == (T value) const { + bool operator == (const T& value) const { if (!this->isNumber()) { return false; } @@ -319,6 +323,14 @@ class ERaParam return (*this->valueobject == *value); } + bool operator == (ERaDataJson& value) const { + return operator == (&value); + } + + bool operator == (const ERaDataJson& value) const { + return operator == (&value); + } + bool operator == (ERaParam& value) const { return operator == ((const ERaParam&)value); } @@ -342,8 +354,11 @@ class ERaParam return false; } + bool operator == (ERaString& value) const; + bool operator == (const ERaString& value) const; + template - bool operator != (T value) const { + bool operator != (const T& value) const { return !(operator == (value)); } @@ -371,6 +386,14 @@ class ERaParam return !(operator == (value)); } + bool operator != (ERaDataJson& value) const { + return !(operator == (value)); + } + + bool operator != (const ERaDataJson& value) const { + return !(operator == (value)); + } + bool operator != (ERaParam& value) const { return !(operator == (value)); } @@ -379,6 +402,14 @@ class ERaParam return !(operator == (value)); } + bool operator != (ERaString& value) const { + return !(operator == (value)); + } + + bool operator != (const ERaString& value) const { + return !(operator == (value)); + } + ERaDataJson::iterator operator [] (int index) const { if (this->valueobject != nullptr) { return this->valueobject->at(index); @@ -406,7 +437,7 @@ class ERaParam protected: private: template - void addParam(T value) { + void addParam(const T& value) { double number = (double)value; if (number >= ERA_INT_MAX) { this->valueint = ERA_INT_MAX; @@ -464,8 +495,8 @@ class ERaParam this->setTypeString(); } - void addParam(ERaDataJson& value) { - this->valueobject = &value; + void addParam(const ERaDataJson& value) { + this->valueobject = &const_cast(value); this->setType(ERaParamTypeT::ERA_PARAM_TYPE_OBJECT, true); } @@ -508,12 +539,28 @@ class ERaParam }; inline -void ERaDataJson::add(const char* name, ERaParam& value) { - if (value.isString()) { +void ERaDataJson::add(const char* name, const ERaParam& value) { + if (value.isNumber()) { + this->add(name, value.getDouble()); + } + else if (value.isString()) { this->add(name, value.getString()); } - else if (value.isNumber()) { - this->add(name, value.getDouble()); + else if (value.isObject()) { + this->add(name, *value.getObject()); + } +} + +inline +void ERaDataJson::add(const ERaParam& value) { + if (value.isNumber()) { + this->add(value.getDouble()); + } + else if (value.isString()) { + this->add(value.getString()); + } + else if (value.isObject()) { + this->add(*value.getObject()); } } @@ -524,6 +571,16 @@ void ERaParam::addParam(const ERaString& value) { this->addParam(value.getString()); } +inline +bool ERaParam::operator == (ERaString& value) const { + return operator == ((const char*)value.getString()); +} + +inline +bool ERaParam::operator == (const ERaString& value) const { + return operator == ((const char*)value.getString()); +} + inline ERaString ERaDataJson::iterator::type() { return ERaString(cJSON_TypeOf(this->item)); @@ -543,6 +600,16 @@ ERaString ERaDataJson::iterator::operator | (const char* value) const { return ERaString(value); } +inline +void ERaDataJson::add(const char* name, const ERaString& value) { + this->add(name, value.getString()); +} + +inline +void ERaDataJson::add(const ERaString& value) { + this->add(value.getString()); +} + inline ERaString ERaDataJson::typeOf(const ERaDataJson::iterator& value) { return ERaString(cJSON_TypeOf(value.item)); diff --git a/src/ERa/ERaProperty.hpp b/src/ERa/ERaProperty.hpp index a09c6f0..d326a85 100644 --- a/src/ERa/ERaProperty.hpp +++ b/src/ERa/ERaProperty.hpp @@ -140,6 +140,13 @@ class ERaProperty return (*this); } + iterator& resetUpdate() { + if (this->isValid()) { + this->prop->resetUpdate(this->pProp); + } + return (*this); + } + private: void invalidate() { this->prop = nullptr; @@ -166,6 +173,10 @@ class ERaProperty return iterator(this, this->isPropertyIdExist(id)); } + ERaReport& getPropertyReport() { + return this->ERaPropRp; + } + template iterator addPropertyVirtual(T& value, PermissionT const permission) { int pin = this->findVirtualPin(); @@ -393,9 +404,11 @@ class ERaProperty #if !defined(ERA_VIRTUAL_WRITE_LEGACY) template - void virtualWriteProperty(uint8_t pin, T value, bool send) { + void virtualWriteProperty(uint8_t pin, const T& value, bool send) { Property_t* pProp = this->isPropertyIdExist(pin); - if (pProp != nullptr) { + if (pProp == nullptr) { + } + else if (pProp->value->isNumber()) { (*pProp->value) = value; pProp->report.updateReport(value, false, false); if (send) { @@ -403,19 +416,21 @@ class ERaProperty } return; } - double* pValue = (double*)malloc(sizeof(double)); + else { + return; + } + double* pValue = this->create(value); if (pValue == nullptr) { return; } - memset((void*)pValue, 0, sizeof(double)); - (*pValue) = value; WrapperBase* wrapper = new WrapperDouble(*pValue); if (wrapper == nullptr) { free(pValue); pValue = nullptr; return; } - this->addPropertyVirtual(pin, *wrapper, PermissionT::PERMISSION_CLOUD_READ_WRITE).publishOnChange(0, this->writeTimeout).publish().allocatorPointer(pValue); + this->addPropertyVirtual(pin, *wrapper, PermissionT::PERMISSION_CLOUD_READ_WRITE). + publishOnChange(0, this->writeTimeout).publish().allocatorPointer(pValue); } void virtualWriteProperty(uint8_t pin, const ERaParam& value, bool send) { @@ -425,10 +440,21 @@ class ERaProperty else if (value.isString()) { this->virtualWriteProperty(pin, value.getString(), send); } + else if (value.isObject()) { + this->virtualWriteProperty(pin, *value.getObject(), send); + } } - void virtualWriteProperty(uint8_t pin, ERaDataJson& value, bool send) { - this->virtualWriteProperty(pin, value.getString(), send); + void virtualWriteProperty(uint8_t pin, const ERaDataJson& value, bool send) { + if (!value.isSendJSON()) { + this->virtualWriteProperty(pin, const_cast(value).getString(), send); + } + else if (value.isObject()) { + this->virtualObjectProperty(pin, value, send); + } + else if (value.isArray()) { + this->virtualArrayProperty(pin, value, send); + } } void virtualWriteProperty(uint8_t pin, const ERaString& value, bool send) { @@ -437,7 +463,7 @@ class ERaProperty #if defined(ERA_STRING_WRITE_LEGACY) void virtualWriteProperty(uint8_t pin, const char* value, bool send) { - this->thisApi().virtualWriteSingle(pin, value); + this->thisApi().virtualWriteSingle(pin, value, false); ERA_FORCE_UNUSED(send); } #else @@ -451,12 +477,10 @@ class ERaProperty } return; } - ERaString* pValue = (ERaString*)malloc(sizeof(ERaString)); + ERaString* pValue = this->create(value); if (pValue == nullptr) { return; } - memset((void*)pValue, 0, sizeof(ERaString)); - (*pValue) = value; WrapperBase* wrapper = new WrapperString(*pValue); if (wrapper == nullptr) { pValue->~ERaString(); @@ -465,13 +489,51 @@ class ERaProperty return; } wrapper->setOptions(0x01); - this->addPropertyVirtual(pin, *wrapper, PermissionT::PERMISSION_CLOUD_READ_WRITE).publishOnChange(0, this->writeTimeout).allocatorPointer(pValue); + this->addPropertyVirtual(pin, *wrapper, PermissionT::PERMISSION_CLOUD_READ_WRITE). + publishOnChange(0, this->writeTimeout).publish(). + allocatorPointer(pValue).resetUpdate(); } #endif void virtualWriteProperty(uint8_t pin, char* value, bool send) { this->virtualWriteProperty(pin, (const char*)value, send); } + + void virtualObjectProperty(uint8_t pin, const ERaDataJson& value, bool send) { + this->virtualJsonProperty(pin, value, send); + } + + void virtualArrayProperty(uint8_t pin, const ERaDataJson& value, bool send) { + this->virtualJsonProperty(pin, value, send); + } + + template + void virtualJsonProperty(uint8_t pin, const ERaDataJson& value, bool send) { + Property_t* pProp = this->isPropertyIdExist(pin); + if (pProp != nullptr) { + (*pProp->value) = const_cast(value).getString(); + if (send) { + pProp->report(); + pProp->value->updated(); + } + return; + } + ERaDataJson* pValue = this->create(value); + if (pValue == nullptr) { + return; + } + WrapperBase* wrapper = new T(*pValue); + if (wrapper == nullptr) { + pValue->~ERaDataJson(); + free(pValue); + pValue = nullptr; + return; + } + wrapper->setOptions(0x01); + this->addPropertyVirtual(pin, *wrapper, PermissionT::PERMISSION_CLOUD_READ_WRITE). + publishOnChange(0, this->writeTimeout).publish(). + allocatorPointer(pValue).resetUpdate(); + } #endif private: @@ -491,6 +553,7 @@ class ERaProperty unsigned long minInterval = 1000UL, unsigned long maxInterval = 60000UL); bool allocatorPointer(Property_t* pProp, void* ptr); + bool resetUpdate(Property_t* pProp); bool isPropertyFree(); int findVirtualPin(); @@ -506,6 +569,16 @@ class ERaProperty static void _onCallback(void* args); #endif + template + T* create(const T& value) { + T* ptr = (T*)malloc(sizeof(T)); + if (ptr != nullptr) { + memset((void*)ptr, 0, sizeof(T)); + (*ptr) = value; + } + return ptr; + } + bool isValidProperty(const Property_t* pProp) { if (pProp == nullptr) { return false; @@ -604,6 +677,8 @@ void ERaProperty::updateValue(const Property_t* pProp) { pProp->report.updateReport(pProp->value->getDouble(), false, false); break; case WrapperTypeT::WRAPPER_TYPE_STRING: + case WrapperTypeT::WRAPPER_TYPE_OBJECT: + case WrapperTypeT::WRAPPER_TYPE_ARRAY: if (pProp->value->updated()) { pProp->report(); } @@ -639,9 +714,17 @@ void ERaProperty::handler(uint8_t pin, const ERaParam& param) { return; } #endif - if (!found) { + if (found) { + } + else if (param.hasNonObject()) { this->virtualWriteProperty(pin, param, false); } + else if (param.isObject() && param.getObject()->isObject()) { + this->virtualObjectProperty(pin, *param.getObject(), false); + } + else if (param.isObject() && param.getObject()->isArray()) { + this->virtualArrayProperty(pin, *param.getObject(), false); + } #endif } @@ -690,6 +773,9 @@ void ERaProperty::handler(const char* id, const ERaParam& param) { if (!pProp->id.isNumber()) { continue; } + if (pProp->id.getInt() < 0) { + continue; + } if (pin.isVPinExist(pProp->id.getInt(), pProp->value)) { if (pProp->report) { pProp->report.executeNow(); @@ -751,6 +837,8 @@ void ERaProperty::getValue(Property_t* pProp, const ERaParam& param) { } break; case WrapperTypeT::WRAPPER_TYPE_STRING: + case WrapperTypeT::WRAPPER_TYPE_OBJECT: + case WrapperTypeT::WRAPPER_TYPE_ARRAY: if (param.isString()) { (*pProp->value) = param.getString(); if (pProp->value->updated()) { @@ -821,7 +909,7 @@ typename ERaProperty::Property_t* ERaProperty::setupProperty(const cha return nullptr; } - pProp->id = 0xFF; + pProp->id = -1; pProp->id = id; pProp->value = value; pProp->callback = nullptr; @@ -927,14 +1015,14 @@ bool ERaProperty::publishOnChange(Property_t* pProp, float minChange, else { pProp->report = this->ERaPropRp.setReporting(minInterval, maxInterval, minChange, this->propertyCb, pProp); } - if (pProp->value->isString()) { + if (pProp->value->isNumber()) { + this->updateValue(pProp); + } + else { #if defined(ERA_STRING_REPORT_INTERVAL) pProp->report.updateReport(0.0f, false, false); #endif } - else { - this->updateValue(pProp); - } return true; } @@ -948,6 +1036,16 @@ bool ERaProperty::allocatorPointer(Property_t* pProp, void* ptr) { return true; } +template +bool ERaProperty::resetUpdate(Property_t* pProp) { + if (pProp == nullptr) { + return false; + } + + pProp->value->updated(); + return true; +} + template void ERaProperty::onCallbackVirtual(const Property_t* const pProp) { if (pProp->value->isNumber() && ERaIsSpN(pProp->value->getDouble())) { @@ -956,38 +1054,45 @@ void ERaProperty::onCallbackVirtual(const Property_t* const pProp) { switch (pProp->value->getType()) { case WrapperTypeT::WRAPPER_TYPE_BOOL: - this->thisApi().virtualWriteSingle(pProp->id.getInt(), pProp->value->getBool()); + this->thisApi().virtualWriteSingle(pProp->id.getInt(), pProp->value->getBool(), false); break; case WrapperTypeT::WRAPPER_TYPE_INT: - this->thisApi().virtualWriteSingle(pProp->id.getInt(), pProp->value->getInt()); + this->thisApi().virtualWriteSingle(pProp->id.getInt(), pProp->value->getInt(), false); break; case WrapperTypeT::WRAPPER_TYPE_UNSIGNED_INT: - this->thisApi().virtualWriteSingle(pProp->id.getInt(), pProp->value->getUnsignedInt()); + this->thisApi().virtualWriteSingle(pProp->id.getInt(), pProp->value->getUnsignedInt(), false); break; case WrapperTypeT::WRAPPER_TYPE_LONG: - this->thisApi().virtualWriteSingle(pProp->id.getInt(), pProp->value->getLong()); + this->thisApi().virtualWriteSingle(pProp->id.getInt(), pProp->value->getLong(), false); break; case WrapperTypeT::WRAPPER_TYPE_UNSIGNED_LONG: - this->thisApi().virtualWriteSingle(pProp->id.getInt(), pProp->value->getUnsignedLong()); + this->thisApi().virtualWriteSingle(pProp->id.getInt(), pProp->value->getUnsignedLong(), false); break; case WrapperTypeT::WRAPPER_TYPE_LONG_LONG: - this->thisApi().virtualWriteSingle(pProp->id.getInt(), pProp->value->getLongLong()); + this->thisApi().virtualWriteSingle(pProp->id.getInt(), pProp->value->getLongLong(), false); break; case WrapperTypeT::WRAPPER_TYPE_UNSIGNED_LONG_LONG: - this->thisApi().virtualWriteSingle(pProp->id.getInt(), pProp->value->getUnsignedLongLong()); + this->thisApi().virtualWriteSingle(pProp->id.getInt(), pProp->value->getUnsignedLongLong(), false); break; case WrapperTypeT::WRAPPER_TYPE_FLOAT: - this->thisApi().virtualWriteSingle(pProp->id.getInt(), pProp->value->getFloat()); + this->thisApi().virtualWriteSingle(pProp->id.getInt(), pProp->value->getFloat(), false); break; case WrapperTypeT::WRAPPER_TYPE_DOUBLE: - this->thisApi().virtualWriteSingle(pProp->id.getInt(), pProp->value->getDouble()); + this->thisApi().virtualWriteSingle(pProp->id.getInt(), pProp->value->getDouble(), false); break; case WrapperTypeT::WRAPPER_TYPE_NUMBER: - this->thisApi().virtualWriteSingle(pProp->id.getInt(), pProp->value->getNumber()); + this->thisApi().virtualWriteSingle(pProp->id.getInt(), pProp->value->getNumber(), false); break; case WrapperTypeT::WRAPPER_TYPE_STRING: if (pProp->value->getString() != nullptr) { - this->thisApi().virtualWriteSingle(pProp->id.getInt(), pProp->value->getString()); + this->thisApi().virtualWriteSingle(pProp->id.getInt(), pProp->value->getString(), false); + } + break; + case WrapperTypeT::WRAPPER_TYPE_OBJECT: + case WrapperTypeT::WRAPPER_TYPE_ARRAY: + if (pProp->value->getString() != nullptr) { + this->thisApi().virtualWriteSingle(pProp->id.getInt(), pProp->value->toJSON(), + pProp->value->isSendJSON() | ERA_API_JSON); } break; default: diff --git a/src/ERa/ERaProtocol.hpp b/src/ERa/ERaProtocol.hpp index 84bbc47..7eb9069 100644 --- a/src/ERa/ERaProtocol.hpp +++ b/src/ERa/ERaProtocol.hpp @@ -1044,6 +1044,9 @@ bool ERaProto::sendPinData(ERaRsp_t& rsp) { else if (rsp.param.isString()) { cJSON_AddStringToObject(root, name, rsp.param.getString()); } + else if (rsp.param.isObject() && (rsp.json || rsp.param.getObject()->isSendJSON())) { + cJSON_AddItemToObject(root, name, rsp.param.getObject()->duplicateObject()); + } else if (rsp.param.isObject()) { cJSON_AddStringToObject(root, name, rsp.param.getObject()->getString()); } @@ -1090,6 +1093,9 @@ bool ERaProto::sendConfigIdData(ERaRsp_t& rsp) { else if (rsp.param.isString()) { cJSON_AddStringToObject(root, "v", rsp.param.getString()); } + else if (rsp.param.isObject() && (rsp.json || rsp.param.getObject()->isSendJSON())) { + cJSON_AddItemToObject(root, "v", rsp.param.getObject()->duplicateObject()); + } else if (rsp.param.isObject()) { cJSON_AddStringToObject(root, "v", rsp.param.getObject()->getString()); } @@ -1239,6 +1245,9 @@ void ERaProto::sendCommand(const char* auth, ERaRsp_t& rsp, ApiDa else if (rsp.param.isString()) { cJSON_AddStringToObject(root, "value", rsp.param.getString()); } + else if (rsp.param.isObject() && (rsp.json || rsp.param.getObject()->isSendJSON())) { + cJSON_AddItemToObject(root, "value", rsp.param.getObject()->duplicateObject()); + } else if (rsp.param.isObject()) { cJSON_AddStringToObject(root, "value", rsp.param.getObject()->getString()); } @@ -1270,6 +1279,9 @@ void ERaProto::sendCommandVirtualMulti(const char* auth, ERaRsp_t else if (it.isBool() || it.isNumber()) { rsp.param = it.getDouble(); } + else if (it.isObject() || it.isArray()) { + rsp.param = it.getObject(); + } this->sendCommand(auth, rsp); } } @@ -1340,12 +1352,7 @@ void ERaProto::sendCommandVirtual(ERaRsp_t& rsp, ERaDataJson* dat if (configId < 0) { cJSON* item = data->detach(it); FormatString(name, "virtual_pin_%s", it.getName()); - if (it.isString()) { - dataNoConfig.add(name, item); - } - else if (it.isBool() || it.isNumber()) { - dataNoConfig.add(name, item); - } + dataNoConfig.add(name, item); if (!current) { it.shared(data->begin()); } @@ -1384,9 +1391,10 @@ void ERaProto::sendCommandVirtual(ERaRsp_t& rsp, ERaDataJson* dat if (root == nullptr) { return; } - int mbTotal {0}; - int mbFailed {0}; - int mbWriteFailed {0}; + int mbFailRead {0}; + int mbFailWrite {0}; + int mbTotalRead {0}; + int mbTotalWrite {0}; size_t index {0}; size_t dataLen = ERaMax(data->getLen(), data->getDataLen()) + 1; char* mbData = (char*)ERA_CALLOC(dataLen, sizeof(char)); @@ -1402,15 +1410,19 @@ void ERaProto::sendCommandVirtual(ERaRsp_t& rsp, ERaDataJson* dat for (ERaDataBuff::iterator it = data->begin(data->getDataLen()); it < e; ++it) { if (it == MODBUS_STRING_FAIL_READ) { ++it; - mbFailed = it.getInt(); + mbFailRead = it.getInt(); } else if (it == MODBUS_STRING_FAIL_WRITE) { ++it; - mbWriteFailed = it.getInt(); + mbFailWrite = it.getInt(); + } + else if (it == MODBUS_STRING_TOTAL_READ) { + ++it; + mbTotalRead = it.getInt(); } - else if (it == MODBUS_STRING_TOTAL) { + else if (it == MODBUS_STRING_TOTAL_WRITE) { ++it; - mbTotal = it.getInt(); + mbTotalWrite = it.getInt(); } else if (it == MODBUS_STRING_SCAN) { ++it; @@ -1427,9 +1439,10 @@ void ERaProto::sendCommandVirtual(ERaRsp_t& rsp, ERaDataJson* dat } cJSON_AddStringToObject(root, INFO_MB_DATA, mbData); cJSON_AddStringToObject(root, INFO_MB_ACK, mbAck); - cJSON_AddNumberToObject(root, INFO_MB_READ_FAIL, mbFailed); - cJSON_AddNumberToObject(root, INFO_MB_WRITE_FAIL, mbWriteFailed); - cJSON_AddNumberToObject(root, INFO_MB_TOTAL, mbTotal); + cJSON_AddNumberToObject(root, INFO_MB_FAIL_READ, mbFailRead); + cJSON_AddNumberToObject(root, INFO_MB_FAIL_WRITE, mbFailWrite); + cJSON_AddNumberToObject(root, INFO_MB_TOTAL_READ, mbTotalRead); + cJSON_AddNumberToObject(root, INFO_MB_TOTAL_WRITE, mbTotalWrite); Base::addModbusInfo(root); if (mbScan != nullptr) { cJSON_AddStringToObject(root, INFO_MB_SCAN, mbScan); diff --git a/src/ERa/ERaReport.cpp b/src/ERa/ERaReport.cpp index ab77e4c..c04c74a 100644 --- a/src/ERa/ERaReport.cpp +++ b/src/ERa/ERaReport.cpp @@ -45,10 +45,10 @@ void ERaReport::run() { if (!this->isValidReport(pReport)) { continue; } - if (this->getFlag(pReport->called, ReportFlagT::REPORT_ON_DELETE)) { + if (!this->isCalled(pReport, ReportFlagT::REPORT_ON_CALLED)) { continue; } - if (!this->isCalled(pReport, ReportFlagT::REPORT_ON_CALLED)) { + if (this->getFlag(pReport->called, ReportFlagT::REPORT_ON_DELETE)) { continue; } if (pReport->callback_p == nullptr) { diff --git a/src/ERa/ERaTimer.cpp b/src/ERa/ERaTimer.cpp index 6adccf6..e97c85d 100644 --- a/src/ERa/ERaTimer.cpp +++ b/src/ERa/ERaTimer.cpp @@ -33,10 +33,10 @@ void ERaTimer::run() { if (!this->isValidTimer(pTimer)) { continue; } - if (this->getFlag(pTimer->called, TimerFlagT::TIMER_ON_DELETE)) { + if (!this->isCalled(pTimer, TimerFlagT::TIMER_ON_CALLED)) { continue; } - if (!this->isCalled(pTimer, TimerFlagT::TIMER_ON_CALLED)) { + if (this->getFlag(pTimer->called, TimerFlagT::TIMER_ON_DELETE)) { continue; } if (pTimer->callback_p == nullptr) { diff --git a/src/ERa/ERaVersion.hpp b/src/ERa/ERaVersion.hpp index 0a093c1..e92af4a 100644 --- a/src/ERa/ERaVersion.hpp +++ b/src/ERa/ERaVersion.hpp @@ -3,7 +3,7 @@ #define ERA_MAJOR 1 #define ERA_MINOR 3 -#define ERA_PATCH 2 +#define ERA_PATCH 3 #define ERA_VERSION_TO_STR_2(val) # val #define ERA_VERSION_TO_STR(val) ERA_VERSION_TO_STR_2(val) @@ -14,10 +14,10 @@ #define ERA_VERSION_NUMBER ERA_VERSION_VAL(ERA_MAJOR, \ ERA_MINOR, ERA_PATCH) -#define ERA_VERSION ERA_VERSION_TO_STR(ERA_MAJOR) "." \ - ERA_VERSION_TO_STR(ERA_MINOR) "." \ +#define ERA_VERSION ERA_VERSION_TO_STR(ERA_MAJOR) "." \ + ERA_VERSION_TO_STR(ERA_MINOR) "." \ ERA_VERSION_TO_STR(ERA_PATCH) -#define ERA_VERSION_1_3_2 +#define ERA_VERSION_1_3_3 #if !defined(ERA_FIRMWARE_VERSION) #define ERA_FIRMWARE_VERSION ERA_VERSION diff --git a/src/ERa/types/WrapperArray.hpp b/src/ERa/types/WrapperArray.hpp new file mode 100644 index 0000000..912496a --- /dev/null +++ b/src/ERa/types/WrapperArray.hpp @@ -0,0 +1,49 @@ +#ifndef INC_WRAPPER_ARRAY_HPP_ +#define INC_WRAPPER_ARRAY_HPP_ + +#include "WrapperObject.hpp" + +class WrapperArray + : public WrapperObject +{ +public: + WrapperArray(ERaDataJson& js) + : WrapperObject(js) + { + this->type = WrapperTypeT::WRAPPER_TYPE_ARRAY; + } + ~WrapperArray() override + { + if (!this->options) { + return; + } + this->value.~ERaDataJson(); + } + + WrapperObject& operator = (const ERaDataJson& rjs) override { + if (rjs.isArray()) { + this->value = rjs; + } + return (*this); + } + + void setOptions(int option) override { + this->options = option; + } + + void setPointer(const void* cptr) override { + cJSON* root = cJSON_Parse((const char*)cptr); + if (cJSON_IsArray(root)) { + this->value.setObject(root, true); + } + else { + cJSON_Delete(root); + } + root = nullptr; + } + +private: + int options; +}; + +#endif /* INC_WRAPPER_ARRAY_HPP_ */ diff --git a/src/ERa/types/WrapperBase.hpp b/src/ERa/types/WrapperBase.hpp index 89c99fa..58e5154 100644 --- a/src/ERa/types/WrapperBase.hpp +++ b/src/ERa/types/WrapperBase.hpp @@ -20,7 +20,9 @@ enum WrapperTypeT WRAPPER_TYPE_FLOAT = 8, WRAPPER_TYPE_DOUBLE = 9, WRAPPER_TYPE_NUMBER = 10, - WRAPPER_TYPE_STRING = 11 + WRAPPER_TYPE_STRING = 11, + WRAPPER_TYPE_OBJECT = 12, + WRAPPER_TYPE_ARRAY = 13 }; class WrapperBase @@ -74,13 +76,23 @@ class WrapperBase bool isNumber() const { return ((this->type != WrapperTypeT::WRAPPER_TYPE_INVALID) && - (this->type != WrapperTypeT::WRAPPER_TYPE_STRING)); + (this->type != WrapperTypeT::WRAPPER_TYPE_STRING) && + (this->type != WrapperTypeT::WRAPPER_TYPE_OBJECT) && + (this->type != WrapperTypeT::WRAPPER_TYPE_ARRAY)); } bool isString() const { return this->type == WrapperTypeT::WRAPPER_TYPE_STRING; } + bool isObject() const { + return this->type == WrapperTypeT::WRAPPER_TYPE_OBJECT; + } + + bool isArray() const { + return this->type == WrapperTypeT::WRAPPER_TYPE_ARRAY; + } + virtual bool updated() { return false; } @@ -133,12 +145,18 @@ class WrapperBase } ERaDataJson toJSON() const { - if (!this->isString()) { + if (!this->isString() && + !this->isObject() && + !this->isArray()) { return ERaDataJson(); } return ERaDataJson((const char*)this->getPointer()); } + virtual bool isSendJSON() const { + return false; + } + operator double() const { return this->get(); } @@ -159,11 +177,11 @@ class WrapperBase if (this == &rwb) { return (*this); } - if (this->isString()) { - return operator = ((const char*)rwb.getPointer()); + if (this->isNumber()) { + return operator = (rwb.get()); } else { - return operator = (rwb.get()); + return operator = ((const char*)rwb.getPointer()); } } @@ -207,7 +225,7 @@ class WrapperBase } template - WrapperBase& operator = (T rt) { + WrapperBase& operator = (const T& rt) { return operator = ((double)rt); } @@ -224,11 +242,11 @@ class WrapperBase if (this == &rwb) { return true; } - if (this->isString()) { - return operator == ((const char*)rwb.getPointer()); + if (this->isNumber()) { + return operator == (rwb.get()); } else { - return operator == (rwb.get()); + return operator == ((const char*)rwb.getPointer()); } } @@ -269,7 +287,7 @@ class WrapperBase } template - bool operator == (T rt) const { + bool operator == (const T& rt) const { return operator == ((double)rt); } @@ -285,11 +303,11 @@ class WrapperBase if (this == &rwb) { return false; } - if (this->isString()) { - return operator != ((const char*)rwb.getPointer()); + if (this->isNumber()) { + return operator != (rwb.get()); } else { - return operator != (rwb.get()); + return operator != ((const char*)rwb.getPointer()); } } @@ -330,7 +348,7 @@ class WrapperBase } template - bool operator != (T rt) const { + bool operator != (const T& rt) const { return operator != ((double)rt); } @@ -346,11 +364,11 @@ class WrapperBase if (this == &rwb) { return false; } - if (this->isString()) { - return operator > ((const char*)rwb.getPointer()); + if (this->isNumber()) { + return operator > (rwb.get()); } else { - return operator > (rwb.get()); + return operator > ((const char*)rwb.getPointer()); } } @@ -394,7 +412,7 @@ class WrapperBase } template - bool operator > (T rt) const { + bool operator > (const T& rt) const { return operator > ((double)rt); } @@ -413,11 +431,11 @@ class WrapperBase if (this == &rwb) { return false; } - if (this->isString()) { - return operator < ((const char*)rwb.getPointer()); + if (this->isNumber()) { + return operator < (rwb.get()); } else { - return operator < (rwb.get()); + return operator < ((const char*)rwb.getPointer()); } } @@ -461,7 +479,7 @@ class WrapperBase } template - bool operator < (T rt) const { + bool operator < (const T& rt) const { return operator < ((double)rt); } @@ -480,11 +498,11 @@ class WrapperBase if (this == &rwb) { return true; } - if (this->isString()) { - return operator >= ((const char*)rwb.getPointer()); + if (this->isNumber()) { + return operator >= (rwb.get()); } else { - return operator >= (rwb.get()); + return operator >= ((const char*)rwb.getPointer()); } } @@ -528,7 +546,7 @@ class WrapperBase } template - bool operator >= (T rt) const { + bool operator >= (const T& rt) const { return operator >= ((double)rt); } @@ -547,11 +565,11 @@ class WrapperBase if (this == &rwb) { return true; } - if (this->isString()) { - return operator <= ((const char*)rwb.getPointer()); + if (this->isNumber()) { + return operator <= (rwb.get()); } else { - return operator <= (rwb.get()); + return operator <= ((const char*)rwb.getPointer()); } } @@ -595,7 +613,7 @@ class WrapperBase } template - bool operator <= (T rt) const { + bool operator <= (const T& rt) const { return operator <= ((double)rt); } diff --git a/src/ERa/types/WrapperNumber.hpp b/src/ERa/types/WrapperNumber.hpp index 8883b03..6aacd38 100644 --- a/src/ERa/types/WrapperNumber.hpp +++ b/src/ERa/types/WrapperNumber.hpp @@ -14,7 +14,7 @@ class WrapperNumber this->type = WrapperTypeT::WRAPPER_TYPE_NUMBER; } - WrapperNumber& operator = (T num) { + WrapperNumber& operator = (const T& num) { this->value = num; return (*this); } diff --git a/src/ERa/types/WrapperObject.hpp b/src/ERa/types/WrapperObject.hpp index 4a0077f..d13ad0a 100644 --- a/src/ERa/types/WrapperObject.hpp +++ b/src/ERa/types/WrapperObject.hpp @@ -11,10 +11,10 @@ class WrapperObject : value(js) , local() { - this->type = WrapperTypeT::WRAPPER_TYPE_STRING; + this->type = WrapperTypeT::WRAPPER_TYPE_OBJECT; } - WrapperObject& operator = (const ERaDataJson& rjs) { + virtual WrapperObject& operator = (const ERaDataJson& rjs) { this->value = rjs; return (*this); } @@ -31,6 +31,10 @@ class WrapperObject return true; } + bool isSendJSON() const override { + return this->value.isSendJSON(); + } + protected: double get() const override { return 0.0f; @@ -45,10 +49,14 @@ class WrapperObject } void setPointer(const void* cptr) override { - this->value.parse((const char*)cptr); + cJSON* root = cJSON_Parse((const char*)cptr); + if (root != nullptr) { + this->value.setObject(root, true); + } + root = nullptr; } -private: +protected: ERaDataJson& value; ERaDataJson local; }; diff --git a/src/ERa/types/WrapperString.hpp b/src/ERa/types/WrapperString.hpp index a82f4a7..9b7e771 100644 --- a/src/ERa/types/WrapperString.hpp +++ b/src/ERa/types/WrapperString.hpp @@ -629,11 +629,23 @@ class WrapperString; return this->indexOf(c, 0); } + int indexOfIgnoreCase(char c) const { + return this->indexOfIgnoreCase(c, 0); + } + int indexOf(char c, size_t index) const { + return this->indexOfCaseSensitive(c, index, true); + } + + int indexOfIgnoreCase(char c, size_t index) const { + return this->indexOfCaseSensitive(c, index, false); + } + + int indexOfCaseSensitive(char c, size_t index, bool caseSensitive) const { if (index >= this->length()) { return -1; } - const char* ptr = strchr(this->value.buffer + index, c); + const char* ptr = ERaStrChr(this->value.buffer + index, c, caseSensitive); if (ptr == nullptr) { return -1; } @@ -644,14 +656,26 @@ class WrapperString; return this->indexOf(str, 0); } + int indexOfIgnoreCase(const ERaString& str) const { + return this->indexOfIgnoreCase(str, 0); + } + int indexOf(const ERaString& str, size_t index) const { + return this->indexOfCaseSensitive(str, index, true); + } + + int indexOfIgnoreCase(const ERaString& str, size_t index) const { + return this->indexOfCaseSensitive(str, index, false); + } + + int indexOfCaseSensitive(const ERaString& str, size_t index, bool caseSensitive) const { if (!str.length()) { return -1; } if (index >= this->length()) { return -1; } - const char* ptr = strstr(this->value.buffer + index, str.value.buffer); + const char* ptr = ERaStrStr(this->value.buffer + index, str.value.buffer, caseSensitive); if (ptr == nullptr) { return -1; } @@ -662,13 +686,25 @@ class WrapperString; return this->lastIndexOf(c, this->length() - 1); } + int lastIndexOfIgnoreCase(char c) const { + return this->lastIndexOfIgnoreCase(c, this->length() - 1); + } + int lastIndexOf(char c, size_t index) const { + return this->lastIndexOfCaseSensitive(c, index, true); + } + + int lastIndexOfIgnoreCase(char c, size_t index) const { + return this->lastIndexOfCaseSensitive(c, index, false); + } + + int lastIndexOfCaseSensitive(char c, size_t index, bool caseSensitive) const { if (index >= this->length()) { return -1; } const char temp = this->value.buffer[index + 1]; this->value.buffer[index + 1] = '\0'; - const char* ptr = strrchr(this->value.buffer, c); + const char* ptr = ERaStrrChr(this->value.buffer, c, caseSensitive); this->value.buffer[index + 1] = temp; if (ptr == nullptr) { return -1; @@ -680,7 +716,19 @@ class WrapperString; return this->lastIndexOf(str, this->length() - str.length()); } + int lastIndexOfIgnoreCase(const ERaString& str) const { + return this->lastIndexOfIgnoreCase(str, this->length() - str.length()); + } + int lastIndexOf(const ERaString& str, size_t index) const { + return this->lastIndexOfCaseSensitive(str, index, true); + } + + int lastIndexOfIgnoreCase(const ERaString& str, size_t index) const { + return this->lastIndexOfCaseSensitive(str, index, false); + } + + int lastIndexOfCaseSensitive(const ERaString& str, size_t index, bool caseSensitive) const { if (!this->length() || !str.length()) { return -1; } @@ -692,7 +740,7 @@ class WrapperString; } int found = -1; for (char* ptr = this->value.buffer; ptr <= (this->value.buffer + index); ++ptr) { - ptr = strstr(ptr, str.value.buffer); + ptr = ERaStrStr(ptr, str.value.buffer, caseSensitive); if (ptr == nullptr) { break; } diff --git a/src/ERa/types/WrapperTypes.hpp b/src/ERa/types/WrapperTypes.hpp index 4e13d6f..b09a777 100644 --- a/src/ERa/types/WrapperTypes.hpp +++ b/src/ERa/types/WrapperTypes.hpp @@ -13,6 +13,7 @@ #include "WrapperNumber.hpp" #include "WrapperString.hpp" #include "WrapperObject.hpp" +#include "WrapperArray.hpp" #include "CloudColor.hpp" typedef bool CloudContact; diff --git a/src/Modbus/ERaDefineModbus.hpp b/src/Modbus/ERaDefineModbus.hpp index d4d3912..695ec04 100644 --- a/src/Modbus/ERaDefineModbus.hpp +++ b/src/Modbus/ERaDefineModbus.hpp @@ -42,9 +42,10 @@ #define MODBUS_SINGLE_COIL_OFF (uint16_t)0x0000 #define MODBUS_STRING_SCAN "scan" -#define MODBUS_STRING_TOTAL "total" #define MODBUS_STRING_FAIL_READ "fail_read" #define MODBUS_STRING_FAIL_WRITE "fail_write" +#define MODBUS_STRING_TOTAL_READ "total_read" +#define MODBUS_STRING_TOTAL_WRITE "total_write" #if !defined(ERA_MAX_REGISTERS) #define ERA_MAX_REGISTERS ERA_MAX_VIRTUAL_PINS @@ -115,4 +116,18 @@ typedef struct __ModbusAction_t { uint16_t param; } ModbusAction_t; +typedef struct __ModbusActionRaw_t { + uint8_t addr; + uint8_t func; + uint8_t sa1; + uint8_t sa2; + uint8_t len1; + uint8_t len2; + uint8_t* pExtra; + size_t pExtraLen; + uint16_t prevdelay; + uint16_t postdelay; + IPSlave_t ip; +} ModbusActionRaw_t; + #endif /* INC_ERA_DEFINE_MODBUS_HPP_ */ diff --git a/src/Modbus/ERaModbus.hpp b/src/Modbus/ERaModbus.hpp index c0fd7a4..b222019 100644 --- a/src/Modbus/ERaModbus.hpp +++ b/src/Modbus/ERaModbus.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include using namespace std; @@ -17,6 +18,7 @@ using namespace std; template class ERaModbus : public ERaModbusStream + , public ERaModbusAction , public ERaModbusTransp < ERaModbus > , public ERaModbusSlave < ERaModbus > { @@ -32,6 +34,7 @@ class ERaModbus const IPAddress ipNone{0, 0, 0, 0}; typedef ERaModbusStream ModbusStream; + typedef ERaModbusAction ModbusAction; friend class ERaModbusTransp < ERaModbus >; typedef ERaModbusTransp < ERaModbus > ModbusTransp; @@ -53,9 +56,10 @@ class ERaModbus , prevMillis(0) , predelay(0) , postdelay(0) - , total(0) , failRead(0) , failWrite(0) + , totalRead(0) + , totalWrite(0) , dePin(-1) , transp(0) , streamRTU(NULL) @@ -76,7 +80,9 @@ class ERaModbus , messageHandle(NULL) , mutex(NULL) , skipModbus(false) + , skipPubModbus(false) , wifiConfig(false) + , actionWriteTask(true) , runApiResponse(true) {} ~ERaModbus() @@ -133,10 +139,18 @@ class ERaModbus this->skipModbus = skip; } + void setSkipPublishModbus(bool skip) { + this->skipPubModbus = skip; + } + void setConnectWiFiModbus(bool enable) { this->wifiConfig = enable; } + void setActionOnReadTaskModbus(bool enable) { + this->actionWriteTask = !enable; + } + protected: void begin() { ERaModbusEntry::getConfig(); @@ -170,6 +184,8 @@ class ERaModbus if (!ERaRemainingTime(this->modbusConfig->modbusInterval.prevMillis, this->modbusConfig->modbusInterval.delay)) { this->modbusConfig->modbusInterval.prevMillis = ERaMillis(); this->readModbusConfig(); + this->getModbusAction(false); + this->getModbusActionRaw(false); ModbusStream::streamStart(); } else { @@ -188,7 +204,8 @@ class ERaModbus void runWrite(bool forever = true) { this->waitTCPIpReady(!forever); for (;;) { - this->getModbusAction(); + this->getModbusAction(true); + this->getModbusActionRaw(true); #if defined(ERA_PNP_MODBUS) this->processModbusControl(); #endif @@ -264,35 +281,6 @@ class ERaModbus this->dataBuff.clearBuffer(); } - bool addModbusAction(const char* ptr) { - return this->addModbusAction(ptr, ModbusActionTypeT::MODBUS_ACTION_DEFAULT, 0); - } - - bool addModbusAction(const char* ptr, uint16_t param) { - return this->addModbusAction(ptr, ModbusActionTypeT::MODBUS_ACTION_PARAMS, param); - } - - bool addModbusAction(const char* ptr, uint8_t type, uint16_t param) { - if (strlen(ptr) != 36) { - return false; - } - if (!this->queue.writeable()) { - return false; - } - char* buf = (char*)ERA_MALLOC(37 * sizeof(char)); - if (buf == nullptr) { - return false; - } - ModbusAction_t req; - req.key = buf; - req.type = type; - req.param = param; - memset(req.key, 0, 37 * sizeof(char)); - strcpy(req.key, ptr); - this->queue += req; - return true; - } - void initModbusTask(); #if defined(LINUX) @@ -402,23 +390,42 @@ class ERaModbus if (this->clientTCP == nullptr) { return; } - if (param.ipSlave.ip.dword) { - this->switchToModbusTCP(); + if (!param.ipSlave.ip.dword) { + return; + } + if (!param.ipSlave.port) { + return; } - if ((ip.ip == IPAddress(param.ipSlave.ip.dword)) || - (param.ipSlave.ip.dword == 0)) { + if (!this->thisApi().afterNetwork()) { return; } - if (param.ipSlave.port == 0) { + this->switchToModbusTCP(); + if (this->ip.ip != IPAddress(param.ipSlave.ip.dword)) { + } + else if (this->clientTCP->connected()) { return; } - ip.ip = param.ipSlave.ip.dword; - ip.port = param.ipSlave.port; + this->ip.ip = param.ipSlave.ip.dword; + this->ip.port = param.ipSlave.port; // TODO + this->connectTcpIp(2); + } + + void connectTcpIp(size_t retry) { if (this->clientTCP->connected()) { this->clientTCP->stop(); } - this->clientTCP->connect(ip.ip, ip.port); + + do { + this->clientTCP->connect(this->ip.ip, this->ip.port); + if (this->clientTCP->connected()) { + break; + } + ERA_LOG(TAG, "Connect to %d.%d.%d.%d:%d failed", this->ip.ip[0], this->ip.ip[1], + this->ip.ip[2], this->ip.ip[3], + this->ip.port); + ERaDelay(1000); + } while (--retry); } void switchToModbusRTU() { @@ -431,19 +438,22 @@ class ERaModbus this->transp = ModbusTransportT::MODBUS_TRANSPORT_TCP; } - void getModbusAction() { - if (!this->isRequest()) { + void getModbusAction(bool writeTask) { + if (this->actionWriteTask != writeTask) { + return; + } + if (!ModbusAction::isRequest()) { return; } if (ModbusState::is(ModbusStateT::STATE_MB_PARSE)) { return; } - ModbusAction_t& req = this->getRequest(); + ModbusAction_t& req = ModbusAction::getRequest(); if (req.key == nullptr) { return; } this->actionModbus(req); - if (this->isEmptyRequest()) { + if (ModbusAction::isEmptyRequest()) { this->executeNow(); if (!ModbusState::is(ModbusStateT::STATE_MB_PARSE)) { ModbusState::set(ModbusStateT::STATE_MB_CONTROLLED); @@ -453,12 +463,37 @@ class ERaModbus req.key = nullptr; } + void getModbusActionRaw(bool writeTask) { + if (this->actionWriteTask != writeTask) { + return; + } + if (!ModbusAction::isRawRequest()) { + return; + } + if (ModbusState::is(ModbusStateT::STATE_MB_PARSE)) { + return; + } + ModbusActionRaw_t& req = ModbusAction::getRawRequest(); + this->actionModbusRaw(req); + if (ModbusAction::isEmptyRawRequest()) { + this->executeNow(); + if (!ModbusState::is(ModbusStateT::STATE_MB_PARSE)) { + ModbusState::set(ModbusStateT::STATE_MB_CONTROLLED); + } + } + if (req.pExtra != nullptr) { + free(req.pExtra); + req.pExtra = nullptr; + } + } + void readModbusConfig(); bool checkPubDataInterval(); void delayModbus(const int address, bool unlock = false, bool skip = false); void delays(MillisTime_t ms); ModbusConfigAlias_t* getModbusAlias(const char* key); ModbusConfig_t* getModbusConfig(int id); + ModbusConfig_t* getModbusConfigWithAddress(uint8_t addr); void addScanData(); void processModbusScan(); #if defined(ERA_PNP_MODBUS) @@ -467,6 +502,7 @@ class ERaModbus void writeAllModbusWithOption(bool execute = false); bool actionModbus(ModbusAction_t& request); bool eachActionModbus(ModbusAction_t& request, Action_t& action, ModbusConfig_t*& config); + bool actionModbusRaw(ModbusActionRaw_t& request); void sendModbusRead(ModbusConfig_t& param); bool handlerModbusRead(ModbusConfig_t* param); bool sendModbusWrite(ModbusConfig_t& param); @@ -501,10 +537,16 @@ class ERaModbus } #endif - void updateTotalTransmit() { - if (this->total++ > 99) { - this->total = 1; + void updateTotalRead() { + if (this->totalRead++ > 99) { + this->totalRead = 1; this->failRead = 0; + } + } + + void updateTotalWrite() { + if (this->totalWrite++ > 99) { + this->totalWrite = 1; this->failWrite = 0; } } @@ -516,9 +558,13 @@ class ERaModbus if (this->clientTCP == nullptr) { return; } + MillisTime_t startMillis = ERaMillis(); do { ERaDelay(1000); - } while (!this->thisApi().connected()); + if (this->thisApi().afterNetwork()) { + break; + } + } while (ERaRemainingTime(startMillis, ERA_MODBUS_WAIT_TCP_MS)); } bool getBit(uint8_t byte, uint8_t pos) { @@ -529,18 +575,6 @@ class ERaModbus this->modbusConfig->modbusInterval.prevMillis = (ERaMillis() - this->modbusConfig->modbusInterval.delay + ERA_MODBUS_EXECUTE_MS); } - bool isRequest() { - return this->queue.readable(); - } - - ModbusAction_t& getRequest() { - return this->queue; - } - - bool isEmptyRequest() { - return this->queue.isEmpty(); - } - inline const Api& thisApi() const { return static_cast(*this); @@ -558,7 +592,6 @@ class ERaModbus ERaTimer::iterator timerFatal; #endif - ERaQueue queue; ERaDataBuffDynamic dataBuff; ERaModbusEntry*& modbusConfig; ERaModbusEntry*& modbusControl; @@ -568,9 +601,10 @@ class ERaModbus unsigned long prevMillis; unsigned long predelay; unsigned long postdelay; - int total; int failRead; int failWrite; + int totalRead; + int totalWrite; int dePin; uint8_t transp; Stream* streamRTU; @@ -591,7 +625,9 @@ class ERaModbus QueueMessage_t messageHandle; ERaMutex_t mutex; bool skipModbus; + bool skipPubModbus; bool wifiConfig; + bool actionWriteTask; volatile bool runApiResponse; }; @@ -621,10 +657,13 @@ void ERaModbus::readModbusConfig() { #endif this->dataBuff.add_multi(ERA_F(MODBUS_STRING_FAIL_READ), this->failRead, ERA_F(MODBUS_STRING_FAIL_WRITE), this->failWrite, - ERA_F(MODBUS_STRING_TOTAL), this->total); + ERA_F(MODBUS_STRING_TOTAL_READ), this->totalRead, + ERA_F(MODBUS_STRING_TOTAL_WRITE), this->totalWrite); this->addScanData(); this->dataBuff.done(); - this->thisApi().modbusDataWrite(&this->dataBuff); + if (!this->skipPubModbus) { + this->thisApi().modbusDataWrite(&this->dataBuff); + } } template @@ -751,13 +790,21 @@ void ERaModbus::processModbusScan() { .sa1 = 0, .sa2 = 0, .len1 = 0, - .len2 = 1 + .len2 = 1, + .extra = {}, + .ipSlave = { + .ip = { + .dword = this->modbusScan->ipSlave.ip.dword + }, + .port = this->modbusScan->ipSlave.port + } }; for (size_t i = this->modbusScan->start; (i < this->modbusScan->end) && (this->modbusScan->numberDevice < this->modbusScan->numberScan); ++i) { param.addr = i; ERaGuardLock(this->mutex); + this->nextTransport(param); if (ModbusTransp::readHoldingRegisters(this->transp, param, true)) { this->modbusScan->addr[this->modbusScan->numberDevice++] = i; } @@ -840,8 +887,10 @@ bool ERaModbus::actionModbus(ModbusAction_t& request) { for (int i = 0; i < alias->readActionCount; ++i) { ERaGuardLock(this->mutex); this->eachActionModbus(request, alias->action[i], config); - if ((config != nullptr) && - (i != (alias->readActionCount - 1))) { + if (config == nullptr) { + continue; + } + if (i != (alias->readActionCount - 1)) { this->delayModbus(config->addr, true); } else { @@ -866,6 +915,20 @@ ModbusConfig_t* ERaModbus::getModbusConfig(int id) { return nullptr; } +template +ModbusConfig_t* ERaModbus::getModbusConfigWithAddress(uint8_t addr) { + const ERaList::iterator* e = this->modbusControl->modbusConfigParam.end(); + for (ERaList::iterator* it = this->modbusControl->modbusConfigParam.begin(); it != e; it = it->getNext()) { + if (it->get() == nullptr) { + continue; + } + if (it->get()->addr == addr) { + return it->get(); + } + } + return nullptr; +} + template bool ERaModbus::eachActionModbus(ModbusAction_t& request, Action_t& action, ModbusConfig_t*& config) { config = this->getModbusConfig(action.id); @@ -895,6 +958,41 @@ bool ERaModbus::eachActionModbus(ModbusAction_t& request, Action_t& action, return this->sendModbusWrite(*config); } +template +bool ERaModbus::actionModbusRaw(ModbusActionRaw_t& request) { + bool status {false}; + ModbusConfig_t config {0}; + ModbusConfig_t* writeConfig = this->getModbusConfigWithAddress(request.addr); + + memcpy(&config.addr, &request.addr, 6); + if (request.pExtra != nullptr) { + memcpy(config.extra, request.pExtra, ERaMin(sizeof(config.extra), request.pExtraLen)); + } + if (request.ip.ip.dword) { + config.ipSlave.port = request.ip.port; + config.ipSlave.ip.dword = request.ip.ip.dword; + } + else if (writeConfig != nullptr) { + config.ipSlave.port = writeConfig->ipSlave.port; + config.ipSlave.ip.dword = writeConfig->ipSlave.ip.dword; + } + + if (request.prevdelay) { + this->delays(request.prevdelay); + } + ERaGuardLock(this->mutex); + status = this->sendModbusWrite(config); + if (request.postdelay) { + this->delays(request.postdelay); + ERaGuardUnlock(this->mutex); + } + else { + this->delayModbus(request.addr, true, true); + } + + return status; +} + template void ERaModbus::writeAllModbus(uint8_t len1, uint8_t len2, const uint8_t* pData, size_t pDataLen, bool force, bool execute) { @@ -929,8 +1027,16 @@ void ERaModbus::writeAllModbus(uint8_t len1, uint8_t len2, const uint8_t* p template void ERaModbus::sendModbusRead(ModbusConfig_t& param) { + bool skip {false}; bool status {false}; this->nextTransport(param); + + if (!this->failCounter.min) { + } + else if (param.totalFail >= this->failCounter.min) { + skip = true; + } + switch (param.func) { case ModbusFunctionT::READ_COIL_STATUS: status = ModbusTransp::readCoilStatus(this->transp, param); @@ -952,9 +1058,9 @@ void ERaModbus::sendModbusRead(ModbusConfig_t& param) { ERaWatchdogFeed(); #endif - if (status) { + if (skip) { } - else { + else if (!status) { this->failRead++; } } @@ -1266,8 +1372,6 @@ bool ERaModbus::waitResponse(ERaModbusRequest* request, ERaModbusResponse* return false; } - this->updateTotalTransmit(); - MillisTime_t startMillis = ERaMillis(); ModbusStream::streamStart(); diff --git a/src/Modbus/ERaModbusAction.hpp b/src/Modbus/ERaModbusAction.hpp new file mode 100644 index 0000000..156ae29 --- /dev/null +++ b/src/Modbus/ERaModbusAction.hpp @@ -0,0 +1,144 @@ +#ifndef INC_ERA_MODBUS_ACTION_HPP_ +#define INC_ERA_MODBUS_ACTION_HPP_ + +#include +#include +#include +#include + +class ERaModbusAction +{ +public: + ERaModbusAction() + {} + ~ERaModbusAction() + {} + + bool addModbusActionRaw(const uint8_t* pData, size_t pDataLen, + uint16_t prevMs = 0U, uint16_t postMs = 0U, IPSlave_t* ip = nullptr) { + if (pData == nullptr) { + return false; + } + if (pDataLen < 6UL) { + return false; + } + return this->addModbusActionRaw(pData[0], pData[1], pData[2], pData[3], pData[4], pData[5], + (pDataLen > 6UL) ? (pData + 6) : nullptr, + (pDataLen > 6UL) ? (pDataLen - 6) : 0UL, prevMs, postMs, ip); + } + + bool addModbusActionRaw(uint8_t addr, uint8_t func, uint16_t reg, uint16_t len, + const uint8_t* pExtra, size_t pExtraLen, + uint16_t prevMs = 0U, uint16_t postMs = 0U, IPSlave_t* ip = nullptr) { + return this->addModbusActionRaw(addr, func, HI_WORD(reg), LO_WORD(reg), HI_WORD(len), + LO_WORD(len), pExtra, pExtraLen, prevMs, postMs, ip); + } + + bool addModbusActionRaw(uint8_t addr, uint8_t func, uint8_t sa1, uint8_t sa2, uint8_t len1, + uint8_t len2, const uint8_t* pExtra, size_t pExtraLen, + uint16_t prevMs = 0U, uint16_t postMs = 0U, IPSlave_t* ip = nullptr); + +protected: + bool addModbusAction(const char* ptr) { + return this->addModbusAction(ptr, ModbusActionTypeT::MODBUS_ACTION_DEFAULT, 0); + } + + bool addModbusAction(const char* ptr, uint16_t param) { + return this->addModbusAction(ptr, ModbusActionTypeT::MODBUS_ACTION_PARAMS, param); + } + + bool addModbusAction(const char* ptr, uint8_t type, uint16_t param); + + bool isRequest() { + return this->queue.readable(); + } + + ModbusAction_t& getRequest() { + return this->queue; + } + + bool isEmptyRequest() { + return this->queue.isEmpty(); + } + + bool isRawRequest() { + return this->queueRaw.readable(); + } + + ModbusActionRaw_t& getRawRequest() { + return this->queueRaw; + } + + bool isEmptyRawRequest() { + return this->queueRaw.isEmpty(); + } + +private: + ERaQueue queue; + ERaQueue queueRaw; +}; + +inline +bool ERaModbusAction::addModbusActionRaw(uint8_t addr, uint8_t func, uint8_t sa1, uint8_t sa2, uint8_t len1, + uint8_t len2, const uint8_t* pExtra, size_t pExtraLen, + uint16_t prevMs, uint16_t postMs, IPSlave_t* ip) { + if (!this->queueRaw.writeable()) { + return false; + } + ModbusActionRaw_t req; + req.addr = addr; + req.func = func; + req.sa1 = sa1; + req.sa2 = sa2; + req.len1 = len1; + req.len2 = len2; + if (pExtraLen && (pExtra != nullptr)) { + req.pExtra = (uint8_t*)malloc(pExtraLen); + } + else { + req.pExtra = nullptr; + } + if (req.pExtra != nullptr) { + req.pExtraLen = pExtraLen; + memcpy(req.pExtra, pExtra, pExtraLen); + } + else { + req.pExtraLen = 0UL; + } + if (ip != nullptr) { + req.ip.port = ip->port; + req.ip.ip.dword = ip->ip.dword; + } + else { + req.ip.port = 0; + req.ip.ip.dword = 0UL; + } + req.prevdelay = prevMs; + req.postdelay = postMs; + this->queueRaw += req; + return true; +} + +inline +bool ERaModbusAction::addModbusAction(const char* ptr, uint8_t type, uint16_t param) { + if (strlen(ptr) != 36) { + return false; + } + if (!this->queue.writeable()) { + return false; + } + char* buf = (char*)ERA_MALLOC(37 * sizeof(char)); + if (buf == nullptr) { + return false; + } + ModbusAction_t req; + req.key = buf; + req.type = type; + req.param = param; + memset(req.key, 0, 37 * sizeof(char)); + strcpy(req.key, ptr); + this->queue += req; + return true; +} + +#endif /* INC_ERA_MODBUS_ACTION_HPP_ */ diff --git a/src/Modbus/ERaModbusConfig.hpp b/src/Modbus/ERaModbusConfig.hpp index c2b8368..cd43a31 100644 --- a/src/Modbus/ERaModbusConfig.hpp +++ b/src/Modbus/ERaModbusConfig.hpp @@ -106,6 +106,10 @@ #define ERA_MODBUS_MUTEX_MS 100UL #endif +#if !defined(ERA_MODBUS_WAIT_TCP_MS) + #define ERA_MODBUS_WAIT_TCP_MS 60000UL +#endif + #if !defined(ERA_DISABLE_PNP_MODBUS) #define ERA_PNP_MODBUS #endif diff --git a/src/Modbus/ERaModbusSlave.hpp b/src/Modbus/ERaModbusSlave.hpp index a7689d4..1ad6018 100644 --- a/src/Modbus/ERaModbusSlave.hpp +++ b/src/Modbus/ERaModbusSlave.hpp @@ -212,7 +212,7 @@ bool ERaModbusSlave::handlerCallbacks() { if (this->callbacks != nullptr) { this->callbacks(this); } - if (this->getSlaveAddress() != pReg->addr) { + if (pReg->addr && (this->getSlaveAddress() != pReg->addr)) { continue; } if (this->getFunction() != pReg->func) { @@ -247,7 +247,7 @@ void ERaModbusSlave::sendResponse(Register_t* pReg) { case ModbusFunctionT::READ_HOLDING_REGISTERS: case ModbusFunctionT::READ_INPUT_REGISTERS: { uint8_t data[5] {0}; - data[0] = pReg->addr; + data[0] = this->getSlaveAddress(); data[1] = (pReg->func | 0x80); data[2] = ModbusStatusT::MODBUS_STATUS_ILLEGAL_DATA_VALUE; uint16_t crc = this->modbusCRC(data, sizeof(data) - 2); diff --git a/src/Modbus/ERaModbusTransp.hpp b/src/Modbus/ERaModbusTransp.hpp index d6287b6..cebf870 100644 --- a/src/Modbus/ERaModbusTransp.hpp +++ b/src/Modbus/ERaModbusTransp.hpp @@ -767,7 +767,7 @@ class ERaModbusTransp ERaModbusResponse* response = new_modbus ERaModbusResponse(request, request->responseLength()); ERA_ASSERT_NULL(response, false) if (ERaModbusInternal::handlerRead(request, response, status)) { - this->thisModbus().updateTotalTransmit(); + this->thisModbus().updateTotalRead(); ERaLogHex("IB <<", response->getMessage(), response->getSize()); } else if (this->thisModbus().failCounter.min && !skip && @@ -777,6 +777,7 @@ class ERaModbusTransp request->getSlaveAddress(), param.totalFail); } else { + this->thisModbus().updateTotalRead(); this->thisModbus().sendRequest(request->getMessage(), request->getSize()); status = this->thisModbus().waitResponse(request, response); } @@ -801,8 +802,8 @@ class ERaModbusTransp ERA_ASSERT_NULL(request, false) ERaModbusResponse* response = new_modbus ERaModbusResponse(request, request->responseLength()); ERA_ASSERT_NULL(response, false) + this->thisModbus().updateTotalWrite(); if (ERaModbusInternal::handlerWrite(request, response, status)) { - this->thisModbus().updateTotalTransmit(); ERaLogHex("IB <<", request->getMessage(), request->getSize()); } else { diff --git a/src/Modbus/ERaParse.hpp b/src/Modbus/ERaParse.hpp index fd8f831..54cddeb 100644 --- a/src/Modbus/ERaParse.hpp +++ b/src/Modbus/ERaParse.hpp @@ -10,6 +10,14 @@ #define MAX_DEVICE_MODBUS 20 #endif +#ifndef MAX_CONFIG_MODBUS + #define MAX_CONFIG_MODBUS 50 +#endif + +#ifndef MAX_ALIAS_MODBUS + #define MAX_ALIAS_MODBUS 100 +#endif + #ifndef DEFAULT_MODBUS_BAUD_SPEED #define DEFAULT_MODBUS_BAUD_SPEED 9600 #endif @@ -54,8 +62,8 @@ typedef struct __ModbusConfig_t { IPSlave_t ipSlave; uint8_t totalFail; uint8_t sizeData; - uint32_t value; uint8_t ack; + uint32_t value; } ModbusConfig_t; typedef struct __Action_t { @@ -85,6 +93,8 @@ typedef struct __IntervalDelay_t { unsigned long prevMillis; } IntervalDelay_t; +class ERaScanEntry; + class ERaModbusEntry { enum ParseConfigT { @@ -100,7 +110,8 @@ class ERaModbusEntry PARSE_CONFIG_ALIAS_TOTAL_ROW = 10, PARSE_CONFIG_ALIAS_DATA = 11, PARSE_IS_BLUETOOTH = 12, - PARSE_AUTO_CLOSING = 13 + PARSE_TCP_IP_CONFIG = 13, + PARSE_AUTO_CLOSING = 14 }; enum ParseIntervalT { @@ -109,6 +120,8 @@ class ERaModbusEntry PARSE_PUB_MODBUS_INTERVAL = 2 }; + friend class ERaScanEntry; + public: ERaModbusEntry() : id(0) @@ -228,13 +241,79 @@ class ERaModbusEntry void parseOneAction(const char* ptr, size_t len, ModbusConfigAlias_t& config); void actOneAction(const int* ptr, size_t len, ModbusConfigAlias_t& config); void processParseIsEnableBluetooth(const char* ptr, size_t len); + void parseOneTcpIp(const char* ptr, size_t len); + void processParseTcpIpConfig(const char* ptr, size_t len); void processParseEnableAutoClosing(const char* ptr, size_t len); + static inline + bool portFromString(IPSlave_t& ip, const char* port) { + uint32_t acc = 0; + while (*port) { + char c = *port++; + if ((c >= '0') && (c <= '9')) { + acc = (acc * 10) + (c - '0'); + if (acc > 65535) { + return false; + } + } + else if (c == '"') { + continue; + } + else { + break; + } + } + ip.port = acc; + return true; + } + + static inline + bool ipFromString(IPSlave_t& ip, const char* address) { + uint16_t acc = 0; + uint8_t dots = 0; + + while (*address) { + char c = *address++; + if ((c >= '0') && (c <= '9')) { + acc = (acc * 10) + (c - '0'); + if (acc > 255) { + return false; + } + } + else if (c == '.') { + if (dots == 3) { + return false; + } + ip.ip.bytes[dots++] = acc; + acc = 0; + } + else if (c == '"') { + continue; + } + else if (c == ':') { + ERaModbusEntry::portFromString(ip, address); + break; + } + else { + return false; + } + } + + if (dots != 3) { + return false; + } + if (ip.port == 0) { + ip.port = 502; + } + ip.ip.bytes[3] = acc; + return true; + } + template T* create() { T* ptr = (T*)ERA_MALLOC(sizeof(T)); if (ptr != nullptr) { - memset(ptr, 0, sizeof(T)); + memset((void*)ptr, 0, sizeof(T)); } return ptr; } @@ -272,6 +351,21 @@ class ERaModbusEntry } } + template + T find(ERaList& value, int id) { + const typename ERaList::iterator* e = value.end(); + for (typename ERaList::iterator* it = value.begin(); it != e; it = it->getNext()) { + T& item = it->get(); + if (item == nullptr) { + continue; + } + if (item->id == id) { + return item; + } + } + return nullptr; + } + }; inline @@ -400,6 +494,9 @@ void ERaModbusEntry::processParseConfig(int part, const char* ptr, size_t len) { case ParseConfigT::PARSE_IS_BLUETOOTH: this->processParseIsEnableBluetooth(ptr, len); break; + case ParseConfigT::PARSE_TCP_IP_CONFIG: + this->processParseTcpIpConfig(ptr, len); + break; case ParseConfigT::PARSE_AUTO_CLOSING: this->processParseEnableAutoClosing(ptr, len); break; @@ -475,7 +572,7 @@ void ERaModbusEntry::processParseConfigSensorDelay(const char* ptr, size_t len) */ SensorDelay_t* sensor = nullptr; - ERaList::iterator* it = nullptr; + ERaList::iterator* it = nullptr; for (size_t i = 0; i < len; ++i) { buf[bufLen++] = ptr[i]; @@ -549,7 +646,7 @@ void ERaModbusEntry::processParseConfigSensorReadWrite(const char* ptr, size_t l if (newItem) { this->modbusConfigParam.put(config); } - if (this->readConfigCount++ >= MAX_DEVICE_MODBUS) { + if (this->readConfigCount++ >= MAX_CONFIG_MODBUS) { break; } } @@ -709,7 +806,7 @@ void ERaModbusEntry::processParseConfigAliasData(const char* ptr, size_t len) { if (newItem) { this->modbusConfigAliasParam.put(config); } - if (this->readConfigAliasCount++ >= MAX_DEVICE_MODBUS) { + if (this->readConfigAliasCount++ >= MAX_ALIAS_MODBUS) { break; } } @@ -847,6 +944,48 @@ void ERaModbusEntry::processParseIsEnableBluetooth(const char* ptr, size_t len) this->isBluetooth = ((ptr[0] == '1') ? true : false); } +inline +void ERaModbusEntry::parseOneTcpIp(const char* ptr, size_t len) { + if (!len) { + return; + } + + LOC_BUFFER_PARSE + + size_t bufLen {0}; + ModbusConfig_t* config = nullptr; + for (size_t i = 0; i < len; ++i) { + buf[bufLen++] = ptr[i]; + if (ptr[i] == ',') { + buf[--bufLen] = '\0'; + bufLen = 0; + config = this->find(this->modbusConfigParam, atoi(buf)); + if (config == nullptr) { + break; + } + if (!ERaModbusEntry::ipFromString(config->ipSlave, ptr + i + 1)) { + config->ipSlave.port = 0; + config->ipSlave.ip.dword = 0UL; + } + break; + } + } + + FREE_BUFFER_PARSE +} + +inline +void ERaModbusEntry::processParseTcpIpConfig(const char* ptr, size_t len) { + size_t position {0}; + + for (size_t i = 0; i < len; ++i) { + if (ptr[i] == '-') { + this->parseOneTcpIp(ptr + position, i - position); + position = i + 1; + } + } +} + inline void ERaModbusEntry::processParseEnableAutoClosing(const char* ptr, size_t len) { if (!len) { @@ -859,11 +998,18 @@ class ERaScanEntry { enum ParseConfigT { PARSE_CONFIG_RANGE = 1, - PARSE_CONFIG_NUMBER_SCAN = 2 + PARSE_CONFIG_NUMBER_SCAN = 2, + PARSE_TCP_IP_CONFIG = 3 }; public: ERaScanEntry() + : start(0) + , end(0) + , numberScan(0) + , numberDevice(0) + , addr {} + , ipSlave {} {} ~ERaScanEntry() {} @@ -873,6 +1019,7 @@ class ERaScanEntry uint8_t numberScan; uint8_t numberDevice; uint8_t addr[MAX_DEVICE_MODBUS]; + IPSlave_t ipSlave; void parseConfig(const char* ptr); @@ -893,6 +1040,7 @@ class ERaScanEntry void processParseConfig(int part, const char* ptr, size_t len); void processParseConfigRange(const char* ptr, size_t len); void processParseConfigNumberScan(const char* ptr, size_t len); + void processParseTcpIpConfig(const char* ptr, size_t len); }; inline @@ -927,6 +1075,9 @@ void ERaScanEntry::processParseConfig(int part, const char* ptr, size_t len) { case ParseConfigT::PARSE_CONFIG_NUMBER_SCAN: this->processParseConfigNumberScan(ptr, len); break; + case ParseConfigT::PARSE_TCP_IP_CONFIG: + this->processParseTcpIpConfig(ptr, len); + break; default: break; } @@ -972,4 +1123,16 @@ void ERaScanEntry::processParseConfigNumberScan(const char* ptr, size_t len) { this->numberScan = atoi(buf); } +inline +void ERaScanEntry::processParseTcpIpConfig(const char* ptr, size_t len) { + if (!len) { + return; + } + + if (!ERaModbusEntry::ipFromString(this->ipSlave, ptr)) { + this->ipSlave.port = 0; + this->ipSlave.ip.dword = 0UL; + } +} + #endif /* INC_ERA_PARSE_HPP_ */ diff --git a/src/PnP/ERaPnPArduino.hpp b/src/PnP/ERaPnPArduino.hpp index 2ee878a..139e638 100644 --- a/src/PnP/ERaPnPArduino.hpp +++ b/src/PnP/ERaPnPArduino.hpp @@ -764,7 +764,9 @@ void ERaPnP::configMode() { ERaState::set(StateT::STATE_WAIT_CONFIG); } #endif - if (ERaConfig.getFlag(ConfigFlagT::CONFIG_FLAG_VALID)) { + if (ERaState::is(StateT::STATE_SWITCH_TO_AP_STA)) { + } + else if (ERaConfig.getFlag(ConfigFlagT::CONFIG_FLAG_VALID)) { if (!ERaRemainingTime(tick, WIFI_NET_CHECK_TIMEOUT)) { ERaState::set(StateT::STATE_SWITCH_TO_STA); break; @@ -1200,6 +1202,7 @@ void ERaPnP::connectNetwork() { /* Udp */ if (ERaConfig.getFlag(ConfigFlagT::CONFIG_FLAG_UDP)) { String content = ERA_F(R"json({"status":"ok","message":"Connected to WiFi"})json"); + ERaDelay(100); udpERa.send(content.c_str()); ERaDelay(100); udpERa.sendBoardInfo(); @@ -1222,7 +1225,9 @@ void ERaPnP::connectNetwork() { /* Udp */ if (ERaConfig.getFlag(ConfigFlagT::CONFIG_FLAG_UDP)) { String content = ERA_F(R"json({"status":"error","message":"Connect WiFi failed"})json"); + ERaDelay(100); udpERa.send(content.c_str()); + ERaDelay(500); ERaConfig.setFlag(ConfigFlagT::CONFIG_FLAG_UDP, false); } /* Udp */ @@ -1394,10 +1399,12 @@ void ERaPnP::switchToAP() { WiFi.mode(WIFI_AP); ERaDelay(2000); WiFi.softAPConfig(WIFI_AP_IP, WIFI_AP_IP, WIFI_AP_Subnet); + ERaDelay(200); WiFi.softAP(ssidAP, WIFI_AP_PASS); #elif defined(RTL8722DM) || \ defined(ARDUINO_AMEBA) WiFi.config(WIFI_AP_IP); + ERaDelay(200); WiFi.apbegin(ssidAP, WIFI_AP_PASS, (char*)this->channelAP, 0); #else #if defined(ERA_ARDUINO_WIFI_ESP_AT) @@ -1405,6 +1412,7 @@ void ERaPnP::switchToAP() { #else WiFi.config(WIFI_AP_IP); #endif + ERaDelay(200); WiFi.beginAP(ssidAP, WIFI_AP_PASS); #endif diff --git a/src/PnP/ERaPnPEsp32.hpp b/src/PnP/ERaPnPEsp32.hpp index b59ce9d..7458e06 100644 --- a/src/PnP/ERaPnPEsp32.hpp +++ b/src/PnP/ERaPnPEsp32.hpp @@ -171,6 +171,7 @@ class ERaPnP uint16_t port, const char* username, const char* password) { + WiFi.persistent(false); WiFi.onEvent(this->wifiCb); WiFi.mode(WIFI_STA); Base::init(); @@ -1345,7 +1346,9 @@ void ERaPnP::configMode() { ERaState::set(StateT::STATE_CONNECTING_CLOUD); break; } - if (ERaConfig.getFlag(ConfigFlagT::CONFIG_FLAG_VALID)) { + if (ERaState::is(StateT::STATE_SWITCH_TO_AP_STA)) { + } + else if (ERaConfig.getFlag(ConfigFlagT::CONFIG_FLAG_VALID)) { if (!ERaRemainingTime(tick, WIFI_NET_CHECK_TIMEOUT)) { ERaState::set(StateT::STATE_SWITCH_TO_STA); break; @@ -1498,6 +1501,7 @@ void ERaPnP::connectNetwork() { /* Udp */ if (ERaConfig.getFlag(ConfigFlagT::CONFIG_FLAG_UDP)) { String content = ERA_F(R"json({"status":"ok","message":"Connected to WiFi"})json"); + ERaDelay(100); this->udpERa.send(content.c_str()); ERaDelay(100); this->udpERa.sendBoardInfo(); @@ -1517,7 +1521,9 @@ void ERaPnP::connectNetwork() { /* Udp */ if (ERaConfig.getFlag(ConfigFlagT::CONFIG_FLAG_UDP)) { String content = ERA_F(R"json({"status":"error","message":"Connect WiFi failed"})json"); + ERaDelay(100); this->udpERa.send(content.c_str()); + ERaDelay(500); ERaConfig.setFlag(ConfigFlagT::CONFIG_FLAG_UDP, false); } /* Udp */ @@ -1693,6 +1699,7 @@ void ERaPnP::switchToAP() { WiFi.mode(WIFI_AP); ERaDelay(2000); WiFi.softAPConfig(WIFI_AP_IP, WIFI_AP_IP, WIFI_AP_Subnet); + ERaDelay(200); WiFi.softAP(ssidAP, WIFI_AP_PASS); ERaDelay(500); ERaWatchdogFeed(); diff --git a/src/PnP/ERaPnPEsp8266.hpp b/src/PnP/ERaPnPEsp8266.hpp index d3cded3..a1c45b0 100644 --- a/src/PnP/ERaPnPEsp8266.hpp +++ b/src/PnP/ERaPnPEsp8266.hpp @@ -161,6 +161,7 @@ class ERaPnP uint16_t port, const char* username, const char* password) { + WiFi.persistent(false); WiFi.mode(WIFI_STA); Base::init(); this->config(auth, host, port, username, password); @@ -1214,7 +1215,9 @@ void ERaPnP::configMode() { else if (!WiFi.softAPgetStationNum()) { this->scanNetworks(); } - if (ERaConfig.getFlag(ConfigFlagT::CONFIG_FLAG_VALID)) { + if (ERaState::is(StateT::STATE_SWITCH_TO_AP_STA)) { + } + else if (ERaConfig.getFlag(ConfigFlagT::CONFIG_FLAG_VALID)) { if (!ERaRemainingTime(tick, WIFI_NET_CHECK_TIMEOUT)) { ERaState::set(StateT::STATE_SWITCH_TO_STA); break; @@ -1351,6 +1354,7 @@ void ERaPnP::connectNetwork() { /* Udp */ if (ERaConfig.getFlag(ConfigFlagT::CONFIG_FLAG_UDP)) { String content = ERA_F(R"json({"status":"ok","message":"Connected to WiFi"})json"); + ERaDelay(100); this->udpERa.send(content.c_str()); ERaDelay(100); this->udpERa.sendBoardInfo(); @@ -1370,7 +1374,9 @@ void ERaPnP::connectNetwork() { /* Udp */ if (ERaConfig.getFlag(ConfigFlagT::CONFIG_FLAG_UDP)) { String content = ERA_F(R"json({"status":"error","message":"Connect WiFi failed"})json"); + ERaDelay(100); this->udpERa.send(content.c_str()); + ERaDelay(500); ERaConfig.setFlag(ConfigFlagT::CONFIG_FLAG_UDP, false); } /* Udp */ @@ -1545,6 +1551,7 @@ void ERaPnP::switchToAP() { WiFi.mode(WIFI_AP); ERaDelay(2000); WiFi.softAPConfig(WIFI_AP_IP, WIFI_AP_IP, WIFI_AP_Subnet); + ERaDelay(200); WiFi.softAP(ssidAP, WIFI_AP_PASS); ERaDelay(500); ERaWatchdogFeed(); diff --git a/src/PnP/ERaPnPTiny.hpp b/src/PnP/ERaPnPTiny.hpp index e40f29a..abdb85c 100644 --- a/src/PnP/ERaPnPTiny.hpp +++ b/src/PnP/ERaPnPTiny.hpp @@ -870,7 +870,9 @@ void ERaPnP::configMode() { ERaWatchdogFeed(); Base::appLoop(); ERaWatchdogFeed(); - if (ERaConfig.getFlag(ConfigFlagT::CONFIG_FLAG_VALID)) { + if (ERaState::is(StateT::STATE_SWITCH_TO_AP_STA)) { + } + else if (ERaConfig.getFlag(ConfigFlagT::CONFIG_FLAG_VALID)) { if (!ERaRemainingTime(tick, WIFI_NET_CHECK_TIMEOUT)) { ERaState::set(StateT::STATE_SWITCH_TO_STA); break; @@ -1322,6 +1324,7 @@ void ERaPnP::connectNetwork() { /* Udp */ if (ERaConfig.getFlag(ConfigFlagT::CONFIG_FLAG_UDP)) { String content = ERA_F(R"json({"status":"ok","message":"Connected to WiFi"})json"); + ERaDelay(100); udpERa.send(content.c_str()); ERaDelay(100); udpERa.sendBoardInfo(); @@ -1341,7 +1344,9 @@ void ERaPnP::connectNetwork() { /* Udp */ if (ERaConfig.getFlag(ConfigFlagT::CONFIG_FLAG_UDP)) { String content = ERA_F(R"json({"status":"error","message":"Connect WiFi failed"})json"); + ERaDelay(100); udpERa.send(content.c_str()); + ERaDelay(500); ERaConfig.setFlag(ConfigFlagT::CONFIG_FLAG_UDP, false); } /* Udp */ diff --git a/src/Utility/ERaUtility.cpp b/src/Utility/ERaUtility.cpp index c704e25..7ca8396 100644 --- a/src/Utility/ERaUtility.cpp +++ b/src/Utility/ERaUtility.cpp @@ -1515,3 +1515,119 @@ bool ERaStrCmp(const char* str, const char* str2) { void ERaStrConcat(char* str, const char* str2) { strcat(str, str2); } + +char* ERaStrStr(const char* str, const char* str2, bool caseSensitive) { + if ((str == NULL) || (str2 == NULL)) { + return NULL; + } + + if (caseSensitive) { + return (char*)strstr(str, str2); + } + + if (!*str2) { + return (char*)str; + } + + for (; *str; ++str) { + if (::tolower((unsigned char)*str) == ::tolower((unsigned char)*str2)) { + const char* h; + const char* n; + for (h = str, n = str2; *h && *n; ++h, ++n) { + if (::tolower((unsigned char)*h) != ::tolower((unsigned char)*n)) { + break; + } + } + if (!*n) { + return (char*)str; + } + } + } + return NULL; +} + +static char* ERaStrrStr(const char* str, const char* str2) { + if (!*str2) { + return (char*)(str + strlen(str)); + } + + const char* result = NULL; + const char* p = str; + + while ((p = strstr(p, str2)) != NULL) { + result = p; + p++; + } + return (char*)result; +} + +char* ERaStrrStr(const char* str, const char* str2, bool caseSensitive) { + if ((str == NULL) || (str2 == NULL)) { + return NULL; + } + + if (caseSensitive) { + return (char*)ERaStrrStr(str, str2); + } + + if (!*str2) { + return (char*)(str + strlen(str)); + } + + const char* result = NULL; + const char* p = str; + + while (*p) { + const char* h = p; + const char* n = str2; + while (*h && *n && (::tolower((unsigned char)*h) == ::tolower((unsigned char)*n))) { + h++; + n++; + } + if (!*n) { + result = p; + } + p++; + } + return (char*)result; +} + +char* ERaStrChr(const char* str, int c, bool caseSensitive) { + if (str == NULL) { + return NULL; + } + + if (caseSensitive) { + return (char*)strchr(str, c); + } + + char lower = ::tolower((unsigned char)c); + while (*str) { + if (::tolower((unsigned char)*str) == lower) { + return (char*)str; + } + str++; + } + return NULL; +} + +char* ERaStrrChr(const char* str, int c, bool caseSensitive) { + if (str == NULL) { + return NULL; + } + + if (caseSensitive) { + return (char*)strrchr(str, c); + } + + char lower = ::tolower((unsigned char)c); + const char* last = NULL; + + while (*str) { + if (::tolower((unsigned char)*str) == lower) { + last = str; + } + str++; + } + return (char*)last; +} diff --git a/src/Utility/ERaUtility.hpp b/src/Utility/ERaUtility.hpp index 872d16d..ded9de7 100644 --- a/src/Utility/ERaUtility.hpp +++ b/src/Utility/ERaUtility.hpp @@ -196,6 +196,11 @@ char* ERaFindStr(const char* str, const char* str2); bool ERaStrCmp(const char* str, const char* str2); void ERaStrConcat(char* str, const char* str2); +char* ERaStrStr(const char* str, const char* str2, bool caseSensitive); +char* ERaStrrStr(const char* str, const char* str2, bool caseSensitive); +char* ERaStrChr(const char* str, int c, bool caseSensitive); +char* ERaStrrChr(const char* str, int c, bool caseSensitive); + template inline bool ERaStrNCmp(const char* str, const char(&str2)[size]) { diff --git a/src/Utility/ERacJSON.cpp b/src/Utility/ERacJSON.cpp index ef83a51..edc4fc5 100644 --- a/src/Utility/ERacJSON.cpp +++ b/src/Utility/ERacJSON.cpp @@ -469,6 +469,18 @@ CJSON_PUBLIC(const char*) cJSON_TypeOf(const cJSON* const object) { } } +CJSON_PUBLIC(cJSON*) cJSON_AddMultiItemToObject(cJSON* const object, const char* const name, cJSON* const item) { + if ((object == NULL) || (name == NULL) || (item == NULL)) { + return NULL; + } + if (cJSON_AddItemToObject(object, name, item)) { + return item; + } + else { + return NULL; + } +} + CJSON_PUBLIC(cJSON*) cJSON_AddMultiItemToObject(cJSON* const object, const char* const name, const double number) { if ((object == NULL) || (name == NULL)) { return NULL; diff --git a/src/Utility/ERacJSON.hpp b/src/Utility/ERacJSON.hpp index e17575b..547556e 100644 --- a/src/Utility/ERacJSON.hpp +++ b/src/Utility/ERacJSON.hpp @@ -43,6 +43,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Empty(const cJSON* const object); CJSON_PUBLIC(const char*) cJSON_TypeOf(const cJSON* const object); +CJSON_PUBLIC(cJSON*) cJSON_AddMultiItemToObject(cJSON* const object, const char* const name, cJSON* const item); CJSON_PUBLIC(cJSON*) cJSON_AddMultiItemToObject(cJSON* const object, const char* const name, const double number); CJSON_PUBLIC(cJSON*) cJSON_AddMultiItemToObject(cJSON* const object, const char* const name, const char* const string); diff --git a/src/Utility/cJSON.cpp b/src/Utility/cJSON.cpp index d5d14d6..5d9e2e3 100644 --- a/src/Utility/cJSON.cpp +++ b/src/Utility/cJSON.cpp @@ -251,6 +251,7 @@ CJSON_PUBLIC(cJSON_Int_t) cJSON_SetIntHelper(cJSON *object, cJSON_Int_t number) return object->valueint = number; } +/* Note: when passing a NULL valuestring, cJSON_SetValuestring treats this as an error and return NULL */ CJSON_PUBLIC(char*) cJSON_SetValueString(cJSON *object, const char *valuestring) { char *copy = NULL; @@ -259,8 +260,8 @@ CJSON_PUBLIC(char*) cJSON_SetValueString(cJSON *object, const char *valuestring) { return NULL; } - /* return NULL if the object is corrupted */ - if (object->valuestring == NULL) + /* return NULL if the object is corrupted or valuestring is NULL */ + if (object->valuestring == NULL || valuestring == NULL) { return NULL; } @@ -401,7 +402,7 @@ CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) } /* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ -#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 17) +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 18) #error "cJSON.hpp and cJSON.cpp have different versions. Make sure that both have the same". #endif @@ -702,6 +703,7 @@ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) return object->valuedouble = number; } +/* Note: when passing a NULL valuestring, cJSON_SetValuestring treats this as an error and return NULL */ CJSON_PUBLIC(char*) cJSON_SetValueString(cJSON *object, const char *valuestring) { char *copy = NULL; @@ -710,8 +712,8 @@ CJSON_PUBLIC(char*) cJSON_SetValueString(cJSON *object, const char *valuestring) { return NULL; } - /* return NULL if the object is corrupted */ - if (object->valuestring == NULL) + /* return NULL if the object is corrupted or valuestring is NULL */ + if (object->valuestring == NULL || valuestring == NULL) { return NULL; } @@ -885,7 +887,7 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out { length = sprintf((char*)number_buffer, "null"); } - else if(d == (double)item->valueint) + else if (d == (double)item->valueint) { length = sprintf((char*)number_buffer, CJSON_INT_FORMAT, item->valueint); } @@ -1211,6 +1213,7 @@ static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_bu if (output != NULL) { input_buffer->hooks.deallocate(output); + output = NULL; } if (input_pointer != NULL) @@ -1553,6 +1556,7 @@ static unsigned char *print(const cJSON * const item, cJSON_bool format, const i /* free the buffer */ hooks->deallocate(buffer->buffer); + buffer->buffer = NULL; } return printed; @@ -1561,11 +1565,13 @@ static unsigned char *print(const cJSON * const item, cJSON_bool format, const i if (buffer->buffer != NULL) { hooks->deallocate(buffer->buffer); + buffer->buffer = NULL; } if (printed != NULL) { hooks->deallocate(printed); + printed = NULL; } return NULL; @@ -1606,6 +1612,7 @@ CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON if (!print_value(item, &p)) { global_hooks.deallocate(p.buffer); + p.buffer = NULL; return NULL; } @@ -1980,6 +1987,11 @@ static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_bu current_item = new_item; } + if (cannot_access_at_index(input_buffer, 1)) + { + goto fail; /* nothing comes after the comma */ + } + /* parse the name of the child */ input_buffer->offset++; buffer_skip_whitespace(input_buffer); @@ -3522,6 +3534,7 @@ CJSON_PUBLIC(void *) cJSON_malloc(size_t size) CJSON_PUBLIC(void) cJSON_free(void *object) { global_hooks.deallocate(object); + object = NULL; } #endif diff --git a/src/Utility/cJSON.hpp b/src/Utility/cJSON.hpp index 7fa2113..457ab36 100644 --- a/src/Utility/cJSON.hpp +++ b/src/Utility/cJSON.hpp @@ -84,7 +84,7 @@ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJ /* project version */ #define CJSON_VERSION_MAJOR 1 #define CJSON_VERSION_MINOR 7 -#define CJSON_VERSION_PATCH 17 +#define CJSON_VERSION_PATCH 18 /* cJSON Types: */ #define cJSON_Invalid (0) diff --git a/src/Widgets/ERaWidgetTerminalBox.hpp b/src/Widgets/ERaWidgetTerminalBox.hpp index e655a8f..dc5173d 100644 --- a/src/Widgets/ERaWidgetTerminalBox.hpp +++ b/src/Widgets/ERaWidgetTerminalBox.hpp @@ -66,11 +66,25 @@ class ERaWidgetTerminalBox return 1; } + void trimTxBuffer() { + for (size_t i = 0; i < this->txBufferCount; ++i) { + if (!isspace(this->txBuffer[i])) { + return; + } + } + this->txBufferCount = 0; + } + virtual void flush() override { + this->trimTxBuffer(); + if (this->txBufferCount && ERa.connected()) { this->txBuffer[this->txBufferCount] = '\0'; ERa.virtualWrite(this->toPin, (char*)this->txBuffer, true); +#if !defined(ERA_ABBR) + ERa.getPropertyReport().run(); +#endif this->txBufferCount = 0; } } @@ -125,10 +139,13 @@ class ERaWidgetTerminalBox void onUpdate() { this->process(); + ERa.virtualWrite(this->fromPin, this->estr, true); +#if !defined(ERA_ABBR) + ERa.getPropertyReport().run(); +#endif if (this->callback != NULL) { this->callback(); } - ERa.virtualWrite(this->fromPin, this->estr, true); } ERaString& estr;