| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // ====================================================================== | ||
| 2 | // \title DpCatalog.hpp | ||
| 3 | // \author tcanham | ||
| 4 | // \brief hpp file for DpCatalog component implementation class | ||
| 5 | // ====================================================================== | ||
| 6 | |||
| 7 | #ifndef Svc_DpCatalog_HPP | ||
| 8 | #define Svc_DpCatalog_HPP | ||
| 9 | |||
| 10 | #include "Svc/DpCatalog/DpCatalogComponentAc.hpp" | ||
| 11 | #include "Svc/DpCatalog/DpCatalog_EntryLookupStatusEnumAc.hpp" | ||
| 12 | #include "Svc/DpCatalog/DpCatalog_PriorityChangeStatusEnumAc.hpp" | ||
| 13 | #include "Svc/DpCatalog/DpCatalog_StateFileUpdateStatusEnumAc.hpp" | ||
| 14 | #include "Svc/DpCatalog/DpCatalog_StateFileWriteStatusEnumAc.hpp" | ||
| 15 | #include "Svc/DpCatalog/DpRecordSerializableAc.hpp" | ||
| 16 | |||
| 17 | #include <Fw/DataStructures/ExternalArray.hpp> | ||
| 18 | #include <Fw/DataStructures/RedBlackTreeSet.hpp> | ||
| 19 | #include <Fw/Deprecate.hpp> | ||
| 20 | #include <Fw/Types/MemAllocator.hpp> | ||
| 21 | #include <limits> | ||
| 22 | |||
| 23 | #include <Fw/Types/FileNameString.hpp> | ||
| 24 | #include <config/DpCatalogCfg.hpp> | ||
| 25 | #include <config/DpCfg.hpp> | ||
| 26 | |||
| 27 | #define DIRECTORY_DELIMITER "/" | ||
| 28 | |||
| 29 | namespace Svc { | ||
| 30 | |||
| 31 | class DpCatalog final : public DpCatalogComponentBase { | ||
| 32 | friend class DpCatalogTester; | ||
| 33 | |||
| 34 | public: | ||
| 35 | // ---------------------------------------------------------------------- | ||
| 36 | // Component construction and destruction | ||
| 37 | // ---------------------------------------------------------------------- | ||
| 38 | |||
| 39 | /// @brief DpCatalog constructor | ||
| 40 | /// @param compName component name | ||
| 41 | DpCatalog(const char* const compName //!< The component name | ||
| 42 | ); | ||
| 43 | |||
| 44 | /// @brief DpCatalog destructor | ||
| 45 |
3/4✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
|
6 | ~DpCatalog() = default; |
| 46 | |||
| 47 | /// @brief Configure the DpCatalog | ||
| 48 | /// @param directories array of directories to scan; at most DP_MAX_DIRECTORIES entries | ||
| 49 | /// @param stateFile file to store transmit state. Provide a zero-length string if no state tracking | ||
| 50 | /// @param memId memory ID for allocator | ||
| 51 | /// @param allocator Allocator to supply memory for catalog. | ||
| 52 | /// Instance must survive for shutdown to use for reclaiming memory | ||
| 53 | void configure(const Fw::ExternalArray<Fw::FileNameString>& directories, | ||
| 54 | Fw::FileNameString& stateFile, | ||
| 55 | FwEnumStoreType memId, | ||
| 56 | Fw::MemAllocator& allocator); | ||
| 57 | |||
| 58 | /// @brief Configure the DpCatalog | ||
| 59 | /// @param directories list of directories to scan | ||
| 60 | /// @param numDirs number of supplied directories | ||
| 61 | /// @param stateFile file to store transmit state. Provide a zero-length string if no state tracking | ||
| 62 | /// @param memId memory ID for allocator | ||
| 63 | /// @param allocator Allocator to supply memory for catalog. | ||
| 64 | /// Instance must survive for shutdown to use for reclaiming memory | ||
| 65 | DEPRECATED(void configure(Fw::FileNameString directories[DP_MAX_DIRECTORIES], | ||
| 66 | FwSizeType numDirs, | ||
| 67 | Fw::FileNameString& stateFile, | ||
| 68 | FwEnumStoreType memId, | ||
| 69 | Fw::MemAllocator& allocator), | ||
| 70 | "Use configure(const Fw::ExternalArray<Fw::FileNameString>& directories, ...) instead"); | ||
| 71 | |||
| 72 | // @brief clean up component. | ||
| 73 | // Deallocates memory. | ||
| 74 | void shutdown(); | ||
| 75 | |||
| 76 | private: | ||
| 77 | // ---------------------------------------------------------------------- | ||
| 78 | // Handler implementations for user-defined typed input ports | ||
| 79 | // ---------------------------------------------------------------------- | ||
| 80 | |||
| 81 | //! Handler implementation for fileDone | ||
| 82 | //! | ||
| 83 | //! File Downlink send complete port | ||
| 84 | void fileDone_handler(FwIndexType portNum, //!< The port number | ||
| 85 | const Svc::SendFileResponse& resp) override; | ||
| 86 | |||
| 87 | //! Handler implementation for pingIn | ||
| 88 | //! | ||
| 89 | //! Ping input port | ||
| 90 | void pingIn_handler(FwIndexType portNum, //!< The port number | ||
| 91 | U32 key //!< Value to return to pinger | ||
| 92 | ) override; | ||
| 93 | |||
| 94 | //! Handler implementation for addToCat | ||
| 95 | //! | ||
| 96 | //! DP Writer Add File to Cat | ||
| 97 | void addToCat_handler(FwIndexType portNum, //!< The port number | ||
| 98 | const Fw::StringBase& fileName, //!< The file name | ||
| 99 | FwDpPriorityType priority, //!< The priority | ||
| 100 | FwSizeType size //!< The file size | ||
| 101 | ) override; | ||
| 102 | |||
| 103 | private: | ||
| 104 | // ---------------------------------------------------------------------- | ||
| 105 | // Handler implementations for commands | ||
| 106 | // ---------------------------------------------------------------------- | ||
| 107 | |||
| 108 | //! Handler implementation for command BUILD_CATALOG | ||
| 109 | //! | ||
| 110 | //! Build catalog from data product directory | ||
| 111 | void BUILD_CATALOG_cmdHandler(FwOpcodeType opCode, //!< The opcode | ||
| 112 | U32 cmdSeq //!< The command sequence number | ||
| 113 | ) override; | ||
| 114 | |||
| 115 | //! Handler implementation for command START_XMIT_CATALOG | ||
| 116 | //! | ||
| 117 | //! Start transmitting catalog | ||
| 118 | void START_XMIT_CATALOG_cmdHandler( | ||
| 119 | FwOpcodeType opCode, //!< The opcode | ||
| 120 | U32 cmdSeq, //!< The command sequence number | ||
| 121 | const Fw::Wait& wait, //!< have START_XMIT command wait for catalog to complete transmitting | ||
| 122 | bool remainActive //!< should the catalog resume transmission when Dps are added at runtime | ||
| 123 | ) override; | ||
| 124 | |||
| 125 | //! Handler implementation for command START_XMIT_CATALOG_WITH_PRIORITY | ||
| 126 | //! | ||
| 127 | //! Start transmitting the catalog products inside a priority range | ||
| 128 | void START_XMIT_CATALOG_WITH_PRIORITY_cmdHandler( | ||
| 129 | FwOpcodeType opCode, //!< The opcode | ||
| 130 | U32 cmdSeq, //!< The command sequence number | ||
| 131 | const Fw::Wait& wait, //!< have the command wait for the in-range products to finish transmitting | ||
| 132 | bool remainActive, //!< resume transmission when an in-range DP is added at runtime | ||
| 133 | FwDpPriorityType startPriority, //!< lowest priority value (most urgent) to transmit, inclusive | ||
| 134 | FwDpPriorityType endPriority //!< highest priority value (least urgent) to transmit, inclusive | ||
| 135 | ) override; | ||
| 136 | |||
| 137 | //! Handler implementation for command STOP_XMIT_CATALOG | ||
| 138 | //! | ||
| 139 | //! Stop transmitting catalog | ||
| 140 | void STOP_XMIT_CATALOG_cmdHandler(FwOpcodeType opCode, //!< The opcode | ||
| 141 | U32 cmdSeq //!< The command sequence number | ||
| 142 | ) override; | ||
| 143 | |||
| 144 | //! Handler implementation for command CLEAR_CATALOG | ||
| 145 | //! | ||
| 146 | //! clear existing catalog | ||
| 147 | void CLEAR_CATALOG_cmdHandler(FwOpcodeType opCode, //!< The opcode | ||
| 148 | U32 cmdSeq //!< The command sequence number | ||
| 149 | ) override; | ||
| 150 | |||
| 151 | //! Handler implementation for command SET_DP_PRIORITY | ||
| 152 | //! | ||
| 153 | //! Set the transmit priority of one data product; the change is recorded in the state file if one is configured | ||
| 154 | void SET_DP_PRIORITY_cmdHandler(FwOpcodeType opCode, //!< The opcode | ||
| 155 | U32 cmdSeq, //!< The command sequence number | ||
| 156 | FwDpIdType id, //!< Container ID of the data product | ||
| 157 | U32 tSec, //!< Seconds of the data product time tag | ||
| 158 | U32 tSub, //!< Microseconds of the data product time tag | ||
| 159 | FwDpPriorityType priority //!< New transmit priority | ||
| 160 | ) override; | ||
| 161 | |||
| 162 | // ---------------------------------- | ||
| 163 | // Private data structures | ||
| 164 | // ---------------------------------- | ||
| 165 | |||
| 166 | struct DpStateEntry { | ||
| 167 | friend class DpCatalogTester; | ||
| 168 | FwIndexType dir; //!< index to m_directories entry that has directory name where DP exists | ||
| 169 | DpRecord record; //!< data product metadata | ||
| 170 | |||
| 171 | /// @brief check whether the entry is the data product with the given identity (id and time tag) | ||
| 172 | /// @param id container ID | ||
| 173 | /// @param tSec seconds of the time tag | ||
| 174 | /// @param tSub microseconds of the time tag | ||
| 175 | /// @return true if the entry has that identity | ||
| 176 | bool hasIdentity(FwDpIdType id, U32 tSec, U32 tSub) const; | ||
| 177 | |||
| 178 | /// @brief check whether the entry is the same data product as another: same directory and identity | ||
| 179 | /// (id and time tag). The priority and state, which change, are not compared | ||
| 180 | /// @param other the entry to compare with | ||
| 181 | /// @return true if both entries are the same data product | ||
| 182 | bool isSameProduct(const DpStateEntry& other) const; | ||
| 183 | |||
| 184 | /// @brief compare two entries by transmit priority, then time tag, id, and directory (the catalog sort order) | ||
| 185 | /// @param left an entry to compare | ||
| 186 | /// @param right other entry to compare | ||
| 187 | /// @return -1 if left is higher priority, 0 if equal, and 1 if right is higher priority | ||
| 188 | static I8 compareEntries(const DpStateEntry& left, const DpStateEntry& right); | ||
| 189 | |||
| 190 | bool operator==(const DpStateEntry& other) const; | ||
| 191 | bool operator!=(const DpStateEntry& other) const; | ||
| 192 | bool operator>(const DpStateEntry& other) const; | ||
| 193 | bool operator<(const DpStateEntry& other) const; | ||
| 194 | }; | ||
| 195 | |||
| 196 | struct DpDstateFileEntry { | ||
| 197 | bool used; //!< if the entry is used | ||
| 198 | bool visited; //!< used for state file state; indicates that the entry was found in the search of current data | ||
| 199 | //!< products | ||
| 200 | DpStateEntry entry; //!< state entry from file | ||
| 201 | }; | ||
| 202 | |||
| 203 | // ---------------------------------- | ||
| 204 | // Private helpers | ||
| 205 | // ---------------------------------- | ||
| 206 | |||
| 207 | //! Status of a processFile() call | ||
| 208 | enum class ProcessFileStatus { | ||
| 209 | SUCCESS, //!< file added to the catalog | ||
| 210 | FAILED, //!< file could not be processed; continue with the next file | ||
| 211 | QUIT //!< catalog is full; stop processing files | ||
| 212 | }; | ||
| 213 | |||
| 214 | /// @brief determine in which managed directory a file resides | ||
| 215 | /// @param fullFile full path to file to be processed | ||
| 216 | /// @return directory index in m_directories; DP_MAX_DIRECTORIES if not in a managed dir | ||
| 217 | FwSizeType determineDirectory(const Fw::String& fullFile); | ||
| 218 | |||
| 219 | /// @brief add entry to sorted list and state file; called on each file in it & upon addToCat | ||
| 220 | /// @param fullFile full path to file to be processed | ||
| 221 | /// @param dir directory index in m_directories | ||
| 222 | /// @return QUIT to stop processing, FAILED for failure but continue, SUCCESS for success | ||
| 223 | ProcessFileStatus processFile(const Fw::String& fullFile, FwSizeType dir); | ||
| 224 | |||
| 225 | /// @brief insert an entry into the sorted catalog; if it exists, update the metadata | ||
| 226 | /// @param entry new entry | ||
| 227 | /// @return true if inserted successfully, false if the tree insert failed | ||
| 228 | bool insertEntry(DpStateEntry& entry); | ||
| 229 | |||
| 230 | /// @brief reset the catalog | ||
| 231 | void resetCatalog(); | ||
| 232 | |||
| 233 | /// #brief fill the binary tree from DP files | ||
| 234 | Fw::CmdResponse fillBinaryTree(); | ||
| 235 | |||
| 236 | /// @brief reset the state file data | ||
| 237 | void resetStateFileData(); | ||
| 238 | |||
| 239 | /// @brief get file state from the stored state file | ||
| 240 | /// @param entry entry to update from file state | ||
| 241 | void getFileState(DpStateEntry& entry); | ||
| 242 | |||
| 243 | /// @brief prune the state file data and write the remaining entries back | ||
| 244 | /// @return WRITTEN, or the error (also reported by event) | ||
| 245 | DpCatalog_StateFileWriteStatus pruneAndWriteStateFile(); | ||
| 246 | |||
| 247 | /// @brief load state data from file | ||
| 248 | Fw::CmdResponse loadStateFile(); | ||
| 249 | |||
| 250 | /// @brief get file state from the stored state file | ||
| 251 | /// @param entry entry to add to state file | ||
| 252 | void appendFileState(const DpStateEntry& entry); | ||
| 253 | |||
| 254 | /// @brief send the next entry to file downlink | ||
| 255 | void sendNextEntry(); | ||
| 256 | |||
| 257 | /// @brief find the next entry in the catalog inside the active priority range | ||
| 258 | /// @param entry entry to return | ||
| 259 | /// @return true if an in-range entry was found, false if no more entries are in range | ||
| 260 | bool findNextEntry(DpStateEntry& entry); | ||
| 261 | |||
| 262 | /// @brief check to see if component successfully initialized | ||
| 263 | /// @return bool if it was initialized | ||
| 264 | bool checkInit(); | ||
| 265 | |||
| 266 | /// @brief build catalog. Shared between command and port | ||
| 267 | /// @return command response for pass/fail | ||
| 268 | Fw::CmdResponse doCatalogBuild(); | ||
| 269 | |||
| 270 | /// @brief handle a START_XMIT_CATALOG or START_XMIT_CATALOG_WITH_PRIORITY command: validate the range, | ||
| 271 | /// then start transmitting and send (or arm, for a waited command) the command response | ||
| 272 | /// @param opCode the command opcode | ||
| 273 | /// @param cmdSeq the command sequence number | ||
| 274 | /// @param wait whether the command response waits for the transmission to complete | ||
| 275 | /// @param remainActive whether runtime additions resume transmission | ||
| 276 | /// @param startPriority lowest priority value to transmit, inclusive | ||
| 277 | /// @param endPriority highest priority value to transmit, inclusive | ||
| 278 | void startXmitCmd(FwOpcodeType opCode, | ||
| 279 | U32 cmdSeq, | ||
| 280 | const Fw::Wait& wait, | ||
| 281 | bool remainActive, | ||
| 282 | FwDpPriorityType startPriority, | ||
| 283 | FwDpPriorityType endPriority); | ||
| 284 | |||
| 285 | /// @brief start transmitting catalog. Called only by startXmitCmd, which validates the range | ||
| 286 | /// @param startPriority lowest priority value to transmit, inclusive | ||
| 287 | /// @param endPriority highest priority value to transmit, inclusive; must be >= startPriority | ||
| 288 | /// @return command response for pass/fail | ||
| 289 | Fw::CmdResponse doCatalogXmit(FwDpPriorityType startPriority, FwDpPriorityType endPriority); | ||
| 290 | |||
| 291 | /// @brief send a cmdResponse to the start xmit cmd if user waited | ||
| 292 | /// @param response the command response for pass/fail | ||
| 293 | void dispatchWaitedResponse(Fw::CmdResponse response); | ||
| 294 | |||
| 295 | /// @brief abandon the transmit: clear both transmit flags and answer a waited START_XMIT_CATALOG | ||
| 296 | /// @param response the command response for the waited command | ||
| 297 | void abortXmit(Fw::CmdResponse response); | ||
| 298 | |||
| 299 | /// @brief find the catalog entry for a data product by its stable identity (directory, id and time tag) | ||
| 300 | /// @param product the data product to find; its priority and state are not compared | ||
| 301 | /// @param entry the entry found | ||
| 302 | /// @return FOUND if an entry was found (and copied to entry), NOT_FOUND otherwise | ||
| 303 | DpCatalog_EntryLookupStatus findEntryByIdentity(const DpStateEntry& product, DpStateEntry& entry) const; | ||
| 304 | |||
| 305 | /// @brief find the first catalog entry of a data product named by id and time tag, as SET_DP_PRIORITY names it | ||
| 306 | /// @param id container ID | ||
| 307 | /// @param tSec seconds of the time tag | ||
| 308 | /// @param tSub microseconds of the time tag | ||
| 309 | /// @param entry the entry found | ||
| 310 | /// @return FOUND if an entry was found (and copied to entry), NOT_FOUND otherwise | ||
| 311 | DpCatalog_EntryLookupStatus findProductEntry(FwDpIdType id, U32 tSec, U32 tSub, DpStateEntry& entry) const; | ||
| 312 | |||
| 313 | /// @brief set the transmit priority of a catalog entry and record it in the state file, if one is configured | ||
| 314 | /// @param entry a copy of the catalog entry; updated to the new priority | ||
| 315 | /// @param priority the new priority | ||
| 316 | /// @return UNCHANGED if the entry already had the priority, CHANGED if it was set (and recorded if a state file | ||
| 317 | /// is configured), NOT_RECORDED if it was set but could not be recorded in the configured state file | ||
| 318 | DpCatalog_PriorityChangeStatus setDpPriority(DpStateEntry& entry, FwDpPriorityType priority); | ||
| 319 | |||
| 320 | /// @brief find the state file data slot for an entry's product (same directory, id and time tag) | ||
| 321 | /// @param entry the entry to look up | ||
| 322 | /// @return the slot index, or m_stateFileEntries if the product has no slot | ||
| 323 | FwSizeType findStateFileSlot(const DpStateEntry& entry) const; | ||
| 324 | |||
| 325 | /// @brief record an entry's state in the state file data, reusing the product's slot or a stale one | ||
| 326 | /// @param entry the entry to record | ||
| 327 | /// @return RECORDED, or FULL if the state file data has no free slot (StateFileFull is reported when a state | ||
| 328 | /// file is configured) | ||
| 329 | DpCatalog_StateFileUpdateStatus updateStateFileData(const DpStateEntry& entry); | ||
| 330 | |||
| 331 | // ---------------------------------- | ||
| 332 | // Private data | ||
| 333 | // ---------------------------------- | ||
| 334 | bool m_initialized = false; //!< set when the component has been initialized | ||
| 335 | |||
| 336 | Fw::RedBlackTreeSet<DpStateEntry, DP_MAX_FILES> m_dpCatalog; //!< The sorted catalog of DPs | ||
| 337 | DpStateEntry m_currentXmitEntry; //!< Entry currently being transmitted | ||
| 338 | bool m_hasCurrentXmit = false; //!< Whether m_currentXmitEntry is valid | ||
| 339 | U32 m_currXmitContext = 0; //!< FileDownlink context of the send in flight, echoed back in fileDone | ||
| 340 | |||
| 341 | FwSizeType m_numDpSlots = 0; //!< Stores the available number of record slots. | ||
| 342 | |||
| 343 | Fw::FileNameString m_directories[DP_MAX_DIRECTORIES]; //!< List of supplied DP directories | ||
| 344 | FwSizeType m_numDirectories = 0; //!< number of supplied directories | ||
| 345 | |||
| 346 | Fw::FileNameString m_stateFile; //!< file to store transmit state | ||
| 347 | DpDstateFileEntry* m_stateFileData = nullptr; //!< DP state loaded from file | ||
| 348 | FwSizeType m_stateFileEntries = 0; //!< size of state file data | ||
| 349 | |||
| 350 | FwSizeType m_memSize = 0; //!< size of allocated buffer | ||
| 351 | void* m_memPtr = nullptr; //!< stored for shutdown | ||
| 352 | FwEnumStoreType m_allocatorId = 0; //!< stored for shutdown | ||
| 353 | Fw::MemAllocator* m_allocator = nullptr; //!< stored for shutdown | ||
| 354 | |||
| 355 | bool m_catalogBuilt = false; //!< catalog build is complete (can add DPs at runtime) | ||
| 356 | bool m_xmitInProgress = false; //!< set if DP files are in the process of being sent | ||
| 357 | Fw::FileNameString m_currXmitFileName; //!< current file being transmitted | ||
| 358 | bool m_xmitCmdWait = false; //!< true if waiting for transmission complete to complete xmit command | ||
| 359 | U64 m_xmitBytes = 0; //!< bytes transmitted for downlink session | ||
| 360 | FwOpcodeType m_xmitOpCode = 0; //!< stored xmit command opcode | ||
| 361 | U32 m_xmitCmdSeq = 0; //!< stored command sequence id | ||
| 362 | |||
| 363 | U32 m_pendingFiles = 0; //!< Pending Files to Transmit | ||
| 364 | U64 m_pendingDpBytes = 0; //!< Pending Bytes to Transmit | ||
| 365 | |||
| 366 | bool m_remainActive = false; //!< Does the DpCat resume transmission when | ||
| 367 | //!< a runtime Dp is received after | ||
| 368 | //!< the in-range catalog is sent | ||
| 369 | |||
| 370 | FwDpPriorityType m_xmitStartPriority = 0; //!< Lowest priority value sent by the active/last transmit command | ||
| 371 | FwDpPriorityType m_xmitEndPriority = | ||
| 372 | std::numeric_limits<FwDpPriorityType>::max(); //!< Highest priority value sent by the active/last | ||
| 373 | //!< transmit command | ||
| 374 | }; | ||
| 375 | |||
| 376 | } // namespace Svc | ||
| 377 | |||
| 378 | #endif | ||
| 379 |