Extended configuration to allow string names

* Current config will be migrated to new format
* Already extended web API to get/post new format
This commit is contained in:
Thomas Basler 2022-11-15 19:31:31 +01:00
parent 0c46ecf121
commit d28fadbdac
5 changed files with 64 additions and 18 deletions

View File

@ -4,7 +4,7 @@
#include <Arduino.h>
#define CONFIG_FILENAME "/config.json"
#define CONFIG_VERSION 0x00011600 // 0.1.22 // make sure to clean all after change
#define CONFIG_VERSION 0x00011700 // 0.1.23 // make sure to clean all after change
#define WIFI_MAX_SSID_STRLEN 31
#define WIFI_MAX_PASSWORD_STRLEN 64
@ -25,12 +25,19 @@
#define INV_MAX_COUNT 10
#define INV_MAX_CHAN_COUNT 4
#define CHAN_MAX_NAME_STRLEN 31
#define JSON_BUFFER_SIZE 6144
struct CHANNEL_CONFIG_T {
uint16_t MaxChannelPower;
char Name[CHAN_MAX_NAME_STRLEN];
};
struct INVERTER_CONFIG_T {
uint64_t Serial;
char Name[INV_MAX_NAME_STRLEN + 1];
uint16_t MaxChannelPower[INV_MAX_CHAN_COUNT];
CHANNEL_CONFIG_T channel[INV_MAX_CHAN_COUNT];
};
struct CONFIG_T {

View File

@ -84,9 +84,11 @@ bool ConfigurationClass::write()
inv["serial"] = config.Inverter[i].Serial;
inv["name"] = config.Inverter[i].Name;
JsonArray channels = inv.createNestedArray("channels");
JsonArray channel = inv.createNestedArray("channel");
for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) {
channels.add(config.Inverter[i].MaxChannelPower[c]);
JsonObject chanData = channel.createNestedObject();
chanData["name"] = config.Inverter[i].channel[c].Name;
chanData["max_power"] = config.Inverter[i].channel[c].MaxChannelPower;
}
}
@ -202,9 +204,10 @@ bool ConfigurationClass::read()
config.Inverter[i].Serial = inv["serial"] | 0ULL;
strlcpy(config.Inverter[i].Name, inv["name"] | "", sizeof(config.Inverter[i].Name));
JsonArray channels = inv["channels"];
JsonArray channel = inv["channel"];
for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) {
config.Inverter[i].MaxChannelPower[c] = channels[c];
config.Inverter[i].channel[c].MaxChannelPower = channel[c]["max_power"] | 0;
strlcpy(config.Inverter[i].channel[c].Name, channel[c]["name"] | "", sizeof(config.Inverter[i].channel[c].Name));
}
}
@ -214,8 +217,35 @@ bool ConfigurationClass::read()
void ConfigurationClass::migrate()
{
if (config.Cfg_Version < 0x00011700) {
File f = LittleFS.open(CONFIG_FILENAME, "r", false);
if (!f) {
Serial.println(F("Failed to open file, cancel migration"));
return;
}
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
// Deserialize the JSON document
DeserializationError error = deserializeJson(doc, f);
if (error) {
Serial.println(F("Failed to read file, cancel migration"));
return;
}
JsonArray inverters = doc["inverters"];
for (uint8_t i = 0; i < INV_MAX_COUNT; i++) {
JsonObject inv = inverters[i].as<JsonObject>();
JsonArray channels = inv["channels"];
for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) {
config.Inverter[i].channel[c].MaxChannelPower = channels[c];
strlcpy(config.Inverter[i].channel[c].Name, "", sizeof(config.Inverter[i].channel[c].Name));
}
}
}
config.Cfg_Version = CONFIG_VERSION;
write();
read();
}
CONFIG_T& ConfigurationClass::get()

View File

@ -62,8 +62,11 @@ void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request)
max_channels = inv->Statistics()->getChannelCount();
}
JsonArray channel = obj.createNestedArray("channel");
for (uint8_t c = 0; c < max_channels; c++) {
obj[F("max_power")][c] = config.Inverter[i].MaxChannelPower[c];
JsonObject chanData = channel.createNestedObject();
chanData["name"] = config.Inverter[i].channel[c].Name;
chanData["max_power"] = config.Inverter[i].channel[c].MaxChannelPower;
}
}
}
@ -154,7 +157,7 @@ void WebApiInverterClass::onInverterAdd(AsyncWebServerRequest* request)
if (inv != nullptr) {
for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) {
inv->Statistics()->setChannelMaxPower(c, inverter->MaxChannelPower[c]);
inv->Statistics()->setChannelMaxPower(c, inverter->channel[c].MaxChannelPower);
}
}
@ -197,7 +200,7 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
return;
}
if (!(root.containsKey("id") && root.containsKey("serial") && root.containsKey("name") && root.containsKey("max_power"))) {
if (!(root.containsKey("id") && root.containsKey("serial") && root.containsKey("name") && root.containsKey("channel"))) {
retMsg[F("message")] = F("Values are missing!");
response->setLength();
request->send(response);
@ -225,8 +228,8 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
return;
}
JsonArray maxPowerArray = root[F("max_power")].as<JsonArray>();
if (maxPowerArray.size() == 0 || maxPowerArray.size() > INV_MAX_CHAN_COUNT) {
JsonArray channelArray = root[F("channel")].as<JsonArray>();
if (channelArray.size() == 0 || channelArray.size() > INV_MAX_CHAN_COUNT) {
retMsg[F("message")] = F("Invalid amount of max channel setting given!");
response->setLength();
request->send(response);
@ -243,8 +246,9 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
strncpy(inverter.Name, root[F("name")].as<String>().c_str(), INV_MAX_NAME_STRLEN);
uint8_t arrayCount = 0;
for (JsonVariant maxPower : maxPowerArray) {
inverter.MaxChannelPower[arrayCount] = maxPower.as<uint16_t>();
for (JsonVariant channel : channelArray) {
inverter.channel[arrayCount].MaxChannelPower = channel[F("max_power")].as<uint16_t>();
strncpy(inverter.channel[arrayCount].Name, channel[F("name")] | "", sizeof(inverter.channel[arrayCount].Name));
arrayCount++;
}
@ -272,7 +276,7 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
if (inv != nullptr) {
for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) {
inv->Statistics()->setChannelMaxPower(c, inverter.MaxChannelPower[c]);
inv->Statistics()->setChannelMaxPower(c, inverter.channel[c].MaxChannelPower);
}
}

View File

@ -116,7 +116,7 @@ void setup()
if (inv != nullptr) {
for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) {
inv->Statistics()->setChannelMaxPower(c, config.Inverter[i].MaxChannelPower[c]);
inv->Statistics()->setChannelMaxPower(c, config.Inverter[i].channel[c].MaxChannelPower);
}
}
Serial.println(F(" done"));

View File

@ -81,12 +81,12 @@
class="form-control" maxlength="31" />
</div>
<div v-for="(max, index) in selectedInverterData.max_power" :key="`${index}`">
<div v-for="(max, index) in selectedInverterData.channel" :key="`${index}`">
<label :for="`inverter-max_${index}`" class="col-form-label">Max power string {{ index +1 }}:</label>
<div class="d-flex mb-2">
<div class="input-group">
<input type="number" class="form-control" :id="`inverter-max_${index}`" min="0"
v-model="selectedInverterData.max_power[index]"
v-model="selectedInverterData.channel[index].max_power"
:aria-describedby="`inverter-maxDescription_${index} inverter-customizer`" />
<span class="input-group-text" :id="`inverter-maxDescription_${index}`">W<sup>*</sup></span>
</div>
@ -139,12 +139,17 @@ import * as bootstrap from 'bootstrap';
import BootstrapAlert from "@/components/BootstrapAlert.vue";
import { handleResponse, authHeader } from '@/utils/authentication';
declare interface Channel {
name: string;
max_power: number;
}
declare interface Inverter {
id: string;
serial: number;
name: string;
type: string;
max_power: number[];
channel: Array<Channel>;
}
declare interface AlertResponse {