GCC Code Coverage Report


Directory: ./
File: Svc/DpCatalog/DpCatalog.cpp
Date: 2026-09-03 21:13:48
Exec Total Coverage
Lines: 368 496 74.2%
Functions: 31 32 96.9%
Branches: 288 451 63.9%

Line Branch Exec Source
1 // ======================================================================
2
3 // \title DpCatalog.cpp
4 // \author tcanham
5 // \brief cpp file for DpCatalog component implementation class
6 // ======================================================================
7
8 #include "Svc/DpCatalog/DpCatalog.hpp"
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
7/13
✓ Branch 15 taken 526 times.
✓ Branch 21 taken 526 times.
✓ Branch 35 taken 1052 times.
✓ Branch 38 taken 1052 times.
✓ Branch 39 taken 526 times.
✓ Branch 49 taken 526 times.
✓ Branch 87 taken 526 times.
✗ Branch 117 not taken.
✗ Branch 118 not taken.
✗ Branch 120 not taken.
✗ Branch 121 not taken.
✗ Branch 131 not taken.
✗ Branch 132 not taken.
1578 DpCatalog ::DpCatalog(const char* const compName) : DpCatalogComponentBase(compName) {
26 // Members are default-initialized via in-class initializers in the header.
27 526 }
28
29 618 void DpCatalog::configure(const Fw::ExternalArray<Fw::FileNameString>& directories,
30 Fw::FileNameString& stateFile,
31 FwEnumStoreType memId,
32 Fw::MemAllocator& allocator) {
33 618 const FwSizeType numDirs = directories.getSize();
34 // Do some assertion checks
35 618 FW_ASSERT(numDirs <= DP_MAX_DIRECTORIES, static_cast<FwAssertArgType>(numDirs));
36
37
1/1
✓ Branch 7 taken 618 times.
618 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 618 this->m_memSize = DP_MAX_FILES * slotSize;
44 618 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
1/1
✓ Branch 11 taken 618 times.
618 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
2/4
✓ Branch 4 taken 618 times.
✗ Branch 5 not taken.
✓ Branch 10 taken 618 times.
✗ Branch 11 not taken.
618 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 // never exceeding the DP_MAX_FILES working arrays even if the allocator returned more
52 // memory than was requested
53 618 const FwSizeType allocatedSlots = this->m_memSize / slotSize;
54 618 this->m_numDpSlots = (allocatedSlots < DP_MAX_FILES) ? allocatedSlots : DP_MAX_FILES;
55 // Initialize the catalog
56
1/1
✓ Branch 4 taken 618 times.
618 this->resetCatalog();
57 // assign pointer for the state file storage
58 618 this->m_stateFileData = static_cast<DpDstateFileEntry*>(this->m_memPtr);
59 618 } else {
60 // if we don't have enough memory, set the number of records
61 // to zero for later detection
62 this->m_numDpSlots = 0;
63 }
64
65 // assign directory names
66
2/2
✓ Branch 0 taken 721 times.
✓ Branch 1 taken 618 times.
1339 for (FwSizeType dir = 0; dir < numDirs; dir++) {
67
2/2
✓ Branch 2 taken 721 times.
✓ Branch 11 taken 721 times.
721 this->m_directories[dir] = directories[dir];
68 }
69 618 this->m_numDirectories = numDirs;
70
71 // store allocator
72 618 this->m_allocator = &allocator;
73 618 this->m_allocatorId = memId;
74 618 this->m_initialized = true;
75 618 }
76
77 void DpCatalog::configure(Fw::FileNameString directories[DP_MAX_DIRECTORIES],
78 FwSizeType numDirs,
79 Fw::FileNameString& stateFile,
80 FwEnumStoreType memId,
81 Fw::MemAllocator& allocator) {
82 FW_ASSERT(numDirs <= DP_MAX_DIRECTORIES, static_cast<FwAssertArgType>(numDirs));
83 const Fw::ExternalArray<Fw::FileNameString> directoryArray(directories, numDirs);
84 this->configure(directoryArray, stateFile, memId, allocator);
85 }
86
87 1235 void DpCatalog::resetCatalog() {
88 // Clear the catalog
89 1235 this->m_dpCatalog.clear();
90 // Clear transmission state
91 1235 this->m_hasCurrentXmit = false;
92 // Reset counters
93 1235 this->m_pendingFiles = 0;
94 1235 this->m_pendingDpBytes = 0;
95 // Mark the catalog as un-built
96 1235 this->m_catalogBuilt = false;
97 1235 }
98
99 115 void DpCatalog::resetStateFileData() {
100 // clear state file data
101
2/2
✓ Branch 4 taken 14605 times.
✓ Branch 5 taken 115 times.
14720 for (FwSizeType slot = 0; slot < this->m_numDpSlots; slot++) {
102 14605 this->m_stateFileData[slot].used = false;
103 14605 this->m_stateFileData[slot].visited = false;
104
1/3
✓ Branch 9 taken 14605 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
14605 (void)new (&this->m_stateFileData[slot].entry.record) DpRecord();
105 }
106 115 this->m_stateFileEntries = 0;
107 115 }
108
109 112 Fw::CmdResponse DpCatalog::loadStateFile() {
110 112 FW_ASSERT(this->m_stateFileData != nullptr);
111
112 // Make sure that a file was specified
113
3/3
✓ Branch 6 taken 112 times.
✓ Branch 8 taken 5 times.
✓ Branch 9 taken 107 times.
112 if (this->m_stateFile.length() == 0) {
114
1/1
✓ Branch 5 taken 5 times.
5 this->log_WARNING_LO_NoStateFileSpecified();
115
1/1
✓ Branch 1 taken 5 times.
5 return Fw::CmdResponse::OK;
116 }
117
118 // buffer for reading entries
119
120 107 BYTE buffer[sizeof(FwIndexType) + DpRecord::SERIALIZED_SIZE];
121
1/1
✓ Branch 2 taken 107 times.
107 Fw::ExternalSerializeBuffer entryBuffer(buffer, sizeof(buffer));
122
123 // open the state file
124
1/1
✓ Branch 2 taken 107 times.
107 Os::File stateFile;
125
1/1
✓ Branch 8 taken 107 times.
107 Os::File::Status stat = stateFile.open(this->m_stateFile.toChar(), Os::File::OPEN_READ);
126
2/2
✓ Branch 0 taken 106 times.
✓ Branch 1 taken 1 times.
107 if (stat == Os::File::DOESNT_EXIST) {
127 // A missing state file is expected on first boot and is not an error
128
1/1
✓ Branch 8 taken 106 times.
106 this->log_WARNING_LO_NoStateFile(this->m_stateFile);
129
1/1
✓ Branch 1 taken 106 times.
106 return Fw::CmdResponse::OK;
130 }
131
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (stat != Os::File::OP_OK) {
132 this->log_WARNING_HI_StateFileOpenError(this->m_stateFile, stat);
133 return Fw::CmdResponse::EXECUTION_ERROR;
134 }
135
136 1 FwSizeType fileLoc = 0;
137 1 this->m_stateFileEntries = 0;
138
139 // read entries from the state file
140
1/2
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 for (FwSizeType entry = 0; entry < this->m_numDpSlots; entry++) {
141 1 FwSizeType size = static_cast<FwSizeType>(sizeof(buffer));
142 // read the directory index
143
1/1
✓ Branch 2 taken 1 times.
1 stat = stateFile.read(buffer, size);
144
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (stat != Os::File::OP_OK) {
145 this->log_WARNING_HI_StateFileReadError(this->m_stateFile, stat, static_cast<I32>(fileLoc));
146 stateFile.close();
147 return Fw::CmdResponse::EXECUTION_ERROR;
148 }
149
150
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (0 == size) {
151 // no more entries
152 break;
153 }
154
155 // check to see if the full entry was read. If not,
156 // abandon it and finish. We can at least operate on
157 // the entries that were read.
158
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (size != sizeof(buffer)) {
159 this->log_WARNING_HI_StateFileTruncated(this->m_stateFile, static_cast<I32>(fileLoc),
160 static_cast<I32>(size));
161 stateFile.close();
162 return Fw::CmdResponse::OK;
163 }
164
165 // reset the buffer for deserializing the entry
166
1/1
✓ Branch 2 taken 1 times.
1 Fw::SerializeStatus serStat = entryBuffer.setBuffLen(static_cast<Fw::Serializable::SizeType>(size));
167 // should always fit
168 1 FW_ASSERT(Fw::FW_SERIALIZE_OK == serStat, serStat);
169
1/1
✓ Branch 2 taken 1 times.
1 entryBuffer.resetDeser();
170
171 // deserialization after this point should always work, since
172 // the source buffer was specifically sized to hold the data
173
174 // Deserialize the file directory index. If an error occurs processing the file,
175 // generate event and return EXECUTION_ERROR.
176
1/1
✓ Branch 10 taken 1 times.
1 Fw::SerializeStatus status = entryBuffer.deserializeTo(this->m_stateFileData[entry].entry.dir);
177
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (status != Fw::FW_SERIALIZE_OK) {
178 this->log_WARNING_HI_FileCorruptedDataError(this->m_stateFile, static_cast<I32>(status));
179 stateFile.close();
180 return Fw::CmdResponse::EXECUTION_ERROR;
181 }
182
1/1
✓ Branch 10 taken 1 times.
1 status = entryBuffer.deserializeTo(this->m_stateFileData[entry].entry.record);
183
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (status != Fw::FW_SERIALIZE_OK) {
184
1/1
✓ Branch 8 taken 1 times.
1 this->log_WARNING_HI_FileCorruptedDataError(this->m_stateFile, static_cast<I32>(status));
185
1/1
✓ Branch 2 taken 1 times.
1 stateFile.close();
186
1/1
✓ Branch 1 taken 1 times.
1 return Fw::CmdResponse::EXECUTION_ERROR;
187 }
188 this->m_stateFileData[entry].used = true;
189 this->m_stateFileData[entry].visited = false;
190
191 // increment the file location
192 fileLoc += size;
193 this->m_stateFileEntries++;
194 }
195 stateFile.close();
196 return Fw::CmdResponse::OK;
197 107 }
198
199 6149 void DpCatalog::getFileState(DpStateEntry& entry) {
200 6149 FW_ASSERT(this->m_stateFileData != nullptr);
201 // search the file state data for the entry
202
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 6149 times.
6149 for (FwSizeType line = 0; line < this->m_stateFileEntries; line++) {
203 // check for a match (compare dir, then id, priority, & time)
204 if (this->m_stateFileData[line].entry.dir == entry.dir && this->m_stateFileData[line].entry == entry) {
205 // update the transmitted state
206 entry.record.set_state(this->m_stateFileData[line].entry.record.get_state());
207 entry.record.set_blocks(this->m_stateFileData[line].entry.record.get_blocks());
208 // mark it as visited for later pruning if necessary
209 this->m_stateFileData[line].visited = true;
210 return;
211 }
212 }
213 }
214
215 2901 void DpCatalog::pruneAndWriteStateFile() {
216 2901 FW_ASSERT(this->m_stateFileData != nullptr);
217
218 // There is a chance that a data product file can disappear after
219 // the state file is written from the last catalog build and transmit.
220 // This function will walk the state file data and write back only
221 // the entries that were visited during the last catalog build. This will
222 // remove any entries that are no longer valid.
223
224 // open the state file
225
1/1
✓ Branch 2 taken 2901 times.
2901 Os::File stateFile;
226 // we open it as a new file so we don't accumulate invalid entries
227 Os::File::Status stat =
228
1/1
✓ Branch 8 taken 2901 times.
2901 stateFile.open(this->m_stateFile.toChar(), Os::File::OPEN_CREATE, Os::FileInterface::OVERWRITE);
229
230
1/2
✓ Branch 0 taken 2901 times.
✗ Branch 1 not taken.
2901 if (stat != Os::File::OP_OK) {
231
1/1
✓ Branch 8 taken 2901 times.
2901 this->log_WARNING_HI_StateFileOpenError(this->m_stateFile, stat);
232 2901 return;
233 }
234
235 // buffer for writing entries
236 BYTE buffer[sizeof(FwIndexType) + DpRecord::SERIALIZED_SIZE];
237 Fw::ExternalSerializeBuffer entryBuffer(buffer, sizeof(buffer));
238
239 // write entries to the state file
240 for (FwSizeType entry = 0; entry < this->m_numDpSlots; entry++) {
241 // only write entries that were used
242 if ((this->m_stateFileData[entry].used) and (this->m_stateFileData[entry].visited)) {
243 // reset the buffer for serializing the entry
244 entryBuffer.resetSer();
245 // serialize the file directory index
246 Fw::SerializeStatus serStat = entryBuffer.serializeFrom(this->m_stateFileData[entry].entry.dir);
247 // Should always fit
248 FW_ASSERT(Fw::FW_SERIALIZE_OK == serStat, serStat);
249 serStat = entryBuffer.serializeFrom(this->m_stateFileData[entry].entry.record);
250 // Should always fit
251 FW_ASSERT(Fw::FW_SERIALIZE_OK == serStat, serStat);
252 // write the entry
253 FwSizeType size = entryBuffer.getSize();
254 // Protect against overflow
255 stat = stateFile.write(buffer, size);
256 if (stat != Os::File::OP_OK) {
257 this->log_WARNING_HI_StateFileWriteError(this->m_stateFile, stat);
258 stateFile.close();
259 return;
260 }
261 }
262 }
263
264 // close the state file
265 stateFile.close();
266 2901 }
267
268 6015 void DpCatalog::appendFileState(const DpStateEntry& entry) {
269 6015 FW_ASSERT(this->m_stateFileData != nullptr);
270 6015 FW_ASSERT(entry.dir < static_cast<FwIndexType>(this->m_numDirectories), static_cast<FwAssertArgType>(entry.dir),
271 static_cast<FwAssertArgType>(this->m_numDirectories));
272
273 // We will append state to the existing state file
274 // TODO: Have to handle case where state file has partially transmitted
275 // state already
276
277 // open the state file
278
1/1
✓ Branch 2 taken 6015 times.
6015 Os::File stateFile;
279 // we open it as a new file so we don't accumulate invalid entries
280
1/1
✓ Branch 8 taken 6015 times.
6015 Os::File::Status stat = stateFile.open(this->m_stateFile.toChar(), Os::File::OPEN_APPEND);
281
1/2
✓ Branch 0 taken 6015 times.
✗ Branch 1 not taken.
6015 if (stat != Os::File::OP_OK) {
282
1/1
✓ Branch 8 taken 6015 times.
6015 this->log_WARNING_HI_StateFileOpenError(this->m_stateFile, stat);
283 6015 return;
284 }
285
286 // buffer for writing entries
287 BYTE buffer[sizeof(FwIndexType) + DpRecord::SERIALIZED_SIZE];
288 Fw::ExternalSerializeBuffer entryBuffer(buffer, sizeof(buffer));
289 // reset the buffer for serializing the entry
290 entryBuffer.resetSer();
291 // serialize the file directory index
292 Fw::SerializeStatus serStat = entryBuffer.serializeFrom(entry.dir);
293 // should fit
294 FW_ASSERT(serStat == Fw::FW_SERIALIZE_OK, serStat);
295 serStat = entryBuffer.serializeFrom(entry.record);
296 // should fit
297 FW_ASSERT(serStat == Fw::FW_SERIALIZE_OK, serStat);
298 // write the entry
299 FwSizeType size = entryBuffer.getSize();
300 stat = stateFile.write(buffer, size);
301 if (stat != Os::File::OP_OK) {
302 stateFile.close();
303 this->log_WARNING_HI_StateFileWriteError(this->m_stateFile, stat);
304 return;
305 }
306
307 // close the state file
308 stateFile.close();
309 6015 }
310
311 112 Fw::CmdResponse DpCatalog::doCatalogBuild() {
312 // check initialization
313
2/3
✓ Branch 4 taken 112 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 112 times.
112 if (not this->checkInit()) {
314 return Fw::CmdResponse::EXECUTION_ERROR;
315 }
316
317 // check that initialization got memory
318
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 112 times.
112 if (0 == this->m_numDpSlots) {
319 this->log_WARNING_HI_NoDpMemory();
320 return Fw::CmdResponse::EXECUTION_ERROR;
321 }
322
323 // make sure a downlink is not in progress
324
2/4
✗ Branch 3 not taken.
✓ Branch 4 taken 112 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 112 times.
112 if (this->m_xmitInProgress) {
325 this->log_WARNING_LO_DpXmitInProgress();
326 return Fw::CmdResponse::EXECUTION_ERROR;
327 }
328
329 // reset state file data
330
1/1
✓ Branch 4 taken 112 times.
112 this->resetStateFileData();
331
332 // load state data from file; proceeding on a failed load would later
333 // overwrite the state file and destroy the transmit state it records
334
1/1
✓ Branch 2 taken 112 times.
112 Fw::CmdResponse response = this->loadStateFile();
335
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 111 times.
112 if (response != Fw::CmdResponse::OK) {
336
1/1
✓ Branch 4 taken 1 times.
1 this->resetStateFileData();
337
1/1
✓ Branch 1 taken 1 times.
1 return response;
338 }
339
340 // reset catalog
341
1/1
✓ Branch 4 taken 111 times.
111 this->resetCatalog();
342
343 // fill the catalog with DP files
344
2/2
✓ Branch 2 taken 111 times.
✓ Branch 7 taken 111 times.
111 response = this->fillBinaryTree();
345
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 111 times.
111 if (response != Fw::CmdResponse::OK) {
346 // clean up the catalog
347 this->resetCatalog();
348 this->resetStateFileData();
349 return response;
350 }
351
352 // prune and rewrite the state file
353
1/1
✓ Branch 4 taken 111 times.
111 this->pruneAndWriteStateFile();
354
355
1/1
✓ Branch 5 taken 111 times.
111 this->log_ACTIVITY_HI_CatalogBuildComplete();
356
357 // Flag so addToCat knows it is good to go
358 111 this->m_catalogBuilt = true;
359
360
1/1
✓ Branch 1 taken 111 times.
111 return Fw::CmdResponse::OK;
361 112 }
362
363 111 Fw::CmdResponse DpCatalog::fillBinaryTree() {
364 // keep cumulative number of files
365 111 FwSizeType totalFiles = 0;
366
367 // get file listings from file system
368 // double bounds to appease static analysis
369
3/4
✓ Branch 4 taken 214 times.
✓ Branch 5 taken 110 times.
✓ Branch 6 taken 214 times.
✗ Branch 7 not taken.
324 for (FwSizeType dir = 0; dir < this->m_numDirectories && dir < static_cast<FwSizeType>(DP_MAX_DIRECTORIES); dir++) {
370 // read in each directory and keep track of total
371
1/1
✓ Branch 9 taken 214 times.
214 this->log_ACTIVITY_LO_ProcessingDirectory(this->m_directories[dir]);
372 214 FwSizeType filesProcessed = 0;
373
374
1/1
✓ Branch 2 taken 214 times.
214 Os::Directory dpDir;
375
1/1
✓ Branch 9 taken 214 times.
214 Os::Directory::Status status = dpDir.open(this->m_directories[dir].toChar(), Os::Directory::OpenMode::READ);
376
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 214 times.
214 if (status != Os::Directory::OP_OK) {
377 this->log_WARNING_HI_DirectoryOpenError(this->m_directories[dir], status);
378 return Fw::CmdResponse::EXECUTION_ERROR;
379 }
380
381 // bound the read loop by the number of entries in the directory
382 214 FwSizeType fileCount = 0;
383
1/1
✓ Branch 2 taken 214 times.
214 status = dpDir.getFileCount(fileCount);
384
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 214 times.
214 if (status != Os::Directory::OP_OK) {
385 this->log_WARNING_HI_DirectoryOpenError(this->m_directories[dir], status);
386 return Fw::CmdResponse::EXECUTION_ERROR;
387 }
388
389 // read entries one at a time so non-DP files do not consume catalog slots
390
1/1
✓ Branch 2 taken 214 times.
214 Fw::String fileName;
391
2/2
✓ Branch 0 taken 3489 times.
✓ Branch 1 taken 214 times.
3703 for (FwSizeType entry = 0; entry < fileCount; entry++) {
392
1/1
✓ Branch 2 taken 3489 times.
3489 status = dpDir.read(fileName);
393
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3489 times.
3489 if (status == Os::Directory::NO_MORE_FILES) {
394 break;
395 }
396
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3489 times.
3489 if (status != Os::Directory::OP_OK) {
397 this->log_WARNING_HI_DirectoryOpenError(this->m_directories[dir], status);
398 return Fw::CmdResponse::EXECUTION_ERROR;
399 }
400
401 // only consider files with the DP extension
402
1/1
✓ Branch 2 taken 3489 times.
3489 const FwSizeType fileNameLength = fileName.length();
403
1/1
✓ Branch 1 taken 3489 times.
3489 const FwSizeType dpExtLength = Fw::StringUtils::string_length(DP_EXT, sizeof(DP_EXT));
404 const FwSignedSizeType loc =
405
1/1
✓ Branch 3 taken 3489 times.
3489 Fw::StringUtils::substring_find_last(fileName.toChar(), fileNameLength, DP_EXT, dpExtLength);
406
407 // Only accept files whose final suffix is the data product extension
408
3/4
✓ Branch 0 taken 3362 times.
✓ Branch 1 taken 127 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3362 times.
3489 if ((-1 == loc) || (static_cast<FwSizeType>(loc) + dpExtLength != fileNameLength)) {
409 127 continue;
410 }
411
412 // stop if there is no free catalog slot for this DP file
413
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 3362 times.
3362 if ((totalFiles + filesProcessed) == this->m_numDpSlots) {
414 break;
415 }
416
417
1/1
✓ Branch 2 taken 3362 times.
3362 Fw::String fullFile;
418 Fw::FormatStatus formatStatus =
419
1/1
✓ Branch 11 taken 3362 times.
3362 fullFile.format("%s/%s", this->m_directories[dir].toChar(), fileName.toChar());
420
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3362 times.
3362 if (formatStatus != Fw::FormatStatus::SUCCESS) {
421 this->log_WARNING_HI_FileNameFormatError(fileName,
422 static_cast<Fw::StringFormatStatus::T>(formatStatus));
423 continue;
424 }
425
426
1/1
✓ Branch 4 taken 3362 times.
3362 const ProcessFileStatus ret = processFile(fullFile, dir);
427
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3362 times.
3362 if (ret == ProcessFileStatus::QUIT) {
428 break;
429 }
430
431
2/2
✓ Branch 0 taken 3359 times.
✓ Branch 1 taken 3 times.
3362 if (ret == ProcessFileStatus::SUCCESS) {
432 3359 filesProcessed++;
433 }
434
435 3362 } // end for each file in a directory
436
437 214 totalFiles += filesProcessed;
438
439
1/1
✓ Branch 13 taken 214 times.
642 this->log_ACTIVITY_HI_ProcessingDirectoryComplete(this->m_directories[dir], static_cast<U32>(totalFiles),
440 428 this->m_pendingFiles, this->m_pendingDpBytes);
441
442 // check to see if catalog is full
443 // that means generated products exceed the catalog size
444
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 213 times.
214 if (totalFiles == this->m_numDpSlots) {
445
1/1
✓ Branch 9 taken 1 times.
1 this->log_WARNING_HI_CatalogFull(this->m_directories[dir]);
446 1 break;
447 }
448 215 } // end for each directory
449
450 111 return Fw::CmdResponse::OK;
451
452 } // end fillBinaryTree()
453
454 2790 FwSizeType DpCatalog::determineDirectory(const Fw::String& fullFile) {
455 2790 FW_ASSERT(this->m_numDirectories <= DP_MAX_DIRECTORIES, static_cast<FwAssertArgType>(this->m_numDirectories));
456 // Grab the directory string (up until the final slash)
457 // Could be found directly w/ a dirname func or regex
458 8370 FwSignedSizeType loc = Fw::StringUtils::substring_find_last(
459 5580 fullFile.toChar(), fullFile.length(), DIRECTORY_DELIMITER,
460 Fw::StringUtils::string_length(DIRECTORY_DELIMITER, sizeof(DIRECTORY_DELIMITER)));
461
462 // Seems like the logic works so long as the path styles match (i.e. relative vs absolute)
463 // Full path resolution might be a worthwhile add
464
465 // No directory delimiter found; return DP_MAX_DIRECTORIES to signal failure
466
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2790 times.
2790 if (-1 == loc) {
467 return DP_MAX_DIRECTORIES;
468 }
469
470
1/2
✓ Branch 4 taken 4195 times.
✗ Branch 5 not taken.
4195 for (FwSizeType dir = 0; dir < this->m_numDirectories; dir++) {
471 4195 const Fw::FileNameString& dir_string = this->m_directories[dir];
472
473 // Compare both strings up to location of final slash
474 // StringUtils::substring_find will return zero if both paths agree
475 // memory safe since both are fixed width strings
476 // and loc is before the fixed width
477
3/4
✓ Branch 7 taken 4195 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 2790 times.
✓ Branch 10 taken 1405 times.
8390 if ((dir_string.length() == static_cast<FwSizeType>(loc)) &&
478
2/2
✓ Branch 16 taken 2790 times.
✓ Branch 17 taken 1405 times.
4195 (Fw::StringUtils::substring_find(dir_string.toChar(), dir_string.length(), fullFile.toChar(),
479 static_cast<FwSizeType>(loc)) == 0)) {
480 2790 return dir;
481 }
482 }
483
484 // No directory matched
485 return DP_MAX_DIRECTORIES;
486 }
487
488 6152 DpCatalog::ProcessFileStatus DpCatalog::processFile(const Fw::String& fullFile, FwSizeType dir) {
489 6152 FW_ASSERT(dir < static_cast<FwSizeType>(DP_MAX_DIRECTORIES), static_cast<FwAssertArgType>(dir));
490 // file class instance for processing files
491
1/1
✓ Branch 2 taken 6152 times.
6152 Os::File dpFile;
492
493 // Working buffer for DP headers
494 6152 U8 dpBuff[Fw::DpContainer::MIN_PACKET_SIZE]; // Header buffer
495
1/1
✓ Branch 2 taken 6152 times.
6152 Fw::Buffer hdrBuff(dpBuff, sizeof(dpBuff)); // buffer for container header decoding
496
1/1
✓ Branch 2 taken 6152 times.
6152 Fw::DpContainer container; // container object for extracting header fields
497
498
1/1
✓ Branch 8 taken 6152 times.
6152 this->log_ACTIVITY_LO_ProcessingFile(fullFile);
499
500 // get file size
501 6152 FwSizeType fileSize = 0;
502
1/1
✓ Branch 5 taken 6152 times.
6152 Os::FileSystem::Status sizeStat = Os::FileSystem::getFileSize(fullFile.toChar(), fileSize);
503
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6152 times.
6152 if (sizeStat != Os::FileSystem::OP_OK) {
504 this->log_WARNING_HI_FileSizeError(fullFile, sizeStat);
505 return ProcessFileStatus::FAILED;
506 }
507
508
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6151 times.
6152 if (fileSize < Fw::DpContainer::MIN_PACKET_SIZE) {
509
1/1
✓ Branch 8 taken 1 times.
1 this->log_WARNING_HI_FileReadError(fullFile, Os::File::BAD_SIZE);
510 1 return ProcessFileStatus::FAILED;
511 }
512
513
1/1
✓ Branch 6 taken 6151 times.
6151 Os::File::Status stat = dpFile.open(fullFile.toChar(), Os::File::OPEN_READ);
514
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6151 times.
6151 if (stat != Os::File::OP_OK) {
515 this->log_WARNING_HI_FileOpenError(fullFile, stat);
516 return ProcessFileStatus::FAILED;
517 }
518
519 // Read DP header and header hash
520 6151 FwSizeType size = Fw::DpContainer::MIN_PACKET_SIZE;
521
522
1/1
✓ Branch 2 taken 6151 times.
6151 stat = dpFile.read(dpBuff, size);
523
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6151 times.
6151 if (stat != Os::File::OP_OK) {
524 this->log_WARNING_HI_FileReadError(fullFile, stat);
525 dpFile.close();
526 return ProcessFileStatus::FAILED;
527 }
528
529 // if full header and hashes aren't read, something's wrong with the file, so skip
530
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6151 times.
6151 if (size != Fw::DpContainer::MIN_PACKET_SIZE) {
531 this->log_WARNING_HI_FileReadError(fullFile, Os::File::BAD_SIZE);
532 dpFile.close();
533 return ProcessFileStatus::FAILED;
534 }
535
536 // if all is well, don't need the file any more
537
1/1
✓ Branch 2 taken 6151 times.
6151 dpFile.close();
538
539 // give buffer to container instance
540
1/1
✓ Branch 2 taken 6151 times.
6151 container.setBuffer(hdrBuff);
541
542 // make sure the header metadata matches its stored hash before trusting it
543
1/1
✓ Branch 2 taken 6151 times.
6151 Utils::HashBuffer storedHash;
544
1/1
✓ Branch 2 taken 6151 times.
6151 Utils::HashBuffer computedHash;
545
1/1
✓ Branch 2 taken 6151 times.
6151 Fw::Success::T hashStatus = container.checkHeaderHash(storedHash, computedHash);
546
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6150 times.
6151 if (hashStatus != Fw::Success::SUCCESS) {
547
4/4
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 13 taken 1 times.
✓ Branch 20 taken 1 times.
2 this->log_WARNING_HI_FileHdrError(fullFile, DpHdrField::CRC, computedHash.asBigEndianU32(),
548 1 storedHash.asBigEndianU32());
549 1 return ProcessFileStatus::FAILED;
550 }
551
552 // reset header deserialization in the container
553
1/1
✓ Branch 2 taken 6150 times.
6150 Fw::SerializeStatus desStat = container.deserializeHeader();
554
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6150 times.
6150 if (desStat != Fw::FW_SERIALIZE_OK) {
555 this->log_WARNING_HI_FileHdrDesError(fullFile, desStat);
556 return ProcessFileStatus::FAILED;
557 }
558
559 6150 const FwSizeType dataSize = container.getDataSize();
560 6150 const FwSizeType expectedDataSize = fileSize - Fw::DpContainer::MIN_PACKET_SIZE;
561
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6150 times.
6150 if (dataSize != expectedDataSize) {
562 this->log_WARNING_HI_FileReadError(fullFile, Os::File::BAD_SIZE);
563 return ProcessFileStatus::FAILED;
564 }
565
566
1/1
✓ Branch 2 taken 6150 times.
6150 Fw::FileNameString canonicalFileName;
567 Fw::FormatStatus canonicalFormatStatus =
568
3/3
✓ Branch 2 taken 6150 times.
✓ Branch 5 taken 6150 times.
✓ Branch 17 taken 6150 times.
18450 canonicalFileName.format(DP_FILENAME_FORMAT, this->m_directories[dir].toChar(), container.getId(),
569
2/2
✓ Branch 2 taken 6150 times.
✓ Branch 9 taken 6150 times.
18450 container.getTimeTag().getSeconds(), container.getTimeTag().getUSeconds());
570
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6150 times.
6150 if (canonicalFormatStatus != Fw::FormatStatus::SUCCESS) {
571 this->log_WARNING_HI_FileNameFormatError(fullFile,
572 static_cast<Fw::StringFormatStatus::T>(canonicalFormatStatus));
573 return ProcessFileStatus::FAILED;
574 }
575
3/3
✓ Branch 5 taken 6150 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 6149 times.
6150 if (canonicalFileName != fullFile) {
576
1/1
✓ Branch 8 taken 1 times.
1 this->log_WARNING_HI_InvalidFileName(fullFile, canonicalFileName);
577 1 return ProcessFileStatus::FAILED;
578 }
579
580 // skip adding an already transmitted file
581
2/3
✓ Branch 2 taken 6149 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 6149 times.
6149 if (container.getState() == Fw::DpState::TRANSMITTED) {
582 this->log_ACTIVITY_HI_DpFileSkipped(fullFile);
583 return ProcessFileStatus::FAILED;
584 }
585
586 // add entry to catalog.
587
1/1
✓ Branch 2 taken 6149 times.
6149 DpStateEntry entry;
588 6149 entry.dir = static_cast<FwIndexType>(dir);
589
1/1
✓ Branch 4 taken 6149 times.
6149 entry.record.set_id(container.getId());
590
1/1
✓ Branch 4 taken 6149 times.
6149 entry.record.set_priority(container.getPriority());
591
2/2
✓ Branch 3 taken 6149 times.
✓ Branch 10 taken 6149 times.
6149 entry.record.set_state(container.getState());
592
3/3
✓ Branch 3 taken 6149 times.
✓ Branch 9 taken 6149 times.
✓ Branch 12 taken 6149 times.
6149 entry.record.set_tSec(container.getTimeTag().getSeconds());
593
3/3
✓ Branch 3 taken 6149 times.
✓ Branch 9 taken 6149 times.
✓ Branch 12 taken 6149 times.
6149 entry.record.set_tSub(container.getTimeTag().getUSeconds());
594
1/1
✓ Branch 2 taken 6149 times.
6149 entry.record.set_size(static_cast<U64>(fileSize));
595
596 // check the state file to see if there is transmit state
597
1/1
✓ Branch 4 taken 6149 times.
6149 this->getFileState(entry);
598
599 // a duplicate insert updates the tree in place; skip it so pending counters are not double-counted
600
2/3
✓ Branch 4 taken 6149 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 6149 times.
6149 if (this->m_dpCatalog.find(entry) == Fw::Success::SUCCESS) {
601 this->log_ACTIVITY_HI_DpFileSkipped(fullFile);
602 return ProcessFileStatus::FAILED;
603 }
604
605 // insert entry into sorted catalog. if can't insert, quit
606
1/1
✓ Branch 4 taken 6149 times.
6149 bool inserted = this->insertEntry(entry);
607
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6149 times.
6149 if (!inserted) {
608 this->log_WARNING_HI_DpInsertError(entry.record);
609 // return and hope new slots open up later
610 return ProcessFileStatus::QUIT;
611 }
612
613 // increment our counters
614 6149 this->m_pendingFiles++;
615 6149 this->m_pendingDpBytes += entry.record.get_size();
616
617 // make sure we haven't exceeded the limit
618
1/2
✗ Branch 8 not taken.
✓ Branch 9 taken 6149 times.
6149 if (this->m_pendingFiles > this->m_numDpSlots) {
619 this->log_WARNING_HI_DpCatalogFull(entry.record);
620 return ProcessFileStatus::QUIT;
621 }
622
623
1/1
✓ Branch 5 taken 6149 times.
6149 this->log_ACTIVITY_HI_DpFileAdded(canonicalFileName);
624
625 // No need to track iterator state - begin() always gives us the highest priority entry
626 // and we remove entries as we transmit them
627
628 6149 return ProcessFileStatus::SUCCESS;
629 6152 }
630
631 // ----------------------------------------------------------------------
632 // DpStateEntry Comparison Ops
633 // ----------------------------------------------------------------------
634 1714096 I8 DpCatalog::DpStateEntry::compareEntries(const DpStateEntry& left, const DpStateEntry& right) {
635 // check priority. Lower is higher priority
636
2/2
✓ Branch 22 taken 635398 times.
✓ Branch 23 taken 1078698 times.
1714096 if (left.record.get_priority() < right.record.get_priority()) {
637 635398 return -1;
638
2/2
✓ Branch 22 taken 329050 times.
✓ Branch 23 taken 749648 times.
1078698 } else if (left.record.get_priority() > right.record.get_priority()) {
639 329050 return 1;
640 }
641
642 // check time. Older is higher priority
643
2/2
✓ Branch 22 taken 238625 times.
✓ Branch 23 taken 511023 times.
749648 else if (left.record.get_tSec() < right.record.get_tSec()) {
644 238625 return -1;
645
2/2
✓ Branch 22 taken 121252 times.
✓ Branch 23 taken 389771 times.
511023 } else if (left.record.get_tSec() > right.record.get_tSec()) {
646 121252 return 1;
647 }
648
649 // check subsecond time. Older is higher priority
650
2/2
✓ Branch 22 taken 2 times.
✓ Branch 23 taken 389769 times.
389771 else if (left.record.get_tSub() < right.record.get_tSub()) {
651 2 return -1;
652
2/2
✓ Branch 22 taken 1 times.
✓ Branch 23 taken 389768 times.
389769 } else if (left.record.get_tSub() > right.record.get_tSub()) {
653 1 return 1;
654 }
655
656 // check ID. Lower is higher priority
657
2/2
✓ Branch 22 taken 177013 times.
✓ Branch 23 taken 212755 times.
389768 else if (left.record.get_id() < right.record.get_id()) {
658 177013 return -1;
659
2/2
✓ Branch 22 taken 136640 times.
✓ Branch 23 taken 76115 times.
212755 } else if (left.record.get_id() > right.record.get_id()) {
660 136640 return 1;
661 }
662
663 // if ids are equal we have two nodes with the same value
664 else {
665 76115 return 0;
666 }
667 }
668
669 662246 bool DpCatalog::DpStateEntry::operator==(const DpStateEntry& other) const {
670 662246 return compareEntries(*this, other) == 0;
671 }
672 1 bool DpCatalog::DpStateEntry::operator!=(const DpStateEntry& other) const {
673 1 return compareEntries(*this, other) != 0;
674 }
675
676 1 bool DpCatalog::DpStateEntry::operator>(const DpStateEntry& other) const {
677 1 return compareEntries(*this, other) > 0;
678 }
679 1051848 bool DpCatalog::DpStateEntry::operator<(const DpStateEntry& other) const {
680 1051848 return compareEntries(*this, other) < 0;
681 }
682
683 66960 bool DpCatalog::insertEntry(DpStateEntry& entry) {
684 // Insert into the RedBlackTreeSet
685 // The tree maintains sorting by priority, time, and ID via DpStateEntry comparison operators
686
1/1
✓ Branch 5 taken 66960 times.
66960 Fw::Success status = this->m_dpCatalog.insert(entry);
687 133920 return (status == Fw::Success::SUCCESS);
688 66960 }
689
690 6129 void DpCatalog::sendNextEntry() {
691 // Use xmit flag to break upon STOP_XMIT_CATALOG
692
2/4
✗ Branch 3 not taken.
✓ Branch 4 taken 6129 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6129 times.
6129 if (this->m_xmitInProgress != true) {
693 return;
694 }
695
696 // Look for the next entry to send
697
1/1
✓ Branch 2 taken 6129 times.
6129 DpStateEntry entry;
698
3/3
✓ Branch 4 taken 6129 times.
✓ Branch 6 taken 111 times.
✓ Branch 7 taken 6018 times.
6129 if (!this->findNextEntry(entry)) {
699 // if no entry found, we are done
700 111 this->m_xmitInProgress = false;
701
1/1
✓ Branch 9 taken 111 times.
111 this->log_ACTIVITY_HI_CatalogXmitCompleted(this->m_xmitBytes);
702
2/2
✓ Branch 5 taken 111 times.
✓ Branch 8 taken 111 times.
111 this->dispatchWaitedResponse(Fw::CmdResponse::OK);
703 111 return;
704 }
705
706 // Save current entry for fileDone_handler
707
1/1
✓ Branch 4 taken 6018 times.
6018 this->m_currentXmitEntry = entry;
708 6018 this->m_hasCurrentXmit = true;
709
710 // Build file name based on the found entry
711 Fw::FormatStatus formatStatus =
712
1/1
✓ Branch 16 taken 6018 times.
24072 this->m_currXmitFileName.format(DP_FILENAME_FORMAT, this->m_directories[entry.dir].toChar(),
713 18054 entry.record.get_id(), entry.record.get_tSec(), entry.record.get_tSub());
714
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6018 times.
6018 if (formatStatus != Fw::FormatStatus::SUCCESS) {
715 this->log_WARNING_HI_FileNameFormatError(this->m_currXmitFileName,
716 static_cast<Fw::StringFormatStatus::T>(formatStatus));
717 // No send is in flight, so no fileDone will arrive: abort the transmit
718 // rather than leaving it wedged in progress
719 this->m_hasCurrentXmit = false;
720 this->m_xmitInProgress = false;
721 this->dispatchWaitedResponse(Fw::CmdResponse::EXECUTION_ERROR);
722 return;
723 }
724
1/1
✓ Branch 11 taken 6018 times.
12036 this->log_ACTIVITY_LO_SendingProduct(this->m_currXmitFileName, static_cast<U32>(entry.record.get_size()),
725 6018 entry.record.get_priority());
726
1/1
✓ Branch 9 taken 6018 times.
6018 Svc::SendFileResponse resp = this->fileOut_out(0, this->m_currXmitFileName, this->m_currXmitFileName, 0, 0);
727
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 6018 times.
6018 if (resp.get_status() != Svc::SendFileStatus::STATUS_OK) {
728 this->log_WARNING_HI_DpFileSendError(this->m_currXmitFileName, resp.get_status());
729 // A rejected send produces no fileDone callback: abort the transmit
730 // rather than leaving it wedged in progress
731 this->m_hasCurrentXmit = false;
732 this->m_xmitInProgress = false;
733 this->dispatchWaitedResponse(Fw::CmdResponse::EXECUTION_ERROR);
734 }
735 6129 } // end sendNextEntry()
736
737 63304 bool DpCatalog::findNextEntry(DpStateEntry& entry) {
738 // If catalog is empty, return false
739
3/3
✓ Branch 6 taken 63304 times.
✓ Branch 8 taken 1119 times.
✓ Branch 9 taken 62185 times.
63304 if (this->m_dpCatalog.getSize() == 0) {
740 1119 return false;
741 }
742
743 // Get the highest priority entry (begin() returns highest priority)
744 // Since we remove entries as we transmit them, begin() always gives us the next entry
745
1/1
✓ Branch 4 taken 62185 times.
62185 typename Fw::RedBlackTreeSet<DpStateEntry, DP_MAX_FILES>::ConstIterator iter = this->m_dpCatalog.begin();
746
747 // Verify iterator is valid
748
3/4
✓ Branch 4 taken 62185 times.
✓ Branch 8 taken 62185 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 62185 times.
62185 if (iter == this->m_dpCatalog.end()) {
749 return false;
750 }
751
752 // Get the entry
753
2/2
✓ Branch 1 taken 62185 times.
✓ Branch 5 taken 62185 times.
62185 entry = *iter;
754
755 62185 return true;
756 62185 }
757
758 3015 bool DpCatalog::checkInit() {
759
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 3015 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 3014 times.
3015 if (not this->m_initialized) {
760 1 this->log_WARNING_HI_ComponentNotInitialized();
761 1 return false;
762
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 3014 times.
3014 } else if (0 == this->m_numDpSlots) {
763 this->log_WARNING_HI_ComponentNoMemory();
764 return false;
765 }
766
767 3014 return true;
768 }
769
770 618 void DpCatalog::shutdown() {
771 // only try to deallocate if both pointers are non-zero
772 // it's a way to more gracefully shut down if there are missing
773 // pointers
774
2/4
✓ Branch 4 taken 618 times.
✗ Branch 5 not taken.
✓ Branch 10 taken 618 times.
✗ Branch 11 not taken.
618 if ((this->m_allocator != nullptr) and (this->m_memPtr != nullptr)) {
775 618 this->m_allocator->deallocate(this->m_allocatorId, this->m_memPtr);
776 }
777 618 }
778
779 // ----------------------------------------------------------------------
780 // Handler implementations for user-defined typed input ports
781 // ----------------------------------------------------------------------
782
783 6020 void DpCatalog ::fileDone_handler(FwIndexType portNum, const Svc::SendFileResponse& resp) {
784 // check file status
785
2/2
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 6017 times.
6020 if (resp.get_status() != Svc::SendFileStatus::STATUS_OK) {
786
2/2
✓ Branch 10 taken 3 times.
✓ Branch 17 taken 3 times.
3 this->log_WARNING_HI_DpFileXmitError(this->m_currXmitFileName, resp.get_status());
787 3 this->m_xmitInProgress = false;
788
2/2
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
3 this->dispatchWaitedResponse(Fw::CmdResponse::EXECUTION_ERROR);
789 3 return;
790 }
791
792 // Catalog cleared while this file was sent; clear xmit state and answer any waited command
793
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 6017 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 6015 times.
6017 if (!this->m_catalogBuilt) {
794 2 this->m_hasCurrentXmit = false;
795 2 this->m_xmitInProgress = false;
796
2/2
✓ Branch 5 taken 2 times.
✓ Branch 8 taken 2 times.
2 this->dispatchWaitedResponse(Fw::CmdResponse::EXECUTION_ERROR);
797 2 return;
798 }
799
800 // Should have a valid current transmit entry
801 6015 FW_ASSERT(this->m_hasCurrentXmit);
802
803 // Reduce pending
804 6015 this->m_pendingDpBytes -= this->m_currentXmitEntry.record.get_size();
805 6015 this->m_pendingFiles--;
806 // Log File Complete & pending
807
1/1
✓ Branch 16 taken 6015 times.
6015 this->log_ACTIVITY_LO_ProductComplete(this->m_currXmitFileName, this->m_pendingFiles, this->m_pendingDpBytes);
808
809 // mark the entry as transmitted
810
1/1
✓ Branch 6 taken 6015 times.
6015 this->m_currentXmitEntry.record.set_state(Fw::DpState::TRANSMITTED);
811 // update the transmitted state in the state file
812
1/1
✓ Branch 7 taken 6015 times.
6015 this->appendFileState(this->m_currentXmitEntry);
813 // add the size
814 6015 this->m_xmitBytes += this->m_currentXmitEntry.record.get_size();
815
816 // Remove from catalog
817
1/1
✓ Branch 7 taken 6015 times.
6015 Fw::Success status = this->m_dpCatalog.remove(this->m_currentXmitEntry);
818 6015 FW_ASSERT(status == Fw::Success::SUCCESS);
819
820 6015 this->m_hasCurrentXmit = false;
821
822 // send the next entry, if it exists
823
1/1
✓ Branch 4 taken 6015 times.
6015 this->sendNextEntry();
824 6015 }
825
826 1 void DpCatalog ::pingIn_handler(FwIndexType portNum, U32 key) {
827 // return code for health ping
828 1 this->pingOut_out(0, key);
829 1 }
830
831 2790 void DpCatalog ::addToCat_handler(FwIndexType portNum,
832 const Fw::StringBase& fileName,
833 FwDpPriorityType priority,
834 FwSizeType size) {
835 // check initialization
836
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 2790 times.
2790 if (not this->checkInit()) {
837 return;
838 }
839
840 // check that initialization got memory
841
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 2790 times.
2790 if (0 == this->m_numDpSlots) {
842 this->log_WARNING_HI_NoDpMemory();
843 return;
844 }
845
846 // Check the catalog has been built
847
2/4
✗ Branch 3 not taken.
✓ Branch 4 taken 2790 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2790 times.
2790 if (not this->m_catalogBuilt) {
848 this->log_ACTIVITY_HI_NotLoaded(fileName);
849 return;
850 }
851
852 // Both of these are grabbed from the header
853 (void)priority;
854 (void)size;
855
856 // Since this is a runtime addition
857 // Check if file is in one of our directories
858
2/2
✓ Branch 8 taken 2790 times.
✓ Branch 12 taken 2790 times.
2790 FwSizeType dir = this->determineDirectory(fileName);
859
860 // Not in one of our directories; skip this file
861
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2790 times.
2790 if (dir == DP_MAX_DIRECTORIES) {
862 this->log_WARNING_HI_DirectoryNotManaged(fileName);
863 return;
864 }
865
866
2/2
✓ Branch 8 taken 2790 times.
✓ Branch 12 taken 2790 times.
2790 const ProcessFileStatus ret = processFile(fileName, dir);
867
868
1/2
✓ Branch 0 taken 2790 times.
✗ Branch 1 not taken.
2790 if (ret == ProcessFileStatus::SUCCESS) {
869 // If we already finished, sendNext only if remainingActive
870
5/8
✗ Branch 3 not taken.
✓ Branch 4 taken 2790 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 2786 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 4 times.
✓ Branch 12 taken 4 times.
✗ Branch 13 not taken.
2790 if (!this->m_xmitInProgress && this->m_remainActive) {
871 4 this->m_xmitInProgress = true;
872 4 this->sendNextEntry();
873 }
874 // Otherwise, Current File finishing will invoke sendNextFile & find the right file
875 // Or will be manually tx-ed at next command
876
877 // prune and rewrite the state file
878 2790 this->pruneAndWriteStateFile();
879 }
880 }
881
882 // ----------------------------------------------------------------------
883 // Handler implementations for commands
884 // ----------------------------------------------------------------------
885
886 112 void DpCatalog ::BUILD_CATALOG_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
887 // invoke helper
888
2/2
✓ Branch 6 taken 112 times.
✓ Branch 9 taken 112 times.
112 this->cmdResponse_out(opCode, cmdSeq, this->doCatalogBuild());
889 112 }
890
891 113 void DpCatalog ::START_XMIT_CATALOG_cmdHandler(FwOpcodeType opCode,
892 U32 cmdSeq,
893 const Fw::Wait& wait,
894 bool remainActive) {
895 113 this->m_remainActive = remainActive;
896
897 // Arm the waited response before starting: an empty catalog completes the
898 // transmit inside doCatalogXmit and must still answer a waited command
899
2/2
✓ Branch 4 taken 49 times.
✓ Branch 5 taken 64 times.
113 if (Fw::Wait::WAIT == wait) {
900 49 this->m_xmitCmdWait = true;
901 49 this->m_xmitOpCode = opCode;
902 49 this->m_xmitCmdSeq = cmdSeq;
903 }
904
905
1/1
✓ Branch 2 taken 113 times.
113 Fw::CmdResponse resp = this->doCatalogXmit();
906 113 FW_ASSERT(resp.isValid(), static_cast<FwAssertArgType>(resp.e));
907
908
2/2
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 110 times.
113 if (resp != Fw::CmdResponse::OK) {
909 3 this->m_xmitCmdWait = false;
910 3 this->m_xmitOpCode = 0;
911 3 this->m_xmitCmdSeq = 0;
912
2/2
✓ Branch 6 taken 3 times.
✓ Branch 9 taken 3 times.
3 this->cmdResponse_out(opCode, cmdSeq, resp);
913
2/2
✓ Branch 4 taken 62 times.
✓ Branch 5 taken 48 times.
110 } else if (Fw::Wait::NO_WAIT == wait) {
914
2/2
✓ Branch 6 taken 62 times.
✓ Branch 9 taken 62 times.
62 this->cmdResponse_out(opCode, cmdSeq, resp);
915 }
916 226 }
917
918 113 Fw::CmdResponse DpCatalog::doCatalogXmit() {
919 // check initialization
920
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 112 times.
113 if (not this->checkInit()) {
921 1 return Fw::CmdResponse::EXECUTION_ERROR;
922 }
923
924 // check that initialization got memory
925
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 112 times.
112 if (0 == this->m_numDpSlots) {
926 this->log_WARNING_HI_NoDpMemory();
927 return Fw::CmdResponse::EXECUTION_ERROR;
928 }
929
930 // make sure a downlink is not in progress
931
2/4
✗ Branch 3 not taken.
✓ Branch 4 taken 112 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 112 times.
112 if (this->m_xmitInProgress) {
932 this->log_WARNING_LO_DpXmitInProgress();
933 return Fw::CmdResponse::EXECUTION_ERROR;
934 }
935
936 // Check the catalog has been built
937
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 112 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 110 times.
112 if (not this->m_catalogBuilt) {
938 2 this->log_WARNING_HI_XmitUnbuiltCatalog();
939 2 return Fw::CmdResponse::EXECUTION_ERROR;
940 }
941
942 // start transmission
943 110 this->m_xmitBytes = 0;
944
945 110 this->m_xmitInProgress = true;
946 // Step 3b - search for and send first entry
947 110 this->sendNextEntry();
948 110 return Fw::CmdResponse::OK;
949 }
950
951 3 void DpCatalog ::STOP_XMIT_CATALOG_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
952
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 2 times.
3 if (not this->m_xmitInProgress) {
953 1 this->log_WARNING_LO_XmitNotActive();
954 // benign error, so don't fail the command
955
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
956 } else {
957 2 this->log_ACTIVITY_HI_CatalogXmitStopped(this->m_xmitBytes);
958 // Disarm the flag so next sendNextEntry stops transmission
959 2 this->m_xmitInProgress = false;
960 // Respond to original cmd to start xmit
961 // (if we haven't already)
962
2/2
✓ Branch 5 taken 2 times.
✓ Branch 8 taken 2 times.
2 this->dispatchWaitedResponse(Fw::CmdResponse::OK);
963
964 // Respond to this command
965
2/2
✓ Branch 6 taken 2 times.
✓ Branch 9 taken 2 times.
2 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
966 }
967 3 }
968
969 2 void DpCatalog ::CLEAR_CATALOG_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
970 2 this->resetCatalog();
971 2 this->resetStateFileData();
972
973
2/2
✓ Branch 6 taken 2 times.
✓ Branch 9 taken 2 times.
2 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
974 2 }
975
976 118 void DpCatalog ::dispatchWaitedResponse(Fw::CmdResponse response) {
977
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 118 times.
✓ Branch 5 taken 48 times.
✓ Branch 6 taken 70 times.
118 if (this->m_xmitCmdWait) {
978
2/2
✓ Branch 6 taken 48 times.
✓ Branch 17 taken 48 times.
48 this->cmdResponse_out(this->m_xmitOpCode, this->m_xmitCmdSeq, response);
979
980 // Prevent a Duplicate Cmd Response
981 48 this->m_xmitCmdWait = false;
982 48 this->m_xmitOpCode = 0;
983 48 this->m_xmitCmdSeq = 0;
984 }
985 118 }
986
987 } // namespace Svc
988