Skip to content

Commit

Permalink
WIP: config write guard
Browse files Browse the repository at this point in the history
  • Loading branch information
schlimmchen committed Oct 25, 2024
1 parent cfb5c3f commit 0974019
Show file tree
Hide file tree
Showing 21 changed files with 373 additions and 233 deletions.
24 changes: 22 additions & 2 deletions include/Configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
#include "PinMapping.h"
#include <cstdint>
#include <ArduinoJson.h>
#include <TaskSchedulerDeclarations.h>
#include <mutex>
#include <condition_variable>

#define CONFIG_FILENAME "/config.json"
#define CONFIG_VERSION 0x00011c00 // 0.1.28 // make sure to clean all after change
Expand Down Expand Up @@ -333,11 +336,23 @@ struct CONFIG_T {

class ConfigurationClass {
public:
void init();
void init(Scheduler& scheduler);
bool read();
bool write();
void migrate();
CONFIG_T& get();
CONFIG_T const& get();

class WriteGuard {
public:
WriteGuard();
CONFIG_T& getConfig();
~WriteGuard();

private:
std::unique_lock<std::mutex> _lock;
};

WriteGuard getWriteGuard();

INVERTER_CONFIG_T* getFreeInverterSlot();
INVERTER_CONFIG_T* getInverterConfig(const uint64_t serial);
Expand All @@ -356,6 +371,11 @@ class ConfigurationClass {
static void deserializePowerMeterHttpJsonConfig(JsonObject const& source, PowerMeterHttpJsonConfig& target);
static void deserializePowerMeterHttpSmlConfig(JsonObject const& source, PowerMeterHttpSmlConfig& target);
static void deserializeBatteryConfig(JsonObject const& source, BatteryConfig& target);

private:
void loop();

Task _loopTask;
};

extern ConfigurationClass Configuration;
4 changes: 2 additions & 2 deletions src/Battery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ void BatteryClass::updateSettings()
_upProvider = nullptr;
}

CONFIG_T& config = Configuration.get();
auto const& config = Configuration.get();
if (!config.Battery.Enabled) { return; }

bool verboseLogging = config.Battery.VerboseLogging;
Expand Down Expand Up @@ -86,7 +86,7 @@ void BatteryClass::loop()

float BatteryClass::getDischargeCurrentLimit()
{
CONFIG_T& config = Configuration.get();
auto const& config = Configuration.get();

if (!config.Battery.EnableDischargeCurrentLimit) { return FLT_MAX; }

Expand Down
64 changes: 62 additions & 2 deletions src/Configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,17 @@

CONFIG_T config;

void ConfigurationClass::init()
static std::condition_variable sWriterCv;
static std::mutex sWriterMutex;
static unsigned sWriterCount = 0;

void ConfigurationClass::init(Scheduler& scheduler)
{
scheduler.addTask(_loopTask);
_loopTask.setCallback(std::bind(&ConfigurationClass::loop, this));
_loopTask.setIterations(TASK_FOREVER);
_loopTask.enable();

memset(&config, 0x0, sizeof(config));
}

Expand Down Expand Up @@ -662,6 +671,20 @@ bool ConfigurationClass::read()
config.Huawei.Auto_Power_Target_Power_Consumption = huawei["target_power_consumption"] | HUAWEI_AUTO_POWER_TARGET_POWER_CONSUMPTION;

f.close();

// Check for default DTU serial
MessageOutput.print("Check for default DTU serial... ");
if (config.Dtu.Serial == DTU_SERIAL) {
MessageOutput.print("generate serial based on ESP chip id: ");
const uint64_t dtuId = Utils::generateDtuSerial();
MessageOutput.printf("%0" PRIx32 "%08" PRIx32 "... ",
((uint32_t)((dtuId >> 32) & 0xFFFFFFFF)),
((uint32_t)(dtuId & 0xFFFFFFFF)));
config.Dtu.Serial = dtuId;
Configuration.write();
}
MessageOutput.println("done");

return true;
}

Expand Down Expand Up @@ -734,11 +757,16 @@ void ConfigurationClass::migrate()
read();
}

CONFIG_T& ConfigurationClass::get()
CONFIG_T const& ConfigurationClass::get()
{
return config;
}

ConfigurationClass::WriteGuard ConfigurationClass::getWriteGuard()
{
return WriteGuard();
}

INVERTER_CONFIG_T* ConfigurationClass::getFreeInverterSlot()
{
for (uint8_t i = 0; i < INV_MAX_COUNT; i++) {
Expand Down Expand Up @@ -783,4 +811,36 @@ void ConfigurationClass::deleteInverterById(const uint8_t id)
}
}

void ConfigurationClass::loop()
{
std::unique_lock<std::mutex> lock(sWriterMutex);
if (sWriterCount == 0) { return; }

MessageOutput.println("notify all");
sWriterCv.notify_all();
MessageOutput.println("waiting for writers to finish");
sWriterCv.wait(lock, [] { return sWriterCount == 0; });
MessageOutput.println("configurationclass::loop done");
}

