Fix DPL Mode 2 MQTT Status (#402)

* DPL MQTT handler: modernize

* there is no need to tokenize and check the topic of a received MQTT
  message if we only subscribe to a single topic. all messages will be
  for that topic. avoid testing the topic in the callback alltogether.
* use std::string and std::stoi over allocating and deleting a buffer
  and copying charactes around.
* use a switch statement to process the actual payload.
* break a long line.

* DPL: fix getMode() return value

getMode() returned a bool. probably its return type was not adjusted
when the third mode was introduced. this lead to mode 2 being cast to
true implicitly, which in turn was used to construct a String, such that
"1" was published as the DPL mode when in fact it was 2.

make the mode an enum class to avoid such problems in the future.

inline getMode() and setMode().

fix indention.
This commit is contained in:
Bernhard Kirchen 2023-08-28 13:20:56 +02:00 committed by GitHub
parent ca308d0895
commit 96ee78156d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 50 additions and 61 deletions

View File

@ -10,7 +10,7 @@ public:
void loop(); void loop();
private: private:
void onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total); void onCmdMode(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total);
uint32_t _lastPublishStats; uint32_t _lastPublishStats;
uint32_t _lastPublish; uint32_t _lastPublish;

View File

@ -51,8 +51,15 @@ public:
void loop(); void loop();
uint8_t getPowerLimiterState(); uint8_t getPowerLimiterState();
int32_t getLastRequestedPowerLimit(); int32_t getLastRequestedPowerLimit();
void setMode(uint8_t mode);
bool getMode(); enum class Mode : unsigned {
Normal = 0,
Disabled = 1,
UnconditionalFullSolarPassthrough = 2
};
void setMode(Mode m) { _mode = m; }
Mode getMode() const { return _mode; }
void calcNextInverterRestart(); void calcNextInverterRestart();
private: private:
@ -63,7 +70,7 @@ private:
uint32_t _lastCalculation = 0; uint32_t _lastCalculation = 0;
static constexpr uint32_t _calculationBackoffMsDefault = 128; static constexpr uint32_t _calculationBackoffMsDefault = 128;
uint32_t _calculationBackoffMs = _calculationBackoffMsDefault; uint32_t _calculationBackoffMs = _calculationBackoffMsDefault;
uint8_t _mode = PL_MODE_ENABLE_NORMAL_OP; Mode _mode = Mode::Normal;
std::shared_ptr<InverterAbstract> _inverter = nullptr; std::shared_ptr<InverterAbstract> _inverter = nullptr;
bool _batteryDischargeEnabled = false; bool _batteryDischargeEnabled = false;
uint32_t _nextInverterRestart = 0; // Values: 0->not calculated / 1->no restart configured / >1->time of next inverter restart in millis() uint32_t _nextInverterRestart = 0; // Values: 0->not calculated / 1->no restart configured / >1->time of next inverter restart in millis()

View File

@ -7,8 +7,7 @@
#include "MqttHandlePowerLimiter.h" #include "MqttHandlePowerLimiter.h"
#include "PowerLimiter.h" #include "PowerLimiter.h"
#include <ctime> #include <ctime>
#include <string>
#define TOPIC_SUB_POWER_LIMITER "mode"
MqttHandlePowerLimiterClass MqttHandlePowerLimiter; MqttHandlePowerLimiterClass MqttHandlePowerLimiter;
@ -21,11 +20,10 @@ void MqttHandlePowerLimiterClass::init()
using std::placeholders::_5; using std::placeholders::_5;
using std::placeholders::_6; using std::placeholders::_6;
String topic = MqttSettings.getPrefix(); String topic = MqttSettings.getPrefix() + "powerlimiter/cmd/mode";
MqttSettings.subscribe(String(topic + "powerlimiter/cmd/" + TOPIC_SUB_POWER_LIMITER).c_str(), 0, std::bind(&MqttHandlePowerLimiterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); MqttSettings.subscribe(topic.c_str(), 0, std::bind(&MqttHandlePowerLimiterClass::onCmdMode, this, _1, _2, _3, _4, _5, _6));
_lastPublish = millis(); _lastPublish = millis();
} }
@ -38,60 +36,52 @@ void MqttHandlePowerLimiterClass::loop()
const CONFIG_T& config = Configuration.get(); const CONFIG_T& config = Configuration.get();
if ((millis() - _lastPublish) > (config.Mqtt_PublishInterval * 1000) ) { if ((millis() - _lastPublish) > (config.Mqtt_PublishInterval * 1000) ) {
MqttSettings.publish("powerlimiter/status/mode", String(PowerLimiter.getMode())); auto val = static_cast<unsigned>(PowerLimiter.getMode());
MqttSettings.publish("powerlimiter/status/mode", String(val));
yield(); yield();
_lastPublish = millis(); _lastPublish = millis();
} }
} }
void MqttHandlePowerLimiterClass::onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total) void MqttHandlePowerLimiterClass::onCmdMode(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(); const CONFIG_T& config = Configuration.get();
// ignore messages if PowerLimiter is disabled // ignore messages if PowerLimiter is disabled
if (!config.PowerLimiter_Enabled) { if (!config.PowerLimiter_Enabled) {
return; return;
} }
char token_topic[MQTT_MAX_TOPIC_STRLEN + 40]; // respect all subtopics std::string strValue(reinterpret_cast<const char*>(payload), len);
strncpy(token_topic, topic, MQTT_MAX_TOPIC_STRLEN + 40); // convert const char* to char* int intValue = -1;
try {
char* setting; intValue = std::stoi(strValue);
char* rest = &token_topic[strlen(config.Mqtt_Topic)]; }
catch (std::invalid_argument const& e) {
strtok_r(rest, "/", &rest); // Remove "powerlimiter" MessageOutput.printf("PowerLimiter MQTT handler: cannot parse payload of topic '%s' as int: %s\r\n",
strtok_r(rest, "/", &rest); // Remove "cmd" topic, strValue.c_str());
setting = strtok_r(rest, "/", &rest);
if (setting == NULL) {
return; return;
} }
char* str = new char[len + 1]; using Mode = PowerLimiterClass::Mode;
memcpy(str, payload, len); switch (static_cast<Mode>(intValue)) {
str[len] = '\0'; case Mode::UnconditionalFullSolarPassthrough:
uint8_t payload_val = atoi(str); MessageOutput.println("Power limiter unconditional full solar PT");
delete[] str; PowerLimiter.setMode(Mode::UnconditionalFullSolarPassthrough);
break;
if (!strcmp(setting, TOPIC_SUB_POWER_LIMITER)) { case Mode::Disabled:
if(payload_val == 2) { MessageOutput.println("Power limiter disabled (override)");
MessageOutput.println("Power limiter full solar PT"); PowerLimiter.setMode(Mode::Disabled);
PowerLimiter.setMode(PL_MODE_SOLAR_PT_ONLY); break;
return; case Mode::Normal:
} MessageOutput.println("Power limiter normal operation");
if(payload_val == 1) { PowerLimiter.setMode(Mode::Normal);
MessageOutput.println("Power limiter disabled"); break;
PowerLimiter.setMode(PL_MODE_FULL_DISABLE); default:
return; MessageOutput.printf("PowerLimiter - unknown mode %d\r\n", intValue);
} break;
if(payload_val == 0) { }
MessageOutput.println("Power limiter enabled");
PowerLimiter.setMode(PL_MODE_ENABLE_NORMAL_OP);
return;
}
MessageOutput.println("Power limiter enable / disable - unknown command received. Please use 0 or 1");
}
} }

View File

@ -131,7 +131,7 @@ void PowerLimiterClass::loop()
return; return;
} }
if (PL_MODE_FULL_DISABLE == _mode) { if (Mode::Disabled == _mode) {
shutdown(Status::DisabledByMqtt); shutdown(Status::DisabledByMqtt);
return; return;
} }
@ -185,7 +185,7 @@ void PowerLimiterClass::loop()
return announceStatus(Status::InverterDevInfoPending); return announceStatus(Status::InverterDevInfoPending);
} }
if (PL_MODE_SOLAR_PT_ONLY == _mode) { if (Mode::UnconditionalFullSolarPassthrough == _mode) {
// handle this mode of operation separately // handle this mode of operation separately
return unconditionalSolarPassthrough(_inverter); return unconditionalSolarPassthrough(_inverter);
} }
@ -398,14 +398,6 @@ int32_t PowerLimiterClass::getLastRequestedPowerLimit() {
return _lastRequestedPowerLimit; return _lastRequestedPowerLimit;
} }
bool PowerLimiterClass::getMode() {
return _mode;
}
void PowerLimiterClass::setMode(uint8_t mode) {
_mode = mode;
}
bool PowerLimiterClass::canUseDirectSolarPower() bool PowerLimiterClass::canUseDirectSolarPower()
{ {
CONFIG_T& config = Configuration.get(); CONFIG_T& config = Configuration.get();

View File

@ -125,7 +125,7 @@ void WebApiPowerLimiterClass::onAdminPost(AsyncWebServerRequest* request)
CONFIG_T& config = Configuration.get(); CONFIG_T& config = Configuration.get();
config.PowerLimiter_Enabled = root[F("enabled")].as<bool>(); config.PowerLimiter_Enabled = root[F("enabled")].as<bool>();
PowerLimiter.setMode(PL_MODE_ENABLE_NORMAL_OP); // User input sets PL to normal operation PowerLimiter.setMode(PowerLimiterClass::Mode::Normal); // User input sets PL to normal operation
config.PowerLimiter_VerboseLogging = root[F("verbose_logging")].as<bool>(); config.PowerLimiter_VerboseLogging = root[F("verbose_logging")].as<bool>();
config.PowerLimiter_SolarPassThroughEnabled = root[F("solar_passthrough_enabled")].as<bool>(); config.PowerLimiter_SolarPassThroughEnabled = root[F("solar_passthrough_enabled")].as<bool>();
config.PowerLimiter_SolarPassThroughLosses = root[F("solar_passthrough_losses")].as<uint8_t>(); config.PowerLimiter_SolarPassThroughLosses = root[F("solar_passthrough_losses")].as<uint8_t>();