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