CONFIG_T& ConfigurationClass::WriteGuard::getConfig()
{
return config;
}

ConfigurationClass::WriteGuard::WriteGuard()
: _lock(sWriterMutex)
{
MessageOutput.println("writeguard c'tor locked mutex");
sWriterCount++;
MessageOutput.println("writeguard c'tor incremented writer count");
sWriterCv.wait(_lock);
MessageOutput.println("writeguard c'tor completed");
}

ConfigurationClass::WriteGuard::~WriteGuard() {
sWriterCount--;
if (sWriterCount == 0) { sWriterCv.notify_all(); }
}

ConfigurationClass Configuration;
4 changes: 2 additions & 2 deletions src/JkBmsController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ void Controller::deinit()

Controller::Interface Controller::getInterface() const
{
CONFIG_T& config = Configuration.get();
auto const& config = Configuration.get();
if (0x00 == config.Battery.JkBmsInterface) { return Interface::Uart; }
if (0x01 == config.Battery.JkBmsInterface) { return Interface::Transceiver; }
return Interface::Invalid;
Expand Down Expand Up @@ -141,7 +141,7 @@ void Controller::sendRequest(uint8_t pollInterval)

void Controller::loop()
{
CONFIG_T& config = Configuration.get();
auto const& config = Configuration.get();
uint8_t pollInterval = config.Battery.JkBmsPollingInterval;

while (_upSerial->available()) {
Expand Down
4 changes: 2 additions & 2 deletions src/MqttHandleBatteryHass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ void MqttHandleBatteryHassClass::init(Scheduler& scheduler)

void MqttHandleBatteryHassClass::loop()
{
CONFIG_T& config = Configuration.get();
auto const& config = Configuration.get();

if (!config.Battery.Enabled) { return; }

Expand Down Expand Up @@ -323,7 +323,7 @@ void MqttHandleBatteryHassClass::createDeviceInfo(JsonObject& object)
{
object["name"] = "Battery(" + serial + ")";

auto& config = Configuration.get();
auto const& config = Configuration.get();
if (config.Battery.Provider == 1) {
object["name"] = "JK BMS (" + Battery.getStats()->getManufacturer() + ")";
}
Expand Down
2 changes: 1 addition & 1 deletion src/MqttHandleVedirect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ void MqttHandleVedirectClass::forceUpdate()

void MqttHandleVedirectClass::loop()
{
CONFIG_T& config = Configuration.get();
auto const& config = Configuration.get();

if (!MqttSettings.getConnected() || !config.Vedirect.Enabled) {
return;
Expand Down
2 changes: 1 addition & 1 deletion src/VictronMppt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ void VictronMpptClass::updateSettings()
}
_serialPortOwners.clear();

CONFIG_T& config = Configuration.get();
auto const& config = Configuration.get();
if (!config.Vedirect.Enabled) { return; }

const PinMapping_t& pin = PinMapping.get();
Expand Down
4 changes: 2 additions & 2 deletions src/WebApi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ void WebApiClass::reload()

bool WebApiClass::checkCredentials(AsyncWebServerRequest* request)
{
CONFIG_T& config = Configuration.get();
auto const& config = Configuration.get();
if (request->authenticate(AUTH_USERNAME, config.Security.Password)) {
return true;
}
Expand All @@ -76,7 +76,7 @@ bool WebApiClass::checkCredentials(AsyncWebServerRequest* request)

bool WebApiClass::checkCredentialsReadonly(AsyncWebServerRequest* request)
{
CONFIG_T& config = Configuration.get();
auto const& config = Configuration.get();
if (config.Security.AllowReadonly) {
return true;
} else {
Expand Down
30 changes: 17 additions & 13 deletions src/WebApi_Huawei.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,19 +200,22 @@ void WebApiHuaweiClass::onAdminPost(AsyncWebServerRequest* request)
return;
}

CONFIG_T& config = Configuration.get();
config.Huawei.Enabled = root["enabled"].as<bool>();
config.Huawei.VerboseLogging = root["verbose_logging"];
config.Huawei.CAN_Controller_Frequency = root["can_controller_frequency"].as<uint32_t>();
config.Huawei.Auto_Power_Enabled = root["auto_power_enabled"].as<bool>();
config.Huawei.Auto_Power_BatterySoC_Limits_Enabled = root["auto_power_batterysoc_limits_enabled"].as<bool>();
config.Huawei.Emergency_Charge_Enabled = root["emergency_charge_enabled"].as<bool>();
config.Huawei.Auto_Power_Voltage_Limit = root["voltage_limit"].as<float>();
config.Huawei.Auto_Power_Enable_Voltage_Limit = root["enable_voltage_limit"].as<float>();
config.Huawei.Auto_Power_Lower_Power_Limit = root["lower_power_limit"].as<float>();
config.Huawei.Auto_Power_Upper_Power_Limit = root["upper_power_limit"].as<float>();
config.Huawei.Auto_Power_Stop_BatterySoC_Threshold = root["stop_batterysoc_threshold"];
config.Huawei.Auto_Power_Target_Power_Consumption = root["target_power_consumption"];
{
auto guard = Configuration.getWriteGuard();
auto& config = guard.getConfig();
config.Huawei.Enabled = root["enabled"].as<bool>();
config.Huawei.VerboseLogging = root["verbose_logging"];
config.Huawei.CAN_Controller_Frequency = root["can_controller_frequency"].as<uint32_t>();
config.Huawei.Auto_Power_Enabled = root["auto_power_enabled"].as<bool>();
config.Huawei.Auto_Power_BatterySoC_Limits_Enabled = root["auto_power_batterysoc_limits_enabled"].as<bool>();
config.Huawei.Emergency_Charge_Enabled = root["emergency_charge_enabled"].as<bool>();
config.Huawei.Auto_Power_Voltage_Limit = root["voltage_limit"].as<float>();
config.Huawei.Auto_Power_Enable_Voltage_Limit = root["enable_voltage_limit"].as<float>();
config.Huawei.Auto_Power_Lower_Power_Limit = root["lower_power_limit"].as<float>();
config.Huawei.Auto_Power_Upper_Power_Limit = root["upper_power_limit"].as<float>();
config.Huawei.Auto_Power_Stop_BatterySoC_Threshold = root["stop_batterysoc_threshold"];
config.Huawei.Auto_Power_Target_Power_Consumption = root["target_power_consumption"];
}

WebApi.writeConfig(retMsg);

Expand All @@ -226,6 +229,7 @@ void WebApiHuaweiClass::onAdminPost(AsyncWebServerRequest* request)
yield();
ESP.restart();

auto const& config = Configuration.get();
const PinMapping_t& pin = PinMapping.get();
// Properly turn this on
if (config.Huawei.Enabled) {
Expand Down
7 changes: 5 additions & 2 deletions src/WebApi_battery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,11 @@ void WebApiBatteryClass::onAdminPost(AsyncWebServerRequest* request)
return;
}

auto& config = Configuration.get();
ConfigurationClass::deserializeBatteryConfig(root.as<JsonObject>(), config.Battery);
{
auto guard = Configuration.getWriteGuard();
auto& config = guard.getConfig();
ConfigurationClass::deserializeBatteryConfig(root.as<JsonObject>(), config.Battery);
}

WebApi.writeConfig(retMsg);

Expand Down
35 changes: 20 additions & 15 deletions src/WebApi_device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,23 +158,28 @@ void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request)
return;
}

CONFIG_T& config = Configuration.get();
bool performRestart = root["curPin"]["name"].as<String>() != config.Dev_PinMapping;

strlcpy(config.Dev_PinMapping, root["curPin"]["name"].as<String>().c_str(), sizeof(config.Dev_PinMapping));
config.Display.Rotation = root["display"]["rotation"].as<uint8_t>();
config.Display.PowerSafe = root["display"]["power_safe"].as<bool>();
config.Display.ScreenSaver = root["display"]["screensaver"].as<bool>();
config.Display.Contrast = root["display"]["contrast"].as<uint8_t>();
config.Display.Language = root["display"]["language"].as<uint8_t>();
config.Display.Diagram.Duration = root["display"]["diagramduration"].as<uint32_t>();
config.Display.Diagram.Mode = root["display"]["diagrammode"].as<DiagramMode_t>();

for (uint8_t i = 0; i < PINMAPPING_LED_COUNT; i++) {
config.Led_Single[i].Brightness = root["led"][i]["brightness"].as<uint8_t>();
config.Led_Single[i].Brightness = min<uint8_t>(100, config.Led_Single[i].Brightness);
{
auto guard = Configuration.getWriteGuard();
auto& config = guard.getConfig();

strlcpy(config.Dev_PinMapping, root["curPin"]["name"].as<String>().c_str(), sizeof(config.Dev_PinMapping));
config.Display.Rotation = root["display"]["rotation"].as<uint8_t>();
config.Display.PowerSafe = root["display"]["power_safe"].as<bool>();
config.Display.ScreenSaver = root["display"]["screensaver"].as<bool>();
config.Display.Contrast = root["display"]["contrast"].as<uint8_t>();
config.Display.Language = root["display"]["language"].as<uint8_t>();
config.Display.Diagram.Duration = root["display"]["diagramduration"].as<uint32_t>();
config.Display.Diagram.Mode = root["display"]["diagrammode"].as<DiagramMode_t>();

for (uint8_t i = 0; i < PINMAPPING_LED_COUNT; i++) {
config.Led_Single[i].Brightness = root["led"][i]["brightness"].as<uint8_t>();
config.Led_Single[i].Brightness = min<uint8_t>(100, config.Led_Single[i].Brightness);
}
}

auto const& config = Configuration.get();
bool performRestart = root["curPin"]["name"].as<String>() != config.Dev_PinMapping;

Display.setDiagramMode(static_cast<DiagramMode_t>(config.Display.Diagram.Mode));
Display.setOrientation(config.Display.Rotation);
Display.enablePowerSafe = config.Display.PowerSafe;
Expand Down
Loading

0 comments on commit 0974019

Please sign in to comment.