From 62e05dc42ee8901f9e468de34e47b0cd48129e46 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sun, 26 Jan 2025 16:36:51 +0100 Subject: [PATCH 1/4] optional attemptNr rollback if offline --- .../Model/ConnectorBase/Connector.cpp | 28 +++++++++++++++++++ src/MicroOcpp/Model/ConnectorBase/Connector.h | 4 +++ 2 files changed, 32 insertions(+) diff --git a/src/MicroOcpp/Model/ConnectorBase/Connector.cpp b/src/MicroOcpp/Model/ConnectorBase/Connector.cpp index c0911858..d41902e6 100644 --- a/src/MicroOcpp/Model/ConnectorBase/Connector.cpp +++ b/src/MicroOcpp/Model/ConnectorBase/Connector.cpp @@ -1172,6 +1172,7 @@ std::unique_ptr Connector::fetchFrontRequest() { return nullptr; } + const auto attemptNr_capture = transactionFront->getStartSync().getAttemptNr(); transactionFront->getStartSync().advanceAttemptNr(); transactionFront->getStartSync().setAttemptTime(model.getClock().now()); transactionFront->commit(); @@ -1203,6 +1204,19 @@ std::unique_ptr Connector::fetchFrontRequest() { } }); + #if MO_TX_ATTEMPT_TIMEOUT == 0 + //a timeout should not increase the attemptNr. Roll back to previous attemptNr + startTx->setOnTimeoutListener([transactionFront_capture, attemptNr_capture] () { + MO_DBG_DEBUG("StartTx timeout -> roll back attemptNr and try again"); + if (transactionFront_capture) { + transactionFront_capture->getStartSync().setAttemptNr(attemptNr_capture); + transactionFront_capture->commit(); + } + }); + #else + (void)attemptNr_capture; + #endif + return startTx; } @@ -1229,6 +1243,7 @@ std::unique_ptr Connector::fetchFrontRequest() { return nullptr; } + const auto attemptNr_capture = transactionFront->getStopSync().getAttemptNr(); transactionFront->getStopSync().advanceAttemptNr(); transactionFront->getStopSync().setAttemptTime(model.getClock().now()); transactionFront->commit(); @@ -1264,6 +1279,19 @@ std::unique_ptr Connector::fetchFrontRequest() { } }); + #if MO_TX_ATTEMPT_TIMEOUT == 0 + //a timeout should not increase the attemptNr. Roll back to previous attemptNr + stopTx->setOnTimeoutListener([transactionFront_capture, attemptNr_capture] () { + MO_DBG_DEBUG("StopTx timeout -> roll back attemptNr and try again"); + if (transactionFront_capture) { + transactionFront_capture->getStopSync().setAttemptNr(attemptNr_capture); + transactionFront_capture->commit(); + } + }); + #else + (void)attemptNr_capture; + #endif + return stopTx; } } diff --git a/src/MicroOcpp/Model/ConnectorBase/Connector.h b/src/MicroOcpp/Model/ConnectorBase/Connector.h index 541c1f4c..183c4ac9 100644 --- a/src/MicroOcpp/Model/ConnectorBase/Connector.h +++ b/src/MicroOcpp/Model/ConnectorBase/Connector.h @@ -28,6 +28,10 @@ #define MO_REPORT_NOERROR 0 #endif +#ifndef MO_TX_ATTEMPT_TIMEOUT +#define MO_TX_ATTEMPT_TIMEOUT 1 //if the timeout of a tx-related msg should increase its attempt counter +#endif + namespace MicroOcpp { class Context; From 0d72b4ce1bc6e4e33aa02ec9fd437d702f76fe1d Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sun, 26 Jan 2025 17:00:57 +0100 Subject: [PATCH 2/4] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff0a8467..d512b27d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - Support for TransactionMessageAttempts/-RetryInterval ([#345](https://github.com/matth-x/MicroOcpp/pull/345)) - Heap profiler and custom allocator support ([#350](https://github.com/matth-x/MicroOcpp/pull/350)) - Migration of persistent storage ([#355](https://github.com/matth-x/MicroOcpp/pull/355)) +- Build flag `MO_TX_ATTEMPT_TIMEOUT` to ignore offline attempts ([#398](https://github.com/matth-x/MicroOcpp/pull/398)) ### Removed From ed70e5942cd8f2402a01b9024e6b275da51b5518 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sat, 26 Jul 2025 13:33:46 +0530 Subject: [PATCH 3/4] fix stalled tx send queue if tx file numbers have gap --- .../Model/ConnectorBase/Connector.cpp | 63 +++++++++++-------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/src/MicroOcpp/Model/ConnectorBase/Connector.cpp b/src/MicroOcpp/Model/ConnectorBase/Connector.cpp index d41902e6..9e320a59 100644 --- a/src/MicroOcpp/Model/ConnectorBase/Connector.cpp +++ b/src/MicroOcpp/Model/ConnectorBase/Connector.cpp @@ -117,8 +117,30 @@ Connector::Connector(Context& context, std::shared_ptr filesy txNrFront = txNrBegin; if (model.getTransactionStore()) { - unsigned int txNrLatest = (txNrEnd + MAX_TX_CNT - 1) % MAX_TX_CNT; //txNr of the most recent tx on flash - transaction = model.getTransactionStore()->getTransaction(connectorId, txNrLatest); //returns nullptr if txNrLatest does not exist on flash + + //find and preload front transaction + unsigned int txSize = (txNrEnd + MAX_TX_CNT - txNrFront) % MAX_TX_CNT; + for (unsigned int i = 0; i < txSize; i++) { + + transactionFront = model.getTransactionStore()->getTransaction(connectorId, txNrFront); + + if (!transactionFront || (transactionFront->isAborted() || transactionFront->isCompleted() || transactionFront->isSilent())) { + //advance front + transactionFront = nullptr; + txNrFront = (txNrFront + 1) % MAX_TX_CNT; + MO_DBG_VERBOSE("txNrBegin=%u, txNrFront=%u, txNrEnd=%u", txNrBegin, txNrFront, txNrEnd); + } else { + //front is accurate. Done here + break; + } + } + + //preload back transaction + if (txNrEnd != txNrBegin) { + //there exists at least 1 transaction. Take most recent + unsigned int txNrLatest = (txNrEnd + MAX_TX_CNT - 1) % MAX_TX_CNT; //txNr of the most recent tx on flash + transaction = model.getTransactionStore()->getTransaction(connectorId, txNrLatest); //returns nullptr if txNrLatest does not exist on flash + } } else { MO_DBG_ERR("must initialize TxStore before Connector"); } @@ -249,9 +271,11 @@ void Connector::loop() { } } - if (transaction && ((transaction->isAborted() && MO_TX_CLEAN_ABORTED) || (transaction->isSilent() && transaction->getStopSync().isRequested()))) { + if (transaction && ((transaction->isAborted() && MO_TX_CLEAN_ABORTED) || (transaction->isSilent() && transaction->getStopSync().isRequested())) && + transaction->getTxNr() != txNrFront) { //If the transaction is aborted (invalidated before started) or is silent and has stopped. Delete all artifacts from flash //This is an optimization. The memory management will attempt to remove those files again later + //Don't do this if tx is at front. Then, `getFrontRequestOpNr()` has responsibility for object lifecycle bool removed = true; if (auto mService = model.getMeteringService()) { mService->abortTxMeterData(connectorId); @@ -263,9 +287,6 @@ void Connector::loop() { } if (removed) { - if (txNrFront == txNrEnd) { - txNrFront = transaction->getTxNr(); - } txNrEnd = transaction->getTxNr(); //roll back creation of last tx entry } @@ -549,10 +570,10 @@ std::shared_ptr Connector::allocateTransaction() { std::shared_ptr tx; - //clean possible aborted tx + //clean possible aborted tx. Go from one after front to end (all unsent messages, but skip front) unsigned int txr = txNrEnd; - unsigned int txSize = (txNrEnd + MAX_TX_CNT - txNrBegin) % MAX_TX_CNT; - for (unsigned int i = 0; i < txSize; i++) { + unsigned int txSize = (txNrEnd + MAX_TX_CNT - txNrFront) % MAX_TX_CNT; + for (unsigned int i = 0; i + 1 < txSize; i++) { txr = (txr + MAX_TX_CNT - 1) % MAX_TX_CNT; //decrement by 1 auto tx = model.getTransactionStore()->getTransaction(connectorId, txr); @@ -567,9 +588,6 @@ std::shared_ptr Connector::allocateTransaction() { removed &= model.getTransactionStore()->remove(connectorId, txr); } if (removed) { - if (txNrFront == txNrEnd) { - txNrFront = txr; - } txNrEnd = txr; MO_DBG_WARN("deleted dangling silent or aborted tx for new transaction"); } else { @@ -593,7 +611,7 @@ std::shared_ptr Connector::allocateTransaction() { //could not create transaction - now, try to replace tx history entry unsigned int txl = txNrBegin; - txSize = (txNrEnd + MAX_TX_CNT - txNrBegin) % MAX_TX_CNT; + txSize = (txNrFront + MAX_TX_CNT - txNrBegin) % MAX_TX_CNT; //range from oldest history tx to one tx before front for (unsigned int i = 0; i < txSize; i++) { @@ -617,9 +635,6 @@ std::shared_ptr Connector::allocateTransaction() { } if (removed) { txNrBegin = (txl + 1) % MAX_TX_CNT; - if (txNrFront == txl) { - txNrFront = txNrBegin; - } MO_DBG_DEBUG("deleted tx history entry for new transaction"); MO_DBG_VERBOSE("txNrBegin=%u, txNrFront=%u, txNrEnd=%u", txNrBegin, txNrFront, txNrEnd); @@ -1085,13 +1100,6 @@ unsigned int Connector::getFrontRequestOpNr() { unsigned int txSize = (txNrEnd + MAX_TX_CNT - txNrFront) % MAX_TX_CNT; - if (transactionFront && txSize == 0) { - //catch edge case where txBack has been rolled back and txFront was equal to txBack - MO_DBG_DEBUG("collect front transaction %u-%u after tx rollback", connectorId, transactionFront->getTxNr()); - MO_DBG_VERBOSE("txNrBegin=%u, txNrFront=%u, txNrEnd=%u", txNrBegin, txNrFront, txNrEnd); - transactionFront = nullptr; - } - for (unsigned int i = 0; i < txSize; i++) { if (!transactionFront) { @@ -1105,9 +1113,14 @@ unsigned int Connector::getFrontRequestOpNr() { #endif } - if (transactionFront && (transactionFront->isAborted() || transactionFront->isCompleted() || transactionFront->isSilent())) { + if (transactionFront && transactionFront == transaction) { + // Don't advance front tx beyond back tx + break; + } + + if (!transactionFront || (transactionFront->isAborted() || transactionFront->isCompleted() || transactionFront->isSilent())) { //advance front - MO_DBG_DEBUG("collect front transaction %u-%u", connectorId, transactionFront->getTxNr()); + MO_DBG_DEBUG("collect front transaction %u-%u", connectorId, txNrFront); transactionFront = nullptr; txNrFront = (txNrFront + 1) % MAX_TX_CNT; MO_DBG_VERBOSE("txNrBegin=%u, txNrFront=%u, txNrEnd=%u", txNrBegin, txNrFront, txNrEnd); From 48679d675b39d6e338eb93bcfed8d6ffda067062 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 31 Jul 2025 12:10:56 +0200 Subject: [PATCH 4/4] remove timeout disable for StatusNotification --- src/MicroOcpp/Model/ConnectorBase/Connector.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/MicroOcpp/Model/ConnectorBase/Connector.cpp b/src/MicroOcpp/Model/ConnectorBase/Connector.cpp index 9e320a59..f44c8da3 100644 --- a/src/MicroOcpp/Model/ConnectorBase/Connector.cpp +++ b/src/MicroOcpp/Model/ConnectorBase/Connector.cpp @@ -538,7 +538,6 @@ void Connector::loop() { makeRequest( new Ocpp16::StatusNotification(connectorId, reportedStatus, reportedTimestamp, errorData)); - statusNotification->setTimeout(0); context.initiateRequest(std::move(statusNotification)); return; }