GCC Code Coverage Report


Directory: Svc/DpCatalog/
File: DpCatalog.cpp
Date: 2026-10-14 21:18:41
Exec Total Coverage
Lines: 415 501 82.8%
Functions: 32 33 97.0%
Branches: 340 470 72.3%

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 533 times.
✓ Branch 21 taken 533 times.
✓ Branch 39 taken 1066 times.
✓ Branch 42 taken 1066 times.
✓ Branch 43 taken 533 times.
✓ Branch 53 taken 533 times.
✓ Branch 91 taken 533 times.
✗ Branch 121 not taken.
✗ Branch 122 not taken.
✗ Branch 124 not taken.
✗ Branch 125 not taken.
✗ Branch 135 not taken.
✗ Branch 136 not taken.
1599 DpCatalog ::DpCatalog(const char* const compName) : DpCatalogComponentBase(compName) {
26 // Members are default-initialized via in-class initializers in the header.
27 533 }
28
29 627 void DpCatalog::configure(const Fw::ExternalArray<Fw::FileNameString>& directories,
30 Fw::FileNameString& stateFile,
31 FwEnumStoreType memId,
32 Fw::MemAllocator& allocator) {
33 627 const FwSizeType numDirs = directories.getSize();
34 // Do some assertion checks
35 627 FW_ASSERT(numDirs <= DP_MAX_DIRECTORIES, static_cast<FwAssertArgType>(numDirs));
36
37
1/1
✓ Branch 7 taken 627 times.
627 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 627 this->m_memSize = DP_MAX_FILES * slotSize;
44 627 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 627 times.
627 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 627 times.
✗ Branch 5 not taken.
✓ Branch 10 taken 627 times.
✗ Branch 11 not taken.
627 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 627 const FwSizeType allocatedSlots = this->m_memSize / slotSize;
54 627 this->m_numDpSlots = (allocatedSlots < DP_MAX_FILES) ? allocatedSlots : DP_MAX_FILES;
55 // Initialize the catalog
56
1/1
✓ Branch 4 taken 627 times.
627 this->resetCatalog();
57 // assign pointer for the state file storage
58 627 this->m_stateFileData = static_cast<DpDstateFileEntry*>(this->m_memPtr);
59 627 } 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 730 times.
✓ Branch 1 taken 627 times.
1357 for (FwSizeType dir = 0; dir < numDirs; dir++) {
67
2/2
✓ Branch 2 taken 730 times.
✓ Branch 11 taken 730 times.
730 this->m_directories[dir] = directories[dir];
68 }
69 627 this->m_numDirectories = numDirs;
70
71 // store allocator
72 627 this->m_allocator = &allocator;
73 627 this->m_allocatorId = memId;
74 627 this->m_initialized = true;
75 627 }
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 1257 void DpCatalog::resetCatalog() {
88 // Clear the catalog
89 1257 this->m_dpCatalog.clear();
90 // Clear transmission state
91 1257 this->m_hasCurrentXmit = false;
92 // Reset counters
93 1257 this->m_pendingFiles = 0;
94 1257 this->m_pendingDpBytes = 0;
95 // Mark the catalog as un-built
96 1257 this->m_catalogBuilt = false;
97 1257 }
98
99 128 void DpCatalog::resetStateFileData() {
100 // clear state file data
101
2/2
✓ Branch 4 taken 16256 times.
✓ Branch 5 taken 128 times.
16384 for (FwSizeType slot = 0; slot < this->m_numDpSlots; slot++) {
102 16256 this->m_stateFileData[slot].used = false;
103 16256 this->m_stateFileData[slot].visited = false;
104
1/3
✓ Branch 9 taken 16256 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
16256 (void)new (&this->m_stateFileData[slot].entry.record) DpRecord();
105 }
106 128 this->m_stateFileEntries = 0;
107 128 }
108
109 124 Fw::CmdResponse DpCatalog::loadStateFile() {
110 124 FW_ASSERT(this->m_stateFileData != nullptr);
111
112 // Make sure that a file was specified
113
3/3
✓ Branch 6 taken 124 times.
✓ Branch 8 taken 14 times.
✓ Branch 9 taken 110 times.
124 if (this->m_stateFile.length() == 0) {
114
1/1
✓ Branch 5 taken 14 times.
14 this->log_WARNING_LO_NoStateFileSpecified();
115
1/1
✓ Branch 1 taken 14 times.
14 return Fw::CmdResponse::OK;
116 }
117
118 // buffer for reading entries
119
120 110 BYTE buffer[sizeof(FwIndexType) + DpRecord::SERIALIZED_SIZE];
121
1/1
✓ Branch 2 taken 110 times.
110 Fw::ExternalSerializeBuffer entryBuffer(buffer, sizeof(buffer));
122
123 // open the state file
124
1/1
✓ Branch 2 taken 110 times.
110 Os::File stateFile;
125
1/1
✓ Branch 8 taken 110 times.
110 Os::File::Status stat = stateFile.open(this->m_stateFile.toChar(), Os::File::OPEN_READ);
126
2/2
✓ Branch 0 taken 107 times.
✓ Branch 1 taken 3 times.
110 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 107 times.
107 this->log_WARNING_LO_NoStateFile(this->m_stateFile);
129
1/1
✓ Branch 1 taken 107 times.
107 return Fw::CmdResponse::OK;
130 }
131
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 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 3 FwSizeType fileLoc = 0;
137 3 this->m_stateFileEntries = 0;
138
139 // read entries from the state file
140
1/2
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
5 for (FwSizeType entry = 0; entry < this->m_numDpSlots; entry++) {
141 5 FwSizeType size = static_cast<FwSizeType>(sizeof(buffer));
142 // read the directory index
143
1/1
✓ Branch 2 taken 5 times.
5 stat = stateFile.read(buffer, size);
144
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 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
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 if (0 == size) {
151 // no more entries
152 2 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 3 times.
3 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 3 times.
3 Fw::SerializeStatus serStat = entryBuffer.setBuffLen(static_cast<Fw::Serializable::SizeType>(size));
167 // should always fit
168 3 FW_ASSERT(Fw::FW_SERIALIZE_OK == serStat, serStat);
169
1/1
✓ Branch 2 taken 3 times.
3 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 3 times.
3 Fw::SerializeStatus status = entryBuffer.deserializeTo(this->m_stateFileData[entry].entry.dir);
177
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 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 3 times.
3 status = entryBuffer.deserializeTo(this->m_stateFileData[entry].entry.record);
183
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 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 2 this->m_stateFileData[entry].used = true;
189 2 this->m_stateFileData[entry].visited = false;
190
191 // increment the file location
192 2 fileLoc += size;
193 2 this->m_stateFileEntries++;
194 }
195
1/1
✓ Branch 2 taken 2 times.
2 stateFile.close();
196
1/1
✓ Branch 1 taken 2 times.
2 return Fw::CmdResponse::OK;
197 110 }
198
199 6261 void DpCatalog::getFileState(DpStateEntry& entry) {
200 6261 FW_ASSERT(this->m_stateFileData != nullptr);
201 // search the file state data for the entry
202
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 6259 times.
6261 for (FwSizeType line = 0; line < this->m_stateFileEntries; line++) {
203 // check for a match (compare dir, then id, priority, & time)
204
3/6
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 20 taken 2 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 2 times.
✗ Branch 23 not taken.
2 if (this->m_stateFileData[line].entry.dir == entry.dir && this->m_stateFileData[line].entry == entry) {
205 // update the transmitted state
206 2 entry.record.set_state(this->m_stateFileData[line].entry.record.get_state());
207 2 entry.record.set_blocks(this->m_stateFileData[line].entry.record.get_blocks());
208 // mark it as visited for later pruning if necessary
209 2 this->m_stateFileData[line].visited = true;
210 2 return;
211 }
212 }
213 }
214
215 3520 void DpCatalog::pruneAndWriteStateFile() {
216 3520 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 3520 times.
3520 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 3520 times.
3520 stateFile.open(this->m_stateFile.toChar(), Os::File::OPEN_CREATE, Os::FileInterface::OVERWRITE);
229
230
2/2
✓ Branch 0 taken 3517 times.
✓ Branch 1 taken 3 times.
3520 if (stat != Os::File::OP_OK) {
231
1/1
✓ Branch 8 taken 3517 times.
3517 this->log_WARNING_HI_StateFileOpenError(this->m_stateFile, stat);
232 3517 return;
233 }
234
235 // buffer for writing entries
236 3 BYTE buffer[sizeof(FwIndexType) + DpRecord::SERIALIZED_SIZE];
237
1/1
✓ Branch 2 taken 3 times.
3 Fw::ExternalSerializeBuffer entryBuffer(buffer, sizeof(buffer));
238
239 // write entries to the state file
240
2/2
✓ Branch 4 taken 381 times.
✓ Branch 5 taken 3 times.
384 for (FwSizeType entry = 0; entry < this->m_numDpSlots; entry++) {
241 // only write entries that were used
242
5/8
✗ Branch 6 not taken.
✓ Branch 7 taken 381 times.
✓ Branch 8 taken 2 times.
✓ Branch 9 taken 379 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 2 times.
✓ Branch 18 taken 2 times.
✗ Branch 19 not taken.
381 if ((this->m_stateFileData[entry].used) and (this->m_stateFileData[entry].visited)) {
243 // reset the buffer for serializing the entry
244
1/1
✓ Branch 2 taken 2 times.
2 entryBuffer.resetSer();
245 // serialize the file directory index
246
1/1
✓ Branch 9 taken 2 times.
2 Fw::SerializeStatus serStat = entryBuffer.serializeFrom(this->m_stateFileData[entry].entry.dir);
247 // Should always fit
248 2 FW_ASSERT(Fw::FW_SERIALIZE_OK == serStat, serStat);
249
1/1
✓ Branch 10 taken 2 times.
2 serStat = entryBuffer.serializeFrom(this->m_stateFileData[entry].entry.record);
250 // Should always fit
251 2 FW_ASSERT(Fw::FW_SERIALIZE_OK == serStat, serStat);
252 // write the entry
253
1/1
✓ Branch 3 taken 2 times.
2 FwSizeType size = entryBuffer.getSize();
254 // Protect against overflow
255
1/1
✓ Branch 2 taken 2 times.
2 stat = stateFile.write(buffer, size);
256
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 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
1/1
✓ Branch 2 taken 3 times.
3 stateFile.close();
266 3520 }
267
268 6120 void DpCatalog::appendFileState(const DpStateEntry& entry) {
269 6120 FW_ASSERT(this->m_stateFileData != nullptr);
270 6120 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 6120 times.
6120 Os::File stateFile;
279 // we open it as a new file so we don't accumulate invalid entries
280
1/1
✓ Branch 8 taken 6120 times.
6120 Os::File::Status stat = stateFile.open(this->m_stateFile.toChar(), Os::File::OPEN_APPEND);
281
2/2
✓ Branch 0 taken 6119 times.
✓ Branch 1 taken 1 times.
6120 if (stat != Os::File::OP_OK) {
282
1/1
✓ Branch 8 taken 6119 times.
6119 this->log_WARNING_HI_StateFileOpenError(this->m_stateFile, stat);
283 6119 return;
284 }
285
286 // buffer for writing entries
287 1 BYTE buffer[sizeof(FwIndexType) + DpRecord::SERIALIZED_SIZE];
288
1/1
✓ Branch 2 taken 1 times.
1 Fw::ExternalSerializeBuffer entryBuffer(buffer, sizeof(buffer));
289 // reset the buffer for serializing the entry
290
1/1
✓ Branch 2 taken 1 times.
1 entryBuffer.resetSer();
291 // serialize the file directory index
292
1/1
✓ Branch 3 taken 1 times.
1 Fw::SerializeStatus serStat = entryBuffer.serializeFrom(entry.dir);
293 // should fit
294 1 FW_ASSERT(serStat == Fw::FW_SERIALIZE_OK, serStat);
295
1/1
✓ Branch 7 taken 1 times.
1 serStat = entryBuffer.serializeFrom(entry.record);
296 // should fit
297 1 FW_ASSERT(serStat == Fw::FW_SERIALIZE_OK, serStat);
298 // write the entry
299
1/1
✓ Branch 3 taken 1 times.
1 FwSizeType size = entryBuffer.getSize();
300
1/1
✓ Branch 2 taken 1 times.
1 stat = stateFile.write(buffer, size);
301
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 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
1/1
✓ Branch 2 taken 1 times.
1 stateFile.close();
309 6120 }
310
311 124 Fw::CmdResponse DpCatalog::doCatalogBuild() {
312 // check initialization
313
2/3
✓ Branch 4 taken 124 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 124 times.
124 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 124 times.
124 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 124 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 124 times.
124 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 124 times.
124 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 124 times.
124 Fw::CmdResponse response = this->loadStateFile();
335
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 123 times.
124 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 123 times.
123 this->resetCatalog();
342
343 // fill the catalog with DP files
344
2/2
✓ Branch 2 taken 123 times.
✓ Branch 7 taken 123 times.
123 response = this->fillBinaryTree();
345
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 123 times.
123 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 123 times.
123 this->pruneAndWriteStateFile();
354
355
1/1
✓ Branch 5 taken 123 times.
123 this->log_ACTIVITY_HI_CatalogBuildComplete();
356
357 // Flag so addToCat knows it is good to go
358 123 this->m_catalogBuilt = true;
359
360
1/1
✓ Branch 1 taken 123 times.
123 return Fw::CmdResponse::OK;
361 124 }
362
363 123 Fw::CmdResponse DpCatalog::fillBinaryTree() {
364 // keep cumulative number of files
365 123 FwSizeType totalFiles = 0;
366
367 // get file listings from file system
368 // double bounds to appease static analysis
369
3/4
✓ Branch 4 taken 226 times.
✓ Branch 5 taken 122 times.
✓ Branch 6 taken 226 times.
✗ Branch 7 not taken.
348 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 226 times.
226 this->log_ACTIVITY_LO_ProcessingDirectory(this->m_directories[dir]);
372 226 FwSizeType filesProcessed = 0;
373
374
1/1
✓ Branch 2 taken 226 times.
226 Os::Directory dpDir;
375
1/1
✓ Branch 9 taken 226 times.
226 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 226 times.
226 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 226 FwSizeType fileCount = 0;
383
1/1
✓ Branch 2 taken 226 times.
226 status = dpDir.getFileCount(fileCount);
384
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 226 times.
226 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 226 times.
226 Fw::String fileName;
391
2/2
✓ Branch 0 taken 2996 times.
✓ Branch 1 taken 226 times.
3222 for (FwSizeType entry = 0; entry < fileCount; entry++) {
392
1/1
✓ Branch 2 taken 2996 times.
2996 status = dpDir.read(fileName);
393
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2996 times.
2996 if (status == Os::Directory::NO_MORE_FILES) {
394 ✗ break;
395 }
396
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2996 times.
2996 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 2996 times.
2996 const FwSizeType fileNameLength = fileName.length();
403
1/1
✓ Branch 1 taken 2996 times.
2996 const FwSizeType dpExtLength = Fw::StringUtils::string_length(DP_EXT, sizeof(DP_EXT));
404 const FwSignedSizeType loc =
405
1/1
✓ Branch 3 taken 2996 times.
2996 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 2867 times.
✓ Branch 1 taken 129 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2867 times.
2996 if ((-1 == loc) || (static_cast<FwSizeType>(loc) + dpExtLength != fileNameLength)) {
409 129 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 2867 times.
2867 if ((totalFiles + filesProcessed) == this->m_numDpSlots) {
414 ✗ break;
415 }
416
417
1/1
✓ Branch 2 taken 2867 times.
2867 Fw::String fullFile;
418 Fw::FormatStatus formatStatus =
419
1/1
✓ Branch 11 taken 2867 times.
2867 fullFile.format("%s/%s", this->m_directories[dir].toChar(), fileName.toChar());
420
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2867 times.
2867 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 2867 times.
2867 const ProcessFileStatus ret = processFile(fullFile, dir);
427
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2867 times.
2867 if (ret == ProcessFileStatus::QUIT) {
428 ✗ break;
429 }
430
431
2/2
✓ Branch 0 taken 2862 times.
✓ Branch 1 taken 5 times.
2867 if (ret == ProcessFileStatus::SUCCESS) {
432 2862 filesProcessed++;
433 }
434
435 2867 } // end for each file in a directory
436
437 226 totalFiles += filesProcessed;
438
439
1/1
✓ Branch 13 taken 226 times.
678 this->log_ACTIVITY_HI_ProcessingDirectoryComplete(this->m_directories[dir], static_cast<U32>(totalFiles),
440 452 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 225 times.
226 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 227 } // end for each directory
449
450 123 return Fw::CmdResponse::OK;
451
452 } // end fillBinaryTree()
453
454 3397 FwSizeType DpCatalog::determineDirectory(const Fw::String& fullFile) {
455 3397 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 10191 FwSignedSizeType loc = Fw::StringUtils::substring_find_last(
459 6794 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 3397 times.
3397 if (-1 == loc) {
467 ✗ return DP_MAX_DIRECTORIES;
468 }
469
470
1/2
✓ Branch 4 taken 5083 times.
✗ Branch 5 not taken.
5083 for (FwSizeType dir = 0; dir < this->m_numDirectories; dir++) {
471 5083 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 5083 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 3397 times.
✓ Branch 10 taken 1686 times.
10166 if ((dir_string.length() == static_cast<FwSizeType>(loc)) &&
478
2/2
✓ Branch 16 taken 3397 times.
✓ Branch 17 taken 1686 times.
5083 (Fw::StringUtils::substring_find(dir_string.toChar(), dir_string.length(), fullFile.toChar(),
479 static_cast<FwSizeType>(loc)) == 0)) {
480 3397 return dir;
481 }
482 }
483
484 // No directory matched
485 ✗ return DP_MAX_DIRECTORIES;
486 }
487
488 6264 DpCatalog::ProcessFileStatus DpCatalog::processFile(const Fw::String& fullFile, FwSizeType dir) {
489 6264 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 6264 times.
6264 Os::File dpFile;
492
493 // Working buffer for DP headers
494 6264 U8 dpBuff[Fw::DpContainer::MIN_PACKET_SIZE]; // Header buffer
495
1/1
✓ Branch 2 taken 6264 times.
6264 Fw::Buffer hdrBuff(dpBuff, sizeof(dpBuff)); // buffer for container header decoding
496
1/1
✓ Branch 2 taken 6264 times.
6264 Fw::DpContainer container; // container object for extracting header fields
497
498
1/1
✓ Branch 8 taken 6264 times.
6264 this->log_ACTIVITY_LO_ProcessingFile(fullFile);
499
500 // get file size
501 6264 FwSizeType fileSize = 0;
502
1/1
✓ Branch 5 taken 6264 times.
6264 Os::FileSystem::Status sizeStat = Os::FileSystem::getFileSize(fullFile.toChar(), fileSize);
503
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6264 times.
6264 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 6263 times.
6264 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 6263 times.
6263 Os::File::Status stat = dpFile.open(fullFile.toChar(), Os::File::OPEN_READ);
514
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6263 times.
6263 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 6263 FwSizeType size = Fw::DpContainer::MIN_PACKET_SIZE;
521
522
1/1
✓ Branch 2 taken 6263 times.
6263 stat = dpFile.read(dpBuff, size);
523
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6263 times.
6263 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 6263 times.
6263 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 6263 times.
6263 dpFile.close();
538
539 // give buffer to container instance
540
1/1
✓ Branch 2 taken 6263 times.
6263 container.setBuffer(hdrBuff);
541
542 // make sure the header metadata matches its stored hash before trusting it
543
1/1
✓ Branch 2 taken 6263 times.
6263 Utils::HashBuffer storedHash;
544
1/1
✓ Branch 2 taken 6263 times.
6263 Utils::HashBuffer computedHash;
545
1/1
✓ Branch 2 taken 6263 times.
6263 Fw::Success::T hashStatus = container.checkHeaderHash(storedHash, computedHash);
546
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6262 times.
6263 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 6262 times.
6262 Fw::SerializeStatus desStat = container.deserializeHeader();
554
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6262 times.
6262 if (desStat != Fw::FW_SERIALIZE_OK) {
555 ✗ this->log_WARNING_HI_FileHdrDesError(fullFile, desStat);
556 ✗ return ProcessFileStatus::FAILED;
557 }
558
559 6262 const FwSizeType dataSize = container.getDataSize();
560 6262 const FwSizeType expectedDataSize = fileSize - Fw::DpContainer::MIN_PACKET_SIZE;
561
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6262 times.
6262 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 6262 times.
6262 Fw::FileNameString canonicalFileName;
567 Fw::FormatStatus canonicalFormatStatus =
568
3/3
✓ Branch 2 taken 6262 times.
✓ Branch 5 taken 6262 times.
✓ Branch 17 taken 6262 times.
18786 canonicalFileName.format(DP_FILENAME_FORMAT, this->m_directories[dir].toChar(), container.getId(),
569
2/2
✓ Branch 2 taken 6262 times.
✓ Branch 9 taken 6262 times.
18786 container.getTimeTag().getSeconds(), container.getTimeTag().getUSeconds());
570
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6262 times.
6262 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 6262 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 6261 times.
6262 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 // add entry to catalog.
581
1/1
✓ Branch 2 taken 6261 times.
6261 DpStateEntry entry;
582 6261 entry.dir = static_cast<FwIndexType>(dir);
583
1/1
✓ Branch 4 taken 6261 times.
6261 entry.record.set_id(container.getId());
584
1/1
✓ Branch 4 taken 6261 times.
6261 entry.record.set_priority(container.getPriority());
585
2/2
✓ Branch 3 taken 6261 times.
✓ Branch 10 taken 6261 times.
6261 entry.record.set_state(container.getState());
586
3/3
✓ Branch 3 taken 6261 times.
✓ Branch 9 taken 6261 times.
✓ Branch 12 taken 6261 times.
6261 entry.record.set_tSec(container.getTimeTag().getSeconds());
587
3/3
✓ Branch 3 taken 6261 times.
✓ Branch 9 taken 6261 times.
✓ Branch 12 taken 6261 times.
6261 entry.record.set_tSub(container.getTimeTag().getUSeconds());
588
1/1
✓ Branch 2 taken 6261 times.
6261 entry.record.set_size(static_cast<U64>(fileSize));
589
590 // check the state file to see if there is transmit state
591
1/1
✓ Branch 4 taken 6261 times.
6261 this->getFileState(entry);
592
593 // skip a file the state file records as already transmitted
594
2/2
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 6259 times.
6261 if (entry.record.get_state() == Fw::DpState::TRANSMITTED) {
595
1/1
✓ Branch 8 taken 2 times.
2 this->log_ACTIVITY_HI_DpFileSkipped(fullFile);
596 2 return ProcessFileStatus::FAILED;
597 }
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 6259 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 6259 times.
6259 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 6259 times.
6259 bool inserted = this->insertEntry(entry);
607
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6259 times.
6259 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 6259 this->m_pendingFiles++;
615 6259 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 6259 times.
6259 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 6259 times.
6259 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 6259 return ProcessFileStatus::SUCCESS;
629 6264 }
630
631 // ----------------------------------------------------------------------
632 // DpStateEntry Comparison Ops
633 // ----------------------------------------------------------------------
634 1716795 I8 DpCatalog::DpStateEntry::compareEntries(const DpStateEntry& left, const DpStateEntry& right) {
635 // check priority. Lower is higher priority
636
2/2
✓ Branch 22 taken 640065 times.
✓ Branch 23 taken 1076730 times.
1716795 if (left.record.get_priority() < right.record.get_priority()) {
637 640065 return -1;
638
2/2
✓ Branch 22 taken 327865 times.
✓ Branch 23 taken 748865 times.
1076730 } else if (left.record.get_priority() > right.record.get_priority()) {
639 327865 return 1;
640 }
641
642 // check time. Older is higher priority
643
2/2
✓ Branch 22 taken 238883 times.
✓ Branch 23 taken 509982 times.
748865 else if (left.record.get_tSec() < right.record.get_tSec()) {
644 238883 return -1;
645
2/2
✓ Branch 22 taken 120841 times.
✓ Branch 23 taken 389141 times.
509982 } else if (left.record.get_tSec() > right.record.get_tSec()) {
646 120841 return 1;
647 }
648
649 // check subsecond time. Older is higher priority
650
2/2
✓ Branch 22 taken 2 times.
✓ Branch 23 taken 389139 times.
389141 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 389138 times.
389139 } 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 175979 times.
✓ Branch 23 taken 213159 times.
389138 else if (left.record.get_id() < right.record.get_id()) {
658 175979 return -1;
659
2/2
✓ Branch 22 taken 136973 times.
✓ Branch 23 taken 76186 times.
213159 } else if (left.record.get_id() > right.record.get_id()) {
660 136973 return 1;
661 }
662
663 // if ids are equal we have two nodes with the same value
664 else {
665 76186 return 0;
666 }
667 }
668
669 663281 bool DpCatalog::DpStateEntry::operator==(const DpStateEntry& other) const {
670 663281 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 1053512 bool DpCatalog::DpStateEntry::operator<(const DpStateEntry& other) const {
680 1053512 return compareEntries(*this, other) < 0;
681 }
682
683 67070 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 67070 times.
67070 Fw::Success status = this->m_dpCatalog.insert(entry);
687 134140 return (status == Fw::Success::SUCCESS);
688 67070 }
689
690 6248 void DpCatalog::sendNextEntry() {
691 // Use xmit flag to break upon STOP_XMIT_CATALOG
692
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 6248 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 6247 times.
6248 if (this->m_xmitInProgress != true) {
693 1 return;
694 }
695 // A send is still in flight (START_XMIT_CATALOG after a STOP, before the file completed): its
696 // fileDone resumes the walk, so do not re-send the entry that is still in the catalog (#5777)
697
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 6247 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 6246 times.
6247 if (this->m_hasCurrentXmit) {
698 1 return;
699 }
700
701 // Look for the next entry to send
702
1/1
✓ Branch 2 taken 6246 times.
6246 DpStateEntry entry;
703
3/3
✓ Branch 4 taken 6246 times.
✓ Branch 6 taken 118 times.
✓ Branch 7 taken 6128 times.
6246 if (!this->findNextEntry(entry)) {
704 // if no entry found, we are done
705 118 this->m_xmitInProgress = false;
706
1/1
✓ Branch 9 taken 118 times.
118 this->log_ACTIVITY_HI_CatalogXmitCompleted(this->m_xmitBytes);
707
2/2
✓ Branch 5 taken 118 times.
✓ Branch 8 taken 118 times.
118 this->dispatchWaitedResponse(Fw::CmdResponse::OK);
708 118 return;
709 }
710
711 // Save current entry for fileDone_handler
712
1/1
✓ Branch 4 taken 6128 times.
6128 this->m_currentXmitEntry = entry;
713 6128 this->m_hasCurrentXmit = true;
714
715 // Build file name based on the found entry
716 Fw::FormatStatus formatStatus =
717
1/1
✓ Branch 16 taken 6128 times.
24512 this->m_currXmitFileName.format(DP_FILENAME_FORMAT, this->m_directories[entry.dir].toChar(),
718 18384 entry.record.get_id(), entry.record.get_tSec(), entry.record.get_tSub());
719
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6128 times.
6128 if (formatStatus != Fw::FormatStatus::SUCCESS) {
720 ✗ this->log_WARNING_HI_FileNameFormatError(this->m_currXmitFileName,
721 static_cast<Fw::StringFormatStatus::T>(formatStatus));
722 // No send is in flight, so no fileDone will arrive: abort the transmit
723 // rather than leaving it wedged in progress
724 ✗ this->abortXmit(Fw::CmdResponse::EXECUTION_ERROR);
725 ✗ return;
726 }
727
1/1
✓ Branch 11 taken 6128 times.
12256 this->log_ACTIVITY_LO_SendingProduct(this->m_currXmitFileName, static_cast<U32>(entry.record.get_size()),
728 6128 entry.record.get_priority());
729
1/1
✓ Branch 9 taken 6128 times.
6128 Svc::SendFileResponse resp = this->fileOut_out(0, this->m_currXmitFileName, this->m_currXmitFileName, 0, 0);
730
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 6128 times.
6128 if (resp.get_status() != Svc::SendFileStatus::STATUS_OK) {
731 ✗ this->log_WARNING_HI_DpFileSendError(this->m_currXmitFileName, resp.get_status());
732 // A rejected send produces no fileDone callback: abort the transmit
733 // rather than leaving it wedged in progress
734 ✗ this->abortXmit(Fw::CmdResponse::EXECUTION_ERROR);
735 ✗ return;
736 }
737 // FileDownlink echoes this context in fileDone; it identifies this send (#5777)
738 6128 this->m_currXmitContext = resp.get_context();
739 6246 } // end sendNextEntry()
740
741 63439 bool DpCatalog::findNextEntry(DpStateEntry& entry) {
742 // If catalog is empty, return false
743
3/3
✓ Branch 6 taken 63439 times.
✓ Branch 8 taken 1126 times.
✓ Branch 9 taken 62313 times.
63439 if (this->m_dpCatalog.getSize() == 0) {
744 1126 return false;
745 }
746
747 // Get the highest priority entry (begin() returns highest priority)
748 // Since we remove entries as we transmit them, begin() always gives us the next entry
749
1/1
✓ Branch 4 taken 62313 times.
62313 typename Fw::RedBlackTreeSet<DpStateEntry, DP_MAX_FILES>::ConstIterator iter = this->m_dpCatalog.begin();
750
751 // Verify iterator is valid
752
3/4
✓ Branch 4 taken 62313 times.
✓ Branch 8 taken 62313 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 62313 times.
62313 if (iter == this->m_dpCatalog.end()) {
753 ✗ return false;
754 }
755
756 // Get the entry
757
2/2
✓ Branch 1 taken 62313 times.
✓ Branch 5 taken 62313 times.
62313 entry = *iter;
758
759 62313 return true;
760 62313 }
761
762 3647 bool DpCatalog::checkInit() {
763
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 3647 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 3646 times.
3647 if (not this->m_initialized) {
764 1 this->log_WARNING_HI_ComponentNotInitialized();
765 1 return false;
766
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 3646 times.
3646 } else if (0 == this->m_numDpSlots) {
767 ✗ this->log_WARNING_HI_ComponentNoMemory();
768 ✗ return false;
769 }
770
771 3646 return true;
772 }
773
774 627 void DpCatalog::shutdown() {
775 // only try to deallocate if both pointers are non-zero
776 // it's a way to more gracefully shut down if there are missing
777 // pointers
778
2/4
✓ Branch 4 taken 627 times.
✗ Branch 5 not taken.
✓ Branch 10 taken 627 times.
✗ Branch 11 not taken.
627 if ((this->m_allocator != nullptr) and (this->m_memPtr != nullptr)) {
779 627 this->m_allocator->deallocate(this->m_allocatorId, this->m_memPtr);
780 }
781 627 }
782
783 // ----------------------------------------------------------------------
784 // Handler implementations for user-defined typed input ports
785 // ----------------------------------------------------------------------
786
787 6139 void DpCatalog ::fileDone_handler(FwIndexType portNum, const Svc::SendFileResponse& resp) {
788 // Accept only the fileDone for the send in flight (matching FileDownlink context); anything
789 // else is a late callback from an abandoned send (#5777) and must not touch the current transmit
790
7/8
✗ Branch 3 not taken.
✓ Branch 4 taken 6139 times.
✓ Branch 5 taken 6133 times.
✓ Branch 6 taken 6 times.
✓ Branch 15 taken 11 times.
✓ Branch 16 taken 6122 times.
✓ Branch 17 taken 17 times.
✓ Branch 18 taken 6122 times.
6139 if (!this->m_hasCurrentXmit || resp.get_context() != this->m_currXmitContext) {
791
2/2
✓ Branch 10 taken 17 times.
✓ Branch 18 taken 17 times.
17 this->log_WARNING_LO_StaleFileDone(resp.get_context(), resp.get_status());
792 17 return;
793 }
794
795 // check file status
796
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 6120 times.
6122 if (resp.get_status() != Svc::SendFileStatus::STATUS_OK) {
797
2/2
✓ Branch 10 taken 2 times.
✓ Branch 17 taken 2 times.
2 this->log_WARNING_HI_DpFileXmitError(this->m_currXmitFileName, resp.get_status());
798
2/2
✓ Branch 5 taken 2 times.
✓ Branch 8 taken 2 times.
2 this->abortXmit(Fw::CmdResponse::EXECUTION_ERROR);
799 2 return;
800 }
801
802 // Reduce pending
803 6120 this->m_pendingDpBytes -= this->m_currentXmitEntry.record.get_size();
804 6120 this->m_pendingFiles--;
805 // Log File Complete & pending
806
1/1
✓ Branch 16 taken 6120 times.
6120 this->log_ACTIVITY_LO_ProductComplete(this->m_currXmitFileName, this->m_pendingFiles, this->m_pendingDpBytes);
807
808 // mark the entry as transmitted
809
1/1
✓ Branch 6 taken 6120 times.
6120 this->m_currentXmitEntry.record.set_state(Fw::DpState::TRANSMITTED);
810 // update the transmitted state in the state file
811
1/1
✓ Branch 7 taken 6120 times.
6120 this->appendFileState(this->m_currentXmitEntry);
812 // add the size
813 6120 this->m_xmitBytes += this->m_currentXmitEntry.record.get_size();
814
815 // Remove from catalog
816
1/1
✓ Branch 7 taken 6120 times.
6120 Fw::Success status = this->m_dpCatalog.remove(this->m_currentXmitEntry);
817 6120 FW_ASSERT(status == Fw::Success::SUCCESS);
818
819 6120 this->m_hasCurrentXmit = false;
820
821 // send the next entry, if it exists
822
1/1
✓ Branch 4 taken 6120 times.
6120 this->sendNextEntry();
823 6120 }
824
825 1 void DpCatalog ::pingIn_handler(FwIndexType portNum, U32 key) {
826 // return code for health ping
827 1 this->pingOut_out(0, key);
828 1 }
829
830 3397 void DpCatalog ::addToCat_handler(FwIndexType portNum,
831 const Fw::StringBase& fileName,
832 FwDpPriorityType priority,
833 FwSizeType size) {
834 // check initialization
835
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 3397 times.
3397 if (not this->checkInit()) {
836 ✗ return;
837 }
838
839 // check that initialization got memory
840
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 3397 times.
3397 if (0 == this->m_numDpSlots) {
841 ✗ this->log_WARNING_HI_NoDpMemory();
842 ✗ return;
843 }
844
845 // Check the catalog has been built
846
2/4
✗ Branch 3 not taken.
✓ Branch 4 taken 3397 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3397 times.
3397 if (not this->m_catalogBuilt) {
847 ✗ this->log_ACTIVITY_HI_NotLoaded(fileName);
848 ✗ return;
849 }
850
851 // Both of these are grabbed from the header
852 (void)priority;
853 (void)size;
854
855 // Since this is a runtime addition
856 // Check if file is in one of our directories
857
2/2
✓ Branch 8 taken 3397 times.
✓ Branch 12 taken 3397 times.
3397 FwSizeType dir = this->determineDirectory(fileName);
858
859 // Not in one of our directories; skip this file
860
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3397 times.
3397 if (dir == DP_MAX_DIRECTORIES) {
861 ✗ this->log_WARNING_HI_DirectoryNotManaged(fileName);
862 ✗ return;
863 }
864
865
2/2
✓ Branch 8 taken 3397 times.
✓ Branch 12 taken 3397 times.
3397 const ProcessFileStatus ret = processFile(fileName, dir);
866
867
1/2
✓ Branch 0 taken 3397 times.
✗ Branch 1 not taken.
3397 if (ret == ProcessFileStatus::SUCCESS) {
868 // If we already finished, sendNext only if remainingActive
869
5/8
✗ Branch 3 not taken.
✓ Branch 4 taken 3397 times.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 3392 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 5 times.
✓ Branch 12 taken 5 times.
✗ Branch 13 not taken.
3397 if (!this->m_xmitInProgress && this->m_remainActive) {
870 5 this->m_xmitInProgress = true;
871 5 this->sendNextEntry();
872 }
873 // Otherwise, Current File finishing will invoke sendNextFile & find the right file
874 // Or will be manually tx-ed at next command
875
876 // prune and rewrite the state file
877 3397 this->pruneAndWriteStateFile();
878 }
879 }
880
881 // ----------------------------------------------------------------------
882 // Handler implementations for commands
883 // ----------------------------------------------------------------------
884
885 124 void DpCatalog ::BUILD_CATALOG_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
886 // invoke helper
887
2/2
✓ Branch 6 taken 124 times.
✓ Branch 9 taken 124 times.
124 this->cmdResponse_out(opCode, cmdSeq, this->doCatalogBuild());
888 124 }
889
890 126 void DpCatalog ::START_XMIT_CATALOG_cmdHandler(FwOpcodeType opCode,
891 U32 cmdSeq,
892 const Fw::Wait& wait,
893 bool remainActive) {
894 126 this->m_remainActive = remainActive;
895
896 // Arm the waited response before starting: an empty catalog completes the
897 // transmit inside doCatalogXmit and must still answer a waited command
898
2/2
✓ Branch 4 taken 63 times.
✓ Branch 5 taken 63 times.
126 if (Fw::Wait::WAIT == wait) {
899 63 this->m_xmitCmdWait = true;
900 63 this->m_xmitOpCode = opCode;
901 63 this->m_xmitCmdSeq = cmdSeq;
902 }
903
904
1/1
✓ Branch 2 taken 126 times.
126 Fw::CmdResponse resp = this->doCatalogXmit();
905 126 FW_ASSERT(resp.isValid(), static_cast<FwAssertArgType>(resp.e));
906
907
2/2
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 123 times.
126 if (resp != Fw::CmdResponse::OK) {
908 3 this->m_xmitCmdWait = false;
909 3 this->m_xmitOpCode = 0;
910 3 this->m_xmitCmdSeq = 0;
911
2/2
✓ Branch 6 taken 3 times.
✓ Branch 9 taken 3 times.
3 this->cmdResponse_out(opCode, cmdSeq, resp);
912
2/2
✓ Branch 4 taken 61 times.
✓ Branch 5 taken 62 times.
123 } else if (Fw::Wait::NO_WAIT == wait) {
913
2/2
✓ Branch 6 taken 61 times.
✓ Branch 9 taken 61 times.
61 this->cmdResponse_out(opCode, cmdSeq, resp);
914 }
915 252 }
916
917 126 Fw::CmdResponse DpCatalog::doCatalogXmit() {
918 // check initialization
919
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 125 times.
126 if (not this->checkInit()) {
920 1 return Fw::CmdResponse::EXECUTION_ERROR;
921 }
922
923 // check that initialization got memory
924
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 125 times.
125 if (0 == this->m_numDpSlots) {
925 ✗ this->log_WARNING_HI_NoDpMemory();
926 ✗ return Fw::CmdResponse::EXECUTION_ERROR;
927 }
928
929 // make sure a downlink is not in progress
930
2/4
✗ Branch 3 not taken.
✓ Branch 4 taken 125 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 125 times.
125 if (this->m_xmitInProgress) {
931 ✗ this->log_WARNING_LO_DpXmitInProgress();
932 ✗ return Fw::CmdResponse::EXECUTION_ERROR;
933 }
934
935 // Check the catalog has been built
936
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 125 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 123 times.
125 if (not this->m_catalogBuilt) {
937 2 this->log_WARNING_HI_XmitUnbuiltCatalog();
938 2 return Fw::CmdResponse::EXECUTION_ERROR;
939 }
940
941 // start transmission; a START that resumes a send left in flight by STOP keeps the
942 // session's byte tally, so CatalogXmitCompleted reports the whole session
943
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 123 times.
✓ Branch 5 taken 122 times.
✓ Branch 6 taken 1 times.
123 if (!this->m_hasCurrentXmit) {
944 122 this->m_xmitBytes = 0;
945 }
946 123 this->m_xmitInProgress = true;
947 // Step 3b - search for and send first entry
948 123 this->sendNextEntry();
949 123 return Fw::CmdResponse::OK;
950 }
951
952 8 void DpCatalog ::STOP_XMIT_CATALOG_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
953
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 7 times.
8 if (not this->m_xmitInProgress) {
954 1 this->log_WARNING_LO_XmitNotActive();
955 // benign error, so don't fail the command
956
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
957 } else {
958 7 this->log_ACTIVITY_HI_CatalogXmitStopped(this->m_xmitBytes);
959 // Disarm xmitInProgress so sendNextEntry stops; keep hasCurrentXmit so the
960 // in-flight file's fileDone is still recorded (the context check rejects any other)
961 7 this->m_xmitInProgress = false;
962 // Respond to original cmd to start xmit
963 // (if we haven't already)
964
2/2
✓ Branch 5 taken 7 times.
✓ Branch 8 taken 7 times.
7 this->dispatchWaitedResponse(Fw::CmdResponse::OK);
965
966 // Respond to this command
967
2/2
✓ Branch 6 taken 7 times.
✓ Branch 9 taken 7 times.
7 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
968 }
969 8 }
970
971 3 void DpCatalog ::CLEAR_CATALOG_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
972 // Dropping the send in flight closes the transmit session here, as STOP does, so a waited
973 // START_XMIT_CATALOG is answered now and the next BUILD/START is not refused as in progress
974
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 2 times.
3 if (this->m_xmitInProgress) {
975
2/2
✓ Branch 5 taken 1 times.
✓ Branch 8 taken 1 times.
1 this->abortXmit(Fw::CmdResponse::EXECUTION_ERROR);
976 }
977 3 this->log_ACTIVITY_HI_CatalogCleared(this->m_pendingFiles, this->m_pendingDpBytes);
978 3 this->resetCatalog();
979 3 this->resetStateFileData();
980
981
2/2
✓ Branch 6 taken 3 times.
✓ Branch 9 taken 3 times.
3 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
982 3 }
983
984 128 void DpCatalog ::dispatchWaitedResponse(Fw::CmdResponse response) {
985
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 128 times.
✓ Branch 5 taken 62 times.
✓ Branch 6 taken 66 times.
128 if (this->m_xmitCmdWait) {
986
2/2
✓ Branch 6 taken 62 times.
✓ Branch 17 taken 62 times.
62 this->cmdResponse_out(this->m_xmitOpCode, this->m_xmitCmdSeq, response);
987
988 // Prevent a Duplicate Cmd Response
989 62 this->m_xmitCmdWait = false;
990 62 this->m_xmitOpCode = 0;
991 62 this->m_xmitCmdSeq = 0;
992 }
993 128 }
994
995 3 void DpCatalog ::abortXmit(Fw::CmdResponse response) {
996 3 this->m_hasCurrentXmit = false;
997 3 this->m_xmitInProgress = false;
998
2/2
✓ Branch 5 taken 3 times.
✓ Branch 8 taken 3 times.
3 this->dispatchWaitedResponse(response);
999 3 }
1000
1001 } // namespace Svc
1002