F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
Loading...
Searching...
No Matches
DpCatalog.cpp
Go to the documentation of this file.
1// ======================================================================
2// \title DpCatalog.cpp
3// \author tcanham
4// \brief cpp file for DpCatalog component implementation class
5// ======================================================================
6
7#include "FpConfig.hpp"
10
11#include "Os/FileSystem.hpp"
12#include "Os/File.hpp"
14#include <new> // placement new
15
16namespace Svc {
17
18 // ----------------------------------------------------------------------
19 // Component construction and destruction
20 // ----------------------------------------------------------------------
21
22 DpCatalog ::
23 DpCatalog(const char* const compName) :
24 DpCatalogComponentBase(compName),
25 m_initialized(false),
26 m_dpTree(nullptr),
27 m_freeListHead(nullptr),
28 m_freeListFoot(nullptr),
29 m_traverseStack(nullptr),
30 m_currentNode(nullptr),
31 m_currentXmitNode(nullptr),
32 m_numDpRecords(0),
33 m_numDpSlots(0),
34 m_numDirectories(0),
35 m_stateFileData(nullptr),
36 m_stateFileEntries(0),
37 m_memSize(0),
38 m_memPtr(nullptr),
39 m_allocatorId(0),
40 m_allocator(nullptr),
41 m_xmitInProgress(false),
42 m_xmitCmdWait(false),
43 m_xmitBytes(0),
44 m_xmitOpCode(0),
45 m_xmitCmdSeq(0)
46 {
47
48 }
49
50 DpCatalog ::
51 ~DpCatalog()
52 {}
53
56 FwSizeType numDirs,
57 Fw::FileNameString& stateFile,
58 NATIVE_UINT_TYPE memId,
59 Fw::MemAllocator& allocator
60 ) {
61
62 // Do some assertion checks
63 FW_ASSERT(numDirs <= DP_MAX_DIRECTORIES, static_cast<FwAssertArgType>(numDirs));
64 this->m_stateFile = stateFile;
65
66 // request memory for catalog which is DP_MAX_FILES * slot size.
67 //
68 // A "slot" consists of a set of three memory locations for each data product consisting
69 // an entry in the binary tree, an entry in the binary tree traversal stack, and
70 // an entry in the state file data. These may not be fully used in a given
71 // situation based on the number of actual data products, but this provides room for the
72 // maximum possible.
73 static const FwSizeType slotSize = sizeof(DpBtreeNode) + sizeof(DpBtreeNode**) + sizeof(DpDstateFileEntry);
74 this->m_memSize = DP_MAX_FILES * slotSize;
75 bool notUsed; // we don't need to recover the catalog.
76 // request memory. this->m_memSize will be modified if there is less than we requested
77 this->m_memPtr = allocator.allocate(memId, this->m_memSize, notUsed);
78 // adjust to actual size if less allocated and only initialize
79 // if there is enough room for at least one record and memory
80 // was allocated.
81
82 // Since we are given a monolithic block of memory, the data structures
83 // are interspersed in the memory using the following method:
84 //
85 // 1) Recompute how many slots can fit in the provided memory if we
86 // don't get the full amount requested. This allows for graceful degradation
87 // if there are memory issues.
88 //
89 // 2) Place the binary tree free list at the beginning of the memory.
90 //
91 // 3) Place the binary tree traverse stack in memory just after the binary
92 // tree free list by indexing the free list as an array one element past the
93 // end of the free list.
94 //
95 // 4) Place the state file data in memory after the binary tree traverse
96 // stack by indexing the traverse stack to one element past the end of
97 // the traverse tree.
98
99 if (
100 (this->m_memSize >= sizeof(DpBtreeNode)) and
101 (this->m_memPtr != nullptr)
102 ) {
103 // set the number of available record slots based on how much memory we actually got
104 this->m_numDpSlots = this->m_memSize / slotSize; // Step 1.
105 this->resetBinaryTree(); // Step 2
106 // assign pointer for the stack - Step 3
107 this->m_traverseStack = reinterpret_cast<DpBtreeNode**>(&this->m_freeListHead[this->m_numDpSlots]);
108 this->resetTreeStack();
109 // assign pointer for the state file storage - Step 4
110 this->m_stateFileData = reinterpret_cast<DpDstateFileEntry*>(&this->m_traverseStack[this->m_numDpSlots]);
111 }
112 else {
113 // if we don't have enough memory, set the number of records
114 // to zero for later detection
115 this->m_numDpSlots = 0;
116 }
117
118 // assign directory names
119 for (FwSizeType dir = 0; dir < numDirs; dir++) {
120 this->m_directories[dir] = directories[dir];
121 }
122 this->m_numDirectories = numDirs;
123
124 // store allocator
125 this->m_allocator = &allocator;
126 this->m_allocatorId = memId;
127 this->m_initialized = true;
128 }
129
130 void DpCatalog::resetBinaryTree() {
131 // initialize data structures in the free list
132 // Step 2 in memory partition (see configure() comments)
133 this->m_freeListHead = static_cast<DpBtreeNode*>(this->m_memPtr);
134 for (FwSizeType slot = 0; slot < this->m_numDpSlots; slot++) {
135 // overlay new instance of the DpState entry on the memory
136 (void) new(&this->m_freeListHead[slot]) DpBtreeNode();
137 this->m_freeListHead[slot].left = nullptr;
138 this->m_freeListHead[slot].right = nullptr;
139 // link the free list
140 if (slot > 0) {
141 this->m_freeListHead[slot-1].left = &this->m_freeListHead[slot];
142 }
143 }
144 // set the foot of the free list
145 this->m_freeListFoot = &this->m_freeListHead[this->m_numDpSlots - 1];
146 // clear binary tree
147 this->m_dpTree = nullptr;
148 // reset number of records
149 this->m_numDpRecords = 0;
150 }
151
152 void DpCatalog::resetStateFileData() {
153 // clear state file data
154 for (FwSizeType slot = 0; slot < this->m_numDpSlots; slot++) {
155 this->m_stateFileData[slot].used = false;
156 this->m_stateFileData[slot].visited = false;
157 (void) new(&this->m_stateFileData[slot].entry.record) DpRecord();
158 }
159 this->m_stateFileEntries = 0;
160 }
161
162 Fw::CmdResponse DpCatalog::loadStateFile() {
163
164 FW_ASSERT(this->m_stateFileData);
165
166 // Make sure that a file was specified
167 if (this->m_stateFile.length() == 0) {
169 return Fw::CmdResponse::OK;
170 }
171
172 // buffer for reading entries
173
175 Fw::ExternalSerializeBuffer entryBuffer(buffer, sizeof(buffer));
176
177 // open the state file
178 Os::File stateFile;
179 Os::File::Status stat = stateFile.open(this->m_stateFile.toChar(), Os::File::OPEN_READ);
180 if (stat != Os::File::OP_OK) {
181 this->log_WARNING_HI_StateFileOpenError(this->m_stateFile, stat);
183 }
184
185 FwSignedSizeType fileLoc = 0;
186 this->m_stateFileEntries = 0;
187
188 // read entries from the state file
189 for (FwSizeType entry = 0; entry < this->m_numDpSlots; entry++) {
190
191 FwSignedSizeType size = sizeof(buffer);
192 // read the directory index
193 stat = stateFile.read(buffer, size);
194 if (stat != Os::File::OP_OK) {
195 this->log_WARNING_HI_StateFileReadError(this->m_stateFile, stat, static_cast<I32>(fileLoc));
197 }
198
199 if (0 == size) {
200 // no more entries
201 break;
202 }
203
204 // check to see if the full entry was read. If not,
205 // abandon it and finish. We can at least operate on
206 // the entries that were read.
207 if (size != sizeof(buffer)) {
208 this->log_WARNING_HI_StateFileTruncated(this->m_stateFile, static_cast<I32>(fileLoc), static_cast<I32>(size));
209 return Fw::CmdResponse::OK;
210 }
211
212 // reset the buffer for deserializing the entry
213 entryBuffer.setBuffLen(static_cast<Fw::Serializable::SizeType>(size));
214 entryBuffer.resetDeser();
215
216 // deserialization after this point should always work, since
217 // the source buffer was specifically sized to hold the data
218
219 // Deserialize the file directory index
220 Fw::SerializeStatus status = entryBuffer.deserialize(this->m_stateFileData[entry].entry.dir);
221 FW_ASSERT(Fw::FW_SERIALIZE_OK == status,status);
222 status = entryBuffer.deserialize(this->m_stateFileData[entry].entry.record);
223 FW_ASSERT(Fw::FW_SERIALIZE_OK == status,status);
224 this->m_stateFileData[entry].used = true;
225 this->m_stateFileData[entry].visited = false;
226
227 // increment the file location
228 fileLoc += size;
229 this->m_stateFileEntries++;
230 }
231
232 return Fw::CmdResponse::OK;
233 }
234
235 void DpCatalog::getFileState(DpStateEntry& entry) {
236 FW_ASSERT(this->m_stateFileData);
237 // search the file state data for the entry
238 for (FwSizeType line = 0; line < this->m_stateFileEntries; line++) {
239 // check for a match
240 if (
241 (this->m_stateFileData[line].entry.dir == entry.dir) and
242 (this->m_stateFileData[line].entry.record.getid() == entry.record.getid()) and
243 (this->m_stateFileData[line].entry.record.gettSec() == entry.record.gettSec()) and
244 (this->m_stateFileData[line].entry.record.gettSub() == entry.record.gettSub()) and
245 (this->m_stateFileData[line].entry.record.getpriority() == entry.record.getpriority())
246 ) {
247 // update the transmitted state
248 entry.record.setstate(this->m_stateFileData[line].entry.record.getstate());
249 entry.record.setblocks(this->m_stateFileData[line].entry.record.getblocks());
250 // mark it as visited for later pruning if necessary
251 this->m_stateFileData[line].visited = true;
252 return;
253 }
254 }
255 }
256
257 void DpCatalog::pruneAndWriteStateFile() {
258 FW_ASSERT(this->m_stateFileData);
259
260 // There is a chance that a data product file can disappear after
261 // the state file is written from the last catalog build and transmit.
262 // This function will walk the state file data and write back only
263 // the entries that were visited during the last catalog build. This will
264 // remove any entries that are no longer valid.
265
266 // open the state file
267 Os::File stateFile;
268 // we open it as a new file so we don't accumulate invalid entries
269 Os::File::Status stat = stateFile.open(
270 this->m_stateFile.toChar(),
273
274 if (stat != Os::File::OP_OK) {
275 this->log_WARNING_HI_StateFileOpenError(this->m_stateFile, stat);
276 return;
277 }
278
279 // buffer for writing entries
281 Fw::ExternalSerializeBuffer entryBuffer(buffer, sizeof(buffer));
282
283 // write entries to the state file
284 for (FwSizeType entry = 0; entry < this->m_numDpSlots; entry++) {
285 // only write entries that were used
286 if (
287 (this->m_stateFileData[entry].used) and
288 (this->m_stateFileData[entry].visited)
289 ) {
290 // reset the buffer for serializing the entry
291 entryBuffer.resetSer();
292 // serialize the file directory index
293 entryBuffer.serialize(this->m_stateFileData[entry].entry.dir);
294 entryBuffer.serialize(this->m_stateFileData[entry].entry.record);
295 // write the entry
296 FwSignedSizeType size = entryBuffer.getBuffLength();
297 stat = stateFile.write(buffer, size);
298 if (stat != Os::File::OP_OK) {
299 this->log_WARNING_HI_StateFileWriteError(this->m_stateFile, stat);
300 return;
301 }
302 }
303 }
304
305 // close the state file
306 stateFile.close();
307 }
308
309 void DpCatalog::appendFileState(const DpStateEntry& entry) {
310 FW_ASSERT(this->m_stateFileData);
311
312 // We will append state to the existing state file
313 // FIXME: Have to handle case where state file has partially transmitted
314 // state already
315
316 // open the state file
317 Os::File stateFile;
318 // we open it as a new file so we don't accumulate invalid entries
319 Os::File::Status stat = stateFile.open(this->m_stateFile.toChar(), Os::File::OPEN_APPEND);
320 if (stat != Os::File::OP_OK) {
321 this->log_WARNING_HI_StateFileOpenError(this->m_stateFile, stat);
322 return;
323 }
324
325 // buffer for writing entries
327 Fw::ExternalSerializeBuffer entryBuffer(buffer, sizeof(buffer));
328 // reset the buffer for serializing the entry
329 entryBuffer.resetSer();
330 // serialize the file directory index
331 entryBuffer.serialize(entry.dir);
332 entryBuffer.serialize(entry.record);
333 // write the entry
334 FwSignedSizeType size = entryBuffer.getBuffLength();
335 stat = stateFile.write(buffer, size);
336 if (stat != Os::File::OP_OK) {
337 this->log_WARNING_HI_StateFileWriteError(this->m_stateFile, stat);
338 return;
339 }
340
341 // close the state file
342 stateFile.close();
343 }
344
345
346 Fw::CmdResponse DpCatalog::doCatalogBuild() {
347
348 // check initialization
349 if (not this->checkInit()) {
352 }
353
354 // check that initialization got memory
355 if (0 == this->m_numDpSlots) {
358 }
359
360 // make sure a downlink is not in progress
361 if (this->m_xmitInProgress) {
364 }
365
366 // reset state file data
367 this->resetStateFileData();
368
369 // load state data from file
370 Fw::CmdResponse response = this->loadStateFile();
371
372 // reset free list for entries
373 this->resetBinaryTree();
374
375 // fill the binary tree with DP files
376 response = this->fillBinaryTree();
377 if (response != Fw::CmdResponse::OK) {
378 return response;
379 }
380
381 // prune and rewrite the state file
382 this->pruneAndWriteStateFile();
383
385
386 return Fw::CmdResponse::OK;
387 }
388
389 Fw::CmdResponse DpCatalog::fillBinaryTree() {
390
391 // keep cumulative number of files
392 FwSizeType totalFiles = 0;
393
394 // file class instance for processing files
395 Os::File dpFile;
396 // Working buffer for DP headers
397 U8 dpBuff[Fw::DpContainer::MIN_PACKET_SIZE]; // Header buffer
398 Fw::Buffer hdrBuff(dpBuff, sizeof(dpBuff)); // buffer for container header decoding
399 Fw::DpContainer container; // container object for extracting header fields
400
401 // get file listings from file system
402 for (FwSizeType dir = 0; dir < this->m_numDirectories; dir++) {
403 // read in each directory and keep track of total
404 this->log_ACTIVITY_LO_ProcessingDirectory(this->m_directories[dir]);
405 FwSizeType filesRead = 0;
406 U32 pendingFiles = 0;
407 U64 pendingDpBytes = 0;
408 U32 filesProcessed = 0;
409
410 Os::Directory dpDir;
411 Os::Directory::Status status = dpDir.open(this->m_directories[dir].toChar(), Os::Directory::OpenMode::READ);
412 if (status != Os::Directory::OP_OK) {
414 this->m_directories[dir],
415 status
416 );
418 }
419 status = dpDir.readDirectory(this->m_fileList, (this->m_numDpSlots - totalFiles), filesRead);
420
421 if (status != Os::Directory::OP_OK) {
423 this->m_directories[dir],
424 status
425 );
427 }
428
429 // Assert number of files isn't more than asked
430 FW_ASSERT(
431 filesRead <= this->m_numDpSlots - totalFiles,
432 static_cast<FwAssertArgType>(filesRead),
433 static_cast<FwAssertArgType>(this->m_numDpSlots - totalFiles));
434
435 // extract metadata for each file
436 for (FwNativeUIntType file = 0; file < filesRead; file++) {
437
438 // only consider files with the DP extension
439
441 this->m_fileList[file].toChar(),
442 this->m_fileList[file].length(),
443 DP_EXT,
445 );
446
447 if (-1 == loc) {
448 continue;
449 }
450
451 Fw::String fullFile;
452 fullFile.format("%s/%s",
453 this->m_directories[dir].toChar(),
454 this->m_fileList[file].toChar()
455 );
456
457 this->log_ACTIVITY_LO_ProcessingFile(fullFile);
458
459 // get file size
460 FwSignedSizeType fileSize = 0;
461 Os::FileSystem::Status sizeStat =
462 Os::FileSystem::getFileSize(fullFile.toChar(),fileSize);
463 if (sizeStat != Os::FileSystem::OP_OK) {
464 this->log_WARNING_HI_FileSizeError(fullFile, sizeStat);
465 continue;
466 }
467
468 Os::File::Status stat = dpFile.open(fullFile.toChar(), Os::File::OPEN_READ);
469 if (stat != Os::File::OP_OK) {
470 this->log_WARNING_HI_FileOpenError(fullFile, stat);
471 continue;
472 }
473
474 // Read DP header
476
477 stat = dpFile.read(dpBuff, size);
478 if (stat != Os::File::OP_OK) {
479 this->log_WARNING_HI_FileReadError(fullFile, stat);
480 dpFile.close();
481 continue; // maybe next file is fine
482 }
483
484 // if full header isn't read, something's wrong with the file, so skip
485 if (size != Fw::DpContainer::Header::SIZE) {
487 dpFile.close();
488 continue; // maybe next file is fine
489 }
490
491 // if all is well, don't need the file any more
492 dpFile.close();
493
494 // give buffer to container instance
495 container.setBuffer(hdrBuff);
496
497 // reset header deserialization in the container
498 Fw::SerializeStatus desStat = container.deserializeHeader();
499 if (desStat != Fw::FW_SERIALIZE_OK) {
500 this->log_WARNING_HI_FileHdrDesError(fullFile, desStat);
501 }
502
503 // add entry to catalog.
504 DpStateEntry entry;
505 entry.dir = static_cast<FwIndexType>(dir);
506 entry.record.setid(container.getId());
507 entry.record.setpriority(container.getPriority());
508 entry.record.setstate(container.getState());
509 entry.record.settSec(container.getTimeTag().getSeconds());
510 entry.record.settSub(container.getTimeTag().getUSeconds());
511 entry.record.setsize(static_cast<U64>(fileSize));
512
513 // check the state file to see if there is transmit state
514 this->getFileState(entry);
515
516 // insert entry into sorted list. if can't insert, quit
517 if (not this->insertEntry(entry)) {
518 this->log_WARNING_HI_DpInsertError(entry.record);
519 break;
520 }
521
522 if (entry.record.getstate() == Fw::DpState::UNTRANSMITTED) {
523 pendingFiles++;
524 pendingDpBytes += entry.record.getsize();
525 }
526
527 // make sure we haven't exceeded the limit
528 if (this->m_numDpRecords > this->m_numDpSlots) {
529 this->log_WARNING_HI_DpCatalogFull(entry.record);
530 break;
531 }
532
533 filesProcessed++;
534
535 } // end for each file in a directory
536
537 totalFiles += filesProcessed;
538
540 this->m_directories[dir],
541 static_cast<U32>(totalFiles),
542 pendingFiles,
543 pendingDpBytes
544 );
545
546 // check to see if catalog is full
547 // that means generated products exceed the catalog size
548 if (totalFiles == this->m_numDpSlots) {
549 this->log_WARNING_HI_CatalogFull(this->m_directories[dir]);
550 break;
551 }
552 } // end for each directory
553
554 return Fw::CmdResponse::OK;
555
556 } // end fillBinaryTree()
557
558
559 bool DpCatalog::insertEntry(DpStateEntry& entry) {
560
561 // the tree is filled in the following priority order:
562 // 1. DP priority - lower number is higher priority
563 // 2. DP time - older is higher priority
564 // 3. DP ID - lower number is higher priority
565
566 // Higher priority is to the left of the tree
567
568 // if the tree is empty, add the first entry
569 if (this->m_dpTree == nullptr) {
570 this->allocateNode(this->m_dpTree,entry);
571 // otherwise, search depth-first to sort the entry
572 } else {
573 // to avoid recursion, loop through a max of the number of available records
574 DpBtreeNode* node = this->m_dpTree;
575 for (FwSizeType record = 0; record < this->m_numDpSlots; record++) {
576 CheckStat stat = CheckStat::CHECK_CONT;
577 // check priority. Lower is higher priority
578 if (entry.record.getpriority() == node->entry.record.getpriority()) {
579 // check time. Older is higher priority
580 if (entry.record.gettSec() == node->entry.record.gettSec()) {
581 // check ID. Lower is higher priority
582 stat = this->checkLeftRight(
583 entry.record.getid() < node->entry.record.getid(),
584 node,
585 entry
586 );
587 } else { // if seconds are not equal. Older is higher priority
588 stat = this->checkLeftRight(
589 entry.record.gettSec() < node->entry.record.gettSec(),
590 node,
591 entry
592 );
593 }
594 } else { // if priority is not equal. Lower is higher priority.
595 stat = this->checkLeftRight(
596 entry.record.getpriority() < node->entry.record.getpriority(),
597 node,
598 entry
599 );
600 } // end checking for left/right insertion
601
602 // act on status
603 if (stat == CheckStat::CHECK_ERROR) {
604 return false;
605 } else if (stat == CheckStat::CHECK_OK) {
606 break;
607 }
608 } // end for each possible record
609 }
610
611 // increment the number of records
612
613 this->m_numDpRecords++;
614
615 return true;
616
617 }
618
619 DpCatalog::CheckStat DpCatalog::checkLeftRight(bool condition, DpBtreeNode* &node, const DpStateEntry& newEntry) {
620 if (condition) {
621 if (node->left == nullptr) {
622 if (!this->allocateNode(node->left,newEntry)) {
623 return CheckStat::CHECK_ERROR;
624 }
625 return CheckStat::CHECK_OK;
626 } else {
627 node = node->left;
628 return CheckStat::CHECK_CONT;
629 }
630 } else {
631 if (node->right == nullptr) {
632 if (!this->allocateNode(node->right,newEntry)) {
633 return CheckStat::CHECK_ERROR;
634 }
635 return CheckStat::CHECK_OK;
636 } else {
637 node = node->right;
638 return CheckStat::CHECK_CONT;
639 }
640 }
641 }
642
643
644 bool DpCatalog::allocateNode(DpBtreeNode* &newNode,
645 const DpStateEntry& newEntry) {
646 // should always be null since we are allocating an empty slot
647 FW_ASSERT(newNode == nullptr);
648 // make sure there is an entry from the free list
649 if (this->m_freeListHead == nullptr) {
650 this->log_WARNING_HI_DpCatalogFull(newEntry.record);
651 return false;
652 }
653 // get a new node from the free list
654 newNode = this->m_freeListHead;
655 // move the head of the free list to the next node
656 this->m_freeListHead = this->m_freeListHead->left;
657
658 // initialize the new node
659 newNode->left = nullptr;
660 newNode->right = nullptr;
661 newNode->entry = newEntry;
662
663 // we got one, so return success
664 return true;
665
666 }
667
668
669 void DpCatalog::deleteEntry(DpStateEntry& entry) {
670
671 }
672
673 void DpCatalog::sendNextEntry() {
674
675 // check some asserts
676 FW_ASSERT(this->m_dpTree);
677 FW_ASSERT(this->m_xmitInProgress);
678 FW_ASSERT(this->m_traverseStack);
679
680 // look in the tree for the next entry to send
681 this->m_currentXmitNode = this->findNextTreeNode();
682
683 if (this->m_currentXmitNode == nullptr) {
684 // if no entry found, we are done
685 this->m_xmitInProgress = false;
686 this->log_ACTIVITY_HI_CatalogXmitCompleted(this->m_xmitBytes);
687 if (this->m_xmitCmdWait) {
688 this->cmdResponse_out(this->m_xmitOpCode,this->m_xmitCmdSeq,Fw::CmdResponse::OK);
689 }
690 return;
691 } else {
692 // build file name based on the found entry
693 this->m_currXmitFileName.format(DP_FILENAME_FORMAT,
694 this->m_directories[this->m_currentXmitNode->entry.dir].toChar(),
695 this->m_currentXmitNode->entry.record.getid(),
696 this->m_currentXmitNode->entry.record.gettSec(),
697 this->m_currentXmitNode->entry.record.gettSub()
698 );
700 this->m_currXmitFileName,
701 static_cast<U32>(this->m_currentXmitNode->entry.record.getsize()),
702 this->m_currentXmitNode->entry.record.getpriority()
703 );
704 this->fileOut_out(0, this->m_currXmitFileName, this->m_currXmitFileName, 0, 0);
705 }
706
707 } // end sendNextEntry()
708
709 DpCatalog::DpBtreeNode* DpCatalog::findNextTreeNode() {
710
711 // check some asserts
712 FW_ASSERT(this->m_dpTree);
713 FW_ASSERT(this->m_xmitInProgress);
714 FW_ASSERT(this->m_traverseStack);
715
716 DpBtreeNode* found = nullptr;
717
718 // traverse the tree, finding nodes in order. Max iteration of the loop
719 // would be the number of records in the tree
720 for (FwSizeType record = 0; record < this->m_numDpRecords; record++) {
721 // initialize found entry to nullptr
722 found = nullptr;
723 // check for current node to be null
724 if (this->m_currentNode == nullptr) {
725 // see if we fully traversed the tree
726 if (this->m_currStackEntry < 0) {
727 // Step 5 - we are done
728 return nullptr;
729 } else {
730 // Step 4 - if the current node is null, pop back up the stack
731 this->m_currentNode = this->m_traverseStack[this->m_currStackEntry--];
732 if (this->m_currentNode->entry.record.getstate() != Fw::DpState::TRANSMITTED) {
733 found = this->m_currentNode;
734 }// check if transmitted
735 this->m_currentNode = this->m_currentNode->right;
736 if (found != nullptr) {
737 return found;
738 }
739 }
740 break;
741 } else {
742 if (this->m_currentNode->left != nullptr) {
743 // Step 3 - push current entry on the stack
744 this->m_traverseStack[++this->m_currStackEntry] = this->m_currentNode;
745 this->m_currentNode = this->m_currentNode->left;
746 } else {
747 // Step 4 - check to see if this node has already been transmitted, if so, pop back up the stack
748 if (this->m_currentNode->entry.record.getstate() != Fw::DpState::TRANSMITTED) {
749 // we found an entry, so set the return to the current node
750 found = this->m_currentNode;
751 } // check if transmitted
752 // go to the right node
753 this->m_currentNode = this->m_currentNode->right;
754 // if a node was found, return it
755 if (found != nullptr) {
756 return found;
757 }
758 } // check if left is null
759 } // end else current node is not null
760 } // end for each possible node in the tree
761
762 return found;
763 }
764
765 bool DpCatalog::checkInit() {
766 if (not this->m_initialized) {
768 return false;
769 }
770 else if (0 == this->m_numDpSlots) {
772 return false;
773 }
774
775 return true;
776 }
777
779 // only try to deallocate if both pointers are non-zero
780 // it's a way to more gracefully shut down if there are missing
781 // pointers
782 if ((this->m_allocator != nullptr) and (this->m_memPtr != nullptr)) {
783 this->m_allocator->deallocate(this->m_allocatorId, this->m_memPtr);
784 }
785
786 }
787
788
789 // ----------------------------------------------------------------------
790 // Handler implementations for user-defined typed input ports
791 // ----------------------------------------------------------------------
792
793 void DpCatalog ::
794 fileDone_handler(
795 NATIVE_INT_TYPE portNum,
796 const Svc::SendFileResponse& resp
797 )
798 {
799 // check some asserts
800 FW_ASSERT(this->m_dpTree);
801 FW_ASSERT(this->m_traverseStack);
802
803 // check file status
805 this->log_WARNING_HI_StateFileXmitError(this->m_currXmitFileName,resp.getstatus());
806 this->m_xmitInProgress = false;
807 this->cmdResponse_out(this->m_xmitOpCode,this->m_xmitCmdSeq,Fw::CmdResponse::EXECUTION_ERROR);
808 }
809
810 this->log_ACTIVITY_LO_ProductComplete(this->m_currXmitFileName);
811
812 // mark the entry as transmitted
813 this->m_currentXmitNode->entry.record.setstate(Fw::DpState::TRANSMITTED);
814 // update the transmitted state in the state file
815 this->appendFileState(this->m_currentXmitNode->entry);
816 // add the size
817 this->m_xmitBytes += this->m_currentXmitNode->entry.record.getsize();
818 // send the next entry, if it exists
819 this->sendNextEntry();
820 }
821
822 void DpCatalog ::
823 pingIn_handler(
824 NATIVE_INT_TYPE portNum,
825 U32 key
826 )
827 {
828 // return code for health ping
829 this->pingOut_out(0, key);
830 }
831
832 // ----------------------------------------------------------------------
833 // Handler implementations for commands
834 // ----------------------------------------------------------------------
835
836 void DpCatalog ::
837 BUILD_CATALOG_cmdHandler(
838 FwOpcodeType opCode,
839 U32 cmdSeq
840 )
841 {
842 // invoke helper
843 this->cmdResponse_out(opCode, cmdSeq,this->doCatalogBuild());
844
845 }
846
847 void DpCatalog ::
848 START_XMIT_CATALOG_cmdHandler(
849 FwOpcodeType opCode,
850 U32 cmdSeq,
851 Fw::Wait wait
852 )
853 {
854
855 Fw::CmdResponse resp = this->doCatalogXmit();
856
857 if (resp != Fw::CmdResponse::OK) {
858 this->cmdResponse_out(opCode, cmdSeq, resp);
859 } else {
860 if (Fw::Wait::NO_WAIT == wait) {
861 this->cmdResponse_out(opCode, cmdSeq, resp);
862 this->m_xmitCmdWait = false;
863 this->m_xmitOpCode = 0;
864 this->m_xmitCmdSeq = 0;
865 }
866 else {
867 this->m_xmitCmdWait = true;
868 this->m_xmitOpCode = opCode;
869 this->m_xmitCmdSeq = cmdSeq;
870 }
871 }
872
873 }
874
875 Fw::CmdResponse DpCatalog::doCatalogXmit() {
876
877 // check initialization
878 if (not this->checkInit()) {
881 }
882
883 // check that initialization got memory
884 if (0 == this->m_numDpSlots) {
887 }
888
889 // make sure a downlink is not in progress
890 if (this->m_xmitInProgress) {
893 }
894
895 // start transmission
896 this->m_xmitBytes = 0;
897
898 // make sure we have valid pointers
899 FW_ASSERT(this->m_dpTree);
900 FW_ASSERT(this->m_traverseStack);
901
902 // Traverse the tree using a stack to avoid recursion
903 // https://codestandard.net/articles/binary-tree-inorder-traversal/
904
905 this->resetTreeStack();
906 this->m_xmitInProgress = true;
907 // Step 3b - search for and send first entry
908 this->sendNextEntry();
909 return Fw::CmdResponse::OK;
910
911 }
912
913 void DpCatalog::resetTreeStack() {
914 // See URL above
915 // Step 1 - reset the stack
916 this->m_currStackEntry = -1;
917 // Step 2 - assign root of the tree to the current entry
918 this->m_currentNode = this->m_dpTree;
919 }
920
921 void DpCatalog ::
922 STOP_XMIT_CATALOG_cmdHandler(
923 FwOpcodeType opCode,
924 U32 cmdSeq
925 )
926 {
927 if (not this->m_xmitInProgress) {
928 this->log_WARNING_LO_XmitNotActive();
929 // benign error, so don't fail the command
930 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
931 } else {
932 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
933 }
934 }
935
936 void DpCatalog ::
937 CLEAR_CATALOG_cmdHandler(
938 FwOpcodeType opCode,
939 U32 cmdSeq
940 )
941 {
942 // TODO
943 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
944 }
945
946
947}
#define FW_ASSERT(...)
Definition Assert.hpp:14
PlatformIntType NATIVE_INT_TYPE
Definition BasicTypes.h:55
U8 BYTE
byte type
Definition BasicTypes.h:31
uint8_t U8
8-bit unsigned integer
Definition BasicTypes.h:30
PlatformUIntType NATIVE_UINT_TYPE
Definition BasicTypes.h:56
constexpr const char * DP_FILENAME_FORMAT
Definition DpCfg.hpp:21
#define DP_EXT
Definition DpCfg.hpp:20
PlatformAssertArgType FwAssertArgType
Definition FpConfig.h:39
PlatformUIntType FwNativeUIntType
Definition FpConfig.h:47
PlatformSignedSizeType FwSignedSizeType
Definition FpConfig.h:30
U32 FwOpcodeType
Definition FpConfig.h:91
PlatformSizeType FwSizeType
Definition FpConfig.h:35
PlatformIndexType FwIndexType
Definition FpConfig.h:25
C++-compatible configuration header for fprime configuration.
Enum representing a command response.
@ EXECUTION_ERROR
Command had execution error.
@ OK
Command successfully executed.
A data product Container.
Fw::Time getTimeTag() const
FwDpIdType getId() const
static constexpr FwSizeType MIN_PACKET_SIZE
FwDpPriorityType getPriority() const
Fw::DpState getState() const
Get the product state.
Fw::SerializeStatus deserializeHeader()
void setBuffer(const Buffer &buffer)
Set the packet buffer.
@ UNTRANSMITTED
The untransmitted state.
@ TRANSMITTED
The transmitted state.
External serialize buffer with no copy semantics.
const char * toChar() const
virtual void * allocate(const NATIVE_UINT_TYPE identifier, NATIVE_UINT_TYPE &size, bool &recoverable)=0
Allocate memory.
virtual void deallocate(const NATIVE_UINT_TYPE identifier, void *ptr)=0
Deallocate memory.
NATIVE_UINT_TYPE SizeType
void format(const CHAR *formatString,...)
write formatted string to buffer
SizeType length() const
Get length of string.
const char * toChar() const
Definition String.hpp:50
U32 getUSeconds() const
Definition Time.cpp:139
U32 getSeconds() const
Definition Time.cpp:135
Wait or don't wait for something.
Directory class.
Status open(const char *path, OpenMode mode) override
Open or create a directory.
Definition Directory.cpp:30
Status readDirectory(Fw::String filenameArray[], const FwSizeType arraySize, FwSizeType &filenameCount)
Read the contents of the directory and store filenames in filenameArray of size arraySize.
@ READ
Error if directory doesn't exist.
Definition Directory.hpp:34
@ OP_OK
Operation was successful.
Definition Directory.hpp:20
Status read(U8 *buffer, FwSignedSizeType &size)
read data from this file into supplied buffer bounded by size
Definition File.cpp:143
void close() override
close the file, if not opened then do nothing
Definition File.cpp:70
Os::FileInterface::Status open(const char *path, Mode mode)
open file with supplied path and mode
Definition File.cpp:45
Status write(const U8 *buffer, FwSignedSizeType &size)
write data to this file from the supplied buffer bounded by size
Definition File.cpp:163
@ OVERWRITE
Overwrite file when it exists and creation was requested.
Definition File.hpp:46
@ BAD_SIZE
Invalid size parameter.
Definition File.hpp:34
@ OP_OK
Operation was successful.
Definition File.hpp:30
@ OPEN_CREATE
Open file for writing and truncates file if it exists, ie same flags as creat()
Definition File.hpp:22
@ OPEN_READ
Open file for reading.
Definition File.hpp:21
@ OPEN_APPEND
Open file for appending.
Definition File.hpp:25
static Status getFileSize(const char *path, FwSignedSizeType &size)
Get the size of the file (in bytes) at the specified path.
@ OP_OK
Operation was successful.
Auto-generated base for DpCatalog component.
Svc::SendFileResponse fileOut_out(FwIndexType portNum, const Fw::StringBase &sourceFileName, const Fw::StringBase &destFileName, U32 offset, U32 length)
Invoke output port fileOut.
void cmdResponse_out(FwOpcodeType opCode, U32 cmdSeq, Fw::CmdResponse response)
Emit command response.
void log_ACTIVITY_HI_CatalogXmitCompleted(U64 bytes) const
void log_WARNING_HI_FileOpenError(const Fw::StringBase &loc, I32 stat)
void log_WARNING_HI_DpInsertError(Svc::DpRecord dp)
void log_WARNING_HI_StateFileWriteError(const Fw::StringBase &file, I32 stat) const
Log event StateFileWriteError.
void log_WARNING_HI_DirectoryOpenError(const Fw::StringBase &loc, I32 stat) const
void log_WARNING_HI_FileReadError(const Fw::StringBase &file, I32 stat)
void log_WARNING_HI_StateFileTruncated(const Fw::StringBase &file, I32 offset, I32 size) const
Log event StateFileTruncated.
void log_WARNING_HI_FileSizeError(const Fw::StringBase &file, I32 stat)
void log_WARNING_HI_CatalogFull(const Fw::StringBase &dir)
void log_WARNING_HI_StateFileOpenError(const Fw::StringBase &file, I32 stat) const
Log event StateFileOpenError.
void log_WARNING_HI_DpCatalogFull(Svc::DpRecord dp)
void log_ACTIVITY_LO_SendingProduct(const Fw::StringBase &file, U32 bytes, U32 prio) const
void log_ACTIVITY_LO_ProcessingDirectory(const Fw::StringBase &directory) const
void log_WARNING_HI_FileHdrDesError(const Fw::StringBase &file, I32 stat)
void log_ACTIVITY_HI_ProcessingDirectoryComplete(const Fw::StringBase &loc, U32 total, U32 pending, U64 pending_bytes) const
void log_WARNING_HI_NotInitialized() const
Log event NotInitialized.
void log_WARNING_LO_NoStateFileSpecified() const
Log event NoStateFileSpecified.
void log_WARNING_HI_StateFileReadError(const Fw::StringBase &file, I32 stat, I32 offset) const
Log event StateFileReadError.
void log_ACTIVITY_LO_ProcessingFile(const Fw::StringBase &file) const
void log_WARNING_HI_NoDpMemory() const
Log event NoDpMemory.
void configure(Fw::FileNameString directories[DP_MAX_DIRECTORIES], FwSizeType numDirs, Fw::FileNameString &stateFile, NATIVE_UINT_TYPE memId, Fw::MemAllocator &allocator)
Configure the DpCatalog.
Definition DpCatalog.cpp:54
@ SERIALIZED_SIZE
The size of the serial representation.
Svc::SendFileStatus::T getstatus() const
Get member status.
FwSizeType string_length(const CHAR *source, FwSizeType buffer_size)
get the length of the source string
FwSignedSizeType substring_find(const CHAR *source_string, FwSizeType source_size, const CHAR *sub_string, FwSizeType sub_size)
find the first occurrence of a substring
SerializeStatus
forward declaration for string
@ FW_SERIALIZE_OK
Serialization/Deserialization operation was successful.
static const FwSizeType DP_MAX_FILES
static const FwSizeType DP_MAX_DIRECTORIES
#define U64(C)
Definition sha.h:176
static constexpr FwSizeType SIZE
The header size.