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