mirror of
https://github.com/lumapu/ahoy.git
synced 2025-06-01 18:21:39 +02:00
improved payload handling (request / retransmit) #464
included alarm ID parse to serial console (in development)
This commit is contained in:
parent
43c07be148
commit
3d3e3dc8c6
13 changed files with 202 additions and 115 deletions
|
@ -2,6 +2,13 @@
|
|||
|
||||
(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
|
||||
* improved wifi handling and tickers, many thanks to @beegee3 #571
|
||||
* fixed YieldTotal correction calculation #589
|
||||
|
|
|
@ -92,7 +92,7 @@ void app::loopStandard(void) {
|
|||
|
||||
yield();
|
||||
|
||||
if (ah::checkTicker(&mRxTicker, 5)) {
|
||||
if (ah::checkTicker(&mRxTicker, 4)) {
|
||||
bool rxRdy = mSys->Radio.switchRxCh();
|
||||
|
||||
if (!mSys->BufCtrl.empty()) {
|
||||
|
|
|
@ -66,7 +66,6 @@ class app : public IApp, public ah::Scheduler {
|
|||
void handleIntr(void);
|
||||
void cbMqtt(char* topic, byte* payload, unsigned int length);
|
||||
void saveValues(void);
|
||||
void resetPayload(Inverter<>* iv);
|
||||
bool getWifiApActive(void);
|
||||
|
||||
uint32_t getUptime() {
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
//-------------------------------------
|
||||
#define VERSION_MAJOR 0
|
||||
#define VERSION_MINOR 5
|
||||
#define VERSION_PATCH 72
|
||||
#define VERSION_PATCH 73
|
||||
|
||||
//-------------------------------------
|
||||
typedef struct {
|
||||
|
|
|
@ -106,7 +106,7 @@ const byteAssign_t AlarmDataAssignment[] = {
|
|||
};
|
||||
#define HMALARMDATA_LIST_LEN (sizeof(AlarmDataAssignment) / sizeof(byteAssign_t))
|
||||
#define HMALARMDATA_PAYLOAD_LEN 0 // 0: means check is off
|
||||
|
||||
#define ALARM_LOG_ENTRY_SIZE 12
|
||||
|
||||
|
||||
//-------------------------------------
|
||||
|
|
|
@ -105,32 +105,33 @@ const calcFunc_t<T> calcFunctions[] = {
|
|||
template <class REC_TYP>
|
||||
class Inverter {
|
||||
public:
|
||||
cfgIv_t *config; // stored settings
|
||||
uint8_t id; // unique id
|
||||
uint8_t type; // integer which refers to inverter type
|
||||
uint16_t alarmMesIndex; // Last recorded Alarm Message Index
|
||||
uint16_t powerLimit[2]; // limit power output
|
||||
float actPowerLimit; // actual power limit
|
||||
uint8_t devControlCmd; // carries the requested cmd
|
||||
bool devControlRequest; // true if change needed
|
||||
serial_u radioId; // id converted to modbus
|
||||
uint8_t channels; // number of PV channels (1-4)
|
||||
record_t<REC_TYP> recordMeas; // structure for measured values
|
||||
record_t<REC_TYP> recordInfo; // structure for info values
|
||||
record_t<REC_TYP> recordConfig; // structure for system config values
|
||||
record_t<REC_TYP> recordAlarm; // structure for alarm values
|
||||
cfgIv_t *config; // stored settings
|
||||
uint8_t id; // unique id
|
||||
uint8_t type; // integer which refers to inverter type
|
||||
uint16_t alarmMesIndex; // Last recorded Alarm Message Index
|
||||
uint16_t powerLimit[2]; // limit power output
|
||||
float actPowerLimit; // actual power limit
|
||||
uint8_t devControlCmd; // carries the requested cmd
|
||||
serial_u radioId; // id converted to modbus
|
||||
uint8_t channels; // number of PV channels (1-4)
|
||||
record_t<REC_TYP> recordMeas; // structure for measured values
|
||||
record_t<REC_TYP> recordInfo; // structure for info values
|
||||
record_t<REC_TYP> recordConfig; // structure for system config values
|
||||
record_t<REC_TYP> recordAlarm; // structure for alarm values
|
||||
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() {
|
||||
powerLimit[0] = 0xffff; // 65535 W Limit -> unlimited
|
||||
powerLimit[1] = AbsolutNonPersistent; // default power limit setting
|
||||
actPowerLimit = 0xffff; // init feedback from inverter to -1
|
||||
devControlRequest = false;
|
||||
devControlCmd = InitDataState;
|
||||
initialized = false;
|
||||
lastAlarmMsg = "nothing";
|
||||
alarmMesIndex = 0;
|
||||
powerLimit[0] = 0xffff; // 65535 W Limit -> unlimited
|
||||
powerLimit[1] = AbsolutNonPersistent; // default power limit setting
|
||||
actPowerLimit = 0xffff; // init feedback from inverter to -1
|
||||
mDevControlRequest = false;
|
||||
devControlCmd = InitDataState;
|
||||
initialized = false;
|
||||
lastAlarmMsg = "nothing";
|
||||
alarmMesIndex = 0;
|
||||
isConnected = false;
|
||||
}
|
||||
|
||||
~Inverter() {
|
||||
|
@ -140,7 +141,7 @@ class Inverter {
|
|||
template <typename T>
|
||||
void enqueCommand(uint8_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() {
|
||||
|
@ -161,10 +162,10 @@ class Inverter {
|
|||
uint8_t getQueuedCmd() {
|
||||
if (_commandQueue.empty()) {
|
||||
if (getFwVersion() == 0)
|
||||
enqueCommand<InfoCommand>(InverterDevInform_All);
|
||||
enqueCommand<InfoCommand>(RealTimeRunData_Debug);
|
||||
enqueCommand<InfoCommand>(InverterDevInform_All); // firmware version
|
||||
enqueCommand<InfoCommand>(RealTimeRunData_Debug); // live data
|
||||
if (actPowerLimit == 0xffff)
|
||||
enqueCommand<InfoCommand>(SystemConfigPara);
|
||||
enqueCommand<InfoCommand>(SystemConfigPara); // power limit info
|
||||
}
|
||||
return _commandQueue.front().get()->getCmd();
|
||||
}
|
||||
|
@ -219,6 +220,20 @@ class Inverter {
|
|||
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) {
|
||||
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:addValue"));
|
||||
if(NULL != rec) {
|
||||
|
@ -256,11 +271,10 @@ class Inverter {
|
|||
if (alarmMesIndex < rec->record[pos]){
|
||||
alarmMesIndex = rec->record[pos];
|
||||
//enqueCommand<InfoCommand>(AlarmUpdate); // What is the function of AlarmUpdate?
|
||||
|
||||
DPRINTLN(DBG_INFO, "alarm ID incremented to " + String(alarmMesIndex));
|
||||
enqueCommand<InfoCommand>(AlarmData);
|
||||
}
|
||||
else {
|
||||
alarmMesIndex = rec->record[pos]; // no change
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (rec->assign == InfoAssignment) {
|
||||
|
@ -273,6 +287,7 @@ class Inverter {
|
|||
if (getPosByChFld(0, FLD_ACT_ACTIVE_PWR_LIMIT, rec) == pos){
|
||||
actPowerLimit = rec->record[pos];
|
||||
DPRINT(DBG_DEBUG, F("Inverter actual power limit: ") + String(actPowerLimit, 1));
|
||||
isConnected = true;
|
||||
}
|
||||
}
|
||||
else if (rec->assign == AlarmDataAssignment) {
|
||||
|
@ -345,10 +360,10 @@ class Inverter {
|
|||
|
||||
record_t<> *getRecordStruct(uint8_t cmd) {
|
||||
switch (cmd) {
|
||||
case RealTimeRunData_Debug: return &recordMeas;
|
||||
case InverterDevInform_All: return &recordInfo;
|
||||
case SystemConfigPara: return &recordConfig;
|
||||
case AlarmData: return &recordAlarm;
|
||||
case RealTimeRunData_Debug: return &recordMeas; // 11 = 0x0b
|
||||
case InverterDevInform_All: return &recordInfo; // 1 = 0x01
|
||||
case SystemConfigPara: return &recordConfig; // 5 = 0x05
|
||||
case AlarmData: return &recordAlarm; // 17 = 0x11
|
||||
default: break;
|
||||
}
|
||||
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!
|
||||
case 1: return String(F("Inverter start"));
|
||||
case 2: return String(F("DTU command failed"));
|
||||
|
@ -486,7 +521,6 @@ class Inverter {
|
|||
}
|
||||
|
||||
private:
|
||||
std::queue<std::shared_ptr<CommandAbstract>> _commandQueue;
|
||||
void toRadioId(void) {
|
||||
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:toRadioId"));
|
||||
radioId.u64 = 0ULL;
|
||||
|
@ -496,6 +530,9 @@ class Inverter {
|
|||
radioId.b[1] = config->serial.b[3];
|
||||
radioId.b[0] = 0x01;
|
||||
}
|
||||
|
||||
std::queue<std::shared_ptr<CommandAbstract>> _commandQueue;
|
||||
bool mDevControlRequest; // true if change needed
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -195,7 +195,7 @@ class HmRadio {
|
|||
}
|
||||
|
||||
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);
|
||||
uint8_t cnt = 0;
|
||||
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) {
|
||||
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);
|
||||
mTxBuf[10] = cmd; // cid
|
||||
mTxBuf[11] = 0x00;
|
||||
|
|
149
src/hm/payload.h
149
src/hm/payload.h
|
@ -24,6 +24,7 @@ typedef struct {
|
|||
bool lastFound;
|
||||
uint8_t retransmits;
|
||||
bool requested;
|
||||
bool gotFragment;
|
||||
} invPayload_t;
|
||||
|
||||
|
||||
|
@ -41,7 +42,9 @@ class Payload : public Handler<payloadListenerType> {
|
|||
mStat = stat;
|
||||
mMaxRetrans = maxRetransmits;
|
||||
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;
|
||||
mHighPrioIv = NULL;
|
||||
}
|
||||
|
@ -69,42 +72,44 @@ class Payload : public Handler<payloadListenerType> {
|
|||
|
||||
void ivSend(Inverter<> *iv, bool highPrio = false) {
|
||||
if(!highPrio) {
|
||||
if (!mPayload[iv->id].complete)
|
||||
process(false);
|
||||
if (mPayload[iv->id].requested) {
|
||||
if (!mPayload[iv->id].complete)
|
||||
process(false); // no retransmit
|
||||
|
||||
if (!mPayload[iv->id].complete) {
|
||||
if (0 == mPayload[iv->id].maxPackId)
|
||||
mStat->rxFailNoAnser++;
|
||||
else
|
||||
mStat->rxFail++;
|
||||
if (!mPayload[iv->id].complete) {
|
||||
if (MAX_PAYLOAD_ENTRIES == mPayload[iv->id].maxPackId)
|
||||
mStat->rxFailNoAnser++; // got nothing
|
||||
else
|
||||
mStat->rxFail++; // got fragments but not complete response
|
||||
|
||||
iv->setQueuedCmdFinished(); // command failed
|
||||
if (mSerialDebug)
|
||||
DPRINTLN(DBG_INFO, F("enqueued cmd failed/timeout"));
|
||||
if (mSerialDebug) {
|
||||
DPRINT(DBG_INFO, F("(#") + String(iv->id) + ") ");
|
||||
DPRINTLN(DBG_INFO, F("no Payload received! (retransmits: ") + String(mPayload[iv->id].retransmits) + ")");
|
||||
iv->setQueuedCmdFinished(); // command failed
|
||||
if (mSerialDebug)
|
||||
DPRINTLN(DBG_INFO, F("enqueued cmd failed/timeout"));
|
||||
if (mSerialDebug) {
|
||||
DPRINT(DBG_INFO, F("(#") + String(iv->id) + ") ");
|
||||
DPRINTLN(DBG_INFO, F("no Payload received! (retransmits: ") + String(mPayload[iv->id].retransmits) + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reset(iv);
|
||||
reset(iv->id);
|
||||
mPayload[iv->id].requested = true;
|
||||
|
||||
yield();
|
||||
if (mSerialDebug)
|
||||
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)
|
||||
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);
|
||||
mPayload[iv->id].txCmd = iv->devControlCmd;
|
||||
iv->clearCmdQueue();
|
||||
iv->enqueCommand<InfoCommand>(SystemConfigPara); // read back power limit
|
||||
//iv->clearCmdQueue();
|
||||
//iv->enqueCommand<InfoCommand>(SystemConfigPara); // read back power limit
|
||||
} else {
|
||||
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);
|
||||
mPayload[iv->id].txCmd = cmd;
|
||||
}
|
||||
|
@ -112,7 +117,11 @@ class Payload : public Handler<payloadListenerType> {
|
|||
|
||||
void add(packet_t *p, uint8_t len) {
|
||||
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];
|
||||
DPRINTLN(DBG_DEBUG, F("Response from info request received"));
|
||||
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"));
|
||||
} else {
|
||||
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);
|
||||
mPayload[iv->id].len[(*pid & 0x7F) - 1] = len - 11;
|
||||
mPayload[iv->id].gotFragment = true;
|
||||
}
|
||||
|
||||
if ((*pid & ALL_FRAMES) == ALL_FRAMES) {
|
||||
// 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);
|
||||
if (*pid > 0x81)
|
||||
mPayload[iv->id].lastFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((NULL != iv) && (p->packet[0] == (TX_REQ_DEVCONTROL + ALL_FRAMES))) { // response from dev control command
|
||||
} else if (p->packet[0] == (TX_REQ_DEVCONTROL + ALL_FRAMES)) { // response from dev control command
|
||||
DPRINTLN(DBG_DEBUG, F("Response from devcontrol request received"));
|
||||
|
||||
mPayload[iv->id].txId = p->packet[0];
|
||||
iv->devControlRequest = false;
|
||||
iv->clearDevControlRequest();
|
||||
|
||||
if ((p->packet[12] == ActivePowerContr) && (p->packet[13] == 0x00)) {
|
||||
String msg = "";
|
||||
|
@ -148,31 +157,13 @@ class Payload : public Handler<payloadListenerType> {
|
|||
else
|
||||
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]));
|
||||
iv->clearCmdQueue();
|
||||
iv->enqueCommand<InfoCommand>(SystemConfigPara); // read back power limit
|
||||
}
|
||||
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) {
|
||||
for (uint8_t id = 0; id < mSys->getNumInverters(); 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)) {
|
||||
// no processing needed if txId is not 0x95
|
||||
mPayload[iv->id].complete = true;
|
||||
continue; // skip to next inverter
|
||||
}
|
||||
|
||||
if (!mPayload[iv->id].complete) {
|
||||
|
@ -191,18 +183,21 @@ class Payload : public Handler<payloadListenerType> {
|
|||
// This is required to prevent retransmissions without answer.
|
||||
DPRINTLN(DBG_INFO, F("Prevent retransmit on Restart / CleanState_LockAndAlarm..."));
|
||||
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 {
|
||||
if (mPayload[iv->id].retransmits < mMaxRetrans) {
|
||||
mPayload[iv->id].retransmits++;
|
||||
if(false == mPayload[iv->id].lastFound) {
|
||||
DPRINTLN(DBG_WARN, F("while retrieving data: last frame missing: Request Complete Retransmit"));
|
||||
if(false == mPayload[iv->id].gotFragment) {
|
||||
DPRINTLN(DBG_WARN, F("nothing received: Request Complete Retransmit"));
|
||||
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);
|
||||
} else {
|
||||
for (uint8_t i = 0; i < (mPayload[iv->id].maxPackId - 1); i++) {
|
||||
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);
|
||||
break; // only request retransmit one frame per loop
|
||||
}
|
||||
|
@ -214,7 +209,7 @@ class Payload : public Handler<payloadListenerType> {
|
|||
}
|
||||
}
|
||||
} 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_DEBUG, F("procPyld: max: ") + String(mPayload[iv->id].maxPackId));
|
||||
record_t<> *rec = iv->getRecordStruct(mPayload[iv->id].txCmd); // choose the parser
|
||||
|
@ -250,6 +245,15 @@ class Payload : public Handler<payloadListenerType> {
|
|||
}
|
||||
iv->doCalculations();
|
||||
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 {
|
||||
DPRINTLN(DBG_ERROR, F("plausibility check failed, expected ") + String(rec->pyldLen) + F(" bytes"));
|
||||
mStat->rxFail++;
|
||||
|
@ -264,19 +268,40 @@ class Payload : public Handler<payloadListenerType> {
|
|||
}
|
||||
}
|
||||
|
||||
void reset(Inverter<> *iv) {
|
||||
DPRINTLN(DBG_INFO, "resetPayload: id: " + String(iv->id));
|
||||
memset(mPayload[iv->id].len, 0, MAX_PAYLOAD_ENTRIES);
|
||||
mPayload[iv->id].txCmd = 0;
|
||||
mPayload[iv->id].retransmits = 0;
|
||||
mPayload[iv->id].maxPackId = 0;
|
||||
mPayload[iv->id].lastFound = false;
|
||||
mPayload[iv->id].complete = false;
|
||||
mPayload[iv->id].requested = false;
|
||||
mPayload[iv->id].ts = *mTimestamp;
|
||||
private:
|
||||
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 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;
|
||||
HMSYSTEM *mSys;
|
||||
statistics_t *mStat;
|
||||
|
|
|
@ -148,10 +148,10 @@ class PubMqtt {
|
|||
if(!mClient.connected())
|
||||
return;
|
||||
|
||||
char topic[(MQTT_TOPIC_LEN << 1) + 2];
|
||||
snprintf(topic, ((MQTT_TOPIC_LEN << 1) + 2), "%s/%s", mCfgMqtt->topic, subTopic);
|
||||
if(addTopic)
|
||||
mClient.publish(topic, QOS_0, retained, payload);
|
||||
if(addTopic) {
|
||||
String topic = String(mCfgMqtt->topic) + "/" + String(subTopic);
|
||||
mClient.publish(topic.c_str(), QOS_0, retained, payload);
|
||||
}
|
||||
else
|
||||
mClient.publish(subTopic, QOS_0, retained, payload);
|
||||
mTxCnt++;
|
||||
|
|
|
@ -40,6 +40,15 @@ namespace ah {
|
|||
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) {
|
||||
char tmp[3];
|
||||
uint64_t ret = 0ULL;
|
||||
|
|
|
@ -21,6 +21,7 @@ namespace ah {
|
|||
void ip2Char(uint8_t ip[], char *str);
|
||||
double round3(double value);
|
||||
String getDateTimeStr(time_t t);
|
||||
String getTimeStr(time_t t);
|
||||
uint64_t Serial2u64(const char *val);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ class RestApi {
|
|||
RestApi() {
|
||||
mTimezoneOffset = 0;
|
||||
mFreeHeap = 0;
|
||||
nr = 0;
|
||||
}
|
||||
|
||||
void setup(IApp *app, HMSYSTEM *sys, AsyncWebServer *srv, settings_t *config) {
|
||||
|
@ -539,6 +540,7 @@ class RestApi {
|
|||
|
||||
bool setCtrl(JsonObject jsonIn, JsonObject jsonOut) {
|
||||
Inverter<> *iv = mSys->getInverterByPos(jsonIn[F("id")]);
|
||||
bool accepted = true;
|
||||
if(NULL == iv) {
|
||||
jsonOut[F("error")] = F("inverter index invalid: ") + jsonIn[F("id")].as<String>();
|
||||
return false;
|
||||
|
@ -546,10 +548,10 @@ class RestApi {
|
|||
|
||||
if(F("power") == jsonIn[F("cmd")]) {
|
||||
iv->devControlCmd = (jsonIn[F("val")] == 1) ? TurnOn : TurnOff;
|
||||
iv->devControlRequest = true;
|
||||
accepted = iv->setDevControlRequest();
|
||||
} else if(F("restart") == jsonIn[F("restart")]) {
|
||||
iv->devControlCmd = Restart;
|
||||
iv->devControlRequest = true;
|
||||
accepted = iv->setDevControlRequest();
|
||||
}
|
||||
else if(0 == strncmp("limit_", jsonIn[F("cmd")].as<const char*>(), 6)) {
|
||||
iv->powerLimit[0] = jsonIn["val"];
|
||||
|
@ -562,8 +564,9 @@ class RestApi {
|
|||
else if(F("limit_nonpersistent_absolute") == jsonIn[F("cmd")])
|
||||
iv->powerLimit[1] = AbsolutNonPersistent;
|
||||
iv->devControlCmd = ActivePowerContr;
|
||||
iv->devControlRequest = true;
|
||||
mApp->ivSendHighPrio(iv);
|
||||
accepted = iv->setDevControlRequest();
|
||||
if(accepted)
|
||||
mApp->ivSendHighPrio(iv);
|
||||
}
|
||||
else if(F("dev") == jsonIn[F("cmd")]) {
|
||||
DPRINTLN(DBG_INFO, F("dev cmd"));
|
||||
|
@ -574,6 +577,11 @@ class RestApi {
|
|||
return false;
|
||||
}
|
||||
|
||||
if(!accepted) {
|
||||
jsonOut[F("error")] = F("inverter does not accept dev control request at this moment");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -605,6 +613,7 @@ class RestApi {
|
|||
|
||||
uint32_t mTimezoneOffset;
|
||||
uint32_t mFreeHeap;
|
||||
uint16_t nr;
|
||||
};
|
||||
|
||||
#endif /*__WEB_API_H__*/
|
||||
|
|
|
@ -71,7 +71,7 @@ class Web {
|
|||
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("/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
|
||||
mWeb.on("/json", HTTP_ANY, std::bind(&Web::showJson, this, std::placeholders::_1));
|
||||
|
@ -584,7 +584,7 @@ class Web {
|
|||
request->send(response);
|
||||
}
|
||||
|
||||
void showWebApi(AsyncWebServerRequest *request) {
|
||||
/*void showWebApi(AsyncWebServerRequest *request) {
|
||||
// TODO: remove
|
||||
DPRINTLN(DBG_VERBOSE, F("web::showWebApi"));
|
||||
DPRINTLN(DBG_DEBUG, request->arg("plain"));
|
||||
|
@ -647,7 +647,7 @@ class Web {
|
|||
}
|
||||
}
|
||||
request->send(200, "text/json", "{success:true}");
|
||||
}
|
||||
}*/
|
||||
|
||||
void onSerial(AsyncWebServerRequest *request) {
|
||||
DPRINTLN(DBG_VERBOSE, F("onSerial"));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue