From b6bfeea93653740b26150adc47c63c0f0a059670 Mon Sep 17 00:00:00 2001 From: Slider0007 <115730895+Slider0007@users.noreply.github.com> Date: Tue, 28 Mar 2023 20:41:25 +0200 Subject: [PATCH] Set prevalue using MQTT + set prevalue to RAW value (REST+MQTT) (#2252) * Use double instead of float * Error handling + set to RAW if newvalue < 0 * REST SetPrevalue: Set to RAW if newvalue < 0 * set prevalue with MQTT --- .../jomjol_flowcontroll/ClassFlowControll.cpp | 43 +++++---- .../jomjol_flowcontroll/ClassFlowControll.h | 4 +- .../ClassFlowPostProcessing.cpp | 46 +++++++--- .../ClassFlowPostProcessing.h | 2 +- code/components/jomjol_mqtt/CMakeLists.txt | 2 +- .../components/jomjol_mqtt/interface_mqtt.cpp | 51 ++++++++++- .../jomjol_tfliteclass/server_tflite.cpp | 91 +++++++++++++------ 7 files changed, 169 insertions(+), 70 deletions(-) diff --git a/code/components/jomjol_flowcontroll/ClassFlowControll.cpp b/code/components/jomjol_flowcontroll/ClassFlowControll.cpp index e9d56e3ec..085fa48a4 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowControll.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowControll.cpp @@ -470,10 +470,10 @@ string ClassFlowControll::getReadoutAll(int _type) } -string ClassFlowControll::getReadout(bool _rawvalue = false, bool _noerror = false) +string ClassFlowControll::getReadout(bool _rawvalue = false, bool _noerror = false, int _number = 0) { if (flowpostprocessing) - return flowpostprocessing->getReadoutParam(_rawvalue, _noerror); + return flowpostprocessing->getReadoutParam(_rawvalue, _noerror, _number); string zw = ""; string result = ""; @@ -501,37 +501,40 @@ string ClassFlowControll::GetPrevalue(std::string _number) return flowpostprocessing->GetPreValue(_number); } + return std::string(""); } -std::string ClassFlowControll::UpdatePrevalue(std::string _newvalue, std::string _numbers, bool _extern) +bool ClassFlowControll::UpdatePrevalue(std::string _newvalue, std::string _numbers, bool _extern) { - float zw; + double newvalueAsDouble; char* p; _newvalue = trim(_newvalue); -// ESP_LOGD(TAG, "Input UpdatePreValue: %s", _newvalue.c_str()); + //ESP_LOGD(TAG, "Input UpdatePreValue: %s", _newvalue.c_str()); - if (_newvalue.compare("0.0") == 0) - { - zw = 0; + if (_newvalue.substr(0,8).compare("0.000000") == 0 || _newvalue.compare("0.0") == 0 || _newvalue.compare("0") == 0) { + newvalueAsDouble = 0; // preset to value = 0 } - else - { - zw = strtof(_newvalue.c_str(), &p); - if (zw == 0) - return "- Error in String to Value Conversion!!! Must be of format value=123.456"; + else { + newvalueAsDouble = strtod(_newvalue.c_str(), &p); + if (newvalueAsDouble == 0) { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "UpdatePrevalue: No valid value for processing: " + _newvalue); + return false; + } } - - if (flowpostprocessing) - { - flowpostprocessing->SetPreValue(zw, _numbers, _extern); - return _newvalue; + if (flowpostprocessing) { + if (flowpostprocessing->SetPreValue(newvalueAsDouble, _numbers, _extern)) + return true; + else + return false; + } + else { + LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "UpdatePrevalue: ERROR - Class Post-Processing not initialized"); + return false; } - - return std::string(); } diff --git a/code/components/jomjol_flowcontroll/ClassFlowControll.h b/code/components/jomjol_flowcontroll/ClassFlowControll.h index 6a2194de6..da401738d 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowControll.h +++ b/code/components/jomjol_flowcontroll/ClassFlowControll.h @@ -46,9 +46,9 @@ class ClassFlowControll : bool doFlow(string time); void doFlowTakeImageOnly(string time); bool getStatusSetupModus(){return SetupModeActive;}; - string getReadout(bool _rawvalue, bool _noerror); + string getReadout(bool _rawvalue, bool _noerror, int _number); string getReadoutAll(int _type); - string UpdatePrevalue(std::string _newvalue, std::string _numbers, bool _extern); + bool UpdatePrevalue(std::string _newvalue, std::string _numbers, bool _extern); string GetPrevalue(std::string _number = ""); bool ReadParameter(FILE* pfile, string& aktparamgraph); string getJSON(); diff --git a/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp b/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp index e9abf7c99..ceed7d3a2 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp +++ b/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp @@ -96,27 +96,49 @@ string ClassFlowPostProcessing::GetPreValue(std::string _number) return result; } -void ClassFlowPostProcessing::SetPreValue(double zw, string _numbers, bool _extern) + +bool ClassFlowPostProcessing::SetPreValue(double _newvalue, string _numbers, bool _extern) { - ESP_LOGD(TAG, "SetPrevalue: %f, %s", zw, _numbers.c_str()); - for (int j = 0; j < NUMBERS.size(); ++j) - { -// ESP_LOGD(TAG, "Number %d, %s", j, NUMBERS[j]->name.c_str()); - if (NUMBERS[j]->name == _numbers) - { - NUMBERS[j]->PreValue = zw; - NUMBERS[j]->ReturnPreValue = std::to_string(zw); + //ESP_LOGD(TAG, "SetPrevalue: %f, %s", zw, _numbers.c_str()); + + for (int j = 0; j < NUMBERS.size(); ++j) { + //ESP_LOGD(TAG, "Number %d, %s", j, NUMBERS[j]->name.c_str()); + if (NUMBERS[j]->name == _numbers) { + if (_newvalue >= 0) { // if new value posivive, use provided value to preset PreValue + NUMBERS[j]->PreValue = _newvalue; + } + else { // if new value negative, use last raw value to preset PreValue + char* p; + double ReturnRawValueAsDouble = strtod(NUMBERS[j]->ReturnRawValue.c_str(), &p); + if (ReturnRawValueAsDouble == 0) { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "SetPreValue: RawValue not a valid value for further processing: " + + NUMBERS[j]->ReturnRawValue); + return false; + } + NUMBERS[j]->PreValue = ReturnRawValueAsDouble; + } + + NUMBERS[j]->ReturnPreValue = std::to_string(NUMBERS[j]->PreValue); NUMBERS[j]->PreValueOkay = true; + if (_extern) { time(&(NUMBERS[j]->lastvalue)); localtime(&(NUMBERS[j]->lastvalue)); } -// ESP_LOGD(TAG, "Found %d! - set to %f", j, NUMBERS[j]->PreValue); + //ESP_LOGD(TAG, "Found %d! - set to %.8f", j, NUMBERS[j]->PreValue); + + UpdatePreValueINI = true; // Only update prevalue file if a new value is set + SavePreValue(); + + LogFile.WriteToFile(ESP_LOG_INFO, TAG, "SetPreValue: PreValue for " + NUMBERS[j]->name + " set to " + + std::to_string(NUMBERS[j]->PreValue)); + return true; } } - UpdatePreValueINI = true; - SavePreValue(); + + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "SetPreValue: Numbersname not found or not valid"); + return false; // No new value was set (e.g. wrong numbersname, no numbers at all) } diff --git a/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.h b/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.h index 3fc8f11df..aae28e6a3 100644 --- a/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.h +++ b/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.h @@ -68,7 +68,7 @@ class ClassFlowPostProcessing : void SavePreValue(); string getJsonFromNumber(int i, std::string _lineend); string GetPreValue(std::string _number = ""); - void SetPreValue(double zw, string _numbers, bool _extern = false); + bool SetPreValue(double zw, string _numbers, bool _extern = false); std::string GetJSON(std::string _lineend = "\n"); std::string getNumbersName(); diff --git a/code/components/jomjol_mqtt/CMakeLists.txt b/code/components/jomjol_mqtt/CMakeLists.txt index 6e59dc685..4b3f3b0e1 100644 --- a/code/components/jomjol_mqtt/CMakeLists.txt +++ b/code/components/jomjol_mqtt/CMakeLists.txt @@ -2,4 +2,4 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*) idf_component_register(SRCS ${app_sources} INCLUDE_DIRS "." - REQUIRES tflite-lib mqtt jomjol_tfliteclass jomjol_helper jomjol_mqtt jomjol_wlan) + REQUIRES tflite-lib mqtt jomjol_tfliteclass jomjol_helper jomjol_mqtt jomjol_wlan json) diff --git a/code/components/jomjol_mqtt/interface_mqtt.cpp b/code/components/jomjol_mqtt/interface_mqtt.cpp index 7ce4bb3e9..099985de1 100644 --- a/code/components/jomjol_mqtt/interface_mqtt.cpp +++ b/code/components/jomjol_mqtt/interface_mqtt.cpp @@ -6,6 +6,7 @@ #include "mqtt_client.h" #include "ClassLogFile.h" #include "server_tflite.h" +#include "cJSON.h" #include "../../include/defines.h" static const char *TAG = "MQTT IF"; @@ -336,17 +337,54 @@ bool getMQTTisConnected() { } -bool mqtt_handler_flow_start(std::string _topic, char* _data, int _data_len) { +bool mqtt_handler_flow_start(std::string _topic, char* _data, int _data_len) +{ ESP_LOGD(TAG, "Handler called: topic %s, data %.*s", _topic.c_str(), _data_len, _data); if (_data_len > 0) { MQTTCtrlFlowStart(_topic); } + else { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_flow_start: handler called, but no data"); + } return ESP_OK; } +bool mqtt_handler_set_prevalue(std::string _topic, char* _data, int _data_len) +{ + //ESP_LOGD(TAG, "Handler called: topic %s, data %.*s", _topic.c_str(), _data_len, _data); + //example: {"numbersname": "main", "value": 12345.1234567} + + if (_data_len > 0) { // Check if data length > 0 + cJSON *jsonData = cJSON_Parse(_data); + cJSON *numbersname = cJSON_GetObjectItemCaseSensitive(jsonData, "numbersname"); + cJSON *value = cJSON_GetObjectItemCaseSensitive(jsonData, "value"); + + if (cJSON_IsString(numbersname) && (numbersname->valuestring != NULL)) { // Check if numbersname is valid + if (cJSON_IsNumber(value)) { // Check if value is a number + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "handler_set_prevalue called: numbersname: " + std::string(numbersname->valuestring) + + ", value: " + std::to_string(value->valuedouble)); + if (tfliteflow.UpdatePrevalue(std::to_string(value->valuedouble), std::string(numbersname->valuestring), true)) + return ESP_OK; + } + else { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_set_prevalue: value not a valid number (\"value\": 12345.12345)"); + } + } + else { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_set_prevalue: numbersname not a valid string (\"numbersname\": \"main\")"); + } + } + else { + LogFile.WriteToFile(ESP_LOG_WARN, TAG, "handler_set_prevalue: handler called, but no data received"); + } + + return ESP_FAIL; +} + + void MQTTconnected(){ if (mqtt_connected) { LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Connected to broker"); @@ -358,9 +396,14 @@ void MQTTconnected(){ } } - /* Subcribe to topics */ - std::function subHandler = mqtt_handler_flow_start; - MQTTregisterSubscribeFunction(maintopic + "/ctrl/flow_start", subHandler); // subcribe to maintopic/ctrl/flow_start + // Subcribe to topics + // Note: Further subsriptions are handled in GPIO class + //***************************************** + std::function subHandler1 = mqtt_handler_flow_start; + MQTTregisterSubscribeFunction(maintopic + "/ctrl/flow_start", subHandler1); // subcribe to maintopic/ctrl/flow_start + + std::function subHandler2 = mqtt_handler_set_prevalue; + MQTTregisterSubscribeFunction(maintopic + "/ctrl/set_prevalue", subHandler2); // subcribe to maintopic/ctrl/set_prevalue if (subscribeFunktionMap != NULL) { for(std::map>::iterator it = subscribeFunktionMap->begin(); it != subscribeFunktionMap->end(); ++it) { diff --git a/code/components/jomjol_tfliteclass/server_tflite.cpp b/code/components/jomjol_tfliteclass/server_tflite.cpp index 68ed4d892..2a71840ce 100644 --- a/code/components/jomjol_tfliteclass/server_tflite.cpp +++ b/code/components/jomjol_tfliteclass/server_tflite.cpp @@ -399,7 +399,7 @@ esp_err_t handler_wasserzaehler(httpd_req_t *req) } - zw = tfliteflow.getReadout(_rawValue, _noerror); + zw = tfliteflow.getReadout(_rawValue, _noerror, 0); if (zw.length() > 0) httpd_resp_sendstr_chunk(req, zw.c_str()); @@ -706,7 +706,7 @@ esp_err_t handler_editflow(httpd_req_t *req) esp_err_t handler_statusflow(httpd_req_t *req) { #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_prevalue - Start"); + LogFile.WriteHeapInfo("handler_statusflow - Start"); #endif const char* resp_str; @@ -715,7 +715,7 @@ esp_err_t handler_statusflow(httpd_req_t *req) if (bTaskAutoFlowCreated) { #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "handler_prevalue: %s", req->uri); + ESP_LOGD(TAG, "handler_statusflow: %s", req->uri); #endif string* zw = tfliteflow.getActStatusWithTime(); @@ -730,7 +730,7 @@ esp_err_t handler_statusflow(httpd_req_t *req) } #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_prevalue - Done"); + LogFile.WriteHeapInfo("handler_statusflow - Done"); #endif return ESP_OK; @@ -802,50 +802,81 @@ esp_err_t handler_uptime(httpd_req_t *req) esp_err_t handler_prevalue(httpd_req_t *req) { #ifdef DEBUG_DETAIL_ON - LogFile.WriteHeapInfo("handler_prevalue - Start"); + LogFile.WriteHeapInfo("handler_prevalue - Start"); + ESP_LOGD(TAG, "handler_prevalue: %s", req->uri); #endif - const char* resp_str; - string zw; + // Default usage message when handler gets called without any parameter + const std::string RESTUsageInfo = + "00: Handler usage:
" + "- To retrieve actual PreValue, please provide only a numbersname, e.g. /setPreValue?numbers=main
" + "- To set PreValue to a new value, please provide a numbersname and a value, e.g. /setPreValue?numbers=main&value=1234.5678
" + "NOTE:
" + "value >= 0.0: Set PreValue to provided value
" + "value < 0.0: Set PreValue to actual RAW value (as long RAW value is a valid number, without N)"; - #ifdef DEBUG_DETAIL_ON - ESP_LOGD(TAG, "handler_prevalue: %s", req->uri); - #endif + // Default return error message when no return is programmed + std::string sReturnMessage = "E90: Uninitialized"; char _query[100]; - char _size[10] = ""; - char _numbers[50] = "default"; + char _numbersname[50] = "default"; + char _value[20] = ""; - if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK) - { + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); + + if (httpd_req_get_url_query_str(req, _query, 100) == ESP_OK) { #ifdef DEBUG_DETAIL_ON ESP_LOGD(TAG, "Query: %s", _query); #endif - if (httpd_query_key_value(_query, "value", _size, 10) == ESP_OK) - { + if (httpd_query_key_value(_query, "numbers", _numbersname, 50) != ESP_OK) { // If request is incomplete + sReturnMessage = "E91: Query parameter incomplete or not valid!
" + "Call /setPreValue to show REST API usage info and/or check documentation"; + httpd_resp_send(req, sReturnMessage.c_str(), sReturnMessage.length()); + return ESP_FAIL; + } + + if (httpd_query_key_value(_query, "value", _value, 20) == ESP_OK) { #ifdef DEBUG_DETAIL_ON ESP_LOGD(TAG, "Value: %s", _size); #endif } + } + else { // if no parameter is provided, print handler usage + httpd_resp_send(req, RESTUsageInfo.c_str(), RESTUsageInfo.length()); + return ESP_OK; + } + + if (strlen(_value) == 0) { // If no value is povided --> return actual PreValue + sReturnMessage = tfliteflow.GetPrevalue(std::string(_numbersname)); + + if (sReturnMessage.empty()) { + sReturnMessage = "E92: Numbers name not found"; + httpd_resp_send(req, sReturnMessage.c_str(), sReturnMessage.length()); + return ESP_FAIL; + } + } + else { + // New value is positive: Set PreValue to provided value and return value + // New value is negative and actual RAW value is a valid number: Set PreValue to RAW value and return value + LogFile.WriteToFile(ESP_LOG_DEBUG, TAG, "REST API handler_prevalue called: numbersname: " + std::string(_numbersname) + + ", value: " + std::string(_value)); + if (!tfliteflow.UpdatePrevalue(_value, _numbersname, true)) { + sReturnMessage = "E93: Update request rejected. Please check device logs for more details"; + httpd_resp_send(req, sReturnMessage.c_str(), sReturnMessage.length()); + return ESP_FAIL; + } - httpd_query_key_value(_query, "numbers", _numbers, 50); - } + sReturnMessage = tfliteflow.GetPrevalue(std::string(_numbersname)); - if (strlen(_size) == 0) - { - zw = tfliteflow.GetPrevalue(std::string(_numbers)); - } - else - { - zw = "SetPrevalue to " + tfliteflow.UpdatePrevalue(_size, _numbers, true); + if (sReturnMessage.empty()) { + sReturnMessage = "E94: Numbers name not found"; + httpd_resp_send(req, sReturnMessage.c_str(), sReturnMessage.length()); + return ESP_FAIL; + } } - - resp_str = zw.c_str(); - httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); - - httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN); + httpd_resp_send(req, sReturnMessage.c_str(), sReturnMessage.length()); #ifdef DEBUG_DETAIL_ON LogFile.WriteHeapInfo("handler_prevalue - End");