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