mirror of
https://github.com/lumapu/ahoy.git
synced 2025-05-10 15:36:38 +02:00
0.8.43
* fix display of sunrise in `/system` #1308 * fix overflow of `getLossRate` calculation #1318 * improved MqTT by marking sent data and improved `last_success` resends #1319 * added timestamp for `max ac power` as tooltip #1324 #1123 #1199 * repaired Power-limit acknowledge #1322
This commit is contained in:
parent
0d10d19b30
commit
31ecb9620f
13 changed files with 97 additions and 87 deletions
|
@ -1,5 +1,12 @@
|
||||||
# Development Changes
|
# Development Changes
|
||||||
|
|
||||||
|
## 0.8.43 - 2024-01-04
|
||||||
|
* fix display of sunrise in `/system` #1308
|
||||||
|
* fix overflow of `getLossRate` calculation #1318
|
||||||
|
* improved MqTT by marking sent data and improved `last_success` resends #1319
|
||||||
|
* added timestamp for `max ac power` as tooltip #1324 #1123 #1199
|
||||||
|
* repaired Power-limit acknowledge #1322
|
||||||
|
|
||||||
## 0.8.42 - 2024-01-02
|
## 0.8.42 - 2024-01-02
|
||||||
* add LED to display whether it's night time or not. Can be reused as output to control battery system #1308
|
* add LED to display whether it's night time or not. Can be reused as output to control battery system #1308
|
||||||
* merge PR: beautifiying typography, added spaces between value and unit for `/visualization` #1314
|
* merge PR: beautifiying typography, added spaces between value and unit for `/visualization` #1314
|
||||||
|
|
|
@ -52,6 +52,7 @@ void app::setup() {
|
||||||
|
|
||||||
mCommunication.setup(&mTimestamp, &mConfig->serial.debug, &mConfig->serial.privacyLog, &mConfig->serial.printWholeTrace, &mConfig->inst.gapMs);
|
mCommunication.setup(&mTimestamp, &mConfig->serial.debug, &mConfig->serial.privacyLog, &mConfig->serial.printWholeTrace, &mConfig->inst.gapMs);
|
||||||
mCommunication.addPayloadListener(std::bind(&app::payloadEventListener, this, std::placeholders::_1, std::placeholders::_2));
|
mCommunication.addPayloadListener(std::bind(&app::payloadEventListener, this, std::placeholders::_1, std::placeholders::_2));
|
||||||
|
mCommunication.addPowerLimitAckListener([this] (Inverter<> *iv) { mMqtt.setPowerLimitAck(iv); });
|
||||||
mSys.setup(&mTimestamp, &mConfig->inst);
|
mSys.setup(&mTimestamp, &mConfig->inst);
|
||||||
for (uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
|
for (uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
|
||||||
initInverter(i);
|
initInverter(i);
|
||||||
|
|
|
@ -180,10 +180,6 @@ class app : public IApp, public ah::Scheduler {
|
||||||
once(std::bind(&PubMqttType::sendDiscoveryConfig, &mMqtt), 1, "disCf");
|
once(std::bind(&PubMqttType::sendDiscoveryConfig, &mMqtt), 1, "disCf");
|
||||||
}
|
}
|
||||||
|
|
||||||
void setMqttPowerLimitAck(Inverter<> *iv) {
|
|
||||||
mMqtt.setPowerLimitAck(iv);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getMqttIsConnected() {
|
bool getMqttIsConnected() {
|
||||||
return mMqtt.isConnected();
|
return mMqtt.isConnected();
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,6 @@ class IApp {
|
||||||
virtual bool getRebootRequestState() = 0;
|
virtual bool getRebootRequestState() = 0;
|
||||||
virtual bool getSettingsValid() = 0;
|
virtual bool getSettingsValid() = 0;
|
||||||
virtual void setMqttDiscoveryFlag() = 0;
|
virtual void setMqttDiscoveryFlag() = 0;
|
||||||
virtual void setMqttPowerLimitAck(Inverter<> *iv) = 0;
|
|
||||||
virtual bool getMqttIsConnected() = 0;
|
virtual bool getMqttIsConnected() = 0;
|
||||||
|
|
||||||
virtual bool getNrfEnabled() = 0;
|
virtual bool getNrfEnabled() = 0;
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
#define VERSION_MAJOR 0
|
#define VERSION_MAJOR 0
|
||||||
#define VERSION_MINOR 8
|
#define VERSION_MINOR 8
|
||||||
#define VERSION_PATCH 42
|
#define VERSION_PATCH 43
|
||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#define MAX_BUFFER 250
|
#define MAX_BUFFER 250
|
||||||
|
|
||||||
typedef std::function<void(uint8_t, Inverter<> *)> payloadListenerType;
|
typedef std::function<void(uint8_t, Inverter<> *)> payloadListenerType;
|
||||||
|
typedef std::function<void(Inverter<> *)> powerLimitAckListenerType;
|
||||||
typedef std::function<void(Inverter<> *)> alarmListenerType;
|
typedef std::function<void(Inverter<> *)> alarmListenerType;
|
||||||
|
|
||||||
class Communication : public CommQueue<> {
|
class Communication : public CommQueue<> {
|
||||||
|
@ -40,6 +41,10 @@ class Communication : public CommQueue<> {
|
||||||
mCbPayload = cb;
|
mCbPayload = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void addPowerLimitAckListener(powerLimitAckListenerType cb) {
|
||||||
|
mCbPwrAck = cb;
|
||||||
|
}
|
||||||
|
|
||||||
void addAlarmListener(alarmListenerType cb) {
|
void addAlarmListener(alarmListenerType cb) {
|
||||||
mCbAlarm = cb;
|
mCbAlarm = cb;
|
||||||
}
|
}
|
||||||
|
@ -401,6 +406,7 @@ class Communication : public CommQueue<> {
|
||||||
DBGPRINT(F(" with PowerLimitControl "));
|
DBGPRINT(F(" with PowerLimitControl "));
|
||||||
DBGPRINTLN(String(q->iv->powerLimit[1]));
|
DBGPRINTLN(String(q->iv->powerLimit[1]));
|
||||||
q->iv->actPowerLimit = 0xffff; // unknown, readback current value
|
q->iv->actPowerLimit = 0xffff; // unknown, readback current value
|
||||||
|
(mCbPwrAck)(q->iv);
|
||||||
|
|
||||||
return accepted;
|
return accepted;
|
||||||
}
|
}
|
||||||
|
@ -921,6 +927,7 @@ class Communication : public CommQueue<> {
|
||||||
uint8_t mMaxFrameId;
|
uint8_t mMaxFrameId;
|
||||||
uint8_t mPayload[MAX_BUFFER];
|
uint8_t mPayload[MAX_BUFFER];
|
||||||
payloadListenerType mCbPayload = NULL;
|
payloadListenerType mCbPayload = NULL;
|
||||||
|
powerLimitAckListenerType mCbPwrAck = NULL;
|
||||||
alarmListenerType mCbAlarm = NULL;
|
alarmListenerType mCbAlarm = NULL;
|
||||||
Heuristic mHeu;
|
Heuristic mHeu;
|
||||||
uint32_t mLastEmptyQueueMillis = 0;
|
uint32_t mLastEmptyQueueMillis = 0;
|
||||||
|
|
|
@ -64,6 +64,20 @@ struct calcFunc_t {
|
||||||
func_t<T>* func; // function pointer
|
func_t<T>* func; // function pointer
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class MqttSentStatus : uint8_t {
|
||||||
|
NEW_DATA,
|
||||||
|
LAST_SUCCESS_SENT,
|
||||||
|
DATA_SENT
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class InverterStatus : uint8_t {
|
||||||
|
OFF,
|
||||||
|
STARTING,
|
||||||
|
PRODUCING,
|
||||||
|
WAS_PRODUCING,
|
||||||
|
WAS_ON
|
||||||
|
};
|
||||||
|
|
||||||
template<class T=float>
|
template<class T=float>
|
||||||
struct record_t {
|
struct record_t {
|
||||||
byteAssign_t* assign; // assignment of bytes in payload
|
byteAssign_t* assign; // assignment of bytes in payload
|
||||||
|
@ -71,6 +85,7 @@ struct record_t {
|
||||||
T *record; // data pointer
|
T *record; // data pointer
|
||||||
uint32_t ts; // timestamp of last received payload
|
uint32_t ts; // timestamp of last received payload
|
||||||
uint8_t pyldLen; // expected payload length for plausibility check
|
uint8_t pyldLen; // expected payload length for plausibility check
|
||||||
|
MqttSentStatus mqttSentStatus; // indicates the current MqTT sent status
|
||||||
};
|
};
|
||||||
|
|
||||||
struct alarm_t {
|
struct alarm_t {
|
||||||
|
@ -94,14 +109,6 @@ const calcFunc_t<T> calcFunctions[] = {
|
||||||
{ CALC_MPDC_CH, &calcMaxPowerDc }
|
{ CALC_MPDC_CH, &calcMaxPowerDc }
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class InverterStatus : uint8_t {
|
|
||||||
OFF,
|
|
||||||
STARTING,
|
|
||||||
PRODUCING,
|
|
||||||
WAS_PRODUCING,
|
|
||||||
WAS_ON
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class REC_TYP>
|
template <class REC_TYP>
|
||||||
class Inverter {
|
class Inverter {
|
||||||
public:
|
public:
|
||||||
|
@ -124,26 +131,28 @@ class Inverter {
|
||||||
bool isConnected; // shows if inverter was successfully identified (fw version and hardware info)
|
bool isConnected; // shows if inverter was successfully identified (fw version and hardware info)
|
||||||
InverterStatus status; // indicates the current inverter status
|
InverterStatus status; // indicates the current inverter status
|
||||||
std::array<alarm_t, 10> lastAlarm; // holds last 10 alarms
|
std::array<alarm_t, 10> lastAlarm; // holds last 10 alarms
|
||||||
uint8_t alarmNxtWrPos; // indicates the position in array (rolling buffer)
|
int8_t rssi; // RSSI
|
||||||
uint16_t alarmCnt; // counts the total number of occurred alarms
|
uint16_t alarmCnt; // counts the total number of occurred alarms
|
||||||
uint16_t alarmLastId; // lastId which was received
|
uint16_t alarmLastId; // lastId which was received
|
||||||
int8_t rssi; // RSSI
|
uint8_t mCmd; // holds the command to send
|
||||||
|
bool mGotFragment; // shows if inverter has sent at least one fragment
|
||||||
uint8_t miMultiParts; // helper info for MI multiframe msgs
|
uint8_t miMultiParts; // helper info for MI multiframe msgs
|
||||||
uint8_t outstandingFrames; // helper info to count difference between expected and received frames
|
uint8_t outstandingFrames; // helper info to count difference between expected and received frames
|
||||||
bool mGotFragment; // shows if inverter has sent at least one fragment
|
|
||||||
uint8_t curFrmCnt; // count received frames in current loop
|
uint8_t curFrmCnt; // count received frames in current loop
|
||||||
bool mGotLastMsg; // shows if inverter has already finished transmission cycle
|
bool mGotLastMsg; // shows if inverter has already finished transmission cycle
|
||||||
uint8_t mCmd; // holds the command to send
|
|
||||||
bool mIsSingleframeReq; // indicates this is a missing single frame request
|
bool mIsSingleframeReq; // indicates this is a missing single frame request
|
||||||
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; // heuristic information / logic
|
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
|
||||||
|
uint32_t tsMaxAcPower; // holds the timestamp when the MaxAC power was seen
|
||||||
|
|
||||||
static uint32_t *timestamp; // system timestamp
|
static uint32_t *timestamp; // system timestamp
|
||||||
static cfgInst_t *generalConfig; // general inverter configuration from setup
|
static cfgInst_t *generalConfig; // general inverter configuration from setup
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
Inverter() {
|
Inverter() {
|
||||||
ivGen = IV_HM;
|
ivGen = IV_HM;
|
||||||
powerLimit[0] = 0xffff; // 6553.5 W Limit -> unlimited
|
powerLimit[0] = 0xffff; // 6553.5 W Limit -> unlimited
|
||||||
|
@ -155,7 +164,6 @@ class Inverter {
|
||||||
alarmMesIndex = 0;
|
alarmMesIndex = 0;
|
||||||
isConnected = false;
|
isConnected = false;
|
||||||
status = InverterStatus::OFF;
|
status = InverterStatus::OFF;
|
||||||
alarmNxtWrPos = 0;
|
|
||||||
alarmCnt = 0;
|
alarmCnt = 0;
|
||||||
alarmLastId = 0;
|
alarmLastId = 0;
|
||||||
rssi = -127;
|
rssi = -127;
|
||||||
|
@ -165,6 +173,7 @@ class Inverter {
|
||||||
mIsSingleframeReq = false;
|
mIsSingleframeReq = false;
|
||||||
radio = NULL;
|
radio = NULL;
|
||||||
commEnabled = true;
|
commEnabled = true;
|
||||||
|
tsMaxAcPower = 0;
|
||||||
|
|
||||||
memset(&radioStatistics, 0, sizeof(statistics_t));
|
memset(&radioStatistics, 0, sizeof(statistics_t));
|
||||||
memset(heuristics.txRfQuality, -6, 5);
|
memset(heuristics.txRfQuality, -6, 5);
|
||||||
|
@ -310,11 +319,11 @@ class Inverter {
|
||||||
rec->record[pos] = (REC_TYP)(val);
|
rec->record[pos] = (REC_TYP)(val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
rec->mqttSentStatus = MqttSentStatus::NEW_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(rec == &recordMeas) {
|
if(rec == &recordMeas) {
|
||||||
DPRINTLN(DBG_VERBOSE, "add real time");
|
DPRINTLN(DBG_VERBOSE, "add real time");
|
||||||
|
|
||||||
// get last alarm message index and save it in the inverter object
|
// get last alarm message index and save it in the inverter object
|
||||||
if (getPosByChFld(0, FLD_EVT, rec) == pos) {
|
if (getPosByChFld(0, FLD_EVT, rec) == pos) {
|
||||||
if (alarmMesIndex < rec->record[pos]) {
|
if (alarmMesIndex < rec->record[pos]) {
|
||||||
|
@ -498,6 +507,7 @@ class Inverter {
|
||||||
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:initAssignment"));
|
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:initAssignment"));
|
||||||
rec->ts = 0;
|
rec->ts = 0;
|
||||||
rec->length = 0;
|
rec->length = 0;
|
||||||
|
rec->mqttSentStatus = MqttSentStatus::DATA_SENT; // nothing new to transmit
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case RealTimeRunData_Debug:
|
case RealTimeRunData_Debug:
|
||||||
if (INV_TYPE_1CH == type) {
|
if (INV_TYPE_1CH == type) {
|
||||||
|
@ -582,7 +592,7 @@ class Inverter {
|
||||||
|
|
||||||
void resetAlarms() {
|
void resetAlarms() {
|
||||||
lastAlarm.fill({0, 0, 0});
|
lastAlarm.fill({0, 0, 0});
|
||||||
alarmNxtWrPos = 0;
|
mAlarmNxtWrPos = 0;
|
||||||
alarmCnt = 0;
|
alarmCnt = 0;
|
||||||
alarmLastId = 0;
|
alarmLastId = 0;
|
||||||
|
|
||||||
|
@ -596,11 +606,19 @@ class Inverter {
|
||||||
uint16_t txCnt = (pyld[2] << 8) + pyld[3];
|
uint16_t txCnt = (pyld[2] << 8) + pyld[3];
|
||||||
|
|
||||||
if (mIvRxCnt || mIvTxCnt) { // there was successful GetLossRate in the past
|
if (mIvRxCnt || mIvTxCnt) { // there was successful GetLossRate in the past
|
||||||
radioStatistics.ivLoss = mDtuTxCnt - (rxCnt - mIvRxCnt);
|
|
||||||
radioStatistics.ivSent = mDtuTxCnt;
|
radioStatistics.ivSent = mDtuTxCnt;
|
||||||
radioStatistics.dtuLoss = txCnt - mIvTxCnt - mDtuRxCnt;
|
if (rxCnt < mIvRxCnt) // overflow
|
||||||
|
radioStatistics.ivLoss = radioStatistics.ivSent - (rxCnt + ((uint16_t)65535 - mIvRxCnt) + 1);
|
||||||
|
else
|
||||||
|
radioStatistics.ivLoss = radioStatistics.ivSent - (rxCnt - mIvRxCnt);
|
||||||
|
|
||||||
|
if (txCnt < mIvTxCnt) // overflow
|
||||||
|
radioStatistics.dtuSent = txCnt + ((uint16_t)65535 - mIvTxCnt) + 1;
|
||||||
|
else
|
||||||
radioStatistics.dtuSent = txCnt - mIvTxCnt;
|
radioStatistics.dtuSent = txCnt - mIvTxCnt;
|
||||||
|
|
||||||
|
radioStatistics.dtuLoss = radioStatistics.dtuSent - mDtuRxCnt;
|
||||||
|
|
||||||
DPRINT_IVID(DBG_INFO, id);
|
DPRINT_IVID(DBG_INFO, id);
|
||||||
DBGPRINT(F("Inv loss: "));
|
DBGPRINT(F("Inv loss: "));
|
||||||
DBGPRINT(String(radioStatistics.ivLoss));
|
DBGPRINT(String(radioStatistics.ivLoss));
|
||||||
|
@ -790,9 +808,9 @@ class Inverter {
|
||||||
|
|
||||||
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[mAlarmNxtWrPos] = alarm_t(code, start, end);
|
||||||
if(++alarmNxtWrPos >= 10) // rolling buffer
|
if(++mAlarmNxtWrPos >= 10) // rolling buffer
|
||||||
alarmNxtWrPos = 0;
|
mAlarmNxtWrPos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void toRadioId(void) {
|
void toRadioId(void) {
|
||||||
|
@ -813,6 +831,7 @@ class Inverter {
|
||||||
uint8_t mGetLossInterval; // request iv every AHOY_GET_LOSS_INTERVAL RealTimeRunData_Debug
|
uint8_t mGetLossInterval; // request iv every AHOY_GET_LOSS_INTERVAL RealTimeRunData_Debug
|
||||||
uint16_t mIvRxCnt = 0;
|
uint16_t mIvRxCnt = 0;
|
||||||
uint16_t mIvTxCnt = 0;
|
uint16_t mIvTxCnt = 0;
|
||||||
|
uint8_t mAlarmNxtWrPos = 0; // indicates the position in array (rolling buffer)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
uint16_t mDtuRxCnt = 0;
|
uint16_t mDtuRxCnt = 0;
|
||||||
|
@ -948,9 +967,11 @@ static T calcMaxPowerDc(Inverter<> *iv, uint8_t arg0) {
|
||||||
dcMaxPower = iv->getValue(i, rec);
|
dcMaxPower = iv->getValue(i, rec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(dcPower > dcMaxPower)
|
if(dcPower > dcMaxPower) {
|
||||||
|
iv->tsMaxAcPower = *iv->timestamp;
|
||||||
return dcPower;
|
return dcPower;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return dcMaxPower;
|
return dcMaxPower;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ class PubMqttIvData {
|
||||||
mState = IDLE;
|
mState = IDLE;
|
||||||
mZeroValues = false;
|
mZeroValues = false;
|
||||||
|
|
||||||
memset(mIvLastRTRpub, 0, MAX_NUM_INVERTERS * sizeof(uint32_t));
|
|
||||||
mRTRDataHasBeenSent = false;
|
mRTRDataHasBeenSent = false;
|
||||||
|
|
||||||
mTable[IDLE] = &PubMqttIvData::stateIdle;
|
mTable[IDLE] = &PubMqttIvData::stateIdle;
|
||||||
|
@ -102,7 +101,7 @@ class PubMqttIvData {
|
||||||
mPos = 0;
|
mPos = 0;
|
||||||
if(found) {
|
if(found) {
|
||||||
record_t<> *rec = mIv->getRecordStruct(mCmd);
|
record_t<> *rec = mIv->getRecordStruct(mCmd);
|
||||||
if((RealTimeRunData_Debug == mCmd) && mIv->getLastTs(rec) != 0 ) { //workaround for startup. Suspect, mCmd might cause to much messages....
|
if(MqttSentStatus::NEW_DATA == rec->mqttSentStatus) {
|
||||||
snprintf(mSubTopic, 32 + MAX_NAME_LENGTH, "%s/last_success", mIv->config->name);
|
snprintf(mSubTopic, 32 + MAX_NAME_LENGTH, "%s/last_success", mIv->config->name);
|
||||||
snprintf(mVal, 40, "%d", mIv->getLastTs(rec));
|
snprintf(mVal, 40, "%d", mIv->getLastTs(rec));
|
||||||
mPublish(mSubTopic, mVal, true, QOS_0);
|
mPublish(mSubTopic, mVal, true, QOS_0);
|
||||||
|
@ -112,13 +111,14 @@ class PubMqttIvData {
|
||||||
snprintf(mVal, 40, "%d", mIv->rssi);
|
snprintf(mVal, 40, "%d", mIv->rssi);
|
||||||
mPublish(mSubTopic, mVal, false, QOS_0);
|
mPublish(mSubTopic, mVal, false, QOS_0);
|
||||||
}
|
}
|
||||||
|
rec->mqttSentStatus = MqttSentStatus::LAST_SUCCESS_SENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
mIv->isProducing(); // recalculate status
|
mIv->isProducing(); // recalculate status
|
||||||
mState = SEND_DATA;
|
mState = SEND_DATA;
|
||||||
} else if(mSendTotals && mTotalFound)
|
} else if(mSendTotals && mTotalFound) {
|
||||||
mState = SEND_TOTALS;
|
mState = SEND_TOTALS;
|
||||||
else {
|
} else {
|
||||||
mSendList->pop();
|
mSendList->pop();
|
||||||
mZeroValues = false;
|
mZeroValues = false;
|
||||||
mState = START;
|
mState = START;
|
||||||
|
@ -132,12 +132,8 @@ class PubMqttIvData {
|
||||||
DPRINT(DBG_WARN, "unknown record to publish!");
|
DPRINT(DBG_WARN, "unknown record to publish!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint32_t lastTs = mIv->getLastTs(rec);
|
|
||||||
bool pubData = (lastTs > 0);
|
|
||||||
if (mCmd == RealTimeRunData_Debug)
|
|
||||||
pubData &= (lastTs != mIvLastRTRpub[mIv->id]);
|
|
||||||
|
|
||||||
if (pubData) {
|
if (MqttSentStatus::LAST_SUCCESS_SENT == rec->mqttSentStatus) {
|
||||||
if(mPos < rec->length) {
|
if(mPos < rec->length) {
|
||||||
bool retained = false;
|
bool retained = false;
|
||||||
if (mCmd == RealTimeRunData_Debug) {
|
if (mCmd == RealTimeRunData_Debug) {
|
||||||
|
@ -172,21 +168,16 @@ class PubMqttIvData {
|
||||||
} else
|
} else
|
||||||
mAllTotalFound = false;
|
mAllTotalFound = false;
|
||||||
}
|
}
|
||||||
} else
|
}
|
||||||
mIvLastRTRpub[mIv->id] = lastTs;
|
|
||||||
|
|
||||||
uint8_t qos = QOS_0;
|
uint8_t qos = (FLD_ACT_ACTIVE_PWR_LIMIT == rec->assign[mPos].fieldId) ? QOS_2 : QOS_0;
|
||||||
if(FLD_ACT_ACTIVE_PWR_LIMIT == rec->assign[mPos].fieldId)
|
|
||||||
qos = QOS_2;
|
|
||||||
|
|
||||||
if((mIvSend == mIv) || (NULL == mIvSend)) { // send only updated values, or all if the inverter is NULL
|
|
||||||
snprintf(mSubTopic, 32 + MAX_NAME_LENGTH, "%s/ch%d/%s", mIv->config->name, rec->assign[mPos].ch, fields[rec->assign[mPos].fieldId]);
|
snprintf(mSubTopic, 32 + MAX_NAME_LENGTH, "%s/ch%d/%s", mIv->config->name, rec->assign[mPos].ch, fields[rec->assign[mPos].fieldId]);
|
||||||
snprintf(mVal, 40, "%g", ah::round3(mIv->getValue(mPos, rec)));
|
snprintf(mVal, 40, "%g", ah::round3(mIv->getValue(mPos, rec)));
|
||||||
mPublish(mSubTopic, mVal, retained, qos);
|
mPublish(mSubTopic, mVal, retained, qos);
|
||||||
}
|
|
||||||
mPos++;
|
mPos++;
|
||||||
} else {
|
} else {
|
||||||
sendRadioStat(rec->length);
|
sendRadioStat(rec->length);
|
||||||
|
rec->mqttSentStatus = MqttSentStatus::DATA_SENT;
|
||||||
mState = FIND_NXT_IV;
|
mState = FIND_NXT_IV;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
|
@ -263,7 +254,6 @@ class PubMqttIvData {
|
||||||
|
|
||||||
Inverter<> *mIv, *mIvSend;
|
Inverter<> *mIv, *mIvSend;
|
||||||
uint8_t mPos;
|
uint8_t mPos;
|
||||||
uint32_t mIvLastRTRpub[MAX_NUM_INVERTERS];
|
|
||||||
bool mRTRDataHasBeenSent;
|
bool mRTRDataHasBeenSent;
|
||||||
|
|
||||||
char mSubTopic[32 + MAX_NAME_LENGTH + 1];
|
char mSubTopic[32 + MAX_NAME_LENGTH + 1];
|
||||||
|
|
|
@ -485,6 +485,7 @@ class RestApi {
|
||||||
obj[F("status")] = (uint8_t)iv->getStatus();
|
obj[F("status")] = (uint8_t)iv->getStatus();
|
||||||
obj[F("alarm_cnt")] = iv->alarmCnt;
|
obj[F("alarm_cnt")] = iv->alarmCnt;
|
||||||
obj[F("rssi")] = iv->rssi;
|
obj[F("rssi")] = iv->rssi;
|
||||||
|
obj[F("ts_max_ac_pwr")] = iv->tsMaxAcPower;
|
||||||
|
|
||||||
JsonArray ch = obj.createNestedArray("ch");
|
JsonArray ch = obj.createNestedArray("ch");
|
||||||
|
|
||||||
|
|
|
@ -99,17 +99,17 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if(obj.disNightComm) {
|
if(obj.disNightComm) {
|
||||||
if(((obj.ts_sunrise - obj.ts_offsSr) < obj.ts_now)
|
if(((obj.ts_sunrise + obj.ts_offsSr) < obj.ts_now)
|
||||||
&& ((obj.ts_sunset + obj.ts_offsSs) > obj.ts_now)) {
|
&& ((obj.ts_sunset + obj.ts_offsSs) > obj.ts_now)) {
|
||||||
commInfo = "Polling inverter(s), will pause at sunset " + (new Date((obj.ts_sunset + obj.ts_offsSs) * 1000).toLocaleString('de-DE'));
|
commInfo = "Polling inverter(s), will pause at sunset " + (new Date((obj.ts_sunset + obj.ts_offsSs) * 1000).toLocaleString('de-DE'));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
commInfo = "Night time, inverter polling disabled, ";
|
commInfo = "Night time, inverter polling disabled, ";
|
||||||
if(obj.ts_now > (obj.ts_sunrise - obj.ts_offsSr)) {
|
if(obj.ts_now > (obj.ts_sunrise + obj.ts_offsSr)) {
|
||||||
commInfo += "paused at " + (new Date((obj.ts_sunset + obj.ts_offsSs) * 1000).toLocaleString('de-DE'));
|
commInfo += "paused at " + (new Date((obj.ts_sunset + obj.ts_offsSs) * 1000).toLocaleString('de-DE'));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
commInfo += "will start polling at " + (new Date((obj.ts_sunrise - obj.ts_offsSr) * 1000).toLocaleString('de-DE'));
|
commInfo += "will start polling at " + (new Date((obj.ts_sunrise + obj.ts_offsSr) * 1000).toLocaleString('de-DE'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -765,7 +765,7 @@
|
||||||
ml("div", {class: "col-2"}, cbDisNightCom)
|
ml("div", {class: "col-2"}, cbDisNightCom)
|
||||||
]),
|
]),
|
||||||
ml("div", {class: "row mb-3"}, [
|
ml("div", {class: "row mb-3"}, [
|
||||||
ml("div", {class: "col-10"}, "Include inverter to sum of total (should be checked by default)"),
|
ml("div", {class: "col-10"}, "Include inverter to sum of total (should be checked by default, MqTT only)"),
|
||||||
ml("div", {class: "col-2"}, cbAddTotal)
|
ml("div", {class: "col-2"}, cbAddTotal)
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
|
|
|
@ -650,39 +650,24 @@ div.hr {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.css-tooltip{
|
.tooltip{
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.css-tooltip:hover:after{
|
.tooltip:hover:after {
|
||||||
content:attr(data-tooltip);
|
content: attr(data);
|
||||||
background:#000;
|
background: var(--nav-active);
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
transform: translate(-50%,-100%);
|
transform: translate(-50%,-100%);
|
||||||
margin:0 auto;
|
margin:0 auto;
|
||||||
color:#FFF;
|
color: var(--fg2);
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
min-width:150px;
|
|
||||||
top: -5px;
|
top: -5px;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
text-align:center;
|
text-align:center;
|
||||||
}
|
font-size: 1rem;
|
||||||
.css-tooltip:hover:before {
|
|
||||||
top:-5px;
|
|
||||||
left: 50%;
|
|
||||||
border: solid transparent;
|
|
||||||
content: " ";
|
|
||||||
height: 0;
|
|
||||||
width: 0;
|
|
||||||
position: absolute;
|
|
||||||
pointer-events: none;
|
|
||||||
border-color: rgba(0, 0, 0, 0);
|
|
||||||
border-top-color: #000;
|
|
||||||
border-width: 5px;
|
|
||||||
margin-left: -5px;
|
|
||||||
transform: translate(0,0px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#modal {
|
#modal {
|
||||||
|
|
|
@ -45,13 +45,14 @@
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function numMid(val, unit, des) {
|
function numMid(val, unit, des, opt={class: "fs-6"}) {
|
||||||
return ml("div", {class: "col-6 col-sm-4 col-md-3 mb-2"}, [
|
return ml("div", {class: "col-6 col-sm-4 col-md-3 mb-2"}, [
|
||||||
ml("div", {class: "row"},
|
ml("div", {class: "row"},
|
||||||
ml("div", {class: "col"}, [
|
ml("div", {class: "col"}, [
|
||||||
ml("span", {class: "fs-6"}, String(Math.round(val * 100) / 100)),
|
ml("span", opt, String(Math.round(val * 100) / 100)),
|
||||||
ml("span", {class: "fs-8 mx-1"}, unit)
|
ml("span", {class: "fs-8 mx-1"}, unit)
|
||||||
])),
|
])
|
||||||
|
),
|
||||||
ml("div", {class: "row"},
|
ml("div", {class: "row"},
|
||||||
ml("div", {class: "col"},
|
ml("div", {class: "col"},
|
||||||
ml("span", {class: "fs-9"}, des)
|
ml("span", {class: "fs-9"}, des)
|
||||||
|
@ -108,6 +109,8 @@
|
||||||
if(0 != obj.max_pwr)
|
if(0 != obj.max_pwr)
|
||||||
pwrLimit += ", " + Math.round(obj.max_pwr * obj.power_limit_read / 100) + " W";
|
pwrLimit += ", " + Math.round(obj.max_pwr * obj.power_limit_read / 100) + " W";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var maxAcPwr = toIsoDateStr(new Date(obj.ts_max_ac_pwr * 1000));
|
||||||
return ml("div", {class: "row mt-2"},
|
return ml("div", {class: "row mt-2"},
|
||||||
ml("div", {class: "col"}, [
|
ml("div", {class: "col"}, [
|
||||||
ml("div", {class: "p-2 " + clh},
|
ml("div", {class: "p-2 " + clh},
|
||||||
|
@ -133,7 +136,7 @@
|
||||||
]),
|
]),
|
||||||
ml("div", {class: "hr"}),
|
ml("div", {class: "hr"}),
|
||||||
ml("div", {class: "row mt-2"},[
|
ml("div", {class: "row mt-2"},[
|
||||||
numMid(obj.ch[0][11], "W", "Max AC Power"),
|
numMid(obj.ch[0][11], "W", "Max AC Power", {class: "fs-6 tooltip", data: maxAcPwr}),
|
||||||
numMid(obj.ch[0][8], "W", "DC Power"),
|
numMid(obj.ch[0][8], "W", "DC Power"),
|
||||||
numMid(obj.ch[0][0], "V", "AC Voltage"),
|
numMid(obj.ch[0][0], "V", "AC Voltage"),
|
||||||
numMid(obj.ch[0][1], "A", "AC Current"),
|
numMid(obj.ch[0][1], "A", "AC Current"),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue