diff --git a/src/fsfw/tmtcservices/CommandingServiceBase.cpp b/src/fsfw/tmtcservices/CommandingServiceBase.cpp index c890ecfb6..0815c838b 100644 --- a/src/fsfw/tmtcservices/CommandingServiceBase.cpp +++ b/src/fsfw/tmtcservices/CommandingServiceBase.cpp @@ -422,7 +422,7 @@ void CommandingServiceBase::setCustomTmStore(StorageManagerIF& store) { tmStoreHelper.setTmStore(store); } ReturnValue_t CommandingServiceBase::setUpTcReader(store_address_t storeId) { - return tc::prepareTcReader(tcStore, storeId, tcReader); + return tc::prepareTcReader(*tcStore, storeId, tcReader); } void CommandingServiceBase::prepareVerificationFailureWithFullInfo(uint8_t reportId, diff --git a/src/fsfw/tmtcservices/PusServiceBase.cpp b/src/fsfw/tmtcservices/PusServiceBase.cpp index 8d250a0f7..c283d655f 100644 --- a/src/fsfw/tmtcservices/PusServiceBase.cpp +++ b/src/fsfw/tmtcservices/PusServiceBase.cpp @@ -10,6 +10,7 @@ #include "fsfw/tmtcservices/tcHelpers.h" object_id_t PusServiceBase::packetDestination = 0; +object_id_t PusServiceBase::pusDistributor = 0; PusServiceBase::PusServiceBase(PsbParams params) : SystemObject(params.objectId), psbParams(params) {} @@ -54,9 +55,11 @@ void PusServiceBase::handleRequestQueue() { #endif break; } - result = tc::prepareTcReader(tcStore, message.getStorageId(), currentPacket); + result = tc::prepareTcReader(*psbParams.tcPool, message.getStorageId(), currentPacket); if (result != HasReturnvaluesIF::RETURN_OK) { - auto verifParams = VerifFailureParams(tcverif::START_FAILURE, currentPacket, result); + // We were not even able to retrieve the TC, so we can not retrieve any TC properties either + // without segfaulting + auto verifParams = VerifFailureParams(tcverif::START_FAILURE, 0, 0, result); verifParams.errorParam1 = errorParameter1; verifParams.errorParam2 = errorParameter2; psbParams.verifReporter->sendFailureReport(verifParams); @@ -72,7 +75,7 @@ void PusServiceBase::handleRequestQueue() { failParams.errorParam2 = errorParameter2; psbParams.verifReporter->sendFailureReport(failParams); } - tcStore->deleteData(message.getStorageId()); + psbParams.tcPool->deleteData(message.getStorageId()); errorParameter1 = 0; errorParameter2 = 0; } @@ -100,9 +103,16 @@ ReturnValue_t PusServiceBase::initialize() { } } - if (tcStore == nullptr) { - tcStore = ObjectManager::instance()->get(objects::TC_STORE); - if (tcStore == nullptr) { + if (psbParams.pusDistributor != nullptr) { + psbParams.pusDistributor = ObjectManager::instance()->get(pusDistributor); + if (psbParams.pusDistributor != nullptr) { + registerService(*psbParams.pusDistributor); + } + } + + if (psbParams.tcPool == nullptr) { + psbParams.tcPool = ObjectManager::instance()->get(objects::TC_STORE); + if (psbParams.tcPool == nullptr) { return ObjectManagerIF::CHILD_INIT_FAILED; } } @@ -124,7 +134,7 @@ ReturnValue_t PusServiceBase::initializeAfterTaskCreation() { return HasReturnvaluesIF::RETURN_OK; } -void PusServiceBase::setCustomTcStore(StorageManagerIF* tcStore_) { tcStore = tcStore_; } +void PusServiceBase::setCustomTcStore(StorageManagerIF* tcPool) { psbParams.tcPool = tcPool; } void PusServiceBase::setCustomErrorReporter(InternalErrorReporterIF* errReporter_) { psbParams.errReporter = errReporter_; diff --git a/src/fsfw/tmtcservices/PusServiceBase.h b/src/fsfw/tmtcservices/PusServiceBase.h index fabe61412..1c1c417d2 100644 --- a/src/fsfw/tmtcservices/PusServiceBase.h +++ b/src/fsfw/tmtcservices/PusServiceBase.h @@ -56,6 +56,19 @@ struct PsbParams { * @objects::INTERNAL_ERROR_REPORTER */ InternalErrorReporterIF* errReporter = nullptr; + /** + * The storage manager which will be used to retrieve any TC packet using the store ID + * received via a message. If this is not set, the class will attempt to find a suitable global + * object with the ID @objects::TC_STORE + */ + StorageManagerIF* tcPool = nullptr; + /** + * Usually, packets are sent via a dedicated PUS distributor. If this distributor is set, + * the PUS service will register itself there. Otherwise, the base class will try to find + * a suitable global distributor with the static ID @PusServiceBase::pusDistributor and + * register itself at that object. + */ + PUSDistributorIF* pusDistributor = nullptr; }; /** @@ -200,10 +213,10 @@ class PusServiceBase : public ExecutableObjectIF, * It is deleted after handleRequest was executed. */ PusTcReader currentPacket; - StorageManagerIF* tcStore = nullptr; bool ownedQueue = true; static object_id_t packetDestination; + static object_id_t pusDistributor; VerifSuccessParams successParams; private: diff --git a/src/fsfw/tmtcservices/VerificationReporter.cpp b/src/fsfw/tmtcservices/VerificationReporter.cpp index b08756d3e..25d7cd4af 100644 --- a/src/fsfw/tmtcservices/VerificationReporter.cpp +++ b/src/fsfw/tmtcservices/VerificationReporter.cpp @@ -1,8 +1,11 @@ #include "fsfw/tmtcservices/VerificationReporter.h" +#include "fsfw/objectmanager.h" #include "fsfw/serviceinterface/ServiceInterface.h" #include "fsfw/tmtcservices/PusVerificationReport.h" +object_id_t VerificationReporter::DEFAULT_RECEIVER = objects::PUS_SERVICE_1_VERIFICATION; + VerificationReporter::VerificationReporter(AcceptsVerifyMessageIF* receiver, object_id_t objectId) : SystemObject(objectId) { if (receiver != nullptr) { @@ -42,3 +45,26 @@ ReturnValue_t VerificationReporter::sendSuccessReport(VerifSuccessParams params) } return status; } + +ReturnValue_t VerificationReporter::initialize() { + if (acknowledgeQueue == MessageQueueIF::NO_QUEUE) { + auto* receiver = ObjectManager::instance()->get(DEFAULT_RECEIVER); + if (receiver != nullptr) { + acknowledgeQueue = receiver->getVerificationQueue(); + } else { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error + << "Could not find a suitable verification message receiver. Please ensure that it is set" + " via the constructor or creating a global one with the ID " + "VerificationReporter::DEFAULT_RECEIVER" + << std::endl; +#else + sif::printError( + "Could not find a suitable verification message receiver. Please ensure " + "that it is set via the constructor or creating a global one with the ID " + "VerificationReporter::DEFAULT_RECEIVER\n"); +#endif + } + } + return SystemObject::initialize(); +} diff --git a/src/fsfw/tmtcservices/VerificationReporter.h b/src/fsfw/tmtcservices/VerificationReporter.h index 8ea45f79e..8b224808a 100644 --- a/src/fsfw/tmtcservices/VerificationReporter.h +++ b/src/fsfw/tmtcservices/VerificationReporter.h @@ -33,6 +33,9 @@ class VerificationReporter : public SystemObject, public VerificationReporterIF ReturnValue_t sendFailureReport(VerifFailureParams params) override; + static object_id_t DEFAULT_RECEIVER; + ReturnValue_t initialize() override; + private: MessageQueueId_t acknowledgeQueue; }; diff --git a/src/fsfw/tmtcservices/tcHelpers.cpp b/src/fsfw/tmtcservices/tcHelpers.cpp index 91f6e3bea..a51bab467 100644 --- a/src/fsfw/tmtcservices/tcHelpers.cpp +++ b/src/fsfw/tmtcservices/tcHelpers.cpp @@ -1,10 +1,10 @@ #include "tcHelpers.h" -ReturnValue_t tc::prepareTcReader(StorageManagerIF *tcStore, store_address_t storeId, +ReturnValue_t tc::prepareTcReader(StorageManagerIF &tcStore, store_address_t storeId, PusTcReader &tcReader) { const uint8_t *dataPtr; size_t dataLen = 0; - ReturnValue_t result = tcStore->getData(storeId, &dataPtr, &dataLen); + ReturnValue_t result = tcStore.getData(storeId, &dataPtr, &dataLen); if (result != HasReturnvaluesIF::RETURN_OK) { return result; } diff --git a/src/fsfw/tmtcservices/tcHelpers.h b/src/fsfw/tmtcservices/tcHelpers.h index fc19ae33b..e2bbf509b 100644 --- a/src/fsfw/tmtcservices/tcHelpers.h +++ b/src/fsfw/tmtcservices/tcHelpers.h @@ -7,7 +7,7 @@ namespace tc { -ReturnValue_t prepareTcReader(StorageManagerIF* tcStore, store_address_t storeId, +ReturnValue_t prepareTcReader(StorageManagerIF& tcStore, store_address_t storeId, PusTcReader& tcReader); } // namespace tc diff --git a/unittests/mocks/PusServiceBaseMock.cpp b/unittests/mocks/PusServiceBaseMock.cpp index ef36d09e6..21f6a360c 100644 --- a/unittests/mocks/PusServiceBaseMock.cpp +++ b/unittests/mocks/PusServiceBaseMock.cpp @@ -31,3 +31,11 @@ void PsbMock::makeNextHandleReqCallFail(ReturnValue_t retval) { handleReqFailPair.first = true; handleReqFailPair.second = retval; } +bool PsbMock::getAndPopNextSubservice(uint8_t& subservice) { + if (subserviceQueue.empty()) { + return false; + } + subservice = subserviceQueue.front(); + subserviceQueue.pop(); + return true; +} diff --git a/unittests/mocks/PusServiceBaseMock.h b/unittests/mocks/PusServiceBaseMock.h index 42620662e..31ac43d30 100644 --- a/unittests/mocks/PusServiceBaseMock.h +++ b/unittests/mocks/PusServiceBaseMock.h @@ -18,6 +18,7 @@ class PsbMock : public PusServiceBase { ReturnValue_t performService() override; void makeNextHandleReqCallFail(ReturnValue_t retval); + bool getAndPopNextSubservice(uint8_t& subservice); void reset(); }; diff --git a/unittests/mocks/PusVerificationReporterMock.cpp b/unittests/mocks/PusVerificationReporterMock.cpp index fdc61aca7..87b37a5a0 100644 --- a/unittests/mocks/PusVerificationReporterMock.cpp +++ b/unittests/mocks/PusVerificationReporterMock.cpp @@ -16,8 +16,19 @@ void PusVerificationReporterMock::popNextFailParams() { VerifFailureParams& PusVerificationReporterMock::getNextFailCallParams() { return failParams.front(); } + void PusVerificationReporterMock::popNextSuccessParams() { if (not successParams.empty()) { successParams.pop(); } } + +ReturnValue_t PusVerificationReporterMock::sendSuccessReport(VerifSuccessParams params) { + successParams.push(params); + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t PusVerificationReporterMock::sendFailureReport(VerifFailureParams params) { + failParams.push(params); + return HasReturnvaluesIF::RETURN_OK; +} diff --git a/unittests/mocks/PusVerificationReporterMock.h b/unittests/mocks/PusVerificationReporterMock.h index 87595637e..5bbd36cc4 100644 --- a/unittests/mocks/PusVerificationReporterMock.h +++ b/unittests/mocks/PusVerificationReporterMock.h @@ -17,7 +17,7 @@ class PusVerificationReporterMock : public VerificationReporterIF { VerifFailureParams& getNextFailCallParams(); void popNextFailParams(); - ReturnValue_t sendSuccessReport(VerifSuccessParams params) override { return 0; } - ReturnValue_t sendFailureReport(VerifFailureParams params) override { return 0; } + ReturnValue_t sendSuccessReport(VerifSuccessParams params) override; + ReturnValue_t sendFailureReport(VerifFailureParams params) override; }; #endif // FSFW_TESTS_PUSVERIFICATIONREPORTERMOCK_H diff --git a/unittests/tmtcservices/testPsb.cpp b/unittests/tmtcservices/testPsb.cpp index 53fe763dd..78ae775eb 100644 --- a/unittests/tmtcservices/testPsb.cpp +++ b/unittests/tmtcservices/testPsb.cpp @@ -1,7 +1,9 @@ #include #include "fsfw/ipc/QueueFactory.h" +#include "fsfw/storagemanager/LocalPool.h" #include "mocks/AcceptsTmMock.h" +#include "mocks/CdsShortTimestamperMock.h" #include "mocks/MessageQueueMock.h" #include "mocks/PusServiceBaseMock.h" #include "mocks/PusVerificationReporterMock.h" @@ -11,13 +13,26 @@ TEST_CASE("Pus Service Base", "[pus-service-base]") { auto msgQueue = MessageQueueMock(1); auto tmReceiver = AcceptsTmMock(2); auto psbParams = PsbParams(0, 0x02, 17); + + LocalPool::LocalPoolConfig cfg = {{5, 32}, {2, 64}}; + LocalPool pool(objects::NO_OBJECT, cfg); + psbParams.verifReporter = &verificationReporter; psbParams.reqQueue = &msgQueue; psbParams.tmReceiver = &tmReceiver; + psbParams.tcPool = &pool; auto psb = PsbMock(psbParams); - store_address_t dummyId(1); - auto reqQueue = psb.getRequestQueue(); - TmTcMessage tmtcMsg(dummyId); + + store_address_t storeId; + TmTcMessage tmtcMsg; + + // Components to create valid PUS packets + auto packetId = PacketId(ccsds::PacketType::TC, true, 0x02); + auto spParams = + SpacePacketParams(packetId, PacketSeqCtrl(ccsds::SequenceFlags::UNSEGMENTED, 0x34), 0x00); + auto pusParams = PusTcParams(17, 1); + PusTcCreator creator(spParams, pusParams); + REQUIRE(psb.initialize() == HasReturnvaluesIF::RETURN_OK); SECTION("State") { @@ -25,8 +40,51 @@ TEST_CASE("Pus Service Base", "[pus-service-base]") { REQUIRE(psb.getObjectId() == 0); } - SECTION("Send Request") { + SECTION("Perform Service") { + REQUIRE(psb.performServiceCallCnt == 0); + REQUIRE(psb.performOperation(0) == retval::OK); + REQUIRE(psb.performServiceCallCnt == 1); + } + + SECTION("Send Request with Successful Handling") { + REQUIRE(psb.performServiceCallCnt == 0); + uint8_t* dataPtr; + REQUIRE(pool.getFreeElement(&storeId, creator.getSerializedSize(), &dataPtr) == retval::OK); + REQUIRE(creator.serializeBe(dataPtr, creator.getSerializedSize()) == retval::OK); + tmtcMsg.setStorageId(storeId); msgQueue.addReceivedMessage(tmtcMsg); REQUIRE(psb.performOperation(0) == retval::OK); + uint8_t subservice = 0; + REQUIRE(psb.getAndPopNextSubservice(subservice)); + REQUIRE(subservice == 1); + REQUIRE(psb.performServiceCallCnt == 1); + // PSB should take care of freeing the pool slot + REQUIRE(not pool.hasDataAtId(storeId)); + REQUIRE(verificationReporter.successCallCount() == 1); + REQUIRE(verificationReporter.failCallCount() == 0); + auto verifParams = verificationReporter.getNextSuccessCallParams(); + REQUIRE(verifParams.tcPacketId == creator.getPacketIdRaw()); + REQUIRE(verifParams.tcPsc == creator.getPacketSeqCtrlRaw()); } -} \ No newline at end of file + + SECTION("Send Request with Failed Handling") { + uint8_t* dataPtr; + REQUIRE(pool.getFreeElement(&storeId, creator.getSerializedSize(), &dataPtr) == retval::OK); + REQUIRE(creator.serializeBe(dataPtr, creator.getSerializedSize()) == retval::OK); + tmtcMsg.setStorageId(storeId); + msgQueue.addReceivedMessage(tmtcMsg); + psb.makeNextHandleReqCallFail(3); + REQUIRE(psb.performOperation(0) == retval::OK); + uint8_t subservice = 0; + REQUIRE(psb.getAndPopNextSubservice(subservice)); + REQUIRE(subservice == 1); + REQUIRE(psb.performServiceCallCnt == 1); + // PSB should take care of freeing the pool slot + REQUIRE(not pool.hasDataAtId(storeId)); + REQUIRE(verificationReporter.successCallCount() == 0); + REQUIRE(verificationReporter.failCallCount() == 1); + auto verifParams = verificationReporter.getNextFailCallParams(); + REQUIRE(verifParams.tcPacketId == creator.getPacketIdRaw()); + REQUIRE(verifParams.tcPsc == creator.getPacketSeqCtrlRaw()); + } +}