GCC Code Coverage Report


Directory: ./
File: Svc/FileManager/FileManager.cpp
Date: 2026-09-03 21:13:48
Exec Total Coverage
Lines: 250 285 87.7%
Functions: 17 18 94.4%
Branches: 203 257 79.0%

Line Branch Exec Source
1 // ======================================================================
2 // \title FileManager.hpp
3 // \author bocchino
4 // \brief hpp file for FileManager component implementation class
5 //
6 // \copyright
7 // Copyright 2009-2015, by the California Institute of Technology.
8 // ALL RIGHTS RESERVED. United States Government Sponsorship
9 // acknowledged.
10 //
11 // ======================================================================
12
13 #include <cstdio>
14 #include <cstdlib>
15
16 #include <Fw/FPrimeBasicTypes.hpp>
17 #include "Fw/Types/Assert.hpp"
18 #include "Fw/Types/ExternalString.hpp"
19 #include "Os/Directory.hpp"
20 #include "Svc/FileManager/FileManager.hpp"
21 #include "config/FileManagerConfig.hpp"
22
23 namespace Svc {
24
25 // ----------------------------------------------------------------------
26 // Construction, initialization, and destruction
27 // ----------------------------------------------------------------------
28
29 28 FileManager ::FileManager(const char* const compName //!< The component name
30 28 )
31 : FileManagerComponentBase(compName),
32 28 commandCount(0),
33 28 errorCount(0),
34 28 m_listState(IDLE),
35 28 m_totalEntries(0),
36 28 m_currentOpCode(0),
37 28 m_currentCmdSeq(0),
38 28 m_runQueued(false),
39 28 m_dpState(DP_IDLE),
40 28 m_dpFileSize(0),
41 28 m_dpOffset(0),
42 28 m_dpChunkSize(0),
43 28 m_dpEndOffset(0),
44 28 m_dpPriority(0),
45 28 m_dpChunkCount(0),
46 28 m_dpOpCode(0),
47 28 m_dpCmdSeq(0),
48
4/4
✓ Branch 10 taken 28 times.
✓ Branch 16 taken 28 times.
✓ Branch 22 taken 28 times.
✓ Branch 28 taken 28 times.
112 m_dpBuffer{} {}
49
50 56 FileManager ::~FileManager() {}
51
52 // ----------------------------------------------------------------------
53 // Command handler implementations
54 // ----------------------------------------------------------------------
55
56 2 void FileManager ::CreateDirectory_cmdHandler(const FwOpcodeType opCode,
57 const U32 cmdSeq,
58 const Fw::CmdStringArg& dirName) {
59
1/1
✓ Branch 6 taken 2 times.
2 Fw::LogStringArg logStringDirName(dirName.toChar());
60
1/1
✓ Branch 5 taken 2 times.
2 this->log_ACTIVITY_HI_CreateDirectoryStarted(logStringDirName);
61 2 bool errorIfDirExists = true;
62
1/1
✓ Branch 5 taken 2 times.
2 const Os::FileSystem::Status status = Os::FileSystem::createDirectory(dirName.toChar(), errorIfDirExists);
63
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (status != Os::FileSystem::OP_OK) {
64
1/1
✓ Branch 5 taken 1 times.
1 this->log_WARNING_HI_DirectoryCreateError(logStringDirName, status);
65 } else {
66
1/1
✓ Branch 5 taken 1 times.
1 this->log_ACTIVITY_HI_CreateDirectorySucceeded(logStringDirName);
67 }
68
1/1
✓ Branch 4 taken 2 times.
2 this->emitTelemetry(status);
69
1/1
✓ Branch 4 taken 2 times.
2 this->sendCommandResponse(opCode, cmdSeq, status);
70 4 }
71
72 2 void FileManager ::RemoveFile_cmdHandler(const FwOpcodeType opCode,
73 const U32 cmdSeq,
74 const Fw::CmdStringArg& fileName,
75 const bool ignoreErrors) {
76
1/1
✓ Branch 6 taken 2 times.
2 Fw::LogStringArg logStringFileName(fileName.toChar());
77
1/1
✓ Branch 5 taken 2 times.
2 this->log_ACTIVITY_HI_RemoveFileStarted(logStringFileName);
78
1/1
✓ Branch 5 taken 2 times.
2 const Os::FileSystem::Status status = Os::FileSystem::removeFile(fileName.toChar());
79
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (status != Os::FileSystem::OP_OK) {
80
1/1
✓ Branch 5 taken 1 times.
1 this->log_WARNING_HI_FileRemoveError(logStringFileName, status);
81
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ignoreErrors == true) {
82 ++this->errorCount;
83 this->tlmWrite_Errors(this->errorCount);
84 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
85 return;
86 }
87 } else {
88
1/1
✓ Branch 5 taken 1 times.
1 this->log_ACTIVITY_HI_RemoveFileSucceeded(logStringFileName);
89 }
90
1/1
✓ Branch 4 taken 2 times.
2 this->emitTelemetry(status);
91
1/1
✓ Branch 4 taken 2 times.
2 this->sendCommandResponse(opCode, cmdSeq, status);
92 2 }
93
94 2 void FileManager ::MoveFile_cmdHandler(const FwOpcodeType opCode,
95 const U32 cmdSeq,
96 const Fw::CmdStringArg& sourceFileName,
97 const Fw::CmdStringArg& destFileName) {
98
1/1
✓ Branch 6 taken 2 times.
2 Fw::LogStringArg logStringSource(sourceFileName.toChar());
99
1/1
✓ Branch 6 taken 2 times.
2 Fw::LogStringArg logStringDest(destFileName.toChar());
100
1/1
✓ Branch 5 taken 2 times.
2 this->log_ACTIVITY_HI_MoveFileStarted(logStringSource, logStringDest);
101
1/1
✓ Branch 9 taken 2 times.
2 const Os::FileSystem::Status status = Os::FileSystem::moveFile(sourceFileName.toChar(), destFileName.toChar());
102
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (status != Os::FileSystem::OP_OK) {
103
1/1
✓ Branch 5 taken 1 times.
1 this->log_WARNING_HI_FileMoveError(logStringSource, logStringDest, status);
104 } else {
105
1/1
✓ Branch 5 taken 1 times.
1 this->log_ACTIVITY_HI_MoveFileSucceeded(logStringSource, logStringDest);
106 }
107
1/1
✓ Branch 4 taken 2 times.
2 this->emitTelemetry(status);
108
1/1
✓ Branch 4 taken 2 times.
2 this->sendCommandResponse(opCode, cmdSeq, status);
109 4 }
110
111 2 void FileManager ::RemoveDirectory_cmdHandler(const FwOpcodeType opCode,
112 const U32 cmdSeq,
113 const Fw::CmdStringArg& dirName) {
114
1/1
✓ Branch 6 taken 2 times.
2 Fw::LogStringArg logStringDirName(dirName.toChar());
115
1/1
✓ Branch 5 taken 2 times.
2 this->log_ACTIVITY_HI_RemoveDirectoryStarted(logStringDirName);
116
1/1
✓ Branch 5 taken 2 times.
2 const Os::FileSystem::Status status = Os::FileSystem::removeDirectory(dirName.toChar());
117
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (status != Os::FileSystem::OP_OK) {
118
1/1
✓ Branch 5 taken 1 times.
1 this->log_WARNING_HI_DirectoryRemoveError(logStringDirName, status);
119 } else {
120
1/1
✓ Branch 5 taken 1 times.
1 this->log_ACTIVITY_HI_RemoveDirectorySucceeded(logStringDirName);
121 }
122
1/1
✓ Branch 4 taken 2 times.
2 this->emitTelemetry(status);
123
1/1
✓ Branch 4 taken 2 times.
2 this->sendCommandResponse(opCode, cmdSeq, status);
124 4 }
125
126 3 void FileManager ::AppendFile_cmdHandler(const FwOpcodeType opCode,
127 const U32 cmdSeq,
128 const Fw::CmdStringArg& source,
129 const Fw::CmdStringArg& target) {
130
1/1
✓ Branch 6 taken 3 times.
3 Fw::LogStringArg logStringSource(source.toChar());
131
1/1
✓ Branch 6 taken 3 times.
3 Fw::LogStringArg logStringTarget(target.toChar());
132
1/1
✓ Branch 5 taken 3 times.
3 this->log_ACTIVITY_HI_AppendFileStarted(logStringSource, logStringTarget);
133
134 Os::FileSystem::Status status;
135
1/1
✓ Branch 9 taken 3 times.
3 status = Os::FileSystem::appendFile(source.toChar(), target.toChar(), true);
136
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (status != Os::FileSystem::OP_OK) {
137
1/1
✓ Branch 5 taken 1 times.
1 this->log_WARNING_HI_AppendFileFailed(logStringSource, logStringTarget, status);
138 } else {
139
1/1
✓ Branch 5 taken 2 times.
2 this->log_ACTIVITY_HI_AppendFileSucceeded(logStringSource, logStringTarget);
140 }
141
142
1/1
✓ Branch 4 taken 3 times.
3 this->emitTelemetry(status);
143
1/1
✓ Branch 4 taken 3 times.
3 this->sendCommandResponse(opCode, cmdSeq, status);
144 6 }
145
146 2 void FileManager ::FileSize_cmdHandler(const FwOpcodeType opCode, const U32 cmdSeq, const Fw::CmdStringArg& fileName) {
147
1/1
✓ Branch 6 taken 2 times.
2 Fw::LogStringArg logStringFileName(fileName.toChar());
148
1/1
✓ Branch 5 taken 2 times.
2 this->log_ACTIVITY_HI_FileSizeStarted(logStringFileName);
149
150 2 FwSizeType size_arg;
151
1/1
✓ Branch 5 taken 2 times.
2 const Os::FileSystem::Status status = Os::FileSystem::getFileSize(fileName.toChar(), size_arg);
152
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (status != Os::FileSystem::OP_OK) {
153
1/1
✓ Branch 5 taken 1 times.
1 this->log_WARNING_HI_FileSizeError(logStringFileName, status);
154 } else {
155
1/1
✓ Branch 5 taken 1 times.
1 this->log_ACTIVITY_HI_FileSizeSucceeded(logStringFileName, size_arg);
156 }
157
1/1
✓ Branch 4 taken 2 times.
2 this->emitTelemetry(status);
158
1/1
✓ Branch 4 taken 2 times.
2 this->sendCommandResponse(opCode, cmdSeq, status);
159 4 }
160
161 3 void FileManager ::ListDirectory_cmdHandler(const FwOpcodeType opCode,
162 const U32 cmdSeq,
163 const Fw::CmdStringArg& dirName) {
164 // Check if we're already listing a directory
165
2/4
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
3 if (m_listState == LISTING_IN_PROGRESS) {
166 this->log_WARNING_HI_ListDirectoryError(dirName, static_cast<U32>(Os::Directory::OTHER_ERROR));
167 this->emitTelemetry(Os::FileSystem::OTHER_ERROR);
168 this->sendCommandResponse(opCode, cmdSeq, Os::FileSystem::OTHER_ERROR);
169 return;
170 }
171
172 3 this->log_ACTIVITY_HI_ListDirectoryStarted(dirName);
173
174 // Open the directory for reading
175 3 Os::Directory::Status status = m_currentDir.open(dirName.toChar(), Os::Directory::OpenMode::READ);
176
177
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (status != Os::Directory::OP_OK) {
178 1 this->log_WARNING_HI_ListDirectoryError(dirName, static_cast<U32>(status));
179 1 this->emitTelemetry(Os::FileSystem::OTHER_ERROR);
180 1 this->sendCommandResponse(opCode, cmdSeq, Os::FileSystem::OTHER_ERROR);
181 1 return;
182 }
183
184 // Initialize state machine for asynchronous processing
185 2 m_listState = LISTING_IN_PROGRESS;
186 2 m_currentDirName = dirName;
187 2 m_currentOpCode = opCode;
188 2 m_currentCmdSeq = cmdSeq;
189 2 m_totalEntries = 0;
190
191 // Directory listing will be processed asynchronously by the rate group.
192 // The schedIn_handler will process FILES_PER_RATE_TICK directory entries per rate tick to
193 // prevent event flooding while maintaining configurable performance.
194 // Command response will be sent when listing completes.
195 }
196
197 1 void FileManager ::CalculateCrc_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, const Fw::CmdStringArg& filename) {
198
1/1
✓ Branch 2 taken 1 times.
1 Os::File file;
199 1 U32 crcValue = 0;
200
1/1
✓ Branch 8 taken 1 times.
1 this->log_ACTIVITY_HI_CalculateCrcStarted(filename);
201
202
1/1
✓ Branch 6 taken 1 times.
1 Os::File::Status status = file.open(filename.toChar(), Os::File::OPEN_READ);
203
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (status == Os::File::OP_OK) {
204
1/1
✓ Branch 2 taken 1 times.
1 status = file.calculateCrc(crcValue);
205 }
206
207
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (status == Os::File::OP_OK) {
208
1/1
✓ Branch 8 taken 1 times.
1 this->log_ACTIVITY_HI_CalculateCrcSucceeded(filename, crcValue);
209
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
210 } else {
211 this->log_WARNING_HI_CalculateCrcFailed(filename, status);
212 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
213 }
214
1/1
✓ Branch 2 taken 1 times.
1 file.close();
215 2 }
216
217 12 void FileManager ::GenerateDp_cmdHandler(FwOpcodeType opCode,
218 U32 cmdSeq,
219 const Fw::CmdStringArg& fileName,
220 U32 chunkSize,
221 U64 beginOffset,
222 U64 endOffset,
223 U32 priority,
224 const FileManager_GenerateDpMode& mode) {
225
1/1
✓ Branch 6 taken 12 times.
12 Fw::LogStringArg logFileName(fileName.toChar());
226
227 // Reject a second request while one is already running
228
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 11 times.
12 if (this->m_dpState != DP_IDLE) {
229
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_GenerateDpFailed(logFileName, FileManager_GenerateDpStage::BUSY, 0);
230
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
231 1 return;
232 }
233
234 // Data products must be available
235
5/8
✓ Branch 5 taken 11 times.
✓ Branch 7 taken 11 times.
✗ Branch 8 not taken.
✓ Branch 14 taken 11 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 11 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 11 times.
11 if (!this->isConnected_productGetOut_OutputPort(0) || !this->isConnected_productSendOut_OutputPort(0)) {
236 this->log_WARNING_HI_GenerateDpBufferFailed(logFileName);
237 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
238 return;
239 }
240
241 // Clamp the requested chunk size to the configured read buffer
242 11 U32 effectiveChunkSize = chunkSize;
243
3/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 10 times.
11 if ((effectiveChunkSize == 0) || (effectiveChunkSize > FileManagerConfig::GENERATE_DP_MAX_CHUNK_SIZE)) {
244 1 effectiveChunkSize = FileManagerConfig::GENERATE_DP_MAX_CHUNK_SIZE;
245 }
246
247
1/1
✓ Branch 10 taken 11 times.
11 Os::File::Status status = this->m_dpFile.open(fileName.toChar(), Os::File::OPEN_READ);
248
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 10 times.
11 if (status != Os::File::OP_OK) {
249
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_GenerateDpFailed(logFileName, FileManager_GenerateDpStage::OPEN, static_cast<U32>(status));
250
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
251 1 return;
252 }
253
254 10 FwSizeType fileSize = 0;
255
1/1
✓ Branch 6 taken 10 times.
10 status = this->m_dpFile.size(fileSize);
256
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (status != Os::File::OP_OK) {
257 this->m_dpFile.close();
258 this->log_WARNING_HI_GenerateDpFailed(logFileName, FileManager_GenerateDpStage::SIZE, static_cast<U32>(status));
259 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
260 return;
261 }
262
263 // An end offset of zero, or one past the end of the file, means the end of
264 // the file. Ranges let an operator retransmit part of a file or spread the
265 // downlink over several commands.
266 10 U64 effectiveEnd = endOffset;
267
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
10 if ((effectiveEnd == 0) || (effectiveEnd > static_cast<U64>(fileSize))) {
268 8 effectiveEnd = static_cast<U64>(fileSize);
269 }
270
271 10 const bool emptyFile = (fileSize == 0);
272
5/6
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 8 times.
10 const bool badRange = (beginOffset > static_cast<U64>(fileSize)) || (!emptyFile && (beginOffset >= effectiveEnd));
273
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 9 times.
10 if (badRange) {
274
1/1
✓ Branch 6 taken 1 times.
1 this->m_dpFile.close();
275
1/1
✓ Branch 5 taken 1 times.
1 this->log_WARNING_HI_GenerateDpInvalidRange(logFileName, beginOffset, endOffset, static_cast<U64>(fileSize));
276
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
277 1 return;
278 }
279
280 // Position the file at the start of the requested range
281
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 8 times.
9 if (beginOffset > 0) {
282
1/1
✓ Branch 6 taken 1 times.
1 status = this->m_dpFile.seek(static_cast<FwSignedSizeType>(beginOffset), Os::File::SeekType::ABSOLUTE);
283
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (status != Os::File::OP_OK) {
284 this->m_dpFile.close();
285 this->log_WARNING_HI_GenerateDpFailed(logFileName, FileManager_GenerateDpStage::SEEK,
286 static_cast<U32>(status));
287 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
288 return;
289 }
290 }
291
292
2/2
✓ Branch 6 taken 9 times.
✓ Branch 15 taken 9 times.
9 this->m_dpFileName = Fw::String(fileName.toChar());
293 9 this->m_dpFileSize = fileSize;
294 9 this->m_dpOffset = beginOffset;
295 9 this->m_dpEndOffset = effectiveEnd;
296 9 this->m_dpChunkSize = effectiveChunkSize;
297 9 this->m_dpChunkCount = 0;
298 9 this->m_dpOpCode = opCode;
299 9 this->m_dpCmdSeq = cmdSeq;
300 // A priority of zero reverts to the configured default
301
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 8 times.
9 this->m_dpPriority = (priority == 0) ? static_cast<FwDpPriorityType>(FileManagerCfg::DEFAULT_DP_PRIORITY)
302 : static_cast<FwDpPriorityType>(priority);
303 9 this->m_dpState = DP_IN_PROGRESS;
304
305 // Report the number of bytes that will be written, which is the requested
306 // range rather than the size of the whole file
307
1/1
✓ Branch 13 taken 9 times.
9 this->log_ACTIVITY_HI_GenerateDpStarted(logFileName, this->m_dpEndOffset - this->m_dpOffset);
308
309 // An empty range produces no chunks, so complete immediately
310
2/2
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 8 times.
9 if (this->m_dpOffset >= this->m_dpEndOffset) {
311
1/1
✓ Branch 9 taken 1 times.
1 this->log_ACTIVITY_HI_GenerateDpComplete(logFileName, this->m_dpChunkCount);
312
1/1
✓ Branch 4 taken 1 times.
1 this->finishDpGeneration();
313 1 return;
314 }
315
316 // In immediate mode the whole range is emitted here, so that a project that
317 // wants the file out quickly is not limited by the rate group. In paced
318 // mode the rate group meters the work out and the response is deferred.
319
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 6 times.
8 if (mode == FileManager_GenerateDpMode::IMMEDIATE) {
320
1/1
✓ Branch 4 taken 2 times.
2 this->processDpChunks(0);
321 }
322 12 }
323
324 19 void FileManager ::processDpChunks(U32 chunkLimit) {
325
1/1
✓ Branch 8 taken 19 times.
19 Fw::LogStringArg logFileName(this->m_dpFileName.toChar());
326
327 // A limit of zero means emit the whole remaining range in this call
328 19 const bool paced = (chunkLimit > 0);
329
330
4/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 17 times.
✓ Branch 3 taken 11 times.
33 for (U32 chunk = 0; !paced || (chunk < chunkLimit); chunk++) {
331 // Number of bytes remaining in the requested range. The loop returns as
332 // soon as the range is exhausted, so this is always non-zero here.
333 22 const FwSizeType remaining = static_cast<FwSizeType>(this->m_dpEndOffset - this->m_dpOffset);
334
335 22 const FwSizeType requestedSize = (remaining < static_cast<FwSizeType>(this->m_dpChunkSize))
336 ? remaining
337 : static_cast<FwSizeType>(this->m_dpChunkSize);
338
339 // The file size is known, so a short read means the file changed underneath us
340 22 FwSizeType readSize = requestedSize;
341
1/1
✓ Branch 8 taken 22 times.
22 const Os::File::Status status = this->m_dpFile.read(this->m_dpBuffer, readSize);
342
2/4
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 22 times.
22 if ((status != Os::File::OP_OK) || (readSize != requestedSize)) {
343 this->log_WARNING_HI_GenerateDpFailed(logFileName, FileManager_GenerateDpStage::READ,
344 static_cast<U32>(status));
345 this->finishDpGeneration();
346 return;
347 }
348
349 // Request a container large enough for this chunk's header and data
350 22 const FwSizeType dpSize = SIZE_OF_FileChunkHeaderRecord_RECORD + SIZE_OF_FileChunkDataRecord_RECORD(readSize);
351
1/1
✓ Branch 2 taken 22 times.
22 DpContainer container;
352
1/1
✓ Branch 5 taken 22 times.
22 const Fw::Success::T dpStatus = this->dpGet_FileDpContainer(dpSize, container);
353
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 21 times.
22 if (dpStatus != Fw::Success::SUCCESS) {
354
1/1
✓ Branch 5 taken 1 times.
1 this->log_WARNING_HI_GenerateDpBufferFailed(logFileName);
355
1/1
✓ Branch 4 taken 1 times.
1 this->finishDpGeneration();
356 1 return;
357 }
358 21 container.setPriority(this->m_dpPriority);
359
360 // Each chunk is a metadata record followed by a data record, so that
361 // ground tools can reassemble the file from any number of containers
362
1/1
✓ Branch 9 taken 21 times.
21 const FileManager_FileChunkHeader header(this->m_dpFileName, this->m_dpOffset, static_cast<U32>(readSize));
363
1/1
✓ Branch 2 taken 21 times.
21 Fw::SerializeStatus serializeStatus = container.serializeRecord_FileChunkHeaderRecord(header);
364
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 1 times.
21 if (serializeStatus == Fw::FW_SERIALIZE_OK) {
365
1/1
✓ Branch 4 taken 20 times.
20 serializeStatus = container.serializeRecord_FileChunkDataRecord(this->m_dpBuffer, readSize);
366 }
367
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 20 times.
21 if (serializeStatus != Fw::FW_SERIALIZE_OK) {
368
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_GenerateDpFailed(logFileName, FileManager_GenerateDpStage::SERIALIZE,
369 static_cast<U32>(serializeStatus));
370
1/1
✓ Branch 4 taken 1 times.
1 this->finishDpGeneration();
371 1 return;
372 }
373
374
2/2
✓ Branch 7 taken 20 times.
✓ Branch 10 taken 20 times.
20 this->dpSend(container);
375
376 20 this->m_dpOffset += static_cast<U64>(readSize);
377 20 this->m_dpChunkCount++;
378
379 // Last chunk of the requested range
380
2/2
✓ Branch 8 taken 6 times.
✓ Branch 9 taken 14 times.
20 if (this->m_dpOffset >= this->m_dpEndOffset) {
381
1/1
✓ Branch 9 taken 6 times.
6 this->log_ACTIVITY_HI_GenerateDpComplete(logFileName, this->m_dpChunkCount);
382
1/1
✓ Branch 4 taken 6 times.
6 this->finishDpGeneration();
383 6 return;
384 }
385 29 }
386 19 }
387
388 9 void FileManager ::finishDpGeneration() {
389 9 this->m_dpFile.close();
390 9 this->m_dpState = DP_IDLE;
391 9 this->m_dpOffset = 0;
392 9 this->m_dpEndOffset = 0;
393 9 this->m_dpFileSize = 0;
394 // Failures emit a warning event but still respond with OK, so that a bad
395 // file name or a transient resource problem does not stop a whole sequence
396
2/2
✓ Branch 6 taken 9 times.
✓ Branch 17 taken 9 times.
9 this->cmdResponse_out(this->m_dpOpCode, this->m_dpCmdSeq, Fw::CmdResponse::OK);
397 9 }
398
399 void FileManager ::pingIn_handler(const FwIndexType portNum, U32 key) {
400 // return key
401 this->pingOut_out(0, key);
402 }
403
404 29 void FileManager ::schedIn_handler(const FwIndexType portNum, U32 context) {
405 29 bool isQueued = false;
406 // m_runQueued will be compared to isQueued (false). When equal (i.e. m_runQueued is false) the atomic will be
407 // set to true and the function will return true indicating that a run was successfully marked as queued and thus
408 // the internal handler should be invoked.
409 29 bool expects_enqueue = this->m_runQueued.compare_exchange_strong(isQueued, true);
410
1/2
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
29 if (expects_enqueue) {
411
1/1
✓ Branch 5 taken 29 times.
29 this->run_internalInterfaceInvoke();
412 }
413 29 }
414
415 29 void FileManager ::run_internalInterfaceHandler() {
416 29 FW_ASSERT(this->m_runQueued);
417 29 this->m_runQueued = false; // Run is not queued anymore (we are running)
418 // Data product generation is paced the same way as directory listing
419
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 29 times.
✓ Branch 5 taken 17 times.
✓ Branch 6 taken 12 times.
29 if (this->m_dpState == DP_IN_PROGRESS) {
420 17 this->processDpChunks(FileManagerConfig::CHUNKS_PER_RATE_TICK);
421 }
422
423 // Only process if we're in the middle of a directory listing
424
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 29 times.
✓ Branch 5 taken 12 times.
✓ Branch 6 taken 17 times.
29 if (m_listState == LISTING_IN_PROGRESS) {
425 // Process multiple files per rate tick based on configuration
426
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 10 times.
22 for (U32 fileCount = 0; fileCount < Svc::FileManagerConfig::FILES_PER_RATE_TICK; fileCount++) {
427
1/1
✓ Branch 2 taken 12 times.
12 Fw::String filename;
428
1/1
✓ Branch 6 taken 12 times.
12 Os::Directory::Status status = m_currentDir.read(filename);
429
430
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 10 times.
12 if (status == Os::Directory::NO_MORE_FILES) {
431 // We're done listing - close directory and send response
432
1/1
✓ Branch 6 taken 2 times.
2 m_currentDir.close();
433 2 m_listState = IDLE;
434
435
1/1
✓ Branch 12 taken 2 times.
2 this->log_ACTIVITY_HI_ListDirectorySucceeded(m_currentDirName, m_totalEntries);
436
1/1
✓ Branch 4 taken 2 times.
2 this->emitTelemetry(Os::FileSystem::OP_OK);
437
1/1
✓ Branch 12 taken 2 times.
2 this->sendCommandResponse(m_currentOpCode, m_currentCmdSeq, Os::FileSystem::OP_OK);
438 2 break; // Exit the loop since we're done
439
440
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 } else if (status == Os::Directory::OP_OK) {
441 // Construct full path for type checking
442
1/1
✓ Branch 2 taken 10 times.
10 Fw::String fullPath;
443
1/1
✓ Branch 10 taken 10 times.
10 Fw::FormatStatus formatStatus = fullPath.format("%s/%s", m_currentDirName.toChar(), filename.toChar());
444
445 // Determine entry type
446 Os::FileSystem::PathType pathType = (formatStatus == Fw::FormatStatus::SUCCESS)
447
2/3
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 5 taken 10 times.
10 ? Os::FileSystem::getPathType(fullPath.toChar())
448 10 : Os::FileSystem::NOT_EXIST;
449
450
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (formatStatus != Fw::FormatStatus::SUCCESS) {
451 // Cannot determine the type of an entry whose path did not format
452 this->log_WARNING_HI_FileNameFormatError(filename,
453 static_cast<Fw::StringFormatStatus::T>(formatStatus));
454
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
10 } else if (pathType == Os::FileSystem::FILE) {
455 // Regular file: get size and emit file event
456 6 FwSizeType fileSize;
457
1/1
✓ Branch 3 taken 6 times.
6 Os::FileSystem::Status sizeStatus = Os::FileSystem::getFileSize(fullPath.toChar(), fileSize);
458
2/3
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 10 taken 6 times.
6 this->log_ACTIVITY_HI_DirectoryListing(
459 m_currentDirName, filename,
460 (sizeStatus == Os::FileSystem::OP_OK) ? fileSize : static_cast<FwSizeType>(0));
461
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 } else if (pathType == Os::FileSystem::DIRECTORY) {
462 // Subdirectory: emit subdirectory event
463
1/1
✓ Branch 8 taken 4 times.
4 this->log_ACTIVITY_HI_DirectoryListingSubdir(m_currentDirName, filename);
464 } else {
465 // Special file or inaccessible: treat as file with 0 size
466 this->log_ACTIVITY_HI_DirectoryListing(m_currentDirName, filename, static_cast<FwSizeType>(0));
467 }
468
469 10 m_totalEntries++;
470
471 10 } else {
472 // Error reading directory - close and send error response
473 m_currentDir.close();
474 m_listState = IDLE;
475
476 this->log_WARNING_HI_ListDirectoryError(m_currentDirName, static_cast<U32>(status));
477 this->emitTelemetry(Os::FileSystem::OTHER_ERROR);
478 this->sendCommandResponse(m_currentOpCode, m_currentCmdSeq, Os::FileSystem::OTHER_ERROR);
479 break; // Exit the loop since we had an error
480 }
481 12 }
482 }
483 29 }
484
485 // ----------------------------------------------------------------------
486 // Helper methods
487 // ----------------------------------------------------------------------
488
489 16 void FileManager ::emitTelemetry(const Os::FileSystem::Status status) {
490
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 7 times.
16 if (status == Os::FileSystem::OP_OK) {
491 9 ++this->commandCount;
492
2/2
✓ Branch 6 taken 9 times.
✓ Branch 13 taken 9 times.
9 this->tlmWrite_CommandsExecuted(this->commandCount);
493 } else {
494 7 ++this->errorCount;
495
2/2
✓ Branch 6 taken 7 times.
✓ Branch 13 taken 7 times.
7 this->tlmWrite_Errors(this->errorCount);
496 }
497 16 }
498
499 16 void FileManager ::sendCommandResponse(const FwOpcodeType opCode,
500 const U32 cmdSeq,
501 const Os::FileSystem::Status status) {
502
4/4
✓ Branch 5 taken 9 times.
✓ Branch 6 taken 7 times.
✓ Branch 8 taken 16 times.
✓ Branch 11 taken 16 times.
16 this->cmdResponse_out(opCode, cmdSeq,
503 (status == Os::FileSystem::OP_OK) ? Fw::CmdResponse::OK : Fw::CmdResponse::EXECUTION_ERROR);
504 16 }
505
506 } // namespace Svc
507