GCC Code Coverage Report


Directory: Svc/Ccsds/CfdpManager/
File: TransactionTx.cpp
Date: 2026-09-03 21:16:27
Exec Total Coverage
Lines: 325 356 91.3%
Functions: 26 27 96.3%
Branches: 241 322 74.8%

Line Branch Exec Source
1 // ======================================================================
2 // \title TransactionTx.cpp
3 // \brief cpp file for CFDP TX Transaction state machine
4 //
5 // This file is a port of TX transaction state machine operations from the following files
6 // from the NASA Core Flight System (cFS) CFDP (CF) Application, version 3.0.0,
7 // adapted for use within the F-Prime (F') framework:
8 // - cf_cfdp_s.c (send-file transaction state handling routines)
9 // - cf_cfdp_dispatch.c (TX state machine dispatch functions)
10 //
11 // This file contains various state handling routines for
12 // transactions which are sending a file, as well as dispatch
13 // functions for TX state machines and top-level transaction dispatch.
14 //
15 // ======================================================================
16 //
17 // NASA Docket No. GSC-18,447-1
18 //
19 // Copyright (c) 2019 United States Government as represented by the
20 // Administrator of the National Aeronautics and Space Administration.
21 // All Rights Reserved.
22 //
23 // Licensed under the Apache License, Version 2.0 (the "License"); you may
24 // not use this file except in compliance with the License. You may obtain
25 // a copy of the License at
26 //
27 // http://www.apache.org/licenses/LICENSE-2.0
28 //
29 // Unless required by applicable law or agreed to in writing, software
30 // distributed under the License is distributed on an "AS IS" BASIS,
31 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
32 // See the License for the specific language governing permissions and
33 // limitations under the License.
34 //
35 // ======================================================================
36
37 #include <stdio.h>
38 #include <string.h>
39
40 #include <Svc/Ccsds/CfdpManager/CfdpManager.hpp>
41 #include <Svc/Ccsds/CfdpManager/Channel.hpp>
42 #include <Svc/Ccsds/CfdpManager/Chunk.hpp>
43 #include <Svc/Ccsds/CfdpManager/Engine.hpp>
44 #include <Svc/Ccsds/CfdpManager/Timer.hpp>
45 #include <Svc/Ccsds/CfdpManager/Transaction.hpp>
46 #include <Svc/Ccsds/CfdpManager/Utils.hpp>
47
48 namespace Svc {
49 namespace Ccsds {
50 namespace Cfdp {
51
52 // ======================================================================
53 // TX State Machine - Private Helper (anonymous namespace)
54 // ======================================================================
55
56 namespace {
57
58 // Helper to build dispatch tables
59 3 FileDirectiveDispatchTable makeFileDirectiveTable(StateRecvFunc fin, StateRecvFunc ack, StateRecvFunc nak) {
60 3 FileDirectiveDispatchTable table = {};
61
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 memset(&table, 0, sizeof(table));
62
63 3 table.fdirective[static_cast<U32>(FileDirective::FILE_DIRECTIVE_FIN)] = fin;
64 3 table.fdirective[static_cast<U32>(FileDirective::FILE_DIRECTIVE_ACK)] = ack;
65 3 table.fdirective[static_cast<U32>(FileDirective::FILE_DIRECTIVE_NAK)] = nak;
66
67 3 return table;
68 }
69
70 } // anonymous namespace
71
72 // ======================================================================
73 // TX State Machine - Public Methods
74 // ======================================================================
75
76 void Transaction::s1Recv(const Fw::Buffer& buffer) {
77 // s1 doesn't need to receive anything
78 static const SSubstateRecvDispatchTable substate_fns = {{nullptr}};
79 this->sDispatchRecv(buffer, &substate_fns);
80 }
81
82 14 void Transaction::s2Recv(const Fw::Buffer& buffer) {
83 static const FileDirectiveDispatchTable s2_meta =
84
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 13 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
14 makeFileDirectiveTable(&Transaction::s2EarlyFin, nullptr, nullptr);
85
86 static const FileDirectiveDispatchTable s2_fd_or_eof =
87
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 13 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
14 makeFileDirectiveTable(&Transaction::s2EarlyFin, nullptr, &Transaction::s2Nak);
88
89 static const FileDirectiveDispatchTable s2_wait_ack =
90
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 13 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
14 makeFileDirectiveTable(&Transaction::s2Fin, &Transaction::s2EofAck, &Transaction::s2NakArm);
91
92 static const SSubstateRecvDispatchTable substate_fns = {{
93 &s2_meta, /* TxSubState::TX_SUB_STATE_METADATA */
94 &s2_fd_or_eof, /* TxSubState::TX_SUB_STATE_FILEDATA */
95 &s2_fd_or_eof, /* TxSubState::TX_SUB_STATE_EOF */
96 &s2_wait_ack /* TxSubState::TX_SUB_STATE_CLOSEOUT_SYNC */
97 }};
98
99 14 this->sDispatchRecv(buffer, &substate_fns);
100 14 }
101
102 15 void Transaction::initTxFile(Class::T cfdp_class, Keep::T keep, U8 chan, U8 priority) {
103 15 m_chan_num = chan;
104 15 m_priority = priority;
105 15 m_keep = keep;
106 15 m_txn_class = cfdp_class;
107
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 6 times.
15 m_state = (cfdp_class == Cfdp::Class::CLASS_2) ? TxnState::TXN_STATE_S2 : TxnState::TXN_STATE_S1;
108 15 m_state_data.send.sub_state = TxSubState::TX_SUB_STATE_METADATA;
109 15 }
110
111 11 void Transaction::s1Tx() {
112 static const SSubstateSendDispatchTable substate_fns = {{
113 &Transaction::sSubstateSendMetadata, // TxSubState::TX_SUB_STATE_METADATA
114 &Transaction::sSubstateSendFileData, // TxSubState::TX_SUB_STATE_FILEDATA
115 &Transaction::s1SubstateSendEof, // TxSubState::TX_SUB_STATE_EOF
116 nullptr // TxSubState::TX_SUB_STATE_CLOSEOUT_SYNC
117 }};
118
119 11 this->sDispatchTransmit(&substate_fns);
120 11 }
121
122 47 void Transaction::s2Tx() {
123 static const SSubstateSendDispatchTable substate_fns = {{
124 &Transaction::sSubstateSendMetadata, // TxSubState::TX_SUB_STATE_METADATA
125 &Transaction::s2SubstateSendFileData, // TxSubState::TX_SUB_STATE_FILEDATA
126 &Transaction::s2SubstateSendEof, // TxSubState::TX_SUB_STATE_EOF
127 nullptr // TxSubState::TX_SUB_STATE_CLOSEOUT_SYNC
128 }};
129
130 47 this->sDispatchTransmit(&substate_fns);
131 47 }
132
133 56 void Transaction::sAckTimerTick() {
134 56 U8 ack_limit = 0;
135
136 // note: the ack timer is only ever relevant on class 2
137
5/6
✓ Branch 1 taken 23 times.
✓ Branch 2 taken 33 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 23 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 22 times.
56 if (this->m_state != TxnState::TXN_STATE_S2 || !this->m_flags.com.ack_timer_armed) {
138 // nothing to do
139 34 return;
140 }
141
142
2/2
✓ Branch 4 taken 19 times.
✓ Branch 5 taken 3 times.
22 if (this->m_ack_timer.getStatus() == Timer::Status::RUNNING) {
143 19 this->m_ack_timer.run();
144
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 } else if (this->m_state_data.send.sub_state == TxSubState::TX_SUB_STATE_CLOSEOUT_SYNC) {
145 // Check limit and handle if needed
146 3 ack_limit = this->m_cfdpManager->getAckLimitParam(this->m_chan_num);
147
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
3 if (this->m_state_data.send.s2.acknak_count >= ack_limit) {
148
2/2
✓ Branch 15 taken 1 times.
✓ Branch 19 taken 1 times.
2 this->m_cfdpManager->log_WARNING_LO_TxAckLimitReached(this->getClass(), this->m_history->src_eid,
149 1 this->m_history->seq_num);
150 1 this->m_engine->setTxnStatus(this, TxnStatus::TXN_STATUS_ACK_LIMIT_NO_EOF);
151 1 this->m_cfdpManager->incrementFaultAckLimit(this->m_chan_num);
152
153 // give up on this
154 1 this->m_engine->finishTransaction(this, true);
155 1 this->m_flags.com.ack_timer_armed = false;
156 } else {
157 // Increment acknak counter
158 2 ++this->m_state_data.send.s2.acknak_count;
159
160 // If the peer sent FIN that is an implicit EOF ack, it is not supposed
161 // to send it before EOF unless an error occurs, and either way we do not
162 // re-transmit anything after FIN unless we get another FIN
163
4/8
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 2 times.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
2 if (!this->m_flags.tx.eof_ack_recv && !this->m_flags.tx.fin_recv) {
164 2 this->m_flags.tx.send_eof = true;
165 } else {
166 // no response is pending
167 this->m_flags.com.ack_timer_armed = false;
168 }
169 }
170
171 // reset the ack timer if still waiting on something
172
3/4
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 times.
3 if (this->m_flags.com.ack_timer_armed) {
173 2 this->m_engine->armAckTimer(this);
174 }
175 } else {
176 // if we are not waiting for anything, why is the ack timer armed?
177 this->m_flags.com.ack_timer_armed = false;
178 }
179 }
180
181 65 void Transaction::sTick(I32* cont /* unused */) {
182 bool pending_send;
183
184 65 pending_send = true; // maybe; tbd, will be reset if not
185
186 // at each tick, various timers used by S are checked
187 // first, check inactivity timer
188
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 65 times.
✓ Branch 3 taken 65 times.
✗ Branch 4 not taken.
65 if (!this->m_flags.com.inactivity_fired) {
189
1/2
✓ Branch 4 taken 65 times.
✗ Branch 5 not taken.
65 if (this->m_inactivity_timer.getStatus() == Timer::Status::RUNNING) {
190 65 this->m_inactivity_timer.run();
191 // Check if timer just expired naturally (after run())
192
2/2
✓ Branch 4 taken 12 times.
✓ Branch 5 taken 53 times.
65 if (this->m_inactivity_timer.getStatus() == Timer::Status::EXPIRED) {
193 12 this->m_flags.com.inactivity_fired = true;
194
195 // HOLD state is the normal path to recycle transaction objects, not an error
196 // Canceled transactions timing out while waiting for EOF-ACK is also normal
197 // inactivity is abnormal in any other state
198
3/4
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 11 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
12 if (this->m_state != TxnState::TXN_STATE_HOLD && this->m_state == TxnState::TXN_STATE_S2 &&
199
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
1 !this->m_flags.com.canceled) {
200
2/2
✓ Branch 15 taken 1 times.
✓ Branch 19 taken 1 times.
2 this->m_cfdpManager->log_WARNING_LO_TxInactivityTimeout(this->getClass(), this->m_history->src_eid,
201 1 this->m_history->seq_num);
202 1 this->m_engine->setTxnStatus(this, TxnStatus::TXN_STATUS_INACTIVITY_DETECTED);
203
204 1 this->m_cfdpManager->incrementFaultInactivityTimer(this->m_chan_num);
205 }
206 }
207 }
208 }
209
210 // tx maintenance: possibly process send_eof, or send_fin_ack
211 // On ERROR, clear the flag so we do not retry forever. On NO_BUF_AVAIL, leave it set to retry.
212
3/4
✗ Branch 1 not taken.
✓ Branch 2 taken 65 times.
✓ Branch 3 taken 15 times.
✓ Branch 4 taken 50 times.
65 if (this->m_flags.tx.send_eof) {
213 15 Status::T sret = this->sSendEof();
214
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 if (sret == Cfdp::Status::SUCCESS) {
215 15 this->m_flags.tx.send_eof = false;
216 } else if (sret == Cfdp::Status::ERROR) {
217 this->m_flags.tx.send_eof = false;
218 pending_send = false;
219 }
220
3/4
✗ Branch 1 not taken.
✓ Branch 2 taken 50 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 44 times.
50 } else if (this->m_flags.tx.send_fin_ack) {
221 6 Status::T sret = this->sSendFinAck();
222
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (sret == Cfdp::Status::SUCCESS) {
223 6 this->m_flags.tx.send_fin_ack = false;
224 } else if (sret == Cfdp::Status::ERROR) {
225 this->m_flags.tx.send_fin_ack = false;
226 pending_send = false;
227 }
228 } else {
229 44 pending_send = false;
230 }
231
232 // if the inactivity timer ran out, then there is no sense
233 // pending for responses for anything. Send out anything
234 // that we need to send (i.e. the EOF) just in case the sender
235 // is still listening to us but do not expect any future ACKs
236 //
237 // Recycle the transaction once the inactivity timer has fired, but never while a send
238 // is still pending (e.g. a throttled FIN-ACK). The send is attempted above before this
239 // check, so if one is still queued we defer recycle a cycle to give it a chance to go
240 // out. Once the send succeeds, pending_send clears, so this cannot strand the transaction.
241 // This covers the HOLD (finished) and S2/CLOSEOUT_SYNC (stuck waiting for a FIN) cases,
242 // which by then have no pending send, without dropping a FIN-ACK that has not yet been
243 // transmitted.
244 //
245 // Bound the deferral so a send that keeps failing on a buffer shortage cannot hold the slot
246 // forever: after a small retry budget, recycle regardless. A late FIN is answered statelessly.
247 65 bool retries_exhausted = false;
248
5/6
✗ Branch 1 not taken.
✓ Branch 2 taken 65 times.
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 53 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 11 times.
65 if (this->m_flags.com.inactivity_fired && pending_send) {
249 1 if (this->m_flags.com.post_inactivity_send_retries >=
250
1/2
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
1 this->m_cfdpManager->getPostInactivitySendRetriesParam()) {
251 retries_exhausted = true;
252 } else {
253 1 this->m_flags.com.post_inactivity_send_retries++;
254 }
255 }
256
6/8
✗ Branch 1 not taken.
✓ Branch 2 taken 65 times.
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 53 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 11 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
65 bool should_recycle = this->m_flags.com.inactivity_fired && (!pending_send || retries_exhausted);
257
258
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 54 times.
65 if (should_recycle) {
259 // the transaction is now recyclable - this means we will
260 // no longer have a record of this transaction seq. If the sender
261 // wakes up or if the network delivers severely delayed PDUs at
262 // some future point, then they will be seen as spurious. They
263 // will no longer be associable with this transaction at all
264 11 this->m_chan->recycleTransaction(this);
265
266 // NOTE: this must be the last thing in here. Do not use txn after this
267 } else {
268 // transaction still valid so process the ACK timer, if relevant
269 54 this->sAckTimerTick();
270 }
271 65 }
272
273 57 void Transaction::sTickNak(I32* cont) {
274 57 bool nakProcessed = false;
275 Status::T status;
276
277 // Only Class 2 transactions should process NAKs
278
3/4
✗ Branch 1 not taken.
✓ Branch 2 taken 57 times.
✓ Branch 3 taken 42 times.
✓ Branch 4 taken 15 times.
57 if (this->m_txn_class == Cfdp::Class::CLASS_2) {
279
1/1
✓ Branch 2 taken 42 times.
42 status = this->sCheckAndRespondNak(&nakProcessed);
280
4/6
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 42 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 38 times.
42 if ((status == Cfdp::Status::SUCCESS) && nakProcessed) {
281 4 *cont = 1; // cause dispatcher to re-enter this scheduler cycle
282 }
283 }
284 57 }
285
286 1 void Transaction::sCancel() {
287
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (this->m_state_data.send.sub_state < TxSubState::TX_SUB_STATE_EOF) {
288 // if state has not reached TxSubState::TX_SUB_STATE_EOF, then set it to TxSubState::TX_SUB_STATE_EOF now.
289 this->m_state_data.send.sub_state = TxSubState::TX_SUB_STATE_EOF;
290 }
291 1 }
292
293 // ======================================================================
294 // TX State Machine - Private Helper Methods
295 // ======================================================================
296
297 15 Status::T Transaction::sSendEof() {
298 // note the crc is "finalized" regardless of success or failure of the txn
299 // this is OK as we still need to put some value into the EOF
300
3/4
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 13 times.
✓ Branch 4 taken 2 times.
15 if (!this->m_flags.com.crc_calc) {
301 // The F' version does not have an equivalent finalize call as it
302 // - Never stores a partial word internally
303 // - Never needs to "flush" anything
304 // - Always accounts for padding at update time
305 13 this->m_flags.com.crc_calc = true;
306 }
307 15 return this->m_engine->sendEof(this);
308 }
309
310 3 void Transaction::s1SubstateSendEof() {
311 // set the flag, the EOF is sent by the tick handler
312 3 this->m_flags.tx.send_eof = true;
313
314 // In class 1 this is the end of normal operation
315 // NOTE: this is not always true, as class 1 can request an EOF ack.
316 // In this case we could change state to CLOSEOUT_SYNC instead and wait,
317 // but right now we do not request an EOF ack in S1
318 3 this->m_engine->finishTransaction(this, true);
319 3 }
320
321 9 void Transaction::s2SubstateSendEof() {
322 // set the flag, the EOF is sent by the tick handler
323 9 this->m_flags.tx.send_eof = true;
324
325 // wait for remaining responses to close out the state machine
326 9 this->m_state_data.send.sub_state = TxSubState::TX_SUB_STATE_CLOSEOUT_SYNC;
327
328 // always move the transaction onto the wait queue now
329 9 this->m_chan->dequeueTransaction(this);
330 9 this->m_chan->insertSortPrio(this, QueueId::TXW);
331
332 // the ack timer is armed in class 2 only
333 9 this->m_engine->armAckTimer(this);
334 9 }
335
336 36 Status::T Transaction::sSendFileData(FileSize foffs, FileSize bytes_to_read, U8 calc_crc, FileSize* bytes_processed) {
337 36 FW_ASSERT(bytes_processed != nullptr);
338 36 *bytes_processed = 0;
339
340 36 Status::T status = Cfdp::Status::SUCCESS;
341
342 // Local buffer for file data
343 36 U8 fileDataBuffer[MaxPduSize];
344
345 // Create File Data PDU
346
1/1
✓ Branch 2 taken 36 times.
36 FileDataPdu fdPdu;
347 36 Cfdp::PduDirection direction = PduDirection::DIRECTION_TOWARD_RECEIVER;
348
349 // Calculate maximum data size we can send, accounting for PDU overhead
350
1/1
✓ Branch 2 taken 36 times.
36 U32 maxDataCapacity = fdPdu.getMaxFileDataSize();
351
352 // Limited by: bytes_to_read, outgoing_file_chunk_size, and maxDataCapacity
353
1/1
✓ Branch 6 taken 36 times.
36 FileSize outgoing_file_chunk_size = this->m_cfdpManager->getOutgoingFileChunkSizeParam();
354 36 FileSize max_data_bytes = bytes_to_read;
355
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 16 times.
36 if (max_data_bytes > outgoing_file_chunk_size) {
356 20 max_data_bytes = outgoing_file_chunk_size;
357 }
358
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
36 if (max_data_bytes > maxDataCapacity) {
359 max_data_bytes = maxDataCapacity;
360 }
361
362 // Seek to file offset if needed
363 36 FwSizeType actual_bytes = max_data_bytes;
364
1/2
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
36 if (status == Cfdp::Status::SUCCESS) {
365
2/2
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 32 times.
36 if (this->m_state_data.send.cached_pos != foffs) {
366
1/1
✓ Branch 6 taken 4 times.
4 Os::File::Status fileStatus = this->m_fd.seek(foffs, Os::File::SeekType::ABSOLUTE);
367
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (fileStatus != Os::File::OP_OK) {
368 status = Cfdp::Status::ERROR;
369 }
370 }
371 }
372
373 // Read file data
374
1/2
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
36 if (status == Cfdp::Status::SUCCESS) {
375
1/1
✓ Branch 6 taken 36 times.
36 Os::File::Status fileStatus = this->m_fd.read(fileDataBuffer, actual_bytes, Os::File::WaitType::WAIT);
376
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
36 if (fileStatus != Os::File::OP_OK) {
377 status = Cfdp::Status::ERROR;
378 }
379 }
380
381 // Initialize and send PDU
382
1/2
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
36 if (status == Cfdp::Status::SUCCESS) {
383 // File has been read successfully, update cached_pos to reflect new file position
384 // This MUST be done before attempting to send, so if send fails (throttle/error),
385 // we don't try to read the same data again on next cycle
386 36 this->m_state_data.send.cached_pos += static_cast<FileSize>(actual_bytes);
387
388
2/2
✓ Branch 4 taken 36 times.
✓ Branch 8 taken 36 times.
180 fdPdu.initialize(direction,
389 36 this->getClass(), // transmission mode
390 36 this->m_cfdpManager->getLocalEidParam(), // source EID
391 36 this->m_history->seq_num, // transaction sequence number
392 36 this->m_history->peer_eid, // destination EID
393 foffs, // file offset
394 static_cast<U16>(actual_bytes), // data size
395 fileDataBuffer // data pointer
396 );
397
398
1/1
✓ Branch 6 taken 36 times.
36 status = this->m_engine->sendFd(this, fdPdu);
399 }
400
401 // Update CRC and bytes_processed
402
1/2
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
36 if (status == Cfdp::Status::SUCCESS) {
403 36 FW_ASSERT((foffs + actual_bytes) <= this->m_fsize, static_cast<FwAssertArgType>(foffs),
404 static_cast<FwAssertArgType>(actual_bytes), static_cast<FwAssertArgType>(this->m_fsize));
405
406
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 4 times.
36 if (calc_crc) {
407
1/1
✓ Branch 4 taken 32 times.
32 this->m_crc.update(fileDataBuffer, foffs, static_cast<U32>(actual_bytes));
408 }
409
410 36 *bytes_processed = static_cast<U32>(actual_bytes);
411 }
412
413 36 return status;
414 36 }
415
416 32 void Transaction::sSubstateSendFileData() {
417 32 FileSize bytes_processed = 0;
418
1/1
✓ Branch 8 taken 32 times.
32 Status::T status = this->sSendFileData(this->m_foffs, (this->m_fsize - this->m_foffs), 1, &bytes_processed);
419
420 // When SEND_PDU_NO_BUF_AVAIL_ERROR is returned, it means either:
421 // 1) The throttle limit (max_outgoing_pdus_per_cycle) was reached, OR
422 // 2) Buffer allocation failed
423 // In either case, we should stay in FILEDATA state and retry next cycle.
424 // This is NOT a file I/O error, so we should NOT transition to EOF.
425 // We also need to break the cycleTx loop by setting m_chan->m_currentTxn.
426
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 if (status == Cfdp::Status::SEND_PDU_NO_BUF_AVAIL_ERROR) {
427 // Throttle limit or buffer exhaustion - stay in FILEDATA, retry next cycle
428 // Set m_currentTxn to break the cycleTx loop for this cycle
429 this->m_chan->setCurrentTxn(this);
430
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 } else if (status != Cfdp::Status::SUCCESS) {
431 // IO error -- change state and send EOF
432 this->m_engine->setTxnStatus(this, TxnStatus::TXN_STATUS_FILESTORE_REJECTION);
433 this->m_state_data.send.sub_state = TxSubState::TX_SUB_STATE_EOF;
434
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
32 } else if (bytes_processed > 0) {
435 32 this->m_foffs += bytes_processed;
436
2/2
✓ Branch 4 taken 12 times.
✓ Branch 5 taken 20 times.
32 if (this->m_foffs == this->m_fsize) {
437 // file is done - transition to EOF state, which will be sent in next loop iteration
438 12 this->m_state_data.send.sub_state = TxSubState::TX_SUB_STATE_EOF;
439 }
440 } else {
441 // don't care about other cases
442 }
443 32 }
444
445 71 Status::T Transaction::sCheckAndRespondNak(bool* nakProcessed) {
446 const Chunk* chunk;
447 Status::T sret;
448 71 Status::T ret = Cfdp::Status::SUCCESS;
449 71 FileSize bytes_processed = 0;
450
451 71 FW_ASSERT(nakProcessed != nullptr);
452 71 *nakProcessed = false;
453
454 // Class 2 transactions must have had chunks allocated
455 71 FW_ASSERT(this->m_chunks != nullptr);
456
457
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 71 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 71 times.
71 if (this->m_flags.tx.md_need_send) {
458 sret = this->m_engine->sendMd(this);
459 if (sret == Cfdp::Status::ERROR) {
460 ret = Cfdp::Status::ERROR; // serialization failure -- fail the transaction
461 } else {
462 if (sret == Cfdp::Status::SUCCESS) {
463 this->m_flags.tx.md_need_send = false;
464 }
465 // On SUCCESS or SEND_PDU_NO_BUF_AVAIL_ERROR (throttled, retry next cycle),
466 // mark nak processed to keep caller from sending file data this cycle
467 *nakProcessed = true; // nak processed, so don't send filedata
468 }
469 } else {
470 // Get first chunk and process if available
471
1/1
✓ Branch 5 taken 71 times.
71 chunk = this->m_chunks->chunks.getFirstChunk();
472
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 67 times.
71 if (chunk != nullptr) {
473
1/1
✓ Branch 5 taken 4 times.
4 ret = this->sSendFileData(chunk->offset, chunk->size, 0, &bytes_processed);
474
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (ret != Cfdp::Status::SUCCESS) {
475 // error occurred
476 ret = Cfdp::Status::ERROR; // error occurred
477
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 } else if (bytes_processed > 0) {
478
1/1
✓ Branch 5 taken 4 times.
4 this->m_chunks->chunks.removeFromFirst(bytes_processed);
479 4 *nakProcessed = true; // nak processed, so caller doesn't send file data
480 }
481 }
482 }
483
484 71 return ret;
485 }
486
487 29 void Transaction::s2SubstateSendFileData() {
488 Status::T status;
489 29 bool nakProcessed = false;
490
491
1/1
✓ Branch 2 taken 29 times.
29 status = this->sCheckAndRespondNak(&nakProcessed);
492
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
29 if (status != Cfdp::Status::SUCCESS) {
493 this->m_engine->setTxnStatus(this, TxnStatus::TXN_STATUS_NAK_RESPONSE_ERROR);
494 this->m_flags.tx.send_eof = true; /* do not leave the remote hanging */
495 this->m_engine->finishTransaction(this, true);
496 return;
497 }
498
499
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
29 if (!nakProcessed) {
500
1/1
✓ Branch 2 taken 29 times.
29 this->sSubstateSendFileData();
501 } else {
502 // NAK was processed, so do not send filedata
503 }
504 }
505
506 16 void Transaction::sSubstateSendMetadata() {
507 Status::T status;
508 Os::File::Status fileStatus;
509 16 bool success = true;
510
511
1/2
✓ Branch 6 taken 16 times.
✗ Branch 7 not taken.
16 if (false == this->m_fd.isOpen()) {
512 16 fileStatus = this->m_fd.open(this->m_history->fnames.src_filename.toChar(), Os::File::OPEN_READ);
513
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 15 times.
16 if (fileStatus != Os::File::OP_OK) {
514
2/2
✓ Branch 16 taken 1 times.
✓ Branch 20 taken 1 times.
3 this->m_cfdpManager->log_WARNING_LO_TxFileOpenFailed(this->getClass(), this->m_history->src_eid,
515 1 this->m_history->seq_num,
516 1 this->m_history->fnames.src_filename, fileStatus);
517 1 this->m_cfdpManager->incrementFaultFileOpen(this->m_chan_num);
518 1 success = false;
519 }
520
521
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 1 times.
16 if (success) {
522 15 FwSizeType file_size;
523
1/1
✓ Branch 6 taken 15 times.
15 fileStatus = this->m_fd.size(file_size);
524 15 this->m_fsize = static_cast<FileSize>(file_size);
525
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 14 times.
15 if (fileStatus != Os::File::Status::OP_OK) {
526
2/2
✓ Branch 15 taken 1 times.
✓ Branch 19 taken 1 times.
2 this->m_cfdpManager->log_WARNING_LO_TxFileSeekFailed(this->getClass(), this->m_history->src_eid,
527 1 this->m_history->seq_num, fileStatus);
528
1/1
✓ Branch 8 taken 1 times.
1 this->m_cfdpManager->incrementFaultFileSeek(this->m_chan_num);
529 1 success = false;
530
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 13 times.
14 } else if (this->m_fsize == 0) {
531 // Zero-length file - fail transaction gracefully instead of asserting
532
2/2
✓ Branch 16 taken 1 times.
✓ Branch 20 taken 1 times.
3 this->m_cfdpManager->log_WARNING_LO_TxZeroLengthFile(this->getClass(), this->m_history->src_eid,
533 1 this->m_history->seq_num,
534 1 this->m_history->fnames.src_filename);
535
1/1
✓ Branch 8 taken 1 times.
1 this->m_cfdpManager->incrementFaultFileSizeMismatch(this->m_chan_num);
536 1 success = false;
537 }
538 }
539 }
540
541
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 3 times.
16 if (success) {
542 13 status = this->m_engine->sendMd(this);
543
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 12 times.
13 if (status == Cfdp::Status::ERROR) {
544 /* failed to send md (generic ERROR from a PDU serialization failure) */
545
2/2
✓ Branch 15 taken 1 times.
✓ Branch 19 taken 1 times.
2 this->m_cfdpManager->log_WARNING_LO_TxSendMetadataFailed(this->getClass(), this->m_history->src_eid,
546 1 this->m_history->seq_num);
547 1 success = false;
548
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 } else if (status == Cfdp::Status::SUCCESS) {
549 /* once metadata is sent, switch to filedata mode */
550 12 this->m_state_data.send.sub_state = TxSubState::TX_SUB_STATE_FILEDATA;
551
552
2/2
✓ Branch 15 taken 12 times.
✓ Branch 19 taken 12 times.
84 this->m_cfdpManager->log_ACTIVITY_HI_TxFileTransferStarted(
553 36 this->getClass(), this->m_history->seq_num, this->m_history->src_eid,
554 36 this->m_history->fnames.src_filename, this->m_history->peer_eid, this->m_history->fnames.dst_filename,
555 12 static_cast<U32>(this->m_fsize));
556 }
557 /* if status==Cfdp::Status::SEND_PDU_NO_BUF_AVAIL_ERROR, then the send buffer is throttled;
558 leave success==true and retry the metadata send on the next cycle */
559 }
560
561
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 12 times.
16 if (!success) {
562 4 this->m_engine->setTxnStatus(this, TxnStatus::TXN_STATUS_FILESTORE_REJECTION);
563 4 this->m_engine->finishTransaction(this, true);
564 }
565
566 // don't need to reset the CRC since its taken care of by reset_cfdp()
567 16 }
568
569 6 Status::T Transaction::sSendFinAck() {
570 Status::T ret =
571 24 this->m_engine->sendAck(this, static_cast<AckTxnStatus>(GetTxnStatus(this)), FileDirective::FILE_DIRECTIVE_FIN,
572 6 static_cast<ConditionCode>(this->m_state_data.send.s2.fin_cc),
573 12 this->m_history->peer_eid, this->m_history->seq_num);
574 6 return ret;
575 }
576
577 1 void Transaction::s2EarlyFin(const Fw::Buffer& buffer) {
578 // received early fin, so just cancel
579
2/2
✓ Branch 15 taken 1 times.
✓ Branch 19 taken 1 times.
2 this->m_cfdpManager->log_WARNING_LO_TxEarlyFinReceived(this->getClass(), this->m_history->src_eid,
580 1 this->m_history->seq_num);
581 1 this->m_engine->setTxnStatus(this, TxnStatus::TXN_STATUS_EARLY_FIN);
582
583 1 this->m_state_data.send.sub_state = TxSubState::TX_SUB_STATE_CLOSEOUT_SYNC;
584
585 // otherwise do normal fin processing
586 1 this->s2Fin(buffer);
587 1 }
588
589 8 void Transaction::s2Fin(const Fw::Buffer& buffer) {
590 // Deserialize FIN PDU from buffer
591
1/1
✓ Branch 2 taken 8 times.
8 FinPdu fin;
592 // const_cast: Fw::SerialBuffer requires non-const U8* even for deserialization (read-only)
593
3/3
✓ Branch 5 taken 8 times.
✓ Branch 11 taken 8 times.
✓ Branch 14 taken 8 times.
8 Fw::SerialBuffer sb(const_cast<U8*>(buffer.getData()), buffer.getSize());
594
2/2
✓ Branch 5 taken 8 times.
✓ Branch 8 taken 8 times.
8 sb.setBuffLen(buffer.getSize());
595
596
1/1
✓ Branch 2 taken 8 times.
8 Fw::SerializeStatus deserStatus = fin.deserializeFrom(sb);
597
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
8 if (deserStatus != Fw::FW_SERIALIZE_OK) {
598 // Bad FIN PDU
599
1/1
✓ Branch 9 taken 1 times.
1 this->m_cfdpManager->log_WARNING_LO_FailFinPduDeserialization(this->getChannelId(),
600 static_cast<I32>(deserStatus));
601 1 return;
602 }
603
604
2/3
✓ Branch 6 taken 7 times.
✓ Branch 8 taken 7 times.
✗ Branch 9 not taken.
7 if (!this->m_engine->recvFin(this, fin)) {
605 // Always set flag to send FIN-ACK, even if it is a retransmit
606 7 this->m_flags.tx.send_fin_ack = true;
607
608 // set the CC only on the first time we get the FIN. If this is a dupe
609 // then re-ack but otherwise ignore it
610
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
7 if (!this->m_flags.tx.fin_recv) {
611 7 this->m_flags.tx.fin_recv = true;
612 7 this->m_state_data.send.s2.fin_cc = static_cast<U8>(fin.getConditionCode());
613 7 this->m_state_data.send.s2.acknak_count = 0; // in case retransmits had occurred
614
615 // note this is a no-op unless the status was unset previously
616
1/1
✓ Branch 8 taken 7 times.
7 this->m_engine->setTxnStatus(this, static_cast<TxnStatus>(this->m_state_data.send.s2.fin_cc));
617
618 // Generally FIN is the last exchange in an S2 transaction, the remote is not supposed
619 // to send it until after the EOF+ACK. So at this point we stop trying to send anything
620 // to the peer, regardless of whether we got every ACK we expected.
621
1/1
✓ Branch 6 taken 7 times.
7 this->m_engine->finishTransaction(this, true);
622 }
623 }
624 9 }
625
626 5 void Transaction::s2Nak(const Fw::Buffer& buffer) {
627 U8 counter;
628 U8 bad_sr;
629
630 5 bad_sr = 0;
631
632 // Deserialize NAK PDU from buffer
633
1/1
✓ Branch 2 taken 5 times.
5 NakPdu nak;
634 // const_cast: Fw::SerialBuffer requires non-const U8* even for deserialization (read-only)
635
3/3
✓ Branch 5 taken 5 times.
✓ Branch 11 taken 5 times.
✓ Branch 14 taken 5 times.
5 Fw::SerialBuffer sb(const_cast<U8*>(buffer.getData()), buffer.getSize());
636
2/2
✓ Branch 5 taken 5 times.
✓ Branch 8 taken 5 times.
5 sb.setBuffLen(buffer.getSize());
637
638
1/1
✓ Branch 2 taken 5 times.
5 Fw::SerializeStatus deserStatus = nak.deserializeFrom(sb);
639
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
5 if (deserStatus != Fw::FW_SERIALIZE_OK) {
640 // Bad NAK PDU
641
1/1
✓ Branch 9 taken 1 times.
1 this->m_cfdpManager->log_WARNING_LO_FailNakPduDeserialization(this->getChannelId(),
642 static_cast<I32>(deserStatus));
643
1/1
✓ Branch 8 taken 1 times.
1 this->m_cfdpManager->incrementRecvErrors(this->m_chan_num);
644 1 return;
645 }
646
647 // this function is only invoked for NAK PDU types
648
6/7
✓ Branch 6 taken 4 times.
✓ Branch 8 taken 4 times.
✗ Branch 9 not taken.
✓ Branch 12 taken 3 times.
✓ Branch 13 taken 1 times.
✓ Branch 14 taken 3 times.
✓ Branch 15 taken 1 times.
4 if (this->m_engine->recvNak(this, nak) == Cfdp::Status::SUCCESS && nak.getNumSegments() > 0) {
649
2/2
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 3 times.
12 for (counter = 0; counter < nak.getNumSegments(); ++counter) {
650 9 const Cfdp::SegmentRequest& sr = nak.getSegment(counter);
651
652
3/4
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 8 times.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
9 if (sr.offsetStart == 0 && sr.offsetEnd == 0) {
653 // need to re-send metadata PDU
654 1 this->m_flags.tx.md_need_send = true;
655 } else {
656
2/2
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 7 times.
8 if (sr.offsetEnd < sr.offsetStart) {
657 1 ++bad_sr;
658 1 continue;
659 }
660
661 // overflow probably won't be an issue
662
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 6 times.
7 if (sr.offsetEnd > this->m_fsize) {
663 1 ++bad_sr;
664 1 continue;
665 }
666
667 // insert gap data in chunks
668
1/1
✓ Branch 9 taken 6 times.
6 this->m_chunks->chunks.add(sr.offsetStart, sr.offsetEnd - sr.offsetStart);
669 }
670 }
671
672
1/1
✓ Branch 10 taken 3 times.
3 this->m_cfdpManager->addRecvNakSegmentRequests(this->m_chan_num, nak.getNumSegments());
673
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (bad_sr) {
674
2/2
✓ Branch 15 taken 1 times.
✓ Branch 19 taken 1 times.
2 this->m_cfdpManager->log_WARNING_LO_TxInvalidSegmentRequests(this->getClass(), this->m_history->src_eid,
675 1 this->m_history->seq_num, bad_sr);
676 }
677 } else {
678
2/2
✓ Branch 15 taken 1 times.
✓ Branch 19 taken 1 times.
2 this->m_cfdpManager->log_WARNING_LO_TxInvalidNakPdu(this->getClass(), this->m_history->src_eid,
679 1 this->m_history->seq_num);
680
1/1
✓ Branch 8 taken 1 times.
1 this->m_cfdpManager->incrementRecvErrors(this->m_chan_num);
681 }
682 6 }
683
684 2 void Transaction::s2NakArm(const Fw::Buffer& buffer) {
685 2 this->m_engine->armAckTimer(this);
686 2 this->s2Nak(buffer);
687 2 }
688
689 7 void Transaction::s2EofAck(const Fw::Buffer& buffer) {
690 // Deserialize ACK PDU from buffer
691
1/1
✓ Branch 2 taken 7 times.
7 AckPdu ack;
692 // const_cast: Fw::SerialBuffer requires non-const U8* even for deserialization (read-only)
693
3/3
✓ Branch 5 taken 7 times.
✓ Branch 11 taken 7 times.
✓ Branch 14 taken 7 times.
7 Fw::SerialBuffer sb(const_cast<U8*>(buffer.getData()), buffer.getSize());
694
2/2
✓ Branch 5 taken 7 times.
✓ Branch 8 taken 7 times.
7 sb.setBuffLen(buffer.getSize());
695
696
1/1
✓ Branch 2 taken 7 times.
7 Fw::SerializeStatus deserStatus = ack.deserializeFrom(sb);
697
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
7 if (deserStatus != Fw::FW_SERIALIZE_OK) {
698 // Bad ACK PDU
699
1/1
✓ Branch 9 taken 1 times.
1 this->m_cfdpManager->log_WARNING_LO_FailAckPduDeserialization(this->getChannelId(),
700 static_cast<I32>(deserStatus));
701 1 return;
702 }
703
704 // ACK PDU has been validated during deserialization
705 // Check if this is an EOF acknowledgment
706
1/2
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 if (ack.getDirectiveCode() == FileDirective::FILE_DIRECTIVE_END_OF_FILE) {
707 6 this->m_flags.tx.eof_ack_recv = true;
708 6 this->m_flags.com.ack_timer_armed = false; // just wait for FIN now, nothing to re-send
709 6 this->m_state_data.send.s2.acknak_count = 0; // in case EOF retransmits had occurred
710
711 // For canceled transactions, finish immediately after receiving EOF-ACK
712 // The remote side does not send FIN for canceled transactions per CFDP protocol
713 // if FIN was also received then we are done (these can come out of order)
714
4/8
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 6 times.
6 if (this->m_flags.com.canceled || this->m_flags.tx.fin_recv) {
715 this->m_engine->finishTransaction(this, true);
716 }
717 }
718 8 }
719
720 // ======================================================================
721 // Dispatch Methods (ported from cf_cfdp_dispatch.c)
722 // ======================================================================
723
724 16 void Transaction::sDispatchRecv(const Fw::Buffer& buffer, const SSubstateRecvDispatchTable* dispatch) {
725 const FileDirectiveDispatchTable* substate_tbl;
726 StateRecvFunc selected_handler;
727
728 16 FW_ASSERT(this->m_state_data.send.sub_state < TxSubState::TX_SUB_STATE_NUM_STATES,
729 static_cast<U8>(this->m_state_data.send.sub_state), static_cast<U8>(TxSubState::TX_SUB_STATE_NUM_STATES));
730
731 // Peek at PDU type from buffer
732 16 Cfdp::PduTypeEnum::T pduType = Cfdp::peekPduType(buffer);
733
734 // send state, so we only care about file directive PDU
735 16 selected_handler = nullptr;
736
737
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 15 times.
16 if (pduType == Cfdp::PduTypeEnum::FILE_DATA) {
738
2/2
✓ Branch 15 taken 1 times.
✓ Branch 19 taken 1 times.
2 this->m_cfdpManager->log_WARNING_LO_TxNonFileDirectivePduReceived(this->getClass(), this->m_history->src_eid,
739 1 this->m_history->seq_num);
740 } else {
741 // Not a file-data PDU - parse as a directive PDU to get the directive code.
742 // const_cast: Fw::SerialBuffer requires non-const U8* even for deserialization (read-only)
743
3/3
✓ Branch 5 taken 15 times.
✓ Branch 11 taken 15 times.
✓ Branch 14 taken 15 times.
15 Fw::SerialBuffer sb(const_cast<U8*>(buffer.getData()), buffer.getSize());
744
2/2
✓ Branch 5 taken 15 times.
✓ Branch 8 taken 15 times.
15 sb.setBuffLen(buffer.getSize());
745
746 15 Cfdp::PduHeader header;
747
2/3
✓ Branch 1 taken 15 times.
✓ Branch 3 taken 15 times.
✗ Branch 4 not taken.
15 if (header.fromSerialBuffer(sb) == Fw::FW_SERIALIZE_OK) {
748 // Read directive code (first byte after header for directive PDUs)
749 15 U8 directiveCodeByte;
750
2/3
✓ Branch 2 taken 15 times.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
15 if (sb.deserializeTo(directiveCodeByte) == Fw::FW_SERIALIZE_OK) {
751 15 FileDirective directiveCode = static_cast<FileDirective>(directiveCodeByte);
752
753
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 1 times.
15 if (directiveCode < FileDirective::FILE_DIRECTIVE_INVALID_MAX) {
754 // This should be silent (no event) if no handler is defined in the table
755 14 substate_tbl = dispatch->substate[static_cast<U32>(this->m_state_data.send.sub_state)];
756
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (substate_tbl != nullptr) {
757 14 selected_handler = substate_tbl->fdirective[static_cast<U32>(directiveCode)];
758 }
759 } else {
760
2/2
✓ Branch 11 taken 1 times.
✓ Branch 15 taken 1 times.
4 this->m_cfdpManager->log_WARNING_LO_TxInvalidDirectiveCode(
761 3 this->getClass(), this->m_history->src_eid, this->m_history->seq_num, directiveCodeByte,
762 1 static_cast<U8>(this->m_state_data.send.sub_state));
763 }
764 }
765 }
766 15 }
767
768 // check that there's a valid function pointer. If there isn't,
769 // then silently ignore. We may want to discuss if it's worth
770 // shutting down the whole transaction if a PDU is received
771 // that doesn't make sense to be received (For example,
772 // class 1 CFDP receiving a NAK PDU) but for now, we silently
773 // ignore the received packet and keep chugging along.
774
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 2 times.
16 if (selected_handler) {
775
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
14 (this->*selected_handler)(buffer);
776 }
777 16 }
778
779 58 void Transaction::sDispatchTransmit(const SSubstateSendDispatchTable* dispatch) {
780 StateSendFunc selected_handler;
781
782 58 selected_handler = dispatch->substate[static_cast<U32>(this->m_state_data.send.sub_state)];
783
1/2
✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
58 if (selected_handler != nullptr) {
784
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 58 times.
58 (this->*selected_handler)();
785 }
786 58 }
787
788 58 void Transaction::txStateDispatch(const TxnSendDispatchTable* dispatch) {
789 StateSendFunc selected_handler;
790
791 58 FW_ASSERT(this->m_state < TxnState::TXN_STATE_INVALID, static_cast<U8>(this->m_state),
792 static_cast<U8>(TxnState::TXN_STATE_INVALID));
793
794 58 selected_handler = dispatch->tx[static_cast<U32>(this->m_state)];
795
1/2
✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
58 if (selected_handler != nullptr) {
796
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 58 times.
58 (this->*selected_handler)();
797 }
798 58 }
799
800 } // namespace Cfdp
801 } // namespace Ccsds
802 } // namespace Svc
803