GCC Code Coverage Report


Directory: Svc/Ccsds/CfdpManager/
File: CfdpManager.cpp
Date: 2026-09-03 21:16:27
Exec Total Coverage
Lines: 343 352 97.4%
Functions: 40 40 100.0%
Branches: 211 238 88.7%

Line Branch Exec Source
1 // ======================================================================
2 // \title CfdpManager.cpp
3 // \author Brian Campuzano
4 // \brief cpp file for CfdpManager component implementation class
5 // ======================================================================
6
7 #include <Fw/Com/ComPacket.hpp>
8 #include <Fw/Prm/ParamValid.hpp>
9 #include <Os/QueueString.hpp>
10 #include <Svc/Ccsds/CfdpManager/CfdpManager.hpp>
11 #include <Svc/Ccsds/CfdpManager/Channel.hpp>
12 #include <Svc/Ccsds/CfdpManager/Engine.hpp>
13 #include <new>
14
15 namespace Svc {
16 namespace Ccsds {
17 namespace Cfdp {
18
19 // ----------------------------------------------------------------------
20 // Component construction and destruction
21 // ----------------------------------------------------------------------
22
23
2/2
✓ Branch 14 taken 131 times.
✓ Branch 24 taken 131 times.
131 CfdpManager ::CfdpManager(const char* const compName) : CfdpManagerComponentBase(compName), m_engine(nullptr) {}
24
25 786 CfdpManager ::~CfdpManager() {
26 // Clean up the queue resources allocated during initialization
27 262 this->deinit();
28
29 // If cleanup() was not called, clean up manually
30
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 131 times.
262 if (this->m_engine != nullptr) {
31 this->cleanup();
32 }
33 524 }
34
35 131 void CfdpManager ::configure(Fw::MemAllocator& allocator, FwSizeType fileQueueDepth, FwEnumStoreType memId) {
36 // Allocate and initialize the CFDP engine
37 131 FwSizeType engineSize = sizeof(Engine);
38
1/1
✓ Branch 6 taken 131 times.
131 this->m_engine = static_cast<Engine*>(allocator.allocate(memId, engineSize));
39 131 FW_ASSERT(this->m_engine != nullptr);
40
1/3
✓ Branch 7 taken 131 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
131 (void)new (this->m_engine) Engine(this);
41
1/1
✓ Branch 8 taken 131 times.
131 this->m_engine->init(allocator, memId);
42
43 // Store allocator for cleanup
44 131 this->m_allocator = &allocator;
45 131 this->m_allocatorId = memId;
46
47 // Initialize telemetry counters to zero
48
2/2
✓ Branch 0 taken 262 times.
✓ Branch 1 taken 131 times.
393 for (U8 i = 0; i < Cfdp::NumChannels; i++) {
49
3/3
✓ Branch 2 taken 262 times.
✓ Branch 11 taken 262 times.
✓ Branch 17 taken 262 times.
262 this->m_channelTelemetry[i] = Cfdp::ChannelTelemetry();
50 }
51
52 // Create the fileIn request handoff queue
53 131 this->m_fileInQueueDepth = fileQueueDepth;
54 Os::Queue::Status queueStat =
55
3/3
✓ Branch 7 taken 131 times.
✓ Branch 15 taken 131 times.
✓ Branch 18 taken 131 times.
131 this->m_fileInQueue.create(this->getInstance(), Os::QueueString("cfdpFileInQueue"), fileQueueDepth,
56 static_cast<FwSizeType>(sizeof(FileInRequest)));
57 131 FW_ASSERT(queueStat == Os::Queue::OP_OK, static_cast<FwAssertArgType>(queueStat));
58 131 }
59
60 131 void CfdpManager ::deinit() {
61 131 this->m_fileInQueue.teardown();
62 131 CfdpManagerComponentBase::deinit();
63 131 }
64
65 131 void CfdpManager ::cleanup() {
66 // Only try to deallocate if both pointers are non-null
67
2/4
✓ Branch 4 taken 131 times.
✗ Branch 5 not taken.
✓ Branch 10 taken 131 times.
✗ Branch 11 not taken.
131 if ((this->m_allocator != nullptr) && (this->m_engine != nullptr)) {
68 // Manually call destructor since we used placement new
69 131 this->m_engine->~Engine();
70 // Deallocate the memory
71 131 this->m_allocator->deallocate(this->m_allocatorId, this->m_engine);
72 131 this->m_engine = nullptr;
73 }
74 131 }
75
76 // ----------------------------------------------------------------------
77 // Handler implementations for typed input ports
78 // ----------------------------------------------------------------------
79
80 1947 void CfdpManager ::run1Hz_handler(FwIndexType portNum, U32 context) {
81 // The timer logic built into the CFDP engine requires it to be driven at 1 Hz
82 1947 FW_ASSERT(this->m_engine != nullptr);
83
84 // Drain any port-initiated file send requests before cycling the engine
85 1947 this->drainFileInQueue();
86
87 1947 this->m_engine->cycle();
88
89 // Emit telemetry once per second
90
2/2
✓ Branch 6 taken 1947 times.
✓ Branch 12 taken 1947 times.
1947 this->tlmWrite_ChannelTelemetry(this->m_channelTelemetry);
91 1947 }
92
93 1951 void CfdpManager ::drainFileInQueue() {
94 1951 FW_ASSERT(this->m_engine != nullptr);
95
96 // Drain the whole queue; the depth bound guarantees the loop terminates.
97
1/2
✓ Branch 4 taken 1955 times.
✗ Branch 5 not taken.
1955 for (FwSizeType drained = 0; drained < this->m_fileInQueueDepth; drained++) {
98
1/1
✓ Branch 2 taken 1955 times.
1955 FileInRequest request;
99 1955 FwSizeType actualSize = 0;
100 1955 FwQueuePriorityType priority = 0;
101 Os::Queue::Status status =
102
1/1
✓ Branch 6 taken 1955 times.
1955 this->m_fileInQueue.receive(reinterpret_cast<U8*>(&request), static_cast<FwSizeType>(sizeof(request)),
103 Os::Queue::BlockingType::NONBLOCKING, actualSize, priority);
104
105 // Queue empty (or any non-OK status) ends the drain for this cycle
106
3/4
✓ Branch 0 taken 1951 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
1955 if (status != Os::Queue::Status::OP_OK || actualSize != sizeof(request)) {
107 break;
108 }
109
110 // Look up the per-channel default parameters
111
1/1
✓ Branch 2 taken 4 times.
4 Fw::ParamValid valid;
112
1/1
✓ Branch 5 taken 4 times.
4 U8 channelId = this->paramGet_FileInDefaultChannel(valid);
113 4 FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
114
115 // Reject an out-of-range channel parameter rather than letting it assert in Engine::txFile
116
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (channelId >= Cfdp::NumChannels) {
117 this->log_WARNING_LO_InvalidChannel(channelId, Cfdp::NumChannels);
118 this->sendFileComplete(Svc::SendFileStatus::STATUS_INVALID);
119 continue;
120 }
121
122
1/1
✓ Branch 5 taken 4 times.
4 EntityId destEid = this->paramGet_FileInDefaultDestEntityId(valid);
123 4 FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
124
125
1/1
✓ Branch 3 taken 4 times.
4 Class::T cfdpClass = this->paramGet_FileInDefaultClass(valid);
126 4 FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
127
128
1/1
✓ Branch 3 taken 4 times.
4 Keep::T keep = this->paramGet_FileInDefaultKeep(valid);
129 4 FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
130
131
1/1
✓ Branch 5 taken 4 times.
4 U8 priorityParam = this->paramGet_FileInDefaultPriority(valid);
132 4 FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
133
134 // Initiate the transfer on the active thread
135 Status::T txStatus =
136
1/1
✓ Branch 8 taken 4 times.
4 this->m_engine->txFile(request.sourceFileName, request.destFileName, cfdpClass, keep, channelId,
137 priorityParam, destEid, TransactionInitType::INIT_BY_PORT);
138
139 // The caller already received queue-acceptance success; report the deferred initiation
140 // result via fileDoneOut so a failed initiation does not leave the caller waiting forever.
141
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 if (txStatus != Status::SUCCESS) {
142
1/1
✓ Branch 5 taken 1 times.
1 this->log_WARNING_LO_SendFileInitiateFail(request.sourceFileName);
143
1/1
✓ Branch 4 taken 1 times.
1 this->sendFileComplete(Svc::SendFileStatus::STATUS_ERROR);
144 }
145 1955 }
146 1951 }
147
148 2 void CfdpManager ::dataReturnIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer) {
149 // dataReturnIn is the allocated buffer coming back from the dataOut call
150 // Port mapping is the same from bufferAllocate -> dataOut -> dataReturnIn -> bufferDeallocate
151 2 FW_ASSERT(portNum < Cfdp::NumChannels, portNum, Cfdp::NumChannels);
152 2 this->bufferDeallocate_out(portNum, fwBuffer);
153 2 }
154
155 149 void CfdpManager ::dataIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer) {
156 // There is a direct mapping between port number and channel index
157 149 FW_ASSERT(portNum < Cfdp::NumChannels, portNum, Cfdp::NumChannels);
158 149 FW_ASSERT(portNum >= 0, portNum);
159
160 // Strip FW_PACKET_FILE descriptor (first 2 bytes) from buffer
161 // FprimeRouter sends the entire Space Packet data field, which includes the packet type descriptor
162
3/3
✓ Branch 4 taken 149 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 148 times.
149 if (fwBuffer.getSize() < sizeof(FwPacketDescriptorType)) {
163 // Buffer too small - silently ignore
164
1/1
✓ Branch 5 taken 1 times.
1 this->dataInReturn_out(portNum, fwBuffer);
165 1 return;
166 }
167
168 // Read and verify packet type descriptor
169 148 FwPacketDescriptorType packetType = 0;
170
2/2
✓ Branch 2 taken 148 times.
✓ Branch 8 taken 148 times.
148 Fw::SerializeStatus status = fwBuffer.getDeserializer().deserializeTo(packetType);
171
3/4
✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 147 times.
148 if (status != Fw::FW_SERIALIZE_OK || packetType != Fw::ComPacketType::FW_PACKET_FILE) {
172 // Invalid packet type - silently ignore (consistent with FileUplink behavior)
173
1/1
✓ Branch 5 taken 1 times.
1 this->dataInReturn_out(portNum, fwBuffer);
174 1 return;
175 }
176
177 // Create a new buffer view that skips the descriptor
178 // The deserializer advanced past the 2-byte descriptor, but Engine::receivePdu
179 // calls getData() which returns the raw pointer from byte 0. We need to create
180 // a buffer that starts after the descriptor.
181 147 const FwSizeType descriptorSize = sizeof(FwPacketDescriptorType);
182
2/2
✓ Branch 4 taken 147 times.
✓ Branch 9 taken 147 times.
441 Fw::Buffer pduBuffer(fwBuffer.getData() + descriptorSize, fwBuffer.getSize() - descriptorSize,
183
2/2
✓ Branch 4 taken 147 times.
✓ Branch 10 taken 147 times.
441 fwBuffer.getContext());
184
185 // Pass the adjusted buffer to the engine
186 147 FW_ASSERT(this->m_engine != nullptr);
187
1/1
✓ Branch 8 taken 147 times.
147 this->m_engine->receivePdu(static_cast<U8>(portNum), pduBuffer);
188
189 // Return buffer
190
1/1
✓ Branch 5 taken 147 times.
147 this->dataInReturn_out(portNum, fwBuffer);
191 147 }
192
193 5 Svc::SendFileResponse CfdpManager ::fileIn_handler(FwIndexType portNum,
194 const Fw::StringBase& sourceFileName,
195 const Fw::StringBase& destFileName,
196 U32 offset,
197 U32 length) {
198
1/1
✓ Branch 1 taken 5 times.
5 Svc::SendFileResponse response;
199 // Set context to portNum so we can identify this transaction later
200
1/1
✓ Branch 3 taken 5 times.
5 response.set_context(static_cast<U32>(portNum));
201
202 // CFDP engine does not support partial file retransmit at this time
203 // Offset and length must be 0 to send the entire file
204
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
5 if (offset > 0 || length > 0) {
205
1/1
✓ Branch 3 taken 1 times.
1 response.set_status(Svc::SendFileStatus::STATUS_INVALID);
206
1/1
✓ Branch 5 taken 1 times.
1 this->log_WARNING_LO_UnsupportedSendFileArguments(offset, length);
207 1 return response;
208 }
209
210 // Copy the request into the internal queue instead of touching the engine here. This handler
211 // runs on the caller's thread (guarded port); the engine is mutated on the active thread when
212 // run1Hz drains the queue. The synchronous response only indicates that the request was
213 // accepted for processing; the final transfer result is delivered later via fileDoneOut.
214
1/1
✓ Branch 2 taken 4 times.
4 FileInRequest request;
215
216 // Guard against filenames that would not fit in the queued request
217
4/7
✓ Branch 11 taken 4 times.
✓ Branch 15 taken 4 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 18 taken 4 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 4 times.
8 if (sourceFileName.length() >= request.sourceFileName.getCapacity() ||
218
1/1
✓ Branch 11 taken 4 times.
4 destFileName.length() >= request.destFileName.getCapacity()) {
219 response.set_status(Svc::SendFileStatus::STATUS_INVALID);
220 this->log_WARNING_LO_SendFileInitiateFail(sourceFileName);
221 return response;
222 }
223
224
1/1
✓ Branch 5 taken 4 times.
4 request.sourceFileName = sourceFileName;
225
1/1
✓ Branch 5 taken 4 times.
4 request.destFileName = destFileName;
226 4 request.context = static_cast<U32>(portNum);
227
228 Os::Queue::Status status =
229
1/1
✓ Branch 6 taken 4 times.
4 this->m_fileInQueue.send(reinterpret_cast<U8*>(&request), static_cast<FwSizeType>(sizeof(request)), 0,
230 Os::Queue::BlockingType::NONBLOCKING);
231
232
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (status != Os::Queue::Status::OP_OK) {
233 // Queue full - reject the request so the caller can retry later
234 response.set_status(Svc::SendFileStatus::STATUS_BUSY);
235 this->log_WARNING_LO_SendFileInitiateFail(sourceFileName);
236 } else {
237
1/1
✓ Branch 3 taken 4 times.
4 response.set_status(Svc::SendFileStatus::STATUS_OK);
238 }
239
240 4 return response;
241 4 }
242
243 1 void CfdpManager ::pingIn_handler(FwIndexType portNum, U32 key) {
244 // send ping response
245 1 this->pingOut_out(0, key);
246 1 }
247
248 // ----------------------------------------------------------------------
249 // Port calls that are invoked by the CFDP engine
250 // These functions are analogous to the functions in cf_cfdp_sbintf.*
251 // However these functions were not directly migrated due to the
252 // architectural differences between F' and cFE
253 // ----------------------------------------------------------------------
254
255 98 Status::T CfdpManager ::getPduBuffer(Fw::Buffer& buffer, Channel& channel, FwSizeType size) {
256 98 Status::T status = Status::ERROR;
257 FwIndexType portNum;
258
259 // There is a direct mapping between channel index and port number
260 98 portNum = static_cast<FwIndexType>(channel.getChannelId());
261
262 // Check if we have reached the maximum number of output PDUs for this cycle
263 98 U32 max_pdus = getMaxOutgoingPdusPerCycleParam(channel.getChannelId());
264
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 97 times.
98 if (channel.getOutgoingCounter() >= max_pdus) {
265 1 status = Status::SEND_PDU_NO_BUF_AVAIL_ERROR;
266 } else {
267
2/2
✓ Branch 3 taken 97 times.
✓ Branch 10 taken 97 times.
97 buffer = this->bufferAllocate_out(portNum, size);
268 // Check the allocation was successful based on size
269
2/2
✓ Branch 4 taken 96 times.
✓ Branch 5 taken 1 times.
97 if (buffer.getSize() == size) {
270 96 channel.incrementOutgoingCounter();
271 96 status = Status::SUCCESS;
272 } else {
273 1 this->log_WARNING_LO_BuffersExhausted();
274 1 status = Status::SEND_PDU_NO_BUF_AVAIL_ERROR;
275 }
276 }
277 98 return status;
278 }
279
280 1 void CfdpManager ::returnPduBuffer(Channel& channel, Fw::Buffer& pduBuffer) {
281 FwIndexType portNum;
282
283 // There is a direct mapping between channel index and port number
284 1 portNum = static_cast<FwIndexType>(channel.getChannelId());
285
286 // Was unable to successfully populate the PDU buffer, return it
287 1 this->bufferDeallocate_out(portNum, pduBuffer);
288 1 }
289
290 95 void CfdpManager ::sendPduBuffer(Channel& channel, Fw::Buffer& pduBuffer) {
291 FwIndexType portNum;
292
293 // There is a direct mapping between channel index and port number
294 95 portNum = static_cast<FwIndexType>(channel.getChannelId());
295
296 // ComQueue expects buffers to start with a 2-byte packet descriptor (APID)
297 // The PDU data has already been serialized at offset PACKET_DESCRIPTOR_SIZE,
298 // so we just need to write the descriptor at the beginning
299
300 95 U8* bufferData = pduBuffer.getData();
301
302 // Write FW_PACKET_FILE descriptor at the beginning (big-endian U16)
303 95 const FwPacketDescriptorType descriptor = static_cast<FwPacketDescriptorType>(Fw::ComPacketType::FW_PACKET_FILE);
304 95 bufferData[0] = static_cast<U8>((descriptor >> 8) & 0xFF); // High byte
305 95 bufferData[1] = static_cast<U8>(descriptor & 0xFF); // Low byte
306
307 // Send buffer with descriptor
308 95 this->dataOut_out(portNum, pduBuffer);
309 95 }
310
311 4 void CfdpManager::sendFileComplete(Svc::SendFileStatus::T status) {
312
1/1
✓ Branch 2 taken 4 times.
4 Svc::SendFileResponse response;
313
1/1
✓ Branch 2 taken 4 times.
4 response.set_status(status);
314
1/1
✓ Branch 2 taken 4 times.
4 response.set_context(0);
315
316
1/1
✓ Branch 5 taken 4 times.
4 this->fileDoneOut_out(0, response);
317 8 }
318
319 // ----------------------------------------------------------------------
320 // Handler implementations for commands
321 // ----------------------------------------------------------------------
322
323 14 void CfdpManager ::SendFile_cmdHandler(FwOpcodeType opCode,
324 U32 cmdSeq,
325 U8 channelId,
326 EntityId destId,
327 const Class& cfdpClass,
328 const Keep& keep,
329 U8 priority,
330 const Fw::CmdStringArg& sourceFileName,
331 const Fw::CmdStringArg& destFileName) {
332 14 Fw::CmdResponse::T rspStatus = Fw::CmdResponse::OK;
333
334 // Check channel index is in range
335 14 rspStatus = this->checkCommandChannelIndex(channelId);
336 14 FW_ASSERT(this->m_engine != nullptr);
337
338
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 1 times.
14 if (rspStatus == Fw::CmdResponse::OK) {
339
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 12 times.
13 if (Status::SUCCESS !=
340
5/7
✗ Branch 10 not taken.
✓ Branch 11 taken 13 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 13 times.
✓ Branch 22 taken 13 times.
✓ Branch 30 taken 13 times.
✓ Branch 34 taken 13 times.
13 this->m_engine->txFile(sourceFileName, destFileName, cfdpClass.e, keep.e, channelId, priority, destId)) {
341 // Engine emits specific failure reason EVR (e.g., MaxTxTransactionsReached)
342 1 rspStatus = Fw::CmdResponse::EXECUTION_ERROR;
343 }
344 }
345
346
2/2
✓ Branch 6 taken 14 times.
✓ Branch 9 taken 14 times.
14 this->cmdResponse_out(opCode, cmdSeq, rspStatus);
347 14 }
348
349 4 void CfdpManager ::PlaybackDirectory_cmdHandler(FwOpcodeType opCode,
350 U32 cmdSeq,
351 U8 channelId,
352 EntityId destId,
353 const Class& cfdpClass,
354 const Keep& keep,
355 U8 priority,
356 const Fw::CmdStringArg& sourceDirectory,
357 const Fw::CmdStringArg& destDirectory) {
358 4 Fw::CmdResponse::T rspStatus = Fw::CmdResponse::OK;
359
360 4 FW_ASSERT(this->m_engine != nullptr);
361 // Check channel index is in range
362 4 rspStatus = this->checkCommandChannelIndex(channelId);
363
364
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 if (rspStatus == Fw::CmdResponse::OK) {
365
5/5
✓ Branch 12 taken 3 times.
✓ Branch 21 taken 3 times.
✓ Branch 25 taken 3 times.
✓ Branch 35 taken 1 times.
✓ Branch 36 taken 2 times.
6 if (Status::SUCCESS == this->m_engine->playbackDir(sourceDirectory.toChar(), destDirectory.toChar(),
366
2/4
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 3 times.
3 cfdpClass.e, keep.e, channelId, priority, destId)) {
367 1 this->log_ACTIVITY_LO_PlaybackInitiated(sourceDirectory);
368 } else {
369 // Engine emits specific failure reason EVR (e.g., PlaybackDirOpenFailed, PlaybackDirSlotUnavailable)
370 2 rspStatus = Fw::CmdResponse::EXECUTION_ERROR;
371 }
372 }
373
374
2/2
✓ Branch 6 taken 4 times.
✓ Branch 9 taken 4 times.
4 this->cmdResponse_out(opCode, cmdSeq, rspStatus);
375 4 }
376
377 8 void CfdpManager ::PollDirectory_cmdHandler(FwOpcodeType opCode,
378 U32 cmdSeq,
379 U8 channelId,
380 U8 pollId,
381 EntityId destId,
382 const Class& cfdpClass,
383 U8 priority,
384 U32 interval,
385 const Fw::CmdStringArg& sourceDirectory,
386 const Fw::CmdStringArg& destDirectory) {
387 8 Fw::CmdResponse::T rspStatus = Fw::CmdResponse::OK;
388
389 8 FW_ASSERT(this->m_engine != nullptr);
390 // Check channel index and poll index are in range
391 8 rspStatus = this->checkCommandChannelIndex(channelId);
392
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 times.
8 if (rspStatus == Fw::CmdResponse::OK) {
393 7 rspStatus = this->checkCommandChannelPollIndex(pollId);
394 }
395
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 if (rspStatus == Fw::CmdResponse::OK) {
396 6 rspStatus = this->checkCommandPollInterval(interval);
397 }
398
399
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 3 times.
8 if (rspStatus == Fw::CmdResponse::OK) {
400
5/5
✓ Branch 11 taken 5 times.
✓ Branch 19 taken 5 times.
✓ Branch 23 taken 5 times.
✓ Branch 33 taken 4 times.
✓ Branch 34 taken 1 times.
10 if (Status::SUCCESS == this->m_engine->startPollDir(channelId, pollId, sourceDirectory, destDirectory,
401
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
5 cfdpClass.e, priority, destId, interval)) {
402 4 this->log_ACTIVITY_LO_PollDirInitiated(sourceDirectory, pollId);
403 } else {
404 // Failure EVR was already emitted
405 1 rspStatus = Fw::CmdResponse::EXECUTION_ERROR;
406 }
407 }
408
409
2/2
✓ Branch 6 taken 8 times.
✓ Branch 9 taken 8 times.
8 this->cmdResponse_out(opCode, cmdSeq, rspStatus);
410 8 }
411
412 5 void CfdpManager ::StopPollDirectory_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, U8 channelId, U8 pollId) {
413 5 Fw::CmdResponse::T rspStatus = Fw::CmdResponse::OK;
414
415 5 FW_ASSERT(this->m_engine != nullptr);
416 // Check channel index and poll index are in range
417 5 rspStatus = this->checkCommandChannelIndex(channelId);
418
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (rspStatus == Fw::CmdResponse::OK) {
419 5 rspStatus = this->checkCommandChannelPollIndex(pollId);
420 }
421
422
5/6
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 10 taken 4 times.
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 4 times.
✓ Branch 13 taken 1 times.
5 if ((rspStatus == Fw::CmdResponse::OK) && (Status::SUCCESS == this->m_engine->stopPollDir(channelId, pollId))) {
423 4 this->log_ACTIVITY_LO_PollDirStopped(channelId, pollId);
424 }
425 // Failure EVR was already emitted
426 // Not failing the command if the stop request failed
427 // This allows operators to reinforce state prior to calling PollDirectory
428
429
2/2
✓ Branch 6 taken 5 times.
✓ Branch 9 taken 5 times.
5 this->cmdResponse_out(opCode, cmdSeq, rspStatus);
430 5 }
431
432 2 void CfdpManager ::SetChannelFlow_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, U8 channelId, const Flow& flowState) {
433 2 Fw::CmdResponse::T rspStatus = Fw::CmdResponse::OK;
434
435 2 FW_ASSERT(this->m_engine != nullptr);
436 // Check channel index is in range
437 2 rspStatus = checkCommandChannelIndex(channelId);
438
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (rspStatus == Fw::CmdResponse::OK) {
439 1 this->m_engine->setChannelFlowState(channelId, flowState);
440 1 this->log_ACTIVITY_LO_SetFlowState(channelId, flowState);
441 }
442
443
2/2
✓ Branch 6 taken 2 times.
✓ Branch 9 taken 2 times.
2 this->cmdResponse_out(opCode, cmdSeq, rspStatus);
444 2 }
445
446 4 void CfdpManager ::SuspendResumeTransaction_cmdHandler(FwOpcodeType opCode,
447 U32 cmdSeq,
448 U8 channelId,
449 TransactionSeq transactionSeq,
450 EntityId entityId,
451 const SuspendResume& action) {
452 4 Fw::CmdResponse::T rspStatus = Fw::CmdResponse::OK;
453
454 4 FW_ASSERT(this->m_engine != nullptr);
455
456 4 rspStatus = checkCommandChannelIndex(channelId);
457
458
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 if (rspStatus == Fw::CmdResponse::OK) {
459 3 Status::T status = this->m_engine->setSuspendResumeTransaction(channelId, transactionSeq, entityId, action);
460
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if (status == Status::SUCCESS) {
461
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
2 if (action == SuspendResume::SUSPEND) {
462 1 log_ACTIVITY_LO_TransactionSuspended(transactionSeq, entityId);
463 } else {
464 1 log_ACTIVITY_LO_TransactionResumed(transactionSeq, entityId);
465 }
466 } else {
467 1 log_WARNING_LO_TransactionNotFound(transactionSeq, entityId);
468 1 rspStatus = Fw::CmdResponse::EXECUTION_ERROR;
469 }
470 }
471
472
2/2
✓ Branch 6 taken 4 times.
✓ Branch 9 taken 4 times.
4 this->cmdResponse_out(opCode, cmdSeq, rspStatus);
473 4 }
474
475 3 void CfdpManager ::CancelTransaction_cmdHandler(FwOpcodeType opCode,
476 U32 cmdSeq,
477 U8 channelId,
478 TransactionSeq transactionSeq,
479 EntityId entityId) {
480 3 Fw::CmdResponse::T rspStatus = Fw::CmdResponse::OK;
481
482 3 FW_ASSERT(this->m_engine != nullptr);
483
484 3 rspStatus = checkCommandChannelIndex(channelId);
485
486
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if (rspStatus == Fw::CmdResponse::OK) {
487 2 Status::T status = this->m_engine->cancelTransactionBySeq(channelId, transactionSeq, entityId);
488
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (status == Status::SUCCESS) {
489 1 log_ACTIVITY_HI_TransactionCanceled(transactionSeq, entityId);
490 } else {
491 1 log_WARNING_LO_TransactionNotFound(transactionSeq, entityId);
492 1 rspStatus = Fw::CmdResponse::EXECUTION_ERROR;
493 }
494 }
495
496
2/2
✓ Branch 6 taken 3 times.
✓ Branch 9 taken 3 times.
3 this->cmdResponse_out(opCode, cmdSeq, rspStatus);
497 3 }
498
499 3 void CfdpManager ::AbandonTransaction_cmdHandler(FwOpcodeType opCode,
500 U32 cmdSeq,
501 U8 channelId,
502 TransactionSeq transactionSeq,
503 EntityId entityId) {
504 3 Fw::CmdResponse::T rspStatus = Fw::CmdResponse::OK;
505
506 3 FW_ASSERT(this->m_engine != nullptr);
507
508 3 rspStatus = checkCommandChannelIndex(channelId);
509
510
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if (rspStatus == Fw::CmdResponse::OK) {
511 2 Status::T status = this->m_engine->abandonTransaction(channelId, transactionSeq, entityId);
512
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (status == Status::SUCCESS) {
513 1 log_ACTIVITY_HI_TransactionAbandoned(transactionSeq, entityId);
514 } else {
515 1 log_WARNING_LO_TransactionNotFound(transactionSeq, entityId);
516 1 rspStatus = Fw::CmdResponse::EXECUTION_ERROR;
517 }
518 }
519
520
2/2
✓ Branch 6 taken 3 times.
✓ Branch 9 taken 3 times.
3 this->cmdResponse_out(opCode, cmdSeq, rspStatus);
521 3 }
522
523 3 void CfdpManager ::ResetCounters_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, U8 channelId) {
524 // 0xFF means reset all channels
525
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (channelId == 0xFF) {
526
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 for (U8 i = 0; i < Cfdp::NumChannels; i++) {
527
3/3
✓ Branch 2 taken 2 times.
✓ Branch 11 taken 2 times.
✓ Branch 17 taken 2 times.
2 this->m_channelTelemetry[i] = Cfdp::ChannelTelemetry();
528 }
529 1 this->log_ACTIVITY_HI_ResetCounters(0xFF);
530 }
531 // Otherwise reset specific channel
532
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 else if (channelId < Cfdp::NumChannels) {
533
3/3
✓ Branch 2 taken 1 times.
✓ Branch 11 taken 1 times.
✓ Branch 17 taken 1 times.
1 this->m_channelTelemetry[channelId] = Cfdp::ChannelTelemetry();
534 1 this->log_ACTIVITY_HI_ResetCounters(channelId);
535 } else {
536 // Invalid channel ID
537 1 this->log_WARNING_LO_InvalidChannel(channelId, Cfdp::NumChannels - 1);
538
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR);
539 1 return;
540 }
541
542 // Emit updated telemetry
543
2/2
✓ Branch 6 taken 2 times.
✓ Branch 12 taken 2 times.
2 this->tlmWrite_ChannelTelemetry(this->m_channelTelemetry);
544
545
2/2
✓ Branch 6 taken 2 times.
✓ Branch 9 taken 2 times.
2 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
546 }
547
548 // ----------------------------------------------------------------------
549 // Private command helper functions
550 // ----------------------------------------------------------------------
551
552 43 Fw::CmdResponse::T CfdpManager ::checkCommandChannelIndex(U8 channelIndex) {
553
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 36 times.
43 if (channelIndex >= Cfdp::NumChannels) {
554 7 this->log_WARNING_LO_InvalidChannel(channelIndex, Cfdp::NumChannels);
555 7 return Fw::CmdResponse::VALIDATION_ERROR;
556 } else {
557 36 return Fw::CmdResponse::OK;
558 }
559 }
560
561 12 Fw::CmdResponse::T CfdpManager ::checkCommandChannelPollIndex(U8 pollIndex) {
562
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 11 times.
12 if (pollIndex >= MaxPollingDirPerChan) {
563 1 this->log_WARNING_LO_InvalidChannelPoll(pollIndex, MaxPollingDirPerChan);
564 1 return Fw::CmdResponse::VALIDATION_ERROR;
565 } else {
566 11 return Fw::CmdResponse::OK;
567 }
568 }
569
570 6 Fw::CmdResponse::T CfdpManager ::checkCommandPollInterval(U32 interval) {
571 // A zero interval would arm the poll timer with no time remaining, which is
572 // not a valid polling configuration, so reject it here.
573
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
6 if (interval == 0) {
574 1 this->log_WARNING_LO_InvalidPollInterval(interval);
575 1 return Fw::CmdResponse::VALIDATION_ERROR;
576 } else {
577 5 return Fw::CmdResponse::OK;
578 }
579 }
580
581 // ----------------------------------------------------------------------
582 // Parameter helpers used by the CFDP engine
583 // ----------------------------------------------------------------------
584
585 388 EntityId CfdpManager::getLocalEidParam(void) {
586
1/1
✓ Branch 2 taken 388 times.
388 Fw::ParamValid valid;
587
588 // Check for coding errors as all CFDP parameters must have a default
589
1/1
✓ Branch 5 taken 388 times.
388 EntityId localEid = this->paramGet_LocalEid(valid);
590 388 FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
591
592 388 return localEid;
593 388 }
594
595 57 U32 CfdpManager::getOutgoingFileChunkSizeParam(void) {
596
1/1
✓ Branch 2 taken 57 times.
57 Fw::ParamValid valid;
597
598 // Check for coding errors as all CFDP parameters must have a default
599
1/1
✓ Branch 5 taken 57 times.
57 U32 chunkSize = this->paramGet_OutgoingFileChunkSize(valid);
600 57 FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
601
602 57 return chunkSize;
603 57 }
604 8 U32 CfdpManager::getRxCrcCalcBytesPerCycleParam(void) {
605
1/1
✓ Branch 2 taken 8 times.
8 Fw::ParamValid valid;
606
607 // Check for coding errors as all CFDP parameters must have a default
608
1/1
✓ Branch 5 taken 8 times.
8 U32 rxSize = this->paramGet_RxCrcCalcBytesPerCycle(valid);
609 8 FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
610
611 8 return rxSize;
612 8 }
613
614 1 U8 CfdpManager::getPostInactivitySendRetriesParam(void) {
615
1/1
✓ Branch 2 taken 1 times.
1 Fw::ParamValid valid;
616
617 // Check for coding errors as all CFDP parameters must have a default
618
1/1
✓ Branch 5 taken 1 times.
1 U8 retries = this->paramGet_PostInactivitySendRetries(valid);
619 1 FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
620
621 1 return retries;
622 1 }
623
624 3 Fw::String CfdpManager::getTmpDirParam(U8 channelIndex) {
625
1/1
✓ Branch 2 taken 3 times.
3 Fw::ParamValid valid;
626
627 3 FW_ASSERT(channelIndex < Cfdp::NumChannels, channelIndex, Cfdp::NumChannels);
628
629 // Check for coding errors as all CFDP parameters must have a default
630 // Get the array first
631
1/1
✓ Branch 3 taken 3 times.
3 ChannelArrayParams paramArray = paramGet_ChannelConfig(valid);
632 3 FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
633
634 // Now get individual parameter
635
2/2
✓ Branch 2 taken 3 times.
✓ Branch 11 taken 3 times.
6 return paramArray[channelIndex].get_tmp_dir();
636 3 }
637
638 2 Fw::String CfdpManager::getFailDirParam(U8 channelIndex) {
639
1/1
✓ Branch 2 taken 2 times.
2 Fw::ParamValid valid;
640
641 2 FW_ASSERT(channelIndex < Cfdp::NumChannels, channelIndex, Cfdp::NumChannels);
642
643 // Check for coding errors as all CFDP parameters must have a default
644 // Get the array first
645
1/1
✓ Branch 3 taken 2 times.
2 ChannelArrayParams paramArray = paramGet_ChannelConfig(valid);
646 2 FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
647
648 // Now get individual parameter
649
2/2
✓ Branch 2 taken 2 times.
✓ Branch 11 taken 2 times.
4 return paramArray[channelIndex].get_fail_dir();
650 2 }
651
652 12 U8 CfdpManager::getAckLimitParam(U8 channelIndex) {
653
1/1
✓ Branch 2 taken 12 times.
12 Fw::ParamValid valid;
654
655 12 FW_ASSERT(channelIndex < Cfdp::NumChannels, channelIndex, Cfdp::NumChannels);
656
657 // Check for coding errors as all CFDP parameters must have a default
658 // Get the array first
659
1/1
✓ Branch 3 taken 12 times.
12 ChannelArrayParams paramArray = paramGet_ChannelConfig(valid);
660 12 FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
661
662 // Now get individual parameter
663
1/1
✓ Branch 2 taken 12 times.
24 return paramArray[channelIndex].get_ack_limit();
664 12 }
665
666 3 U8 CfdpManager::getNackLimitParam(U8 channelIndex) {
667
1/1
✓ Branch 2 taken 3 times.
3 Fw::ParamValid valid;
668
669 3 FW_ASSERT(channelIndex < Cfdp::NumChannels, channelIndex, Cfdp::NumChannels);
670
671 // Check for coding errors as all CFDP parameters must have a default
672 // Get the array first
673
1/1
✓ Branch 3 taken 3 times.
3 ChannelArrayParams paramArray = paramGet_ChannelConfig(valid);
674 3 FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
675
676 // Now get individual parameter
677
1/1
✓ Branch 2 taken 3 times.
6 return paramArray[channelIndex].get_nack_limit();
678 3 }
679
680 118 U32 CfdpManager::getAckTimerParam(U8 channelIndex) {
681
1/1
✓ Branch 2 taken 118 times.
118 Fw::ParamValid valid;
682
683 118 FW_ASSERT(channelIndex < Cfdp::NumChannels, channelIndex, Cfdp::NumChannels);
684
685 // Check for coding errors as all CFDP parameters must have a default
686 // Get the array first
687
1/1
✓ Branch 3 taken 118 times.
118 ChannelArrayParams paramArray = paramGet_ChannelConfig(valid);
688 118 FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
689
690 // Now get individual parameter
691
1/1
✓ Branch 2 taken 118 times.
236 return paramArray[channelIndex].get_ack_timer();
692 118 }
693
694 166 U32 CfdpManager::getInactivityTimerParam(U8 channelIndex) {
695
1/1
✓ Branch 2 taken 166 times.
166 Fw::ParamValid valid;
696
697 166 FW_ASSERT(channelIndex < Cfdp::NumChannels, channelIndex, Cfdp::NumChannels);
698
699 // Check for coding errors as all CFDP parameters must have a default
700 // Get the array first
701
1/1
✓ Branch 3 taken 166 times.
166 ChannelArrayParams paramArray = paramGet_ChannelConfig(valid);
702 166 FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
703
704 // Now get individual parameter
705
1/1
✓ Branch 2 taken 166 times.
332 return paramArray[channelIndex].get_inactivity_timer();
706 166 }
707
708 3894 Fw::Enabled CfdpManager::getDequeueEnabledParam(U8 channelIndex) {
709
1/1
✓ Branch 2 taken 3894 times.
3894 Fw::ParamValid valid;
710
711 3894 FW_ASSERT(channelIndex < Cfdp::NumChannels, channelIndex, Cfdp::NumChannels);
712
713 // Check for coding errors as all CFDP parameters must have a default
714 // Get the array first
715
1/1
✓ Branch 3 taken 3894 times.
3894 ChannelArrayParams paramArray = paramGet_ChannelConfig(valid);
716 3894 FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
717
718 // Now get individual parameter
719
2/2
✓ Branch 2 taken 3894 times.
✓ Branch 9 taken 3894 times.
7788 return paramArray[channelIndex].get_dequeue_enabled();
720 3894 }
721
722 7 Fw::String CfdpManager::getMoveDirParam(U8 channelIndex) {
723
1/1
✓ Branch 2 taken 7 times.
7 Fw::ParamValid valid;
724
725 7 FW_ASSERT(channelIndex < Cfdp::NumChannels, channelIndex, Cfdp::NumChannels);
726
727 // Check for coding errors as all CFDP parameters must have a default
728 // Get the array first
729
1/1
✓ Branch 3 taken 7 times.
7 ChannelArrayParams paramArray = paramGet_ChannelConfig(valid);
730 7 FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
731
732 // Now get individual parameter
733
2/2
✓ Branch 2 taken 7 times.
✓ Branch 11 taken 7 times.
14 return paramArray[channelIndex].get_move_dir();
734 7 }
735
736 99 U32 CfdpManager ::getMaxOutgoingPdusPerCycleParam(U8 channelIndex) {
737
1/1
✓ Branch 2 taken 99 times.
99 Fw::ParamValid valid;
738
739 99 FW_ASSERT(channelIndex < Cfdp::NumChannels, channelIndex, Cfdp::NumChannels);
740
741 // Check for coding errors as all CFDP parameters must have a default
742 // Get the array first
743
1/1
✓ Branch 3 taken 99 times.
99 ChannelArrayParams paramArray = paramGet_ChannelConfig(valid);
744 99 FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
745
746 // Now get individual parameter
747
1/1
✓ Branch 2 taken 99 times.
198 return paramArray[channelIndex].get_max_outgoing_pdus_per_cycle();
748 99 }
749
750 } // namespace Cfdp
751 } // namespace Ccsds
752 } // namespace Svc
753