GCC Code Coverage Report


Directory: ./
File: Svc/Ccsds/CfdpManager/CfdpManager.cpp
Date: 2026-09-23 22:11:34
Exec Total Coverage
Lines: 0 352 0.0%
Functions: 0 40 0.0%
Branches: 0 228 0.0%

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 ✗ CfdpManager ::CfdpManager(const char* const compName) : CfdpManagerComponentBase(compName), m_engine(nullptr) {}
24
25 ✗ CfdpManager ::~CfdpManager() {
26 // Clean up the queue resources allocated during initialization
27 ✗ this->deinit();
28
29 // If cleanup() was not called, clean up manually
30 ✗ if (this->m_engine != nullptr) {
31 ✗ this->cleanup();
32 }
33 ✗ }
34
35 ✗ void CfdpManager ::configure(Fw::MemAllocator& allocator, FwSizeType fileQueueDepth, FwEnumStoreType memId) {
36 // Allocate and initialize the CFDP engine
37 ✗ FwSizeType engineSize = sizeof(Engine);
38 ✗ this->m_engine = static_cast<Engine*>(allocator.allocate(memId, engineSize));
39 ✗ FW_ASSERT(this->m_engine != nullptr);
40 ✗ (void)new (this->m_engine) Engine(this);
41 ✗ this->m_engine->init(allocator, memId);
42
43 // Store allocator for cleanup
44 ✗ this->m_allocator = &allocator;
45 ✗ this->m_allocatorId = memId;
46
47 // Initialize telemetry counters to zero
48 ✗ for (U8 i = 0; i < Cfdp::NumChannels; i++) {
49 ✗ this->m_channelTelemetry[i] = Cfdp::ChannelTelemetry();
50 }
51
52 // Create the fileIn request handoff queue
53 ✗ this->m_fileInQueueDepth = fileQueueDepth;
54 Os::Queue::Status queueStat =
55 ✗ this->m_fileInQueue.create(this->getInstance(), Os::QueueString("cfdpFileInQueue"), fileQueueDepth,
56 static_cast<FwSizeType>(sizeof(FileInRequest)));
57 ✗ FW_ASSERT(queueStat == Os::Queue::OP_OK, static_cast<FwAssertArgType>(queueStat));
58 ✗ }
59
60 ✗ void CfdpManager ::deinit() {
61 ✗ this->m_fileInQueue.teardown();
62 ✗ CfdpManagerComponentBase::deinit();
63 ✗ }
64
65 ✗ void CfdpManager ::cleanup() {
66 // Only try to deallocate if both pointers are non-null
67 ✗ if ((this->m_allocator != nullptr) && (this->m_engine != nullptr)) {
68 // Manually call destructor since we used placement new
69 ✗ this->m_engine->~Engine();
70 // Deallocate the memory
71 ✗ this->m_allocator->deallocate(this->m_allocatorId, this->m_engine);
72 ✗ this->m_engine = nullptr;
73 }
74 ✗ }
75
76 // ----------------------------------------------------------------------
77 // Handler implementations for typed input ports
78 // ----------------------------------------------------------------------
79
80 ✗ 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 ✗ FW_ASSERT(this->m_engine != nullptr);
83
84 // Drain any port-initiated file send requests before cycling the engine
85 ✗ this->drainFileInQueue();
86
87 ✗ this->m_engine->cycle();
88
89 // Emit telemetry once per second
90 ✗ this->tlmWrite_ChannelTelemetry(this->m_channelTelemetry);
91 ✗ }
92
93 ✗ void CfdpManager ::drainFileInQueue() {
94 ✗ FW_ASSERT(this->m_engine != nullptr);
95
96 // Drain the whole queue; the depth bound guarantees the loop terminates.
97 ✗ for (FwSizeType drained = 0; drained < this->m_fileInQueueDepth; drained++) {
98 ✗ FileInRequest request;
99 ✗ FwSizeType actualSize = 0;
100 ✗ FwQueuePriorityType priority = 0;
101 Os::Queue::Status status =
102 ✗ 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 ✗ if (status != Os::Queue::Status::OP_OK || actualSize != sizeof(request)) {
107 break;
108 }
109
110 // Look up the per-channel default parameters
111 ✗ Fw::ParamValid valid;
112 ✗ U8 channelId = this->paramGet_FileInDefaultChannel(valid);
113 ✗ 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 ✗ 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 ✗ EntityId destEid = this->paramGet_FileInDefaultDestEntityId(valid);
123 ✗ FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
124
125 ✗ Class::T cfdpClass = this->paramGet_FileInDefaultClass(valid);
126 ✗ FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
127
128 ✗ Keep::T keep = this->paramGet_FileInDefaultKeep(valid);
129 ✗ FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
130
131 ✗ U8 priorityParam = this->paramGet_FileInDefaultPriority(valid);
132 ✗ 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 ✗ 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 ✗ if (txStatus != Status::SUCCESS) {
142 ✗ this->log_WARNING_LO_SendFileInitiateFail(request.sourceFileName);
143 ✗ this->sendFileComplete(Svc::SendFileStatus::STATUS_ERROR);
144 }
145 ✗ }
146 ✗ }
147
148 ✗ 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 ✗ FW_ASSERT(portNum < Cfdp::NumChannels, portNum, Cfdp::NumChannels);
152 ✗ this->bufferDeallocate_out(portNum, fwBuffer);
153 ✗ }
154
155 ✗ void CfdpManager ::dataIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer) {
156 // There is a direct mapping between port number and channel index
157 ✗ FW_ASSERT(portNum < Cfdp::NumChannels, portNum, Cfdp::NumChannels);
158 ✗ 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 ✗ if (fwBuffer.getSize() < sizeof(FwPacketDescriptorType)) {
163 // Buffer too small - silently ignore
164 ✗ this->dataInReturn_out(portNum, fwBuffer);
165 ✗ return;
166 }
167
168 // Read and verify packet type descriptor
169 ✗ FwPacketDescriptorType packetType = 0;
170 ✗ Fw::SerializeStatus status = fwBuffer.getDeserializer().deserializeTo(packetType);
171 ✗ if (status != Fw::FW_SERIALIZE_OK || packetType != Fw::ComPacketType::FW_PACKET_FILE) {
172 // Invalid packet type - silently ignore (consistent with FileUplink behavior)
173 ✗ this->dataInReturn_out(portNum, fwBuffer);
174 ✗ 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 ✗ const FwSizeType descriptorSize = sizeof(FwPacketDescriptorType);
182 ✗ Fw::Buffer pduBuffer(fwBuffer.getData() + descriptorSize, fwBuffer.getSize() - descriptorSize,
183 ✗ fwBuffer.getContext());
184
185 // Pass the adjusted buffer to the engine
186 ✗ FW_ASSERT(this->m_engine != nullptr);
187 ✗ this->m_engine->receivePdu(static_cast<U8>(portNum), pduBuffer);
188
189 // Return buffer
190 ✗ this->dataInReturn_out(portNum, fwBuffer);
191 ✗ }
192
193 ✗ Svc::SendFileResponse CfdpManager ::fileIn_handler(FwIndexType portNum,
194 const Fw::StringBase& sourceFileName,
195 const Fw::StringBase& destFileName,
196 U32 offset,
197 U32 length) {
198 ✗ Svc::SendFileResponse response;
199 // Set context to portNum so we can identify this transaction later
200 ✗ 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 ✗ if (offset > 0 || length > 0) {
205 ✗ response.set_status(Svc::SendFileStatus::STATUS_INVALID);
206 ✗ this->log_WARNING_LO_UnsupportedSendFileArguments(offset, length);
207 ✗ 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 ✗ FileInRequest request;
215
216 // Guard against filenames that would not fit in the queued request
217 ✗ if (sourceFileName.length() >= request.sourceFileName.getCapacity() ||
218 ✗ 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 ✗ request.sourceFileName = sourceFileName;
225 ✗ request.destFileName = destFileName;
226 ✗ request.context = static_cast<U32>(portNum);
227
228 Os::Queue::Status status =
229 ✗ this->m_fileInQueue.send(reinterpret_cast<U8*>(&request), static_cast<FwSizeType>(sizeof(request)), 0,
230 Os::Queue::BlockingType::NONBLOCKING);
231
232 ✗ 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 ✗ response.set_status(Svc::SendFileStatus::STATUS_OK);
238 }
239
240 ✗ return response;
241 ✗ }
242
243 ✗ void CfdpManager ::pingIn_handler(FwIndexType portNum, U32 key) {
244 // send ping response
245 ✗ this->pingOut_out(0, key);
246 ✗ }
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 ✗ Status::T CfdpManager ::getPduBuffer(Fw::Buffer& buffer, Channel& channel, FwSizeType size) {
256 ✗ Status::T status = Status::ERROR;
257 FwIndexType portNum;
258
259 // There is a direct mapping between channel index and port number
260 ✗ portNum = static_cast<FwIndexType>(channel.getChannelId());
261
262 // Check if we have reached the maximum number of output PDUs for this cycle
263 ✗ U32 max_pdus = getMaxOutgoingPdusPerCycleParam(channel.getChannelId());
264 ✗ if (channel.getOutgoingCounter() >= max_pdus) {
265 ✗ status = Status::SEND_PDU_NO_BUF_AVAIL_ERROR;
266 } else {
267 ✗ buffer = this->bufferAllocate_out(portNum, size);
268 // Check the allocation was successful based on size
269 ✗ if (buffer.getSize() == size) {
270 ✗ channel.incrementOutgoingCounter();
271 ✗ status = Status::SUCCESS;
272 } else {
273 ✗ this->log_WARNING_LO_BuffersExhausted();
274 ✗ status = Status::SEND_PDU_NO_BUF_AVAIL_ERROR;
275 }
276 }
277 ✗ return status;
278 }
279
280 ✗ 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 ✗ portNum = static_cast<FwIndexType>(channel.getChannelId());
285
286 // Was unable to successfully populate the PDU buffer, return it
287 ✗ this->bufferDeallocate_out(portNum, pduBuffer);
288 ✗ }
289
290 ✗ 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 ✗ 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 ✗ U8* bufferData = pduBuffer.getData();
301
302 // Write FW_PACKET_FILE descriptor at the beginning (big-endian U16)
303 ✗ const FwPacketDescriptorType descriptor = static_cast<FwPacketDescriptorType>(Fw::ComPacketType::FW_PACKET_FILE);
304 ✗ bufferData[0] = static_cast<U8>((descriptor >> 8) & 0xFF); // High byte
305 ✗ bufferData[1] = static_cast<U8>(descriptor & 0xFF); // Low byte
306
307 // Send buffer with descriptor
308 ✗ this->dataOut_out(portNum, pduBuffer);
309 ✗ }
310
311 ✗ void CfdpManager::sendFileComplete(Svc::SendFileStatus::T status) {
312 ✗ Svc::SendFileResponse response;
313 ✗ response.set_status(status);
314 ✗ response.set_context(0);
315
316 ✗ this->fileDoneOut_out(0, response);
317 ✗ }
318
319 // ----------------------------------------------------------------------
320 // Handler implementations for commands
321 // ----------------------------------------------------------------------
322
323 ✗ 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 ✗ Fw::CmdResponse::T rspStatus = Fw::CmdResponse::OK;
333
334 // Check channel index is in range
335 ✗ rspStatus = this->checkCommandChannelIndex(channelId);
336 ✗ FW_ASSERT(this->m_engine != nullptr);
337
338 ✗ if (rspStatus == Fw::CmdResponse::OK) {
339 ✗ if (Status::SUCCESS !=
340 ✗ this->m_engine->txFile(sourceFileName, destFileName, cfdpClass.e, keep.e, channelId, priority, destId)) {
341 // Engine emits specific failure reason EVR (e.g., MaxTxTransactionsReached)
342 ✗ rspStatus = Fw::CmdResponse::EXECUTION_ERROR;
343 }
344 }
345
346 ✗ this->cmdResponse_out(opCode, cmdSeq, rspStatus);
347 ✗ }
348
349 ✗ 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 ✗ Fw::CmdResponse::T rspStatus = Fw::CmdResponse::OK;
359
360 ✗ FW_ASSERT(this->m_engine != nullptr);
361 // Check channel index is in range
362 ✗ rspStatus = this->checkCommandChannelIndex(channelId);
363
364 ✗ if (rspStatus == Fw::CmdResponse::OK) {
365 ✗ if (Status::SUCCESS == this->m_engine->playbackDir(sourceDirectory.toChar(), destDirectory.toChar(),
366 ✗ cfdpClass.e, keep.e, channelId, priority, destId)) {
367 ✗ this->log_ACTIVITY_LO_PlaybackInitiated(sourceDirectory);
368 } else {
369 // Engine emits specific failure reason EVR (e.g., PlaybackDirOpenFailed, PlaybackDirSlotUnavailable)
370 ✗ rspStatus = Fw::CmdResponse::EXECUTION_ERROR;
371 }
372 }
373
374 ✗ this->cmdResponse_out(opCode, cmdSeq, rspStatus);
375 ✗ }
376
377 ✗ 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 ✗ Fw::CmdResponse::T rspStatus = Fw::CmdResponse::OK;
388
389 ✗ FW_ASSERT(this->m_engine != nullptr);
390 // Check channel index and poll index are in range
391 ✗ rspStatus = this->checkCommandChannelIndex(channelId);
392 ✗ if (rspStatus == Fw::CmdResponse::OK) {
393 ✗ rspStatus = this->checkCommandChannelPollIndex(pollId);
394 }
395 ✗ if (rspStatus == Fw::CmdResponse::OK) {
396 ✗ rspStatus = this->checkCommandPollInterval(interval);
397 }
398
399 ✗ if (rspStatus == Fw::CmdResponse::OK) {
400 ✗ if (Status::SUCCESS == this->m_engine->startPollDir(channelId, pollId, sourceDirectory, destDirectory,
401 ✗ cfdpClass.e, priority, destId, interval)) {
402 ✗ this->log_ACTIVITY_LO_PollDirInitiated(sourceDirectory, pollId);
403 } else {
404 // Failure EVR was already emitted
405 ✗ rspStatus = Fw::CmdResponse::EXECUTION_ERROR;
406 }
407 }
408
409 ✗ this->cmdResponse_out(opCode, cmdSeq, rspStatus);
410 ✗ }
411
412 ✗ void CfdpManager ::StopPollDirectory_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, U8 channelId, U8 pollId) {
413 ✗ Fw::CmdResponse::T rspStatus = Fw::CmdResponse::OK;
414
415 ✗ FW_ASSERT(this->m_engine != nullptr);
416 // Check channel index and poll index are in range
417 ✗ rspStatus = this->checkCommandChannelIndex(channelId);
418 ✗ if (rspStatus == Fw::CmdResponse::OK) {
419 ✗ rspStatus = this->checkCommandChannelPollIndex(pollId);
420 }
421
422 ✗ if ((rspStatus == Fw::CmdResponse::OK) && (Status::SUCCESS == this->m_engine->stopPollDir(channelId, pollId))) {
423 ✗ 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 ✗ this->cmdResponse_out(opCode, cmdSeq, rspStatus);
430 ✗ }
431
432 ✗ void CfdpManager ::SetChannelFlow_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, U8 channelId, const Flow& flowState) {
433 ✗ Fw::CmdResponse::T rspStatus = Fw::CmdResponse::OK;
434
435 ✗ FW_ASSERT(this->m_engine != nullptr);
436 // Check channel index is in range
437 ✗ rspStatus = checkCommandChannelIndex(channelId);
438 ✗ if (rspStatus == Fw::CmdResponse::OK) {
439 ✗ this->m_engine->setChannelFlowState(channelId, flowState);
440 ✗ this->log_ACTIVITY_LO_SetFlowState(channelId, flowState);
441 }
442
443 ✗ this->cmdResponse_out(opCode, cmdSeq, rspStatus);
444 ✗ }
445
446 ✗ void CfdpManager ::SuspendResumeTransaction_cmdHandler(FwOpcodeType opCode,
447 U32 cmdSeq,
448 U8 channelId,
449 TransactionSeq transactionSeq,
450 EntityId entityId,
451 const SuspendResume& action) {
452 ✗ Fw::CmdResponse::T rspStatus = Fw::CmdResponse::OK;
453
454 ✗ FW_ASSERT(this->m_engine != nullptr);
455
456 ✗ rspStatus = checkCommandChannelIndex(channelId);
457
458 ✗ if (rspStatus == Fw::CmdResponse::OK) {
459 ✗ Status::T status = this->m_engine->setSuspendResumeTransaction(channelId, transactionSeq, entityId, action);
460 ✗ if (status == Status::SUCCESS) {
461 ✗ if (action == SuspendResume::SUSPEND) {
462 ✗ log_ACTIVITY_LO_TransactionSuspended(transactionSeq, entityId);
463 } else {
464 ✗ log_ACTIVITY_LO_TransactionResumed(transactionSeq, entityId);
465 }
466 } else {
467 ✗ log_WARNING_LO_TransactionNotFound(transactionSeq, entityId);
468 ✗ rspStatus = Fw::CmdResponse::EXECUTION_ERROR;
469 }
470 }
471
472 ✗ this->cmdResponse_out(opCode, cmdSeq, rspStatus);
473 ✗ }
474
475 ✗ void CfdpManager ::CancelTransaction_cmdHandler(FwOpcodeType opCode,
476 U32 cmdSeq,
477 U8 channelId,
478 TransactionSeq transactionSeq,
479 EntityId entityId) {
480 ✗ Fw::CmdResponse::T rspStatus = Fw::CmdResponse::OK;
481
482 ✗ FW_ASSERT(this->m_engine != nullptr);
483
484 ✗ rspStatus = checkCommandChannelIndex(channelId);
485
486 ✗ if (rspStatus == Fw::CmdResponse::OK) {
487 ✗ Status::T status = this->m_engine->cancelTransactionBySeq(channelId, transactionSeq, entityId);
488 ✗ if (status == Status::SUCCESS) {
489 ✗ log_ACTIVITY_HI_TransactionCanceled(transactionSeq, entityId);
490 } else {
491 ✗ log_WARNING_LO_TransactionNotFound(transactionSeq, entityId);
492 ✗ rspStatus = Fw::CmdResponse::EXECUTION_ERROR;
493 }
494 }
495
496 ✗ this->cmdResponse_out(opCode, cmdSeq, rspStatus);
497 ✗ }
498
499 ✗ void CfdpManager ::AbandonTransaction_cmdHandler(FwOpcodeType opCode,
500 U32 cmdSeq,
501 U8 channelId,
502 TransactionSeq transactionSeq,
503 EntityId entityId) {
504 ✗ Fw::CmdResponse::T rspStatus = Fw::CmdResponse::OK;
505
506 ✗ FW_ASSERT(this->m_engine != nullptr);
507
508 ✗ rspStatus = checkCommandChannelIndex(channelId);
509
510 ✗ if (rspStatus == Fw::CmdResponse::OK) {
511 ✗ Status::T status = this->m_engine->abandonTransaction(channelId, transactionSeq, entityId);
512 ✗ if (status == Status::SUCCESS) {
513 ✗ log_ACTIVITY_HI_TransactionAbandoned(transactionSeq, entityId);
514 } else {
515 ✗ log_WARNING_LO_TransactionNotFound(transactionSeq, entityId);
516 ✗ rspStatus = Fw::CmdResponse::EXECUTION_ERROR;
517 }
518 }
519
520 ✗ this->cmdResponse_out(opCode, cmdSeq, rspStatus);
521 ✗ }
522
523 ✗ void CfdpManager ::ResetCounters_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, U8 channelId) {
524 // 0xFF means reset all channels
525 ✗ if (channelId == 0xFF) {
526 ✗ for (U8 i = 0; i < Cfdp::NumChannels; i++) {
527 ✗ this->m_channelTelemetry[i] = Cfdp::ChannelTelemetry();
528 }
529 ✗ this->log_ACTIVITY_HI_ResetCounters(0xFF);
530 }
531 // Otherwise reset specific channel
532 ✗ else if (channelId < Cfdp::NumChannels) {
533 ✗ this->m_channelTelemetry[channelId] = Cfdp::ChannelTelemetry();
534 ✗ this->log_ACTIVITY_HI_ResetCounters(channelId);
535 } else {
536 // Invalid channel ID
537 ✗ this->log_WARNING_LO_InvalidChannel(channelId, Cfdp::NumChannels - 1);
538 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR);
539 ✗ return;
540 }
541
542 // Emit updated telemetry
543 ✗ this->tlmWrite_ChannelTelemetry(this->m_channelTelemetry);
544
545 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
546 }
547
548 // ----------------------------------------------------------------------
549 // Private command helper functions
550 // ----------------------------------------------------------------------
551
552 ✗ Fw::CmdResponse::T CfdpManager ::checkCommandChannelIndex(U8 channelIndex) {
553 ✗ if (channelIndex >= Cfdp::NumChannels) {
554 ✗ this->log_WARNING_LO_InvalidChannel(channelIndex, Cfdp::NumChannels);
555 ✗ return Fw::CmdResponse::VALIDATION_ERROR;
556 } else {
557 ✗ return Fw::CmdResponse::OK;
558 }
559 }
560
561 ✗ Fw::CmdResponse::T CfdpManager ::checkCommandChannelPollIndex(U8 pollIndex) {
562 ✗ if (pollIndex >= MaxPollingDirPerChan) {
563 ✗ this->log_WARNING_LO_InvalidChannelPoll(pollIndex, MaxPollingDirPerChan);
564 ✗ return Fw::CmdResponse::VALIDATION_ERROR;
565 } else {
566 ✗ return Fw::CmdResponse::OK;
567 }
568 }
569
570 ✗ 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 ✗ if (interval == 0) {
574 ✗ this->log_WARNING_LO_InvalidPollInterval(interval);
575 ✗ return Fw::CmdResponse::VALIDATION_ERROR;
576 } else {
577 ✗ return Fw::CmdResponse::OK;
578 }
579 }
580
581 // ----------------------------------------------------------------------
582 // Parameter helpers used by the CFDP engine
583 // ----------------------------------------------------------------------
584
585 ✗ EntityId CfdpManager::getLocalEidParam(void) {
586 ✗ Fw::ParamValid valid;
587
588 // Check for coding errors as all CFDP parameters must have a default
589 ✗ EntityId localEid = this->paramGet_LocalEid(valid);
590 ✗ FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
591
592 ✗ return localEid;
593 ✗ }
594
595 ✗ U32 CfdpManager::getOutgoingFileChunkSizeParam(void) {
596 ✗ Fw::ParamValid valid;
597
598 // Check for coding errors as all CFDP parameters must have a default
599 ✗ U32 chunkSize = this->paramGet_OutgoingFileChunkSize(valid);
600 ✗ FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
601
602 ✗ return chunkSize;
603 ✗ }
604 ✗ U32 CfdpManager::getRxCrcCalcBytesPerCycleParam(void) {
605 ✗ Fw::ParamValid valid;
606
607 // Check for coding errors as all CFDP parameters must have a default
608 ✗ U32 rxSize = this->paramGet_RxCrcCalcBytesPerCycle(valid);
609 ✗ FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
610
611 ✗ return rxSize;
612 ✗ }
613
614 ✗ U8 CfdpManager::getPostInactivitySendRetriesParam(void) {
615 ✗ Fw::ParamValid valid;
616
617 // Check for coding errors as all CFDP parameters must have a default
618 ✗ U8 retries = this->paramGet_PostInactivitySendRetries(valid);
619 ✗ FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
620
621 ✗ return retries;
622 ✗ }
623
624 ✗ Fw::String CfdpManager::getTmpDirParam(U8 channelIndex) {
625 ✗ Fw::ParamValid valid;
626
627 ✗ 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 ✗ ChannelArrayParams paramArray = paramGet_ChannelConfig(valid);
632 ✗ FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
633
634 // Now get individual parameter
635 ✗ return paramArray[channelIndex].get_tmp_dir();
636 ✗ }
637
638 ✗ Fw::String CfdpManager::getFailDirParam(U8 channelIndex) {
639 ✗ Fw::ParamValid valid;
640
641 ✗ 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 ✗ ChannelArrayParams paramArray = paramGet_ChannelConfig(valid);
646 ✗ FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
647
648 // Now get individual parameter
649 ✗ return paramArray[channelIndex].get_fail_dir();
650 ✗ }
651
652 ✗ U8 CfdpManager::getAckLimitParam(U8 channelIndex) {
653 ✗ Fw::ParamValid valid;
654
655 ✗ 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 ✗ ChannelArrayParams paramArray = paramGet_ChannelConfig(valid);
660 ✗ FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
661
662 // Now get individual parameter
663 ✗ return paramArray[channelIndex].get_ack_limit();
664 ✗ }
665
666 ✗ U8 CfdpManager::getNackLimitParam(U8 channelIndex) {
667 ✗ Fw::ParamValid valid;
668
669 ✗ 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 ✗ ChannelArrayParams paramArray = paramGet_ChannelConfig(valid);
674 ✗ FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
675
676 // Now get individual parameter
677 ✗ return paramArray[channelIndex].get_nack_limit();
678 ✗ }
679
680 ✗ U32 CfdpManager::getAckTimerParam(U8 channelIndex) {
681 ✗ Fw::ParamValid valid;
682
683 ✗ 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 ✗ ChannelArrayParams paramArray = paramGet_ChannelConfig(valid);
688 ✗ FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
689
690 // Now get individual parameter
691 ✗ return paramArray[channelIndex].get_ack_timer();
692 ✗ }
693
694 ✗ U32 CfdpManager::getInactivityTimerParam(U8 channelIndex) {
695 ✗ Fw::ParamValid valid;
696
697 ✗ 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 ✗ ChannelArrayParams paramArray = paramGet_ChannelConfig(valid);
702 ✗ FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
703
704 // Now get individual parameter
705 ✗ return paramArray[channelIndex].get_inactivity_timer();
706 ✗ }
707
708 ✗ Fw::Enabled CfdpManager::getDequeueEnabledParam(U8 channelIndex) {
709 ✗ Fw::ParamValid valid;
710
711 ✗ 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 ✗ ChannelArrayParams paramArray = paramGet_ChannelConfig(valid);
716 ✗ FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
717
718 // Now get individual parameter
719 ✗ return paramArray[channelIndex].get_dequeue_enabled();
720 ✗ }
721
722 ✗ Fw::String CfdpManager::getMoveDirParam(U8 channelIndex) {
723 ✗ Fw::ParamValid valid;
724
725 ✗ 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 ✗ ChannelArrayParams paramArray = paramGet_ChannelConfig(valid);
730 ✗ FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
731
732 // Now get individual parameter
733 ✗ return paramArray[channelIndex].get_move_dir();
734 ✗ }
735
736 ✗ U32 CfdpManager ::getMaxOutgoingPdusPerCycleParam(U8 channelIndex) {
737 ✗ Fw::ParamValid valid;
738
739 ✗ 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 ✗ ChannelArrayParams paramArray = paramGet_ChannelConfig(valid);
744 ✗ FW_ASSERT(FW_PARAM_OK(valid), static_cast<FwAssertArgType>(valid.e));
745
746 // Now get individual parameter
747 ✗ return paramArray[channelIndex].get_max_outgoing_pdus_per_cycle();
748 ✗ }
749
750 } // namespace Cfdp
751 } // namespace Ccsds
752 } // namespace Svc
753