diff --git a/src/CHANGES.md b/src/CHANGES.md index 6c4f0a6d..bbe0bbc3 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -1,5 +1,9 @@ # Development Changes +## 0.8.6 - 2023-11-12 +* merged PR #1225 +* improved heuristics (prevent update of statitistic during testing) + ## 0.8.5 - 2023-11-12 * fixed endless loop while switching CMT frequency * removed obsolete "retries" field from settings #1224 diff --git a/src/hm/Communication.h b/src/hm/Communication.h index fb1709c5..a4eb43eb 100644 --- a/src/hm/Communication.h +++ b/src/hm/Communication.h @@ -44,6 +44,7 @@ class Communication : public CommQueue<> { return; // empty uint16_t timeout = q->iv->ivGen != IV_MI ? DEFAULT_TIMEOUT : MI_TIMEOUT; + bool testMode = false; switch(mState) { case States::RESET: @@ -56,6 +57,7 @@ class Communication : public CommQueue<> { mHeu.printStatus(q->iv); mHeu.getTxCh(q->iv); + testMode = mHeu.getTestModeEnabled(); mGotFragment = false; mFirstTry = mFirstTry ? false : ( (IV_HM == q->iv->ivGen) || (IV_MI == q->iv->ivGen) ) && (q->iv->isAvailable()); //) || (millis() < 120000));} if(NULL == q->iv->radio) @@ -79,7 +81,8 @@ class Communication : public CommQueue<> { q->iv->radio->sendControlPacket(q->iv, q->cmd, q->iv->powerLimit, false); } else q->iv->radio->prepareDevInformCmd(q->iv, q->cmd, q->ts, q->iv->alarmLastId, false); - q->iv->radioStatistics.txCnt++; + if(!testMode) + q->iv->radioStatistics.txCnt++; mWaitTimeout = millis() + timeout; setAttempt(); mState = States::WAIT; @@ -100,14 +103,17 @@ class Communication : public CommQueue<> { DBGPRINTLN(F("ms")); if(!mGotFragment && !mFirstTry) { - q->iv->radioStatistics.rxFailNoAnser++; // got nothing + if(!testMode) + q->iv->radioStatistics.rxFailNoAnser++; // got nothing mHeu.setGotNothing(q->iv); if((IV_HMS == q->iv->ivGen) || (IV_HMT == q->iv->ivGen)) { q->iv->radio->switchFrequency(q->iv, HOY_BOOT_FREQ_KHZ, (q->iv->config->frequency*FREQ_STEP_KHZ + HOY_BASE_FREQ_KHZ)); mWaitTimeout = millis() + 1000; } - } else - q->iv->radioStatistics.rxFail++; + } else { + if(!testMode) + q->iv->radioStatistics.rxFail++; + } mState = States::RESET; break; } @@ -137,7 +143,8 @@ class Communication : public CommQueue<> { ah::dumpBuf(p->packet, p->len); if(checkIvSerial(&p->packet[1], q->iv)) { - q->iv->radioStatistics.frmCnt++; + if(!testMode) + q->iv->radioStatistics.frmCnt++; if (p->packet[0] == (TX_REQ_INFO + ALL_FRAMES)) { // response from get information command parseFrame(p); @@ -149,7 +156,8 @@ class Communication : public CommQueue<> { parseMiFrame(p, q); } } else { - q->iv->radioStatistics.rxFail++; // got no complete payload + if(!testMode) + q->iv->radioStatistics.rxFail++; // got no complete payload DPRINTLN(DBG_WARN, F("Inverter serial does not match")); mWaitTimeout = millis() + timeout; } @@ -158,8 +166,9 @@ class Communication : public CommQueue<> { yield(); } if(0 == q->attempts) { + if(!testMode) + q->iv->radioStatistics.rxFail++; // got no complete payload mHeu.setGotFragment(q->iv); - q->iv->radioStatistics.rxFail++; // got no complete payload cmdDone(true); mState = States::RESET; } else @@ -205,7 +214,7 @@ class Communication : public CommQueue<> { } mHeu.setGotAll(q->iv); - compilePayload(q); + compilePayload(q, testMode); if(NULL != mCbPayload) (mCbPayload)(q->cmd, q->iv); @@ -295,7 +304,7 @@ class Communication : public CommQueue<> { q->iv->actPowerLimit = 0xffff; // unknown, readback current value } - inline void compilePayload(const queue_s *q) { + inline void compilePayload(const queue_s *q, bool testMode) { uint16_t crc = 0xffff, crcRcv = 0x0000; for(uint8_t i = 0; i < mMaxFrameId; i++) { if(i == (mMaxFrameId - 1)) { @@ -311,7 +320,8 @@ class Communication : public CommQueue<> { DBGPRINT(F("CRC Error ")); if(q->attempts == 0) { DBGPRINTLN(F("-> Fail")); - q->iv->radioStatistics.rxFail++; // got fragments but not complete response + if(!testMode) + q->iv->radioStatistics.rxFail++; // got fragments but not complete response cmdDone(); } else DBGPRINTLN(F("-> complete retransmit")); @@ -356,11 +366,13 @@ class Communication : public CommQueue<> { DPRINT(DBG_ERROR, F("plausibility check failed, expected ")); DBGPRINT(String(rec->pyldLen)); DBGPRINTLN(F(" bytes")); - q->iv->radioStatistics.rxFail++; + if(!testMode) + q->iv->radioStatistics.rxFail++; return; } - q->iv->radioStatistics.rxSuccess++; + if(!testMode) + q->iv->radioStatistics.rxSuccess++; rec->ts = q->ts; for (uint8_t i = 0; i < rec->length; i++) { diff --git a/src/hm/Heuristic.h b/src/hm/Heuristic.h index b9f6ab4f..7bf9f4a9 100644 --- a/src/hm/Heuristic.h +++ b/src/hm/Heuristic.h @@ -19,6 +19,13 @@ class Heuristic { if((IV_HMS == iv->ivGen) || (IV_HMT == iv->ivGen)) return 0; // not used for these inverter types + mCycle++; // intended to overflow from time to time + if(mTestEn) { + iv->txRfChId = mCycle % RF_MAX_CHANNEL_ID; + DPRINTLN(DBG_INFO, F("heuristic test mode")); + return id2Ch(iv->txRfChId); + } + uint8_t id = 0; int8_t bestQuality = -6; for(uint8_t i = 0; i < RF_MAX_CHANNEL_ID; i++) { @@ -37,14 +44,20 @@ class Heuristic { void setGotAll(Inverter<> *iv) { updateQuality(iv, 2); // GOOD + mTestEn = false; } void setGotFragment(Inverter<> *iv) { updateQuality(iv, 1); // OK + mTestEn = false; } void setGotNothing(Inverter<> *iv) { - updateQuality(iv, -2); // BAD + if(!mTestEn) { + updateQuality(iv, -2); // BAD + mTestEn = true; + iv->txRfChId = (iv->txRfChId + 1) % RF_MAX_CHANNEL_ID; + } } void printStatus(Inverter<> *iv) { @@ -54,7 +67,18 @@ class Heuristic { DBGPRINT(F(" ")); DBGPRINT(String(iv->txRfQuality[i])); } - DBGPRINTLN(""); + DBGPRINT(F(" | t: ")); + DBGPRINT(String(iv->radioStatistics.txCnt)); + DBGPRINT(F(", s: ")); + DBGPRINT(String(iv->radioStatistics.rxSuccess)); + DBGPRINT(F(", f: ")); + DBGPRINT(String(iv->radioStatistics.rxFail)); + DBGPRINT(F(", n: ")); + DBGPRINTLN(String(iv->radioStatistics.rxFailNoAnser)); + } + + bool getTestModeEnabled(void) { + return mTestEn; } private: @@ -66,17 +90,6 @@ class Heuristic { iv->txRfQuality[iv->txRfChId] = RF_MIN_QUALTIY; } - /*uint8_t ch2Id(uint8_t ch) { - switch(ch) { - case 3: return 0; - case 23: return 1; - case 40: return 2; - case 61: return 3; - case 75: return 4; - } - return 0; // standard - }*/ - inline uint8_t id2Ch(uint8_t id) { switch(id) { case 0: return 3; @@ -90,6 +103,8 @@ class Heuristic { private: uint8_t mChList[5] = {03, 23, 40, 61, 75}; + bool mTestEn = false; + uint8_t mCycle = 0; }; diff --git a/src/web/html/api.js b/src/web/html/api.js index 2eea8d94..e76aab2c 100644 --- a/src/web/html/api.js +++ b/src/web/html/api.js @@ -188,6 +188,19 @@ function tr(val1, val2) { ]); } +function tr2(cols) { + var t = []; + for(val of cols) { + if(typeof val == "number") + val = String(val); + if(t.length == 0) + t.push(ml("th", {}, val)); + else + t.push(ml("td", {}, val)); + } + return ml("tr", {}, t); +} + function badge(success, text, second="error") { return ml("span", {class: "badge badge-" + ((success) ? "success" : second)}, text); } diff --git a/src/web/html/visualization.html b/src/web/html/visualization.html index aa05e414..d4ac1557 100644 --- a/src/web/html/visualization.html +++ b/src/web/html/visualization.html @@ -296,15 +296,17 @@ modal("Info for inverter " + obj.name, ml("div", {}, html)); } + + function parseIvRadioStats(obj) { var html = ml("table", {class: "table"}, [ ml("tbody", {}, [ - tr("TX count", obj.tx_cnt), - tr("RX success", obj.rx_success), - tr("RX fail", obj.rx_fail), - tr("RX no answer", obj.rx_fail_answer), - tr("RX fragments", obj.frame_cnt), - tr("TX retransmits", obj.retransmits) + tr2(["TX count", obj.tx_cnt, ""]), + tr2(["RX success", obj.rx_success, String(Math.round(obj.rx_success / obj.tx_cnt * 10000) / 100) + "%"]), + tr2(["RX fail", obj.rx_fail, String(Math.round(obj.rx_fail / obj.tx_cnt * 10000) / 100) + "%"]), + tr2(["RX no answer", obj.rx_fail_answer, String(Math.round(obj.rx_fail_answer / obj.tx_cnt * 10000) / 100) + "%"]), + tr2(["RX fragments", obj.frame_cnt, ""]), + tr2(["TX retransmits", obj.retransmits, ""]) ]) ]); modal("Radio statistics for inverter " + obj.name, ml("div", {}, html));