Browse Source

improved payload handling (request / retransmit) #464

included alarm ID parse to serial console (in development)
pull/635/head
lumapu 2 years ago
parent
commit
3d3e3dc8c6
  1. 7
      src/CHANGES.md
  2. 2
      src/app.cpp
  3. 1
      src/app.h
  4. 2
      src/defines.h
  5. 2
      src/hm/hmDefines.h
  6. 67
      src/hm/hmInverter.h
  7. 4
      src/hm/hmRadio.h
  8. 131
      src/hm/payload.h
  9. 8
      src/publisher/pubMqtt.h
  10. 9
      src/utils/helper.cpp
  11. 1
      src/utils/helper.h
  12. 15
      src/web/RestApi.h
  13. 6
      src/web/web.h

7
src/CHANGES.md

@ -2,6 +2,13 @@
(starting from release version `0.5.66`) (starting from release version `0.5.66`)
## 0.5.73
* improved payload handling (request / retransmit) #464
* included alarm ID parse to serial console (in development)
## 0.5.72
* repaired system, scheduler was not called any more #596
## 0.5.71 ## 0.5.71
* improved wifi handling and tickers, many thanks to @beegee3 #571 * improved wifi handling and tickers, many thanks to @beegee3 #571
* fixed YieldTotal correction calculation #589 * fixed YieldTotal correction calculation #589

2
src/app.cpp

@ -92,7 +92,7 @@ void app::loopStandard(void) {
yield(); yield();
if (ah::checkTicker(&mRxTicker, 5)) { if (ah::checkTicker(&mRxTicker, 4)) {
bool rxRdy = mSys->Radio.switchRxCh(); bool rxRdy = mSys->Radio.switchRxCh();
if (!mSys->BufCtrl.empty()) { if (!mSys->BufCtrl.empty()) {

1
src/app.h

@ -66,7 +66,6 @@ class app : public IApp, public ah::Scheduler {
void handleIntr(void); void handleIntr(void);
void cbMqtt(char* topic, byte* payload, unsigned int length); void cbMqtt(char* topic, byte* payload, unsigned int length);
void saveValues(void); void saveValues(void);
void resetPayload(Inverter<>* iv);
bool getWifiApActive(void); bool getWifiApActive(void);
uint32_t getUptime() { uint32_t getUptime() {

2
src/defines.h

@ -13,7 +13,7 @@
//------------------------------------- //-------------------------------------
#define VERSION_MAJOR 0 #define VERSION_MAJOR 0
#define VERSION_MINOR 5 #define VERSION_MINOR 5
#define VERSION_PATCH 72 #define VERSION_PATCH 73
//------------------------------------- //-------------------------------------
typedef struct { typedef struct {

2
src/hm/hmDefines.h

@ -106,7 +106,7 @@ const byteAssign_t AlarmDataAssignment[] = {
}; };
#define HMALARMDATA_LIST_LEN (sizeof(AlarmDataAssignment) / sizeof(byteAssign_t)) #define HMALARMDATA_LIST_LEN (sizeof(AlarmDataAssignment) / sizeof(byteAssign_t))
#define HMALARMDATA_PAYLOAD_LEN 0 // 0: means check is off #define HMALARMDATA_PAYLOAD_LEN 0 // 0: means check is off
#define ALARM_LOG_ENTRY_SIZE 12
//------------------------------------- //-------------------------------------

67
src/hm/hmInverter.h

@ -112,7 +112,6 @@ class Inverter {
uint16_t powerLimit[2]; // limit power output uint16_t powerLimit[2]; // limit power output
float actPowerLimit; // actual power limit float actPowerLimit; // actual power limit
uint8_t devControlCmd; // carries the requested cmd uint8_t devControlCmd; // carries the requested cmd
bool devControlRequest; // true if change needed
serial_u radioId; // id converted to modbus serial_u radioId; // id converted to modbus
uint8_t channels; // number of PV channels (1-4) uint8_t channels; // number of PV channels (1-4)
record_t<REC_TYP> recordMeas; // structure for measured values record_t<REC_TYP> recordMeas; // structure for measured values
@ -121,16 +120,18 @@ class Inverter {
record_t<REC_TYP> recordAlarm; // structure for alarm values record_t<REC_TYP> recordAlarm; // structure for alarm values
String lastAlarmMsg; String lastAlarmMsg;
bool initialized; // needed to check if the inverter was correctly added (ESP32 specific - union types are never null) bool initialized; // needed to check if the inverter was correctly added (ESP32 specific - union types are never null)
bool isConnected; // shows if inverter was successfully identified (fw version and hardware info)
Inverter() { Inverter() {
powerLimit[0] = 0xffff; // 65535 W Limit -> unlimited powerLimit[0] = 0xffff; // 65535 W Limit -> unlimited
powerLimit[1] = AbsolutNonPersistent; // default power limit setting powerLimit[1] = AbsolutNonPersistent; // default power limit setting
actPowerLimit = 0xffff; // init feedback from inverter to -1 actPowerLimit = 0xffff; // init feedback from inverter to -1
devControlRequest = false; mDevControlRequest = false;
devControlCmd = InitDataState; devControlCmd = InitDataState;
initialized = false; initialized = false;
lastAlarmMsg = "nothing"; lastAlarmMsg = "nothing";
alarmMesIndex = 0; alarmMesIndex = 0;
isConnected = false;
} }
~Inverter() { ~Inverter() {
@ -140,7 +141,7 @@ class Inverter {
template <typename T> template <typename T>
void enqueCommand(uint8_t cmd) { void enqueCommand(uint8_t cmd) {
_commandQueue.push(std::make_shared<T>(cmd)); _commandQueue.push(std::make_shared<T>(cmd));
DPRINTLN(DBG_INFO, F("(#") + String(id) + F(") enqueuedCmd: ") + String(cmd)); DPRINTLN(DBG_INFO, F("(#") + String(id) + F(") enqueuedCmd: 0x") + String(cmd, HEX));
} }
void setQueuedCmdFinished() { void setQueuedCmdFinished() {
@ -161,10 +162,10 @@ class Inverter {
uint8_t getQueuedCmd() { uint8_t getQueuedCmd() {
if (_commandQueue.empty()) { if (_commandQueue.empty()) {
if (getFwVersion() == 0) if (getFwVersion() == 0)
enqueCommand<InfoCommand>(InverterDevInform_All); enqueCommand<InfoCommand>(InverterDevInform_All); // firmware version
enqueCommand<InfoCommand>(RealTimeRunData_Debug); enqueCommand<InfoCommand>(RealTimeRunData_Debug); // live data
if (actPowerLimit == 0xffff) if (actPowerLimit == 0xffff)
enqueCommand<InfoCommand>(SystemConfigPara); enqueCommand<InfoCommand>(SystemConfigPara); // power limit info
} }
return _commandQueue.front().get()->getCmd(); return _commandQueue.front().get()->getCmd();
} }
@ -219,6 +220,20 @@ class Inverter {
return 0; return 0;
} }
bool setDevControlRequest() {
if(isConnected)
mDevControlRequest = true;
return isConnected;
}
void clearDevControlRequest() {
mDevControlRequest = false;
}
inline bool getDevControlRequest() {
return mDevControlRequest;
}
void addValue(uint8_t pos, uint8_t buf[], record_t<> *rec) { void addValue(uint8_t pos, uint8_t buf[], record_t<> *rec) {
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:addValue")); DPRINTLN(DBG_VERBOSE, F("hmInverter.h:addValue"));
if(NULL != rec) { if(NULL != rec) {
@ -256,11 +271,10 @@ class Inverter {
if (alarmMesIndex < rec->record[pos]){ if (alarmMesIndex < rec->record[pos]){
alarmMesIndex = rec->record[pos]; alarmMesIndex = rec->record[pos];
//enqueCommand<InfoCommand>(AlarmUpdate); // What is the function of AlarmUpdate? //enqueCommand<InfoCommand>(AlarmUpdate); // What is the function of AlarmUpdate?
DPRINTLN(DBG_INFO, "alarm ID incremented to " + String(alarmMesIndex));
enqueCommand<InfoCommand>(AlarmData); enqueCommand<InfoCommand>(AlarmData);
} }
else {
alarmMesIndex = rec->record[pos]; // no change
}
} }
} }
else if (rec->assign == InfoAssignment) { else if (rec->assign == InfoAssignment) {
@ -273,6 +287,7 @@ class Inverter {
if (getPosByChFld(0, FLD_ACT_ACTIVE_PWR_LIMIT, rec) == pos){ if (getPosByChFld(0, FLD_ACT_ACTIVE_PWR_LIMIT, rec) == pos){
actPowerLimit = rec->record[pos]; actPowerLimit = rec->record[pos];
DPRINT(DBG_DEBUG, F("Inverter actual power limit: ") + String(actPowerLimit, 1)); DPRINT(DBG_DEBUG, F("Inverter actual power limit: ") + String(actPowerLimit, 1));
isConnected = true;
} }
} }
else if (rec->assign == AlarmDataAssignment) { else if (rec->assign == AlarmDataAssignment) {
@ -345,10 +360,10 @@ class Inverter {
record_t<> *getRecordStruct(uint8_t cmd) { record_t<> *getRecordStruct(uint8_t cmd) {
switch (cmd) { switch (cmd) {
case RealTimeRunData_Debug: return &recordMeas; case RealTimeRunData_Debug: return &recordMeas; // 11 = 0x0b
case InverterDevInform_All: return &recordInfo; case InverterDevInform_All: return &recordInfo; // 1 = 0x01
case SystemConfigPara: return &recordConfig; case SystemConfigPara: return &recordConfig; // 5 = 0x05
case AlarmData: return &recordAlarm; case AlarmData: return &recordAlarm; // 17 = 0x11
default: break; default: break;
} }
return NULL; return NULL;
@ -411,7 +426,27 @@ class Inverter {
} }
} }
String getAlarmStr(u_int16_t alarmCode) { bool parseAlarmLog(uint8_t id, uint8_t pyld[], uint8_t len) {
uint8_t startOff = 2 + id * ALARM_LOG_ENTRY_SIZE;
if((startOff + ALARM_LOG_ENTRY_SIZE) > len)
return false;
uint16_t wCode = ((uint16_t)pyld[startOff]) << 8 | pyld[startOff+1];
uint32_t startTimeOffset = 0, endTimeOffset = 0;
if (((wCode >> 13) & 0x01) == 1) // check if is AM or PM
startTimeOffset = 12 * 60 * 60;
if (((wCode >> 12) & 0x01) == 1) // check if is AM or PM
endTimeOffset = 12 * 60 * 60;
uint32_t start = (((uint16_t)pyld[startOff + 4] << 8) | ((uint16_t)pyld[startOff + 5])) + startTimeOffset;
uint32_t end = (((uint16_t)pyld[startOff + 6] << 8) | ((uint16_t)pyld[startOff + 7])) + endTimeOffset;
DPRINTLN(DBG_INFO, "Alarm #" + String(pyld[startOff+1]) + " '" + String(getAlarmStr(pyld[startOff+1])) + "' start: " + ah::getTimeStr(start) + ", end: " + ah::getTimeStr(end));
return true;
}
String getAlarmStr(uint16_t alarmCode) {
switch (alarmCode) { // breaks are intentionally missing! switch (alarmCode) { // breaks are intentionally missing!
case 1: return String(F("Inverter start")); case 1: return String(F("Inverter start"));
case 2: return String(F("DTU command failed")); case 2: return String(F("DTU command failed"));
@ -486,7 +521,6 @@ class Inverter {
} }
private: private:
std::queue<std::shared_ptr<CommandAbstract>> _commandQueue;
void toRadioId(void) { void toRadioId(void) {
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:toRadioId")); DPRINTLN(DBG_VERBOSE, F("hmInverter.h:toRadioId"));
radioId.u64 = 0ULL; radioId.u64 = 0ULL;
@ -496,6 +530,9 @@ class Inverter {
radioId.b[1] = config->serial.b[3]; radioId.b[1] = config->serial.b[3];
radioId.b[0] = 0x01; radioId.b[0] = 0x01;
} }
std::queue<std::shared_ptr<CommandAbstract>> _commandQueue;
bool mDevControlRequest; // true if change needed
}; };

4
src/hm/hmRadio.h

@ -195,7 +195,7 @@ class HmRadio {
} }
void sendControlPacket(uint64_t invId, uint8_t cmd, uint16_t *data) { void sendControlPacket(uint64_t invId, uint8_t cmd, uint16_t *data) {
DPRINTLN(DBG_INFO, F("sendControlPacket cmd: ") + String(cmd)); DPRINTLN(DBG_INFO, F("sendControlPacket cmd: 0x") + String(cmd, HEX));
sendCmdPacket(invId, TX_REQ_DEVCONTROL, SINGLE_FRAME, false); sendCmdPacket(invId, TX_REQ_DEVCONTROL, SINGLE_FRAME, false);
uint8_t cnt = 0; uint8_t cnt = 0;
mTxBuf[10 + cnt++] = cmd; // cmd -> 0 on, 1 off, 2 restart, 11 active power, 12 reactive power, 13 power factor mTxBuf[10 + cnt++] = cmd; // cmd -> 0 on, 1 off, 2 restart, 11 active power, 12 reactive power, 13 power factor
@ -219,7 +219,7 @@ class HmRadio {
} }
void sendTimePacket(uint64_t invId, uint8_t cmd, uint32_t ts, uint16_t alarmMesId) { void sendTimePacket(uint64_t invId, uint8_t cmd, uint32_t ts, uint16_t alarmMesId) {
DPRINTLN(DBG_INFO, F("sendTimePacket ") + String(cmd, HEX)); DPRINTLN(DBG_DEBUG, F("sendTimePacket 0x") + String(cmd, HEX));
sendCmdPacket(invId, TX_REQ_INFO, ALL_FRAMES, false); sendCmdPacket(invId, TX_REQ_INFO, ALL_FRAMES, false);
mTxBuf[10] = cmd; // cid mTxBuf[10] = cmd; // cid
mTxBuf[11] = 0x00; mTxBuf[11] = 0x00;

131
src/hm/payload.h

@ -24,6 +24,7 @@ typedef struct {
bool lastFound; bool lastFound;
uint8_t retransmits; uint8_t retransmits;
bool requested; bool requested;
bool gotFragment;
} invPayload_t; } invPayload_t;
@ -41,7 +42,9 @@ class Payload : public Handler<payloadListenerType> {
mStat = stat; mStat = stat;
mMaxRetrans = maxRetransmits; mMaxRetrans = maxRetransmits;
mTimestamp = timestamp; mTimestamp = timestamp;
memset(mPayload, 0, (MAX_NUM_INVERTERS * sizeof(invPayload_t))); for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
reset(i);
}
mSerialDebug = false; mSerialDebug = false;
mHighPrioIv = NULL; mHighPrioIv = NULL;
} }
@ -69,14 +72,15 @@ class Payload : public Handler<payloadListenerType> {
void ivSend(Inverter<> *iv, bool highPrio = false) { void ivSend(Inverter<> *iv, bool highPrio = false) {
if(!highPrio) { if(!highPrio) {
if (mPayload[iv->id].requested) {
if (!mPayload[iv->id].complete) if (!mPayload[iv->id].complete)
process(false); process(false); // no retransmit
if (!mPayload[iv->id].complete) { if (!mPayload[iv->id].complete) {
if (0 == mPayload[iv->id].maxPackId) if (MAX_PAYLOAD_ENTRIES == mPayload[iv->id].maxPackId)
mStat->rxFailNoAnser++; mStat->rxFailNoAnser++; // got nothing
else else
mStat->rxFail++; mStat->rxFail++; // got fragments but not complete response
iv->setQueuedCmdFinished(); // command failed iv->setQueuedCmdFinished(); // command failed
if (mSerialDebug) if (mSerialDebug)
@ -87,24 +91,25 @@ class Payload : public Handler<payloadListenerType> {
} }
} }
} }
}
reset(iv); reset(iv->id);
mPayload[iv->id].requested = true; mPayload[iv->id].requested = true;
yield(); yield();
if (mSerialDebug) if (mSerialDebug)
DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") Requesting Inv SN ") + String(iv->config->serial.u64, HEX)); DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") Requesting Inv SN ") + String(iv->config->serial.u64, HEX));
if (iv->devControlRequest) { if (iv->getDevControlRequest()) {
if (mSerialDebug) if (mSerialDebug)
DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") Devcontrol request ") + String(iv->devControlCmd) + F(" power limit ") + String(iv->powerLimit[0])); DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") Devcontrol request 0x") + String(iv->devControlCmd, HEX) + F(" power limit ") + String(iv->powerLimit[0]));
mSys->Radio.sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit); mSys->Radio.sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit);
mPayload[iv->id].txCmd = iv->devControlCmd; mPayload[iv->id].txCmd = iv->devControlCmd;
iv->clearCmdQueue(); //iv->clearCmdQueue();
iv->enqueCommand<InfoCommand>(SystemConfigPara); // read back power limit //iv->enqueCommand<InfoCommand>(SystemConfigPara); // read back power limit
} else { } else {
uint8_t cmd = iv->getQueuedCmd(); uint8_t cmd = iv->getQueuedCmd();
DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") sendTimePacket")); DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") sendTimePacket")); // + String(cmd, HEX));
mSys->Radio.sendTimePacket(iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmMesIndex); mSys->Radio.sendTimePacket(iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmMesIndex);
mPayload[iv->id].txCmd = cmd; mPayload[iv->id].txCmd = cmd;
} }
@ -112,7 +117,11 @@ class Payload : public Handler<payloadListenerType> {
void add(packet_t *p, uint8_t len) { void add(packet_t *p, uint8_t len) {
Inverter<> *iv = mSys->findInverter(&p->packet[1]); Inverter<> *iv = mSys->findInverter(&p->packet[1]);
if ((NULL != iv) && (p->packet[0] == (TX_REQ_INFO + ALL_FRAMES))) { // response from get information command
if(NULL == iv)
return;
if (p->packet[0] == (TX_REQ_INFO + ALL_FRAMES)) { // response from get information command
mPayload[iv->id].txId = p->packet[0]; mPayload[iv->id].txId = p->packet[0];
DPRINTLN(DBG_DEBUG, F("Response from info request received")); DPRINTLN(DBG_DEBUG, F("Response from info request received"));
uint8_t *pid = &p->packet[9]; uint8_t *pid = &p->packet[9];
@ -120,26 +129,26 @@ class Payload : public Handler<payloadListenerType> {
DPRINT(DBG_DEBUG, F("fragment number zero received and ignored")); DPRINT(DBG_DEBUG, F("fragment number zero received and ignored"));
} else { } else {
DPRINTLN(DBG_DEBUG, "PID: 0x" + String(*pid, HEX)); DPRINTLN(DBG_DEBUG, "PID: 0x" + String(*pid, HEX));
if ((*pid & 0x7F) < 5) { if ((*pid & 0x7F) < MAX_PAYLOAD_ENTRIES) {
memcpy(mPayload[iv->id].data[(*pid & 0x7F) - 1], &p->packet[10], len - 11); memcpy(mPayload[iv->id].data[(*pid & 0x7F) - 1], &p->packet[10], len - 11);
mPayload[iv->id].len[(*pid & 0x7F) - 1] = len - 11; mPayload[iv->id].len[(*pid & 0x7F) - 1] = len - 11;
mPayload[iv->id].gotFragment = true;
} }
if ((*pid & ALL_FRAMES) == ALL_FRAMES) { if ((*pid & ALL_FRAMES) == ALL_FRAMES) {
// Last packet // Last packet
if ((*pid & 0x7f) > mPayload[iv->id].maxPackId) { if (((*pid & 0x7f) > mPayload[iv->id].maxPackId) || (MAX_PAYLOAD_ENTRIES == mPayload[iv->id].maxPackId)) {
mPayload[iv->id].maxPackId = (*pid & 0x7f); mPayload[iv->id].maxPackId = (*pid & 0x7f);
if (*pid > 0x81) if (*pid > 0x81)
mPayload[iv->id].lastFound = true; mPayload[iv->id].lastFound = true;
} }
} }
} }
} } else if (p->packet[0] == (TX_REQ_DEVCONTROL + ALL_FRAMES)) { // response from dev control command
if ((NULL != iv) && (p->packet[0] == (TX_REQ_DEVCONTROL + ALL_FRAMES))) { // response from dev control command
DPRINTLN(DBG_DEBUG, F("Response from devcontrol request received")); DPRINTLN(DBG_DEBUG, F("Response from devcontrol request received"));
mPayload[iv->id].txId = p->packet[0]; mPayload[iv->id].txId = p->packet[0];
iv->devControlRequest = false; iv->clearDevControlRequest();
if ((p->packet[12] == ActivePowerContr) && (p->packet[13] == 0x00)) { if ((p->packet[12] == ActivePowerContr) && (p->packet[13] == 0x00)) {
String msg = ""; String msg = "";
@ -148,31 +157,13 @@ class Payload : public Handler<payloadListenerType> {
else else
msg = "NOT "; msg = "NOT ";
DPRINTLN(DBG_INFO, F("Inverter ") + String(iv->id) + F(" has ") + msg + F("accepted power limit set point ") + String(iv->powerLimit[0]) + F(" with PowerLimitControl ") + String(iv->powerLimit[1])); DPRINTLN(DBG_INFO, F("Inverter ") + String(iv->id) + F(" has ") + msg + F("accepted power limit set point ") + String(iv->powerLimit[0]) + F(" with PowerLimitControl ") + String(iv->powerLimit[1]));
iv->clearCmdQueue();
iv->enqueCommand<InfoCommand>(SystemConfigPara); // read back power limit
} }
iv->devControlCmd = Init; iv->devControlCmd = Init;
} }
} }
bool build(uint8_t id) {
DPRINTLN(DBG_VERBOSE, F("build"));
uint16_t crc = 0xffff, crcRcv = 0x0000;
if (mPayload[id].maxPackId > MAX_PAYLOAD_ENTRIES)
mPayload[id].maxPackId = MAX_PAYLOAD_ENTRIES;
for (uint8_t i = 0; i < mPayload[id].maxPackId; i++) {
if (mPayload[id].len[i] > 0) {
if (i == (mPayload[id].maxPackId - 1)) {
crc = ah::crc16(mPayload[id].data[i], mPayload[id].len[i] - 2, crc);
crcRcv = (mPayload[id].data[i][mPayload[id].len[i] - 2] << 8) | (mPayload[id].data[i][mPayload[id].len[i] - 1]);
} else
crc = ah::crc16(mPayload[id].data[i], mPayload[id].len[i], crc);
}
yield();
}
return (crc == crcRcv) ? true : false;
}
void process(bool retransmit) { void process(bool retransmit) {
for (uint8_t id = 0; id < mSys->getNumInverters(); id++) { for (uint8_t id = 0; id < mSys->getNumInverters(); id++) {
Inverter<> *iv = mSys->getInverterByPos(id); Inverter<> *iv = mSys->getInverterByPos(id);
@ -182,6 +173,7 @@ class Payload : public Handler<payloadListenerType> {
if ((mPayload[iv->id].txId != (TX_REQ_INFO + ALL_FRAMES)) && (0 != mPayload[iv->id].txId)) { if ((mPayload[iv->id].txId != (TX_REQ_INFO + ALL_FRAMES)) && (0 != mPayload[iv->id].txId)) {
// no processing needed if txId is not 0x95 // no processing needed if txId is not 0x95
mPayload[iv->id].complete = true; mPayload[iv->id].complete = true;
continue; // skip to next inverter
} }
if (!mPayload[iv->id].complete) { if (!mPayload[iv->id].complete) {
@ -191,18 +183,21 @@ class Payload : public Handler<payloadListenerType> {
// This is required to prevent retransmissions without answer. // This is required to prevent retransmissions without answer.
DPRINTLN(DBG_INFO, F("Prevent retransmit on Restart / CleanState_LockAndAlarm...")); DPRINTLN(DBG_INFO, F("Prevent retransmit on Restart / CleanState_LockAndAlarm..."));
mPayload[iv->id].retransmits = mMaxRetrans; mPayload[iv->id].retransmits = mMaxRetrans;
} else if(iv->devControlCmd == ActivePowerContr) {
DPRINTLN(DBG_INFO, F("retransmit power limit"));
mSys->Radio.sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit);
} else { } else {
if (mPayload[iv->id].retransmits < mMaxRetrans) { if (mPayload[iv->id].retransmits < mMaxRetrans) {
mPayload[iv->id].retransmits++; mPayload[iv->id].retransmits++;
if(false == mPayload[iv->id].lastFound) { if(false == mPayload[iv->id].gotFragment) {
DPRINTLN(DBG_WARN, F("while retrieving data: last frame missing: Request Complete Retransmit")); DPRINTLN(DBG_WARN, F("nothing received: Request Complete Retransmit"));
mPayload[iv->id].txCmd = iv->getQueuedCmd(); mPayload[iv->id].txCmd = iv->getQueuedCmd();
DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") sendTimePacket")); DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") sendTimePacket 0x") + String(mPayload[iv->id].txCmd, HEX));
mSys->Radio.sendTimePacket(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmMesIndex); mSys->Radio.sendTimePacket(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmMesIndex);
} else { } else {
for (uint8_t i = 0; i < (mPayload[iv->id].maxPackId - 1); i++) { for (uint8_t i = 0; i < (mPayload[iv->id].maxPackId - 1); i++) {
if (mPayload[iv->id].len[i] == 0) { if (mPayload[iv->id].len[i] == 0) {
DPRINTLN(DBG_WARN, F("while retrieving data: Frame ") + String(i + 1) + F(" missing: Request Retransmit")); DPRINTLN(DBG_WARN, F("Frame ") + String(i + 1) + F(" missing: Request Retransmit"));
mSys->Radio.sendCmdPacket(iv->radioId.u64, TX_REQ_INFO, (SINGLE_FRAME + i), true); mSys->Radio.sendCmdPacket(iv->radioId.u64, TX_REQ_INFO, (SINGLE_FRAME + i), true);
break; // only request retransmit one frame per loop break; // only request retransmit one frame per loop
} }
@ -214,7 +209,7 @@ class Payload : public Handler<payloadListenerType> {
} }
} }
} else { // payload complete } else { // payload complete
DPRINTLN(DBG_INFO, F("procPyld: cmd: ") + String(mPayload[iv->id].txCmd)); DPRINTLN(DBG_INFO, F("procPyld: cmd: 0x") + String(mPayload[iv->id].txCmd, HEX));
DPRINTLN(DBG_INFO, F("procPyld: txid: 0x") + String(mPayload[iv->id].txId, HEX)); DPRINTLN(DBG_INFO, F("procPyld: txid: 0x") + String(mPayload[iv->id].txId, HEX));
DPRINTLN(DBG_DEBUG, F("procPyld: max: ") + String(mPayload[iv->id].maxPackId)); DPRINTLN(DBG_DEBUG, F("procPyld: max: ") + String(mPayload[iv->id].maxPackId));
record_t<> *rec = iv->getRecordStruct(mPayload[iv->id].txCmd); // choose the parser record_t<> *rec = iv->getRecordStruct(mPayload[iv->id].txCmd); // choose the parser
@ -250,6 +245,15 @@ class Payload : public Handler<payloadListenerType> {
} }
iv->doCalculations(); iv->doCalculations();
notify(mPayload[iv->id].txCmd); notify(mPayload[iv->id].txCmd);
if(AlarmData == mPayload[iv->id].txCmd) {
uint8_t i = 0;
while(1) {
if(!iv->parseAlarmLog(i++, payload, payloadLen))
break;
yield();
}
}
} else { } else {
DPRINTLN(DBG_ERROR, F("plausibility check failed, expected ") + String(rec->pyldLen) + F(" bytes")); DPRINTLN(DBG_ERROR, F("plausibility check failed, expected ") + String(rec->pyldLen) + F(" bytes"));
mStat->rxFail++; mStat->rxFail++;
@ -264,19 +268,40 @@ class Payload : public Handler<payloadListenerType> {
} }
} }
void reset(Inverter<> *iv) { private:
DPRINTLN(DBG_INFO, "resetPayload: id: " + String(iv->id)); bool build(uint8_t id) {
memset(mPayload[iv->id].len, 0, MAX_PAYLOAD_ENTRIES); DPRINTLN(DBG_VERBOSE, F("build"));
mPayload[iv->id].txCmd = 0; uint16_t crc = 0xffff, crcRcv = 0x0000;
mPayload[iv->id].retransmits = 0; if (mPayload[id].maxPackId > MAX_PAYLOAD_ENTRIES)
mPayload[iv->id].maxPackId = 0; mPayload[id].maxPackId = MAX_PAYLOAD_ENTRIES;
mPayload[iv->id].lastFound = false;
mPayload[iv->id].complete = false; for (uint8_t i = 0; i < mPayload[id].maxPackId; i++) {
mPayload[iv->id].requested = false; if (mPayload[id].len[i] > 0) {
mPayload[iv->id].ts = *mTimestamp; if (i == (mPayload[id].maxPackId - 1)) {
crc = ah::crc16(mPayload[id].data[i], mPayload[id].len[i] - 2, crc);
crcRcv = (mPayload[id].data[i][mPayload[id].len[i] - 2] << 8) | (mPayload[id].data[i][mPayload[id].len[i] - 1]);
} else
crc = ah::crc16(mPayload[id].data[i], mPayload[id].len[i], crc);
}
yield();
}
return (crc == crcRcv) ? true : false;
}
void reset(uint8_t id) {
DPRINTLN(DBG_INFO, "resetPayload: id: " + String(id));
memset(mPayload[id].len, 0, MAX_PAYLOAD_ENTRIES);
mPayload[id].txCmd = 0;
mPayload[id].gotFragment = false;
mPayload[id].retransmits = 0;
mPayload[id].maxPackId = MAX_PAYLOAD_ENTRIES;
mPayload[id].lastFound = false;
mPayload[id].complete = false;
mPayload[id].requested = false;
mPayload[id].ts = *mTimestamp;
} }
private:
IApp *mApp; IApp *mApp;
HMSYSTEM *mSys; HMSYSTEM *mSys;
statistics_t *mStat; statistics_t *mStat;

8
src/publisher/pubMqtt.h

@ -148,10 +148,10 @@ class PubMqtt {
if(!mClient.connected()) if(!mClient.connected())
return; return;
char topic[(MQTT_TOPIC_LEN << 1) + 2]; if(addTopic) {
snprintf(topic, ((MQTT_TOPIC_LEN << 1) + 2), "%s/%s", mCfgMqtt->topic, subTopic); String topic = String(mCfgMqtt->topic) + "/" + String(subTopic);
if(addTopic) mClient.publish(topic.c_str(), QOS_0, retained, payload);
mClient.publish(topic, QOS_0, retained, payload); }
else else
mClient.publish(subTopic, QOS_0, retained, payload); mClient.publish(subTopic, QOS_0, retained, payload);
mTxCnt++; mTxCnt++;

9
src/utils/helper.cpp

@ -40,6 +40,15 @@ namespace ah {
return String(str); return String(str);
} }
String getTimeStr(time_t t) {
char str[9];
if(0 == t)
sprintf(str, "n/a");
else
sprintf(str, "%02d:%02d:%02d", hour(t), minute(t), second(t));
return String(str);
}
uint64_t Serial2u64(const char *val) { uint64_t Serial2u64(const char *val) {
char tmp[3]; char tmp[3];
uint64_t ret = 0ULL; uint64_t ret = 0ULL;

1
src/utils/helper.h

@ -21,6 +21,7 @@ namespace ah {
void ip2Char(uint8_t ip[], char *str); void ip2Char(uint8_t ip[], char *str);
double round3(double value); double round3(double value);
String getDateTimeStr(time_t t); String getDateTimeStr(time_t t);
String getTimeStr(time_t t);
uint64_t Serial2u64(const char *val); uint64_t Serial2u64(const char *val);
} }

15
src/web/RestApi.h

@ -20,6 +20,7 @@ class RestApi {
RestApi() { RestApi() {
mTimezoneOffset = 0; mTimezoneOffset = 0;
mFreeHeap = 0; mFreeHeap = 0;
nr = 0;
} }
void setup(IApp *app, HMSYSTEM *sys, AsyncWebServer *srv, settings_t *config) { void setup(IApp *app, HMSYSTEM *sys, AsyncWebServer *srv, settings_t *config) {
@ -539,6 +540,7 @@ class RestApi {
bool setCtrl(JsonObject jsonIn, JsonObject jsonOut) { bool setCtrl(JsonObject jsonIn, JsonObject jsonOut) {
Inverter<> *iv = mSys->getInverterByPos(jsonIn[F("id")]); Inverter<> *iv = mSys->getInverterByPos(jsonIn[F("id")]);
bool accepted = true;
if(NULL == iv) { if(NULL == iv) {
jsonOut[F("error")] = F("inverter index invalid: ") + jsonIn[F("id")].as<String>(); jsonOut[F("error")] = F("inverter index invalid: ") + jsonIn[F("id")].as<String>();
return false; return false;
@ -546,10 +548,10 @@ class RestApi {
if(F("power") == jsonIn[F("cmd")]) { if(F("power") == jsonIn[F("cmd")]) {
iv->devControlCmd = (jsonIn[F("val")] == 1) ? TurnOn : TurnOff; iv->devControlCmd = (jsonIn[F("val")] == 1) ? TurnOn : TurnOff;
iv->devControlRequest = true; accepted = iv->setDevControlRequest();
} else if(F("restart") == jsonIn[F("restart")]) { } else if(F("restart") == jsonIn[F("restart")]) {
iv->devControlCmd = Restart; iv->devControlCmd = Restart;
iv->devControlRequest = true; accepted = iv->setDevControlRequest();
} }
else if(0 == strncmp("limit_", jsonIn[F("cmd")].as<const char*>(), 6)) { else if(0 == strncmp("limit_", jsonIn[F("cmd")].as<const char*>(), 6)) {
iv->powerLimit[0] = jsonIn["val"]; iv->powerLimit[0] = jsonIn["val"];
@ -562,7 +564,8 @@ class RestApi {
else if(F("limit_nonpersistent_absolute") == jsonIn[F("cmd")]) else if(F("limit_nonpersistent_absolute") == jsonIn[F("cmd")])
iv->powerLimit[1] = AbsolutNonPersistent; iv->powerLimit[1] = AbsolutNonPersistent;
iv->devControlCmd = ActivePowerContr; iv->devControlCmd = ActivePowerContr;
iv->devControlRequest = true; accepted = iv->setDevControlRequest();
if(accepted)
mApp->ivSendHighPrio(iv); mApp->ivSendHighPrio(iv);
} }
else if(F("dev") == jsonIn[F("cmd")]) { else if(F("dev") == jsonIn[F("cmd")]) {
@ -574,6 +577,11 @@ class RestApi {
return false; return false;
} }
if(!accepted) {
jsonOut[F("error")] = F("inverter does not accept dev control request at this moment");
return false;
}
return true; return true;
} }
@ -605,6 +613,7 @@ class RestApi {
uint32_t mTimezoneOffset; uint32_t mTimezoneOffset;
uint32_t mFreeHeap; uint32_t mFreeHeap;
uint16_t nr;
}; };
#endif /*__WEB_API_H__*/ #endif /*__WEB_API_H__*/

6
src/web/web.h

@ -71,7 +71,7 @@ class Web {
mWeb.on("/save", HTTP_ANY, std::bind(&Web::showSave, this, std::placeholders::_1)); mWeb.on("/save", HTTP_ANY, std::bind(&Web::showSave, this, std::placeholders::_1));
mWeb.on("/live", HTTP_ANY, std::bind(&Web::onLive, this, std::placeholders::_1)); mWeb.on("/live", HTTP_ANY, std::bind(&Web::onLive, this, std::placeholders::_1));
mWeb.on("/api1", HTTP_POST, std::bind(&Web::showWebApi, this, std::placeholders::_1)); //mWeb.on("/api1", HTTP_POST, std::bind(&Web::showWebApi, this, std::placeholders::_1));
#ifdef ENABLE_JSON_EP #ifdef ENABLE_JSON_EP
mWeb.on("/json", HTTP_ANY, std::bind(&Web::showJson, this, std::placeholders::_1)); mWeb.on("/json", HTTP_ANY, std::bind(&Web::showJson, this, std::placeholders::_1));
@ -584,7 +584,7 @@ class Web {
request->send(response); request->send(response);
} }
void showWebApi(AsyncWebServerRequest *request) { /*void showWebApi(AsyncWebServerRequest *request) {
// TODO: remove // TODO: remove
DPRINTLN(DBG_VERBOSE, F("web::showWebApi")); DPRINTLN(DBG_VERBOSE, F("web::showWebApi"));
DPRINTLN(DBG_DEBUG, request->arg("plain")); DPRINTLN(DBG_DEBUG, request->arg("plain"));
@ -647,7 +647,7 @@ class Web {
} }
} }
request->send(200, "text/json", "{success:true}"); request->send(200, "text/json", "{success:true}");
} }*/
void onSerial(AsyncWebServerRequest *request) { void onSerial(AsyncWebServerRequest *request) {
DPRINTLN(DBG_VERBOSE, F("onSerial")); DPRINTLN(DBG_VERBOSE, F("onSerial"));

Loading…
Cancel
Save