F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
TransactionTx.cpp
Go to the documentation of this file.
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 
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 FileDirectiveDispatchTable makeFileDirectiveTable(StateRecvFunc fin, StateRecvFunc ack, StateRecvFunc nak) {
60  FileDirectiveDispatchTable table = {};
61  memset(&table, 0, sizeof(table));
62 
63  table.fdirective[static_cast<U32>(FileDirective::FILE_DIRECTIVE_FIN)] = fin;
64  table.fdirective[static_cast<U32>(FileDirective::FILE_DIRECTIVE_ACK)] = ack;
65  table.fdirective[static_cast<U32>(FileDirective::FILE_DIRECTIVE_NAK)] = nak;
66 
67  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 void Transaction::s2Recv(const Fw::Buffer& buffer) {
83  static const FileDirectiveDispatchTable s2_meta =
84  makeFileDirectiveTable(&Transaction::s2EarlyFin, nullptr, nullptr);
85 
86  static const FileDirectiveDispatchTable s2_fd_or_eof =
87  makeFileDirectiveTable(&Transaction::s2EarlyFin, nullptr, &Transaction::s2Nak);
88 
89  static const FileDirectiveDispatchTable s2_wait_ack =
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  this->sDispatchRecv(buffer, &substate_fns);
100 }
101 
102 void Transaction::initTxFile(Class::T cfdp_class, Keep::T keep, U8 chan, U8 priority) {
103  m_chan_num = chan;
104  m_priority = priority;
105  m_keep = keep;
106  m_txn_class = cfdp_class;
109 }
110 
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  this->sDispatchTransmit(&substate_fns);
120 }
121 
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  this->sDispatchTransmit(&substate_fns);
131 }
132 
134  U8 ack_limit = 0;
135 
136  // note: the ack timer is only ever relevant on class 2
137  if (this->m_state != TxnState::TXN_STATE_S2 || !this->m_flags.com.ack_timer_armed) {
138  // nothing to do
139  return;
140  }
141 
142  if (this->m_ack_timer.getStatus() == Timer::Status::RUNNING) {
143  this->m_ack_timer.run();
144  } else if (this->m_state_data.send.sub_state == TxSubState::TX_SUB_STATE_CLOSEOUT_SYNC) {
145  // Check limit and handle if needed
146  ack_limit = this->m_cfdpManager->getAckLimitParam(this->m_chan_num);
147  if (this->m_state_data.send.s2.acknak_count >= ack_limit) {
148  this->m_cfdpManager->log_WARNING_LO_TxAckLimitReached(this->getClass(), this->m_history->src_eid,
149  this->m_history->seq_num);
151  this->m_cfdpManager->incrementFaultAckLimit(this->m_chan_num);
152 
153  // give up on this
154  this->m_engine->finishTransaction(this, true);
155  this->m_flags.com.ack_timer_armed = false;
156  } else {
157  // Increment acknak counter
158  ++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  if (!this->m_flags.tx.eof_ack_recv && !this->m_flags.tx.fin_recv) {
164  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  if (this->m_flags.com.ack_timer_armed) {
173  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 void Transaction::sTick(I32* cont /* unused */) {
182  bool pending_send;
183 
184  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  if (!this->m_flags.com.inactivity_fired) {
189  if (this->m_inactivity_timer.getStatus() == Timer::Status::RUNNING) {
190  this->m_inactivity_timer.run();
191  // Check if timer just expired naturally (after run())
192  if (this->m_inactivity_timer.getStatus() == Timer::Status::EXPIRED) {
193  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  if (this->m_state != TxnState::TXN_STATE_HOLD && this->m_state == TxnState::TXN_STATE_S2 &&
199  !this->m_flags.com.canceled) {
200  this->m_cfdpManager->log_WARNING_LO_TxInactivityTimeout(this->getClass(), this->m_history->src_eid,
201  this->m_history->seq_num);
203 
204  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  if (this->m_flags.tx.send_eof) {
213  Status::T sret = this->sSendEof();
214  if (sret == Cfdp::Status::SUCCESS) {
215  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  } else if (this->m_flags.tx.send_fin_ack) {
221  Status::T sret = this->sSendFinAck();
222  if (sret == Cfdp::Status::SUCCESS) {
223  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  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  bool retries_exhausted = false;
248  if (this->m_flags.com.inactivity_fired && pending_send) {
249  if (this->m_flags.com.post_inactivity_send_retries >=
250  this->m_cfdpManager->getPostInactivitySendRetriesParam()) {
251  retries_exhausted = true;
252  } else {
253  this->m_flags.com.post_inactivity_send_retries++;
254  }
255  }
256  bool should_recycle = this->m_flags.com.inactivity_fired && (!pending_send || retries_exhausted);
257 
258  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  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  this->sAckTimerTick();
270  }
271 }
272 
273 void Transaction::sTickNak(I32* cont) {
274  bool nakProcessed = false;
275  Status::T status;
276 
277  // Only live Class 2 transactions should process NAKs. A finished transaction (HOLD) is
278  // only waiting on its inactivity timer to be recycled and has nothing left to resend;
279  // it may also never have been assigned a chunk list if it finished while still on PEND.
280  if (this->m_txn_class == Cfdp::Class::CLASS_2 && this->m_state != TxnState::TXN_STATE_HOLD) {
281  status = this->sCheckAndRespondNak(&nakProcessed);
282  if ((status == Cfdp::Status::SUCCESS) && nakProcessed) {
283  *cont = 1; // cause dispatcher to re-enter this scheduler cycle
284  }
285  }
286 }
287 
289  if (this->m_state_data.send.sub_state < TxSubState::TX_SUB_STATE_EOF) {
290  // if state has not reached TxSubState::TX_SUB_STATE_EOF, then set it to TxSubState::TX_SUB_STATE_EOF now.
291  this->m_state_data.send.sub_state = TxSubState::TX_SUB_STATE_EOF;
292  }
293 }
294 
295 // ======================================================================
296 // TX State Machine - Private Helper Methods
297 // ======================================================================
298 
299 Status::T Transaction::sSendEof() {
300  // note the crc is "finalized" regardless of success or failure of the txn
301  // this is OK as we still need to put some value into the EOF
302  if (!this->m_flags.com.crc_calc) {
303  // The F' version does not have an equivalent finalize call as it
304  // - Never stores a partial word internally
305  // - Never needs to "flush" anything
306  // - Always accounts for padding at update time
307  this->m_flags.com.crc_calc = true;
308  }
309  return this->m_engine->sendEof(this);
310 }
311 
313  // set the flag, the EOF is sent by the tick handler
314  this->m_flags.tx.send_eof = true;
315 
316  // In class 1 this is the end of normal operation
317  // NOTE: this is not always true, as class 1 can request an EOF ack.
318  // In this case we could change state to CLOSEOUT_SYNC instead and wait,
319  // but right now we do not request an EOF ack in S1
320  this->m_engine->finishTransaction(this, true);
321 }
322 
324  // set the flag, the EOF is sent by the tick handler
325  this->m_flags.tx.send_eof = true;
326 
327  // wait for remaining responses to close out the state machine
329 
330  // always move the transaction onto the wait queue now
331  this->m_chan->dequeueTransaction(this);
332  this->m_chan->insertSortPrio(this, QueueId::TXW);
333 
334  // the ack timer is armed in class 2 only
335  this->m_engine->armAckTimer(this);
336 }
337 
338 Status::T Transaction::sSendFileData(FileSize foffs, FileSize bytes_to_read, U8 calc_crc, FileSize* bytes_processed) {
339  FW_ASSERT(bytes_processed != nullptr);
340  *bytes_processed = 0;
341 
343 
344  // Local buffer for file data
345  U8 fileDataBuffer[MaxPduSize];
346 
347  // Create File Data PDU
348  FileDataPdu fdPdu;
350 
351  // Calculate maximum data size we can send, accounting for PDU overhead
352  U32 maxDataCapacity = fdPdu.getMaxFileDataSize();
353 
354  // Limited by: bytes_to_read, outgoing_file_chunk_size, and maxDataCapacity
355  FileSize outgoing_file_chunk_size = this->m_cfdpManager->getOutgoingFileChunkSizeParam();
356  FileSize max_data_bytes = bytes_to_read;
357  if (max_data_bytes > outgoing_file_chunk_size) {
358  max_data_bytes = outgoing_file_chunk_size;
359  }
360  if (max_data_bytes > maxDataCapacity) {
361  max_data_bytes = maxDataCapacity;
362  }
363 
364  // Seek to file offset if needed
365  FwSizeType actual_bytes = max_data_bytes;
366  if (status == Cfdp::Status::SUCCESS) {
367  if (this->m_state_data.send.cached_pos != foffs) {
368  Os::File::Status fileStatus = this->m_fd.seek(foffs, Os::File::SeekType::ABSOLUTE);
369  if (fileStatus != Os::File::OP_OK) {
370  status = Cfdp::Status::ERROR;
371  }
372  }
373  }
374 
375  // Read file data
376  if (status == Cfdp::Status::SUCCESS) {
377  Os::File::Status fileStatus = this->m_fd.read(fileDataBuffer, actual_bytes, Os::File::WaitType::WAIT);
378  if (fileStatus != Os::File::OP_OK) {
379  status = Cfdp::Status::ERROR;
380  }
381  }
382 
383  // Initialize and send PDU
384  if (status == Cfdp::Status::SUCCESS) {
385  // File has been read successfully, update cached_pos to reflect new file position
386  // This MUST be done before attempting to send, so if send fails (throttle/error),
387  // we don't try to read the same data again on next cycle
388  this->m_state_data.send.cached_pos += static_cast<FileSize>(actual_bytes);
389 
390  fdPdu.initialize(direction,
391  this->getClass(), // transmission mode
392  this->m_cfdpManager->getLocalEidParam(), // source EID
393  this->m_history->seq_num, // transaction sequence number
394  this->m_history->peer_eid, // destination EID
395  foffs, // file offset
396  static_cast<U16>(actual_bytes), // data size
397  fileDataBuffer // data pointer
398  );
399 
400  status = this->m_engine->sendFd(this, fdPdu);
401  }
402 
403  // Update CRC and bytes_processed
404  if (status == Cfdp::Status::SUCCESS) {
405  FW_ASSERT((foffs + actual_bytes) <= this->m_fsize, static_cast<FwAssertArgType>(foffs),
406  static_cast<FwAssertArgType>(actual_bytes), static_cast<FwAssertArgType>(this->m_fsize));
407 
408  if (calc_crc) {
409  this->m_crc.update(fileDataBuffer, foffs, static_cast<U32>(actual_bytes));
410  }
411 
412  *bytes_processed = static_cast<U32>(actual_bytes);
413  }
414 
415  return status;
416 }
417 
419  FileSize bytes_processed = 0;
420  Status::T status = this->sSendFileData(this->m_foffs, (this->m_fsize - this->m_foffs), 1, &bytes_processed);
421 
422  // When SEND_PDU_NO_BUF_AVAIL_ERROR is returned, it means either:
423  // 1) The throttle limit (max_outgoing_pdus_per_cycle) was reached, OR
424  // 2) Buffer allocation failed
425  // In either case, we should stay in FILEDATA state and retry next cycle.
426  // This is NOT a file I/O error, so we should NOT transition to EOF.
427  // We also need to break the cycleTx loop by setting m_chan->m_currentTxn.
429  // Throttle limit or buffer exhaustion - stay in FILEDATA, retry next cycle
430  // Set m_currentTxn to break the cycleTx loop for this cycle
431  this->m_chan->setCurrentTxn(this);
432  } else if (status != Cfdp::Status::SUCCESS) {
433  // IO error -- change state and send EOF
435  this->m_state_data.send.sub_state = TxSubState::TX_SUB_STATE_EOF;
436  } else if (bytes_processed > 0) {
437  this->m_foffs += bytes_processed;
438  if (this->m_foffs == this->m_fsize) {
439  // file is done - transition to EOF state, which will be sent in next loop iteration
440  this->m_state_data.send.sub_state = TxSubState::TX_SUB_STATE_EOF;
441  }
442  } else {
443  // don't care about other cases
444  }
445 }
446 
447 Status::T Transaction::sCheckAndRespondNak(bool* nakProcessed) {
448  const Chunk* chunk;
449  Status::T sret;
451  FileSize bytes_processed = 0;
452 
453  FW_ASSERT(nakProcessed != nullptr);
454  *nakProcessed = false;
455 
456  // Class 2 transactions must have had chunks allocated
457  FW_ASSERT(this->m_chunks != nullptr);
458 
459  if (this->m_flags.tx.md_need_send) {
460  sret = this->m_engine->sendMd(this);
461  if (sret == Cfdp::Status::ERROR) {
462  ret = Cfdp::Status::ERROR; // serialization failure -- fail the transaction
463  } else {
464  if (sret == Cfdp::Status::SUCCESS) {
465  this->m_flags.tx.md_need_send = false;
466  } else {
467  // Throttle limit or buffer exhaustion - keep md_need_send, retry next cycle
468  // Set m_currentTxn to break the cycleTx loop (or tick loop) for this cycle
469  this->m_chan->setCurrentTxn(this);
470  }
471  // On SUCCESS or SEND_PDU_NO_BUF_AVAIL_ERROR (throttled, retry next cycle),
472  // mark nak processed to keep caller from sending file data this cycle
473  *nakProcessed = true; // nak processed, so don't send filedata
474  }
475  } else {
476  // Get first chunk and process if available
477  chunk = this->m_chunks->chunks.getFirstChunk();
478  if (chunk != nullptr) {
479  ret = this->sSendFileData(chunk->offset, chunk->size, 0, &bytes_processed);
481  // Throttle limit or buffer exhaustion is transient, not a NAK response failure.
482  // The chunk stays on the list and is retried next cycle; set m_currentTxn to
483  // break the cycleTx loop (or tick loop) for this cycle
484  this->m_chan->setCurrentTxn(this);
485  *nakProcessed = true; // nak pending, so don't send filedata
486  ret = Cfdp::Status::SUCCESS;
487  } else if (ret != Cfdp::Status::SUCCESS) {
488  // error occurred
489  ret = Cfdp::Status::ERROR; // error occurred
490  } else if (bytes_processed > 0) {
491  this->m_chunks->chunks.removeFromFirst(bytes_processed);
492  *nakProcessed = true; // nak processed, so caller doesn't send file data
493  }
494  }
495  }
496 
497  return ret;
498 }
499 
501  Status::T status;
502  bool nakProcessed = false;
503 
504  status = this->sCheckAndRespondNak(&nakProcessed);
505  if (status != Cfdp::Status::SUCCESS) {
507  this->m_flags.tx.send_eof = true; /* do not leave the remote hanging */
508  this->m_engine->finishTransaction(this, true);
509  return;
510  }
511 
512  if (!nakProcessed) {
513  this->sSubstateSendFileData();
514  } else {
515  // NAK was processed, so do not send filedata
516  }
517 }
518 
520  Status::T status;
521  Os::File::Status fileStatus;
522  bool success = true;
523 
524  if (false == this->m_fd.isOpen()) {
525  fileStatus = this->m_fd.open(this->m_history->fnames.src_filename.toChar(), Os::File::OPEN_READ);
526  if (fileStatus != Os::File::OP_OK) {
527  this->m_cfdpManager->log_WARNING_LO_TxFileOpenFailed(this->getClass(), this->m_history->src_eid,
528  this->m_history->seq_num,
529  this->m_history->fnames.src_filename, fileStatus);
530  this->m_cfdpManager->incrementFaultFileOpen(this->m_chan_num);
531  success = false;
532  }
533 
534  if (success) {
535  FwSizeType file_size;
536  fileStatus = this->m_fd.size(file_size);
537  this->m_fsize = static_cast<FileSize>(file_size);
538  if (fileStatus != Os::File::Status::OP_OK) {
539  this->m_cfdpManager->log_WARNING_LO_TxFileSeekFailed(this->getClass(), this->m_history->src_eid,
540  this->m_history->seq_num, fileStatus);
541  this->m_cfdpManager->incrementFaultFileSeek(this->m_chan_num);
542  success = false;
543  } else if (this->m_fsize == 0) {
544  // Zero-length file - fail transaction gracefully instead of asserting
545  this->m_cfdpManager->log_WARNING_LO_TxZeroLengthFile(this->getClass(), this->m_history->src_eid,
546  this->m_history->seq_num,
547  this->m_history->fnames.src_filename);
548  this->m_cfdpManager->incrementFaultFileSizeMismatch(this->m_chan_num);
549  success = false;
550  }
551  }
552  }
553 
554  if (success) {
555  status = this->m_engine->sendMd(this);
556  if (status == Cfdp::Status::ERROR) {
557  /* failed to send md (generic ERROR from a PDU serialization failure) */
558  this->m_cfdpManager->log_WARNING_LO_TxSendMetadataFailed(this->getClass(), this->m_history->src_eid,
559  this->m_history->seq_num);
560  success = false;
561  } else if (status == Cfdp::Status::SUCCESS) {
562  /* once metadata is sent, switch to filedata mode */
563  this->m_state_data.send.sub_state = TxSubState::TX_SUB_STATE_FILEDATA;
564 
565  this->m_cfdpManager->log_ACTIVITY_HI_TxFileTransferStarted(
566  this->getClass(), this->m_history->seq_num, this->m_history->src_eid,
567  this->m_history->fnames.src_filename, this->m_history->peer_eid, this->m_history->fnames.dst_filename,
568  static_cast<U32>(this->m_fsize));
569  } else {
570  /* status==Cfdp::Status::SEND_PDU_NO_BUF_AVAIL_ERROR, the send buffer is throttled;
571  leave success==true and retry the metadata send on the next cycle. Set m_currentTxn
572  to break the cycleTx loop for this cycle */
573  this->m_chan->setCurrentTxn(this);
574  }
575  }
576 
577  if (!success) {
579  this->m_engine->finishTransaction(this, true);
580  }
581 
582  // don't need to reset the CRC since its taken care of by reset_cfdp()
583 }
584 
585 Status::T Transaction::sSendFinAck() {
586  Status::T ret =
587  this->m_engine->sendAck(this, static_cast<AckTxnStatus>(GetTxnStatus(this)), FileDirective::FILE_DIRECTIVE_FIN,
588  static_cast<ConditionCode>(this->m_state_data.send.s2.fin_cc),
589  this->m_history->peer_eid, this->m_history->seq_num);
590  return ret;
591 }
592 
593 void Transaction::s2EarlyFin(const Fw::Buffer& buffer) {
594  // Parse the FIN before committing any state. A malformed FIN must leave the transaction in its
595  // current sub-state; moving to CLOSEOUT_SYNC (which has no transmit handler) without finishing
596  // the transaction would leave it on TXA where cycleTxFirstActive could never make progress.
597  FinPdu fin;
598  if (!this->s2DeserializeFin(buffer, fin)) {
599  return;
600  }
601 
602  // received early fin, so just cancel
603  this->m_cfdpManager->log_WARNING_LO_TxEarlyFinReceived(this->getClass(), this->m_history->src_eid,
604  this->m_history->seq_num);
605  this->m_engine->setTxnStatus(this, TxnStatus::TXN_STATUS_EARLY_FIN);
606 
608 
609  // otherwise do normal fin processing
610  this->s2HandleFin(fin);
611 }
612 
613 void Transaction::s2Fin(const Fw::Buffer& buffer) {
614  FinPdu fin;
615  if (this->s2DeserializeFin(buffer, fin)) {
616  this->s2HandleFin(fin);
617  }
618 }
619 
621  // const_cast: Fw::SerialBuffer requires non-const U8* even for deserialization (read-only)
622  Fw::SerialBuffer sb(const_cast<U8*>(buffer.getData()), buffer.getSize());
623  sb.setBuffLen(buffer.getSize());
624 
625  Fw::SerializeStatus deserStatus = fin.deserializeFrom(sb);
626  if (deserStatus != Fw::FW_SERIALIZE_OK) {
627  // Bad FIN PDU
628  this->m_cfdpManager->log_WARNING_LO_FailFinPduDeserialization(this->getChannelId(),
629  static_cast<I32>(deserStatus));
630  return false;
631  }
632  return true;
633 }
634 
636  if (!this->m_engine->recvFin(this, fin)) {
637  // Always set flag to send FIN-ACK, even if it is a retransmit
638  this->m_flags.tx.send_fin_ack = true;
639 
640  // set the CC only on the first time we get the FIN. If this is a dupe
641  // then re-ack but otherwise ignore it
642  if (!this->m_flags.tx.fin_recv) {
643  this->m_flags.tx.fin_recv = true;
644  this->m_state_data.send.s2.fin_cc = static_cast<U8>(fin.getConditionCode());
645  this->m_state_data.send.s2.acknak_count = 0; // in case retransmits had occurred
646 
647  // note this is a no-op unless the status was unset previously
648  this->m_engine->setTxnStatus(this, static_cast<TxnStatus>(this->m_state_data.send.s2.fin_cc));
649 
650  // Generally FIN is the last exchange in an S2 transaction, the remote is not supposed
651  // to send it until after the EOF+ACK. So at this point we stop trying to send anything
652  // to the peer, regardless of whether we got every ACK we expected.
653  this->m_engine->finishTransaction(this, true);
654  }
655  }
656 }
657 
658 void Transaction::s2Nak(const Fw::Buffer& buffer) {
659  U8 counter;
660  U8 bad_sr;
661 
662  bad_sr = 0;
663 
664  // Deserialize NAK PDU from buffer
665  NakPdu nak;
666  // const_cast: Fw::SerialBuffer requires non-const U8* even for deserialization (read-only)
667  Fw::SerialBuffer sb(const_cast<U8*>(buffer.getData()), buffer.getSize());
668  sb.setBuffLen(buffer.getSize());
669 
670  Fw::SerializeStatus deserStatus = nak.deserializeFrom(sb);
671  if (deserStatus != Fw::FW_SERIALIZE_OK) {
672  // Bad NAK PDU
673  this->m_cfdpManager->log_WARNING_LO_FailNakPduDeserialization(this->getChannelId(),
674  static_cast<I32>(deserStatus));
675  this->m_cfdpManager->incrementRecvErrors(this->m_chan_num);
676  return;
677  }
678 
679  // this function is only invoked for NAK PDU types
680  if (this->m_engine->recvNak(this, nak) == Cfdp::Status::SUCCESS && nak.getNumSegments() > 0) {
681  for (counter = 0; counter < nak.getNumSegments(); ++counter) {
682  const Cfdp::SegmentRequest& sr = nak.getSegment(counter);
683 
684  if (sr.offsetStart == 0 && sr.offsetEnd == 0) {
685  // need to re-send metadata PDU
686  this->m_flags.tx.md_need_send = true;
687  } else {
688  if (sr.offsetEnd < sr.offsetStart) {
689  ++bad_sr;
690  continue;
691  }
692 
693  // overflow probably won't be an issue
694  if (sr.offsetEnd > this->m_fsize) {
695  ++bad_sr;
696  continue;
697  }
698 
699  // insert gap data in chunks
700  this->m_chunks->chunks.add(sr.offsetStart, sr.offsetEnd - sr.offsetStart);
701  }
702  }
703 
704  this->m_cfdpManager->addRecvNakSegmentRequests(this->m_chan_num, nak.getNumSegments());
705  if (bad_sr) {
706  this->m_cfdpManager->log_WARNING_LO_TxInvalidSegmentRequests(this->getClass(), this->m_history->src_eid,
707  this->m_history->seq_num, bad_sr);
708  }
709  } else {
710  this->m_cfdpManager->log_WARNING_LO_TxInvalidNakPdu(this->getClass(), this->m_history->src_eid,
711  this->m_history->seq_num);
712  this->m_cfdpManager->incrementRecvErrors(this->m_chan_num);
713  }
714 }
715 
716 void Transaction::s2NakArm(const Fw::Buffer& buffer) {
717  this->m_engine->armAckTimer(this);
718  this->s2Nak(buffer);
719 }
720 
721 void Transaction::s2EofAck(const Fw::Buffer& buffer) {
722  // Deserialize ACK PDU from buffer
723  AckPdu ack;
724  // const_cast: Fw::SerialBuffer requires non-const U8* even for deserialization (read-only)
725  Fw::SerialBuffer sb(const_cast<U8*>(buffer.getData()), buffer.getSize());
726  sb.setBuffLen(buffer.getSize());
727 
728  Fw::SerializeStatus deserStatus = ack.deserializeFrom(sb);
729  if (deserStatus != Fw::FW_SERIALIZE_OK) {
730  // Bad ACK PDU
731  this->m_cfdpManager->log_WARNING_LO_FailAckPduDeserialization(this->getChannelId(),
732  static_cast<I32>(deserStatus));
733  return;
734  }
735 
736  // ACK PDU has been validated during deserialization
737  // Check if this is an EOF acknowledgment
739  this->m_flags.tx.eof_ack_recv = true;
740  this->m_flags.com.ack_timer_armed = false; // just wait for FIN now, nothing to re-send
741  this->m_state_data.send.s2.acknak_count = 0; // in case EOF retransmits had occurred
742 
743  // For canceled transactions, finish immediately after receiving EOF-ACK
744  // The remote side does not send FIN for canceled transactions per CFDP protocol
745  // if FIN was also received then we are done (these can come out of order)
746  if (this->m_flags.com.canceled || this->m_flags.tx.fin_recv) {
747  this->m_engine->finishTransaction(this, true);
748  }
749  }
750 }
751 
752 // ======================================================================
753 // Dispatch Methods (ported from cf_cfdp_dispatch.c)
754 // ======================================================================
755 
757  const FileDirectiveDispatchTable* substate_tbl;
758  StateRecvFunc selected_handler;
759 
761  static_cast<U8>(this->m_state_data.send.sub_state), static_cast<U8>(TxSubState::TX_SUB_STATE_NUM_STATES));
762 
763  // Peek at PDU type from buffer
764  Cfdp::PduTypeEnum::T pduType = Cfdp::peekPduType(buffer);
765 
766  // send state, so we only care about file directive PDU
767  selected_handler = nullptr;
768 
769  if (pduType == Cfdp::PduTypeEnum::FILE_DATA) {
770  this->m_cfdpManager->log_WARNING_LO_TxNonFileDirectivePduReceived(this->getClass(), this->m_history->src_eid,
771  this->m_history->seq_num);
772  } else {
773  // Not a file-data PDU - parse as a directive PDU to get the directive code.
774  // const_cast: Fw::SerialBuffer requires non-const U8* even for deserialization (read-only)
775  Fw::SerialBuffer sb(const_cast<U8*>(buffer.getData()), buffer.getSize());
776  sb.setBuffLen(buffer.getSize());
777 
778  Cfdp::PduHeader header;
779  if (header.fromSerialBuffer(sb) == Fw::FW_SERIALIZE_OK) {
780  // Read directive code (first byte after header for directive PDUs)
781  U8 directiveCodeByte;
782  if (sb.deserializeTo(directiveCodeByte) == Fw::FW_SERIALIZE_OK) {
783  FileDirective directiveCode = static_cast<FileDirective>(directiveCodeByte);
784 
785  if (directiveCode < FileDirective::FILE_DIRECTIVE_INVALID_MAX) {
786  // This should be silent (no event) if no handler is defined in the table
787  substate_tbl = dispatch->substate[static_cast<U32>(this->m_state_data.send.sub_state)];
788  if (substate_tbl != nullptr) {
789  selected_handler = substate_tbl->fdirective[static_cast<U32>(directiveCode)];
790  }
791  } else {
792  this->m_cfdpManager->log_WARNING_LO_TxInvalidDirectiveCode(
793  this->getClass(), this->m_history->src_eid, this->m_history->seq_num, directiveCodeByte,
794  static_cast<U8>(this->m_state_data.send.sub_state));
795  }
796  }
797  }
798  }
799 
800  // check that there's a valid function pointer. If there isn't,
801  // then silently ignore. We may want to discuss if it's worth
802  // shutting down the whole transaction if a PDU is received
803  // that doesn't make sense to be received (For example,
804  // class 1 CFDP receiving a NAK PDU) but for now, we silently
805  // ignore the received packet and keep chugging along.
806  if (selected_handler) {
807  (this->*selected_handler)(buffer);
808  }
809 }
810 
812  StateSendFunc selected_handler;
813 
814  selected_handler = dispatch->substate[static_cast<U32>(this->m_state_data.send.sub_state)];
815  if (selected_handler != nullptr) {
816  (this->*selected_handler)();
817  }
818 }
819 
821  StateSendFunc selected_handler;
822 
823  FW_ASSERT(this->m_state < TxnState::TXN_STATE_INVALID, static_cast<U8>(this->m_state),
824  static_cast<U8>(TxnState::TXN_STATE_INVALID));
825 
826  selected_handler = dispatch->tx[static_cast<U32>(this->m_state)];
827  if (selected_handler != nullptr) {
828  (this->*selected_handler)();
829  }
830 }
831 
832 } // namespace Cfdp
833 } // namespace Ccsds
834 } // namespace Svc
Serialization/Deserialization operation was successful.
CfdpTxnFilenames fnames
file names associated with this history entry
Definition: Types.hpp:245
const FileDirectiveDispatchTable * substate[static_cast< U32 >(TxSubState::TX_SUB_STATE_NUM_STATES)]
void s1Recv(const Fw::Buffer &buffer)
S1 receive PDU processing.
void s2EofAck(const Fw::Buffer &pdu)
S2 received ACK PDU.
Status::T sendEof(Transaction *txn)
Create, encode, and send an EOF (End of File) PDU.
Definition: Engine.cpp:227
void incrementRecvErrors(U8 chanId)
Increment receive error counter.
void sAckTimerTick()
Perform acknowledgement timer tick (time-based) processing for S transactions.
A variable-length serializable buffer.
void log_WARNING_LO_FailAckPduDeserialization(U8 channelId, I32 status) const
Log event FailAckPduDeserialization.
void s2Fin(const Fw::Buffer &pdu)
S2 received FIN, so set flag to send FIN-ACK.
void log_WARNING_LO_TxFileSeekFailed(const Svc::Ccsds::Cfdp::Class &cfdpClass, U32 srcEid, U32 seqNum, I32 status) const
Log event TxFileSeekFailed.
Operation succeeded.
Definition: Os.hpp:27
Segment request structure for NAK PDU.
Definition: NakPdu.hpp:18
void addRecvNakSegmentRequests(U8 chanId, U32 count)
Add to received NAK segment requests.
PlatformSizeType FwSizeType
void sDispatchTransmit(const SSubstateSendDispatchTable *dispatch)
Dispatch function to send/generate PDUs on send-file transactions.
void setCurrentTxn(const Transaction *txn)
Set current transaction.
Definition: Channel.cpp:716
void log_WARNING_LO_TxSendMetadataFailed(const Svc::Ccsds::Cfdp::Class &cfdpClass, U32 srcEid, U32 seqNum) const
Log event TxSendMetadataFailed.
StateSendFunc tx[static_cast< U32 >(TxnState::TXN_STATE_INVALID)]
Transmit handler function.
Definition: Transaction.hpp:93
U8 getNumSegments() const
Get number of segments.
Definition: NakPdu.hpp:72
void log_WARNING_LO_TxInactivityTimeout(const Svc::Ccsds::Cfdp::Class &cfdpClass, U32 srcEid, U32 seqNum) const
Log event TxInactivityTimeout.
void sSubstateSendFileData()
Standard state function to send the next file data PDU for active transaction.
void removeFromFirst(FileSize size)
Remove a specified size from the first chunk.
Definition: Chunk.cpp:87
void s2Tx()
S2 dispatch function.
void sTickNak(I32 *cont)
Perform NAK response for TX transactions.
void log_WARNING_LO_TxAckLimitReached(const Svc::Ccsds::Cfdp::Class &cfdpClass, U32 srcEid, U32 seqNum) const
Log event TxAckLimitReached.
Status size(FwSizeType &size_result) override
get size of currently open file
Definition: File.cpp:111
ConditionCode getConditionCode() const
Get condition code.
Definition: FinPdu.hpp:65
U8 * getData() const
Definition: Buffer.cpp:82
CfdpChunkList chunks
Chunk list for gap tracking.
Definition: Types.hpp:261
T
The raw enum type.
CFDP class 2 - Reliable transfer (Acknowledged)
Definition: ClassEnumAc.hpp:44
StateRecvFunc fdirective[static_cast< U32 >(FileDirective::FILE_DIRECTIVE_INVALID_MAX)]
a separate recv handler for each possible file directive PDU in this state
void s2NakArm(const Fw::Buffer &pdu)
S2 NAK handling but with arming the NAK timer.
U8 post_inactivity_send_retries
terminal-send retries attempted after inactivity fired
Definition: Types.hpp:366
Status::T sendAck(Transaction *txn, AckTxnStatus ts, FileDirective dir_code, ConditionCode cc, EntityId peer_eid, TransactionSeq tsn)
Create, encode, and send an ACK (Acknowledgment) PDU.
Definition: Engine.cpp:262
void s2Recv(const Fw::Buffer &buffer)
S2 receive PDU processing.
A dispatch table for send file transactions, receive side.
Fw::SerializeStatus fromSerialBuffer(Fw::SerialBufferBase &serialBuffer)
Initialize this Header from a SerialBufferBase.
Definition: PduHeader.cpp:178
void s2EarlyFin(const Fw::Buffer &pdu)
A FIN was received before file complete, so abandon the transaction.
SerializeStatus
forward declaration for string
void log_WARNING_LO_TxInvalidSegmentRequests(const Svc::Ccsds::Cfdp::Class &cfdpClass, U32 srcEid, U32 seqNum, U32 badCount) const
Log event TxInvalidSegmentRequests.
FileSize size
The size of the chunk.
Definition: Chunk.hpp:51
FileDirective getDirectiveCode() const
Get directive code.
Definition: AckPdu.hpp:66
void finishTransaction(Transaction *txn, bool keep_history)
Finish a transaction.
Definition: Engine.cpp:1066
Os::FileInterface::Status open(const char *path, Mode mode)
open file with supplied path and mode
Definition: File.cpp:50
A dispatch table for send file transactions, transmit side.
State assigned to a transaction after freeing it.
U8 getChannelId() const
Get channel ID.
void incrementFaultFileSeek(U8 chanId)
Increment fault file seek counter.
U8 getAckLimitParam(U8 channelIndex)
void add(FileSize offset, FileSize size)
Add a chunk (file segment) to the list.
Definition: Chunk.cpp:63
Status getStatus(void)
Get the status of a CFDP timer.
Definition: Timer.cpp:35
T
The raw enum type.
Definition: KeepEnumAc.hpp:36
void s1SubstateSendEof()
Sends an EOF for S1.
The type of a File Data PDU.
Definition: FileDataPdu.hpp:19
void log_WARNING_LO_TxZeroLengthFile(const Svc::Ccsds::Cfdp::Class &cfdpClass, U32 srcEid, U32 seqNum, const Fw::StringBase &filename) const
Log event TxZeroLengthFile.
Fw::SerializeStatus deserializeFrom(Fw::SerialBufferBase &buffer, Fw::Endianness mode=Fw::Endianness::BIG) override
Fw::Serializable interface - deserialize from buffer.
Definition: FinPdu.cpp:50
void log_ACTIVITY_HI_TxFileTransferStarted(const Svc::Ccsds::Cfdp::Class &cfdpClass, U32 seqNum, U32 srcEid, const Fw::StringBase &srcFile, U32 destEid, const Fw::StringBase &destFile, U32 fileSize) const
Log event TxFileTransferStarted.
void incrementFaultFileOpen(U8 chanId)
Increment fault file open counter.
virtual Status::T sendMd(Transaction *txn)
Create, encode, and send a Metadata PDU.
Definition: Engine.cpp:189
Status seek(FwSignedSizeType offset, SeekType seekType) override
seek the file pointer to the given offset
Definition: File.cpp:142
FileSize offsetEnd
End offset of missing data.
Definition: NakPdu.hpp:20
Status::T recvNak(Transaction *txn, const NakPdu &pdu)
Unpack a NAK PDU from a received message.
Definition: Engine.cpp:524
void log_WARNING_LO_TxInvalidNakPdu(const Svc::Ccsds::Cfdp::Class &cfdpClass, U32 srcEid, U32 seqNum) const
Log event TxInvalidNakPdu.
void sDispatchRecv(const Fw::Buffer &buffer, const SSubstateRecvDispatchTable *dispatch)
Dispatch function for received PDUs on send-file transactions.
StateSendFunc substate[static_cast< U32 >(TxSubState::TX_SUB_STATE_NUM_STATES)]
void s2SubstateSendEof()
Triggers tick processing to send an EOF and wait for EOF-ACK for S2.
void s1Tx()
S1 dispatch function.
void dequeueTransaction(Transaction *txn)
Free a transaction from the queue it&#39;s on.
Definition: Channel.cpp:503
The type of a Finished PDU.
Definition: FinPdu.hpp:19
void setTxnStatus(Transaction *txn, TxnStatus txn_stat)
Helper function to store transaction status code only.
Definition: Engine.cpp:1172
Generic CFDP error return code.
void update(const U8 *const data, const U32 offset, const U32 length)
Definition: Checksum.cpp:49
Status::T sendFd(Transaction *txn, FileDataPdu &fdPdu)
Encode and send a File Data PDU.
Definition: Engine.cpp:219
void incrementFaultInactivityTimer(U8 chanId)
Increment fault inactivity timer counter.
CfdpTxStateData send
applies to only send file transactions
Definition: Types.hpp:411
FileSize offset
The start offset of the chunk within the file.
Definition: Chunk.hpp:50
const char * toChar() const
Convert to a C-style char*.
void s2SubstateSendFileData()
Send filedata handling for S2.
bool s2DeserializeFin(const Fw::Buffer &pdu, FinPdu &fin)
Deserialize a received FIN PDU.
void(Transaction::*)() StateSendFunc
A member function pointer for dispatching actions to a handler, without existing PDU data...
Definition: Transaction.hpp:71
void log_WARNING_LO_TxNonFileDirectivePduReceived(const Svc::Ccsds::Cfdp::Class &cfdpClass, U32 srcEid, U32 seqNum) const
Log event TxNonFileDirectivePduReceived.
U32 FileSize
File size and offset type.
uint8_t U8
8-bit unsigned integer
Definition: BasicTypes.h:54
SerializeStatus setBuffLen(Serializable::SizeType length) override
Set buffer length manually.
CFDP operation has been successful.
void insertSortPrio(Transaction *txn, QueueId::T queue)
Insert a transaction into a priority sorted transaction queue.
Definition: Channel.cpp:668
Status read(U8 *buffer, FwSizeType &size)
read data from this file into supplied buffer bounded by size
Definition: File.cpp:194
void sTick(I32 *cont)
Perform tick (time-based) processing for S transactions.
FwSizeType getSize() const
Definition: Buffer.cpp:90
FileSize offsetStart
Start offset of missing data.
Definition: NakPdu.hpp:19
void sSubstateSendMetadata()
Send metadata PDU.
Class::T getClass() const
Get transaction class (CLASS_1 or CLASS_2)
TransactionSeq seq_num
transaction identifier, stays constant for entire transfer
Definition: Types.hpp:251
void initTxFile(Class::T cfdp_class, Keep::T keep, U8 chan, U8 priority)
Initialize transaction for outgoing file transfer.
void log_WARNING_LO_TxFileOpenFailed(const Svc::Ccsds::Cfdp::Class &cfdpClass, U32 srcEid, U32 seqNum, const Fw::StringBase &filename, I32 status) const
Log event TxFileOpenFailed.
void log_WARNING_LO_TxInvalidDirectiveCode(const Svc::Ccsds::Cfdp::Class &cfdpClass, U32 srcEid, U32 seqNum, U8 directiveCode, U8 substate) const
Log event TxInvalidDirectiveCode.
Operation was successful.
Definition: File.hpp:42
Fw::SerializeStatus deserializeFrom(Fw::SerialBufferBase &buffer, Fw::Endianness mode=Fw::Endianness::BIG) override
Fw::Serializable interface - deserialize from buffer.
Definition: AckPdu.cpp:47
Status::T recvFin(Transaction *txn, const FinPdu &pdu)
Unpack an FIN PDU from a received message.
Definition: Engine.cpp:507
The type of a PDU header (common to all PDUs)
Definition: PduHeader.hpp:47
void run(void)
Runs a one second increment of the CFDP timers.
Definition: Timer.cpp:39
PduTypeEnum::T peekPduType(const Fw::Buffer &buffer)
Definition: PduHeader.cpp:253
Open file for reading.
Definition: File.hpp:33
void sCancel()
Cancel an S transaction.
CfdpFlagsTx tx
applies to only send file transactions
Definition: Types.hpp:404
bool inactivity_fired
set whenever the inactivity timeout expires
Definition: Types.hpp:364
Marker value for the highest possible state number.
RateGroupDivider component implementation.
CfdpFlagsCommon com
applies to all transactions
Definition: Types.hpp:402
A table of receive handler functions based on file directive code.
void txStateDispatch(const TxnSendDispatchTable *dispatch)
Top-level Dispatch function to send a PDU based on current state.
EntityId src_eid
the source eid of the transaction
Definition: Types.hpp:249
const Chunk * getFirstChunk() const
Get the first chunk in the list.
Definition: Chunk.cpp:83
A table of transmit handler functions based on transaction state.
Definition: Transaction.hpp:92
void incrementFaultFileSizeMismatch(U8 chanId)
Increment fault file size mismatch counter.
const SegmentRequest & getSegment(U8 index) const
Get segment at index (no bounds checking - caller must verify index < getNumSegments()) ...
Definition: NakPdu.hpp:75
AckTxnStatus GetTxnStatus(Transaction *txn)
Gets the status of this transaction.
Definition: Utils.cpp:43
Send PDU: No send buffer available, throttling limit reached.
Fw::SerializeStatus deserializeFrom(Fw::SerialBufferBase &buffer, Fw::Endianness mode=Fw::Endianness::BIG) override
Fw::Serializable interface - deserialize from buffer.
Definition: NakPdu.cpp:60
T
The raw enum type.
Definition: ClassEnumAc.hpp:42
void initialize(PduDirection direction, Cfdp::Class::T txmMode, EntityId sourceEid, TransactionSeq transactionSeq, EntityId destEid, FileSize offset, U16 dataSize, const U8 *data)
Initialize a File Data PDU.
Definition: FileDataPdu.cpp:15
U8 fin_cc
remember the cc in the received FIN PDU to echo in eof-fin
Definition: Types.hpp:318
void s2HandleFin(const FinPdu &fin)
S2 handling of a successfully deserialized FIN.
The type of a NAK PDU.
Definition: NakPdu.hpp:24
void(Transaction::*)(const Fw::Buffer &buffer) StateRecvFunc
A member function pointer for dispatching actions to a handler, with existing PDU data...
Definition: Transaction.hpp:83
void armAckTimer(Transaction *txn)
Arm the ACK timer for a transaction.
Definition: Engine.cpp:105
void recycleTransaction(Transaction *txn)
Recover resources associated with a transaction.
Definition: Channel.cpp:620
void s2Nak(const Fw::Buffer &pdu)
S2 NAK PDU received handling.
#define FW_ASSERT(...)
Definition: Assert.hpp:14
void incrementFaultAckLimit(U8 chanId)
Increment fault ACK limit counter.
bool isOpen() const
determine if the file is open
Definition: File.cpp:105
void log_WARNING_LO_TxEarlyFinReceived(const Svc::Ccsds::Cfdp::Class &cfdpClass, U32 srcEid, U32 seqNum) const
Log event TxEarlyFinReceived.
void log_WARNING_LO_FailFinPduDeserialization(U8 channelId, I32 status) const
Log event FailFinPduDeserialization.
The type of an ACK PDU.
Definition: AckPdu.hpp:18
Pairs an offset with a size to identify a specific piece of a file.
Definition: Chunk.hpp:49
void log_WARNING_LO_FailNakPduDeserialization(U8 channelId, I32 status) const
Log event FailNakPduDeserialization.
EntityId peer_eid
peer_eid is always the "other guy", same src_eid for RX
Definition: Types.hpp:250