clean up Huawei MQTT handler

* bind the callback to a topic (enum value) such that there is no need
  to tokenize the full topic (string) to find out what value is being
  processed. tokenizing is expensive.
* get rid of using the config in the callback, which improves
  thread-safety since the MQTT callback is running in the MQTT thread.
* prefer C++ method stof to convert MQTT value to a float, which saves
  us from using new and delete for a buffer in particular.
* prefer switch statements over if-else-trees.
* split long lines.
* get rid of topic #defines.
* fix indention.
This commit is contained in:
Bernhard Kirchen 2023-12-30 18:31:50 +01:00
parent fe2f82e303
commit 463226082f
2 changed files with 91 additions and 84 deletions

View File

@ -15,7 +15,19 @@ public:
private: private:
void loop(); void loop();
void onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total);
enum class Topic : unsigned {
LimitOnlineVoltage,
LimitOnlineCurrent,
LimitOfflineVoltage,
LimitOfflineCurrent,
Mode
};
void onMqttMessage(Topic t,
const espMqttClientTypes::MessageProperties& properties,
const char* topic, const uint8_t* payload, size_t len,
size_t index, size_t total);
Task _loopTask; Task _loopTask;

View File

@ -10,12 +10,6 @@
#include "WebApi_Huawei.h" #include "WebApi_Huawei.h"
#include <ctime> #include <ctime>
#define TOPIC_SUB_LIMIT_ONLINE_VOLTAGE "limit_online_voltage"
#define TOPIC_SUB_LIMIT_ONLINE_CURRENT "limit_online_current"
#define TOPIC_SUB_LIMIT_OFFLINE_VOLTAGE "limit_offline_voltage"
#define TOPIC_SUB_LIMIT_OFFLINE_CURRENT "limit_offline_current"
#define TOPIC_SUB_MODE "mode"
MqttHandleHuaweiClass MqttHandleHuawei; MqttHandleHuaweiClass MqttHandleHuawei;
void MqttHandleHuaweiClass::init(Scheduler& scheduler) void MqttHandleHuaweiClass::init(Scheduler& scheduler)
@ -25,19 +19,22 @@ void MqttHandleHuaweiClass::init(Scheduler& scheduler)
_loopTask.setIterations(TASK_FOREVER); _loopTask.setIterations(TASK_FOREVER);
_loopTask.enable(); _loopTask.enable();
using std::placeholders::_1; String const& prefix = MqttSettings.getPrefix();
using std::placeholders::_2;
using std::placeholders::_3;
using std::placeholders::_4;
using std::placeholders::_5;
using std::placeholders::_6;
String topic = MqttSettings.getPrefix(); auto subscribe = [&prefix, this](char const* subTopic, Topic t) {
MqttSettings.subscribe(String(topic + "huawei/cmd/" + TOPIC_SUB_LIMIT_ONLINE_VOLTAGE).c_str(), 0, std::bind(&MqttHandleHuaweiClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); String fullTopic(prefix + "huawei/cmd/" + subTopic);
MqttSettings.subscribe(String(topic + "huawei/cmd/" + TOPIC_SUB_LIMIT_ONLINE_CURRENT).c_str(), 0, std::bind(&MqttHandleHuaweiClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); MqttSettings.subscribe(fullTopic.c_str(), 0,
MqttSettings.subscribe(String(topic + "huawei/cmd/" + TOPIC_SUB_LIMIT_OFFLINE_VOLTAGE).c_str(), 0, std::bind(&MqttHandleHuaweiClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); std::bind(&MqttHandleHuaweiClass::onMqttMessage, this, t,
MqttSettings.subscribe(String(topic + "huawei/cmd/" + TOPIC_SUB_LIMIT_OFFLINE_CURRENT).c_str(), 0, std::bind(&MqttHandleHuaweiClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); std::placeholders::_1, std::placeholders::_2,
MqttSettings.subscribe(String(topic + "huawei/cmd/" + TOPIC_SUB_MODE).c_str(), 0, std::bind(&MqttHandleHuaweiClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); std::placeholders::_3, std::placeholders::_4,
std::placeholders::_5, std::placeholders::_6));
};
subscribe("limit_online_voltage", Topic::LimitOnlineVoltage);
subscribe("limit_online_current", Topic::LimitOnlineCurrent);
subscribe("limit_offline_voltage", Topic::LimitOfflineVoltage);
subscribe("limit_offline_current", Topic::LimitOfflineCurrent);
subscribe("mode", Topic::Mode);
_lastPublish = millis(); _lastPublish = millis();
@ -86,81 +83,79 @@ void MqttHandleHuaweiClass::loop()
} }
void MqttHandleHuaweiClass::onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total) void MqttHandleHuaweiClass::onMqttMessage(Topic t,
const espMqttClientTypes::MessageProperties& properties,
const char* topic, const uint8_t* payload, size_t len,
size_t index, size_t total)
{ {
const CONFIG_T& config = Configuration.get(); std::string strValue(reinterpret_cast<const char*>(payload), len);
float payload_val = -1;
char token_topic[MQTT_MAX_TOPIC_STRLEN + 40]; // respect all subtopics try {
strncpy(token_topic, topic, MQTT_MAX_TOPIC_STRLEN + 40); // convert const char* to char* payload_val = std::stof(strValue);
}
char* setting; catch (std::invalid_argument const& e) {
char* rest = &token_topic[strlen(config.Mqtt.Topic)]; MessageOutput.printf("Huawei MQTT handler: cannot parse payload of topic '%s' as float: %s\r\n",
topic, strValue.c_str());
strtok_r(rest, "/", &rest); // Remove "huawei"
strtok_r(rest, "/", &rest); // Remove "cmd"
setting = strtok_r(rest, "/", &rest);
if (setting == NULL) {
return; return;
} }
char* strlimit = new char[len + 1];
memcpy(strlimit, payload, len);
strlimit[len] = '\0';
float payload_val = strtof(strlimit, NULL);
delete[] strlimit;
std::lock_guard<std::mutex> mqttLock(_mqttMutex); std::lock_guard<std::mutex> mqttLock(_mqttMutex);
if (!strcmp(setting, TOPIC_SUB_LIMIT_ONLINE_VOLTAGE)) { switch (t) {
// Set voltage limit case Topic::LimitOnlineVoltage:
MessageOutput.printf("Limit Voltage: %f V\r\n", payload_val); MessageOutput.printf("Limit Voltage: %f V\r\n", payload_val);
_mqttCallbacks.push_back(std::bind(&HuaweiCanClass::setValue, _mqttCallbacks.push_back(std::bind(&HuaweiCanClass::setValue,
&HuaweiCan, payload_val, HUAWEI_ONLINE_VOLTAGE)); &HuaweiCan, payload_val, HUAWEI_ONLINE_VOLTAGE));
break;
} else if (!strcmp(setting, TOPIC_SUB_LIMIT_OFFLINE_VOLTAGE)) { case Topic::LimitOfflineVoltage:
// Set current limit
MessageOutput.printf("Offline Limit Voltage: %f V\r\n", payload_val); MessageOutput.printf("Offline Limit Voltage: %f V\r\n", payload_val);
_mqttCallbacks.push_back(std::bind(&HuaweiCanClass::setValue, _mqttCallbacks.push_back(std::bind(&HuaweiCanClass::setValue,
&HuaweiCan, payload_val, HUAWEI_OFFLINE_VOLTAGE)); &HuaweiCan, payload_val, HUAWEI_OFFLINE_VOLTAGE));
break;
} else if (!strcmp(setting, TOPIC_SUB_LIMIT_ONLINE_CURRENT)) { case Topic::LimitOnlineCurrent:
// Set current limit
MessageOutput.printf("Limit Current: %f A\r\n", payload_val); MessageOutput.printf("Limit Current: %f A\r\n", payload_val);
_mqttCallbacks.push_back(std::bind(&HuaweiCanClass::setValue, _mqttCallbacks.push_back(std::bind(&HuaweiCanClass::setValue,
&HuaweiCan, payload_val, HUAWEI_ONLINE_CURRENT)); &HuaweiCan, payload_val, HUAWEI_ONLINE_CURRENT));
break;
} else if (!strcmp(setting, TOPIC_SUB_LIMIT_OFFLINE_CURRENT)) { case Topic::LimitOfflineCurrent:
// Set current limit
MessageOutput.printf("Offline Limit Current: %f A\r\n", payload_val); MessageOutput.printf("Offline Limit Current: %f A\r\n", payload_val);
_mqttCallbacks.push_back(std::bind(&HuaweiCanClass::setValue, _mqttCallbacks.push_back(std::bind(&HuaweiCanClass::setValue,
&HuaweiCan, payload_val, HUAWEI_OFFLINE_CURRENT)); &HuaweiCan, payload_val, HUAWEI_OFFLINE_CURRENT));
break;
} else if (!strcmp(setting, TOPIC_SUB_MODE)) { case Topic::Mode:
// Control power on/off switch (static_cast<int>(payload_val)) {
if(payload_val == 3) { case 3:
MessageOutput.println("[Huawei MQTT::] Received MQTT msg. New mode: Full internal control"); MessageOutput.println("[Huawei MQTT::] Received MQTT msg. New mode: Full internal control");
_mqttCallbacks.push_back(std::bind(&HuaweiCanClass::setMode, _mqttCallbacks.push_back(std::bind(&HuaweiCanClass::setMode,
&HuaweiCan, HUAWEI_MODE_AUTO_INT)); &HuaweiCan, HUAWEI_MODE_AUTO_INT));
} break;
if(payload_val == 2) { case 2:
MessageOutput.println("[Huawei MQTT::] Received MQTT msg. New mode: Internal on/off control, external power limit"); MessageOutput.println("[Huawei MQTT::] Received MQTT msg. New mode: Internal on/off control, external power limit");
_mqttCallbacks.push_back(std::bind(&HuaweiCanClass::setMode, _mqttCallbacks.push_back(std::bind(&HuaweiCanClass::setMode,
&HuaweiCan, HUAWEI_MODE_AUTO_EXT)); &HuaweiCan, HUAWEI_MODE_AUTO_EXT));
} break;
if(payload_val == 1) { case 1:
MessageOutput.println("[Huawei MQTT::] Received MQTT msg. New mode: Turned ON"); MessageOutput.println("[Huawei MQTT::] Received MQTT msg. New mode: Turned ON");
_mqttCallbacks.push_back(std::bind(&HuaweiCanClass::setMode, _mqttCallbacks.push_back(std::bind(&HuaweiCanClass::setMode,
&HuaweiCan, HUAWEI_MODE_ON)); &HuaweiCan, HUAWEI_MODE_ON));
} break;
if(payload_val == 0) { case 0:
MessageOutput.println("[Huawei MQTT::] Received MQTT msg. New mode: Turned OFF"); MessageOutput.println("[Huawei MQTT::] Received MQTT msg. New mode: Turned OFF");
_mqttCallbacks.push_back(std::bind(&HuaweiCanClass::setMode, _mqttCallbacks.push_back(std::bind(&HuaweiCanClass::setMode,
&HuaweiCan, HUAWEI_MODE_OFF)); &HuaweiCan, HUAWEI_MODE_OFF));
break;
default:
MessageOutput.printf("[Huawei MQTT::] Invalid mode %.0f\r\n", payload_val);
break;
} }
break;
} }
} }