#ifndef FSFW_DATAPOOLLOCAL_LOCALDATAPOOLMANAGER_H_ #define FSFW_DATAPOOLLOCAL_LOCALDATAPOOLMANAGER_H_ #include "ProvidesDataPoolSubscriptionIF.h" #include "HasLocalDataPoolIF.h" #include "../serviceinterface/ServiceInterface.h" #include "../housekeeping/HousekeepingPacketDownlink.h" #include "../housekeeping/HousekeepingMessage.h" #include "../housekeeping/PeriodicHousekeepingHelper.h" #include "../datapool/DataSetIF.h" #include "../datapool/PoolEntry.h" #include "../objectmanager/SystemObjectIF.h" #include "../ipc/MutexIF.h" #include "../ipc/CommandMessage.h" #include "../ipc/MessageQueueIF.h" #include "../ipc/MutexHelper.h" #include #include namespace Factory { void setStaticFrameworkObjectIds(); } class LocalPoolDataSetBase; class HousekeepingPacketUpdate; /** * @brief This class is the managing instance for the local data pool. * @details * The actual data pool structure is a member of this class. Any class which * has a local data pool shall have this manager class as a member and implement * the HasLocalDataPoolIF. * * The manager offers some adaption points and functions which can be used * by the owning class to simplify data handling significantly. * * Please ensure that both initialize and initializeAfterTaskCreation are * called at some point by the owning class in the respective functions of the * same name! * * Users of the data pool use the helper classes LocalDataSet, * LocalPoolVariable and LocalPoolVector to access pool entries in * a thread-safe and efficient way. * * The local data pools employ a blackboard logic: Only the most recent * value is stored. The helper classes offer a read() and commit() interface * through the PoolVariableIF which is used to read and update values. * Each pool entry has a valid state too. * @author R. Mueller */ class LocalDataPoolManager: public ProvidesDataPoolSubscriptionIF, public AccessPoolManagerIF { friend void (Factory::setStaticFrameworkObjectIds)(); //! Some classes using the pool manager directly need to access class internals of the //! manager. The attorney provides granular control of access to these internals. friend class LocalDpManagerAttorney; public: static constexpr uint8_t INTERFACE_ID = CLASS_ID::HOUSEKEEPING_MANAGER; static constexpr ReturnValue_t QUEUE_OR_DESTINATION_INVALID = MAKE_RETURN_CODE(0); static constexpr ReturnValue_t WRONG_HK_PACKET_TYPE = MAKE_RETURN_CODE(1); static constexpr ReturnValue_t REPORTING_STATUS_UNCHANGED = MAKE_RETURN_CODE(2); static constexpr ReturnValue_t PERIODIC_HELPER_INVALID = MAKE_RETURN_CODE(3); static constexpr ReturnValue_t POOLOBJECT_NOT_FOUND = MAKE_RETURN_CODE(4); static constexpr ReturnValue_t DATASET_NOT_FOUND = MAKE_RETURN_CODE(5); /** * This constructor is used by a class which wants to implement * a personal local data pool. The queueToUse can be supplied if it * is already known. * * initialize() has to be called in any case before using the object! * @param owner * @param queueToUse * @param appendValidityBuffer Specify whether a buffer containing the * validity state is generated when serializing or deserializing packets. */ LocalDataPoolManager(HasLocalDataPoolIF* owner, MessageQueueIF* queueToUse, bool appendValidityBuffer = true); virtual~ LocalDataPoolManager(); /** * Assigns the queue to use. Make sure to call this in the #initialize * function of the owner. * @param queueToUse * @param nonDiagInvlFactor See #setNonDiagnosticIntervalFactor doc * @return */ ReturnValue_t initialize(MessageQueueIF* queueToUse); /** * Initializes the map by calling the map initialization function and * setting the periodic factor for non-diagnostic packets. * Don't forget to call this in the #initializeAfterTaskCreation call of * the owner, otherwise the map will be invalid! * @param nonDiagInvlFactor * @return */ ReturnValue_t initializeAfterTaskCreation( uint8_t nonDiagInvlFactor = 5); /** * @brief This should be called in the periodic handler of the owner. * @details * This in generally called in the #performOperation function of the owner. * It performs all the periodic functionalities of the data pool manager, * for example generating periodic HK packets. * Marked virtual as an adaption point for custom data pool managers. * @return */ virtual ReturnValue_t performHkOperation(); /** * @brief Subscribe for the generation of periodic packets. * @details * This subscription mechanism will generally be used by the data creator * to generate housekeeping packets which are downlinked directly. * @return */ ReturnValue_t subscribeForPeriodicPacket(sid_t sid, bool enableReporting, float collectionInterval, bool isDiagnostics, object_id_t packetDestination = defaultHkDestination) override; /** * @brief Subscribe for the generation of packets if the dataset * is marked as changed. * @details * This subscription mechanism will generally be used by the data creator. * @param sid * @param isDiagnostics * @param packetDestination * @return */ ReturnValue_t subscribeForUpdatePackets(sid_t sid, bool reportingEnabled, bool isDiagnostics, object_id_t packetDestination = defaultHkDestination) override; /** * @brief Subscribe for a notification message which will be sent * if a dataset has changed. * @details * This subscription mechanism will generally be used internally by * other software components. * @param setId Set ID of the set to receive update messages from. * @param destinationObject * @param targetQueueId * @param generateSnapshot If this is set to true, a copy of the current * data with a timestamp will be generated and sent via message. * Otherwise, only an notification message is sent. * @return */ ReturnValue_t subscribeForSetUpdateMessages(const uint32_t setId, object_id_t destinationObject, MessageQueueId_t targetQueueId, bool generateSnapshot) override; /** * @brief Subscribe for an notification message which will be sent if a * pool variable has changed. * @details * This subscription mechanism will generally be used internally by * other software components. * @param localPoolId Pool ID of the pool variable * @param destinationObject * @param targetQueueId * @param generateSnapshot If this is set to true, a copy of the current * data with a timestamp will be generated and sent via message. * Otherwise, only an notification message is sent. * @return */ ReturnValue_t subscribeForVariableUpdateMessages(const lp_id_t localPoolId, object_id_t destinationObject, MessageQueueId_t targetQueueId, bool generateSnapshot) override; MutexIF* getLocalPoolMutex() override; /** * Non-Diagnostics packets usually have a lower minimum sampling frequency * than diagnostic packets. * A factor can be specified to determine the minimum sampling frequency * for non-diagnostic packets. The minimum sampling frequency of the * diagnostics packets,which is usually jusst the period of the * performOperation calls, is multiplied with that factor. * @param factor */ void setNonDiagnosticIntervalFactor(uint8_t nonDiagInvlFactor); /** * @brief The manager is also able to handle housekeeping messages. * @details * This most commonly is used to handle messages for the housekeeping * interface, but the manager is also able to handle update notifications * and calls a special function which can be overriden by a child class * to handle data set or pool variable updates. This is relevant * for classes like controllers which have their own local datapool * but pull their data from other local datapools. * @param message * @return */ virtual ReturnValue_t handleHousekeepingMessage(CommandMessage* message); /** * Generate a housekeeping packet with a given SID. * @param sid * @return */ ReturnValue_t generateHousekeepingPacket(sid_t sid, LocalPoolDataSetBase* dataSet, bool forDownlink, MessageQueueId_t destination = MessageQueueIF::NO_QUEUE); HasLocalDataPoolIF* getOwner(); ReturnValue_t printPoolEntry(lp_id_t localPoolId); /** * Different types of housekeeping reporting are possible. * 1. PERIODIC: * HK packets are generated in fixed intervals and sent to * destination. Fromat will be raw. * 2. UPDATE_NOTIFICATION: * Notification will be sent out if HK data has changed. * 3. UPDATE_SNAPSHOT: * HK packets are only generated if explicitely requested. * Propably not necessary, just use multiple local data sets or * shared datasets. */ enum class ReportingType: uint8_t { //! Periodic generation of HK packets. PERIODIC, //! Housekeeping packet will be generated if values have changed. UPDATE_HK, //! Update notification will be sent out as message. UPDATE_NOTIFICATION, //! Notification will be sent out as message and a snapshot of the //! current data will be generated. UPDATE_SNAPSHOT, }; /** * Different data types are possible in the HK receiver map. * For example, updates can be requested for full datasets or * for single pool variables. Periodic reporting is only possible for * data sets. */ enum class DataType: uint8_t { LOCAL_POOL_VARIABLE, DATA_SET }; /* Copying forbidden */ LocalDataPoolManager(const LocalDataPoolManager &) = delete; LocalDataPoolManager operator=(const LocalDataPoolManager&) = delete; /** * This function can be used to clear the receivers list. This is * intended for test functions and not for regular operations, because * the insertion operations allocate dynamically. */ void clearReceiversList(); object_id_t getCreatorObjectId() const; virtual LocalDataPoolManager* getHkManagerHandle() override; private: LocalDataPool localPoolMap; //! Every housekeeping data manager has a mutex to protect access //! to it's data pool. MutexIF* mutex = nullptr; /** The class which actually owns the manager (and its datapool). */ HasLocalDataPoolIF* owner = nullptr; uint8_t nonDiagnosticIntervalFactor = 0; /** Default receiver for periodic HK packets */ static object_id_t defaultHkDestination; MessageQueueId_t hkDestinationId = MessageQueueIF::NO_QUEUE; union DataId { DataId(): sid() {}; sid_t sid; lp_id_t localPoolId; }; /** The data pool manager will keep an internal map of HK receivers. */ struct HkReceiver { /** Object ID of receiver */ object_id_t objectId = objects::NO_OBJECT; DataType dataType = DataType::DATA_SET; DataId dataId; ReportingType reportingType = ReportingType::PERIODIC; MessageQueueId_t destinationQueue = MessageQueueIF::NO_QUEUE; }; /** This vector will contain the list of HK receivers. */ using HkReceivers = std::vector; HkReceivers hkReceiversMap; struct HkUpdateResetHelper { DataType dataType = DataType::DATA_SET; DataId dataId; uint8_t updateCounter; uint8_t currentUpdateCounter; }; using HkUpdateResetList = std::vector; // Will only be created when needed. HkUpdateResetList* hkUpdateResetList = nullptr; /** This is the map holding the actual data. Should only be initialized * once ! */ bool mapInitialized = false; /** This specifies whether a validity buffer is appended at the end * of generated housekeeping packets. */ bool appendValidityBuffer = true; /** * @brief Queue used for communication, for example commands. * Is also used to send messages. Can be set either in the constructor * or in the initialize() function. */ MessageQueueIF* hkQueue = nullptr; /** Global IPC store is used to store all packets. */ StorageManagerIF* ipcStore = nullptr; /** * Get the pointer to the mutex. Can be used to lock the data pool * externally. Use with care and don't forget to unlock locked mutexes! * For now, only friend classes can accss this function. * @return */ MutexIF* getMutexHandle(); /** * Read a variable by supplying its local pool ID and assign the pool * entry to the supplied PoolEntry pointer. The type of the pool entry * is deduced automatically. This call is not thread-safe! * For now, only friend classes like LocalPoolVar may access this * function. * @tparam T Type of the pool entry * @param localPoolId Pool ID of the variable to read * @param poolVar [out] Corresponding pool entry will be assigned to the * supplied pointer. * @return */ template ReturnValue_t fetchPoolEntry(lp_id_t localPoolId, PoolEntry **poolEntry); /** * This function is used to fill the local data pool map with pool * entries. It should only be called once by the pool owner. * @param localDataPoolMap * @return */ ReturnValue_t initializeHousekeepingPoolEntriesOnce(); ReturnValue_t serializeHkPacketIntoStore( HousekeepingPacketDownlink& hkPacket, store_address_t& storeId, bool forDownlink, size_t* serializedSize); void performPeriodicHkGeneration(HkReceiver& hkReceiver); ReturnValue_t togglePeriodicGeneration(sid_t sid, bool enable, bool isDiagnostics); ReturnValue_t changeCollectionInterval(sid_t sid, float newCollectionInterval, bool isDiagnostics); ReturnValue_t generateSetStructurePacket(sid_t sid, bool isDiagnostics); void handleHkUpdateResetListInsertion(DataType dataType, DataId dataId); void handleChangeResetLogic(DataType type, DataId dataId, MarkChangedIF* toReset); void resetHkUpdateResetHelper(); ReturnValue_t handleHkUpdate(HkReceiver& hkReceiver, ReturnValue_t& status); ReturnValue_t handleNotificationUpdate(HkReceiver& hkReceiver, ReturnValue_t& status); ReturnValue_t handleNotificationSnapshot(HkReceiver& hkReceiver, ReturnValue_t& status); ReturnValue_t addUpdateToStore(HousekeepingPacketUpdate& updatePacket, store_address_t& storeId); void printWarningOrError(fsfw::OutputTypes outputType, const char* functionName, ReturnValue_t errorCode = HasReturnvaluesIF::RETURN_FAILED, const char* errorPrint = nullptr); }; template inline ReturnValue_t LocalDataPoolManager::fetchPoolEntry(lp_id_t localPoolId, PoolEntry **poolEntry) { auto poolIter = localPoolMap.find(localPoolId); if (poolIter == localPoolMap.end()) { printWarningOrError(fsfw::OutputTypes::OUT_ERROR, "fetchPoolEntry", HasLocalDataPoolIF::POOL_ENTRY_NOT_FOUND); return HasLocalDataPoolIF::POOL_ENTRY_NOT_FOUND; } *poolEntry = dynamic_cast< PoolEntry* >(poolIter->second); if(*poolEntry == nullptr) { printWarningOrError(fsfw::OutputTypes::OUT_ERROR, "fetchPoolEntry", HasLocalDataPoolIF::POOL_ENTRY_TYPE_CONFLICT); return HasLocalDataPoolIF::POOL_ENTRY_TYPE_CONFLICT; } return HasReturnvaluesIF::RETURN_OK; } #endif /* FSFW_DATAPOOLLOCAL_LOCALDATAPOOLMANAGER_H_ */