F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
Engine.cpp
Go to the documentation of this file.
1 // ======================================================================
2 // \title Engine.cpp
3 // \brief CFDP Engine implementation
4 //
5 // This file is a port of CFDP engine 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.c (CFDP PDU validation, processing, and engine operations)
9 //
10 // This file contains two sets of functions. The first is what is needed
11 // to deal with CFDP PDUs. Specifically validating them for correctness
12 // and ensuring the byte-order is correct for the target. The second
13 // is incoming and outgoing CFDP PDUs pass through here. All receive
14 // CFDP PDU logic is performed here and the data is passed to the
15 // R (rx) and S (tx) logic.
16 //
17 // ======================================================================
18 //
19 // NASA Docket No. GSC-18,447-1
20 //
21 // Copyright (c) 2019 United States Government as represented by the
22 // Administrator of the National Aeronautics and Space Administration.
23 // All Rights Reserved.
24 //
25 // Licensed under the Apache License, Version 2.0 (the "License"); you may
26 // not use this file except in compliance with the License. You may obtain
27 // a copy of the License at
28 //
29 // http://www.apache.org/licenses/LICENSE-2.0
30 //
31 // Unless required by applicable law or agreed to in writing, software
32 // distributed under the License is distributed on an "AS IS" BASIS,
33 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
34 // See the License for the specific language governing permissions and
35 // limitations under the License.
36 //
37 // ======================================================================
38 
39 #include <string.h>
40 #include <new>
41 
42 #include <Fw/Types/StringUtils.hpp>
43 #include <Os/FileSystem.hpp>
44 
51 
52 namespace Svc {
53 namespace Ccsds {
54 namespace Cfdp {
55 
56 // ----------------------------------------------------------------------
57 // Construction and destruction
58 // ----------------------------------------------------------------------
59 
60 Engine::Engine(CfdpManager* manager) : m_manager(manager), m_seqNum(0), m_allocator(nullptr), m_allocatorId(0) {
61  for (U8 i = 0; i < Cfdp::NumChannels; ++i) {
62  m_channels[i] = nullptr;
63  }
64 }
65 
67  FW_ASSERT(m_allocator != nullptr, 0); // init() must have been called
68 
69  for (U8 i = 0; i < Cfdp::NumChannels; ++i) {
70  if (m_channels[i] != nullptr) {
71  // Clean up Channel's internal arrays first
72  m_channels[i]->cleanup(*m_allocator, m_allocatorId);
73 
74  // Call destructor
75  m_channels[i]->~Channel();
76 
77  // Deallocate the Channel object itself
78  m_allocator->deallocate(m_allocatorId, m_channels[i]);
79  m_channels[i] = nullptr;
80  }
81  }
82 }
83 
84 // ----------------------------------------------------------------------
85 // Public interface methods
86 // ----------------------------------------------------------------------
87 
89  // Store allocator for cleanup in destructor
90  m_allocator = &allocator;
91  m_allocatorId = memId;
92 
93  // Allocate and construct all channels using the allocator
94  for (U8 i = 0; i < Cfdp::NumChannels; ++i) {
95  FwSizeType channelSize = sizeof(Channel);
96  void* channelMem = allocator.allocate(memId, channelSize);
97  FW_ASSERT(channelMem != nullptr);
98 
99  // Use placement new to construct Channel in allocated memory
100  m_channels[i] = new (channelMem) Channel(this, i, this->m_manager, allocator, memId);
101  }
102 }
103 
105  txn->m_ack_timer.setTimer(txn->m_cfdpManager->getAckTimerParam(txn->m_chan_num));
106  txn->m_flags.com.ack_timer_armed = true;
107 }
108 
110  U32 timerDuration = 0;
111 
112  // select timeout based on the state
114  // in an active transaction, we expect traffic so use the normal inactivity timer
115  timerDuration = txn->m_cfdpManager->getInactivityTimerParam(txn->m_chan_num);
116  } else {
117  // in an inactive transaction, we do NOT expect traffic, and this timer is now used
118  // just in case any late straggler PDUs dp get delivered. In this case the
119  // time should be longer than the retransmit time (ack timer) but less than the full
120  // inactivity timer (because again, we are not expecting traffic, so waiting the full
121  // timeout would hold resources longer than needed). Using double the ack timer should
122  // ensure that if the remote retransmitted anything, we will see it, and avoids adding
123  // another config option just for this.
124  timerDuration = txn->m_cfdpManager->getAckTimerParam(txn->m_chan_num) * 2;
125  }
126 
127  txn->m_inactivity_timer.setTimer(timerDuration);
128 }
129 
130 void Engine::dispatchRecv(Transaction* txn, const Fw::Buffer& buffer) {
131  // Loop to handle state transitions without recursion
132  // The loop allows recvInit to transition to R2 state and re-dispatch
133  bool needsDispatch = true;
134  while (needsDispatch) {
135  needsDispatch = false; // Assume single dispatch unless state handler requests re-dispatch
136 
137  // Dispatch based on transaction state
138  switch (txn->m_state) {
140  needsDispatch = this->recvInit(txn, buffer);
141  break;
143  txn->r1Recv(buffer);
144  break;
146  txn->s1Recv(buffer);
147  break;
149  txn->r2Recv(buffer);
150  break;
152  txn->s2Recv(buffer);
153  break;
155  this->recvDrop(txn, buffer);
156  break;
158  this->recvHold(txn, buffer);
159  break;
160  default:
161  // Invalid or undefined state
162  break;
163  }
164  }
165 
166  this->armInactTimer(txn); // whenever a packet was received by the other size, always arm its inactivity timer
167 }
168 
170  static const TxnSendDispatchTable state_fns = {{
171  nullptr, // TxnState::TXN_STATE_UNDEF
172  nullptr, // TxnState::TXN_STATE_INIT
173  nullptr, // TxnState::TXN_STATE_R1
174  &Transaction::s1Tx, // TxnState::TXN_STATE_S1
175  nullptr, // TxnState::TXN_STATE_R2
176  &Transaction::s2Tx, // TxnState::TXN_STATE_S2
177  nullptr, // TxnState::TXN_STATE_DROP
178  nullptr // TxnState::TXN_STATE_HOLD
179  }};
180 
181  txn->txStateDispatch(&state_fns);
182 }
183 
185  FW_ASSERT((txn->m_state == TxnState::TXN_STATE_S1) || (txn->m_state == TxnState::TXN_STATE_S2),
186  static_cast<U8>(txn->m_state));
187  FW_ASSERT(txn->m_chan != nullptr);
188 
189  // Create and initialize Metadata PDU
190  MetadataPdu md;
191 
192  // Set closure requested flag based on transaction class
193  // Class 1: closure not requested (0), Class 2: closure requested (1)
194  U8 closureRequested = (txn->m_state == TxnState::TXN_STATE_S2) ? 1 : 0;
195 
196  // Direction is toward receiver for metadata PDU sent by sender
198 
199  md.initialize(direction,
200  txn->getClass(), // transmission mode (Class 1 or 2)
201  m_manager->getLocalEidParam(), // source EID
202  txn->m_history->seq_num, // transaction sequence number
203  txn->m_history->peer_eid, // destination EID
204  txn->m_fsize, // file size
205  txn->m_history->fnames.src_filename, // source filename
206  txn->m_history->fnames.dst_filename, // destination filename
207  ChecksumType::CHECKSUM_TYPE_MODULAR, // checksum type
208  closureRequested // closure requested flag
209  );
210 
211  return serializeAndSendPdu(txn, md);
212 }
213 
215  Status::T status = serializeAndSendPdu(txn, fdPdu);
216  if (status == Cfdp::Status::SUCCESS) {
217  m_manager->addSentFileDataBytes(txn->getChannelId(), fdPdu.getDataSize());
218  }
219  return status;
220 }
221 
223  // Create and initialize EOF PDU
224  EofPdu eof;
225 
226  // Direction is toward receiver for EOF sent by sender
228  ConditionCode conditionCode = static_cast<ConditionCode>(TxnStatusToConditionCode(txn->m_history->txn_stat));
229 
230  // Increment sent EOF counters based on condition code
232  this->m_manager->incrementSentEofCanceled(txn->getChannelId());
233  } else if (conditionCode != ConditionCode::CONDITION_CODE_NO_ERROR) {
234  this->m_manager->incrementFaultTxEofError(txn->getChannelId());
235  }
236 
237  eof.initialize(direction,
238  txn->getClass(), // transmission mode
239  m_manager->getLocalEidParam(), // source EID
240  txn->m_history->seq_num, // transaction sequence number
241  txn->m_history->peer_eid, // destination EID
242  conditionCode, // condition code
243  txn->m_crc.getValue(), // checksum
244  txn->m_fsize // file size
245  );
246 
247  // Add entity ID TLV on error conditions (optional per CCSDS spec)
248  if (conditionCode != ConditionCode::CONDITION_CODE_NO_ERROR) {
249  Cfdp::Tlv tlv;
250  tlv.initialize(m_manager->getLocalEidParam()); // Local entity ID
251  eof.appendTlv(tlv);
252  }
253 
254  return serializeAndSendPdu(txn, eof);
255 }
256 
258  AckTxnStatus ts,
259  FileDirective dir_code,
260  ConditionCode cc,
261  EntityId peer_eid,
262  TransactionSeq tsn) {
263  FW_ASSERT(
265  static_cast<U8>(dir_code));
266 
267  // Determine source and destination EIDs based on transaction direction
268  EntityId src_eid;
269  EntityId dst_eid;
270  if (txn->getHistory()->dir == Direction::DIRECTION_TX) {
271  src_eid = m_manager->getLocalEidParam();
272  dst_eid = peer_eid;
273  } else {
274  src_eid = peer_eid;
275  dst_eid = m_manager->getLocalEidParam();
276  }
277 
278  // Create and initialize ACK PDU
279  AckPdu ack;
280 
281  // Direction: toward sender for EOF ACK, toward receiver for FIN ACK
285 
286  ack.initialize(direction,
287  txn->getClass(), // transmission mode
288  src_eid, // source EID
289  tsn, // transaction sequence number
290  dst_eid, // destination EID
291  dir_code, // directive being acknowledged
292  1, // directive subtype code (always 1)
293  cc, // condition code
294  ts // transaction status
295  );
296 
297  return serializeAndSendPdu(txn, ack);
298 }
299 
301  // Create and initialize FIN PDU
302  FinPdu fin;
303 
304  // Direction is toward sender for FIN sent by receiver
306 
307  fin.initialize(direction,
308  txn->getClass(), // transmission mode
309  txn->m_history->peer_eid, // source EID (receiver)
310  txn->m_history->seq_num, // transaction sequence number
311  m_manager->getLocalEidParam(), // destination EID (sender)
312  cc, // condition code
313  static_cast<FinDeliveryCode>(dc), // delivery code
314  static_cast<FinFileStatus>(fs) // file status
315  );
316 
317  // Add entity ID TLV on error conditions (optional per CCSDS spec)
319  Cfdp::Tlv tlv;
320  tlv.initialize(m_manager->getLocalEidParam()); // Local entity ID
321  fin.appendTlv(tlv);
322  }
323 
324  return serializeAndSendPdu(txn, fin);
325 }
326 
328  // Verify this is a Class 2 transaction (NAK only used in Class 2)
329  Class::T tx_class = txn->getClass();
330  FW_ASSERT(tx_class == Cfdp::Class::CLASS_2, tx_class);
331 
332  return serializeAndSendPdu(txn, nakPdu);
333 }
334 
335 Status::T Engine::serializeAndSendPdu(Transaction* txn, PduBase& pdu) {
336  // Delegate to the channel-based helper; a transaction always carries its channel.
337  return this->serializeAndSendPduOnChannel(*txn->m_chan, pdu);
338 }
339 
340 Status::T Engine::serializeAndSendPduOnChannel(Channel& chan, PduBase& pdu) {
341  Fw::Buffer buffer;
343 
344  // Allocate buffer with space for packet descriptor
345  const FwSizeType bufferSize = pdu.getBufferSize() + CfdpManager::PACKET_DESCRIPTOR_SIZE;
346  status = m_manager->getPduBuffer(buffer, chan, bufferSize);
347 
348  if (status == Cfdp::Status::SUCCESS) {
349  // Serialize to buffer at offset to leave room for descriptor
352  Fw::SerializeStatus serStatus = pdu.serializeTo(sb);
353 
354  if (serStatus != Fw::FW_SERIALIZE_OK) {
355  // Log generic PDU serialization error with PDU type
356  m_manager->log_WARNING_LO_FailPduSerialization(chan.getChannelId(), pdu.getType(),
357  static_cast<I32>(serStatus));
358  m_manager->returnPduBuffer(chan, buffer);
359  status = Cfdp::Status::ERROR;
360  } else {
361  // Update buffer size to actual serialized size plus descriptor
362  buffer.setSize(sb.getSize() + CfdpManager::PACKET_DESCRIPTOR_SIZE);
363  m_manager->sendPduBuffer(chan, buffer);
364  m_manager->incrementSentPdu(chan.getChannelId());
365  }
366  }
367 
368  return status;
369 }
370 
371 Status::T Engine::sendFinAckStateless(Channel& chan,
372  TransactionSeq tsn,
373  EntityId finSrcEid,
374  EntityId finDstEid,
375  ConditionCode cc) {
376  // A FIN has arrived for a downlink transaction we sourced, but no live transaction
377  // remains (it already completed and was recycled). Per CFDP the sender must still
378  // acknowledge a retransmitted FIN; we do so statelessly from the FIN header.
379  //
380  // The FIN was sent toward us (the sender), so its header carries
381  // sourceEid = the transaction source = this local entity,
382  // destEid = the peer (receiver).
383  // The ACK(FIN) we emit travels toward the receiver, mirroring Engine::sendAck's
384  // DIRECTION_TX case: src = local entity, dst = peer.
385  AckPdu ack;
387  Cfdp::Class::CLASS_2, // FIN/ACK only exist in class 2
388  finSrcEid, // source EID (this local entity)
389  tsn, // transaction sequence number
390  finDstEid, // destination EID (the peer/receiver)
391  FileDirective::FILE_DIRECTIVE_FIN, // directive being acknowledged
392  1, // directive subtype code (always 1)
393  cc, // echo the FIN's condition code
394  AckTxnStatus::ACK_TXN_STATUS_UNRECOGNIZED // we no longer recognize this transaction
395  );
396 
397  return this->serializeAndSendPduOnChannel(chan, ack);
398 }
399 
400 void Engine::recvMd(Transaction* txn, const MetadataPdu& md) {
401  /* store the expected file size in transaction */
402  txn->m_fsize = md.getFileSize();
403 
404  /* store the filenames in transaction - validation already done during deserialization */
405  txn->m_history->fnames.src_filename = md.getSourceFilename();
406  txn->m_history->fnames.dst_filename = md.getDestFilename();
407 
408  this->m_manager->log_ACTIVITY_LO_MetadataReceived(txn->m_history->fnames.src_filename,
409  txn->m_history->fnames.dst_filename, txn->m_history->seq_num);
410 }
411 
414 
415  // Extract header
416  const Cfdp::PduHeader& header = fd.asHeader();
417 
418  // Check for segment metadata flag (not currently supported)
419  if (header.hasSegmentMetadata()) {
420  /* If recv PDU has the "segment_meta_flag" set, this is not currently handled in CF. */
421  this->m_manager->log_WARNING_LO_FileDataSegmentMetadata();
423  this->m_manager->incrementRecvErrors(txn->getChannelId());
424  ret = Cfdp::Status::ERROR;
425  }
426 
427  return ret;
428 }
429 
431  // EOF PDU has been validated during fromBuffer()
432 
433  // Process TLVs if present
434  const Cfdp::TlvList& tlvList = eofPdu.getTlvList();
435  for (U8 i = 0; i < tlvList.getNumTlv(); i++) {
436  const Cfdp::Tlv& tlv = tlvList.getTlv(i);
438  // Entity ID TLV present - validation not currently performed
439  // Future enhancement: Add validation or logging if required
440  }
441  // Other TLV types can be processed here in the future
442  }
443 
444  return Cfdp::Status::SUCCESS;
445 }
446 
448  // FIN PDU has been validated during fromBuffer()
449 
450  // Process TLVs if present
451  const Cfdp::TlvList& tlvList = finPdu.getTlvList();
452  for (U8 i = 0; i < tlvList.getNumTlv(); i++) {
453  const Cfdp::Tlv& tlv = tlvList.getTlv(i);
455  // Entity ID TLV present - validation not currently performed
456  // Future enhancement: Add validation or logging if required
457  }
458  // Other TLV types can be processed here in the future
459  }
460 
461  return Cfdp::Status::SUCCESS;
462 }
463 
465  // NAK PDU has been validated during fromBuffer()
466  return Cfdp::Status::SUCCESS;
467 }
468 
469 void Engine::recvDrop(Transaction* txn, const Fw::Buffer& buffer) {
470  this->m_manager->incrementRecvDropped(txn->getChannelId());
471  (void)buffer; // Unused - we're just dropping the PDU
472 }
473 
474 void Engine::recvHold(Transaction* txn, const Fw::Buffer& buffer) {
475  // anything received in this state is considered spurious
476  this->m_manager->incrementRecvSpurious(txn->getChannelId());
477 
478  //
479  // Normally we do not expect PDUs for a transaction in holdover, because
480  // from the local point of view it is completed and done. But the reason
481  // for the holdover is because the remote side might not have gotten all
482  // the acks and could still be [re-]sending us PDUs for anything it does
483  // not know we got already.
484  //
485  // If an R2 sent FIN, it's possible that the peer missed the
486  // FIN-ACK and is sending another FIN. In that case we need to send
487  // another ACK.
488  //
489 
490  // currently the only thing we will re-ack is the FIN.
491 
492  // Use peekPduType to determine the PDU type
493  Cfdp::PduTypeEnum::T pduType = Cfdp::peekPduType(buffer);
494 
495  // Check if this is a FIN PDU for a Class 2 transaction
496  if (pduType == Cfdp::PduTypeEnum::FINISHED && txn->getClass() == Cfdp::Class::CLASS_2) {
497  // Deserialize FIN PDU
498  FinPdu fin;
499  Fw::SerialBuffer sb2(const_cast<U8*>(buffer.getData()), buffer.getSize());
500  sb2.setBuffLen(buffer.getSize());
501 
502  Fw::SerializeStatus deserStatus = fin.deserializeFrom(sb2);
503  if (deserStatus == Fw::FW_SERIALIZE_OK) {
504  // Re-send the FIN-ACK
506  fin.getConditionCode(), txn->m_history->peer_eid, txn->m_history->seq_num);
507  }
508  // Note: Deserialization errors are silently ignored in hold state
509  // as we're just trying to be helpful by re-acknowledging FIN if we can
510  }
511 }
512 
513 bool Engine::recvInit(Transaction* txn, const Fw::Buffer& buffer) {
514  // Use peekPduType to determine the PDU type before deserializing
515  Cfdp::PduTypeEnum::T pduType = Cfdp::peekPduType(buffer);
516 
517  // First parse header to get transaction information
518  // const_cast: Fw::SerialBuffer requires non-const U8* even for deserialization (read-only)
519  Fw::SerialBuffer sb(const_cast<U8*>(buffer.getData()), buffer.getSize());
520  sb.setBuffLen(buffer.getSize());
521 
522  Cfdp::PduHeader header;
523  Fw::SerializeStatus status = header.fromSerialBuffer(sb);
524 
525  if (status == Fw::FW_SERIALIZE_OK) {
526  TransactionSeq transactionSeq = header.getTransactionSeq();
527  EntityId sourceEid = header.getSourceEid();
528  Class::T txmMode = header.getTxmMode();
529 
530  // only RX transactions dare tread here
531  txn->m_history->seq_num = transactionSeq;
532 
533  // peer_eid is always the remote partner. src_eid is always the transaction source.
534  // in this case, they are the same
535  txn->m_history->peer_eid = sourceEid;
536  txn->m_history->src_eid = sourceEid;
537 
538  // all RX transactions will need a chunk list to track file segments
539  if (txn->m_chunks == nullptr) {
540  txn->m_chunks = txn->m_chan->findUnusedChunks(Direction::DIRECTION_RX);
541  }
542  if (txn->m_chunks == nullptr) {
543  this->m_manager->log_WARNING_LO_ChunklistUnavailable(transactionSeq);
544  } else {
545  if (pduType == Cfdp::PduTypeEnum::FILE_DATA) {
546  // file data PDU
547  // being idle and receiving a file data PDU means that no active transaction knew
548  // about the transaction in progress, so most likely PDUs were missed.
549 
550  if (txmMode == Cfdp::Class::CLASS_1) {
551  // R1, can't do anything without metadata first
552  txn->m_state = TxnState::TXN_STATE_DROP; // drop all incoming
553  // use inactivity timer to ultimately free the state
554  } else {
555  // R2 can handle missing metadata, so go ahead and create a temp file
556  txn->m_state = TxnState::TXN_STATE_R2;
557  txn->m_txn_class = Cfdp::Class::CLASS_2;
558  txn->rInit();
559  return true; // Request re-dispatch to enter r2 state handler
560  }
561  } else if (pduType == Cfdp::PduTypeEnum::METADATA) {
562  // file directive PDU with metadata - this is the expected case for starting a new RX transaction
563  MetadataPdu md;
564  Fw::SerialBuffer sb2(const_cast<U8*>(buffer.getData()), buffer.getSize());
565  sb2.setBuffLen(buffer.getSize());
566 
567  Fw::SerializeStatus deserStatus = md.deserializeFrom(sb2);
568  if (deserStatus == Fw::FW_SERIALIZE_OK) {
569  this->recvMd(txn, md);
570 
571  // NOTE: whether or not class 1 or 2, get a free chunks. It's cheap, and simplifies cleanup path
573  txn->m_txn_class = txmMode;
574  txn->m_flags.rx.md_recv = true;
575  txn->rInit(); // initialize R
576  } else {
577  m_manager->log_WARNING_LO_FailMetadataPduDeserialization(txn->getChannelId(),
578  static_cast<I32>(deserStatus));
579  }
580  } else {
581  // Unexpected PDU type in init state
582  this->m_manager->log_WARNING_LO_UnhandledPduInIdleState();
583  this->m_manager->incrementRecvErrors(txn->getChannelId());
584  }
585  }
586 
587  if (txn->m_state == TxnState::TXN_STATE_INIT) {
588  // state was not changed, so free the transaction
589  this->finishTransaction(txn, false);
590  }
591  } else {
592  m_manager->log_WARNING_LO_FailPduHeaderDeserialization(txn->getChannelId(), status);
593  }
594  return false; // No re-dispatch needed
595 }
596 
597 void Engine::receivePdu(U8 chan_id, const Fw::Buffer& buffer) {
598  Transaction* txn = nullptr;
599  Channel* chan = nullptr;
600 
601  FW_ASSERT(chan_id < Cfdp::NumChannels, chan_id, Cfdp::NumChannels);
602 
603  chan = m_channels[chan_id];
604  FW_ASSERT(chan != nullptr);
605 
606  // Parse the header to get transaction routing info
607  // Avoid full PDU deserialization here to defer it until the appropriate handler
608  // const_cast: Fw::SerialBuffer requires non-const U8* even for deserialization (read-only)
609  Fw::SerialBuffer sb(const_cast<U8*>(buffer.getData()), buffer.getSize());
610  sb.setBuffLen(buffer.getSize());
611 
612  Cfdp::PduHeader header;
613  Fw::SerializeStatus status = header.fromSerialBuffer(sb);
614 
615  if (status == Fw::FW_SERIALIZE_OK) {
616  // Increment received PDU counter for PDUs with valid headers
617  this->m_manager->incrementRecvPdu(chan_id);
618 
619  TransactionSeq transactionSeq = header.getTransactionSeq();
620  EntityId sourceEid = header.getSourceEid();
621  EntityId destEid = header.getDestEid();
622 
623  // Look up transaction by sequence number
624  txn = chan->findTransactionBySequenceNumber(transactionSeq, sourceEid);
625 
626  if (txn == nullptr) {
627  // A retransmitted FIN can arrive for a downlink transaction we sourced after
628  // that transaction has already completed and been recycled (e.g. the peer
629  // kept retransmitting FIN across a lossy/quiet link). Per CFDP the sender
630  // must still acknowledge it, so re-ACK statelessly from the FIN header.
631  // Such a FIN carries sourceEid == our local entity (we were the source) and
632  // destEid == the peer, so it would otherwise be dropped as InvalidDestinationEid.
634  sourceEid == this->m_manager->getLocalEidParam()) {
635  FinPdu fin;
636  Fw::SerialBuffer finSb(const_cast<U8*>(buffer.getData()), buffer.getSize());
637  finSb.setBuffLen(buffer.getSize());
638  if (fin.deserializeFrom(finSb) == Fw::FW_SERIALIZE_OK) {
639  this->sendFinAckStateless(*chan, transactionSeq, sourceEid, destEid, fin.getConditionCode());
640  this->m_manager->log_DIAGNOSTIC_TxLateFinAcked(sourceEid, transactionSeq);
641  } else {
643  chan_id, static_cast<I32>(Fw::FW_DESERIALIZE_FORMAT_ERROR));
644  }
645  }
646  // if no match found, then it must be the case that we would be the destination entity id, so verify it
647  else if (destEid == this->m_manager->getLocalEidParam()) {
648  // we didn't find a match, so assign it to a transaction
649  // assume this is initiating an RX transaction, as TX transactions are only commanded
650  txn = this->startRxTransaction(chan->getChannelId());
651  if (txn == nullptr) {
652  this->m_manager->log_WARNING_LO_RxTransactionLimitReached(sourceEid, transactionSeq);
653  }
654  } else {
655  this->m_manager->log_WARNING_LO_InvalidDestinationEid(destEid);
656  }
657  }
658 
659  if (txn != nullptr) {
660  // found one! Send it to the transaction state processor
661  this->dispatchRecv(txn, buffer);
662  } else {
663  // Transaction limit reached - EVR already emitted by findOrStartRxTransaction
664  }
665  } else {
666  // Invalid PDU header, drop packet
667  m_manager->log_WARNING_LO_FailPduHeaderDeserialization(chan_id, static_cast<I32>(status));
668  }
669 }
670 
671 void Engine::setChannelFlowState(U8 channelId, Flow::T flowState) {
672  FW_ASSERT(channelId < Cfdp::NumChannels, channelId, Cfdp::NumChannels);
673  m_channels[channelId]->setFlowState(flowState);
674 }
675 
677  TransactionSeq transactionSeq,
678  EntityId entityId,
679  SuspendResume::T action) {
680  Status::T status = Status::ERROR;
681 
682  FW_ASSERT(channelId < Cfdp::NumChannels, channelId, Cfdp::NumChannels);
683 
684  Channel* chan = m_channels[channelId];
685  Transaction* txn = chan->findTransactionBySequenceNumber(transactionSeq, entityId);
686 
687  if (txn != nullptr) {
688  txn->m_flags.com.suspended = (action == SuspendResume::SUSPEND);
689  status = Status::SUCCESS;
690  }
691 
692  return status;
693 }
694 
695 Status::T Engine::cancelTransactionBySeq(U8 channelId, TransactionSeq transactionSeq, EntityId entityId) {
696  Status::T status = Status::ERROR;
697 
698  FW_ASSERT(channelId < Cfdp::NumChannels, channelId, Cfdp::NumChannels);
699 
700  Channel* chan = m_channels[channelId];
701  Transaction* txn = chan->findTransactionBySequenceNumber(transactionSeq, entityId);
702 
703  if (txn != nullptr) {
704  this->cancelTransaction(txn);
705  status = Status::SUCCESS;
706  }
707 
708  return status;
709 }
710 
711 Status::T Engine::abandonTransaction(U8 channelId, TransactionSeq transactionSeq, EntityId entityId) {
712  Status::T status = Status::ERROR;
713 
714  FW_ASSERT(channelId < Cfdp::NumChannels, channelId, Cfdp::NumChannels);
715 
716  Channel* chan = m_channels[channelId];
717  Transaction* txn = chan->findTransactionBySequenceNumber(transactionSeq, entityId);
718 
719  if (txn != nullptr) {
720  this->finishTransaction(txn, false);
721  status = Status::SUCCESS;
722  }
723 
724  return status;
725 }
726 
728  Class::T cfdp_class,
729  Keep::T keep,
730  U8 chan,
731  U8 priority,
732  EntityId dest_id) {
733  txn->initTxFile(cfdp_class, keep, chan, priority);
734 
735  // Increment sequence number for new transaction
736  ++this->m_seqNum;
737 
738  // Capture info for history
739  txn->m_history->seq_num = this->m_seqNum;
740  txn->m_history->src_eid = m_manager->getLocalEidParam();
741  txn->m_history->peer_eid = dest_id;
742 
743  txn->m_chan->insertSortPrio(txn, QueueId::PEND);
744 }
745 
746 Status::T Engine::txFile(const Fw::String& src_filename,
747  const Fw::String& dst_filename,
748  Class::T cfdp_class,
749  Keep::T keep,
750  U8 chan_num,
751  U8 priority,
752  EntityId dest_id,
753  TransactionInitType initType) {
754  Transaction* txn;
755  Channel* chan = nullptr;
756 
757  FW_ASSERT(chan_num < Cfdp::NumChannels, chan_num, Cfdp::NumChannels);
758  chan = m_channels[chan_num];
759 
761 
764  } else {
765  txn = nullptr;
766  }
767 
768  if (txn == nullptr) {
769  this->m_manager->log_WARNING_LO_MaxTxTransactionsReached();
770  ret = Cfdp::Status::ERROR;
771  } else {
772  // NOTE: the caller of this function ensures the provided src and dst filenames are nullptr terminated
773 
774  txn->m_history->fnames.src_filename = src_filename;
775  txn->m_history->fnames.dst_filename = dst_filename;
776  this->txFileInitiate(txn, cfdp_class, keep, chan_num, priority, dest_id);
777 
778  chan->incrementCmdTxCounter();
779  txn->m_flags.tx.cmd_tx = true;
780 
781  // Set transaction initiation type
782  txn->m_initType = initType;
783 
784  // Log transaction queued event
785  this->m_manager->log_ACTIVITY_LO_TxFileQueued(txn->m_history->fnames.src_filename, txn->m_history->seq_num);
786  }
787 
788  return ret;
789 }
790 
791 Transaction* Engine::startRxTransaction(U8 chan_num) {
792  Channel* chan = nullptr;
793  Transaction* txn;
794 
795  FW_ASSERT(chan_num < Cfdp::NumChannels, chan_num, Cfdp::NumChannels);
796  chan = m_channels[chan_num];
797 
798  // if (CF_AppData.hk.Payload.channel_hk[chan_num].q_size[QueueId::RX] < CF_MAX_SIMULTANEOUS_RX)
799  // {
800  // txn = chan->findUnusedTransaction(Direction::DIRECTION_RX);
801  // }
802  // else
803  // {
804  // txn = nullptr;
805  // }
806  // Receive transactions are limited by MaxRxTransactions parameter
808 
809  if (txn != nullptr) {
810  // set default FIN status
813 
814  txn->m_flags.com.q_index = QueueId::RX;
815  chan->insertBackInQueue(static_cast<QueueId::T>(txn->m_flags.com.q_index), &txn->m_cl_node);
816  }
817 
818  return txn;
819 }
820 
822  const Fw::String& src_filename,
823  const Fw::String& dst_filename,
824  Class::T cfdp_class,
825  Keep::T keep,
826  U8 chan,
827  U8 priority,
828  EntityId dest_id) {
830  Os::Directory::Status dirStatus;
831 
832  // make sure the directory can be open
833  dirStatus = pb->dir.open(src_filename.toChar(), Os::Directory::READ);
834  if (dirStatus != Os::Directory::OP_OK) {
835  this->m_manager->log_WARNING_LO_PlaybackDirOpenFailed(src_filename, dirStatus);
836  this->m_manager->incrementFaultDirectoryRead(chan);
837  status = Cfdp::Status::ERROR;
838  } else {
839  pb->diropen = true;
840  pb->busy = true;
841  pb->keep = keep;
842  pb->priority = priority;
843  pb->dest_id = dest_id;
844  pb->cfdp_class = cfdp_class;
845 
846  // NOTE: the caller of this function ensures the provided src and dst filenames are nullptr terminated
847  pb->fnames.src_filename = src_filename;
848  pb->fnames.dst_filename = dst_filename;
849  }
850 
851  // the executor will start the transfer next cycle
852  return status;
853 }
854 
856  const Fw::String& dst_filename,
857  Class::T cfdp_class,
858  Keep::T keep,
859  U8 chan,
860  U8 priority,
861  EntityId dest_id) {
862  U32 i;
863  Playback* pb;
864  Status::T status;
865 
866  // Loop through the channel's playback directories to find an open slot
867  for (i = 0; i < MaxCommandedPlaybackDirectoriesPerChan; ++i) {
868  pb = m_channels[chan]->getPlayback(i);
869  if (!pb->busy) {
870  break;
871  }
872  }
873 
876  status = Cfdp::Status::ERROR;
877  } else {
878  status = this->playbackDirInitiate(pb, src_filename, dst_filename, cfdp_class, keep, chan, priority, dest_id);
879  }
880 
881  return status;
882 }
883 
885  U8 pollId,
886  const Fw::String& srcDir,
887  const Fw::String& dstDir,
888  Class::T cfdp_class,
889  U8 priority,
890  EntityId destEid,
891  U32 intervalSec) {
893  CfdpPollDir* pd = nullptr;
894 
895  FW_ASSERT(chanId < Cfdp::NumChannels, chanId, Cfdp::NumChannels);
897 
898  // First check if the poll directory is already in use
899  pd = m_channels[chanId]->getPollDir(pollId);
900  if (pd->enabled == Fw::Enabled::DISABLED) {
901  // Populate arguments
902  pd->intervalSec = intervalSec;
903  pd->priority = priority;
904  pd->cfdpClass = cfdp_class;
905  pd->destEid = destEid;
906  pd->srcDir = srcDir;
907  pd->dstDir = dstDir;
908 
909  // Set timer and enable polling
912  } else {
913  // Poll directory slot already in use
914  this->m_manager->log_WARNING_LO_PollDirBusy(chanId, pollId);
915  status = Cfdp::Status::ERROR;
916  }
917 
918  return status;
919 }
920 
923  CfdpPollDir* pd = nullptr;
924 
925  FW_ASSERT(chanId < Cfdp::NumChannels, chanId, Cfdp::NumChannels);
927 
928  // Check if the poll directory is in use
929  pd = m_channels[chanId]->getPollDir(pollId);
930  if (pd->enabled == Fw::Enabled::ENABLED) {
931  // Clear poll directory arguments
932  pd->intervalSec = 0;
933  pd->priority = 0;
934  pd->cfdpClass = static_cast<Class::T>(0);
935  pd->destEid = static_cast<EntityId>(0);
936  pd->srcDir = "";
937  pd->dstDir = "";
938 
939  // Disable timer and polling
942  } else {
943  // Poll directory not active - cannot stop
944  this->m_manager->log_WARNING_LO_PollDirNotActive(chanId, pollId);
945  status = Cfdp::Status::ERROR;
946  }
947 
948  return status;
949 }
950 
951 void Engine::cycle(void) {
952  U32 i;
953 
954  for (i = 0; i < Cfdp::NumChannels; ++i) {
955  Channel* chan = m_channels[i];
956  FW_ASSERT(chan != nullptr);
957 
958  chan->resetOutgoingCounter();
959 
960  if (chan->getFlowState() == Cfdp::Flow::NOT_FROZEN) {
961  // handle ticks before tx cycle. Do this because there may be a limited number of TX messages available
962  // this cycle, and it's important to respond to class 2 ACK/NAK more than it is to send new filedata
963  // PDUs.
964 
965  // cycle all transactions (tick)
966  chan->tickTransactions();
967 
968  // cycle the current tx transaction
969  chan->cycleTx();
970 
973  }
974  }
975 }
976 
977 void Engine::finishTransaction(Transaction* txn, bool keep_history) {
978  if (txn->m_flags.com.q_index == QueueId::FREE) {
979  this->m_manager->log_DIAGNOSTIC_ResetFreedTransaction();
980  return;
981  }
982 
983  // this should always be
984  FW_ASSERT(txn->m_chan != nullptr);
985 
986  // If this was on the TXA queue (transmit side) then we need to move it out
987  // so the tick processor will stop trying to actively transmit something -
988  // it should move on to the next transaction.
989  //
990  // RX transactions can stay on the RX queue, that does not hurt anything
991  // because they are only triggered when a PDU comes in matching that seq_num
992  // (RX queue is not separated into A/W parts)
993  if (txn->m_flags.com.q_index == QueueId::TXA) {
994  txn->m_chan->dequeueTransaction(txn);
995  txn->m_chan->insertSortPrio(txn, QueueId::TXW);
996  }
997 
998  if (true == txn->m_fd.isOpen()) {
999  txn->m_fd.close();
1000 
1001  if (!txn->m_keep) {
1002  this->handleNotKeepFile(txn);
1003  }
1004  }
1005 
1006  if (txn->m_history != nullptr) {
1007  // Emit completion events for successful transactions
1008  if (!TxnStatusIsError(txn->m_history->txn_stat)) {
1009  if (txn->m_history->dir == Direction::DIRECTION_TX) {
1010  this->m_manager->log_ACTIVITY_HI_TxFileTransferCompleted(
1011  txn->m_txn_class, txn->m_history->seq_num, txn->m_history->src_eid,
1012  txn->m_history->fnames.src_filename, txn->m_history->peer_eid, txn->m_history->fnames.dst_filename,
1013  static_cast<U32>(txn->m_fsize));
1014  } else if (txn->m_history->dir == Direction::DIRECTION_RX) {
1015  this->m_manager->log_ACTIVITY_HI_RxFileTransferCompleted(
1016  txn->m_txn_class, txn->m_history->seq_num, txn->m_history->src_eid,
1017  txn->m_history->fnames.src_filename, m_manager->getLocalEidParam(),
1018  txn->m_history->fnames.dst_filename, static_cast<U32>(txn->m_fsize));
1019  }
1020  } else {
1021  // Log failure events for failed transactions
1022  if (txn->m_history->dir == Direction::DIRECTION_TX) {
1023  this->m_manager->log_WARNING_LO_TxFileTransferFailed(
1024  txn->m_txn_class, txn->m_history->seq_num, txn->m_history->src_eid,
1025  txn->m_history->fnames.src_filename, txn->m_history->peer_eid, txn->m_history->fnames.dst_filename,
1026  static_cast<U8>(txn->m_history->txn_stat));
1027  } else if (txn->m_history->dir == Direction::DIRECTION_RX) {
1028  this->m_manager->log_WARNING_LO_RxFileTransferFailed(
1029  txn->m_txn_class, txn->m_history->seq_num, txn->m_history->src_eid,
1030  txn->m_history->fnames.src_filename, txn->m_history->peer_eid, txn->m_history->fnames.dst_filename,
1031  static_cast<U8>(txn->m_history->txn_stat));
1032  }
1033  }
1034 
1035  // extra bookkeeping for tx direction only
1036  if (txn->m_history->dir == Direction::DIRECTION_TX && txn->m_flags.tx.cmd_tx) {
1037  txn->m_chan->decrementCmdTxCounter();
1038  }
1039 
1040  // Notify via port if this was a port-initiated transfer
1041  if (txn->m_initType == TransactionInitType::INIT_BY_PORT) {
1042  // Map transaction status to SendFileStatus
1043  Svc::SendFileStatus::T status;
1044  if (TxnStatusIsError(txn->m_history->txn_stat)) {
1046  } else {
1048  }
1049 
1050  // Invoke the file complete notification
1051  this->m_manager->sendFileComplete(status);
1052  }
1053 
1054  txn->m_flags.com.keep_history = keep_history;
1055  }
1056 
1057  if (txn->m_pb) {
1058  // a playback's transaction is now done, decrement the playback counter
1059  FW_ASSERT(txn->m_pb->num_ts);
1060  --txn->m_pb->num_ts;
1061  }
1062 
1063  txn->m_chan->clearCurrentIfMatch(txn);
1064 
1065  // Put this transaction into the holdover state, inactivity timer will recycle it
1066  txn->m_state = TxnState::TXN_STATE_HOLD;
1067  this->armInactTimer(txn);
1068 }
1069 
1071  if (!TxnStatusIsError(txn->m_history->txn_stat)) {
1072  txn->m_history->txn_stat = txn_stat;
1073  }
1074 }
1075 
1076 void Engine::cancelTransaction(Transaction* txn) {
1077  void (Transaction::* fns[static_cast<U32>(Direction::DIRECTION_NUM)])() = {nullptr};
1078 
1079  fns[static_cast<U32>(Direction::DIRECTION_RX)] = &Transaction::rCancel;
1080  fns[static_cast<U32>(Direction::DIRECTION_TX)] = &Transaction::sCancel;
1081 
1082  if (!txn->m_flags.com.canceled) {
1083  txn->m_flags.com.canceled = true;
1085 
1086  // this should always be true, just confirming before indexing into array
1087  if (txn->m_history->dir < Direction::DIRECTION_NUM) {
1088  (txn->*fns[static_cast<U32>(txn->m_history->dir)])();
1089  }
1090  }
1091 }
1092 
1093 bool Engine::isPollingDir(const Fw::StringBase& src_file, U8 chan_num) {
1094  bool return_code = false;
1095  Fw::String src_dir;
1096  CfdpPollDir* pd;
1097  U32 i;
1098 
1099  // Extract directory portion (everything before last '/')
1100  FwSizeType lastSlashPos = 0;
1101  bool foundSlash = false;
1102  for (FwSizeType pos = 0; pos < src_file.length(); ++pos) {
1103  if (src_file.toChar()[pos] == '/') {
1104  lastSlashPos = pos;
1105  foundSlash = true;
1106  }
1107  }
1108 
1109  if (foundSlash) {
1110  src_dir.format("%.*s", static_cast<int>(lastSlashPos), src_file.toChar());
1111  }
1112 
1113  for (i = 0; i < MaxPollingDirPerChan; ++i) {
1114  pd = m_channels[chan_num]->getPollDir(i);
1115  if (src_dir == pd->srcDir) {
1116  return_code = true;
1117  break;
1118  }
1119  }
1120 
1121  return return_code;
1122 }
1123 
1124 void Engine::handleNotKeepFile(Transaction* txn) {
1126  Fw::String failDir;
1127  Fw::String moveDir;
1128 
1129  // Sender
1130  if (txn->getHistory()->dir == Direction::DIRECTION_TX) {
1131  if (!TxnStatusIsError(txn->getHistory()->txn_stat)) {
1132  // If move directory is defined attempt move
1133  moveDir = m_manager->getMoveDirParam(txn->getChannelId());
1134  if (moveDir.length() > 0) {
1135  fileStatus = Os::FileSystem::moveFile(txn->m_history->fnames.src_filename.toChar(), moveDir.toChar());
1136  if (fileStatus != Os::FileSystem::OP_OK) {
1137  m_manager->log_WARNING_LO_FailKeepFileMove(txn->m_history->fnames.src_filename, moveDir,
1138  fileStatus);
1139  }
1140  }
1141 
1142  // If move_dir is empty or move failed, delete the file
1143  if (fileStatus != Os::FileSystem::OP_OK) {
1144  fileStatus = Os::FileSystem::removeFile(txn->m_history->fnames.src_filename.toChar());
1145  if (fileStatus != Os::FileSystem::OP_OK) {
1146  m_manager->log_WARNING_LO_FileRemoveFailed(txn->m_history->fnames.src_filename, fileStatus);
1147  }
1148  }
1149  } else {
1150  // file inside a polling directory
1151  if (this->isPollingDir(txn->m_history->fnames.src_filename, txn->getChannelId())) {
1152  // If fail directory is defined attempt move
1153  failDir = m_manager->getFailDirParam(txn->getChannelId());
1154  if (failDir.length() > 0) {
1155  fileStatus =
1156  Os::FileSystem::moveFile(txn->m_history->fnames.src_filename.toChar(), failDir.toChar());
1157  if (fileStatus != Os::FileSystem::OP_OK) {
1158  m_manager->log_WARNING_LO_FailPollFileMove(txn->m_history->fnames.src_filename, failDir,
1159  fileStatus);
1160  }
1161  }
1162 
1163  // If fail_dir is empty or move failed, delete the file
1164  if (fileStatus != Os::FileSystem::OP_OK) {
1165  fileStatus = Os::FileSystem::removeFile(txn->m_history->fnames.src_filename.toChar());
1166  if (fileStatus != Os::FileSystem::OP_OK) {
1167  m_manager->log_WARNING_LO_FileRemoveFailed(txn->m_history->fnames.src_filename, fileStatus);
1168  }
1169  }
1170  }
1171  }
1172  }
1173  // Not Sender
1174  else {
1175  fileStatus = Os::FileSystem::removeFile(txn->m_history->fnames.dst_filename.toChar());
1176  if (fileStatus != Os::FileSystem::OP_OK) {
1177  m_manager->log_WARNING_LO_FileRemoveFailed(txn->m_history->fnames.dst_filename, fileStatus);
1178  }
1179  }
1180 }
1181 
1183  return this->m_manager->getChannelTelemetryRef(channelId);
1184 }
1185 
1186 } // namespace Cfdp
1187 } // namespace Ccsds
1188 } // namespace Svc
void incrementSentPdu(U8 chanId)
Increment sent PDU counter.
Status::T recvFd(Transaction *txn, const FileDataPdu &pdu)
Unpack a file data PDU from a received message.
Definition: Engine.cpp:412
Serialization/Deserialization operation was successful.
CfdpTxnFilenames fnames
file names associated with this history entry
Definition: Types.hpp:245
void s1Recv(const Fw::Buffer &buffer)
S1 receive PDU processing.
Status::T sendEof(Transaction *txn)
Create, encode, and send an EOF (End of File) PDU.
Definition: Engine.cpp:222
TransactionInitType
Transaction initiation method.
Definition: Types.hpp:165
void sendFileComplete(Svc::SendFileStatus::T status)
void incrementRecvErrors(U8 chanId)
Increment receive error counter.
Fw::String dstDir
path to destination dir
Definition: Types.hpp:309
U8 getChannelId() const
Get the channel ID.
Definition: Channel.hpp:178
Status::T txFile(const Fw::String &src, const Fw::String &dst, Class::T cfdp_class, Keep::T keep, U8 chan_num, U8 priority, EntityId dest_id, TransactionInitType initType=TransactionInitType::INIT_BY_COMMAND)
Begin transmit of a file.
Definition: Engine.cpp:746
CFDP Channel class.
Definition: Channel.hpp:56
virtual void * allocate(const FwEnumStoreType identifier, FwSizeType &size, bool &recoverable, FwSizeType alignment=alignof(std::max_align_t))=0
Status::T stopPollDir(U8 chanId, U8 pollId)
Stop polling a directory.
Definition: Engine.cpp:921
A variable-length serializable buffer.
~Channel()
Destruct a Channel.
Definition: Channel.cpp:167
void log_WARNING_LO_FailPollFileMove(const Fw::StringBase &srcFile, const Fw::StringBase &failDir, I32 status) const
Log event FailPollFileMove.
EntityId getDestEid() const
Get the destination entity ID.
Definition: PduHeader.hpp:130
CFDP Transaction state machine class.
Status::T cancelTransactionBySeq(U8 channelId, TransactionSeq transactionSeq, EntityId entityId)
Cancel a transaction with graceful close-out.
Definition: Engine.cpp:695
PlatformSizeType FwSizeType
static Status moveFile(const char *sourcePath, const char *destPath)
Move a file from sourcePath to destPath.
Definition: FileSystem.cpp:209
I32 FwEnumStoreType
void setSize(FwSizeType size)
Definition: Buffer.cpp:75
Enabled state.
U16 num_ts
number of transactions
Definition: Types.hpp:282
void init(Fw::MemAllocator &allocator, FwEnumStoreType memId)
Initialize the CFDP engine.
Definition: Engine.cpp:88
State assigned to a newly allocated transaction object.
void rCancel()
Cancel an R transaction.
void initialize(PduDirection direction, Cfdp::Class::T txmMode, EntityId sourceEid, TransactionSeq transactionSeq, EntityId destEid, ConditionCode conditionCode, U32 checksum, FileSize fileSize)
Initialize an EOF PDU.
Definition: EofPdu.cpp:14
bool cmd_tx
indicates transaction is commanded (ground) tx
Definition: Types.hpp:395
void recvMd(Transaction *txn, const MetadataPdu &pdu)
Handle receipt of metadata PDU.
Definition: Engine.cpp:400
U32 EntityId
Entity id size.
first one on this list is active
void r2Recv(const Fw::Buffer &buffer)
R2 receive PDU processing.
void initialize(PduDirection direction, Cfdp::Class::T txmMode, EntityId sourceEid, TransactionSeq transactionSeq, EntityId destEid, FileDirective directiveCode, U8 directiveSubtypeCode, ConditionCode conditionCode, AckTxnStatus transactionStatus)
Initialize an ACK PDU.
Definition: AckPdu.cpp:14
void log_WARNING_LO_InvalidDestinationEid(U32 destEid) const
Log event InvalidDestinationEid.
void s2Tx()
S2 dispatch function.
void disableTimer(void)
Disables a CFDP timer.
Definition: Timer.cpp:32
void log_WARNING_LO_TxFileTransferFailed(const Svc::Ccsds::Cfdp::Class &cfdpClass, U32 seqNum, U32 srcEid, const Fw::StringBase &srcFile, U32 destEid, const Fw::StringBase &destFile, U8 conditionCode) const
Log event TxFileTransferFailed.
List of TLVs.
Definition: Tlv.hpp:88
ConditionCode getConditionCode() const
Get condition code.
Definition: FinPdu.hpp:65
bool hasSegmentMetadata() const
Check if segment metadata is present.
Definition: PduHeader.hpp:142
U8 * getData() const
Definition: Buffer.cpp:56
bool appendTlv(const Tlv &tlv)
Definition: FinPdu.hpp:78
U8 priority
priority to use when placing transactions on the pending queue
Definition: Types.hpp:304
virtual const CHAR * toChar() const =0
Convert to a C-style char*.
void log_WARNING_LO_ChunklistUnavailable(U32 transactionSeq) const
Log event ChunklistUnavailable.
FileSize getFileSize() const
Get the file size.
Definition: MetadataPdu.hpp:73
TlvType getType() const
Definition: Tlv.cpp:69
Fw::String getFailDirParam(U8 channelIndex)
const Tlv & getTlv(U8 index) const
Definition: Tlv.cpp:206
Deserialization data had incorrect values (unexpected data types)
void incrementFaultDirectoryRead(U8 chanId)
Increment fault directory read counter.
T
The raw enum type.
CFDP class 2 - Reliable transfer (Acknowledged)
Definition: ClassEnumAc.hpp:44
const Fw::String & getDestFilename() const
Get the destination filename.
Definition: MetadataPdu.hpp:79
CFDP class 1 - Unreliable transfer (Unacknowledged)
Definition: ClassEnumAc.hpp:46
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:257
void incrementRecvDropped(U8 chanId)
Increment receive dropped counter.
void processPollingDirectories()
Process all polling directories for this channel.
Definition: Channel.cpp:351
void sendPduBuffer(Channel &channel, Fw::Buffer &pduBuffer)
U32 intervalSec
number of seconds to wait before trying a new directory
Definition: Types.hpp:302
TxnStatus
Values for Transaction Status code.
Definition: Types.hpp:193
Status::T startPollDir(U8 chanId, U8 pollId, const Fw::String &srcDir, const Fw::String &dstDir, Class::T cfdp_class, U8 priority, EntityId destEid, U32 intervalSec)
Start polling a directory.
Definition: Engine.cpp:884
void s2Recv(const Fw::Buffer &buffer)
S2 receive PDU processing.
void log_ACTIVITY_HI_TxFileTransferCompleted(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 TxFileTransferCompleted.
CFDP Playback entry.
Definition: Types.hpp:278
bool appendTlv(const Tlv &tlv)
Definition: EofPdu.hpp:75
void incrementCmdTxCounter()
Increment the command TX counter for this channel.
Definition: Channel.hpp:207
const Fw::String & getSourceFilename() const
Get the source filename.
Definition: MetadataPdu.hpp:76
Fw::SerializeStatus fromSerialBuffer(Fw::SerialBufferBase &serialBuffer)
Initialize this Header from a SerialBufferBase.
Definition: PduHeader.cpp:163
SerializeStatus
forward declaration for string
void incrementRecvSpurious(U8 chanId)
Increment receive spurious counter.
void log_ACTIVITY_LO_MetadataReceived(const Fw::StringBase &srcFile, const Fw::StringBase &destFile, U32 transactionSeq) const
Log event MetadataReceived.
void finishTransaction(Transaction *txn, bool keep_history)
Finish a transaction.
Definition: Engine.cpp:977
U32 TransactionSeq
transaction sequence number size
Flow::T getFlowState() const
Get the flow state for this channel.
Definition: Channel.hpp:243
void returnPduBuffer(Channel &channel, Fw::Buffer &pduBuffer)
State assigned to a transaction after freeing it.
void log_WARNING_LO_MaxTxTransactionsReached() const
Log event MaxTxTransactionsReached.
U8 getChannelId() const
Get channel ID.
U8 getNumTlv() const
Definition: Tlv.cpp:202
bool keep_history
whether history should be preserved during recycle
Definition: Types.hpp:365
T
The raw enum type.
Definition: KeepEnumAc.hpp:36
void cycleTx()
Cycle the TX side of this channel.
Definition: Channel.cpp:211
virtual ~Engine()
Destroy the Engine object.
Definition: Engine.cpp:66
The type of a File Data PDU.
Definition: FileDataPdu.hpp:19
void setChannelFlowState(U8 channelId, Flow::T flowState)
Set channel flow state.
Definition: Engine.cpp:671
Fw::SerializeStatus deserializeFrom(Fw::SerialBufferBase &buffer, Fw::Endianness mode=Fw::Endianness::BIG) override
Fw::Serializable interface - deserialize from buffer.
Definition: FinPdu.cpp:50
Status::T sendNak(Transaction *txn, NakPdu &nakPdu)
Encode and send a NAK (Negative Acknowledgment) PDU.
Definition: Engine.cpp:327
Engine(CfdpManager *manager)
Construct a new Engine object.
Definition: Engine.cpp:60
Status::T getPduBuffer(Fw::Buffer &buffer, Channel &channel, FwSizeType size)
void log_WARNING_LO_FileDataSegmentMetadata() const
Log event FileDataSegmentMetadata.
void resetOutgoingCounter()
Reset the outgoing PDU counter to zero.
Definition: Channel.hpp:195
virtual Status::T sendMd(Transaction *txn)
Create, encode, and send a Metadata PDU.
Definition: Engine.cpp:184
void receivePdu(U8 chan_id, const Fw::Buffer &buffer)
Receive and process a PDU.
Definition: Engine.cpp:597
void log_WARNING_LO_RxFileTransferFailed(const Svc::Ccsds::Cfdp::Class &cfdpClass, U32 seqNum, U32 srcEid, const Fw::StringBase &srcFile, U32 destEid, const Fw::StringBase &destFile, U8 conditionCode) const
Log event RxFileTransferFailed.
Status::T recvNak(Transaction *txn, const NakPdu &pdu)
Unpack a NAK PDU from a received message.
Definition: Engine.cpp:464
void armInactTimer(Transaction *txn)
Arm the inactivity timer for a transaction.
Definition: Engine.cpp:109
void clearCurrentIfMatch(Transaction *txn)
Check if current transaction matches and clear if so.
Definition: Channel.cpp:698
ConditionCode TxnStatusToConditionCode(TxnStatus txn_stat)
Converts the internal transaction status to a CFDP condition code.
Definition: Utils.cpp:120
History * getHistory() const
Get transaction history.
void s1Tx()
S1 dispatch function.
void tickTransactions()
Tick all transactions on this channel.
Definition: Channel.cpp:267
Direction dir
direction of this history entry
Definition: Types.hpp:247
The type of a Metadata PDU.
Definition: MetadataPdu.hpp:20
void dequeueTransaction(Transaction *txn)
Free a transaction from the queue it&#39;s on.
Definition: Channel.cpp:503
Single TLV entry.
Definition: Tlv.hpp:59
The type of a Finished PDU.
Definition: FinPdu.hpp:19
void dispatchTx(Transaction *txn)
Dispatch TX state machine for a transaction.
Definition: Engine.cpp:169
void setTimer(U32 timerDuration)
Initialize a CFDP timer and start its execution.
Definition: Timer.cpp:27
U16 getDataSize() const
Get the data size.
Definition: FileDataPdu.hpp:68
void incrementRecvPdu(U8 chanId)
Increment received PDU counter.
void txFileInitiate(Transaction *txn, Class::T cfdp_class, Keep::T keep, U8 chan, U8 priority, EntityId dest_id)
Initiate a file transfer transaction.
Definition: Engine.cpp:727
void setTxnStatus(Transaction *txn, TxnStatus txn_stat)
Helper function to store transaction status code only.
Definition: Engine.cpp:1070
void close() override
close the file, if not opened then do nothing
Definition: File.cpp:90
Generic CFDP error return code.
const TlvList & getTlvList() const
Get TLV list.
Definition: EofPdu.hpp:78
Status::T sendFd(Transaction *txn, FileDataPdu &fdPdu)
Encode and send a File Data PDU.
Definition: Engine.cpp:214
void incrementSentEofCanceled(U8 chanId)
Increment sent EOF canceled counter.
const char * toChar() const
Convert to a C-style char*.
void addSentFileDataBytes(U8 chanId, U32 bytes)
Add sent file data bytes.
U32 getValue() const
Get the checksum value.
Definition: Checksum.cpp:45
Status::T playbackDirInitiate(Playback *pb, const Fw::String &src_filename, const Fw::String &dst_filename, Class::T cfdp_class, Keep::T keep, U8 chan, U8 priority, EntityId dest_id)
Initiate playback of a directory.
Definition: Engine.cpp:821
Fw::String srcDir
path to source dir
Definition: Types.hpp:308
const TlvList & getTlvList() const
Get TLV list.
Definition: FinPdu.hpp:81
void decrementCmdTxCounter()
Decrement the command TX counter for this channel.
Definition: Channel.cpp:693
FormatStatus format(const CHAR *formatString,...)
write formatted string to buffer
Definition: StringBase.cpp:58
void log_WARNING_LO_RxTransactionLimitReached(U32 srcEid, U32 transactionSeq) const
Log event RxTransactionLimitReached.
void log_WARNING_LO_FailKeepFileMove(const Fw::StringBase &srcFile, const Fw::StringBase &moveDir, I32 status) const
Log event FailKeepFileMove.
uint8_t U8
8-bit unsigned integer
Definition: BasicTypes.h:54
void processPlaybackDirectories()
Process all playback directories for this channel.
Definition: Channel.cpp:334
CFDP operation has been successful.
void insertSortPrio(Transaction *txn, QueueId::T queue)
Insert a transaction into a priority sorted transaction queue.
Definition: Channel.cpp:662
FwSizeType getSize() const
Definition: Buffer.cpp:60
U32 getAckTimerParam(U8 channelIndex)
CfdpTxnFilenames fnames
Definition: Types.hpp:281
Cfdp::ChannelTelemetry & getChannelTelemetryRef(U8 channelId)
Get reference to channel telemetry for Channel class.
Definition: Engine.cpp:1182
U32 getInactivityTimerParam(U8 channelIndex)
State where all PDUs are dropped.
Transaction * findUnusedTransaction(Direction direction)
Find an unused transaction on this channel.
Definition: Channel.cpp:396
Class::T getClass() const
Get transaction class (CLASS_1 or CLASS_2)
static constexpr FwSizeType PACKET_DESCRIPTOR_SIZE
Size of packet descriptor prepended to PDUs for ComQueue.
Definition: CfdpManager.hpp:67
TransactionSeq seq_num
transaction identifier, stays constant for entire transfer
Definition: Types.hpp:251
other OS-specific error
Definition: FileSystem.hpp:40
void initTxFile(Class::T cfdp_class, Keep::T keep, U8 chan, U8 priority)
Initialize transaction for outgoing file transfer.
void initialize(PduDirection direction, Cfdp::Class::T txmMode, EntityId sourceEid, TransactionSeq transactionSeq, EntityId destEid, ConditionCode conditionCode, FinDeliveryCode deliveryCode, FinFileStatus fileStatus)
Initialize a Finished PDU.
Definition: FinPdu.cpp:14
void cycle()
Cycle the engine once per scheduler call.
Definition: Engine.cpp:951
void cleanup(Fw::MemAllocator &allocator, FwEnumStoreType memId)
Clean up dynamically allocated resources.
Definition: Channel.cpp:172
Memory Allocation base class.
CFDP channel operations are executing nominally.
Definition: FlowEnumAc.hpp:37
void log_ACTIVITY_LO_TxFileQueued(const Fw::StringBase &sourceFileName, U32 transactionSeq) const
Log event TxFileQueued.
void log_WARNING_LO_PlaybackDirSlotUnavailable() const
Log event PlaybackDirSlotUnavailable.
void insertBackInQueue(QueueId::T queueidx, CListNode *node)
Insert a node at the back of a channel queue.
Definition: Channel.hpp:528
void log_WARNING_LO_UnhandledPduInIdleState() const
Log event UnhandledPduInIdleState.
Class::T cfdpClass
the CFDP class to send
Definition: Types.hpp:305
Status::T recvFin(Transaction *txn, const FinPdu &pdu)
Unpack an FIN PDU from a received message.
Definition: Engine.cpp:447
Operation was successful.
Definition: Directory.hpp:22
The type of a PDU header (common to all PDUs)
Definition: PduHeader.hpp:47
PduTypeEnum::T peekPduType(const Fw::Buffer &buffer)
Definition: PduHeader.cpp:230
Transaction initiated via port interface.
void sCancel()
Cancel an S transaction.
Fw::Enabled enabled
Enabled flag.
Definition: Types.hpp:311
void log_WARNING_LO_FailPduSerialization(U8 channelId, const Svc::Ccsds::Cfdp::PduTypeEnum &pduType, I32 status) const
Log event FailPduSerialization.
void incrementFaultTxEofError(U8 chanId)
Increment sent EOF error counter (any condition code that is not no-error or cancel) ...
void log_ACTIVITY_HI_RxFileTransferCompleted(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 RxFileTransferCompleted.
CfdpFlagsTx tx
applies to only send file transactions
Definition: Types.hpp:404
Status open(const char *path, OpenMode mode) override
Open or create a directory.
Definition: Directory.cpp:31
Transaction * findTransactionBySequenceNumber(TransactionSeq transaction_sequence_number, EntityId src_eid)
Finds an active transaction by sequence number.
Definition: Channel.cpp:457
TransactionSeq getTransactionSeq() const
Get the transaction sequence number.
Definition: PduHeader.hpp:127
Timer intervalTimer
Timer object used to poll the directory.
Definition: Types.hpp:300
Structure for the telemetry array of CFDP channels.
RateGroupDivider component implementation.
CfdpPollDir * getPollDir(U32 index)
Get a polling directory entry.
Definition: Channel.hpp:262
virtual SizeType length() const
Get the length of the string.
EntityId getSourceEid() const
Get the source entity ID.
Definition: PduHeader.hpp:124
U32 getNumCmdTx() const
Get the number of commanded TX transactions.
Definition: Channel.hpp:202
CfdpFlagsCommon com
applies to all transactions
Definition: Types.hpp:402
Status::T setSuspendResumeTransaction(U8 channelId, TransactionSeq transactionSeq, EntityId entityId, SuspendResume::T action)
Set transaction suspend state.
Definition: Engine.cpp:676
TxnStatus txn_stat
final status of operation
Definition: Types.hpp:248
Operation was successful.
Definition: FileSystem.hpp:24
void log_WARNING_LO_FileRemoveFailed(const Fw::StringBase &filename, I32 status) const
Log event FileRemoveFailed.
void txStateDispatch(const TxnSendDispatchTable *dispatch)
Top-level Dispatch function to send a PDU based on current state.
void log_WARNING_LO_PlaybackDirOpenFailed(const Fw::StringBase &directory, I32 status) const
Log event PlaybackDirOpenFailed.
EntityId src_eid
the source eid of the transaction
Definition: Types.hpp:249
Fw::String getMoveDirParam(U8 channelIndex)
virtual void deallocate(const FwEnumStoreType identifier, void *ptr)=0
Directory poll entry.
Definition: Types.hpp:298
A table of transmit handler functions based on transaction state.
Definition: Transaction.hpp:91
The type of an EOF PDU.
Definition: EofPdu.hpp:19
Status::T sendFin(Transaction *txn, FinDeliveryCode dc, FinFileStatus fs, ConditionCode cc)
Create, encode, and send a FIN (Finished) PDU.
Definition: Engine.cpp:300
Cfdp::ChannelTelemetry & getChannelTelemetryRef(U8 chanId)
Get reference to channel telemetry for queue depth updates.
SerializeStatus setBuffLen(Serializable::SizeType length) override
Set buffer length manually.
void log_WARNING_LO_PollDirNotActive(U8 channelId, U8 pollId) const
Log event PollDirNotActive.
Status::T abandonTransaction(U8 channelId, TransactionSeq transactionSeq, EntityId entityId)
Abandon a transaction immediately.
Definition: Engine.cpp:711
void initialize(PduDirection direction, Cfdp::Class::T txmMode, EntityId sourceEid, TransactionSeq transactionSeq, EntityId destEid, FileSize fileSize, const Fw::String &sourceFilename, const Fw::String &destFilename, ChecksumType checksumType, U8 closureRequested)
Initialize a Metadata PDU.
Definition: MetadataPdu.cpp:16
void log_WARNING_LO_FailPduHeaderDeserialization(U8 channelId, I32 status) const
Log event FailPduHeaderDeserialization.
CfdpRxStateData receive
applies to only receive file transactions
Definition: Types.hpp:412
void log_DIAGNOSTIC_ResetFreedTransaction() const
Log event ResetFreedTransaction.
EntityId destEid
destination entity id
Definition: Types.hpp:306
U8 q_index
Q index this is in.
Definition: Types.hpp:359
AckTxnStatus GetTxnStatus(Transaction *txn)
Gets the status of this transaction.
Definition: Utils.cpp:43
T
The raw enum type.
Definition: ClassEnumAc.hpp:42
void log_WARNING_LO_PollDirBusy(U8 channelId, U8 pollId) const
Log event PollDirBusy.
T
The raw enum type.
Definition: FlowEnumAc.hpp:35
The type of a NAK PDU.
Definition: NakPdu.hpp:24
void setFlowState(Flow::T flowState)
Set the flow state for this channel.
Definition: Channel.hpp:236
void armAckTimer(Transaction *txn)
Arm the ACK timer for a transaction.
Definition: Engine.cpp:104
Status::T playbackDir(const Fw::String &src, const Fw::String &dst, Class::T cfdp_class, Keep::T keep, U8 chan, U8 priority, EntityId dest_id)
Begin transmit of a directory.
Definition: Engine.cpp:855
void log_DIAGNOSTIC_TxLateFinAcked(Svc::Ccsds::Cfdp::EntityId srcEid, Svc::Ccsds::Cfdp::TransactionSeq seqNum) const
const PduHeader & asHeader() const
Get this as a Header.
Definition: FileDataPdu.hpp:62
#define FW_ASSERT(...)
Definition: Assert.hpp:14
Disabled state.
static Status removeFile(const char *path)
Remove a file at the specified path.
Definition: FileSystem.cpp:87
bool isOpen() const
determine if the file is open
Definition: File.cpp:98
bool TxnStatusIsError(TxnStatus txn_stat)
Check if the internal transaction status represents an error.
Definition: Utils.cpp:113
Playback * getPlayback(U32 index)
Get a playback directory entry.
Definition: Channel.hpp:251
void r1Recv(const Fw::Buffer &buffer)
R1 receive PDU processing.
Error if directory doesn&#39;t exist.
Definition: Directory.hpp:36
void initialize(EntityId eid)
Definition: Tlv.cpp:59
void log_WARNING_LO_FailFinPduDeserialization(U8 channelId, I32 status) const
Log event FailFinPduDeserialization.
The type of an ACK PDU.
Definition: AckPdu.hpp:18
EntityId peer_eid
peer_eid is always the "other guy", same src_eid for RX
Definition: Types.hpp:250
void log_WARNING_LO_FailMetadataPduDeserialization(U8 channelId, I32 status) const
Log event FailMetadataPduDeserialization.
Status::T recvEof(Transaction *txn, const EofPdu &pdu)
Unpack an EOF PDU from a received message.
Definition: Engine.cpp:430