diff --git a/datapool/PoolDataSetBase.cpp b/datapool/PoolDataSetBase.cpp index bdca22c3..60f08b88 100644 --- a/datapool/PoolDataSetBase.cpp +++ b/datapool/PoolDataSetBase.cpp @@ -8,13 +8,16 @@ PoolDataSetBase::PoolDataSetBase(PoolVariableIF** registeredVariablesArray, const size_t maxFillCount): registeredVariables(registeredVariablesArray), - maxFillCount(maxFillCount) { -} + maxFillCount(maxFillCount) {} PoolDataSetBase::~PoolDataSetBase() {} ReturnValue_t PoolDataSetBase::registerVariable(PoolVariableIF *variable) { + if(registeredVariables == nullptr) { + /* Underlying container invalid */ + return HasReturnvaluesIF::RETURN_FAILED; + } if (state != States::STATE_SET_UNINITIALISED) { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "DataSet::registerVariable: Call made in wrong position." << std::endl; diff --git a/datapool/PoolDataSetIF.h b/datapool/PoolDataSetIF.h index 9151f2f8..f905cc4d 100644 --- a/datapool/PoolDataSetIF.h +++ b/datapool/PoolDataSetIF.h @@ -9,8 +9,8 @@ * and unlock a data pool and read/commit semantics. */ class PoolDataSetIF: - public DataSetIF, - public ReadCommitIF { + virtual public DataSetIF, + virtual public ReadCommitIF { public: virtual~ PoolDataSetIF() {}; diff --git a/datapool/SharedDataSetIF.h b/datapool/SharedDataSetIF.h index b8d98794..4d23f87f 100644 --- a/datapool/SharedDataSetIF.h +++ b/datapool/SharedDataSetIF.h @@ -1,13 +1,15 @@ #ifndef FRAMEWORK_DATAPOOL_SHAREDDATASETIF_H_ #define FRAMEWORK_DATAPOOL_SHAREDDATASETIF_H_ + #include "PoolDataSetIF.h" -class SharedDataSetIF: public PoolDataSetIF { +class SharedDataSetIF { public: virtual ~SharedDataSetIF() {}; private: - virtual ReturnValue_t lockDataset(dur_millis_t mutexTimeout) = 0; + virtual ReturnValue_t lockDataset(MutexIF::TimeoutType timeoutType, + dur_millis_t mutexTimeout) = 0; virtual ReturnValue_t unlockDataset() = 0; }; diff --git a/datapoollocal/HasLocalDataPoolIF.h b/datapoollocal/HasLocalDataPoolIF.h index d52c72b6..74e372c9 100644 --- a/datapoollocal/HasLocalDataPoolIF.h +++ b/datapoollocal/HasLocalDataPoolIF.h @@ -65,34 +65,45 @@ public: * usually be the period the pool owner performs its periodic operation. * @return */ - virtual uint32_t getPeriodicOperationFrequency() const = 0; + virtual dur_millis_t getPeriodicOperationFrequency() const = 0; /** * @brief This function will be called by the manager if an update * notification is received. * @details HasLocalDataPoolIF * Can be overriden by the child class to handle changed datasets. - * @param sid - * @param storeId If a snapshot was requested, data will be located inside + * @param sid SID of the updated set + * @param storeId If a snapshot was requested, data will be located inside * the IPC store with this store ID. + * @param clearMessage If this is set to true, the pool manager will take care of + * clearing the store automatically */ virtual void handleChangedDataset(sid_t sid, - store_address_t storeId = storeId::INVALID_STORE_ADDRESS) { - return; + store_address_t storeId = storeId::INVALID_STORE_ADDRESS, + bool* clearMessage = nullptr) { + if(clearMessage != nullptr) { + *clearMessage = true; + } } /** * @brief This function will be called by the manager if an update * notification is received. * @details - * Can be overriden by the child class to handle changed pool IDs. - * @param sid - * @param storeId If a snapshot was requested, data will be located inside + * Can be overriden by the child class to handle changed pool variables. + * @param gpid GPID of the updated variable. + * @param storeId If a snapshot was requested, data will be located inside * the IPC store with this store ID. + * @param clearMessage Relevant for snapshots. If the boolean this points to is set to true, + * the pool manager will take care of clearing the store automatically + * after the callback. */ - virtual void handleChangedPoolVariable(gp_id_t globPoolId, - store_address_t storeId = storeId::INVALID_STORE_ADDRESS) { - return; + virtual void handleChangedPoolVariable(gp_id_t gpid, + store_address_t storeId = storeId::INVALID_STORE_ADDRESS, + bool* clearMessage = nullptr) { + if(clearMessage != nullptr) { + *clearMessage = true; + } } /** diff --git a/datapoollocal/LocalDataPoolManager.cpp b/datapoollocal/LocalDataPoolManager.cpp index 0c44ab7e..94ee9c26 100644 --- a/datapoollocal/LocalDataPoolManager.cpp +++ b/datapoollocal/LocalDataPoolManager.cpp @@ -38,7 +38,11 @@ LocalDataPoolManager::LocalDataPoolManager(HasLocalDataPoolIF* owner, MessageQue hkQueue = queueToUse; } -LocalDataPoolManager::~LocalDataPoolManager() {} +LocalDataPoolManager::~LocalDataPoolManager() { + if(mutex != nullptr) { + MutexFactory::instance()->deleteMutex(mutex); + } +} ReturnValue_t LocalDataPoolManager::initialize(MessageQueueIF* queueToUse) { if(queueToUse == nullptr) { @@ -132,13 +136,16 @@ ReturnValue_t LocalDataPoolManager::performHkOperation() { ReturnValue_t LocalDataPoolManager::handleHkUpdate(HkReceiver& receiver, ReturnValue_t& status) { if(receiver.dataType == DataType::LOCAL_POOL_VARIABLE) { - // Update packets shall only be generated from datasets. + /* Update packets shall only be generated from datasets. */ return HasReturnvaluesIF::RETURN_FAILED; } LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, receiver.dataId.sid); + if(dataSet == nullptr) { + return DATASET_NOT_FOUND; + } if(dataSet->hasChanged()) { - // prepare and send update notification + /* Prepare and send update notification */ ReturnValue_t result = generateHousekeepingPacket( receiver.dataId.sid, dataSet, true); if(result != HasReturnvaluesIF::RETURN_OK) { @@ -328,7 +335,7 @@ void LocalDataPoolManager::handleChangeResetLogic( toReset->setChanged(false); } /* All recipients have been notified, reset the changed flag */ - if(changeInfo.currentUpdateCounter <= 1) { + else if(changeInfo.currentUpdateCounter <= 1) { toReset->setChanged(false); changeInfo.currentUpdateCounter = 0; } @@ -372,7 +379,7 @@ ReturnValue_t LocalDataPoolManager::subscribeForPeriodicPacket(sid_t sid, LocalPoolDataSetAttorney::setReportingEnabled(*dataSet, enableReporting); LocalPoolDataSetAttorney::setDiagnostic(*dataSet, isDiagnostics); LocalPoolDataSetAttorney::initializePeriodicHelper(*dataSet, collectionInterval, - owner->getPeriodicOperationFrequency(), isDiagnostics); + owner->getPeriodicOperationFrequency()); } hkReceivers.push_back(hkReceiver); @@ -398,7 +405,6 @@ ReturnValue_t LocalDataPoolManager::subscribeForUpdatePacket(sid_t sid, hkReceiver.destinationQueue = hkReceiverObject->getHkQueue(); LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid); - //LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid); if(dataSet != nullptr) { LocalPoolDataSetAttorney::setReportingEnabled(*dataSet, true); LocalPoolDataSetAttorney::setDiagnostic(*dataSet, isDiagnostics); @@ -516,11 +522,19 @@ ReturnValue_t LocalDataPoolManager::handleHousekeepingMessage( } case(HousekeepingMessage::REPORT_DIAGNOSTICS_REPORT_STRUCTURES): { - return generateSetStructurePacket(sid, true); + result = generateSetStructurePacket(sid, true); + if(result == HasReturnvaluesIF::RETURN_OK) { + return result; + } + break; } case(HousekeepingMessage::REPORT_HK_REPORT_STRUCTURES): { - return generateSetStructurePacket(sid, false); + result = generateSetStructurePacket(sid, false); + if(result == HasReturnvaluesIF::RETURN_OK) { + return result; + } + break; } case(HousekeepingMessage::MODIFY_DIAGNOSTICS_REPORT_COLLECTION_INTERVAL): case(HousekeepingMessage::MODIFY_PARAMETER_REPORT_COLLECTION_INTERVAL): { @@ -540,14 +554,15 @@ ReturnValue_t LocalDataPoolManager::handleHousekeepingMessage( case(HousekeepingMessage::GENERATE_ONE_PARAMETER_REPORT): case(HousekeepingMessage::GENERATE_ONE_DIAGNOSTICS_REPORT): { LocalPoolDataSetBase* dataSet =HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid); - //LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid); if(command == HousekeepingMessage::GENERATE_ONE_PARAMETER_REPORT and LocalPoolDataSetAttorney::isDiagnostics(*dataSet)) { - return WRONG_HK_PACKET_TYPE; + result = WRONG_HK_PACKET_TYPE; + break; } else if(command == HousekeepingMessage::GENERATE_ONE_DIAGNOSTICS_REPORT and not LocalPoolDataSetAttorney::isDiagnostics(*dataSet)) { - return WRONG_HK_PACKET_TYPE; + result = WRONG_HK_PACKET_TYPE; + break; } return generateHousekeepingPacket(HousekeepingMessage::getSid(message), dataSet, true); @@ -566,14 +581,22 @@ ReturnValue_t LocalDataPoolManager::handleHousekeepingMessage( case(HousekeepingMessage::UPDATE_SNAPSHOT_SET): { store_address_t storeId; HousekeepingMessage::getUpdateSnapshotSetCommand(message, &storeId); - owner->handleChangedDataset(sid, storeId); + bool clearMessage = true; + owner->handleChangedDataset(sid, storeId, &clearMessage); + if(clearMessage) { + message->clear(); + } return HasReturnvaluesIF::RETURN_OK; } case(HousekeepingMessage::UPDATE_SNAPSHOT_VARIABLE): { store_address_t storeId; gp_id_t globPoolId = HousekeepingMessage::getUpdateSnapshotVariableCommand(message, &storeId); - owner->handleChangedPoolVariable(globPoolId, storeId); + bool clearMessage = true; + owner->handleChangedPoolVariable(globPoolId, storeId, &clearMessage); + if(clearMessage) { + message->clear(); + } return HasReturnvaluesIF::RETURN_OK; } @@ -616,7 +639,7 @@ ReturnValue_t LocalDataPoolManager::generateHousekeepingPacket(sid_t sid, LocalPoolDataSetBase* dataSet, bool forDownlink, MessageQueueId_t destination) { if(dataSet == nullptr) { - // Configuration error. + /* Configuration error. */ printWarningOrError(sif::OutputTypes::OUT_WARNING, "generateHousekeepingPacket", DATASET_NOT_FOUND); @@ -632,7 +655,7 @@ ReturnValue_t LocalDataPoolManager::generateHousekeepingPacket(sid_t sid, return result; } - // and now we set a HK message and send it the HK packet destination. + /* Now we set a HK message and send it the HK packet destination. */ CommandMessage hkMessage; if(LocalPoolDataSetAttorney::isDiagnostics(*dataSet)) { HousekeepingMessage::setHkDiagnosticsReply(&hkMessage, sid, storeId); @@ -642,7 +665,7 @@ ReturnValue_t LocalDataPoolManager::generateHousekeepingPacket(sid_t sid, } if(hkQueue == nullptr) { - // error, no queue available to send packet with. + /* Error, no queue available to send packet with. */ printWarningOrError(sif::OutputTypes::OUT_WARNING, "generateHousekeepingPacket", QUEUE_OR_DESTINATION_INVALID); @@ -650,7 +673,7 @@ ReturnValue_t LocalDataPoolManager::generateHousekeepingPacket(sid_t sid, } if(destination == MessageQueueIF::NO_QUEUE) { if(hkDestinationId == MessageQueueIF::NO_QUEUE) { - // error, all destinations invalid + /* Error, all destinations invalid */ printWarningOrError(sif::OutputTypes::OUT_WARNING, "generateHousekeepingPacket", QUEUE_OR_DESTINATION_INVALID); @@ -729,6 +752,12 @@ void LocalDataPoolManager::performPeriodicHkGeneration(HkReceiver& receiver) { ReturnValue_t LocalDataPoolManager::togglePeriodicGeneration(sid_t sid, bool enable, bool isDiagnostics) { LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid); + if(dataSet == nullptr) { + printWarningOrError(sif::OutputTypes::OUT_WARNING, "togglePeriodicGeneration", + DATASET_NOT_FOUND); + return DATASET_NOT_FOUND; + } + if((LocalPoolDataSetAttorney::isDiagnostics(*dataSet) and not isDiagnostics) or (not LocalPoolDataSetAttorney::isDiagnostics(*dataSet) and isDiagnostics)) { return WRONG_HK_PACKET_TYPE; @@ -746,6 +775,12 @@ ReturnValue_t LocalDataPoolManager::togglePeriodicGeneration(sid_t sid, ReturnValue_t LocalDataPoolManager::changeCollectionInterval(sid_t sid, float newCollectionInterval, bool isDiagnostics) { LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid); + if(dataSet == nullptr) { + printWarningOrError(sif::OutputTypes::OUT_WARNING, "changeCollectionInterval", + DATASET_NOT_FOUND); + return DATASET_NOT_FOUND; + } + bool targetIsDiagnostics = LocalPoolDataSetAttorney::isDiagnostics(*dataSet); if((targetIsDiagnostics and not isDiagnostics) or (not targetIsDiagnostics and isDiagnostics)) { @@ -756,7 +791,7 @@ ReturnValue_t LocalDataPoolManager::changeCollectionInterval(sid_t sid, LocalPoolDataSetAttorney::getPeriodicHelper(*dataSet); if(periodicHelper == nullptr) { - // config error + /* Configuration error, set might not have a corresponding pool manager */ return PERIODIC_HELPER_INVALID; } @@ -766,13 +801,11 @@ ReturnValue_t LocalDataPoolManager::changeCollectionInterval(sid_t sid, ReturnValue_t LocalDataPoolManager::generateSetStructurePacket(sid_t sid, bool isDiagnostics) { - // Get and check dataset first. - //LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid); + /* Get and check dataset first. */ LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid); if(dataSet == nullptr) { printWarningOrError(sif::OutputTypes::OUT_WARNING, - "performPeriodicHkGeneration", - DATASET_NOT_FOUND); + "performPeriodicHkGeneration", DATASET_NOT_FOUND); return DATASET_NOT_FOUND; } @@ -831,6 +864,10 @@ ReturnValue_t LocalDataPoolManager::generateSetStructurePacket(sid_t sid, void LocalDataPoolManager::clearReceiversList() { /* Clear the vector completely and releases allocated memory. */ HkReceivers().swap(hkReceivers); + /* Also clear the reset helper if it exists */ + if(hkUpdateResetList != nullptr) { + HkUpdateResetList().swap(*hkUpdateResetList); + } } MutexIF* LocalDataPoolManager::getLocalPoolMutex() { @@ -843,6 +880,7 @@ object_id_t LocalDataPoolManager::getCreatorObjectId() const { void LocalDataPoolManager::printWarningOrError(sif::OutputTypes outputType, const char* functionName, ReturnValue_t error, const char* errorPrint) { +#if FSFW_VERBOSE_LEVEL >= 1 if(errorPrint == nullptr) { if(error == DATASET_NOT_FOUND) { errorPrint = "Dataset not found"; @@ -873,7 +911,6 @@ void LocalDataPoolManager::printWarningOrError(sif::OutputTypes outputType, } if(outputType == sif::OutputTypes::OUT_WARNING) { -#if FSFW_VERBOSE_LEVEL >= 1 #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::warning << "LocalDataPoolManager::" << functionName << ": Object ID 0x" << std::setw(8) << std::setfill('0') @@ -883,10 +920,8 @@ void LocalDataPoolManager::printWarningOrError(sif::OutputTypes outputType, sif::printWarning("LocalDataPoolManager::%s: Object ID 0x%08x | %s\n", functionName, owner->getObjectId(), errorPrint); #endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ -#endif /* FSFW_VERBOSE_LEVEL >= 1 */ } else if(outputType == sif::OutputTypes::OUT_ERROR) { -#if FSFW_VERBOSE_LEVEL >= 1 #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "LocalDataPoolManager::" << functionName << ": Object ID 0x" << std::setw(8) << std::setfill('0') @@ -896,8 +931,8 @@ void LocalDataPoolManager::printWarningOrError(sif::OutputTypes outputType, sif::printError("LocalDataPoolManager::%s: Object ID 0x%08x | %s\n", functionName, owner->getObjectId(), errorPrint); #endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ -#endif /* FSFW_VERBOSE_LEVEL >= 1 */ } +#endif /* #if FSFW_VERBOSE_LEVEL >= 1 */ } LocalDataPoolManager* LocalDataPoolManager::getPoolManagerHandle() { diff --git a/datapoollocal/LocalDataPoolManager.h b/datapoollocal/LocalDataPoolManager.h index d06cf2c6..2ec81f1c 100644 --- a/datapoollocal/LocalDataPoolManager.h +++ b/datapoollocal/LocalDataPoolManager.h @@ -391,6 +391,10 @@ protected: template inline ReturnValue_t LocalDataPoolManager::fetchPoolEntry(lp_id_t localPoolId, PoolEntry **poolEntry) { + if(poolEntry == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + auto poolIter = localPoolMap.find(localPoolId); if (poolIter == localPoolMap.end()) { printWarningOrError(sif::OutputTypes::OUT_WARNING, "fetchPoolEntry", diff --git a/datapoollocal/SharedLocalDataSet.cpp b/datapoollocal/SharedLocalDataSet.cpp index dd1bdcc4..84c2d1c3 100644 --- a/datapoollocal/SharedLocalDataSet.cpp +++ b/datapoollocal/SharedLocalDataSet.cpp @@ -1,16 +1,37 @@ #include "SharedLocalDataSet.h" + SharedLocalDataSet::SharedLocalDataSet(object_id_t objectId, sid_t sid, const size_t maxSize): SystemObject(objectId), - LocalPoolDataSetBase(sid, nullptr, maxSize) { + LocalPoolDataSetBase(sid, nullptr, maxSize), poolVarVector(maxSize) { this->setContainer(poolVarVector.data()); datasetLock = MutexFactory::instance()->createMutex(); } -ReturnValue_t SharedLocalDataSet::lockDataset(dur_millis_t mutexTimeout) { - return datasetLock->lockMutex(MutexIF::TimeoutType::WAITING, mutexTimeout); +SharedLocalDataSet::SharedLocalDataSet(object_id_t objectId, + HasLocalDataPoolIF *owner, uint32_t setId, + const size_t maxSize): SystemObject(objectId), + LocalPoolDataSetBase(owner, setId, nullptr, maxSize), poolVarVector(maxSize) { + this->setContainer(poolVarVector.data()); + datasetLock = MutexFactory::instance()->createMutex(); +} + +ReturnValue_t SharedLocalDataSet::lockDataset(MutexIF::TimeoutType timeoutType, + dur_millis_t mutexTimeout) { + if(datasetLock != nullptr) { + return datasetLock->lockMutex(timeoutType, mutexTimeout); + } + return HasReturnvaluesIF::RETURN_FAILED; +} + + +SharedLocalDataSet::~SharedLocalDataSet() { + MutexFactory::instance()->deleteMutex(datasetLock); } ReturnValue_t SharedLocalDataSet::unlockDataset() { - return datasetLock->unlockMutex(); + if(datasetLock != nullptr) { + return datasetLock->unlockMutex(); + } + return HasReturnvaluesIF::RETURN_FAILED; } diff --git a/datapoollocal/SharedLocalDataSet.h b/datapoollocal/SharedLocalDataSet.h index 83f2a72f..8d12610a 100644 --- a/datapoollocal/SharedLocalDataSet.h +++ b/datapoollocal/SharedLocalDataSet.h @@ -11,16 +11,22 @@ * multiple threads. It provides a lock in addition to all other functionalities provided * by the LocalPoolDataSetBase class. * - * TODO: override and protect read, commit and some other calls used by pool manager. + * The user is completely responsible for lockingand unlocking the dataset when using the + * shared dataset. */ class SharedLocalDataSet: public SystemObject, public LocalPoolDataSetBase, public SharedDataSetIF { public: - SharedLocalDataSet(object_id_t objectId, sid_t sid, + SharedLocalDataSet(object_id_t objectId, HasLocalDataPoolIF* owner, uint32_t setId, const size_t maxSize); - ReturnValue_t lockDataset(dur_millis_t mutexTimeout) override; + SharedLocalDataSet(object_id_t objectId, sid_t sid, const size_t maxSize); + + virtual~ SharedLocalDataSet(); + + ReturnValue_t lockDataset(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, + dur_millis_t mutexTimeout = 20) override; ReturnValue_t unlockDataset() override; private: diff --git a/datapoollocal/datapoollocal.h b/datapoollocal/datapoollocal.h new file mode 100644 index 00000000..c5c47078 --- /dev/null +++ b/datapoollocal/datapoollocal.h @@ -0,0 +1,12 @@ +#ifndef FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_ +#define FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_ + +/* Collected related headers */ +#include "LocalPoolVariable.h" +#include "LocalPoolVector.h" +#include "StaticLocalDataSet.h" +#include "LocalDataSet.h" +#include "SharedLocalDataSet.h" + + +#endif /* FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_ */ diff --git a/datapoollocal/internal/LocalPoolDataSetAttorney.h b/datapoollocal/internal/LocalPoolDataSetAttorney.h index 7a34e9c8..f81428cd 100644 --- a/datapoollocal/internal/LocalPoolDataSetAttorney.h +++ b/datapoollocal/internal/LocalPoolDataSetAttorney.h @@ -14,9 +14,8 @@ private: } static void initializePeriodicHelper(LocalPoolDataSetBase& set, float collectionInterval, - uint32_t minimumPeriodicIntervalMs, - bool isDiagnostics, uint8_t nonDiagIntervalFactor = 5) { - set.initializePeriodicHelper(collectionInterval, minimumPeriodicIntervalMs, isDiagnostics, + uint32_t minimumPeriodicIntervalMs, uint8_t nonDiagIntervalFactor = 5) { + set.initializePeriodicHelper(collectionInterval, minimumPeriodicIntervalMs, nonDiagIntervalFactor); } diff --git a/datapoollocal/localPoolDefinitions.h b/datapoollocal/localPoolDefinitions.h index af8ce711..daab8b9c 100644 --- a/datapoollocal/localPoolDefinitions.h +++ b/datapoollocal/localPoolDefinitions.h @@ -96,11 +96,11 @@ union gp_id_t { return raw == INVALID_GPID; } - bool operator==(const sid_t& other) const { + bool operator==(const gp_id_t& other) const { return raw == other.raw; } - bool operator!=(const sid_t& other) const { + bool operator!=(const gp_id_t& other) const { return not (raw == other.raw); } }; diff --git a/globalfunctions/arrayprinter.cpp b/globalfunctions/arrayprinter.cpp index 7dc056b0..20a64f5b 100644 --- a/globalfunctions/arrayprinter.cpp +++ b/globalfunctions/arrayprinter.cpp @@ -67,7 +67,9 @@ void arrayprinter::printHex(const uint8_t *data, size_t size, } } } +#if FSFW_DISABLE_PRINTOUT == 0 printf("[%s]\n", printBuffer); +#endif /* FSFW_DISABLE_PRINTOUT == 0 */ #endif } @@ -108,7 +110,9 @@ void arrayprinter::printDec(const uint8_t *data, size_t size, } } } +#if FSFW_DISABLE_PRINTOUT == 0 printf("[%s]\n", printBuffer); +#endif /* FSFW_DISABLE_PRINTOUT == 0 */ #endif } diff --git a/housekeeping/HousekeepingMessage.cpp b/housekeeping/HousekeepingMessage.cpp index d9803ef6..90ca73c8 100644 --- a/housekeeping/HousekeepingMessage.cpp +++ b/housekeeping/HousekeepingMessage.cpp @@ -84,15 +84,21 @@ void HousekeepingMessage::setCollectionIntervalModificationCommand( else { command->setCommand(MODIFY_PARAMETER_REPORT_COLLECTION_INTERVAL); } - command->setParameter3(collectionInterval); + + /* Raw storage of the float in the message. Do not use setParameter3, does + implicit conversion to integer type! */ + std::memcpy(command->getData() + 2 * sizeof(uint32_t), &collectionInterval, + sizeof(collectionInterval)); setSid(command, sid); } sid_t HousekeepingMessage::getCollectionIntervalModificationCommand( const CommandMessage* command, float* newCollectionInterval) { + if(newCollectionInterval != nullptr) { - *newCollectionInterval = command->getParameter3(); + std::memcpy(newCollectionInterval, command->getData() + 2 * sizeof(uint32_t), + sizeof(*newCollectionInterval)); } return getSid(command); @@ -151,7 +157,8 @@ void HousekeepingMessage::clear(CommandMessage* message) { case(DIAGNOSTICS_REPORT): case(HK_DEFINITIONS_REPORT): case(DIAGNOSTICS_DEFINITION_REPORT): - case(UPDATE_SNAPSHOT_SET): { + case(UPDATE_SNAPSHOT_SET): + case(UPDATE_SNAPSHOT_VARIABLE): { store_address_t storeId; getHkDataReply(message, &storeId); StorageManagerIF *ipcStore = objectManager->get( diff --git a/housekeeping/HousekeepingSnapshot.h b/housekeeping/HousekeepingSnapshot.h index 50afd4af..45b40d37 100644 --- a/housekeeping/HousekeepingSnapshot.h +++ b/housekeeping/HousekeepingSnapshot.h @@ -11,7 +11,8 @@ * @brief This helper class will be used to serialize and deserialize update housekeeping packets * into the store. */ -class HousekeepingSnapshot: public SerializeIF { +class HousekeepingSnapshot: + public SerializeIF { public: /** @@ -36,6 +37,17 @@ public: timeStamp(timeStamp), timeStampSize(timeStampSize), updateData(dataSetPtr) {}; + /** + * Update packet constructor for pool variables. + * @param timeStamp + * @param timeStampSize + * @param dataSetPtr + */ + HousekeepingSnapshot(CCSDSTime::CDS_short* cdsShort, LocalPoolObjectBase* dataSetPtr): + timeStamp(reinterpret_cast(cdsShort)), + timeStampSize(sizeof(CCSDSTime::CDS_short)), updateData(dataSetPtr) {}; + + /** * Update packet constructor for pool variables. * @param timeStamp @@ -47,8 +59,8 @@ public: timeStamp(timeStamp), timeStampSize(timeStampSize), updateData(dataSetPtr) {}; - virtual ReturnValue_t serialize(uint8_t **buffer, size_t *size, - size_t maxSize, Endianness streamEndianness) const { + virtual ReturnValue_t serialize(uint8_t **buffer, size_t *size, size_t maxSize, + Endianness streamEndianness) const { if(timeStamp != nullptr) { /* Endianness will always be MACHINE, so we can simply use memcpy here. */ diff --git a/housekeeping/PeriodicHousekeepingHelper.cpp b/housekeeping/PeriodicHousekeepingHelper.cpp index 365f0004..892eb354 100644 --- a/housekeeping/PeriodicHousekeepingHelper.cpp +++ b/housekeeping/PeriodicHousekeepingHelper.cpp @@ -4,46 +4,87 @@ #include PeriodicHousekeepingHelper::PeriodicHousekeepingHelper( - LocalPoolDataSetBase* owner): owner(owner) {} - + LocalPoolDataSetBase* owner): owner(owner) {} void PeriodicHousekeepingHelper::initialize(float collectionInterval, - dur_millis_t minimumPeriodicInterval, bool isDiagnostics, - uint8_t nonDiagIntervalFactor) { - this->minimumPeriodicInterval = minimumPeriodicInterval; - if(not isDiagnostics) { - this->minimumPeriodicInterval = this->minimumPeriodicInterval * - nonDiagIntervalFactor; - } - collectionIntervalTicks = intervalSecondsToInterval(collectionInterval); + dur_millis_t minimumPeriodicInterval, uint8_t nonDiagIntervalFactor) { + this->minimumPeriodicInterval = minimumPeriodicInterval; + this->nonDiagIntervalFactor = nonDiagIntervalFactor; + collectionIntervalTicks = intervalSecondsToIntervalTicks(collectionInterval); + /* This will cause a checkOpNecessary call to be true immediately. I think it's okay + if a HK packet is generated immediately instead of waiting one generation cycle. */ + internalTickCounter = collectionIntervalTicks; } -float PeriodicHousekeepingHelper::getCollectionIntervalInSeconds() { - return intervalToIntervalSeconds(collectionIntervalTicks); +float PeriodicHousekeepingHelper::getCollectionIntervalInSeconds() const { + return intervalTicksToSeconds(collectionIntervalTicks); } bool PeriodicHousekeepingHelper::checkOpNecessary() { - if(internalTickCounter >= collectionIntervalTicks) { - internalTickCounter = 1; - return true; - } - internalTickCounter++; - return false; + if(internalTickCounter >= collectionIntervalTicks) { + internalTickCounter = 1; + return true; + } + internalTickCounter++; + return false; } -uint32_t PeriodicHousekeepingHelper::intervalSecondsToInterval( - float collectionIntervalSeconds) { - return std::ceil(collectionIntervalSeconds * 1000 - / minimumPeriodicInterval); +uint32_t PeriodicHousekeepingHelper::intervalSecondsToIntervalTicks( + float collectionIntervalSeconds) { + if(owner == nullptr) { + return 0; + } + bool isDiagnostics = owner->isDiagnostics(); + + /* Avoid division by zero */ + if(minimumPeriodicInterval == 0) { + if(isDiagnostics) { + /* Perform operation each cycle */ + return 1; + } + else { + return nonDiagIntervalFactor; + } + } + else { + dur_millis_t intervalInMs = collectionIntervalSeconds * 1000; + uint32_t divisor = minimumPeriodicInterval; + if(not isDiagnostics) { + /* We need to multiply the divisor because non-diagnostics only + allow a multiple of the minimum periodic interval */ + divisor *= nonDiagIntervalFactor; + } + uint32_t ticks = std::ceil(static_cast(intervalInMs) / divisor); + if(not isDiagnostics) { + /* Now we need to multiply the calculated ticks with the factor as as well + because the minimum tick count to generate a non-diagnostic is the factor itself. + + Example calculation for non-diagnostic with + 0.4 second interval and 0.2 second task interval. + Resultant tick count of 5 is equal to operation each second. + + Examle calculation for non-diagnostic with 2.0 second interval and 0.2 second + task interval. + Resultant tick count of 10 is equal to operatin every 2 seconds. + + Example calculation for diagnostic with 0.4 second interval and 0.3 + second task interval. Resulting tick count of 2 is equal to operation + every 0.6 seconds. */ + ticks *= nonDiagIntervalFactor; + } + return ticks; + } } -float PeriodicHousekeepingHelper::intervalToIntervalSeconds( - uint32_t collectionInterval) { - return static_cast(collectionInterval * - minimumPeriodicInterval); +float PeriodicHousekeepingHelper::intervalTicksToSeconds( + uint32_t collectionInterval) const { + /* Number of ticks times the minimum interval is in milliseconds, so we divide by 1000 to get + the value in seconds */ + return static_cast(collectionInterval * minimumPeriodicInterval / 1000.0); } void PeriodicHousekeepingHelper::changeCollectionInterval( - float newIntervalSeconds) { - collectionIntervalTicks = intervalSecondsToInterval(newIntervalSeconds); + float newIntervalSeconds) { + collectionIntervalTicks = intervalSecondsToIntervalTicks(newIntervalSeconds); } + diff --git a/housekeeping/PeriodicHousekeepingHelper.h b/housekeeping/PeriodicHousekeepingHelper.h index d96eae1d..3256fbff 100644 --- a/housekeeping/PeriodicHousekeepingHelper.h +++ b/housekeeping/PeriodicHousekeepingHelper.h @@ -10,18 +10,19 @@ class PeriodicHousekeepingHelper { public: PeriodicHousekeepingHelper(LocalPoolDataSetBase* owner); - void initialize(float collectionInterval, - dur_millis_t minimumPeriodicInterval, bool isDiagnostics, - uint8_t nonDiagIntervalFactor); + void initialize(float collectionInterval, dur_millis_t minimumPeriodicInterval, + uint8_t nonDiagIntervalFactor); void changeCollectionInterval(float newInterval); - float getCollectionIntervalInSeconds(); + float getCollectionIntervalInSeconds() const; bool checkOpNecessary(); + private: LocalPoolDataSetBase* owner = nullptr; + uint8_t nonDiagIntervalFactor = 0; - uint32_t intervalSecondsToInterval(float collectionIntervalSeconds); - float intervalToIntervalSeconds(uint32_t collectionInterval); + uint32_t intervalSecondsToIntervalTicks(float collectionIntervalSeconds); + float intervalTicksToSeconds(uint32_t collectionInterval) const; dur_millis_t minimumPeriodicInterval = 0; uint32_t internalTickCounter = 1; diff --git a/osal/linux/TaskFactory.cpp b/osal/linux/TaskFactory.cpp index 93564647..80bf47b7 100644 --- a/osal/linux/TaskFactory.cpp +++ b/osal/linux/TaskFactory.cpp @@ -2,6 +2,7 @@ #include "PeriodicPosixTask.h" #include "../../tasks/TaskFactory.h" +#include "../../serviceinterface/ServiceInterface.h" #include "../../returnvalues/HasReturnvaluesIF.h" //TODO: Different variant than the lazy loading in QueueFactory. What's better and why? diff --git a/osal/windows/CMakeLists.txt b/osal/windows/CMakeLists.txt index b6e76d6a..a3a719e0 100644 --- a/osal/windows/CMakeLists.txt +++ b/osal/windows/CMakeLists.txt @@ -1,11 +1,9 @@ -target_sources(${LIB_FSFW_NAME} - PRIVATE - TcWinUdpPollingTask.cpp - TmTcWinUdpBridge.cpp +target_sources(${LIB_FSFW_NAME} PRIVATE + TcWinUdpPollingTask.cpp + TmTcWinUdpBridge.cpp ) -target_link_libraries(${LIB_FSFW_NAME} - PRIVATE - wsock32 - ws2_32 +target_link_libraries(${LIB_FSFW_NAME} PRIVATE + wsock32 + ws2_32 ) \ No newline at end of file diff --git a/osal/windows/TcWinUdpPollingTask.cpp b/osal/windows/TcWinUdpPollingTask.cpp index f9b3753e..06e75bd5 100644 --- a/osal/windows/TcWinUdpPollingTask.cpp +++ b/osal/windows/TcWinUdpPollingTask.cpp @@ -3,11 +3,6 @@ #include "../../serviceinterface/ServiceInterfaceStream.h" #include -#include -#if defined(_MSC_VER) -#include -typedef SSIZE_T ssize_t; -#endif TcWinUdpPollingTask::TcWinUdpPollingTask(object_id_t objectId, object_id_t tmtcUnixUdpBridge, size_t frameSize, @@ -20,8 +15,8 @@ TcWinUdpPollingTask::TcWinUdpPollingTask(object_id_t objectId, this->frameSize = DEFAULT_MAX_FRAME_SIZE; } - // Set up reception buffer with specified frame size. - // For now, it is assumed that only one frame is held in the buffer! + /* Set up reception buffer with specified frame size. + For now, it is assumed that only one frame is held in the buffer! */ receptionBuffer.reserve(this->frameSize); receptionBuffer.resize(this->frameSize); @@ -36,17 +31,17 @@ TcWinUdpPollingTask::TcWinUdpPollingTask(object_id_t objectId, TcWinUdpPollingTask::~TcWinUdpPollingTask() {} ReturnValue_t TcWinUdpPollingTask::performOperation(uint8_t opCode) { - // Poll for new UDP datagrams in permanent loop. + /* Poll for new UDP datagrams in permanent loop. */ while(true) { //! Sender Address is cached here. struct sockaddr_in senderAddress; int senderAddressSize = sizeof(senderAddress); - ssize_t bytesReceived = recvfrom(serverUdpSocket, + int bytesReceived = recvfrom(serverUdpSocket, reinterpret_cast(receptionBuffer.data()), frameSize, receptionFlags, reinterpret_cast(&senderAddress), &senderAddressSize); if(bytesReceived == SOCKET_ERROR) { - // handle error + /* Handle error */ #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "TcWinUdpPollingTask::performOperation: Reception" " error." << std::endl; @@ -54,9 +49,9 @@ ReturnValue_t TcWinUdpPollingTask::performOperation(uint8_t opCode) { handleReadError(); continue; } -#if FSFW_CPP_OSTREAM_ENABLED == 1 - //sif::debug << "TcWinUdpPollingTask::performOperation: " << bytesReceived - // << " bytes received" << std::endl; +#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_WIRETAPPING_ENABLED == 1 + sif::debug << "TcWinUdpPollingTask::performOperation: " << bytesReceived << + " bytes received" << std::endl; #endif ReturnValue_t result = handleSuccessfullTcRead(bytesReceived); @@ -74,12 +69,14 @@ ReturnValue_t TcWinUdpPollingTask::handleSuccessfullTcRead(size_t bytesRead) { store_address_t storeId; ReturnValue_t result = tcStore->addData(&storeId, receptionBuffer.data(), bytesRead); - // arrayprinter::print(receptionBuffer.data(), bytesRead); +#if FSFW_UDP_WIRETAPPING_ENABLED == 1 + arrayprinter::print(receptionBuffer.data(), bytesRead);# +#endif if (result != HasReturnvaluesIF::RETURN_OK) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "TcSerialPollingTask::transferPusToSoftwareBus: Data " - "storage failed" << std::endl; - sif::error << "Packet size: " << bytesRead << std::endl; + sif::warning<< "TcSerialPollingTask::transferPusToSoftwareBus: Data " + "storage failed" << std::endl; + sif::warning << "Packet size: " << bytesRead << std::endl; #endif return HasReturnvaluesIF::RETURN_FAILED; } @@ -89,8 +86,7 @@ ReturnValue_t TcWinUdpPollingTask::handleSuccessfullTcRead(size_t bytesRead) { result = MessageQueueSenderIF::sendMessage(targetTcDestination, &message); if (result != HasReturnvaluesIF::RETURN_OK) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "Serial Polling: Sending message to queue failed" - << std::endl; + sif::warning << "Serial Polling: Sending message to queue failed" << std::endl; #endif tcStore->deleteData(storeId); } @@ -117,9 +113,9 @@ ReturnValue_t TcWinUdpPollingTask::initialize() { } serverUdpSocket = tmtcBridge->serverSocket; -#if FSFW_CPP_OSTREAM_ENABLED == 1 - //sif::info << "TcWinUdpPollingTask::initialize: Server UDP socket " - // << serverUdpSocket << std::endl; +#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_WIRETAPPING_ENABLED == 1 + sif::info << "TcWinUdpPollingTask::initialize: Server UDP socket " << serverUdpSocket << + std::endl; #endif return HasReturnvaluesIF::RETURN_OK; diff --git a/osal/windows/TcWinUdpPollingTask.h b/osal/windows/TcWinUdpPollingTask.h index 063a783e..8a9cf51f 100644 --- a/osal/windows/TcWinUdpPollingTask.h +++ b/osal/windows/TcWinUdpPollingTask.h @@ -8,6 +8,9 @@ #include +//! Debugging preprocessor define. +#define FSFW_UDP_RCV_WIRETAPPING_ENABLED 0 + /** * @brief This class can be used to implement the polling of a Unix socket, * using UDP for now. @@ -51,8 +54,7 @@ private: //! Reception flags: https://linux.die.net/man/2/recvfrom. int receptionFlags = 0; - //! Server socket, which is member of TMTC bridge and is assigned in - //! constructor + //! Server socket, which is member of TMTC bridge and is assigned in constructor SOCKET serverUdpSocket = 0; std::vector receptionBuffer; diff --git a/osal/windows/TmTcWinUdpBridge.cpp b/osal/windows/TmTcWinUdpBridge.cpp index d0de33d3..03daa6d7 100644 --- a/osal/windows/TmTcWinUdpBridge.cpp +++ b/osal/windows/TmTcWinUdpBridge.cpp @@ -14,7 +14,7 @@ TmTcWinUdpBridge::TmTcWinUdpBridge(object_id_t objectId, mutex = MutexFactory::instance()->createMutex(); communicationLinkUp = false; - // Initiates Winsock DLL. + /* Initiates Winsock DLL. */ WSAData wsaData; WORD wVersionRequested = MAKEWORD(2, 2); int err = WSAStartup(wVersionRequested, &wsaData); @@ -38,13 +38,14 @@ TmTcWinUdpBridge::TmTcWinUdpBridge(object_id_t objectId, setClientPort = clientPort; } - // Set up UDP socket: https://man7.org/linux/man-pages/man7/ip.7.html - //clientSocket = socket(AF_INET, SOCK_DGRAM, 0); + /* Set up UDP socket: + https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-socket + */ serverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(serverSocket == INVALID_SOCKET) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "TmTcWinUdpBridge::TmTcWinUdpBridge: Could not open" - " UDP socket!" << std::endl; + sif::warning << "TmTcWinUdpBridge::TmTcWinUdpBridge: Could not open UDP socket!" << + std::endl; #endif handleSocketError(); return; @@ -52,20 +53,27 @@ TmTcWinUdpBridge::TmTcWinUdpBridge(object_id_t objectId, serverAddress.sin_family = AF_INET; - // Accept packets from any interface. (potentially insecure). + /* Accept packets from any interface. (potentially insecure). */ serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); serverAddress.sin_port = htons(setServerPort); serverAddressLen = sizeof(serverAddress); - setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, + int result = setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&serverSocketOptions), sizeof(serverSocketOptions)); + if(result != 0) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "TmTcWinUdpBridge::TmTcWinUdpBridge: Could not set socket options!" << + std::endl; +#endif + handleSocketError(); + } clientAddress.sin_family = AF_INET; clientAddress.sin_addr.s_addr = htonl(INADDR_ANY); clientAddress.sin_port = htons(setClientPort); clientAddressLen = sizeof(clientAddress); - int result = bind(serverSocket, + result = bind(serverSocket, reinterpret_cast(&serverAddress), serverAddressLen); if(result != 0) { @@ -79,19 +87,19 @@ TmTcWinUdpBridge::TmTcWinUdpBridge(object_id_t objectId, } TmTcWinUdpBridge::~TmTcWinUdpBridge() { + closesocket(serverSocket); WSACleanup(); } ReturnValue_t TmTcWinUdpBridge::sendTm(const uint8_t *data, size_t dataLen) { int flags = 0; - //clientAddress.sin_addr.s_addr = htons(INADDR_ANY); - //clientAddressLen = sizeof(serverAddress); - -// char ipAddress [15]; -#if FSFW_CPP_OSTREAM_ENABLED == 1 -// sif::debug << "IP Address Sender: "<< inet_ntop(AF_INET, -// &clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl; +#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_SEND_WIRETAPPING_ENABLED == 1 + clientAddress.sin_addr.s_addr = htons(INADDR_ANY); + clientAddressLen = sizeof(serverAddress); + char ipAddress [15]; + sif::debug << "IP Address Sender: "<< inet_ntop(AF_INET, + &clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl; #endif ssize_t bytesSent = sendto(serverSocket, @@ -104,9 +112,9 @@ ReturnValue_t TmTcWinUdpBridge::sendTm(const uint8_t *data, size_t dataLen) { #endif handleSendError(); } -#if FSFW_CPP_OSTREAM_ENABLED == 1 -// sif::debug << "TmTcUnixUdpBridge::sendTm: " << bytesSent << " bytes were" -// " sent." << std::endl; +#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_SEND_WIRETAPPING_ENABLED == 1 + sif::debug << "TmTcUnixUdpBridge::sendTm: " << bytesSent << " bytes were" + " sent." << std::endl; #endif return HasReturnvaluesIF::RETURN_OK; } @@ -114,16 +122,16 @@ ReturnValue_t TmTcWinUdpBridge::sendTm(const uint8_t *data, size_t dataLen) { void TmTcWinUdpBridge::checkAndSetClientAddress(sockaddr_in newAddress) { MutexGuard lock(mutex, MutexIF::TimeoutType::WAITING, 10); -// char ipAddress [15]; -#if FSFW_CPP_OSTREAM_ENABLED == 1 -// sif::debug << "IP Address Sender: "<< inet_ntop(AF_INET, -// &newAddress.sin_addr.s_addr, ipAddress, 15) << std::endl; -// sif::debug << "IP Address Old: " << inet_ntop(AF_INET, -// &clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl; +#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_SEND_WIRETAPPING_ENABLED == 1 + char ipAddress [15]; + sif::debug << "IP Address Sender: "<< inet_ntop(AF_INET, + &newAddress.sin_addr.s_addr, ipAddress, 15) << std::endl; + sif::debug << "IP Address Old: " << inet_ntop(AF_INET, + &clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl; #endif registerCommConnect(); - // Set new IP address if it has changed. + /* Set new IP address if it has changed. */ if(clientAddress.sin_addr.s_addr != newAddress.sin_addr.s_addr) { clientAddress.sin_addr.s_addr = newAddress.sin_addr.s_addr; clientAddressLen = sizeof(clientAddress); @@ -135,8 +143,8 @@ void TmTcWinUdpBridge::handleSocketError() { switch(errCode) { case(WSANOTINITIALISED): { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::info << "TmTcWinUdpBridge::handleSocketError: WSANOTINITIALISED: " - << "WSAStartup(...) call necessary" << std::endl; + sif::warning << "TmTcWinUdpBridge::handleSocketError: WSANOTINITIALISED: WSAStartup" + " call necessary" << std::endl; #endif break; } @@ -146,8 +154,7 @@ void TmTcWinUdpBridge::handleSocketError() { windows-sockets-error-codes-2 */ #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::info << "TmTcWinUdpBridge::handleSocketError: Error code: " - << errCode << std::endl; + sif::warning << "TmTcWinUdpBridge::handleSocketError: Error code: " << errCode << std::endl; #endif break; } @@ -160,14 +167,14 @@ void TmTcWinUdpBridge::handleBindError() { case(WSANOTINITIALISED): { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::info << "TmTcWinUdpBridge::handleBindError: WSANOTINITIALISED: " - << "WSAStartup(...) call " << "necessary" << std::endl; + << "WSAStartup call necessary" << std::endl; #endif break; } case(WSAEADDRINUSE): { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::warning << "TmTcWinUdpBridge::handleBindError: WSAEADDRINUSE: " - << "Port is already in use!" << std::endl; + "Port is already in use!" << std::endl; #endif break; } diff --git a/osal/windows/TmTcWinUdpBridge.h b/osal/windows/TmTcWinUdpBridge.h index 8188039c..14354139 100644 --- a/osal/windows/TmTcWinUdpBridge.h +++ b/osal/windows/TmTcWinUdpBridge.h @@ -6,10 +6,13 @@ #include #include +//! Debugging preprocessor define. +#define FSFW_UDP_SEND_WIRETAPPING_ENABLED 0 + class TmTcWinUdpBridge: public TmTcBridge { friend class TcWinUdpPollingTask; public: - // The ports chosen here should not be used by any other process. + /* The ports chosen here should not be used by any other process. */ static constexpr uint16_t DEFAULT_UDP_SERVER_PORT = 7301; static constexpr uint16_t DEFAULT_UDP_CLIENT_PORT = 7302; @@ -38,6 +41,11 @@ private: //! by another task. MutexIF* mutex; + enum class ErrorSources { + SOCKET_CALL, + SETSOCKOPT_CALL + }; + void handleSocketError(); void handleBindError(); void handleSendError(); diff --git a/unittest/tests/action/TestActionHelper.cpp b/unittest/tests/action/TestActionHelper.cpp index a7adfc82..d8bd58c9 100644 --- a/unittest/tests/action/TestActionHelper.cpp +++ b/unittest/tests/action/TestActionHelper.cpp @@ -70,7 +70,7 @@ TEST_CASE( "Action Helper" , "[ActionHelper]") { SECTION("Handle finish"){ CHECK(not testMqMock.wasMessageSent()); ReturnValue_t status = 0x9876; - actionHelper.finish(true, testMqMock.getId(), testActionId, status); + actionHelper.finish(false, testMqMock.getId(), testActionId, status); CHECK(testMqMock.wasMessageSent()); CommandMessage testMessage; REQUIRE(testMqMock.receiveMessage(&testMessage) == static_cast(HasReturnvaluesIF::RETURN_OK)); diff --git a/unittest/tests/datapoollocal/CMakeLists.txt b/unittest/tests/datapoollocal/CMakeLists.txt index f196a080..1c98e7dc 100644 --- a/unittest/tests/datapoollocal/CMakeLists.txt +++ b/unittest/tests/datapoollocal/CMakeLists.txt @@ -3,4 +3,5 @@ target_sources(${TARGET_NAME} PRIVATE LocalPoolVectorTest.cpp DataSetTest.cpp LocalPoolManagerTest.cpp + LocalPoolOwnerBase.cpp ) diff --git a/unittest/tests/datapoollocal/DataSetTest.cpp b/unittest/tests/datapoollocal/DataSetTest.cpp index 56134595..ce4d9d43 100644 --- a/unittest/tests/datapoollocal/DataSetTest.cpp +++ b/unittest/tests/datapoollocal/DataSetTest.cpp @@ -4,13 +4,14 @@ #include #include +#include #include -#include +#include #include #include -TEST_CASE("LocalDataSet" , "[LocDataSetTest]") { +TEST_CASE("DataSetTest" , "[DataSetTest]") { LocalPoolOwnerBase* poolOwner = objectManager-> get(objects::TEST_LOCAL_POOL_OWNER_BASE); REQUIRE(poolOwner != nullptr); @@ -21,6 +22,7 @@ TEST_CASE("LocalDataSet" , "[LocDataSetTest]") { SECTION("BasicTest") { /* Test some basic functions */ + CHECK(localSet.getReportingEnabled() == false); CHECK(localSet.getLocalPoolIdsSerializedSize(false) == 3 * sizeof(lp_id_t)); CHECK(localSet.getLocalPoolIdsSerializedSize(true) == 3 * sizeof(lp_id_t) + sizeof(uint8_t)); @@ -54,7 +56,7 @@ TEST_CASE("LocalDataSet" , "[LocDataSetTest]") { { /* Test read operation. Values should be all zeros */ - PoolReadHelper readHelper(&localSet); + PoolReadGuard readHelper(&localSet); REQUIRE(readHelper.getReadResult() == retval::CATCH_OK); CHECK(not localSet.isValid()); CHECK(localSet.localPoolVarUint8.value == 0); @@ -79,10 +81,15 @@ TEST_CASE("LocalDataSet" , "[LocDataSetTest]") { localSet.localPoolVarUint8 = 0; localSet.localPoolVarFloat = 0; + localSet.setAllVariablesReadOnly(); + CHECK(localSet.localPoolUint16Vec.getReadWriteMode() == pool_rwm_t::VAR_READ); + CHECK(localSet.localPoolVarUint8.getReadWriteMode() == pool_rwm_t::VAR_READ); + CHECK(localSet.localPoolVarFloat.getReadWriteMode() == pool_rwm_t::VAR_READ); + { /* Now we read again and check whether our zeroed values were overwritten with the values in the pool */ - PoolReadHelper readHelper(&localSet); + PoolReadGuard readHelper(&localSet); REQUIRE(readHelper.getReadResult() == retval::CATCH_OK); CHECK(localSet.isValid()); CHECK(localSet.localPoolVarUint8.value == 232); @@ -201,6 +208,76 @@ TEST_CASE("LocalDataSet" , "[LocDataSetTest]") { } + SECTION("MorePoolVariables") { + LocalDataSet set(poolOwner, 2, 10); + + /* Register same variables again to get more than 8 registered variables */ + for(uint8_t idx = 0; idx < 8; idx ++) { + REQUIRE(set.registerVariable(&localSet.localPoolVarUint8) == retval::CATCH_OK); + } + REQUIRE(set.registerVariable(&localSet.localPoolVarUint8) == retval::CATCH_OK); + REQUIRE(set.registerVariable(&localSet.localPoolUint16Vec) == retval::CATCH_OK); + + set.setValidityBufferGeneration(true); + { + PoolReadGuard readHelper(&localSet); + localSet.localPoolVarUint8.value = 42; + localSet.localPoolVarUint8.setValid(true); + localSet.localPoolUint16Vec.setValid(false); + } + + size_t maxSize = set.getSerializedSize(); + CHECK(maxSize == 9 + sizeof(uint16_t) * 3 + 2); + size_t serSize = 0; + /* Already reserve additional space for validity buffer, will be needed later */ + uint8_t buffer[maxSize + 1]; + uint8_t* buffPtr = buffer; + CHECK(set.serialize(&buffPtr, &serSize, maxSize, + SerializeIF::Endianness::MACHINE) == retval::CATCH_OK); + std::array validityBuffer; + std::memcpy(validityBuffer.data(), buffer + 9 + sizeof(uint16_t) * 3, 2); + /* The first 9 variables should be valid */ + CHECK(validityBuffer[0] == 0xff); + CHECK(bitutil::bitGet(validityBuffer.data() + 1, 0) == true); + CHECK(bitutil::bitGet(validityBuffer.data() + 1, 1) == false); + + /* Now we invert the validity */ + validityBuffer[0] = 0; + validityBuffer[1] = 0b0100'0000; + std::memcpy(buffer + 9 + sizeof(uint16_t) * 3, validityBuffer.data(), 2); + const uint8_t* constBuffPtr = buffer; + size_t sizeToDeSerialize = serSize; + CHECK(set.deSerialize(&constBuffPtr, &sizeToDeSerialize, SerializeIF::Endianness::MACHINE) + == retval::CATCH_OK); + CHECK(localSet.localPoolVarUint8.isValid() == false); + CHECK(localSet.localPoolUint16Vec.isValid() == true); + } + + SECTION("SharedDataSet") { + object_id_t sharedSetId = objects::SHARED_SET_ID; + SharedLocalDataSet sharedSet(sharedSetId, poolOwner, lpool::testSetId, 5); + localSet.localPoolVarUint8.setReadWriteMode(pool_rwm_t::VAR_WRITE); + localSet.localPoolUint16Vec.setReadWriteMode(pool_rwm_t::VAR_WRITE); + CHECK(sharedSet.registerVariable(&localSet.localPoolVarUint8) == retval::CATCH_OK); + CHECK(sharedSet.registerVariable(&localSet.localPoolUint16Vec) == retval::CATCH_OK); + CHECK(sharedSet.initialize() == retval::CATCH_OK); + CHECK(sharedSet.lockDataset() == retval::CATCH_OK); + CHECK(sharedSet.unlockDataset() == retval::CATCH_OK); + + { + //PoolReadGuard rg(&sharedSet); + //CHECK(rg.getReadResult() == retval::CATCH_OK); + localSet.localPoolVarUint8.value = 5; + localSet.localPoolUint16Vec.value[0] = 1; + localSet.localPoolUint16Vec.value[1] = 2; + localSet.localPoolUint16Vec.value[2] = 3; + CHECK(sharedSet.commit() == retval::CATCH_OK); + } + + sharedSet.setReadCommitProtectionBehaviour(true); + + } + /* we need to reset the subscription list because the pool owner is a global object. */ CHECK(poolOwner->reset() == retval::CATCH_OK); diff --git a/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp b/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp index a10b4499..4df735cd 100644 --- a/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp +++ b/unittest/tests/datapoollocal/LocalPoolManagerTest.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include #include @@ -20,14 +20,21 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") { REQUIRE(poolOwner->initializeHkManager() == retval::CATCH_OK); REQUIRE(poolOwner->initializeHkManagerAfterTaskCreation() == retval::CATCH_OK); - //REQUIRE(poolOwner->dataset.assignPointers() == retval::CATCH_OK); + MessageQueueMockBase* mqMock = poolOwner->getMockQueueHandle(); REQUIRE(mqMock != nullptr); CommandMessage messageSent; uint8_t messagesSent = 0; - SECTION("BasicTest") { + { + /* For code coverage, should not crash */ + LocalDataPoolManager manager(nullptr, nullptr); + } + auto owner = poolOwner->poolManager.getOwner(); + REQUIRE(owner != nullptr); + CHECK(owner->getObjectId() == objects::TEST_LOCAL_POOL_OWNER_BASE); + /* Subscribe for message generation on update. */ REQUIRE(poolOwner->subscribeWrapperSetUpdate() == retval::CATCH_OK); /* Subscribe for an update message. */ @@ -72,10 +79,10 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") { } - SECTION("SnapshotUpdateTests") { + SECTION("SetSnapshotUpdateTest") { /* Set the variables in the set to certain values. These are checked later. */ { - PoolReadHelper readHelper(&poolOwner->dataset); + PoolReadGuard readHelper(&poolOwner->dataset); REQUIRE(readHelper.getReadResult() == retval::CATCH_OK); poolOwner->dataset.localPoolVarUint8.value = 5; poolOwner->dataset.localPoolVarFloat.value = -12.242; @@ -137,7 +144,69 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") { CHECK(cdsShort.msDay_ll == Catch::Approx(timeCdsNow.msDay_ll).margin(1)); } - SECTION("AdvancedTests") { + SECTION("VariableSnapshotTest") { + /* Acquire subscription interface */ + ProvidesDataPoolSubscriptionIF* subscriptionIF = poolOwner->getSubscriptionInterface(); + REQUIRE(subscriptionIF != nullptr); + + /* Subscribe for variable snapshot */ + REQUIRE(poolOwner->subscribeWrapperVariableSnapshot(lpool::uint8VarId) == retval::CATCH_OK); + auto poolVar = dynamic_cast*>( + poolOwner->getPoolObjectHandle(lpool::uint8VarId)); + REQUIRE(poolVar != nullptr); + + { + PoolReadGuard rg(poolVar); + CHECK(rg.getReadResult() == retval::CATCH_OK); + poolVar->value = 25; + } + + poolVar->setChanged(true); + + /* Store current time, we are going to check the (approximate) time equality later */ + CCSDSTime::CDS_short timeCdsNow; + timeval now; + Clock::getClock_timeval(&now); + CCSDSTime::convertToCcsds(&timeCdsNow, &now); + + REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK); + + /* Check update snapshot was sent. */ + REQUIRE(mqMock->wasMessageSent(&messagesSent) == true); + CHECK(messagesSent == 1); + + /* Should have been reset. */ + CHECK(poolVar->hasChanged() == false); + REQUIRE(mqMock->receiveMessage(&messageSent) == retval::CATCH_OK); + CHECK(messageSent.getCommand() == static_cast( + HousekeepingMessage::UPDATE_SNAPSHOT_VARIABLE)); + /* Now we deserialize the snapshot into a new dataset instance */ + CCSDSTime::CDS_short cdsShort; + lp_var_t varCopy = lp_var_t(lpool::uint8VarGpid); + HousekeepingSnapshot snapshot(&cdsShort, &varCopy); + store_address_t storeId; + HousekeepingMessage::getUpdateSnapshotVariableCommand(&messageSent, &storeId); + ConstAccessorPair accessorPair = tglob::getIpcStoreHandle()->getData(storeId); + REQUIRE(accessorPair.first == retval::CATCH_OK); + const uint8_t* readOnlyPtr = accessorPair.second.data(); + size_t sizeToDeserialize = accessorPair.second.size(); + CHECK(varCopy.value == 0); + /* Fill the dataset and timestamp */ + REQUIRE(snapshot.deSerialize(&readOnlyPtr, &sizeToDeserialize, + SerializeIF::Endianness::MACHINE) == retval::CATCH_OK); + CHECK(varCopy.value == 25); + + /* Now we check that both times are equal */ + CHECK(cdsShort.pField == timeCdsNow.pField); + CHECK(cdsShort.dayLSB == Catch::Approx(timeCdsNow.dayLSB).margin(1)); + CHECK(cdsShort.dayMSB == Catch::Approx(timeCdsNow.dayMSB).margin(1)); + CHECK(cdsShort.msDay_h == Catch::Approx(timeCdsNow.msDay_h).margin(1)); + CHECK(cdsShort.msDay_hh == Catch::Approx(timeCdsNow.msDay_hh).margin(1)); + CHECK(cdsShort.msDay_l == Catch::Approx(timeCdsNow.msDay_l).margin(1)); + CHECK(cdsShort.msDay_ll == Catch::Approx(timeCdsNow.msDay_ll).margin(1)); + } + + SECTION("VariableUpdateTest") { /* Acquire subscription interface */ ProvidesDataPoolSubscriptionIF* subscriptionIF = poolOwner->getSubscriptionInterface(); REQUIRE(subscriptionIF != nullptr); @@ -149,6 +218,7 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") { poolOwner->getPoolObjectHandle(lpool::uint8VarId)); REQUIRE(poolVar != nullptr); poolVar->setChanged(true); + REQUIRE(poolVar->hasChanged() == true); REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK); /* Check update notification was sent. */ @@ -190,6 +260,164 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") { REQUIRE(mqMock->receiveMessage(&messageSent) == static_cast(MessageQueueIF::EMPTY)); } + SECTION("PeriodicHKAndMessaging") { + /* Now we subcribe for a HK periodic generation. Even when it's difficult to simulate + the temporal behaviour correctly the HK manager should generate a HK packet + immediately and the periodic helper depends on HK op function calls anyway instead of + using the clock, so we could also just call performHkOperation multiple times */ + REQUIRE(poolOwner->subscribePeriodicHk(true) == retval::CATCH_OK); + REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK); + /* Now HK packet should be sent as message immediately. */ + REQUIRE(mqMock->wasMessageSent(&messagesSent) == true); + CHECK(messagesSent == 1); + CHECK(mqMock->popMessage() == retval::CATCH_OK); + + LocalPoolDataSetBase* setHandle = poolOwner->getDataSetHandle(lpool::testSid); + REQUIRE(setHandle != nullptr); + CHECK(poolOwner->poolManager.generateHousekeepingPacket(lpool::testSid, + setHandle, false) == retval::CATCH_OK); + REQUIRE(mqMock->wasMessageSent(&messagesSent) == true); + CHECK(messagesSent == 1); + CHECK(mqMock->popMessage() == retval::CATCH_OK); + + CHECK(setHandle->getReportingEnabled() == true); + CommandMessage hkCmd; + HousekeepingMessage::setToggleReportingCommand(&hkCmd, lpool::testSid, false, false); + CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK); + CHECK(setHandle->getReportingEnabled() == false); + REQUIRE(mqMock->wasMessageSent(&messagesSent) == true); + CHECK(messagesSent == 1); + CHECK(mqMock->popMessage() == retval::CATCH_OK); + + HousekeepingMessage::setToggleReportingCommand(&hkCmd, lpool::testSid, true, false); + CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK); + CHECK(setHandle->getReportingEnabled() == true); + REQUIRE(mqMock->wasMessageSent(&messagesSent) == true); + CHECK(mqMock->popMessage() == retval::CATCH_OK); + + HousekeepingMessage::setToggleReportingCommand(&hkCmd, lpool::testSid, false, false); + CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK); + CHECK(setHandle->getReportingEnabled() == false); + REQUIRE(mqMock->wasMessageSent(&messagesSent) == true); + CHECK(mqMock->popMessage() == retval::CATCH_OK); + + HousekeepingMessage::setCollectionIntervalModificationCommand(&hkCmd, + lpool::testSid, 0.4, false); + CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK); + /* For non-diagnostics and a specified minimum frequency of 0.2 seconds, the + resulting collection interval should be 1.0 second */ + CHECK(poolOwner->dataset.getCollectionInterval() == 1.0); + REQUIRE(mqMock->wasMessageSent(&messagesSent) == true); + CHECK(messagesSent == 1); + CHECK(mqMock->popMessage() == retval::CATCH_OK); + + HousekeepingMessage::setStructureReportingCommand(&hkCmd, lpool::testSid, false); + REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK); + CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK); + /* Now HK packet should be sent as message. */ + REQUIRE(mqMock->wasMessageSent(&messagesSent) == true); + CHECK(messagesSent == 1); + CHECK(mqMock->popMessage() == retval::CATCH_OK); + + HousekeepingMessage::setOneShotReportCommand(&hkCmd, lpool::testSid, false); + CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK); + REQUIRE(mqMock->wasMessageSent(&messagesSent) == true); + CHECK(messagesSent == 1); + CHECK(mqMock->popMessage() == retval::CATCH_OK); + + HousekeepingMessage::setUpdateNotificationSetCommand(&hkCmd, lpool::testSid); + sid_t sidToCheck; + store_address_t storeId; + CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK); + CHECK(poolOwner->changedDataSetCallbackWasCalled(sidToCheck, storeId) == true); + CHECK(sidToCheck == lpool::testSid); + + /* Now we test the handling is the dataset is set to diagnostic */ + poolOwner->dataset.setDiagnostic(true); + + HousekeepingMessage::setStructureReportingCommand(&hkCmd, lpool::testSid, false); + CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == + static_cast(LocalDataPoolManager::WRONG_HK_PACKET_TYPE)); + /* We still expect a failure message being sent */ + REQUIRE(mqMock->wasMessageSent(&messagesSent) == true); + CHECK(messagesSent == 1); + CHECK(mqMock->popMessage() == retval::CATCH_OK); + + HousekeepingMessage::setCollectionIntervalModificationCommand(&hkCmd, + lpool::testSid, 0.4, false); + CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == + static_cast(LocalDataPoolManager::WRONG_HK_PACKET_TYPE)); + REQUIRE(mqMock->wasMessageSent(&messagesSent) == true); + CHECK(messagesSent == 1); + CHECK(mqMock->popMessage() == retval::CATCH_OK); + + HousekeepingMessage::setStructureReportingCommand(&hkCmd, lpool::testSid, false); + CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == + static_cast(LocalDataPoolManager::WRONG_HK_PACKET_TYPE)); + REQUIRE(mqMock->wasMessageSent(&messagesSent) == true); + CHECK(messagesSent == 1); + CHECK(mqMock->popMessage() == retval::CATCH_OK); + + HousekeepingMessage::setStructureReportingCommand(&hkCmd, lpool::testSid, true); + CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK); + REQUIRE(mqMock->wasMessageSent(&messagesSent) == true); + CHECK(messagesSent == 1); + CHECK(mqMock->popMessage() == retval::CATCH_OK); + + HousekeepingMessage::setCollectionIntervalModificationCommand(&hkCmd, lpool::testSid, 0.4, + true); + CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK); + REQUIRE(mqMock->wasMessageSent(&messagesSent) == true); + CHECK(messagesSent == 1); + CHECK(mqMock->popMessage() == retval::CATCH_OK); + + HousekeepingMessage::setToggleReportingCommand(&hkCmd, lpool::testSid, true, true); + CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK); + REQUIRE(mqMock->wasMessageSent(&messagesSent) == true); + CHECK(messagesSent == 1); + CHECK(mqMock->popMessage() == retval::CATCH_OK); + + HousekeepingMessage::setToggleReportingCommand(&hkCmd, lpool::testSid, false, true); + CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK); + REQUIRE(mqMock->wasMessageSent(&messagesSent) == true); + CHECK(messagesSent == 1); + CHECK(mqMock->popMessage() == retval::CATCH_OK); + + HousekeepingMessage::setOneShotReportCommand(&hkCmd, lpool::testSid, false); + CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == + static_cast(LocalDataPoolManager::WRONG_HK_PACKET_TYPE)); + REQUIRE(mqMock->wasMessageSent(&messagesSent) == true); + CHECK(messagesSent == 1); + CHECK(mqMock->popMessage() == retval::CATCH_OK); + + HousekeepingMessage::setOneShotReportCommand(&hkCmd, lpool::testSid, true); + CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK); + REQUIRE(mqMock->wasMessageSent(&messagesSent) == true); + CHECK(messagesSent == 1); + CHECK(mqMock->popMessage() == retval::CATCH_OK); + + HousekeepingMessage::setUpdateNotificationVariableCommand(&hkCmd, lpool::uint8VarGpid); + gp_id_t gpidToCheck; + CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK); + CHECK(poolOwner->changedVariableCallbackWasCalled(gpidToCheck, storeId) == true); + CHECK(gpidToCheck == lpool::uint8VarGpid); + + HousekeepingMessage::setUpdateSnapshotSetCommand(&hkCmd, lpool::testSid, + storeId::INVALID_STORE_ADDRESS); + CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK); + CHECK(poolOwner->changedDataSetCallbackWasCalled(sidToCheck, storeId) == true); + CHECK(sidToCheck == lpool::testSid); + + HousekeepingMessage::setUpdateSnapshotVariableCommand(&hkCmd, lpool::uint8VarGpid, + storeId::INVALID_STORE_ADDRESS); + CHECK(poolOwner->poolManager.handleHousekeepingMessage(&hkCmd) == retval::CATCH_OK); + CHECK(poolOwner->changedVariableCallbackWasCalled(gpidToCheck, storeId) == true); + CHECK(gpidToCheck == lpool::uint8VarGpid); + + poolOwner->poolManager.printPoolEntry(lpool::uint8VarId); + + } + /* we need to reset the subscription list because the pool owner is a global object. */ CHECK(poolOwner->reset() == retval::CATCH_OK); diff --git a/unittest/tests/datapoollocal/LocalPoolOwnerBase.cpp b/unittest/tests/datapoollocal/LocalPoolOwnerBase.cpp new file mode 100644 index 00000000..991314a9 --- /dev/null +++ b/unittest/tests/datapoollocal/LocalPoolOwnerBase.cpp @@ -0,0 +1,141 @@ +#include "LocalPoolOwnerBase.h" + +LocalPoolOwnerBase::LocalPoolOwnerBase(object_id_t objectId): + SystemObject(objectId), poolManager(this, messageQueue), + dataset(this, lpool::testSetId) { + messageQueue = new MessageQueueMockBase(); +} + +LocalPoolOwnerBase::~LocalPoolOwnerBase() { + QueueFactory::instance()->deleteMessageQueue(messageQueue); +} + +ReturnValue_t LocalPoolOwnerBase::initializeHkManager() { + if(not initialized) { + initialized = true; + return poolManager.initialize(messageQueue); + } + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t LocalPoolOwnerBase::initializeLocalDataPool(localpool::DataPool &localDataPoolMap, + LocalDataPoolManager &poolManager) { + + // Default initialization empty for now. + localDataPoolMap.emplace(lpool::uint8VarId, + new PoolEntry({0})); + localDataPoolMap.emplace(lpool::floatVarId, + new PoolEntry({0})); + localDataPoolMap.emplace(lpool::uint32VarId, + new PoolEntry({0})); + + localDataPoolMap.emplace(lpool::uint16Vec3Id, + new PoolEntry({0, 0, 0})); + localDataPoolMap.emplace(lpool::int64Vec2Id, + new PoolEntry({0, 0})); + return HasReturnvaluesIF::RETURN_OK; +} + +LocalPoolObjectBase* LocalPoolOwnerBase::getPoolObjectHandle(lp_id_t localPoolId) { + if(localPoolId == lpool::uint8VarId) { + return &testUint8; + } + else if(localPoolId == lpool::uint16Vec3Id) { + return &testUint16Vec; + } + else if(localPoolId == lpool::floatVarId) { + return &testFloat; + } + else if(localPoolId == lpool::int64Vec2Id) { + return &testInt64Vec; + } + else if(localPoolId == lpool::uint32VarId) { + return &testUint32; + } + else { + return &testUint8; + } +} + +ReturnValue_t LocalPoolOwnerBase::reset() { + resetSubscriptionList(); + ReturnValue_t status = HasReturnvaluesIF::RETURN_OK; + { + PoolReadGuard readHelper(&dataset); + if(readHelper.getReadResult() != HasReturnvaluesIF::RETURN_OK) { + status = readHelper.getReadResult(); + } + dataset.localPoolVarUint8.value = 0; + dataset.localPoolVarFloat.value = 0.0; + dataset.localPoolUint16Vec.value[0] = 0; + dataset.localPoolUint16Vec.value[1] = 0; + dataset.localPoolUint16Vec.value[2] = 0; + dataset.setValidity(false, true); + } + + { + PoolReadGuard readHelper(&testUint32); + if(readHelper.getReadResult() != HasReturnvaluesIF::RETURN_OK) { + status = readHelper.getReadResult(); + } + testUint32.value = 0; + testUint32.setValid(false); + } + + { + PoolReadGuard readHelper(&testInt64Vec); + if(readHelper.getReadResult() != HasReturnvaluesIF::RETURN_OK) { + status = readHelper.getReadResult(); + } + testInt64Vec.value[0] = 0; + testInt64Vec.value[1] = 0; + testInt64Vec.setValid(false); + } + return status; +} + +bool LocalPoolOwnerBase::changedDataSetCallbackWasCalled(sid_t &sid, store_address_t &storeId) { + bool condition = false; + if(not this->changedDatasetSid.notSet()) { + condition = true; + } + sid = changedDatasetSid; + storeId = storeIdForChangedSet; + this->changedDatasetSid.raw = sid_t::INVALID_SID; + this->storeIdForChangedSet = storeId::INVALID_STORE_ADDRESS; + return condition; +} + +void LocalPoolOwnerBase::handleChangedDataset(sid_t sid, store_address_t storeId, + bool* clearMessage) { + this->changedDatasetSid = sid; + this->storeIdForChangedSet = storeId; +} + +bool LocalPoolOwnerBase::changedVariableCallbackWasCalled(gp_id_t &gpid, store_address_t &storeId) { + bool condition = false; + if(not this->changedPoolVariableGpid.notSet()) { + condition = true; + } + gpid = changedPoolVariableGpid; + storeId = storeIdForChangedVariable; + this->changedPoolVariableGpid.raw = gp_id_t::INVALID_GPID; + this->storeIdForChangedVariable = storeId::INVALID_STORE_ADDRESS; + return condition; +} + +ReturnValue_t LocalPoolOwnerBase::initializeHkManagerAfterTaskCreation() { + if(not initializedAfterTaskCreation) { + initializedAfterTaskCreation = true; + return poolManager.initializeAfterTaskCreation(); + } + return HasReturnvaluesIF::RETURN_OK; + +} + +void LocalPoolOwnerBase::handleChangedPoolVariable(gp_id_t globPoolId, store_address_t storeId, + bool* clearMessage) { + this->changedPoolVariableGpid = globPoolId; + this->storeIdForChangedVariable = storeId; +} + diff --git a/unittest/tests/datapoollocal/LocalPoolOwnerBase.h b/unittest/tests/datapoollocal/LocalPoolOwnerBase.h index 5c277850..8d2073b0 100644 --- a/unittest/tests/datapoollocal/LocalPoolOwnerBase.h +++ b/unittest/tests/datapoollocal/LocalPoolOwnerBase.h @@ -1,16 +1,17 @@ #ifndef FSFW_UNITTEST_TESTS_DATAPOOLLOCAL_LOCALPOOLOWNERBASE_H_ #define FSFW_UNITTEST_TESTS_DATAPOOLLOCAL_LOCALPOOLOWNERBASE_H_ +#include + #include #include #include #include #include #include -#include #include #include -#include "../../../datapool/PoolReadHelper.h" +#include namespace lpool { static constexpr lp_id_t uint8VarId = 0; @@ -36,10 +37,11 @@ class LocalPoolStaticTestDataSet: public StaticLocalDataSet<3> { public: LocalPoolStaticTestDataSet(): StaticLocalDataSet(lpool::testSid) { + } LocalPoolStaticTestDataSet(HasLocalDataPoolIF* owner, uint32_t setId): - StaticLocalDataSet(owner, setId) { + StaticLocalDataSet(owner, setId) { } lp_var_t localPoolVarUint8 = lp_var_t(lpool::uint8VarGpid, this); @@ -52,8 +54,7 @@ private: class LocalPoolTestDataSet: public LocalDataSet { public: LocalPoolTestDataSet(): - LocalDataSet(lpool::testSid, lpool::dataSetMaxVariables) { - } + LocalDataSet(lpool::testSid, lpool::dataSetMaxVariables) {} LocalPoolTestDataSet(HasLocalDataPoolIF* owner, uint32_t setId): LocalDataSet(owner, setId, lpool::dataSetMaxVariables) { @@ -63,42 +64,26 @@ public: lp_var_t localPoolVarFloat = lp_var_t(lpool::floatVarGpid, this); lp_vec_t localPoolUint16Vec = lp_vec_t(lpool::uint16Vec3Gpid, this); + void setDiagnostic(bool isDiagnostic) { + LocalPoolDataSetBase::setDiagnostic(isDiagnostic); + } private: }; class LocalPoolOwnerBase: public SystemObject, public HasLocalDataPoolIF { public: - LocalPoolOwnerBase( - object_id_t objectId = objects::TEST_LOCAL_POOL_OWNER_BASE): - SystemObject(objectId), poolManager(this, messageQueue), - dataset(this, lpool::testSetId) { - messageQueue = new MessageQueueMockBase(); - } + LocalPoolOwnerBase(object_id_t objectId = objects::TEST_LOCAL_POOL_OWNER_BASE); - ~LocalPoolOwnerBase() { - QueueFactory::instance()->deleteMessageQueue(messageQueue); - } + ~LocalPoolOwnerBase(); object_id_t getObjectId() const override { return SystemObject::getObjectId(); } - ReturnValue_t initializeHkManager() { - if(not initialized) { - initialized = true; - return poolManager.initialize(messageQueue); - } - return HasReturnvaluesIF::RETURN_OK; - } + ReturnValue_t initializeHkManager(); - ReturnValue_t initializeHkManagerAfterTaskCreation() { - if(not initializedAfterTaskCreation) { - initializedAfterTaskCreation = true; - return poolManager.initializeAfterTaskCreation(); - } - return HasReturnvaluesIF::RETURN_OK; - } + ReturnValue_t initializeHkManagerAfterTaskCreation(); /** Command queue for housekeeping messages. */ MessageQueueId_t getCommandQueue() const override { @@ -106,30 +91,15 @@ public: } // This is called by initializeAfterTaskCreation of the HK manager. - virtual ReturnValue_t initializeLocalDataPool( - localpool::DataPool& localDataPoolMap, - LocalDataPoolManager& poolManager) { - // Default initialization empty for now. - localDataPoolMap.emplace(lpool::uint8VarId, - new PoolEntry({0})); - localDataPoolMap.emplace(lpool::floatVarId, - new PoolEntry({0})); - localDataPoolMap.emplace(lpool::uint32VarId, - new PoolEntry({0})); - - localDataPoolMap.emplace(lpool::uint16Vec3Id, - new PoolEntry({0, 0, 0})); - localDataPoolMap.emplace(lpool::int64Vec2Id, - new PoolEntry({0, 0})); - return HasReturnvaluesIF::RETURN_OK; - } + virtual ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap, + LocalDataPoolManager& poolManager) override; LocalDataPoolManager* getHkManagerHandle() override { return &poolManager; } - uint32_t getPeriodicOperationFrequency() const override { - return 0; + dur_millis_t getPeriodicOperationFrequency() const override { + return 200; } /** @@ -142,32 +112,16 @@ public: return &dataset; } - virtual LocalPoolObjectBase* getPoolObjectHandle( - lp_id_t localPoolId) override { - if(localPoolId == lpool::uint8VarId) { - return &testUint8; - } - else if(localPoolId == lpool::uint16Vec3Id) { - return &testUint16Vec; - } - else if(localPoolId == lpool::floatVarId) { - return &testFloat; - } - else if(localPoolId == lpool::int64Vec2Id) { - return &testInt64Vec; - } - else if(localPoolId == lpool::uint32VarId) { - return &testUint32; - } - else { - return &testUint8; - } - } + virtual LocalPoolObjectBase* getPoolObjectHandle(lp_id_t localPoolId) override; MessageQueueMockBase* getMockQueueHandle() const { return dynamic_cast(messageQueue); } + ReturnValue_t subscribePeriodicHk(bool enableReporting) { + return poolManager.subscribeForPeriodicPacket(lpool::testSid, enableReporting, 0.2, false); + } + ReturnValue_t subscribeWrapperSetUpdate() { return poolManager.subscribeForSetUpdateMessage(lpool::testSetId, objects::NO_OBJECT, objects::HK_RECEIVER_MOCK, false); @@ -188,51 +142,33 @@ public: MessageQueueIF::NO_QUEUE, objects::HK_RECEIVER_MOCK, false); } - ReturnValue_t reset() { - resetSubscriptionList(); - ReturnValue_t status = HasReturnvaluesIF::RETURN_OK; - { - PoolReadGuard readHelper(&dataset); - if(readHelper.getReadResult() != HasReturnvaluesIF::RETURN_OK) { - status = readHelper.getReadResult(); - } - dataset.localPoolVarUint8.value = 0; - dataset.localPoolVarFloat.value = 0.0; - dataset.localPoolUint16Vec.value[0] = 0; - dataset.localPoolUint16Vec.value[1] = 0; - dataset.localPoolUint16Vec.value[2] = 0; - dataset.setValidity(false, true); - } - - { - PoolReadGuard readHelper(&testUint32); - if(readHelper.getReadResult() != HasReturnvaluesIF::RETURN_OK) { - status = readHelper.getReadResult(); - } - testUint32.value = 0; - testUint32.setValid(false); - } - - { - PoolReadGuard readHelper(&testInt64Vec); - if(readHelper.getReadResult() != HasReturnvaluesIF::RETURN_OK) { - status = readHelper.getReadResult(); - } - testInt64Vec.value[0] = 0; - testInt64Vec.value[1] = 0; - testInt64Vec.setValid(false); - } - return status; + ReturnValue_t subscribeWrapperVariableSnapshot(lp_id_t localPoolId) { + return poolManager.subscribeForVariableUpdateMessage(localPoolId, + MessageQueueIF::NO_QUEUE, objects::HK_RECEIVER_MOCK, true); } + ReturnValue_t reset(); + void resetSubscriptionList() { poolManager.clearReceiversList(); } + bool changedDataSetCallbackWasCalled(sid_t& sid, store_address_t& storeId); + bool changedVariableCallbackWasCalled(gp_id_t& gpid, store_address_t& storeId); + LocalDataPoolManager poolManager; LocalPoolTestDataSet dataset; private: + void handleChangedDataset(sid_t sid, store_address_t storeId, bool* clearMessage) override; + sid_t changedDatasetSid; + store_address_t storeIdForChangedSet; + + void handleChangedPoolVariable(gp_id_t globPoolId, store_address_t storeId, + bool* clearMessage) override; + gp_id_t changedPoolVariableGpid; + store_address_t storeIdForChangedVariable; + lp_var_t testUint8 = lp_var_t(this, lpool::uint8VarId); lp_var_t testFloat = lp_var_t(this, lpool::floatVarId); lp_var_t testUint32 = lp_var_t(this, lpool::uint32VarId); diff --git a/unittest/tests/datapoollocal/LocalPoolVariableTest.cpp b/unittest/tests/datapoollocal/LocalPoolVariableTest.cpp index e5a5d364..980ffda1 100644 --- a/unittest/tests/datapoollocal/LocalPoolVariableTest.cpp +++ b/unittest/tests/datapoollocal/LocalPoolVariableTest.cpp @@ -10,8 +10,7 @@ TEST_CASE("LocalPoolVariable" , "[LocPoolVarTest]") { get(objects::TEST_LOCAL_POOL_OWNER_BASE); REQUIRE(poolOwner != nullptr); REQUIRE(poolOwner->initializeHkManager() == retval::CATCH_OK); - REQUIRE(poolOwner->initializeHkManagerAfterTaskCreation() - == retval::CATCH_OK); + REQUIRE(poolOwner->initializeHkManagerAfterTaskCreation() == retval::CATCH_OK); SECTION("Basic Tests") { /* very basic test. */ diff --git a/unittest/tests/datapoollocal/LocalPoolVectorTest.cpp b/unittest/tests/datapoollocal/LocalPoolVectorTest.cpp index 2bc47568..db76fc00 100644 --- a/unittest/tests/datapoollocal/LocalPoolVectorTest.cpp +++ b/unittest/tests/datapoollocal/LocalPoolVectorTest.cpp @@ -115,6 +115,7 @@ TEST_CASE("LocalPoolVector" , "[LocPoolVecTest]") { REQUIRE(readOnlyVec.commit() == static_cast(PoolVariableIF::INVALID_READ_WRITE_MODE)); } + poolOwner->reset(); } diff --git a/unittest/tests/mocks/MessageQueueMockBase.h b/unittest/tests/mocks/MessageQueueMockBase.h index 7b810b41..3000f7fb 100644 --- a/unittest/tests/mocks/MessageQueueMockBase.h +++ b/unittest/tests/mocks/MessageQueueMockBase.h @@ -29,16 +29,24 @@ public: return tempMessageSent; } + /** + * Pop a message, clearing it in the process. + * @return + */ + ReturnValue_t popMessage() { + CommandMessage message; + message.clear(); + return receiveMessage(&message); + } + virtual ReturnValue_t reply( MessageQueueMessageIF* message ) { - //messageSent = true; - //lastMessage = *(dynamic_cast(message)); return sendMessage(myQueueId, message); - return HasReturnvaluesIF::RETURN_OK; }; virtual ReturnValue_t receiveMessage(MessageQueueMessageIF* message, MessageQueueId_t *receivedFrom) { return receiveMessage(message); } + virtual ReturnValue_t receiveMessage(MessageQueueMessageIF* message) { if(messagesSentQueue.empty()) { return MessageQueueIF::EMPTY; @@ -61,21 +69,13 @@ public: virtual ReturnValue_t sendMessageFrom( MessageQueueId_t sendTo, MessageQueueMessageIF* message, MessageQueueId_t sentFrom, bool ignoreFault = false ) { - //messageSent = true; - //lastMessage = *(dynamic_cast(message)); - //return HasReturnvaluesIF::RETURN_OK; return sendMessage(sendTo, message); } virtual ReturnValue_t sendToDefaultFrom( MessageQueueMessageIF* message, MessageQueueId_t sentFrom, bool ignoreFault = false ) { - //messageSent = true; - //lastMessage = *(dynamic_cast(message)); - //return HasReturnvaluesIF::RETURN_OK; return sendMessage(myQueueId, message); } virtual ReturnValue_t sendToDefault( MessageQueueMessageIF* message ) { - //messageSent = true; - //lastMessage = *(dynamic_cast(message)); return sendMessage(myQueueId, message); } virtual ReturnValue_t sendMessage( MessageQueueId_t sendTo, @@ -114,7 +114,6 @@ public: private: std::queue messagesSentQueue; - //MessageQueueMessage lastMessage; };