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 
3 // \title DpCatalog.cpp
4 // \author tcanham
5 // \brief cpp file for DpCatalog component implementation class
6 // ======================================================================
7 
9 #include "Fw/Dp/DpContainer.hpp"
10 #include "Fw/FPrimeBasicTypes.hpp"
11 
12 #include <new> // placement new
13 #include "Fw/Types/StringUtils.hpp"
14 #include "Os/File.hpp"
15 #include "Os/FileSystem.hpp"
16 #include "Utils/Hash/Hash.hpp"
17 
18 namespace Svc {
19 static_assert(DP_MAX_DIRECTORIES > 0, "Configuration DP_MAX_DIRECTORIES must be positive");
20 static_assert(DP_MAX_FILES > 0, "Configuration DP_MAX_FILES must be positive");
21 // ----------------------------------------------------------------------
22 // Component construction and destruction
23 // ----------------------------------------------------------------------
24 
25 DpCatalog ::DpCatalog(const char* const compName) : DpCatalogComponentBase(compName) {
26  // Members are default-initialized via in-class initializers in the header.
27 }
28 
30  FwSizeType numDirs,
31  Fw::FileNameString& stateFile,
32  FwEnumStoreType memId,
33  Fw::MemAllocator& allocator) {
34  // Do some assertion checks
35  FW_ASSERT(numDirs <= DP_MAX_DIRECTORIES, static_cast<FwAssertArgType>(numDirs));
36 
37  this->m_stateFile = stateFile;
38 
39  // Request memory for state file data storage.
40  // RedBlackTreeSet storage is allocated as a member variable, so we only need
41  // to allocate memory for the state file tracking array.
42  static const FwSizeType slotSize = sizeof(DpDstateFileEntry);
43  this->m_memSize = DP_MAX_FILES * slotSize;
44  bool notUsed; // we don't need to recover the catalog.
45  // request memory. this->m_memSize will be modified if there is less than we requested
46  this->m_memPtr = allocator.allocate(memId, this->m_memSize, notUsed);
47 
48  // Initialize if there is enough room for at least one record and memory was allocated
49  if ((this->m_memSize >= slotSize) and (this->m_memPtr != nullptr)) {
50  // set the number of available record slots based on how much memory we actually got
51  this->m_numDpSlots = this->m_memSize / slotSize;
52  // Initialize the catalog
53  this->resetCatalog();
54  // assign pointer for the state file storage
55  this->m_stateFileData = static_cast<DpDstateFileEntry*>(this->m_memPtr);
56  } else {
57  // if we don't have enough memory, set the number of records
58  // to zero for later detection
59  this->m_numDpSlots = 0;
60  }
61 
62  // assign directory names
63  for (FwSizeType dir = 0; dir < numDirs; dir++) {
64  this->m_directories[dir] = directories[dir];
65  }
66  this->m_numDirectories = numDirs;
67 
68  // store allocator
69  this->m_allocator = &allocator;
70  this->m_allocatorId = memId;
71  this->m_initialized = true;
72 }
73 
74 void DpCatalog::resetCatalog() {
75  // Clear the catalog
76  this->m_dpCatalog.clear();
77  // Clear transmission state
78  this->m_hasCurrentXmit = false;
79  // Reset counters
80  this->m_pendingFiles = 0;
81  this->m_pendingDpBytes = 0;
82  // Mark the catalog as un-built
83  this->m_catalogBuilt = false;
84 }
85 
86 void DpCatalog::resetStateFileData() {
87  // clear state file data
88  for (FwSizeType slot = 0; slot < this->m_numDpSlots; slot++) {
89  this->m_stateFileData[slot].used = false;
90  this->m_stateFileData[slot].visited = false;
91  (void)new (&this->m_stateFileData[slot].entry.record) DpRecord();
92  }
93  this->m_stateFileEntries = 0;
94 }
95 
96 Fw::CmdResponse DpCatalog::loadStateFile() {
97  FW_ASSERT(this->m_stateFileData);
98 
99  // Make sure that a file was specified
100  if (this->m_stateFile.length() == 0) {
102  return Fw::CmdResponse::OK;
103  }
104 
105  // buffer for reading entries
106 
107  BYTE buffer[sizeof(FwIndexType) + DpRecord::SERIALIZED_SIZE];
108  Fw::ExternalSerializeBuffer entryBuffer(buffer, sizeof(buffer));
109 
110  // open the state file
111  Os::File stateFile;
112  Os::File::Status stat = stateFile.open(this->m_stateFile.toChar(), Os::File::OPEN_READ);
113  if (stat != Os::File::OP_OK) {
114  this->log_WARNING_HI_StateFileOpenError(this->m_stateFile, stat);
116  }
117 
118  FwSizeType fileLoc = 0;
119  this->m_stateFileEntries = 0;
120 
121  // read entries from the state file
122  for (FwSizeType entry = 0; entry < this->m_numDpSlots; entry++) {
123  FwSizeType size = static_cast<FwSizeType>(sizeof(buffer));
124  // read the directory index
125  stat = stateFile.read(buffer, size);
126  if (stat != Os::File::OP_OK) {
127  this->log_WARNING_HI_StateFileReadError(this->m_stateFile, stat, static_cast<I32>(fileLoc));
128  stateFile.close();
130  }
131 
132  if (0 == size) {
133  // no more entries
134  break;
135  }
136 
137  // check to see if the full entry was read. If not,
138  // abandon it and finish. We can at least operate on
139  // the entries that were read.
140  if (size != sizeof(buffer)) {
141  this->log_WARNING_HI_StateFileTruncated(this->m_stateFile, static_cast<I32>(fileLoc),
142  static_cast<I32>(size));
143  stateFile.close();
144  return Fw::CmdResponse::OK;
145  }
146 
147  // reset the buffer for deserializing the entry
148  Fw::SerializeStatus serStat = entryBuffer.setBuffLen(static_cast<Fw::Serializable::SizeType>(size));
149  // should always fit
150  FW_ASSERT(Fw::FW_SERIALIZE_OK == serStat, serStat);
151  entryBuffer.resetDeser();
152 
153  // deserialization after this point should always work, since
154  // the source buffer was specifically sized to hold the data
155 
156  // Deserialize the file directory index. If an error occurs processing the file,
157  // generate event and return EXECUTION_ERROR.
158  Fw::SerializeStatus status = entryBuffer.deserializeTo(this->m_stateFileData[entry].entry.dir);
159  if (status != Fw::FW_SERIALIZE_OK) {
160  this->log_WARNING_HI_FileCorruptedDataError(this->m_stateFile, static_cast<I32>(status));
161  stateFile.close();
163  }
164  status = entryBuffer.deserializeTo(this->m_stateFileData[entry].entry.record);
165  if (status != Fw::FW_SERIALIZE_OK) {
166  this->log_WARNING_HI_FileCorruptedDataError(this->m_stateFile, static_cast<I32>(status));
167  stateFile.close();
169  }
170  this->m_stateFileData[entry].used = true;
171  this->m_stateFileData[entry].visited = false;
172 
173  // increment the file location
174  fileLoc += size;
175  this->m_stateFileEntries++;
176  }
177  stateFile.close();
178  return Fw::CmdResponse::OK;
179 }
180 
181 void DpCatalog::getFileState(DpStateEntry& entry) {
182  FW_ASSERT(this->m_stateFileData);
183  // search the file state data for the entry
184  for (FwSizeType line = 0; line < this->m_stateFileEntries; line++) {
185  // check for a match (compare dir, then id, priority, & time)
186  if (this->m_stateFileData[line].entry.dir == entry.dir && this->m_stateFileData[line].entry == entry) {
187  // update the transmitted state
188  entry.record.set_state(this->m_stateFileData[line].entry.record.get_state());
189  entry.record.set_blocks(this->m_stateFileData[line].entry.record.get_blocks());
190  // mark it as visited for later pruning if necessary
191  this->m_stateFileData[line].visited = true;
192  return;
193  }
194  }
195 }
196 
197 void DpCatalog::pruneAndWriteStateFile() {
198  FW_ASSERT(this->m_stateFileData);
199 
200  // There is a chance that a data product file can disappear after
201  // the state file is written from the last catalog build and transmit.
202  // This function will walk the state file data and write back only
203  // the entries that were visited during the last catalog build. This will
204  // remove any entries that are no longer valid.
205 
206  // open the state file
207  Os::File stateFile;
208  // we open it as a new file so we don't accumulate invalid entries
209  Os::File::Status stat =
210  stateFile.open(this->m_stateFile.toChar(), Os::File::OPEN_CREATE, Os::FileInterface::OVERWRITE);
211 
212  if (stat != Os::File::OP_OK) {
213  this->log_WARNING_HI_StateFileOpenError(this->m_stateFile, stat);
214  return;
215  }
216 
217  // buffer for writing entries
218  BYTE buffer[sizeof(FwIndexType) + DpRecord::SERIALIZED_SIZE];
219  Fw::ExternalSerializeBuffer entryBuffer(buffer, sizeof(buffer));
220 
221  // write entries to the state file
222  for (FwSizeType entry = 0; entry < this->m_numDpSlots; entry++) {
223  // only write entries that were used
224  if ((this->m_stateFileData[entry].used) and (this->m_stateFileData[entry].visited)) {
225  // reset the buffer for serializing the entry
226  entryBuffer.resetSer();
227  // serialize the file directory index
228  Fw::SerializeStatus serStat = entryBuffer.serializeFrom(this->m_stateFileData[entry].entry.dir);
229  // Should always fit
230  FW_ASSERT(Fw::FW_SERIALIZE_OK == serStat, serStat);
231  serStat = entryBuffer.serializeFrom(this->m_stateFileData[entry].entry.record);
232  // Should always fit
233  FW_ASSERT(Fw::FW_SERIALIZE_OK == serStat, serStat);
234  // write the entry
235  FwSizeType size = entryBuffer.getSize();
236  // Protect against overflow
237  stat = stateFile.write(buffer, size);
238  if (stat != Os::File::OP_OK) {
239  this->log_WARNING_HI_StateFileWriteError(this->m_stateFile, stat);
240  stateFile.close();
241  return;
242  }
243  }
244  }
245 
246  // close the state file
247  stateFile.close();
248 }
249 
250 void DpCatalog::appendFileState(const DpStateEntry& entry) {
251  FW_ASSERT(this->m_stateFileData);
252  FW_ASSERT(entry.dir < static_cast<FwIndexType>(this->m_numDirectories), static_cast<FwAssertArgType>(entry.dir),
253  static_cast<FwAssertArgType>(this->m_numDirectories));
254 
255  // We will append state to the existing state file
256  // TODO: Have to handle case where state file has partially transmitted
257  // state already
258 
259  // open the state file
260  Os::File stateFile;
261  // we open it as a new file so we don't accumulate invalid entries
262  Os::File::Status stat = stateFile.open(this->m_stateFile.toChar(), Os::File::OPEN_APPEND);
263  if (stat != Os::File::OP_OK) {
264  this->log_WARNING_HI_StateFileOpenError(this->m_stateFile, stat);
265  return;
266  }
267 
268  // buffer for writing entries
269  BYTE buffer[sizeof(entry.dir) + sizeof(entry.record)];
270  Fw::ExternalSerializeBuffer entryBuffer(buffer, sizeof(buffer));
271  // reset the buffer for serializing the entry
272  entryBuffer.resetSer();
273  // serialize the file directory index
274  Fw::SerializeStatus serStat = entryBuffer.serializeFrom(entry.dir);
275  // should fit
276  FW_ASSERT(serStat == Fw::FW_SERIALIZE_OK, serStat);
277  serStat = entryBuffer.serializeFrom(entry.record);
278  // should fit
279  FW_ASSERT(serStat == Fw::FW_SERIALIZE_OK, serStat);
280  // write the entry
281  FwSizeType size = entryBuffer.getSize();
282  stat = stateFile.write(buffer, size);
283  if (stat != Os::File::OP_OK) {
284  stateFile.close();
285  this->log_WARNING_HI_StateFileWriteError(this->m_stateFile, stat);
286  return;
287  }
288 
289  // close the state file
290  stateFile.close();
291 }
292 
293 Fw::CmdResponse DpCatalog::doCatalogBuild() {
294  // check initialization
295  if (not this->checkInit()) {
297  }
298 
299  // check that initialization got memory
300  if (0 == this->m_numDpSlots) {
303  }
304 
305  // make sure a downlink is not in progress
306  if (this->m_xmitInProgress) {
309  }
310 
311  // reset state file data
312  this->resetStateFileData();
313 
314  // load state data from file
315  Fw::CmdResponse response = this->loadStateFile();
316 
317  // reset catalog
318  this->resetCatalog();
319 
320  // fill the catalog with DP files
321  response = this->fillBinaryTree();
322  if (response != Fw::CmdResponse::OK) {
323  // clean up the catalog
324  this->resetCatalog();
325  this->resetStateFileData();
326  return response;
327  }
328 
329  // prune and rewrite the state file
330  this->pruneAndWriteStateFile();
331 
333 
334  // Flag so addToCat knows it is good to go
335  this->m_catalogBuilt = true;
336 
337  return Fw::CmdResponse::OK;
338 }
339 
340 Fw::CmdResponse DpCatalog::fillBinaryTree() {
341  // keep cumulative number of files
342  FwSizeType totalFiles = 0;
343 
344  // get file listings from file system
345  // double bounds to appease static analysis
346  for (FwSizeType dir = 0; dir < this->m_numDirectories && dir < static_cast<FwSizeType>(DP_MAX_DIRECTORIES); dir++) {
347  // read in each directory and keep track of total
348  this->log_ACTIVITY_LO_ProcessingDirectory(this->m_directories[dir]);
349  FwSizeType filesRead = 0;
350  U32 filesProcessed = 0;
351 
352  Os::Directory dpDir;
353  Os::Directory::Status status = dpDir.open(this->m_directories[dir].toChar(), Os::Directory::OpenMode::READ);
354  if (status != Os::Directory::OP_OK) {
355  this->log_WARNING_HI_DirectoryOpenError(this->m_directories[dir], status);
357  }
358  status = dpDir.readDirectory(this->m_fileList, (this->m_numDpSlots - totalFiles), filesRead);
359 
360  if (status != Os::Directory::OP_OK) {
361  this->log_WARNING_HI_DirectoryOpenError(this->m_directories[dir], status);
363  }
364 
365  // Assert number of files isn't more than asked
366  FW_ASSERT(filesRead <= this->m_numDpSlots - totalFiles, static_cast<FwAssertArgType>(filesRead),
367  static_cast<FwAssertArgType>(this->m_numDpSlots - totalFiles));
368 
369  // extract metadata for each file
370  for (FwSizeType file = 0; file < filesRead; file++) {
371  // only consider files with the DP extension
372 
373  const FwSizeType fileNameLength = this->m_fileList[file].length();
374  const FwSizeType dpExtLength = Fw::StringUtils::string_length(DP_EXT, sizeof(DP_EXT));
375  const FwSignedSizeType loc = Fw::StringUtils::substring_find_last(this->m_fileList[file].toChar(),
376  fileNameLength, DP_EXT, dpExtLength);
377 
378  // Only accept files whose final suffix is the data product extension
379  if ((-1 == loc) || (static_cast<FwSizeType>(loc) + dpExtLength != fileNameLength)) {
380  continue;
381  }
382 
383  Fw::String fullFile;
384  fullFile.format("%s/%s", this->m_directories[dir].toChar(), this->m_fileList[file].toChar());
385 
386  int ret = processFile(fullFile, dir);
387  if (ret < 0) {
388  break;
389  }
390 
391  filesProcessed += static_cast<U32>(ret);
392 
393  } // end for each file in a directory
394 
395  totalFiles += filesProcessed;
396 
397  this->log_ACTIVITY_HI_ProcessingDirectoryComplete(this->m_directories[dir], static_cast<U32>(totalFiles),
398  this->m_pendingFiles, this->m_pendingDpBytes);
399 
400  // check to see if catalog is full
401  // that means generated products exceed the catalog size
402  if (totalFiles == this->m_numDpSlots) {
403  this->log_WARNING_HI_CatalogFull(this->m_directories[dir]);
404  break;
405  }
406  } // end for each directory
407 
408  return Fw::CmdResponse::OK;
409 
410 } // end fillBinaryTree()
411 
412 FwSizeType DpCatalog::determineDirectory(Fw::String fullFile) {
413  // Grab the directory string (up until the final slash)
414  // Could be found directly w/ a dirname func or regex
416  fullFile.toChar(), fullFile.length(), DIRECTORY_DELIMITER,
418 
419  // Seems like the logic works so long as the path styles match (i.e. relative vs absolute)
420  // Full path resolution might be a worthwhile add
421 
422  // No directory delimiter found; return DP_MAX_DIRECTORIES to signal failure
423  if (-1 == loc) {
424  return DP_MAX_DIRECTORIES;
425  }
426 
427  for (FwSizeType dir = 0; dir < this->m_numDirectories; dir++) {
428  const Fw::FileNameString& dir_string = this->m_directories[dir];
429 
430  // Compare both strings up to location of final slash
431  // StringUtils::substring_find will return zero if both paths agree
432  // memory safe since both are fixed width strings
433  // and loc is before the fixed width
434  if (Fw::StringUtils::substring_find(dir_string.toChar(), dir_string.length(), fullFile.toChar(),
435  static_cast<FwSizeType>(loc)) == 0) {
436  return dir;
437  }
438  }
439 
440  // No directory matched
441  return DP_MAX_DIRECTORIES;
442 }
443 
444 int DpCatalog::processFile(Fw::String fullFile, FwSizeType dir) {
445  FW_ASSERT(dir < static_cast<FwSizeType>(DP_MAX_DIRECTORIES), static_cast<FwAssertArgType>(dir));
446  // file class instance for processing files
447  Os::File dpFile;
448 
449  // Working buffer for DP headers
450  U8 dpBuff[Fw::DpContainer::MIN_PACKET_SIZE]; // Header buffer
451  Fw::Buffer hdrBuff(dpBuff, sizeof(dpBuff)); // buffer for container header decoding
452  Fw::DpContainer container; // container object for extracting header fields
453 
454  this->log_ACTIVITY_LO_ProcessingFile(fullFile);
455 
456  // get file size
457  FwSizeType fileSize = 0;
458  Os::FileSystem::Status sizeStat = Os::FileSystem::getFileSize(fullFile.toChar(), fileSize);
459  if (sizeStat != Os::FileSystem::OP_OK) {
460  this->log_WARNING_HI_FileSizeError(fullFile, sizeStat);
461  return 0;
462  }
463 
464  if (fileSize < Fw::DpContainer::MIN_PACKET_SIZE) {
466  return 0;
467  }
468 
469  Os::File::Status stat = dpFile.open(fullFile.toChar(), Os::File::OPEN_READ);
470  if (stat != Os::File::OP_OK) {
471  this->log_WARNING_HI_FileOpenError(fullFile, stat);
472  return 0;
473  }
474 
475  // Read DP header and header hash
477 
478  stat = dpFile.read(dpBuff, size);
479  if (stat != Os::File::OP_OK) {
480  this->log_WARNING_HI_FileReadError(fullFile, stat);
481  dpFile.close();
482  return 0;
483  }
484 
485  // if full header and hashes aren't read, something's wrong with the file, so skip
486  if (size != Fw::DpContainer::MIN_PACKET_SIZE) {
488  dpFile.close();
489  return 0;
490  }
491 
492  // if all is well, don't need the file any more
493  dpFile.close();
494 
495  // give buffer to container instance
496  container.setBuffer(hdrBuff);
497 
498  // make sure the header metadata matches its stored hash before trusting it
499  Utils::HashBuffer storedHash;
500  Utils::HashBuffer computedHash;
501  Fw::Success::T hashStatus = container.checkHeaderHash(storedHash, computedHash);
502  if (hashStatus != Fw::Success::SUCCESS) {
503  this->log_WARNING_HI_FileHdrError(fullFile, DpHdrField::CRC, computedHash.asBigEndianU32(),
504  storedHash.asBigEndianU32());
505  return 0;
506  }
507 
508  // reset header deserialization in the container
509  Fw::SerializeStatus desStat = container.deserializeHeader();
510  if (desStat != Fw::FW_SERIALIZE_OK) {
511  this->log_WARNING_HI_FileHdrDesError(fullFile, desStat);
512  return 0;
513  }
514 
515  const FwSizeType dataSize = container.getDataSize();
516  const FwSizeType expectedDataSize = fileSize - Fw::DpContainer::MIN_PACKET_SIZE;
517  if (dataSize != expectedDataSize) {
519  return 0;
520  }
521 
522  Fw::FileNameString canonicalFileName;
523  canonicalFileName.format(DP_FILENAME_FORMAT, this->m_directories[dir].toChar(), container.getId(),
524  container.getTimeTag().getSeconds(), container.getTimeTag().getUSeconds());
525  if (canonicalFileName != fullFile) {
526  this->log_WARNING_HI_InvalidFileName(fullFile, canonicalFileName);
527  return 0;
528  }
529 
530  // skip adding an already transmitted file
531  if (container.getState() == Fw::DpState::TRANSMITTED) {
532  this->log_ACTIVITY_HI_DpFileSkipped(fullFile);
533  return 0;
534  }
535 
536  // add entry to catalog.
537  DpStateEntry entry;
538  entry.dir = static_cast<FwIndexType>(dir);
539  entry.record.set_id(container.getId());
540  entry.record.set_priority(container.getPriority());
541  entry.record.set_state(container.getState());
542  entry.record.set_tSec(container.getTimeTag().getSeconds());
543  entry.record.set_tSub(container.getTimeTag().getUSeconds());
544  entry.record.set_size(static_cast<U64>(fileSize));
545 
546  // check the state file to see if there is transmit state
547  this->getFileState(entry);
548 
549  // insert entry into sorted catalog. if can't insert, quit
550  bool inserted = this->insertEntry(entry);
551  if (!inserted) {
552  this->log_WARNING_HI_DpInsertError(entry.record);
553  // return and hope new slots open up later
554  return -1;
555  }
556 
557  // increment our counters
558  this->m_pendingFiles++;
559  this->m_pendingDpBytes += entry.record.get_size();
560 
561  // make sure we haven't exceeded the limit
562  if (this->m_pendingFiles > this->m_numDpSlots) {
563  this->log_WARNING_HI_DpCatalogFull(entry.record);
564  return -1;
565  }
566 
567  this->log_ACTIVITY_HI_DpFileAdded(canonicalFileName);
568 
569  // No need to track iterator state - begin() always gives us the highest priority entry
570  // and we remove entries as we transmit them
571 
572  return 1;
573 }
574 
575 // ----------------------------------------------------------------------
576 // DpStateEntry Comparison Ops
577 // ----------------------------------------------------------------------
578 int DpCatalog::DpStateEntry::compareEntries(const DpStateEntry& left, const DpStateEntry& right) {
579  // check priority. Lower is higher priority
580  if (left.record.get_priority() < right.record.get_priority()) {
581  return -1;
582  } else if (left.record.get_priority() > right.record.get_priority()) {
583  return 1;
584  }
585 
586  // check time. Older is higher priority
587  else if (left.record.get_tSec() < right.record.get_tSec()) {
588  return -1;
589  } else if (left.record.get_tSec() > right.record.get_tSec()) {
590  return 1;
591  }
592 
593  // check subsecond time. Older is higher priority
594  else if (left.record.get_tSub() < right.record.get_tSub()) {
595  return -1;
596  } else if (left.record.get_tSub() > right.record.get_tSub()) {
597  return 1;
598  }
599 
600  // check ID. Lower is higher priority
601  else if (left.record.get_id() < right.record.get_id()) {
602  return -1;
603  } else if (left.record.get_id() > right.record.get_id()) {
604  return 1;
605  }
606 
607  // if ids are equal we have two nodes with the same value
608  else {
609  return 0;
610  }
611 }
612 
613 bool DpCatalog::DpStateEntry::operator==(const DpStateEntry& other) const {
614  return compareEntries(*this, other) == 0;
615 }
616 bool DpCatalog::DpStateEntry::operator!=(const DpStateEntry& other) const {
617  return compareEntries(*this, other) != 0;
618 }
619 
620 bool DpCatalog::DpStateEntry::operator>(const DpStateEntry& other) const {
621  return compareEntries(*this, other) > 0;
622 }
623 bool DpCatalog::DpStateEntry::operator<(const DpStateEntry& other) const {
624  return compareEntries(*this, other) < 0;
625 }
626 
627 bool DpCatalog::insertEntry(DpStateEntry& entry) {
628  // Insert into the RedBlackTreeSet
629  // The tree maintains sorting by priority, time, and ID via DpStateEntry comparison operators
630  Fw::Success status = this->m_dpCatalog.insert(entry);
631  return (status == Fw::Success::SUCCESS);
632 }
633 
634 void DpCatalog::sendNextEntry() {
635  // Use xmit flag to break upon STOP_XMIT_CATALOG
636  if (this->m_xmitInProgress != true) {
637  return;
638  }
639 
640  // Look for the next entry to send
641  DpStateEntry entry;
642  if (!this->findNextEntry(entry)) {
643  // if no entry found, we are done
644  this->m_xmitInProgress = false;
645  this->log_ACTIVITY_HI_CatalogXmitCompleted(this->m_xmitBytes);
646  this->dispatchWaitedResponse(Fw::CmdResponse::OK);
647  return;
648  }
649 
650  // Save current entry for fileDone_handler
651  this->m_currentXmitEntry = entry;
652  this->m_hasCurrentXmit = true;
653 
654  // Build file name based on the found entry
655  this->m_currXmitFileName.format(DP_FILENAME_FORMAT, this->m_directories[entry.dir].toChar(), entry.record.get_id(),
656  entry.record.get_tSec(), entry.record.get_tSub());
657  this->log_ACTIVITY_LO_SendingProduct(this->m_currXmitFileName, static_cast<U32>(entry.record.get_size()),
658  entry.record.get_priority());
659  Svc::SendFileResponse resp = this->fileOut_out(0, this->m_currXmitFileName, this->m_currXmitFileName, 0, 0);
660  if (resp.get_status() != Svc::SendFileStatus::STATUS_OK) {
661  // warn, but keep going since it may be an issue with this file but others could
662  // make it
663  this->log_WARNING_HI_DpFileSendError(this->m_currXmitFileName, resp.get_status());
664  }
665 } // end sendNextEntry()
666 
667 bool DpCatalog::findNextEntry(DpStateEntry& entry) {
668  // If catalog is empty, return false
669  if (this->m_dpCatalog.getSize() == 0) {
670  return false;
671  }
672 
673  // Get the highest priority entry (begin() returns highest priority)
674  // Since we remove entries as we transmit them, begin() always gives us the next entry
675  typename Fw::RedBlackTreeSet<DpStateEntry, DP_MAX_FILES>::ConstIterator iter = this->m_dpCatalog.begin();
676 
677  // Verify iterator is valid
678  if (iter == this->m_dpCatalog.end()) {
679  return false;
680  }
681 
682  // Get the entry
683  entry = *iter;
684 
685  return true;
686 }
687 
688 bool DpCatalog::checkInit() {
689  if (not this->m_initialized) {
691  return false;
692  } else if (0 == this->m_numDpSlots) {
694  return false;
695  }
696 
697  return true;
698 }
699 
701  // only try to deallocate if both pointers are non-zero
702  // it's a way to more gracefully shut down if there are missing
703  // pointers
704  if ((this->m_allocator != nullptr) and (this->m_memPtr != nullptr)) {
705  this->m_allocator->deallocate(this->m_allocatorId, this->m_memPtr);
706  }
707 }
708 
709 // ----------------------------------------------------------------------
710 // Handler implementations for user-defined typed input ports
711 // ----------------------------------------------------------------------
712 
713 void DpCatalog ::fileDone_handler(FwIndexType portNum, const Svc::SendFileResponse& resp) {
714  // check file status
716  this->log_WARNING_HI_DpFileXmitError(this->m_currXmitFileName, resp.get_status());
717  this->m_xmitInProgress = false;
718  this->dispatchWaitedResponse(Fw::CmdResponse::EXECUTION_ERROR);
719  return;
720  }
721 
722  // Catalog cleared while this file was sent
723  if (!this->m_catalogBuilt) {
724  return;
725  }
726 
727  // Should have a valid current transmit entry
728  FW_ASSERT(this->m_hasCurrentXmit);
729 
730  // Reduce pending
731  this->m_pendingDpBytes -= this->m_currentXmitEntry.record.get_size();
732  this->m_pendingFiles--;
733  // Log File Complete & pending
734  this->log_ACTIVITY_LO_ProductComplete(this->m_currXmitFileName, this->m_pendingFiles, this->m_pendingDpBytes);
735 
736  // mark the entry as transmitted
737  this->m_currentXmitEntry.record.set_state(Fw::DpState::TRANSMITTED);
738  // update the transmitted state in the state file
739  this->appendFileState(this->m_currentXmitEntry);
740  // add the size
741  this->m_xmitBytes += this->m_currentXmitEntry.record.get_size();
742 
743  // Remove from catalog
744  Fw::Success status = this->m_dpCatalog.remove(this->m_currentXmitEntry);
745  FW_ASSERT(status == Fw::Success::SUCCESS);
746 
747  this->m_hasCurrentXmit = false;
748 
749  // send the next entry, if it exists
750  this->sendNextEntry();
751 }
752 
753 void DpCatalog ::pingIn_handler(FwIndexType portNum, U32 key) {
754  // return code for health ping
755  this->pingOut_out(0, key);
756 }
757 
758 void DpCatalog ::addToCat_handler(FwIndexType portNum,
759  const Fw::StringBase& fileName,
760  FwDpPriorityType priority,
761  FwSizeType size) {
762  // check initialization
763  if (not this->checkInit()) {
764  return;
765  }
766 
767  // check that initialization got memory
768  if (0 == this->m_numDpSlots) {
770  return;
771  }
772 
773  // Check the catalog has been built
774  if (not this->m_catalogBuilt) {
775  this->log_ACTIVITY_HI_NotLoaded(fileName);
776  return;
777  }
778 
779  // Both of these are grabbed from the header
780  (void)priority;
781  (void)size;
782 
783  // Since this is a runtime addition
784  // Check if file is in one of our directories
785  FwSizeType dir = this->determineDirectory(fileName);
786 
787  // Not in one of our directories; skip this file
788  if (dir == DP_MAX_DIRECTORIES) {
789  this->log_WARNING_HI_DirectoryNotManaged(fileName);
790  return;
791  }
792 
793  // ret > 0 := success
794  int ret = processFile(fileName, dir);
795 
796  if (ret > 0) {
797  // If we already finished, sendNext only if remainingActive
798  if (!this->m_xmitInProgress && this->m_remainActive) {
799  this->m_xmitInProgress = true;
800  this->sendNextEntry();
801  }
802  // Otherwise, Current File finishing will invoke sendNextFile & find the right file
803  // Or will be manually tx-ed at next command
804 
805  // prune and rewrite the state file
806  this->pruneAndWriteStateFile();
807  }
808 }
809 
810 // ----------------------------------------------------------------------
811 // Handler implementations for commands
812 // ----------------------------------------------------------------------
813 
814 void DpCatalog ::BUILD_CATALOG_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
815  // invoke helper
816  this->cmdResponse_out(opCode, cmdSeq, this->doCatalogBuild());
817 }
818 
819 void DpCatalog ::START_XMIT_CATALOG_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, Fw::Wait wait, bool remainActive) {
820  Fw::CmdResponse resp = this->doCatalogXmit();
821  this->m_remainActive = remainActive;
822 
823  if (resp != Fw::CmdResponse::OK) {
824  this->cmdResponse_out(opCode, cmdSeq, resp);
825  } else {
826  if (Fw::Wait::NO_WAIT == wait) {
827  this->cmdResponse_out(opCode, cmdSeq, resp);
828  this->m_xmitCmdWait = false;
829  this->m_xmitOpCode = 0;
830  this->m_xmitCmdSeq = 0;
831  } else {
832  this->m_xmitCmdWait = true;
833  this->m_xmitOpCode = opCode;
834  this->m_xmitCmdSeq = cmdSeq;
835  }
836  }
837 }
838 
839 Fw::CmdResponse DpCatalog::doCatalogXmit() {
840  // check initialization
841  if (not this->checkInit()) {
843  }
844 
845  // check that initialization got memory
846  if (0 == this->m_numDpSlots) {
849  }
850 
851  // make sure a downlink is not in progress
852  if (this->m_xmitInProgress) {
855  }
856 
857  // Check the catalog has been built
858  if (not this->m_catalogBuilt) {
861  }
862 
863  // start transmission
864  this->m_xmitBytes = 0;
865 
866  this->m_xmitInProgress = true;
867  // Step 3b - search for and send first entry
868  this->sendNextEntry();
869  return Fw::CmdResponse::OK;
870 }
871 
872 void DpCatalog ::STOP_XMIT_CATALOG_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
873  if (not this->m_xmitInProgress) {
875  // benign error, so don't fail the command
876  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
877  } else {
878  this->log_ACTIVITY_HI_CatalogXmitStopped(this->m_xmitBytes);
879  // Disarm the flag so next sendNextEntry stops transmission
880  this->m_xmitInProgress = false;
881  // Respond to original cmd to start xmit
882  // (if we haven't already)
883  this->dispatchWaitedResponse(Fw::CmdResponse::OK);
884 
885  // Respond to this command
886  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
887  }
888 }
889 
890 void DpCatalog ::CLEAR_CATALOG_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
891  this->resetCatalog();
892  this->resetStateFileData();
893 
894  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
895 }
896 
897 void DpCatalog ::dispatchWaitedResponse(Fw::CmdResponse response) {
898  if (this->m_xmitCmdWait) {
899  this->cmdResponse_out(this->m_xmitOpCode, this->m_xmitCmdSeq, response);
900 
901  // Prevent a Duplicate Cmd Response
902  this->m_xmitCmdWait = false;
903  this->m_xmitOpCode = 0;
904  this->m_xmitCmdSeq = 0;
905  }
906 }
907 
908 } // namespace Svc
void log_WARNING_HI_DpCatalogFull(Svc::DpRecord dp)
void log_WARNING_HI_FileHdrError(const Fw::StringBase &file, Svc::DpHdrField field, U32 exp, U32 act)
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
void pingOut_out(FwIndexType portNum, U32 key) const
Invoke output port pingOut.
FwDpPriorityType getPriority() const
virtual void * allocate(const FwEnumStoreType identifier, FwSizeType &size, bool &recoverable, FwSizeType alignment=alignof(std::max_align_t))=0
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.
Representing success.
PlatformSizeType FwSizeType
U32 FwDpPriorityType
The type of a data product priority.
void log_ACTIVITY_HI_NotLoaded(const Fw::StringBase &file) const
Log event NotLoaded.
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:43
Wait or don&#39;t wait for something.
Definition: WaitEnumAc.hpp:17
static constexpr FwSizeType MIN_PACKET_SIZE
Definition: DpContainer.hpp:65
static const FwIndexType DP_MAX_DIRECTORIES
Svc::SendFileResponse fileOut_out(FwIndexType portNum, const Fw::StringBase &sourceFileName, const Fw::StringBase &destFileName, U32 offset, U32 length) const
Invoke output port fileOut.
ConstIterator begin() const override
Svc::SendFileStatus::T get_status() const
Get member status.
Overwrite file when it exists and creation was requested.
Definition: File.hpp:59
PlatformSignedSizeType FwSignedSizeType
void log_WARNING_HI_StateFileOpenError(const Fw::StringBase &file, I32 stat) const
Log event StateFileOpenError.
Enum representing a command response.
void log_WARNING_HI_DpFileSendError(const Fw::StringBase &file, Svc::SendFileStatus stat)
Log event DpFileSendError.
#define DIRECTORY_DELIMITER
Definition: DpCatalog.hpp:20
void log_ACTIVITY_HI_DpFileAdded(const Fw::StringBase &file) const
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_FileCorruptedDataError(const Fw::StringBase &file, I32 stat) const
void log_WARNING_HI_FileOpenError(const Fw::StringBase &loc, I32 stat)
void log_ACTIVITY_LO_ProductComplete(const Fw::StringBase &file, U32 pending, U64 pending_bytes) const
Os::FileInterface::Status open(const char *path, Mode mode)
open file with supplied path and mode
Definition: File.cpp:43
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)
void clear() override
Clear the set.
Open file for appending.
Definition: File.hpp:37
void log_ACTIVITY_HI_DpFileSkipped(const Fw::StringBase &file) const
void configure(Fw::FileNameString directories[DP_MAX_DIRECTORIES], FwSizeType numDirs, Fw::FileNameString &stateFile, FwEnumStoreType memId, Fw::MemAllocator &allocator)
Configure the DpCatalog.
Definition: DpCatalog.cpp:29
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.
T
The raw enum type.
U32 getSeconds() const
Definition: Time.cpp:118
constexpr const char * DP_FILENAME_FORMAT
Definition: DpCfg.hpp:21
Fw::Time getTimeTag() const
FwSizeType getSize() const override
void close() override
close the file, if not opened then do nothing
Definition: File.cpp:90
Status write(const U8 *buffer, FwSizeType &size)
write data to this file from the supplied buffer bounded by size
Definition: File.cpp:206
void log_WARNING_LO_XmitNotActive() const
Log event XmitNotActive.
const char * toChar() const
Convert to a C-style char*.
void log_ACTIVITY_HI_CatalogXmitCompleted(U64 bytes) const
void log_ACTIVITY_LO_ProcessingFile(const Fw::StringBase &file) const
static const FwIndexType DP_MAX_FILES
The size of the serial representation.
FormatStatus format(const CHAR *formatString,...)
write formatted string to buffer
Definition: StringBase.cpp:39
Command successfully executed.
void log_ACTIVITY_HI_CatalogXmitStopped(U64 bytes) const
uint8_t U8
8-bit unsigned integer
Definition: BasicTypes.h:54
DpCatalog(const char *const compName)
DpCatalog constructor.
Definition: DpCatalog.cpp:25
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:226
FwSignedSizeType substring_find_last(const CHAR *source_string, FwSizeType source_size, const CHAR *sub_string, FwSizeType sub_size)
find the last occurrence of a substring
Definition: StringUtils.cpp:85
Status read(U8 *buffer, FwSizeType &size)
read data from this file into supplied buffer bounded by size
Definition: File.cpp:187
Success remove(const T &element) override
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 log_WARNING_HI_DirectoryNotManaged(const Fw::StringBase &file) const
Memory Allocation base class.
void log_WARNING_HI_CatalogFull(const Fw::StringBase &dir)
Operation was successful.
Definition: File.hpp:42
U32 getUSeconds() const
Definition: Time.cpp:122
void log_WARNING_HI_XmitUnbuiltCatalog() const
Log event XmitUnbuiltCatalog.
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:33
Send file response struct.
U32 asBigEndianU32() const
Convert bytes 0 through 3 of the hash data to a big-Endian U32 value.
#define DP_EXT
Definition: DpCfg.hpp:20
A container class for holding a hash buffer.
Definition: HashBuffer.hpp:26
Success::T checkHeaderHash(Utils::HashBuffer &storedHash, Utils::HashBuffer &computedHash) const
Check the header hash.
Status open(const char *path, OpenMode mode) override
Open or create a directory.
Definition: Directory.cpp:31
Success insert(const T &element) override
RateGroupDivider component implementation.
ConstIterator end() const override
virtual SizeType length() const
Get the length of the string.
U8 BYTE
byte type
Definition: BasicTypes.h:57
Fw::SerializeStatus deserializeHeader()
Definition: DpContainer.cpp:38
Operation was successful.
Definition: FileSystem.hpp:24
virtual void deallocate(const FwEnumStoreType identifier, void *ptr)=0
Invalid size parameter.
Definition: File.hpp:46
FwDpIdType getId() const
Definition: DpContainer.hpp:98
FwSizeType getDataSize() const
Don&#39;t wait for something.
Definition: WaitEnumAc.hpp:35
void log_WARNING_HI_InvalidFileName(const Fw::StringBase &file, const Fw::StringBase &expected)
#define FW_ASSERT(...)
Definition: Assert.hpp:14
Success/Failure.
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:32
Open file for writing and truncates file if it exists, ie same flags as creat()
Definition: File.hpp:34