Merge remote-tracking branch 'tbnobody/OpenDTU/master' into development
This commit is contained in:
commit
27f20a76f0
@ -34,6 +34,7 @@
|
||||
struct CHANNEL_CONFIG_T {
|
||||
uint16_t MaxChannelPower;
|
||||
char Name[CHAN_MAX_NAME_STRLEN];
|
||||
float YieldTotalOffset;
|
||||
};
|
||||
|
||||
struct INVERTER_CONFIG_T {
|
||||
|
||||
@ -59,9 +59,21 @@ const byteAssign_t* StatisticsParser::getAssignmentByChannelField(ChannelType_t
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fieldSettings_t* StatisticsParser::getSettingByChannelField(ChannelType_t type, ChannelNum_t channel, FieldId_t fieldId)
|
||||
{
|
||||
for (auto& i : _fieldSettings) {
|
||||
if (i.type == type && i.ch == channel && i.fieldId == fieldId) {
|
||||
return &i;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
float StatisticsParser::getChannelFieldValue(ChannelType_t type, ChannelNum_t channel, FieldId_t fieldId)
|
||||
{
|
||||
const byteAssign_t* pos = getAssignmentByChannelField(type, channel, fieldId);
|
||||
fieldSettings_t* setting = getSettingByChannelField(type, channel, fieldId);
|
||||
|
||||
if (pos == NULL) {
|
||||
return 0;
|
||||
}
|
||||
@ -88,6 +100,9 @@ float StatisticsParser::getChannelFieldValue(ChannelType_t type, ChannelNum_t ch
|
||||
}
|
||||
|
||||
result /= static_cast<float>(div);
|
||||
if (setting != NULL) {
|
||||
result += setting->offset;
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
// Value has to be calculated
|
||||
@ -121,6 +136,25 @@ uint8_t StatisticsParser::getChannelFieldDigits(ChannelType_t type, ChannelNum_t
|
||||
return pos->digits;
|
||||
}
|
||||
|
||||
float StatisticsParser::getChannelFieldOffset(ChannelType_t type, ChannelNum_t channel, FieldId_t fieldId)
|
||||
{
|
||||
fieldSettings_t* setting = getSettingByChannelField(type, channel, fieldId);
|
||||
if (setting != NULL) {
|
||||
return setting->offset;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void StatisticsParser::setChannelFieldOffset(ChannelType_t type, ChannelNum_t channel, FieldId_t fieldId, float offset)
|
||||
{
|
||||
fieldSettings_t* setting = getSettingByChannelField(type, channel, fieldId);
|
||||
if (setting != NULL) {
|
||||
setting->offset = offset;
|
||||
} else {
|
||||
_fieldSettings.push_back({ type, channel, fieldId, offset });
|
||||
}
|
||||
}
|
||||
|
||||
std::list<ChannelType_t> StatisticsParser::getChannelTypes()
|
||||
{
|
||||
return {
|
||||
|
||||
@ -82,6 +82,13 @@ typedef struct {
|
||||
uint8_t digits; // number of valid digits after the decimal point
|
||||
} byteAssign_t;
|
||||
|
||||
typedef struct {
|
||||
ChannelType_t type;
|
||||
ChannelNum_t ch; // channel 0 - 4
|
||||
FieldId_t fieldId; // field id
|
||||
float offset; // offset (positive/negative) to be applied on the fetched value
|
||||
} fieldSettings_t;
|
||||
|
||||
class StatisticsParser : public Parser {
|
||||
public:
|
||||
void clearBuffer();
|
||||
@ -90,12 +97,17 @@ public:
|
||||
void setByteAssignment(const std::list<byteAssign_t>* byteAssignment);
|
||||
|
||||
const byteAssign_t* getAssignmentByChannelField(ChannelType_t type, ChannelNum_t channel, FieldId_t fieldId);
|
||||
fieldSettings_t* getSettingByChannelField(ChannelType_t type, ChannelNum_t channel, FieldId_t fieldId);
|
||||
|
||||
float getChannelFieldValue(ChannelType_t type, ChannelNum_t channel, FieldId_t fieldId);
|
||||
bool hasChannelFieldValue(ChannelType_t type, ChannelNum_t channel, FieldId_t fieldId);
|
||||
const char* getChannelFieldUnit(ChannelType_t type, ChannelNum_t channel, FieldId_t fieldId);
|
||||
const char* getChannelFieldName(ChannelType_t type, ChannelNum_t channel, FieldId_t fieldId);
|
||||
uint8_t getChannelFieldDigits(ChannelType_t type, ChannelNum_t channel, FieldId_t fieldId);
|
||||
|
||||
float getChannelFieldOffset(ChannelType_t type, ChannelNum_t channel, FieldId_t fieldId);
|
||||
void setChannelFieldOffset(ChannelType_t type, ChannelNum_t channel, FieldId_t fieldId, float offset);
|
||||
|
||||
std::list<ChannelType_t> getChannelTypes();
|
||||
const char* getChannelTypeName(ChannelType_t type);
|
||||
std::list<ChannelNum_t> getChannelsByType(ChannelType_t type);
|
||||
@ -113,6 +125,7 @@ private:
|
||||
uint16_t _stringMaxPower[CH4];
|
||||
|
||||
const std::list<byteAssign_t>* _byteAssignment;
|
||||
std::list<fieldSettings_t> _fieldSettings;
|
||||
|
||||
uint32_t _rxFailureCount = 0;
|
||||
};
|
||||
@ -15,7 +15,7 @@ extra_configs =
|
||||
|
||||
[env]
|
||||
framework = arduino
|
||||
platform = espressif32@>=6.0.0
|
||||
platform = espressif32@>=6.0.1
|
||||
|
||||
build_flags =
|
||||
-DCOMPONENT_EMBED_FILES=webapp_dist/index.html.gz:webapp_dist/zones.json.gz:webapp_dist/favicon.ico:webapp_dist/js/app.js.gz
|
||||
@ -23,7 +23,7 @@ build_flags =
|
||||
|
||||
lib_deps =
|
||||
https://github.com/yubox-node-org/ESPAsyncWebServer
|
||||
bblanchon/ArduinoJson @ ^6.20.0
|
||||
bblanchon/ArduinoJson @ ^6.20.1
|
||||
https://github.com/bertmelis/espMqttClient.git#v1.3.3
|
||||
nrf24/RF24 @ ^1.4.5
|
||||
olikraus/U8g2 @ ^2.34.13
|
||||
|
||||
@ -100,6 +100,7 @@ bool ConfigurationClass::write()
|
||||
JsonObject chanData = channel.createNestedObject();
|
||||
chanData["name"] = config.Inverter[i].channel[c].Name;
|
||||
chanData["max_power"] = config.Inverter[i].channel[c].MaxChannelPower;
|
||||
chanData["yield_total_offset"] = config.Inverter[i].channel[c].YieldTotalOffset;
|
||||
}
|
||||
}
|
||||
|
||||
@ -233,6 +234,7 @@ bool ConfigurationClass::read()
|
||||
JsonArray channel = inv["channel"];
|
||||
for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) {
|
||||
config.Inverter[i].channel[c].MaxChannelPower = channel[c]["max_power"] | 0;
|
||||
config.Inverter[i].channel[c].YieldTotalOffset = channel[c]["yield_total_offset"] | 0.0f;
|
||||
strlcpy(config.Inverter[i].channel[c].Name, channel[c]["name"] | "", sizeof(config.Inverter[i].channel[c].Name));
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,13 +117,13 @@ void MqttHandleHassClass::publishField(std::shared_ptr<InverterAbstract> inv, Ch
|
||||
if (type != TYPE_DC) {
|
||||
name = String(inv->name()) + " " + fieldName;
|
||||
} else {
|
||||
name = String(inv->name()) + " CH" + String(channel) + " " + fieldName;
|
||||
name = String(inv->name()) + " CH" + chanNum + " " + fieldName;
|
||||
}
|
||||
|
||||
DynamicJsonDocument root(1024);
|
||||
root[F("name")] = name;
|
||||
root[F("stat_t")] = stateTopic;
|
||||
root[F("uniq_id")] = serial + "_ch" + String(channel) + "_" + fieldName;
|
||||
root[F("uniq_id")] = serial + "_ch" + chanNum + "_" + fieldName;
|
||||
|
||||
String unit_of_measure = inv->Statistics()->getChannelFieldUnit(type, channel, fieldType.fieldId);
|
||||
if (unit_of_measure != "") {
|
||||
|
||||
@ -67,6 +67,7 @@ void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request)
|
||||
JsonObject chanData = channel.createNestedObject();
|
||||
chanData["name"] = config.Inverter[i].channel[c].Name;
|
||||
chanData["max_power"] = config.Inverter[i].channel[c].MaxChannelPower;
|
||||
chanData["yield_total_offset"] = config.Inverter[i].channel[c].YieldTotalOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -267,6 +268,7 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
|
||||
uint8_t arrayCount = 0;
|
||||
for (JsonVariant channel : channelArray) {
|
||||
inverter.channel[arrayCount].MaxChannelPower = channel[F("max_power")].as<uint16_t>();
|
||||
inverter.channel[arrayCount].YieldTotalOffset = channel[F("yield_total_offset")].as<float>();
|
||||
strncpy(inverter.channel[arrayCount].Name, channel[F("name")] | "", sizeof(inverter.channel[arrayCount].Name));
|
||||
arrayCount++;
|
||||
}
|
||||
@ -297,6 +299,7 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
|
||||
if (inv != nullptr) {
|
||||
for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) {
|
||||
inv->Statistics()->setStringMaxPower(c, inverter.channel[c].MaxChannelPower);
|
||||
inv->Statistics()->setChannelFieldOffset(TYPE_DC, static_cast<ChannelNum_t>(c), FLD_YT, inverter.channel[c].YieldTotalOffset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -155,6 +155,7 @@ void setup()
|
||||
if (inv != nullptr) {
|
||||
for (uint8_t c = 0; c < INV_MAX_CHAN_COUNT; c++) {
|
||||
inv->Statistics()->setStringMaxPower(c, config.Inverter[i].channel[c].MaxChannelPower);
|
||||
inv->Statistics()->setChannelFieldOffset(TYPE_DC, static_cast<ChannelNum_t>(c), FLD_YT, config.Inverter[i].channel[c].YieldTotalOffset);
|
||||
}
|
||||
}
|
||||
MessageOutput.println(F(" done"));
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
"eslint": "^8.34.0",
|
||||
"eslint-plugin-vue": "^9.9.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"sass": "^1.58.0",
|
||||
"sass": "^1.58.1",
|
||||
"typescript": "^4.9.5",
|
||||
"vite": "^4.1.1",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
|
||||
@ -453,6 +453,8 @@
|
||||
"StringNameHint": "Hier kann ein eigener Name für den entsprechenden Port des Wechselrichters angegeben werden.",
|
||||
"StringMaxPower": "Max. Leistung String {num}:",
|
||||
"StringMaxPowerHint": "Eingabe der maximalen Leistung der angeschlossenen Solarmodule.",
|
||||
"StringYtOffset": "Ertragsversatz String {num}:",
|
||||
"StringYtOffsetHint": "Dieser Offset wird beim Auslesen des Gesamtertragswertes des Wechselrichters angewendet. Damit kann der Gesamtertrag des Wechselrichters auf Null gesetzt werden, wenn ein gebrauchter Wechselrichter verwendet wird.",
|
||||
"InverterHint": "*) Geben Sie die W<sub>p</sub> des Ports ein, um die Einstrahlung zu errechnen.",
|
||||
"Cancel": "@:maintenancereboot.Cancel",
|
||||
"Save": "@:dtuadmin.Save",
|
||||
|
||||
@ -453,6 +453,8 @@
|
||||
"StringNameHint": "Here you can specify a custom name for the respective port of your inverter.",
|
||||
"StringMaxPower": "Max power string {num}:",
|
||||
"StringMaxPowerHint": "Enter the max power of the connected solar panels.",
|
||||
"StringYtOffset": "Yield total offset string {num}:",
|
||||
"StringYtOffsetHint": "This offset is applied the read yield total value from the inverter. This can be used to set the yield total of the inverter to zero if a used inverter is used.",
|
||||
"InverterHint": "*) Enter the W<sub>p</sub> of the channel to calculate irradiation.",
|
||||
"Cancel": "@:maintenancereboot.Cancel",
|
||||
"Save": "@:dtuadmin.Save",
|
||||
|
||||
@ -453,6 +453,8 @@
|
||||
"StringNameHint": "Ici, vous pouvez spécifier un nom personnalisé pour le port respectif de votre onduleur.",
|
||||
"StringMaxPower": "Puissance maximale de la ligne {num}:",
|
||||
"StringMaxPowerHint": "Entrez la puissance maximale des panneaux solaires connectés.",
|
||||
"StringYtOffset": "Yield total offset string {num}:",
|
||||
"StringYtOffsetHint": "This offset is applied the read yield total value from the inverter. This can be used to set the yield total of the inverter to zero if a used inverter is used.",
|
||||
"InverterHint": "*) Entrez le W<sub>p</sub> du canal pour calculer l'irradiation.",
|
||||
"Cancel": "@:maintenancereboot.Cancel",
|
||||
"Save": "@:dtuadmin.Save",
|
||||
|
||||
@ -55,7 +55,7 @@
|
||||
</BasePage>
|
||||
|
||||
<div class="modal" id="inverterEdit" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">{{ $t('inverteradmin.EditInverter') }}</h5>
|
||||
@ -90,7 +90,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
</div>
|
||||
<div class="row g-2">
|
||||
<div class="col">
|
||||
<label :for="`inverter-max_${index}`" class="col-form-label">
|
||||
{{ $t('inverteradmin.StringMaxPower', { num: index + 1 }) }}
|
||||
<BIconInfoCircle v-tooltip :title="$t('inverteradmin.StringMaxPowerHint')" />
|
||||
@ -105,6 +107,21 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label :for="`inverter-ytoffset_${index}`" class="col-form-label">
|
||||
{{ $t('inverteradmin.StringYtOffset', { num: index + 1 }) }}
|
||||
<BIconInfoCircle v-tooltip :title="$t('inverteradmin.StringYtOffsetHint')" />
|
||||
</label>
|
||||
<div class="d-flex mb-2">
|
||||
<div class="input-group">
|
||||
<input type="number" class="form-control" :id="`inverter-ytoffset_${index}`"
|
||||
min="0" v-model="selectedInverterData.channel[index].yield_total_offset"
|
||||
:aria-describedby="`inverter-ytoffsetDescription_${index} inverter-customizer`" />
|
||||
<span class="input-group-text"
|
||||
:id="`inverter-ytoffsetDescription_${index}`">kWh</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div :id="`inverter-customizer`" class="form-text" v-html="$t('inverteradmin.InverterHint')">
|
||||
@ -163,6 +180,7 @@ import { defineComponent } from 'vue';
|
||||
declare interface Channel {
|
||||
name: string;
|
||||
max_power: number;
|
||||
yield_total_offset: number;
|
||||
}
|
||||
|
||||
declare interface Inverter {
|
||||
|
||||
@ -1888,10 +1888,10 @@ safe-regex-test@^1.0.0:
|
||||
get-intrinsic "^1.1.3"
|
||||
is-regex "^1.1.4"
|
||||
|
||||
sass@^1.58.0:
|
||||
version "1.58.0"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.58.0.tgz#ee8aea3ad5ea5c485c26b3096e2df6087d0bb1cc"
|
||||
integrity sha512-PiMJcP33DdKtZ/1jSjjqVIKihoDc6yWmYr9K/4r3fVVIEDAluD0q7XZiRKrNJcPK3qkLRF/79DND1H5q1LBjgg==
|
||||
sass@^1.58.1:
|
||||
version "1.58.1"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.58.1.tgz#17ab0390076a50578ed0733f1cc45429e03405f6"
|
||||
integrity sha512-bnINi6nPXbP1XNRaranMFEBZWUfdW/AF16Ql5+ypRxfTvCRTTKrLsMIakyDcayUt2t/RZotmL4kgJwNH5xO+bg==
|
||||
dependencies:
|
||||
chokidar ">=3.0.0 <4.0.0"
|
||||
immutable "^4.0.0"
|
||||
|
||||
Binary file not shown.
Loading…
Reference in New Issue
Block a user