mirror of
https://github.com/lumapu/ahoy.git
synced 2025-05-10 15:36:38 +02:00
0.8.26
* read grid profile as HEX (`live` -> click inverter name -> `show grid profile`)
This commit is contained in:
parent
681c58e6a2
commit
16d0eb2048
10 changed files with 73 additions and 7 deletions
|
@ -1,5 +1,8 @@
|
||||||
# Development Changes
|
# Development Changes
|
||||||
|
|
||||||
|
## 0.8.26 - 2023-12-17
|
||||||
|
* read grid profile as HEX (`live` -> click inverter name -> `show grid profile`)
|
||||||
|
|
||||||
## 0.8.25 - 2023-12-17
|
## 0.8.25 - 2023-12-17
|
||||||
* RX channel ID starts with fixed value #1277
|
* RX channel ID starts with fixed value #1277
|
||||||
* fix static IP for Ethernet
|
* fix static IP for Ethernet
|
||||||
|
|
|
@ -336,7 +336,7 @@ void app::tickSend(void) {
|
||||||
DBGPRINT(String(fill));
|
DBGPRINT(String(fill));
|
||||||
DBGPRINT(F(" of "));
|
DBGPRINT(F(" of "));
|
||||||
DBGPRINT(String(max));
|
DBGPRINT(String(max));
|
||||||
DBGPRINTLN(F("entries used"));
|
DBGPRINTLN(F(" entries used"));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
|
for (uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
* https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html#flash-layout
|
* https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html#flash-layout
|
||||||
* */
|
* */
|
||||||
|
|
||||||
#define CONFIG_VERSION 5
|
#define CONFIG_VERSION 6
|
||||||
|
|
||||||
|
|
||||||
#define PROT_MASK_INDEX 0x0001
|
#define PROT_MASK_INDEX 0x0001
|
||||||
|
@ -157,6 +157,7 @@ typedef struct {
|
||||||
bool startWithoutTime;
|
bool startWithoutTime;
|
||||||
float yieldEffiency;
|
float yieldEffiency;
|
||||||
uint16_t gapMs;
|
uint16_t gapMs;
|
||||||
|
bool readGrid;
|
||||||
} cfgInst_t;
|
} cfgInst_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -440,6 +441,7 @@ class settings {
|
||||||
mCfg.inst.rstMaxValsMidNight = false;
|
mCfg.inst.rstMaxValsMidNight = false;
|
||||||
mCfg.inst.yieldEffiency = 1.0f;
|
mCfg.inst.yieldEffiency = 1.0f;
|
||||||
mCfg.inst.gapMs = 2000;
|
mCfg.inst.gapMs = 2000;
|
||||||
|
mCfg.inst.readGrid = true;
|
||||||
|
|
||||||
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
|
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
|
||||||
mCfg.inst.iv[i].powerLevel = 0xff; // impossible high value
|
mCfg.inst.iv[i].powerLevel = 0xff; // impossible high value
|
||||||
|
@ -481,12 +483,16 @@ class settings {
|
||||||
mCfg.serial.printWholeTrace = false;
|
mCfg.serial.printWholeTrace = false;
|
||||||
}
|
}
|
||||||
if(mCfg.configVersion < 4) {
|
if(mCfg.configVersion < 4) {
|
||||||
mCfg.inst.gapMs = 2000;
|
mCfg.inst.gapMs = 500;
|
||||||
}
|
}
|
||||||
if(mCfg.configVersion < 5) {
|
if(mCfg.configVersion < 5) {
|
||||||
mCfg.inst.sendInterval = SEND_INTERVAL;
|
mCfg.inst.sendInterval = SEND_INTERVAL;
|
||||||
mCfg.serial.printWholeTrace = false;
|
mCfg.serial.printWholeTrace = false;
|
||||||
}
|
}
|
||||||
|
if(mCfg.configVersion < 6) {
|
||||||
|
mCfg.inst.gapMs = 500;
|
||||||
|
mCfg.inst.readGrid = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -718,6 +724,7 @@ class settings {
|
||||||
obj[F("rstMaxMidNight")] = (bool)mCfg.inst.rstMaxValsMidNight;
|
obj[F("rstMaxMidNight")] = (bool)mCfg.inst.rstMaxValsMidNight;
|
||||||
obj[F("yldEff")] = mCfg.inst.yieldEffiency;
|
obj[F("yldEff")] = mCfg.inst.yieldEffiency;
|
||||||
obj[F("gap")] = mCfg.inst.gapMs;
|
obj[F("gap")] = mCfg.inst.gapMs;
|
||||||
|
obj[F("rdGrid")] = (bool)mCfg.inst.readGrid;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
getVal<uint16_t>(obj, F("intvl"), &mCfg.inst.sendInterval);
|
getVal<uint16_t>(obj, F("intvl"), &mCfg.inst.sendInterval);
|
||||||
|
@ -729,6 +736,7 @@ class settings {
|
||||||
getVal<bool>(obj, F("rstMaxMidNight"), &mCfg.inst.rstMaxValsMidNight);
|
getVal<bool>(obj, F("rstMaxMidNight"), &mCfg.inst.rstMaxValsMidNight);
|
||||||
getVal<float>(obj, F("yldEff"), &mCfg.inst.yieldEffiency);
|
getVal<float>(obj, F("yldEff"), &mCfg.inst.yieldEffiency);
|
||||||
getVal<uint16_t>(obj, F("gap"), &mCfg.inst.gapMs);
|
getVal<uint16_t>(obj, F("gap"), &mCfg.inst.gapMs);
|
||||||
|
getVal<bool>(obj, F("rdGrid"), &mCfg.inst.readGrid);
|
||||||
|
|
||||||
if(mCfg.inst.yieldEffiency < 0.5)
|
if(mCfg.inst.yieldEffiency < 0.5)
|
||||||
mCfg.inst.yieldEffiency = 1.0f;
|
mCfg.inst.yieldEffiency = 1.0f;
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
#define VERSION_MAJOR 0
|
#define VERSION_MAJOR 0
|
||||||
#define VERSION_MINOR 8
|
#define VERSION_MINOR 8
|
||||||
#define VERSION_PATCH 25
|
#define VERSION_PATCH 26
|
||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
@ -254,7 +254,7 @@ class Communication : public CommQueue<> {
|
||||||
|
|
||||||
compilePayload(q);
|
compilePayload(q);
|
||||||
|
|
||||||
if(NULL != mCbPayload)
|
if((NULL != mCbPayload) && (GridOnProFilePara != q->cmd))
|
||||||
(mCbPayload)(q->cmd, q->iv);
|
(mCbPayload)(q->cmd, q->iv);
|
||||||
|
|
||||||
closeRequest(q, true);
|
closeRequest(q, true);
|
||||||
|
@ -440,9 +440,15 @@ class Communication : public CommQueue<> {
|
||||||
} else
|
} else
|
||||||
DBGPRINTLN(F(")"));
|
DBGPRINTLN(F(")"));
|
||||||
|
|
||||||
|
if(GridOnProFilePara == q->cmd) {
|
||||||
|
q->iv->addGridProfile(mPayload, len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
record_t<> *rec = q->iv->getRecordStruct(q->cmd);
|
record_t<> *rec = q->iv->getRecordStruct(q->cmd);
|
||||||
if(NULL == rec) {
|
if(NULL == rec) {
|
||||||
DPRINTLN(DBG_ERROR, F("record is NULL!"));
|
DPRINTLN(DBG_ERROR, F("record is NULL!"));
|
||||||
|
closeRequest(q, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if((rec->pyldLen != len) && (0 != rec->pyldLen)) {
|
if((rec->pyldLen != len) && (0 != rec->pyldLen)) {
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
#define F(sl) (sl)
|
#define F(sl) (sl)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define MAX_GRID_LENGTH 150
|
||||||
|
|
||||||
#include "hmDefines.h"
|
#include "hmDefines.h"
|
||||||
#include "HeuristicInv.h"
|
#include "HeuristicInv.h"
|
||||||
#include "../hms/hmsDefines.h"
|
#include "../hms/hmsDefines.h"
|
||||||
|
@ -133,7 +135,7 @@ class Inverter {
|
||||||
bool mGotLastMsg; // shows if inverter has already finished transmission cycle
|
bool mGotLastMsg; // shows if inverter has already finished transmission cycle
|
||||||
Radio *radio; // pointer to associated radio class
|
Radio *radio; // pointer to associated radio class
|
||||||
statistics_t radioStatistics; // information about transmitted, failed, ... packets
|
statistics_t radioStatistics; // information about transmitted, failed, ... packets
|
||||||
HeuristicInv heuristics;
|
HeuristicInv heuristics; // heuristic information / logic
|
||||||
uint8_t curCmtFreq; // current used CMT frequency, used to check if freq. was changed during runtime
|
uint8_t curCmtFreq; // current used CMT frequency, used to check if freq. was changed during runtime
|
||||||
bool commEnabled; // 'pause night communication' sets this field to false
|
bool commEnabled; // 'pause night communication' sets this field to false
|
||||||
|
|
||||||
|
@ -183,6 +185,8 @@ class Inverter {
|
||||||
else if(InitDataState != devControlCmd) {
|
else if(InitDataState != devControlCmd) {
|
||||||
cb(devControlCmd, false); // custom command which was received by API
|
cb(devControlCmd, false); // custom command which was received by API
|
||||||
devControlCmd = InitDataState;
|
devControlCmd = InitDataState;
|
||||||
|
} else if((0 == mGridLen) && generalConfig->readGrid) { // read grid profile
|
||||||
|
cb(GridOnProFilePara, false);
|
||||||
} else
|
} else
|
||||||
cb(RealTimeRunData_Debug, false); // get live data
|
cb(RealTimeRunData_Debug, false); // get live data
|
||||||
} else {
|
} else {
|
||||||
|
@ -721,6 +725,21 @@ class Inverter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void addGridProfile(uint8_t buf[], uint8_t length) {
|
||||||
|
mGridLen = (length > MAX_GRID_LENGTH) ? MAX_GRID_LENGTH : length;
|
||||||
|
std::copy(buf, &buf[mGridLen], mGridProfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
String getGridProfile(void) {
|
||||||
|
char buf[MAX_GRID_LENGTH * 3];
|
||||||
|
memset(buf, 0, MAX_GRID_LENGTH);
|
||||||
|
for(uint8_t i = 0; i < mGridLen; i++) {
|
||||||
|
snprintf(&buf[i*3], 4, "%02X ", mGridProfile[i]);
|
||||||
|
}
|
||||||
|
buf[mGridLen*3] = 0;
|
||||||
|
return String(buf);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
inline void addAlarm(uint16_t code, uint32_t start, uint32_t end) {
|
inline void addAlarm(uint16_t code, uint32_t start, uint32_t end) {
|
||||||
lastAlarm[alarmNxtWrPos] = alarm_t(code, start, end);
|
lastAlarm[alarmNxtWrPos] = alarm_t(code, start, end);
|
||||||
|
@ -741,6 +760,8 @@ class Inverter {
|
||||||
private:
|
private:
|
||||||
float mOffYD[6], mLastYD[6];
|
float mOffYD[6], mLastYD[6];
|
||||||
bool mDevControlRequest; // true if change needed
|
bool mDevControlRequest; // true if change needed
|
||||||
|
uint8_t mGridLen = 0;
|
||||||
|
uint8_t mGridProfile[MAX_GRID_LENGTH];
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class REC_TYP>
|
template <class REC_TYP>
|
||||||
|
|
|
@ -109,6 +109,8 @@ class RestApi {
|
||||||
getIvStatistis(root, request->url().substring(24).toInt());
|
getIvStatistis(root, request->url().substring(24).toInt());
|
||||||
else if(path.substring(0, 16) == "inverter/pwrack/")
|
else if(path.substring(0, 16) == "inverter/pwrack/")
|
||||||
getIvPowerLimitAck(root, request->url().substring(21).toInt());
|
getIvPowerLimitAck(root, request->url().substring(21).toInt());
|
||||||
|
else if(path.substring(0, 14) == "inverter/grid/")
|
||||||
|
getGridProfile(root, request->url().substring(19).toInt());
|
||||||
else
|
else
|
||||||
getNotFound(root, F("http://") + request->host() + F("/api/"));
|
getNotFound(root, F("http://") + request->host() + F("/api/"));
|
||||||
}
|
}
|
||||||
|
@ -395,6 +397,7 @@ class RestApi {
|
||||||
obj[F("rstNotAvail")] = (bool)mConfig->inst.rstValsNotAvail;
|
obj[F("rstNotAvail")] = (bool)mConfig->inst.rstValsNotAvail;
|
||||||
obj[F("rstComStop")] = (bool)mConfig->inst.rstValsCommStop;
|
obj[F("rstComStop")] = (bool)mConfig->inst.rstValsCommStop;
|
||||||
obj[F("strtWthtTm")] = (bool)mConfig->inst.startWithoutTime;
|
obj[F("strtWthtTm")] = (bool)mConfig->inst.startWithoutTime;
|
||||||
|
obj[F("rdGrid")] = (bool)mConfig->inst.readGrid;
|
||||||
obj[F("rstMaxMid")] = (bool)mConfig->inst.rstMaxValsMidNight;
|
obj[F("rstMaxMid")] = (bool)mConfig->inst.rstMaxValsMidNight;
|
||||||
obj[F("yldEff")] = mConfig->inst.yieldEffiency;
|
obj[F("yldEff")] = mConfig->inst.yieldEffiency;
|
||||||
obj[F("gap")] = mConfig->inst.gapMs;
|
obj[F("gap")] = mConfig->inst.gapMs;
|
||||||
|
@ -452,6 +455,16 @@ class RestApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void getGridProfile(JsonObject obj, uint8_t id) {
|
||||||
|
Inverter<> *iv = mSys->getInverterByPos(id);
|
||||||
|
if(NULL == iv) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj[F("name")] = String(iv->config->name);
|
||||||
|
obj[F("grid")] = iv->getGridProfile();
|
||||||
|
}
|
||||||
|
|
||||||
void getIvAlarms(JsonObject obj, uint8_t id) {
|
void getIvAlarms(JsonObject obj, uint8_t id) {
|
||||||
Inverter<> *iv = mSys->getInverterByPos(id);
|
Inverter<> *iv = mSys->getInverterByPos(id);
|
||||||
if(NULL == iv) {
|
if(NULL == iv) {
|
||||||
|
@ -484,6 +497,7 @@ class RestApi {
|
||||||
|
|
||||||
record_t<> *rec = iv->getRecordStruct(InverterDevInform_Simple);
|
record_t<> *rec = iv->getRecordStruct(InverterDevInform_Simple);
|
||||||
|
|
||||||
|
obj[F("id")] = id;
|
||||||
obj[F("name")] = String(iv->config->name);
|
obj[F("name")] = String(iv->config->name);
|
||||||
obj[F("serial")] = String(iv->config->serial.u64, HEX);
|
obj[F("serial")] = String(iv->config->serial.u64, HEX);
|
||||||
obj[F("generation")] = iv->ivGen;
|
obj[F("generation")] = iv->ivGen;
|
||||||
|
|
|
@ -172,6 +172,10 @@
|
||||||
<div class="col-8">Start without time sync (useful in AP-Only-Mode)</div>
|
<div class="col-8">Start without time sync (useful in AP-Only-Mode)</div>
|
||||||
<div class="col-4"><input type="checkbox" name="strtWthtTm"/></div>
|
<div class="col-4"><input type="checkbox" name="strtWthtTm"/></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-8">Read Grid Profile</div>
|
||||||
|
<div class="col-4"><input type="checkbox" name="rdGrid"/></div>
|
||||||
|
</div>
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col-8">Yield Efficiency (Standard 1.0)</div>
|
<div class="col-8">Yield Efficiency (Standard 1.0)</div>
|
||||||
<div class="col-4"><input type="number" name="yldEff" step="any"/></div>
|
<div class="col-4"><input type="number" name="yldEff" step="any"/></div>
|
||||||
|
@ -580,6 +584,7 @@
|
||||||
for(var i of ["Mid", "ComStop", "NotAvail", "MaxMid"])
|
for(var i of ["Mid", "ComStop", "NotAvail", "MaxMid"])
|
||||||
document.getElementsByName("invRst"+i)[0].checked = obj["rst" + i];
|
document.getElementsByName("invRst"+i)[0].checked = obj["rst" + i];
|
||||||
document.getElementsByName("strtWthtTm")[0].checked = obj["strtWthtTm"];
|
document.getElementsByName("strtWthtTm")[0].checked = obj["strtWthtTm"];
|
||||||
|
document.getElementsByName("rdGrid")[0].checked = obj["rdGrid"];
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseSys(obj) {
|
function parseSys(obj) {
|
||||||
|
|
|
@ -289,12 +289,20 @@
|
||||||
tr("Firmware Version / Build", String(obj.fw_ver) + " (build: " + String(obj.fw_date) + " " + String(obj.fw_time) + ")"),
|
tr("Firmware Version / Build", String(obj.fw_ver) + " (build: " + String(obj.fw_date) + " " + String(obj.fw_time) + ")"),
|
||||||
tr("Hardware Version / Build", (obj.hw_ver/100).toFixed(2) + " (build: " + String(obj.prod_cw) + "/" + String(obj.prod_year) + ")"),
|
tr("Hardware Version / Build", (obj.hw_ver/100).toFixed(2) + " (build: " + String(obj.prod_cw) + "/" + String(obj.prod_year) + ")"),
|
||||||
tr("Hardware Number", obj.part_num.toString(16)),
|
tr("Hardware Number", obj.part_num.toString(16)),
|
||||||
tr("Bootloader Version", (obj.boot_ver/100).toFixed(2))
|
tr("Bootloader Version", (obj.boot_ver/100).toFixed(2)),
|
||||||
|
tr("Grid Profile", ml("input", {type: "button", value: "show", class: "btn", onclick: function() {
|
||||||
|
modalClose();
|
||||||
|
getAjax("/api/inverter/grid/" + obj.id, showGridProfile);
|
||||||
|
}}, null))
|
||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
modal("Info for inverter " + obj.name, ml("div", {}, html));
|
modal("Info for inverter " + obj.name, ml("div", {}, html));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showGridProfile(obj) {
|
||||||
|
var html = ml("pre", {}, obj.grid);
|
||||||
|
modal("Grid Profile for inverter " + obj.name, ml("div", {}, html));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function parseIvRadioStats(obj) {
|
function parseIvRadioStats(obj) {
|
||||||
|
|
|
@ -501,6 +501,7 @@ class Web {
|
||||||
mConfig->inst.rstValsCommStop = (request->arg("invRstComStop") == "on");
|
mConfig->inst.rstValsCommStop = (request->arg("invRstComStop") == "on");
|
||||||
mConfig->inst.rstValsNotAvail = (request->arg("invRstNotAvail") == "on");
|
mConfig->inst.rstValsNotAvail = (request->arg("invRstNotAvail") == "on");
|
||||||
mConfig->inst.startWithoutTime = (request->arg("strtWthtTm") == "on");
|
mConfig->inst.startWithoutTime = (request->arg("strtWthtTm") == "on");
|
||||||
|
mConfig->inst.readGrid = (request->arg("rdGrid") == "on");
|
||||||
mConfig->inst.rstMaxValsMidNight = (request->arg("invRstMaxMid") == "on");
|
mConfig->inst.rstMaxValsMidNight = (request->arg("invRstMaxMid") == "on");
|
||||||
mConfig->inst.yieldEffiency = (request->arg("yldEff")).toFloat();
|
mConfig->inst.yieldEffiency = (request->arg("yldEff")).toFloat();
|
||||||
mConfig->inst.gapMs = (request->arg("invGap")).toInt();
|
mConfig->inst.gapMs = (request->arg("invGap")).toInt();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue