diff --git a/config/default_cf_tblstruct.h b/config/default_cf_tblstruct.h
index 30df4779..9ed1a33e 100644
--- a/config/default_cf_tblstruct.h
+++ b/config/default_cf_tblstruct.h
@@ -51,6 +51,7 @@ typedef struct CF_ConfigTable
uint16 outgoing_file_chunk_size; /**< \brief maximum size of outgoing file data chunk in a PDU.
* Limited by CF_MAX_PDU_SIZE minus the PDU header(s) */
char tmp_dir[CF_FILENAME_MAX_PATH]; /**< \brief directory to put temp files */
+ char fail_dir[CF_FILENAME_MAX_PATH]; /**< \brief fail directory */
} CF_ConfigTable_t;
#endif
diff --git a/docs/dox_src/cfs_cf.dox b/docs/dox_src/cfs_cf.dox
index f5022144..3778eb44 100644
--- a/docs/dox_src/cfs_cf.dox
+++ b/docs/dox_src/cfs_cf.dox
@@ -398,16 +398,24 @@
When files are found in a polling directory that is enabled, the files are
automatically queued by CF for output. The polling rate is configurable and
each channel has a configurable number of polling directories. Each polling
- directory has an enable, a class setting, a priority setting, a preserve
- setting and a destination directory. When enabled, CF will periodically check
- the polling directories for files. When a file is found, CF will place the
- file information on the pending queue if it is closed and not already on the
- queue and not currently active. All polling directories are checked at the
- same frequency which is defined by the table parameter
- NumWakeupsPerPollDirChk. Setting this parameter to one will cause CF to check
- the polling directories at the fastest possible rate, every time it receives a
- 'wake-up' command. Checking polling directories is a processor-intensive
- effort, it is best to keep the polling rate as low as possible.
+ directory has an enable, a class setting, a priority setting, a destination
+ directory. When enabled, CF will periodically check the polling directories
+ for files. When a file is found, CF will place the file information on the
+ pending queue if it is closed and not already on the queue and not currently
+ active. All polling directories are checked at the same frequency which is
+ defined by the table parameter NumWakeupsPerPollDirChk. Setting this parameter
+ to one will cause CF to check the polling directories at the fastest possible
+ rate, every time it receives a 'wake-up' command. Checking polling directories
+ is a processor-intensive effort, it is best to keep the polling rate as low as
+ possible.
+
+
Failed Transmit and directory use
+
+ Currently, the fail directory is used only to store class 2 TX files that failed
+ during transmission from a polling directory. When a file fails during transmission
+ from a polling directory, it is deleted from the polling directory to prevent an
+ infinite loop and moved to the fail directory. However, if a file fails during a
+ class 2 TX outside of a polling directory, the file will not be deleted.
Efficiency
@@ -645,7 +653,8 @@ CF_UnionArgs_Payload_t;
and an event will be generated.
If the command is not successful, the command error counter will increment and
- an error event will be generated indicating the reason for failure.
+ an error event will be generated indicating the reason for failure. File will
+ not be deleted if the 'transmit' failed (unless in an polling directories).
\verbatim
typedef struct CF_TxFileCmd
diff --git a/fsw/src/cf_cfdp.c b/fsw/src/cf_cfdp.c
index 6f31fea1..f30fb4b1 100644
--- a/fsw/src/cf_cfdp.c
+++ b/fsw/src/cf_cfdp.c
@@ -1588,9 +1588,7 @@ void CF_CFDP_CycleEngine(void)
*-----------------------------------------------------------------*/
void CF_CFDP_ResetTransaction(CF_Transaction_t *txn, int keep_history)
{
- char * filename;
- char destination[OS_MAX_PATH_LEN];
- osal_status_t status = OS_ERROR;
+
CF_Channel_t *chan = &CF_AppData.engine.channels[txn->chan_num];
CF_Assert(txn->chan_num < CF_NUM_CHANNELS);
@@ -1608,31 +1606,10 @@ void CF_CFDP_ResetTransaction(CF_Transaction_t *txn, int keep_history)
if (OS_ObjectIdDefined(txn->fd))
{
CF_WrappedClose(txn->fd);
+
if (!txn->keep)
{
- if (CF_CFDP_IsSender(txn))
- {
- /* If move directory is defined attempt move */
- if (CF_AppData.config_table->chan[txn->chan_num].move_dir[0] != 0)
- {
- filename = strrchr(txn->history->fnames.src_filename, '/');
- if (filename != NULL)
- {
- snprintf(destination, sizeof(destination), "%s%s",
- CF_AppData.config_table->chan[txn->chan_num].move_dir, filename);
- status = OS_mv(txn->history->fnames.src_filename, destination);
- }
- }
-
- if (status != OS_SUCCESS)
- {
- OS_remove(txn->history->fnames.src_filename);
- }
- }
- else
- {
- OS_remove(txn->history->fnames.dst_filename);
- }
+ CF_CFDP_HandleNotKeepFile(txn);
}
}
@@ -1829,3 +1806,100 @@ void CF_CFDP_DisableEngine(void)
CFE_SB_DeletePipe(chan->pipe);
}
}
+
+/*----------------------------------------------------------------
+ *
+ * Application-scope internal function
+ * See description in cf_cfdp.h for argument/return detail
+ *
+ *-----------------------------------------------------------------*/
+bool CF_CFDP_IsPollingDir(const char * src_file, uint8 chan_num)
+{
+ bool return_code = false;
+ char src_dir[CF_FILENAME_MAX_LEN] = "\0";
+ CF_ChannelConfig_t *cc;
+ CF_PollDir_t * pd;
+ int i;
+
+ char * last_slash = strrchr(src_file, '/');
+ if(last_slash != NULL)
+ {
+ strncpy(src_dir, src_file, last_slash - src_file);
+ }
+
+ cc = &CF_AppData.config_table->chan[chan_num];
+ for (i = 0; i < CF_MAX_POLLING_DIR_PER_CHAN; ++i)
+ {
+ pd = &cc->polldir[i];
+ if(strcmp(src_dir, pd->src_dir) == 0)
+ {
+ return_code = true;
+ break;
+ }
+ }
+
+ return return_code;
+}
+
+/*----------------------------------------------------------------
+ *
+ * Application-scope internal function
+ * See description in cf_cfdp.h for argument/return detail
+ *
+ *-----------------------------------------------------------------*/
+void CF_CFDP_HandleNotKeepFile(CF_Transaction_t *txn)
+{
+ /* Sender */
+ if (CF_CFDP_IsSender(txn))
+ {
+ if(!CF_TxnStatus_IsError(txn->history->txn_stat))
+ {
+ /* If move directory is defined attempt move */
+ CF_CFDP_MoveFile(txn->history->fnames.src_filename, CF_AppData.config_table->chan[txn->chan_num].move_dir);
+ }
+ else
+ {
+ /* file inside an polling directory */
+ if(CF_CFDP_IsPollingDir(txn->history->fnames.src_filename, txn->chan_num))
+ {
+ /* If fail directory is defined attempt move */
+ CF_CFDP_MoveFile(txn->history->fnames.src_filename, CF_AppData.config_table->fail_dir);
+ }
+ }
+ }
+ /* Not Sender */
+ else
+ {
+ OS_remove(txn->history->fnames.dst_filename);
+ }
+}
+
+
+/*----------------------------------------------------------------
+ *
+ * Application-scope internal function
+ * See description in cf_cfdp.h for argument/return detail
+ *
+ *-----------------------------------------------------------------*/
+void CF_CFDP_MoveFile(const char *src, const char *dest_dir)
+{
+ osal_status_t status = OS_ERROR;
+ char * filename;
+ char destination[OS_MAX_PATH_LEN];
+
+ if(dest_dir[0] != 0)
+ {
+ filename = strrchr(src, '/');
+ if (filename != NULL)
+ {
+ snprintf(destination, sizeof(destination), "%s%s",
+ dest_dir, filename);
+ status = OS_mv(src, destination);
+ }
+ }
+
+ if (status != OS_SUCCESS)
+ {
+ OS_remove(src);
+ }
+}
\ No newline at end of file
diff --git a/fsw/src/cf_cfdp.h b/fsw/src/cf_cfdp.h
index 1fa54db0..c1c04870 100644
--- a/fsw/src/cf_cfdp.h
+++ b/fsw/src/cf_cfdp.h
@@ -703,4 +703,34 @@ void CF_CFDP_ProcessPollingDirectories(CF_Channel_t *chan);
*/
CF_CListTraverse_Status_t CF_CFDP_DoTick(CF_CListNode_t *node, void *context);
+/************************************************************************/
+/** @brief Check if source file came from polling directory
+ *
+ *
+ * @par Assumptions, External Events, and Notes:
+ *
+ * @retval true/false
+ */
+bool CF_CFDP_IsPollingDir(const char * src_file, uint8 chan_num);
+
+/************************************************************************/
+/** @brief Remove/Move file after transaction
+ *
+ * This helper is used to handle "not keep" file option after a transaction.
+ *
+ * @par Assumptions, External Events, and Notes:
+ *
+ */
+void CF_CFDP_HandleNotKeepFile(CF_Transaction_t *txn);
+
+/************************************************************************/
+/** @brief Move File
+ *
+ * This helper is used to move a file.
+ *
+ * @par Assumptions, External Events, and Notes:
+ *
+ */
+void CF_CFDP_MoveFile(const char *src, const char *dest_dir);
+
#endif /* !CF_CFDP_H */
diff --git a/fsw/tables/cf_def_config.c b/fsw/tables/cf_def_config.c
index 35bba669..425666ff 100644
--- a/fsw/tables/cf_def_config.c
+++ b/fsw/tables/cf_def_config.c
@@ -80,5 +80,6 @@ CF_ConfigTable_t CF_config_table = {
.move_dir = ""}},
480, /* outgoing_file_chunk_size */
"/cf/tmp", /* temporary file directory */
+ "/cf/fail", /* Stores failed tx file for "polling directory" */
};
CFE_TBL_FILEDEF(CF_config_table, CF.config_table, CF config table, cf_def_config.tbl)
diff --git a/unit-test/cf_cfdp_tests.c b/unit-test/cf_cfdp_tests.c
index 3661d89f..d0c13f10 100644
--- a/unit-test/cf_cfdp_tests.c
+++ b/unit-test/cf_cfdp_tests.c
@@ -1357,6 +1357,7 @@ void Test_CF_CFDP_ResetTransaction(void)
CF_History_t * history;
CF_Channel_t * chan;
CF_Playback_t pb;
+ CF_ConfigTable_t *config;
memset(&pb, 0, sizeof(pb));
@@ -1443,6 +1444,91 @@ void Test_CF_CFDP_ResetTransaction(void)
UtAssert_UINT32_EQ(pb.num_ts, 9);
UtAssert_UINT32_EQ(chan->num_cmd_tx, 7);
UtAssert_STUB_COUNT(CF_FreeTransaction, 1);
+
+ /*
+ * File is in Polling Directory, Not Keep, and is Error
+ * Move to fail directory successful
+ */
+ UT_ResetState(UT_KEY(CF_FreeTransaction));
+ UT_ResetState(UT_KEY(OS_remove));
+ UT_ResetState(UT_KEY(OS_mv));
+ UT_CFDP_SetupBasicTestState(UT_CF_Setup_TX, NULL, &chan, &history, &txn, &config);
+ UT_SetDefaultReturnValue(UT_KEY(CF_TxnStatus_IsError), true);\
+ UT_SetDefaultReturnValue(UT_KEY(OS_mv), OS_SUCCESS);
+ txn->fd = OS_ObjectIdFromInteger(1);
+ txn->keep = 0;
+ txn->state = CF_TxnState_S2;
+ strcpy(history->fnames.src_filename, "/ram/poll1/test1");
+ strcpy(config->chan[0].polldir[0].src_dir, "/ram/poll1");
+ strcpy(config->fail_dir, "/ram/fail");
+
+ UtAssert_VOIDCALL(CF_CFDP_ResetTransaction(txn, 0));
+ UtAssert_STUB_COUNT(CF_FreeTransaction, 1);
+ UtAssert_STUB_COUNT(OS_mv, 1);
+ UtAssert_STUB_COUNT(OS_remove, 0);
+
+ /*
+ * File is in Polling Directory, Not Keep, and is Error
+ * Move to fail directory not successful
+ */
+ UT_ResetState(UT_KEY(CF_FreeTransaction));
+ UT_ResetState(UT_KEY(OS_remove));
+ UT_ResetState(UT_KEY(OS_mv));
+ UT_CFDP_SetupBasicTestState(UT_CF_Setup_TX, NULL, &chan, &history, &txn, &config);
+ UT_SetDefaultReturnValue(UT_KEY(CF_TxnStatus_IsError), true);
+ UT_SetDefaultReturnValue(UT_KEY(OS_mv), OS_ERROR);
+ txn->fd = OS_ObjectIdFromInteger(1);
+ txn->keep = 0;
+ txn->state = CF_TxnState_S2;
+ strcpy(history->fnames.src_filename, "/ram/poll1/test1");
+ strcpy(config->chan[0].polldir[0].src_dir, "/ram/poll1");
+ strcpy(config->fail_dir, "/ram/fail");
+
+ UtAssert_VOIDCALL(CF_CFDP_ResetTransaction(txn, 0));
+ UtAssert_STUB_COUNT(CF_FreeTransaction, 1);
+ UtAssert_STUB_COUNT(OS_mv, 1);
+ UtAssert_STUB_COUNT(OS_remove, 1);
+
+ /*
+ * Source file outside polling directory, Not Keep, is Error
+ */
+ UT_ResetState(UT_KEY(CF_FreeTransaction));
+ UT_ResetState(UT_KEY(OS_remove));
+ UT_ResetState(UT_KEY(OS_mv));
+ UT_CFDP_SetupBasicTestState(UT_CF_Setup_TX, NULL, &chan, &history, &txn, &config);
+ UT_SetDefaultReturnValue(UT_KEY(CF_TxnStatus_IsError), true);
+ txn->fd = OS_ObjectIdFromInteger(1);
+ txn->keep = 0;
+ txn->state = CF_TxnState_S2;
+ strcpy(history->fnames.src_filename, "/ram/test.txt");
+ strcpy(config->chan[0].polldir[0].src_dir, "/ram/poll1");
+ strcpy(config->fail_dir, "/ram/fail");
+
+ UtAssert_VOIDCALL(CF_CFDP_ResetTransaction(txn, 0));
+ UtAssert_STUB_COUNT(CF_FreeTransaction, 1);
+ UtAssert_STUB_COUNT(OS_mv, 0);
+ UtAssert_STUB_COUNT(OS_remove, 0);
+
+ /*
+ * Source File is empty string. Not Keep, is Error
+ */
+ UT_ResetState(UT_KEY(CF_FreeTransaction));
+ UT_ResetState(UT_KEY(OS_remove));
+ UT_ResetState(UT_KEY(OS_mv));
+ UT_CFDP_SetupBasicTestState(UT_CF_Setup_TX, NULL, &chan, &history, &txn, &config);
+ UT_SetDefaultReturnValue(UT_KEY(CF_TxnStatus_IsError), true);
+ txn->fd = OS_ObjectIdFromInteger(1);
+ txn->keep = 0;
+ txn->state = CF_TxnState_S2;
+ strcpy(history->fnames.src_filename, "");
+ strcpy(config->chan[0].polldir[0].src_dir, "/ram/poll1");
+ strcpy(config->fail_dir, "/ram/fail");
+
+ UtAssert_VOIDCALL(CF_CFDP_ResetTransaction(txn, 0));
+ UtAssert_STUB_COUNT(CF_FreeTransaction, 1);
+ UtAssert_STUB_COUNT(OS_mv, 0);
+ UtAssert_STUB_COUNT(OS_remove, 1);
+
}
void Test_CF_CFDP_SetTxnStatus(void)
diff --git a/unit-test/stubs/cf_cfdp_stubs.c b/unit-test/stubs/cf_cfdp_stubs.c
index 3db3c393..c3f844ab 100644
--- a/unit-test/stubs/cf_cfdp_stubs.c
+++ b/unit-test/stubs/cf_cfdp_stubs.c
@@ -243,6 +243,18 @@ void CF_CFDP_EncodeStart(CF_EncoderState_t *penc, void *msgbuf, CF_Logical_PduBu
UT_GenStub_Execute(CF_CFDP_EncodeStart, Basic, NULL);
}
+/*
+ * ----------------------------------------------------
+ * Generated stub function for CF_CFDP_HandleNotKeepFile()
+ * ----------------------------------------------------
+ */
+void CF_CFDP_HandleNotKeepFile(CF_Transaction_t *txn)
+{
+ UT_GenStub_AddParam(CF_CFDP_HandleNotKeepFile, CF_Transaction_t *, txn);
+
+ UT_GenStub_Execute(CF_CFDP_HandleNotKeepFile, Basic, NULL);
+}
+
/*
* ----------------------------------------------------
* Generated stub function for CF_CFDP_InitEngine()
@@ -273,6 +285,36 @@ void CF_CFDP_InitTxnTxFile(CF_Transaction_t *txn, CF_CFDP_Class_t cfdp_class, ui
UT_GenStub_Execute(CF_CFDP_InitTxnTxFile, Basic, NULL);
}
+/*
+ * ----------------------------------------------------
+ * Generated stub function for CF_CFDP_IsPollingDir()
+ * ----------------------------------------------------
+ */
+bool CF_CFDP_IsPollingDir(const char *src_file, uint8 chan_num)
+{
+ UT_GenStub_SetupReturnBuffer(CF_CFDP_IsPollingDir, bool);
+
+ UT_GenStub_AddParam(CF_CFDP_IsPollingDir, const char *, src_file);
+ UT_GenStub_AddParam(CF_CFDP_IsPollingDir, uint8, chan_num);
+
+ UT_GenStub_Execute(CF_CFDP_IsPollingDir, Basic, NULL);
+
+ return UT_GenStub_GetReturnValue(CF_CFDP_IsPollingDir, bool);
+}
+
+/*
+ * ----------------------------------------------------
+ * Generated stub function for CF_CFDP_MoveFile()
+ * ----------------------------------------------------
+ */
+void CF_CFDP_MoveFile(const char *src, const char *dest_dir)
+{
+ UT_GenStub_AddParam(CF_CFDP_MoveFile, const char *, src);
+ UT_GenStub_AddParam(CF_CFDP_MoveFile, const char *, dest_dir);
+
+ UT_GenStub_Execute(CF_CFDP_MoveFile, Basic, NULL);
+}
+
/*
* ----------------------------------------------------
* Generated stub function for CF_CFDP_PlaybackDir()