GCC Code Coverage Report


Directory: ./
File: Svc/DpCatalog/DpCatalog.cpp
Date: 2026-11-03 22:10:14
Exec Total Coverage
Lines: 410 619 66.2%
Functions: 34 43 79.1%
Branches: 310 574 54.0%

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 2 taken 1 times.
✓ Branch 5 taken 1 times.
✓ Branch 8 taken 2 times.
✓ Branch 10 taken 2 times.
✓ Branch 11 taken 1 times.
✓ Branch 13 taken 1 times.
✓ Branch 16 taken 1 times.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
3 DpCatalog ::DpCatalog(const char* const compName) : DpCatalogComponentBase(compName) {
26 // Members are default-initialized via in-class initializers in the header.
27 1 }
28
29 1 void DpCatalog::configure(const Fw::ExternalArray<Fw::FileNameString>& directories,
30 Fw::FileNameString& stateFile,
31 FwEnumStoreType memId,
32 Fw::MemAllocator& allocator) {
33 1 const FwSizeType numDirs = directories.getSize();
34 // Do some assertion checks
35 1 FW_ASSERT(numDirs <= DP_MAX_DIRECTORIES, static_cast<FwAssertArgType>(numDirs));
36
37
1/1
✓ Branch 1 taken 1 times.
1 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 1 this->m_memSize = DP_MAX_FILES * slotSize;
44 bool notUsed; // we don't need to recover the catalog.
45 // request memory. this->m_memSize will be modified if there is less than we requested
46
1/1
✓ Branch 1 taken 1 times.
1 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 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 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 1 const FwSizeType allocatedSlots = this->m_memSize / slotSize;
54 1 this->m_numDpSlots = (allocatedSlots < DP_MAX_FILES) ? allocatedSlots : DP_MAX_FILES;
55 // Initialize the catalog
56
1/1
✓ Branch 1 taken 1 times.
1 this->resetCatalog();
57 // assign pointer for the state file storage
58 1 this->m_stateFileData = static_cast<DpDstateFileEntry*>(this->m_memPtr);
59 1 } 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 1 times.
✓ Branch 1 taken 1 times.
2 for (FwSizeType dir = 0; dir < numDirs; dir++) {
67
2/2
✓ Branch 1 taken 1 times.
✓ Branch 4 taken 1 times.
1 this->m_directories[dir] = directories[dir];
68 }
69 1 this->m_numDirectories = numDirs;
70
71 // store allocator
72 1 this->m_allocator = &allocator;
73 1 this->m_allocatorId = memId;
74 1 this->m_initialized = true;
75 1 }
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 4 void DpCatalog::resetCatalog() {
88 // Clear the catalog
89 4 this->m_dpCatalog.clear();
90 // Clear transmission state
91 4 this->m_hasCurrentXmit = false;
92 // Reset counters
93 4 this->m_pendingFiles = 0;
94 4 this->m_pendingDpBytes = 0;
95 // Mark the catalog as un-built
96 4 this->m_catalogBuilt = false;
97 4 }
98
99 3 void DpCatalog::resetStateFileData() {
100 // clear state file data
101
2/2
✓ Branch 0 taken 381 times.
✓ Branch 1 taken 3 times.
384 for (FwSizeType slot = 0; slot < this->m_numDpSlots; slot++) {
102 381 this->m_stateFileData[slot].used = false;
103 381 this->m_stateFileData[slot].visited = false;
104
1/3
✓ Branch 2 taken 381 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
381 (void)new (&this->m_stateFileData[slot].entry.record) DpRecord();
105 }
106 3 this->m_stateFileEntries = 0;
107 3 }
108
109 2 Fw::CmdResponse DpCatalog::loadStateFile() {
110 2 FW_ASSERT(this->m_stateFileData != nullptr);
111
112 // Make sure that a file was specified
113
2/3
✓ Branch 1 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
2 if (this->m_stateFile.length() == 0) {
114 ✗ this->log_WARNING_LO_NoStateFileSpecified();
115 ✗ return Fw::CmdResponse::OK;
116 }
117
118 // buffer for reading entries
119
120 BYTE buffer[sizeof(FwIndexType) + DpRecord::SERIALIZED_SIZE];
121
1/1
✓ Branch 1 taken 2 times.
2 Fw::ExternalSerializeBuffer entryBuffer(buffer, sizeof(buffer));
122
123 // open the state file
124
1/1
✓ Branch 1 taken 2 times.
2 Os::File stateFile;
125
1/1
✓ Branch 2 taken 2 times.
2 Os::File::Status stat = stateFile.open(this->m_stateFile.toChar(), Os::File::OPEN_READ);
126
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 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 1 taken 1 times.
1 this->log_WARNING_LO_NoStateFile(this->m_stateFile);
129
1/1
✓ Branch 1 taken 1 times.
1 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 fileSize = 0;
137
1/1
✓ Branch 1 taken 1 times.
1 stat = stateFile.size(fileSize);
138
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (stat != Os::File::OP_OK) {
139 ✗ this->log_WARNING_HI_StateFileReadError(this->m_stateFile, stat, 0);
140 ✗ stateFile.close();
141 ✗ return Fw::CmdResponse::EXECUTION_ERROR;
142 }
143
144 1 FwSizeType fileLoc = 0;
145 1 this->m_stateFileEntries = 0;
146
147 // A product may have several records (a priority change, then its completion); the last one
148 // read wins, so the walk is bounded by the record count rather than the slot count
149 1 const FwSizeType maxRecords = fileSize / static_cast<FwSizeType>(sizeof(buffer)) + 1;
150
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 for (FwSizeType record = 0; record < maxRecords; record++) {
151 6 FwSizeType size = static_cast<FwSizeType>(sizeof(buffer));
152 // read the directory index
153
1/1
✓ Branch 1 taken 6 times.
6 stat = stateFile.read(buffer, size);
154
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (stat != Os::File::OP_OK) {
155 ✗ this->log_WARNING_HI_StateFileReadError(this->m_stateFile, stat, static_cast<I32>(fileLoc));
156 ✗ stateFile.close();
157 ✗ return Fw::CmdResponse::EXECUTION_ERROR;
158 }
159
160
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
6 if (0 == size) {
161 // no more entries
162 1 break;
163 }
164
165 // check to see if the full entry was read. If not,
166 // abandon it and finish. We can at least operate on
167 // the entries that were read.
168
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (size != sizeof(buffer)) {
169 ✗ this->log_WARNING_HI_StateFileTruncated(this->m_stateFile, static_cast<I32>(fileLoc),
170 static_cast<I32>(size));
171 ✗ stateFile.close();
172 ✗ return Fw::CmdResponse::OK;
173 }
174
175 // reset the buffer for deserializing the entry
176
1/1
✓ Branch 1 taken 5 times.
5 Fw::SerializeStatus serStat = entryBuffer.setBuffLen(static_cast<Fw::Serializable::SizeType>(size));
177 // should always fit
178 5 FW_ASSERT(Fw::FW_SERIALIZE_OK == serStat, serStat);
179
1/1
✓ Branch 1 taken 5 times.
5 entryBuffer.resetDeser();
180
181 // deserialization after this point should always work, since
182 // the source buffer was specifically sized to hold the data
183
184 // Deserialize the file directory index. If an error occurs processing the file,
185 // generate event and return EXECUTION_ERROR.
186
1/1
✓ Branch 1 taken 5 times.
5 DpStateEntry fileEntry{};
187
1/1
✓ Branch 1 taken 5 times.
5 Fw::SerializeStatus status = entryBuffer.deserializeTo(fileEntry.dir);
188
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (status != Fw::FW_SERIALIZE_OK) {
189 ✗ this->log_WARNING_HI_FileCorruptedDataError(this->m_stateFile, static_cast<I32>(status));
190 ✗ stateFile.close();
191 ✗ return Fw::CmdResponse::EXECUTION_ERROR;
192 }
193
1/1
✓ Branch 1 taken 5 times.
5 status = entryBuffer.deserializeTo(fileEntry.record);
194
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (status != Fw::FW_SERIALIZE_OK) {
195 ✗ this->log_WARNING_HI_FileCorruptedDataError(this->m_stateFile, static_cast<I32>(status));
196 ✗ stateFile.close();
197 ✗ return Fw::CmdResponse::EXECUTION_ERROR;
198 }
199
200
1/1
✓ Branch 1 taken 5 times.
5 const FwSizeType slot = this->findStateFileSlot(fileEntry);
201
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (slot == this->m_stateFileEntries) {
202
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (this->m_stateFileEntries >= this->m_numDpSlots) {
203 // no slot for another product; later records for products that have a slot still apply
204 ✗ fileLoc += size;
205 ✗ continue;
206 }
207 5 this->m_stateFileEntries++;
208 }
209
1/1
✓ Branch 1 taken 5 times.
5 this->m_stateFileData[slot].entry = fileEntry;
210 5 this->m_stateFileData[slot].used = true;
211 5 this->m_stateFileData[slot].visited = false;
212
213 // increment the file location
214 5 fileLoc += size;
215 5 }
216
1/1
✓ Branch 1 taken 1 times.
1 stateFile.close();
217
1/1
✓ Branch 1 taken 1 times.
1 return Fw::CmdResponse::OK;
218 2 }
219
220 10 void DpCatalog::getFileState(DpStateEntry& entry) {
221 10 FW_ASSERT(this->m_stateFileData != nullptr);
222 10 const FwSizeType line = this->findStateFileSlot(entry);
223
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
10 if (line < this->m_stateFileEntries) {
224 // a recorded priority differs from the file header only if SET_DP_PRIORITY changed it
225 5 entry.record.set_priority(this->m_stateFileData[line].entry.record.get_priority());
226 // update the transmitted state
227 5 entry.record.set_state(this->m_stateFileData[line].entry.record.get_state());
228 5 entry.record.set_blocks(this->m_stateFileData[line].entry.record.get_blocks());
229 // mark it as visited for later pruning if necessary
230 5 this->m_stateFileData[line].visited = true;
231 }
232 10 }
233
234 20 FwSizeType DpCatalog::findStateFileSlot(const DpStateEntry& entry) const {
235 20 FW_ASSERT(this->m_stateFileData != nullptr);
236
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 15 times.
56 for (FwSizeType line = 0; line < this->m_stateFileEntries; line++) {
237
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 36 times.
41 if (this->m_stateFileData[line].entry.isSameProduct(entry)) {
238 5 return line;
239 }
240 }
241 15 return this->m_stateFileEntries;
242 }
243
244 5 DpCatalog_StateFileUpdateStatus DpCatalog::updateStateFileData(const DpStateEntry& entry) {
245 5 FwSizeType slot = this->findStateFileSlot(entry);
246
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (slot == this->m_stateFileEntries) {
247 // outside a catalog build, a slot not visited by the last build is for a file that is gone
248
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 5 times.
15 for (FwSizeType line = 0; line < this->m_stateFileEntries; line++) {
249
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (not this->m_stateFileData[line].visited) {
250 ✗ slot = line;
251 ✗ break;
252 }
253 }
254 }
255
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (slot == this->m_stateFileEntries) {
256
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (this->m_stateFileEntries >= this->m_numDpSlots) {
257 // only worth reporting when the state is kept in a state file
258 ✗ if (this->m_stateFile.length() > 0) {
259 ✗ this->log_WARNING_HI_StateFileFull(entry.record.get_id(), entry.record.get_tSec(),
260 entry.record.get_tSub());
261 }
262 ✗ return DpCatalog_StateFileUpdateStatus::FULL;
263 }
264 5 this->m_stateFileEntries++;
265 }
266 5 this->m_stateFileData[slot].entry = entry;
267 5 this->m_stateFileData[slot].used = true;
268 5 this->m_stateFileData[slot].visited = true;
269 5 return DpCatalog_StateFileUpdateStatus::RECORDED;
270 }
271
272 2 DpCatalog_StateFileWriteStatus DpCatalog::pruneAndWriteStateFile() {
273 2 FW_ASSERT(this->m_stateFileData != nullptr);
274
275 // There is a chance that a data product file can disappear after
276 // the state file is written from the last catalog build and transmit.
277 // This function will walk the state file data and write back only
278 // the entries that were visited during the last catalog build. This will
279 // remove any entries that are no longer valid.
280
281 // open the state file
282
1/1
✓ Branch 1 taken 2 times.
2 Os::File stateFile;
283 // we open it as a new file so we don't accumulate invalid entries
284 Os::File::Status stat =
285
1/1
✓ Branch 2 taken 2 times.
2 stateFile.open(this->m_stateFile.toChar(), Os::File::OPEN_CREATE, Os::FileInterface::OVERWRITE);
286
287
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (stat != Os::File::OP_OK) {
288 ✗ this->log_WARNING_HI_StateFileOpenError(this->m_stateFile, stat);
289 ✗ return DpCatalog_StateFileWriteStatus::OPEN_ERROR;
290 }
291
292 // buffer for writing entries
293 BYTE buffer[sizeof(FwIndexType) + DpRecord::SERIALIZED_SIZE];
294
1/1
✓ Branch 1 taken 2 times.
2 Fw::ExternalSerializeBuffer entryBuffer(buffer, sizeof(buffer));
295
296 // write entries to the state file
297
2/2
✓ Branch 0 taken 254 times.
✓ Branch 1 taken 2 times.
256 for (FwSizeType entry = 0; entry < this->m_numDpSlots; entry++) {
298 // only write entries that were used
299
3/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 249 times.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
254 if ((this->m_stateFileData[entry].used) and (this->m_stateFileData[entry].visited)) {
300 // reset the buffer for serializing the entry
301
1/1
✓ Branch 1 taken 5 times.
5 entryBuffer.resetSer();
302 // serialize the file directory index
303
1/1
✓ Branch 1 taken 5 times.
5 Fw::SerializeStatus serStat = entryBuffer.serializeFrom(this->m_stateFileData[entry].entry.dir);
304 // Should always fit
305 5 FW_ASSERT(Fw::FW_SERIALIZE_OK == serStat, serStat);
306
1/1
✓ Branch 1 taken 5 times.
5 serStat = entryBuffer.serializeFrom(this->m_stateFileData[entry].entry.record);
307 // Should always fit
308 5 FW_ASSERT(Fw::FW_SERIALIZE_OK == serStat, serStat);
309 // write the entry
310
1/1
✓ Branch 1 taken 5 times.
5 FwSizeType size = entryBuffer.getSize();
311 // Protect against overflow
312
1/1
✓ Branch 1 taken 5 times.
5 stat = stateFile.write(buffer, size);
313
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (stat != Os::File::OP_OK) {
314 ✗ this->log_WARNING_HI_StateFileWriteError(this->m_stateFile, stat);
315 ✗ stateFile.close();
316 ✗ return DpCatalog_StateFileWriteStatus::WRITE_ERROR;
317 }
318 }
319 }
320
321 // close the state file
322
1/1
✓ Branch 1 taken 2 times.
2 stateFile.close();
323
1/1
✓ Branch 1 taken 2 times.
2 return DpCatalog_StateFileWriteStatus::WRITTEN;
324 2 }
325
326 5 void DpCatalog::appendFileState(const DpStateEntry& entry) {
327 5 FW_ASSERT(this->m_stateFileData != nullptr);
328 5 FW_ASSERT(entry.dir < static_cast<FwIndexType>(this->m_numDirectories), static_cast<FwAssertArgType>(entry.dir),
329 static_cast<FwAssertArgType>(this->m_numDirectories));
330
331 // We will append state to the existing state file
332 // TODO: Have to handle case where state file has partially transmitted
333 // state already
334
335 // open the state file
336
1/1
✓ Branch 1 taken 5 times.
5 Os::File stateFile;
337 // we open it as a new file so we don't accumulate invalid entries
338
1/1
✓ Branch 2 taken 5 times.
5 Os::File::Status stat = stateFile.open(this->m_stateFile.toChar(), Os::File::OPEN_APPEND);
339
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (stat != Os::File::OP_OK) {
340 ✗ this->log_WARNING_HI_StateFileOpenError(this->m_stateFile, stat);
341 ✗ return;
342 }
343
344 // buffer for writing entries
345 BYTE buffer[sizeof(FwIndexType) + DpRecord::SERIALIZED_SIZE];
346
1/1
✓ Branch 1 taken 5 times.
5 Fw::ExternalSerializeBuffer entryBuffer(buffer, sizeof(buffer));
347 // reset the buffer for serializing the entry
348
1/1
✓ Branch 1 taken 5 times.
5 entryBuffer.resetSer();
349 // serialize the file directory index
350
1/1
✓ Branch 1 taken 5 times.
5 Fw::SerializeStatus serStat = entryBuffer.serializeFrom(entry.dir);
351 // should fit
352 5 FW_ASSERT(serStat == Fw::FW_SERIALIZE_OK, serStat);
353
1/1
✓ Branch 1 taken 5 times.
5 serStat = entryBuffer.serializeFrom(entry.record);
354 // should fit
355 5 FW_ASSERT(serStat == Fw::FW_SERIALIZE_OK, serStat);
356 // write the entry
357
1/1
✓ Branch 1 taken 5 times.
5 FwSizeType size = entryBuffer.getSize();
358
1/1
✓ Branch 1 taken 5 times.
5 stat = stateFile.write(buffer, size);
359
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (stat != Os::File::OP_OK) {
360 ✗ stateFile.close();
361 ✗ this->log_WARNING_HI_StateFileWriteError(this->m_stateFile, stat);
362 ✗ return;
363 }
364
365 // close the state file
366
1/1
✓ Branch 1 taken 5 times.
5 stateFile.close();
367 5 }
368
369 2 Fw::CmdResponse DpCatalog::doCatalogBuild() {
370 // check initialization
371
2/3
✓ Branch 1 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
2 if (not this->checkInit()) {
372 ✗ return Fw::CmdResponse::EXECUTION_ERROR;
373 }
374
375 // check that initialization got memory
376
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (0 == this->m_numDpSlots) {
377 ✗ this->log_WARNING_HI_NoDpMemory();
378 ✗ return Fw::CmdResponse::EXECUTION_ERROR;
379 }
380
381 // make sure a downlink is not in progress
382
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (this->m_xmitInProgress) {
383 ✗ this->log_WARNING_LO_DpXmitInProgress();
384 ✗ return Fw::CmdResponse::EXECUTION_ERROR;
385 }
386
387 // reset state file data
388
1/1
✓ Branch 1 taken 2 times.
2 this->resetStateFileData();
389
390 // load state data from file; proceeding on a failed load would later
391 // overwrite the state file and destroy the transmit state it records
392
1/1
✓ Branch 1 taken 2 times.
2 Fw::CmdResponse response = this->loadStateFile();
393
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (response != Fw::CmdResponse::OK) {
394 ✗ this->resetStateFileData();
395 ✗ return response;
396 }
397
398 // reset catalog
399
1/1
✓ Branch 1 taken 2 times.
2 this->resetCatalog();
400
401 // fill the catalog with DP files
402
2/2
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
2 response = this->fillBinaryTree();
403
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (response != Fw::CmdResponse::OK) {
404 // clean up the catalog
405 ✗ this->resetCatalog();
406 ✗ this->resetStateFileData();
407 ✗ return response;
408 }
409
410 // prune and rewrite the state file. A failure is reported by event; the file then holds the records
411 // written before the failure (or the previous contents if it could not be opened), and the build
412 // still completes from the catalog in memory
413
1/1
✓ Branch 1 taken 2 times.
2 (void)this->pruneAndWriteStateFile();
414
415
1/1
✓ Branch 1 taken 2 times.
2 this->log_ACTIVITY_HI_CatalogBuildComplete();
416
417 // Flag so addToCat knows it is good to go
418 2 this->m_catalogBuilt = true;
419
420
1/1
✓ Branch 1 taken 2 times.
2 return Fw::CmdResponse::OK;
421 2 }
422
423 2 Fw::CmdResponse DpCatalog::fillBinaryTree() {
424 // keep cumulative number of files
425 2 FwSizeType totalFiles = 0;
426
427 // get file listings from file system
428 // double bounds to appease static analysis
429
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 for (FwSizeType dir = 0; dir < this->m_numDirectories && dir < static_cast<FwSizeType>(DP_MAX_DIRECTORIES); dir++) {
430 // read in each directory and keep track of total
431
1/1
✓ Branch 1 taken 2 times.
2 this->log_ACTIVITY_LO_ProcessingDirectory(this->m_directories[dir]);
432 2 FwSizeType filesProcessed = 0;
433
434
1/1
✓ Branch 1 taken 2 times.
2 Os::Directory dpDir;
435
1/1
✓ Branch 2 taken 2 times.
2 Os::Directory::Status status = dpDir.open(this->m_directories[dir].toChar(), Os::Directory::OpenMode::READ);
436
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (status != Os::Directory::OP_OK) {
437 ✗ this->log_WARNING_HI_DirectoryOpenError(this->m_directories[dir], status);
438 ✗ return Fw::CmdResponse::EXECUTION_ERROR;
439 }
440
441 // bound the read loop by the number of entries in the directory
442 2 FwSizeType fileCount = 0;
443
1/1
✓ Branch 1 taken 2 times.
2 status = dpDir.getFileCount(fileCount);
444
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (status != Os::Directory::OP_OK) {
445 ✗ this->log_WARNING_HI_DirectoryOpenError(this->m_directories[dir], status);
446 ✗ return Fw::CmdResponse::EXECUTION_ERROR;
447 }
448
449 // read entries one at a time so non-DP files do not consume catalog slots
450
1/1
✓ Branch 1 taken 2 times.
2 Fw::String fileName;
451
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2 times.
10 for (FwSizeType entry = 0; entry < fileCount; entry++) {
452
1/1
✓ Branch 1 taken 8 times.
8 status = dpDir.read(fileName);
453
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (status == Os::Directory::NO_MORE_FILES) {
454 ✗ break;
455 }
456
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (status != Os::Directory::OP_OK) {
457 ✗ this->log_WARNING_HI_DirectoryOpenError(this->m_directories[dir], status);
458 ✗ return Fw::CmdResponse::EXECUTION_ERROR;
459 }
460
461 // only consider files with the DP extension
462
1/1
✓ Branch 1 taken 8 times.
8 const FwSizeType fileNameLength = fileName.length();
463
1/1
✓ Branch 1 taken 8 times.
8 const FwSizeType dpExtLength = Fw::StringUtils::string_length(DP_EXT, sizeof(DP_EXT));
464 const FwSignedSizeType loc =
465
1/1
✓ Branch 2 taken 8 times.
8 Fw::StringUtils::substring_find_last(fileName.toChar(), fileNameLength, DP_EXT, dpExtLength);
466
467 // Only accept files whose final suffix is the data product extension
468
3/4
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
8 if ((-1 == loc) || (static_cast<FwSizeType>(loc) + dpExtLength != fileNameLength)) {
469 1 continue;
470 }
471
472 // stop if there is no free catalog slot for this DP file
473
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if ((totalFiles + filesProcessed) == this->m_numDpSlots) {
474 ✗ break;
475 }
476
477
1/1
✓ Branch 1 taken 7 times.
7 Fw::String fullFile;
478 Fw::FormatStatus formatStatus =
479
1/1
✓ Branch 3 taken 7 times.
7 fullFile.format("%s/%s", this->m_directories[dir].toChar(), fileName.toChar());
480
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (formatStatus != Fw::FormatStatus::SUCCESS) {
481 ✗ this->log_WARNING_HI_FileNameFormatError(fileName,
482 static_cast<Fw::StringFormatStatus::T>(formatStatus));
483 ✗ continue;
484 }
485
486
1/1
✓ Branch 1 taken 7 times.
7 const ProcessFileStatus ret = processFile(fullFile, dir);
487
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (ret == ProcessFileStatus::QUIT) {
488 ✗ break;
489 }
490
491
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
7 if (ret == ProcessFileStatus::SUCCESS) {
492 2 filesProcessed++;
493 }
494
495 7 } // end for each file in a directory
496
497 2 totalFiles += filesProcessed;
498
499
1/1
✓ Branch 1 taken 2 times.
2 this->log_ACTIVITY_HI_ProcessingDirectoryComplete(this->m_directories[dir], static_cast<U32>(totalFiles),
500 this->m_pendingFiles, this->m_pendingDpBytes);
501
502 // check to see if catalog is full
503 // that means generated products exceed the catalog size
504
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (totalFiles == this->m_numDpSlots) {
505 ✗ this->log_WARNING_HI_CatalogFull(this->m_directories[dir]);
506 ✗ break;
507 }
508 2 } // end for each directory
509
510 2 return Fw::CmdResponse::OK;
511
512 } // end fillBinaryTree()
513
514 3 FwSizeType DpCatalog::determineDirectory(const Fw::String& fullFile) {
515 3 FW_ASSERT(this->m_numDirectories <= DP_MAX_DIRECTORIES, static_cast<FwAssertArgType>(this->m_numDirectories));
516 // Grab the directory string (up until the final slash)
517 // Could be found directly w/ a dirname func or regex
518 3 FwSignedSizeType loc = Fw::StringUtils::substring_find_last(
519 fullFile.toChar(), fullFile.length(), DIRECTORY_DELIMITER,
520 Fw::StringUtils::string_length(DIRECTORY_DELIMITER, sizeof(DIRECTORY_DELIMITER)));
521
522 // Seems like the logic works so long as the path styles match (i.e. relative vs absolute)
523 // Full path resolution might be a worthwhile add
524
525 // No directory delimiter found; return DP_MAX_DIRECTORIES to signal failure
526
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (-1 == loc) {
527 ✗ return DP_MAX_DIRECTORIES;
528 }
529
530
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 for (FwSizeType dir = 0; dir < this->m_numDirectories; dir++) {
531 3 const Fw::FileNameString& dir_string = this->m_directories[dir];
532
533 // Compare both strings up to location of final slash
534 // StringUtils::substring_find will return zero if both paths agree
535 // memory safe since both are fixed width strings
536 // and loc is before the fixed width
537
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
6 if ((dir_string.length() == static_cast<FwSizeType>(loc)) &&
538
1/2
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 (Fw::StringUtils::substring_find(dir_string.toChar(), dir_string.length(), fullFile.toChar(),
539 static_cast<FwSizeType>(loc)) == 0)) {
540 3 return dir;
541 }
542 }
543
544 // No directory matched
545 ✗ return DP_MAX_DIRECTORIES;
546 }
547
548 10 DpCatalog::ProcessFileStatus DpCatalog::processFile(const Fw::String& fullFile, FwSizeType dir) {
549 10 FW_ASSERT(dir < static_cast<FwSizeType>(DP_MAX_DIRECTORIES), static_cast<FwAssertArgType>(dir));
550 // file class instance for processing files
551
1/1
✓ Branch 1 taken 10 times.
10 Os::File dpFile;
552
553 // Working buffer for DP headers
554 U8 dpBuff[Fw::DpContainer::MIN_PACKET_SIZE]; // Header buffer
555
1/1
✓ Branch 1 taken 10 times.
10 Fw::Buffer hdrBuff(dpBuff, sizeof(dpBuff)); // buffer for container header decoding
556
1/1
✓ Branch 1 taken 10 times.
10 Fw::DpContainer container; // container object for extracting header fields
557
558
1/1
✓ Branch 1 taken 10 times.
10 this->log_ACTIVITY_LO_ProcessingFile(fullFile);
559
560 // get file size
561 10 FwSizeType fileSize = 0;
562
1/1
✓ Branch 2 taken 10 times.
10 Os::FileSystem::Status sizeStat = Os::FileSystem::getFileSize(fullFile.toChar(), fileSize);
563
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (sizeStat != Os::FileSystem::OP_OK) {
564 ✗ this->log_WARNING_HI_FileSizeError(fullFile, sizeStat);
565 ✗ return ProcessFileStatus::FAILED;
566 }
567
568
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (fileSize < Fw::DpContainer::MIN_PACKET_SIZE) {
569 ✗ this->log_WARNING_HI_FileReadError(fullFile, Os::File::BAD_SIZE);
570 ✗ return ProcessFileStatus::FAILED;
571 }
572
573
1/1
✓ Branch 2 taken 10 times.
10 Os::File::Status stat = dpFile.open(fullFile.toChar(), Os::File::OPEN_READ);
574
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (stat != Os::File::OP_OK) {
575 ✗ this->log_WARNING_HI_FileOpenError(fullFile, stat);
576 ✗ return ProcessFileStatus::FAILED;
577 }
578
579 // Read DP header and header hash
580 10 FwSizeType size = Fw::DpContainer::MIN_PACKET_SIZE;
581
582
1/1
✓ Branch 1 taken 10 times.
10 stat = dpFile.read(dpBuff, size);
583
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (stat != Os::File::OP_OK) {
584 ✗ this->log_WARNING_HI_FileReadError(fullFile, stat);
585 ✗ dpFile.close();
586 ✗ return ProcessFileStatus::FAILED;
587 }
588
589 // if full header and hashes aren't read, something's wrong with the file, so skip
590
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (size != Fw::DpContainer::MIN_PACKET_SIZE) {
591 ✗ this->log_WARNING_HI_FileReadError(fullFile, Os::File::BAD_SIZE);
592 ✗ dpFile.close();
593 ✗ return ProcessFileStatus::FAILED;
594 }
595
596 // if all is well, don't need the file any more
597
1/1
✓ Branch 1 taken 10 times.
10 dpFile.close();
598
599 // give buffer to container instance
600
1/1
✓ Branch 1 taken 10 times.
10 container.setBuffer(hdrBuff);
601
602 // make sure the header metadata matches its stored hash before trusting it
603
1/1
✓ Branch 1 taken 10 times.
10 Utils::HashBuffer storedHash;
604
1/1
✓ Branch 1 taken 10 times.
10 Utils::HashBuffer computedHash;
605
1/1
✓ Branch 1 taken 10 times.
10 Fw::Success::T hashStatus = container.checkHeaderHash(storedHash, computedHash);
606
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (hashStatus != Fw::Success::SUCCESS) {
607 ✗ this->log_WARNING_HI_FileHdrError(fullFile, DpHdrField::CRC, computedHash.asBigEndianU32(),
608 storedHash.asBigEndianU32());
609 ✗ return ProcessFileStatus::FAILED;
610 }
611
612 // reset header deserialization in the container
613
1/1
✓ Branch 1 taken 10 times.
10 Fw::SerializeStatus desStat = container.deserializeHeader();
614
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (desStat != Fw::FW_SERIALIZE_OK) {
615 ✗ this->log_WARNING_HI_FileHdrDesError(fullFile, desStat);
616 ✗ return ProcessFileStatus::FAILED;
617 }
618
619 10 const FwSizeType dataSize = container.getDataSize();
620 10 const FwSizeType expectedDataSize = fileSize - Fw::DpContainer::MIN_PACKET_SIZE;
621
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (dataSize != expectedDataSize) {
622 ✗ this->log_WARNING_HI_FileReadError(fullFile, Os::File::BAD_SIZE);
623 ✗ return ProcessFileStatus::FAILED;
624 }
625
626
1/1
✓ Branch 1 taken 10 times.
10 Fw::FileNameString canonicalFileName;
627 Fw::FormatStatus canonicalFormatStatus =
628
3/3
✓ Branch 1 taken 10 times.
✓ Branch 4 taken 10 times.
✓ Branch 9 taken 10 times.
20 canonicalFileName.format(DP_FILENAME_FORMAT, this->m_directories[dir].toChar(), container.getId(),
629
2/2
✓ Branch 1 taken 10 times.
✓ Branch 4 taken 10 times.
30 container.getTimeTag().getSeconds(), container.getTimeTag().getUSeconds());
630
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (canonicalFormatStatus != Fw::FormatStatus::SUCCESS) {
631 ✗ this->log_WARNING_HI_FileNameFormatError(fullFile,
632 static_cast<Fw::StringFormatStatus::T>(canonicalFormatStatus));
633 ✗ return ProcessFileStatus::FAILED;
634 }
635
2/3
✓ Branch 1 taken 10 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
10 if (canonicalFileName != fullFile) {
636 ✗ this->log_WARNING_HI_InvalidFileName(fullFile, canonicalFileName);
637 ✗ return ProcessFileStatus::FAILED;
638 }
639
640 // add entry to catalog.
641
1/1
✓ Branch 1 taken 10 times.
10 DpStateEntry entry{};
642 10 entry.dir = static_cast<FwIndexType>(dir);
643
1/1
✓ Branch 2 taken 10 times.
10 entry.record.set_id(container.getId());
644
1/1
✓ Branch 2 taken 10 times.
10 entry.record.set_priority(container.getPriority());
645
2/2
✓ Branch 1 taken 10 times.
✓ Branch 5 taken 10 times.
10 entry.record.set_state(container.getState());
646
3/3
✓ Branch 1 taken 10 times.
✓ Branch 4 taken 10 times.
✓ Branch 7 taken 10 times.
10 entry.record.set_tSec(container.getTimeTag().getSeconds());
647
3/3
✓ Branch 1 taken 10 times.
✓ Branch 4 taken 10 times.
✓ Branch 7 taken 10 times.
10 entry.record.set_tSub(container.getTimeTag().getUSeconds());
648
1/1
✓ Branch 1 taken 10 times.
10 entry.record.set_size(static_cast<U64>(fileSize));
649
650 // check the state file to see if there is transmit state
651
1/1
✓ Branch 1 taken 10 times.
10 this->getFileState(entry);
652
653 // skip a file the state file records as already transmitted
654
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 5 times.
10 if (entry.record.get_state() == Fw::DpState::TRANSMITTED) {
655
1/1
✓ Branch 1 taken 5 times.
5 this->log_ACTIVITY_HI_DpFileSkipped(fullFile);
656 5 return ProcessFileStatus::FAILED;
657 }
658
659 // a duplicate insert updates the tree in place; skip it so pending counters are not double-counted.
660 // The sort key includes the priority, which SET_DP_PRIORITY may have changed since the file was
661 // added, so a file already in the catalog is recognized by its stable identity, not by the key
662
1/1
✓ Branch 1 taken 5 times.
5 DpStateEntry existing{};
663
2/3
✓ Branch 1 taken 5 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 5 times.
5 if (this->findEntryByIdentity(entry, existing) == DpCatalog_EntryLookupStatus::FOUND) {
664 ✗ this->log_DIAGNOSTIC_DpDuplicate(entry.record);
665 ✗ return ProcessFileStatus::FAILED;
666 }
667
668 // make sure there is a free slot before inserting, so a full catalog is reported as such
669 // and the entry is not left in the tree uncounted
670
2/3
✓ Branch 1 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
5 if (this->m_dpCatalog.getSize() >= this->m_numDpSlots) {
671 ✗ this->log_WARNING_HI_DpCatalogFull(entry.record);
672 ✗ return ProcessFileStatus::QUIT;
673 }
674
675 // insert into the sorted catalog. A free slot was verified above, so a failure here is a tree
676 // error, not a full catalog
677
1/1
✓ Branch 1 taken 5 times.
5 bool inserted = this->insertEntry(entry);
678
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (!inserted) {
679 ✗ this->log_WARNING_HI_DpInsertError(entry.record);
680 ✗ return ProcessFileStatus::QUIT;
681 }
682
683 // increment our counters
684 5 this->m_pendingFiles++;
685 5 this->m_pendingDpBytes += entry.record.get_size();
686
687
1/1
✓ Branch 1 taken 5 times.
5 this->log_ACTIVITY_HI_DpFileAdded(canonicalFileName);
688
689 // No need to track iterator state - begin() always gives us the highest priority entry
690 // and we remove entries as we transmit them
691
692 5 return ProcessFileStatus::SUCCESS;
693 10 }
694
695 // ----------------------------------------------------------------------
696 // DpStateEntry Comparison Ops
697 // ----------------------------------------------------------------------
698 13 I8 DpCatalog::DpStateEntry::compareEntries(const DpStateEntry& left, const DpStateEntry& right) {
699 // check priority. Lower is higher priority
700
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 13 times.
13 if (left.record.get_priority() < right.record.get_priority()) {
701 ✗ return -1;
702
2/2
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 7 times.
13 } else if (left.record.get_priority() > right.record.get_priority()) {
703 6 return 1;
704 }
705
706 // check time. Older is higher priority
707
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
7 else if (left.record.get_tSec() < right.record.get_tSec()) {
708 ✗ return -1;
709
2/2
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 5 times.
7 } else if (left.record.get_tSec() > right.record.get_tSec()) {
710 2 return 1;
711 }
712
713 // check subsecond time. Older is higher priority
714
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 else if (left.record.get_tSub() < right.record.get_tSub()) {
715 ✗ return -1;
716
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 } else if (left.record.get_tSub() > right.record.get_tSub()) {
717 ✗ return 1;
718 }
719
720 // check ID. Lower is higher priority
721
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 else if (left.record.get_id() < right.record.get_id()) {
722 ✗ return -1;
723
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 } else if (left.record.get_id() > right.record.get_id()) {
724 ✗ return 1;
725 }
726
727 // check directory. A file of the same name in two configured directories is two products, and the
728 // key includes the directory so that a priority change can never move one onto the other's key
729
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 else if (left.dir < right.dir) {
730 ✗ return -1;
731
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 } else if (left.dir > right.dir) {
732 ✗ return 1;
733 }
734
735 // all fields equal: the same product
736 else {
737 5 return 0;
738 }
739 }
740
741 52 bool DpCatalog::DpStateEntry::hasIdentity(FwDpIdType id, U32 tSec, U32 tSub) const {
742
4/6
✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✓ Branch 5 taken 42 times.
✓ Branch 7 taken 10 times.
✗ Branch 8 not taken.
52 return (this->record.get_id() == id) && (this->record.get_tSec() == tSec) && (this->record.get_tSub() == tSub);
743 }
744
745 52 bool DpCatalog::DpStateEntry::isSameProduct(const DpStateEntry& other) const {
746
3/4
✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 42 times.
104 return (this->dir == other.dir) &&
747 104 this->hasIdentity(other.record.get_id(), other.record.get_tSec(), other.record.get_tSub());
748 }
749
750 9 bool DpCatalog::DpStateEntry::operator==(const DpStateEntry& other) const {
751 9 return compareEntries(*this, other) == 0;
752 }
753 ✗ bool DpCatalog::DpStateEntry::operator!=(const DpStateEntry& other) const {
754 ✗ return compareEntries(*this, other) != 0;
755 }
756
757 ✗ bool DpCatalog::DpStateEntry::operator>(const DpStateEntry& other) const {
758 ✗ return compareEntries(*this, other) > 0;
759 }
760 4 bool DpCatalog::DpStateEntry::operator<(const DpStateEntry& other) const {
761 4 return compareEntries(*this, other) < 0;
762 }
763
764 5 bool DpCatalog::insertEntry(DpStateEntry& entry) {
765 // Insert into the RedBlackTreeSet
766 // The tree maintains sorting by priority, time, ID, and directory via DpStateEntry comparison operators
767
1/1
✓ Branch 1 taken 5 times.
5 Fw::Success status = this->m_dpCatalog.insert(entry);
768 10 return (status == Fw::Success::SUCCESS);
769 5 }
770
771 9 void DpCatalog::sendNextEntry() {
772 // Use xmit flag to break upon STOP_XMIT_CATALOG
773
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (this->m_xmitInProgress != true) {
774 4 return;
775 }
776 // A send is still in flight (START_XMIT_CATALOG after a STOP, before the file completed): its
777 // fileDone resumes the walk, so do not re-send the entry that is still in the catalog (#5777)
778
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (this->m_hasCurrentXmit) {
779 ✗ return;
780 }
781
782 // Look for the next entry to send
783
1/1
✓ Branch 1 taken 9 times.
9 DpStateEntry entry;
784
3/3
✓ Branch 1 taken 9 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 5 times.
9 if (!this->findNextEntry(entry)) {
785 // if no entry found, we are done
786 4 this->m_xmitInProgress = false;
787
1/1
✓ Branch 1 taken 4 times.
4 this->log_ACTIVITY_HI_CatalogXmitCompleted(this->m_xmitBytes);
788
2/2
✓ Branch 1 taken 4 times.
✓ Branch 4 taken 4 times.
4 this->dispatchWaitedResponse(Fw::CmdResponse::OK);
789 4 return;
790 }
791
792 // Save current entry for fileDone_handler
793
1/1
✓ Branch 1 taken 5 times.
5 this->m_currentXmitEntry = entry;
794 5 this->m_hasCurrentXmit = true;
795
796 // Build file name based on the found entry
797 Fw::FormatStatus formatStatus =
798
1/1
✓ Branch 5 taken 5 times.
5 this->m_currXmitFileName.format(DP_FILENAME_FORMAT, this->m_directories[entry.dir].toChar(),
799 entry.record.get_id(), entry.record.get_tSec(), entry.record.get_tSub());
800
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (formatStatus != Fw::FormatStatus::SUCCESS) {
801 ✗ this->log_WARNING_HI_FileNameFormatError(this->m_currXmitFileName,
802 static_cast<Fw::StringFormatStatus::T>(formatStatus));
803 // No send is in flight, so no fileDone will arrive: abort the transmit
804 // rather than leaving it wedged in progress
805 ✗ this->abortXmit(Fw::CmdResponse::EXECUTION_ERROR);
806 ✗ return;
807 }
808
1/1
✓ Branch 3 taken 5 times.
5 this->log_ACTIVITY_LO_SendingProduct(this->m_currXmitFileName, static_cast<U32>(entry.record.get_size()),
809 entry.record.get_priority());
810
1/1
✓ Branch 1 taken 5 times.
5 Svc::SendFileResponse resp = this->fileOut_out(0, this->m_currXmitFileName, this->m_currXmitFileName, 0, 0);
811
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
5 if (resp.get_status() != Svc::SendFileStatus::STATUS_OK) {
812 ✗ this->log_WARNING_HI_DpFileSendError(this->m_currXmitFileName, resp.get_status());
813 // A rejected send produces no fileDone callback: abort the transmit
814 // rather than leaving it wedged in progress
815 ✗ this->abortXmit(Fw::CmdResponse::EXECUTION_ERROR);
816 ✗ return;
817 }
818 // FileDownlink echoes this context in fileDone; it identifies this send (#5777)
819 5 this->m_currXmitContext = resp.get_context();
820 9 } // end sendNextEntry()
821
822 static_assert(sizeof(FwDpPriorityType) <= sizeof(U32),
823 "DpRecord stores priority as U32; a wider FwDpPriorityType would be truncated in the catalog");
824
825 9 bool DpCatalog::findNextEntry(DpStateEntry& entry) {
826 // Entries are ordered by ascending priority value (begin() returns the most urgent) and are removed
827 // as they are transmitted, so the next entry to send is the first one at or after m_xmitStartPriority.
828 // Entries before the range are skipped (not removed) so a later START_XMIT_CATALOG can still send
829 // them; once an entry past m_xmitEndPriority is reached nothing further can be in range.
830
1/1
✓ Branch 1 taken 9 times.
9 const FwSizeType size = this->m_dpCatalog.getSize();
831
1/1
✓ Branch 1 taken 9 times.
9 typename Fw::RedBlackTreeSet<DpStateEntry, DP_MAX_FILES>::ConstIterator iter = this->m_dpCatalog.begin();
832 // index gives the walk a fixed upper bound (catalog size); the iterator check is the real end condition
833
10/13
✓ Branch 1 taken 4 times.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 3 times.
✓ Branch 6 taken 10 times.
✓ Branch 9 taken 10 times.
✓ Branch 11 taken 10 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 10 times.
✓ Branch 14 taken 3 times.
✓ Branch 16 taken 10 times.
✓ Branch 17 taken 3 times.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
17 for (FwSizeType index = 0; (index < size) && (iter != this->m_dpCatalog.end()); index++, ++iter) {
834
1/1
✓ Branch 1 taken 10 times.
10 const FwDpPriorityType priority = (*iter).record.get_priority();
835
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 6 times.
10 if (priority < this->m_xmitStartPriority) {
836 4 continue;
837 }
838
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
6 if (priority > this->m_xmitEndPriority) {
839 1 break;
840 }
841
2/2
✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
5 entry = *iter;
842 5 return true;
843 }
844
845 4 return false;
846 9 }
847
848 11 bool DpCatalog::checkInit() {
849
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (not this->m_initialized) {
850 ✗ this->log_WARNING_HI_ComponentNotInitialized();
851 ✗ return false;
852
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 } else if (0 == this->m_numDpSlots) {
853 ✗ this->log_WARNING_HI_ComponentNoMemory();
854 ✗ return false;
855 }
856
857 11 return true;
858 }
859
860 1 void DpCatalog::shutdown() {
861 // only try to deallocate if both pointers are non-zero
862 // it's a way to more gracefully shut down if there are missing
863 // pointers
864
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 if ((this->m_allocator != nullptr) and (this->m_memPtr != nullptr)) {
865 1 this->m_allocator->deallocate(this->m_allocatorId, this->m_memPtr);
866 }
867 1 }
868
869 // ----------------------------------------------------------------------
870 // Handler implementations for user-defined typed input ports
871 // ----------------------------------------------------------------------
872
873 5 void DpCatalog ::fileDone_handler(FwIndexType portNum, const Svc::SendFileResponse& resp) {
874 // Accept only the fileDone for the send in flight (matching FileDownlink context); anything
875 // else is a late callback from an abandoned send (#5777) and must not touch the current transmit
876
3/6
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 5 times.
5 if (!this->m_hasCurrentXmit || resp.get_context() != this->m_currXmitContext) {
877 ✗ this->log_WARNING_LO_StaleFileDone(resp.get_context(), resp.get_status());
878 ✗ return;
879 }
880
881 // check file status
882
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
5 if (resp.get_status() != Svc::SendFileStatus::STATUS_OK) {
883 ✗ this->log_WARNING_HI_DpFileXmitError(this->m_currXmitFileName, resp.get_status());
884 ✗ this->abortXmit(Fw::CmdResponse::EXECUTION_ERROR);
885 ✗ return;
886 }
887
888 // Reduce pending
889 5 this->m_pendingDpBytes -= this->m_currentXmitEntry.record.get_size();
890 5 this->m_pendingFiles--;
891 // Log File Complete & pending
892
1/1
✓ Branch 1 taken 5 times.
5 this->log_ACTIVITY_LO_ProductComplete(this->m_currXmitFileName, this->m_pendingFiles, this->m_pendingDpBytes);
893
894 // mark the entry as transmitted
895
1/1
✓ Branch 1 taken 5 times.
5 this->m_currentXmitEntry.record.set_state(Fw::DpState::TRANSMITTED);
896 // update the transmitted state in the state file data, so a later rewrite of the state file
897 // keeps it (a full table is reported by the update), and in the state file
898
1/1
✓ Branch 1 taken 5 times.
5 (void)this->updateStateFileData(this->m_currentXmitEntry);
899
1/1
✓ Branch 1 taken 5 times.
5 this->appendFileState(this->m_currentXmitEntry);
900 // add the size
901 5 this->m_xmitBytes += this->m_currentXmitEntry.record.get_size();
902
903 // Remove from catalog. The entry is located by its stable identity rather than by the copy kept
904 // for the send, whose sort key may be stale if the priority changed during the send.
905 // Not ground-reachable: only resetCatalog empties the set, and it also clears m_hasCurrentXmit (checked above)
906
1/1
✓ Branch 1 taken 5 times.
5 DpStateEntry catalogEntry{};
907
1/1
✓ Branch 1 taken 5 times.
5 const DpCatalog_EntryLookupStatus found = this->findEntryByIdentity(this->m_currentXmitEntry, catalogEntry);
908 5 FW_ASSERT(found == DpCatalog_EntryLookupStatus::FOUND, static_cast<FwAssertArgType>(found.e));
909 // catalogEntry is a copy of a set element, so its key is present
910
1/1
✓ Branch 1 taken 5 times.
5 const Fw::Success status = this->m_dpCatalog.remove(catalogEntry);
911 5 FW_ASSERT(status == Fw::Success::SUCCESS, static_cast<FwAssertArgType>(status));
912
913 5 this->m_hasCurrentXmit = false;
914
915 // send the next entry, if it exists
916
1/1
✓ Branch 1 taken 5 times.
5 this->sendNextEntry();
917 5 }
918
919 66 void DpCatalog ::pingIn_handler(FwIndexType portNum, U32 key) {
920 // return code for health ping
921 66 this->pingOut_out(0, key);
922 66 }
923
924 5 void DpCatalog ::addToCat_handler(FwIndexType portNum,
925 const Fw::StringBase& fileName,
926 FwDpPriorityType priority,
927 FwSizeType size) {
928 // check initialization
929
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
5 if (not this->checkInit()) {
930 ✗ return;
931 }
932
933 // check that initialization got memory
934
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (0 == this->m_numDpSlots) {
935 ✗ this->log_WARNING_HI_NoDpMemory();
936 ✗ return;
937 }
938
939 // Check the catalog has been built
940
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 if (not this->m_catalogBuilt) {
941 2 this->log_ACTIVITY_HI_NotLoaded(fileName);
942 2 return;
943 }
944
945 // Both of these are grabbed from the header
946 (void)priority;
947 (void)size;
948
949 // Since this is a runtime addition
950 // Check if file is in one of our directories
951
2/2
✓ Branch 1 taken 3 times.
✓ Branch 4 taken 3 times.
3 FwSizeType dir = this->determineDirectory(fileName);
952
953 // Not in one of our directories; skip this file
954
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (dir == DP_MAX_DIRECTORIES) {
955 ✗ this->log_WARNING_HI_DirectoryNotManaged(fileName);
956 ✗ return;
957 }
958
959
2/2
✓ Branch 1 taken 3 times.
✓ Branch 4 taken 3 times.
3 const ProcessFileStatus ret = processFile(fileName, dir);
960
961
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (ret == ProcessFileStatus::SUCCESS) {
962 // If we already finished (or were stopped), resume only if m_remainActive and findNextEntry sees a
963 // pending entry (the new product or one left behind) inside the active priority range. Probing first
964 // avoids the CatalogXmitCompleted that sendNextEntry would report for an empty range.
965
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 if (!this->m_xmitInProgress && this->m_remainActive) {
966 ✗ DpStateEntry nextEntry{};
967 ✗ if (this->findNextEntry(nextEntry)) {
968 ✗ this->m_xmitInProgress = true;
969 ✗ this->sendNextEntry();
970 }
971 ✗ }
972 // Otherwise, the fileDone of the file in flight invokes sendNextEntry, which finds the right file,
973 // or the product is transmitted by the next transmit command
974
975 // The state file is not rewritten here: a newly written product has no transmit state to
976 // record, and pruneAndWriteStateFile() would discard the TRANSMITTED records appended by
977 // fileDone since the catalog was built.
978 }
979 }
980
981 // ----------------------------------------------------------------------
982 // Handler implementations for commands
983 // ----------------------------------------------------------------------
984
985 2 void DpCatalog ::BUILD_CATALOG_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
986 // invoke helper
987
2/2
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
2 this->cmdResponse_out(opCode, cmdSeq, this->doCatalogBuild());
988 2 }
989
990 ✗ void DpCatalog ::START_XMIT_CATALOG_cmdHandler(FwOpcodeType opCode,
991 U32 cmdSeq,
992 const Fw::Wait& wait,
993 bool remainActive) {
994 ✗ this->startXmitCmd(opCode, cmdSeq, wait, remainActive, 0, std::numeric_limits<FwDpPriorityType>::max());
995 ✗ }
996
997 5 void DpCatalog ::START_XMIT_CATALOG_WITH_PRIORITY_cmdHandler(FwOpcodeType opCode,
998 U32 cmdSeq,
999 const Fw::Wait& wait,
1000 bool remainActive,
1001 FwDpPriorityType startPriority,
1002 FwDpPriorityType endPriority) {
1003 5 this->startXmitCmd(opCode, cmdSeq, wait, remainActive, startPriority, endPriority);
1004 5 }
1005
1006 5 void DpCatalog::startXmitCmd(FwOpcodeType opCode,
1007 U32 cmdSeq,
1008 const Fw::Wait& wait,
1009 bool remainActive,
1010 FwDpPriorityType startPriority,
1011 FwDpPriorityType endPriority) {
1012
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
5 if (startPriority > endPriority) {
1013
1/1
✓ Branch 1 taken 1 times.
1 this->log_WARNING_LO_XmitPriorityRangeInvalid(startPriority, endPriority);
1014
2/2
✓ Branch 1 taken 1 times.
✓ Branch 4 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR);
1015 1 return;
1016 }
1017 4 this->m_remainActive = remainActive;
1018
1019 // Arm the waited response before starting: an empty catalog completes the
1020 // transmit inside doCatalogXmit and must still answer a waited command
1021
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 if (Fw::Wait::WAIT == wait) {
1022 4 this->m_xmitCmdWait = true;
1023 4 this->m_xmitOpCode = opCode;
1024 4 this->m_xmitCmdSeq = cmdSeq;
1025 }
1026
1027
1/1
✓ Branch 1 taken 4 times.
4 Fw::CmdResponse resp = this->doCatalogXmit(startPriority, endPriority);
1028 4 FW_ASSERT(resp.isValid(), static_cast<FwAssertArgType>(resp.e));
1029
1030
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (resp != Fw::CmdResponse::OK) {
1031 ✗ this->m_xmitCmdWait = false;
1032 ✗ this->m_xmitOpCode = 0;
1033 ✗ this->m_xmitCmdSeq = 0;
1034 ✗ this->cmdResponse_out(opCode, cmdSeq, resp);
1035
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 } else if (Fw::Wait::NO_WAIT == wait) {
1036 ✗ this->cmdResponse_out(opCode, cmdSeq, resp);
1037 }
1038 4 }
1039
1040 4 Fw::CmdResponse DpCatalog::doCatalogXmit(FwDpPriorityType startPriority, FwDpPriorityType endPriority) {
1041 4 FW_ASSERT(startPriority <= endPriority, static_cast<FwAssertArgType>(startPriority),
1042 static_cast<FwAssertArgType>(endPriority));
1043 // check initialization
1044
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (not this->checkInit()) {
1045 ✗ return Fw::CmdResponse::EXECUTION_ERROR;
1046 }
1047
1048 // check that initialization got memory
1049
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (0 == this->m_numDpSlots) {
1050 ✗ this->log_WARNING_HI_NoDpMemory();
1051 ✗ return Fw::CmdResponse::EXECUTION_ERROR;
1052 }
1053
1054 // make sure a downlink is not in progress
1055
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (this->m_xmitInProgress) {
1056 ✗ this->log_WARNING_LO_DpXmitInProgress();
1057 ✗ return Fw::CmdResponse::EXECUTION_ERROR;
1058 }
1059
1060 // Check the catalog has been built
1061
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (not this->m_catalogBuilt) {
1062 ✗ this->log_WARNING_HI_XmitUnbuiltCatalog();
1063 ✗ return Fw::CmdResponse::EXECUTION_ERROR;
1064 }
1065
1066 // start transmission; a START that resumes a send left in flight by STOP keeps the
1067 // session's byte tally, so CatalogXmitCompleted reports the whole session
1068
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (!this->m_hasCurrentXmit) {
1069 4 this->m_xmitBytes = 0;
1070 }
1071 4 this->m_xmitStartPriority = startPriority;
1072 4 this->m_xmitEndPriority = endPriority;
1073 4 this->log_ACTIVITY_HI_CatalogXmitRangeStarted(startPriority, endPriority);
1074 4 this->m_xmitInProgress = true;
1075 // Step 3b - search for and send first entry
1076 4 this->sendNextEntry();
1077 4 return Fw::CmdResponse::OK;
1078 }
1079
1080 ✗ void DpCatalog ::STOP_XMIT_CATALOG_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
1081 ✗ if (not this->m_xmitInProgress) {
1082 ✗ this->log_WARNING_LO_XmitNotActive();
1083 // benign error, so don't fail the command
1084 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
1085 } else {
1086 ✗ this->log_ACTIVITY_HI_CatalogXmitStopped(this->m_xmitBytes);
1087 // Disarm xmitInProgress so sendNextEntry stops; keep hasCurrentXmit so the
1088 // in-flight file's fileDone is still recorded (the context check rejects any other)
1089 ✗ this->m_xmitInProgress = false;
1090 // Respond to original cmd to start xmit
1091 // (if we haven't already)
1092 ✗ this->dispatchWaitedResponse(Fw::CmdResponse::OK);
1093
1094 // Respond to this command
1095 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
1096 }
1097 ✗ }
1098
1099 1 void DpCatalog ::CLEAR_CATALOG_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
1100 // Dropping the send in flight closes the transmit session here, as STOP does, so a waited
1101 // START_XMIT_CATALOG is answered now and the next BUILD/START is not refused as in progress
1102
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (this->m_xmitInProgress) {
1103 ✗ this->abortXmit(Fw::CmdResponse::EXECUTION_ERROR);
1104 }
1105 1 this->log_ACTIVITY_HI_CatalogCleared(this->m_pendingFiles, this->m_pendingDpBytes);
1106 1 this->resetCatalog();
1107 1 this->resetStateFileData();
1108
1109
2/2
✓ Branch 1 taken 1 times.
✓ Branch 4 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
1110 1 }
1111
1112 4 void DpCatalog ::dispatchWaitedResponse(Fw::CmdResponse response) {
1113
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (this->m_xmitCmdWait) {
1114
2/2
✓ Branch 1 taken 4 times.
✓ Branch 4 taken 4 times.
4 this->cmdResponse_out(this->m_xmitOpCode, this->m_xmitCmdSeq, response);
1115
1116 // Prevent a Duplicate Cmd Response
1117 4 this->m_xmitCmdWait = false;
1118 4 this->m_xmitOpCode = 0;
1119 4 this->m_xmitCmdSeq = 0;
1120 }
1121 4 }
1122
1123 ✗ void DpCatalog ::abortXmit(Fw::CmdResponse response) {
1124 ✗ this->m_hasCurrentXmit = false;
1125 ✗ this->m_xmitInProgress = false;
1126 ✗ this->dispatchWaitedResponse(response);
1127 ✗ }
1128
1129 ✗ void DpCatalog ::SET_DP_PRIORITY_cmdHandler(FwOpcodeType opCode,
1130 U32 cmdSeq,
1131 FwDpIdType id,
1132 U32 tSec,
1133 U32 tSub,
1134 FwDpPriorityType priority) {
1135 ✗ if (not this->checkInit()) {
1136 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
1137 ✗ return;
1138 }
1139
1140 ✗ DpStateEntry entry{};
1141 ✗ if (this->findProductEntry(id, tSec, tSub, entry) == DpCatalog_EntryLookupStatus::NOT_FOUND) {
1142 ✗ this->log_WARNING_HI_DpNotFound(id, tSec, tSub);
1143 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
1144 ✗ return;
1145 }
1146
1147 ✗ const DpCatalog_PriorityChangeStatus status = this->setDpPriority(entry, priority);
1148 ✗ this->cmdResponse_out(opCode, cmdSeq,
1149 ✗ (status == DpCatalog_PriorityChangeStatus::NOT_RECORDED) ? Fw::CmdResponse::EXECUTION_ERROR
1150 : Fw::CmdResponse::OK);
1151 ✗ }
1152
1153 ✗ DpCatalog_PriorityChangeStatus DpCatalog ::setDpPriority(DpStateEntry& entry, FwDpPriorityType priority) {
1154 ✗ const FwDpIdType id = entry.record.get_id();
1155 ✗ const U32 tSec = entry.record.get_tSec();
1156 ✗ const U32 tSub = entry.record.get_tSub();
1157 ✗ const FwDpPriorityType oldPriority = entry.record.get_priority();
1158 ✗ if (priority == oldPriority) {
1159 ✗ this->log_ACTIVITY_HI_DpPriorityUnchanged(id, tSec, tSub, priority);
1160 ✗ return DpCatalog_PriorityChangeStatus::UNCHANGED;
1161 }
1162
1163 // Priority is part of the sort key, so the entry is re-linked by removing and re-inserting it.
1164 // Only this entry moves, and every send restarts the walk at begin(), so a transmit in
1165 // progress follows the new order from its next send
1166 // entry is a copy of a set element, so its key is present
1167 ✗ Fw::Success status = this->m_dpCatalog.remove(entry);
1168 ✗ FW_ASSERT(status == Fw::Success::SUCCESS, static_cast<FwAssertArgType>(status));
1169 ✗ entry.record.set_priority(priority);
1170 // the removal freed a node, and the key (which includes the full product identity) is held by no
1171 // other entry, so the insert cannot fail or collide
1172 ✗ status = this->m_dpCatalog.insert(entry);
1173 ✗ FW_ASSERT(status == Fw::Success::SUCCESS, static_cast<FwAssertArgType>(status));
1174 // The send in flight is not affected; its copy is kept current so that the record written on
1175 // completion carries the priority the product had (fileDone finds the catalog entry by identity)
1176 ✗ if (this->m_hasCurrentXmit && this->m_currentXmitEntry.isSameProduct(entry)) {
1177 ✗ this->m_currentXmitEntry.record.set_priority(priority);
1178 }
1179 ✗ this->log_ACTIVITY_HI_DpPrioritySet(id, tSec, tSub, oldPriority, priority);
1180
1181 // Record the new priority with the product's state, as completion does, so that a rewrite of the
1182 // state file keeps it across a rebuild or restart. Nothing else depends on this record: a runtime
1183 // addToCat recognizes the product by identity, not by priority (processFile)
1184 ✗ const DpCatalog_StateFileUpdateStatus updateStatus = this->updateStateFileData(entry);
1185 ✗ if (this->m_stateFile.length() == 0) {
1186 ✗ return DpCatalog_PriorityChangeStatus::CHANGED;
1187 }
1188 ✗ if (updateStatus == DpCatalog_StateFileUpdateStatus::FULL) {
1189 ✗ return DpCatalog_PriorityChangeStatus::NOT_RECORDED;
1190 }
1191 ✗ const DpCatalog_StateFileWriteStatus writeStatus = this->pruneAndWriteStateFile();
1192 ✗ return (writeStatus == DpCatalog_StateFileWriteStatus::WRITTEN) ? DpCatalog_PriorityChangeStatus::CHANGED
1193 ✗ : DpCatalog_PriorityChangeStatus::NOT_RECORDED;
1194 ✗ }
1195
1196 10 DpCatalog_EntryLookupStatus DpCatalog ::findEntryByIdentity(const DpStateEntry& product, DpStateEntry& entry) const {
1197 // The sort key includes the priority, which SET_DP_PRIORITY changes, so the lookup is by the
1198 // identity fields alone; the walk is bounded by the catalog capacity
1199
1/1
✓ Branch 1 taken 10 times.
10 for (Fw::RedBlackTreeSet<DpStateEntry, DP_MAX_FILES>::ConstIterator iter = this->m_dpCatalog.begin();
1200
5/5
✓ Branch 1 taken 6 times.
✓ Branch 4 taken 16 times.
✓ Branch 7 taken 16 times.
✓ Branch 10 taken 11 times.
✓ Branch 11 taken 5 times.
22 iter != this->m_dpCatalog.end(); ++iter) {
1201
3/3
✓ Branch 1 taken 11 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 6 times.
11 if ((*iter).isSameProduct(product)) {
1202
2/2
✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
5 entry = *iter;
1203
1/1
✓ Branch 1 taken 5 times.
5 return DpCatalog_EntryLookupStatus::FOUND;
1204 }
1205 10 }
1206 5 return DpCatalog_EntryLookupStatus::NOT_FOUND;
1207 }
1208
1209 ✗ DpCatalog_EntryLookupStatus DpCatalog ::findProductEntry(FwDpIdType id, U32 tSec, U32 tSub, DpStateEntry& entry) const {
1210 // The command names the product by id and time tag. The sort key includes the priority, which the
1211 // command changes, so the lookup is a walk by the identity fields, bounded by the catalog capacity
1212 ✗ for (Fw::RedBlackTreeSet<DpStateEntry, DP_MAX_FILES>::ConstIterator iter = this->m_dpCatalog.begin();
1213 ✗ iter != this->m_dpCatalog.end(); ++iter) {
1214 ✗ if ((*iter).hasIdentity(id, tSec, tSub)) {
1215 ✗ entry = *iter;
1216 ✗ return DpCatalog_EntryLookupStatus::FOUND;
1217 }
1218 ✗ }
1219 ✗ return DpCatalog_EntryLookupStatus::NOT_FOUND;
1220 }
1221
1222 } // namespace Svc
1223