diff --git a/fsw/cfe-core/src/es/cfe_es_apps.h b/fsw/cfe-core/src/es/cfe_es_apps.h index c03a5c2c2..d2f0df176 100644 --- a/fsw/cfe-core/src/es/cfe_es_apps.h +++ b/fsw/cfe-core/src/es/cfe_es_apps.h @@ -260,9 +260,14 @@ bool CFE_ES_RunAppTableScan(uint32 ElapsedTime, void *Arg); bool CFE_ES_RunExceptionScan(uint32 ElapsedTime, void *Arg); /* -** Check if ER log dump request is pending -*/ -bool CFE_ES_RunERLogDump(uint32 ElapsedTime, void *Arg); + * Background file write data getter for ER log entry + */ +bool CFE_ES_BackgroundERLogFileDataGetter(void *Meta, uint32 RecordNum, void **Buffer, size_t *BufSize); + +/* + * Background file write event handler for ER log entry + */ +void CFE_ES_BackgroundERLogFileEventHandler(void *Meta, CFE_FS_FileWriteEvent_t Event, int32 Status, uint32 RecordNum, size_t BlockSize, size_t Position); /* ** Perform the requested control action for an application diff --git a/fsw/cfe-core/src/es/cfe_es_backgroundtask.c b/fsw/cfe-core/src/es/cfe_es_backgroundtask.c index e72be0a28..93b70867e 100644 --- a/fsw/cfe-core/src/es/cfe_es_backgroundtask.c +++ b/fsw/cfe-core/src/es/cfe_es_backgroundtask.c @@ -89,9 +89,9 @@ const CFE_ES_BackgroundJobEntry_t CFE_ES_BACKGROUND_JOB_TABLE[] = .ActivePeriod = CFE_PLATFORM_ES_APP_SCAN_RATE, .IdlePeriod = CFE_PLATFORM_ES_APP_SCAN_RATE }, - { /* Check for ER log write requests */ - .RunFunc = CFE_ES_RunERLogDump, - .JobArg = &CFE_ES_TaskData.BackgroundERLogDumpState, + { /* Call FS to handle background file writes */ + .RunFunc = CFE_FS_RunBackgroundFileDump, + .JobArg = NULL, .ActivePeriod = CFE_PLATFORM_ES_APP_SCAN_RATE, .IdlePeriod = CFE_PLATFORM_ES_APP_SCAN_RATE } diff --git a/fsw/cfe-core/src/es/cfe_es_erlog.c b/fsw/cfe-core/src/es/cfe_es_erlog.c index f132d918b..1ebd390ef 100644 --- a/fsw/cfe-core/src/es/cfe_es_erlog.c +++ b/fsw/cfe-core/src/es/cfe_es_erlog.c @@ -181,126 +181,118 @@ int32 CFE_ES_WriteToERLog( CFE_ES_LogEntryType_Enum_t EntryType, uint32 Reset } /* End of CFE_ES_WriteToERLog() */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -/* Function: CFE_ES_RunERLogDump() */ +/* Function: CFE_ES_BackgroundERLogFileDataGetter() */ /* */ /* Purpose: */ -/* Write exception & reset log to a file. */ -/* */ -/* Implemented as an ES background job, but the entire file write is done */ -/* in a single invocation, as the file is expected to be relatively small. */ +/* Gets a single record from exception & reset log to write to a file. */ /* */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -bool CFE_ES_RunERLogDump(uint32 ElapsedTime, void *Arg) +bool CFE_ES_BackgroundERLogFileDataGetter(void *Meta, uint32 RecordNum, void **Buffer, size_t *BufSize) { - CFE_ES_BackgroundLogDumpGlobal_t *State = (CFE_ES_BackgroundLogDumpGlobal_t *)Arg; - int32 Status; - int32 PspStatus; - CFE_FS_Header_t FileHdr; - CFE_ES_ERLog_FileEntry_t FileEntry; + CFE_ES_BackgroundLogDumpGlobal_t *BgFilePtr; + CFE_ES_ERLog_FileEntry_t *FileBufferPtr; CFE_ES_ERLog_MetaData_t *EntryPtr; - uint32 FileSize; - uint32 i; - osal_id_t fd; - + int32 PspStatus; + + BgFilePtr = (CFE_ES_BackgroundLogDumpGlobal_t *)Meta; + FileBufferPtr = &BgFilePtr->EntryBuffer; - if (!State->IsPending) + if (RecordNum < CFE_PLATFORM_ES_ER_LOG_ENTRIES) { - return false; - } + EntryPtr = &CFE_ES_ResetDataPtr->ERLog[RecordNum]; - FileSize = 0; - Status = OS_OpenCreate(&fd, State->DataFileName, OS_FILE_FLAG_CREATE | OS_FILE_FLAG_TRUNCATE, OS_WRITE_ONLY); - if(Status < 0) - { - CFE_EVS_SendEvent(CFE_ES_ERLOG2_ERR_EID,CFE_EVS_EventType_ERROR, - "Error creating file %s, RC = %d", - State->DataFileName, (int)Status); - } - else - { - CFE_FS_InitHeader(&FileHdr, CFE_ES_ER_LOG_DESC, CFE_FS_SubType_ES_ERLOG); + /* First wipe the buffer before re-use */ + memset(FileBufferPtr, 0, sizeof(*FileBufferPtr)); + + CFE_ES_LockSharedData(__func__,__LINE__); + + /* The basic info comes directly from the ES log */ + FileBufferPtr->BaseInfo = EntryPtr->BaseInfo; - /* write the cFE header to the file */ - Status = CFE_FS_WriteHeader(fd, &FileHdr); - if(Status != sizeof(CFE_FS_Header_t)) + /* + * The context info, if available, comes from the PSP. + * This returns the actual size of the context info, or <0 on error. + */ + PspStatus = CFE_PSP_Exception_CopyContext(EntryPtr->PspContextId, &FileBufferPtr->Context, + sizeof(FileBufferPtr->Context)); + if (PspStatus > 0) { - CFE_ES_FileWriteByteCntErr(State->DataFileName,sizeof(CFE_FS_Header_t),Status); + FileBufferPtr->ContextSize = PspStatus; } else { - FileSize += Status; - - /* write a single ER log entry on each pass */ - for(i=0;iERLog[i]; - - /* The basic info comes directly from the ES log */ - FileEntry.BaseInfo = EntryPtr->BaseInfo; - - /* - * The context info, if available, comes from the PSP. - * This returns the actual size of the context info, or <0 on error. - */ - PspStatus = CFE_PSP_Exception_CopyContext(EntryPtr->PspContextId, &FileEntry.Context, sizeof(FileEntry.Context)); - if (PspStatus > 0) - { - FileEntry.ContextSize = PspStatus; - } - else - { - /* - * errors here are OK - just means there is no context available. - * Record a size of 0 in the log file. - */ - FileEntry.ContextSize = 0; - } - - /* - * any unused context space should be cleared. - * - * This is for binary compatibility with historical log files, where a fixed amount - * of space is given per-entry, regardless of the actual size. - */ - if (FileEntry.ContextSize < sizeof(FileEntry.Context)) - { - memset(&FileEntry.Context[FileEntry.ContextSize], 0, - sizeof(FileEntry.Context) - FileEntry.ContextSize); - } - - /* - * Now write to file - */ - Status = OS_write(fd,&FileEntry,sizeof(FileEntry)); - - if(Status != sizeof(FileEntry)) - { - CFE_ES_FileWriteByteCntErr(State->DataFileName,sizeof(FileEntry),Status); - break; - }/* end if */ + /* + * errors here are OK - just means there is no context available. + * Record a size of 0 in the log file. + */ + FileBufferPtr->ContextSize = 0; + } - FileSize += Status; + CFE_ES_UnlockSharedData(__func__,__LINE__); - } /* end for */ + /* + * Export data to caller for actual write + */ + *Buffer = FileBufferPtr; + *BufSize = sizeof(*FileBufferPtr); + } + else + { + *Buffer = NULL; + *BufSize = 0; + } - } /* end if */ + /* Check for EOF (last entry) */ + return (RecordNum >= (CFE_PLATFORM_ES_ER_LOG_ENTRIES-1)); +} - OS_close(fd); - CFE_EVS_SendEvent(CFE_ES_ERLOG2_EID, CFE_EVS_EventType_DEBUG, - "%s written:Size=%lu",State->DataFileName,(unsigned long)FileSize); +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* Function: CFE_ER_BackgroundERLogFileEventHandler() */ +/* */ +/* Purpose: */ +/* Report events during writing exception & reset log to a file. */ +/* */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +void CFE_ES_BackgroundERLogFileEventHandler(void *Meta, CFE_FS_FileWriteEvent_t Event, int32 Status, uint32 RecordNum, size_t BlockSize, size_t Position) +{ + CFE_ES_BackgroundLogDumpGlobal_t *BgFilePtr; - } /* end if */ + BgFilePtr = (CFE_ES_BackgroundLogDumpGlobal_t *)Meta; /* - * Always clear the "pending" flag whether successful or not. - * If unsuccessful, an operator needs to investigate the error and re-issue command. + * Note that this runs in the context of ES background task (file writer background job) + * It does NOT run in the context of the CFE_TBL app task. + * + * Events should use CFE_EVS_SendEventWithAppID() rather than CFE_EVS_SendEvent() + * to get proper association with TBL task. */ - State->IsPending = false; - - return false; -}/* end CFE_ES_RunERLogDump */ - + switch(Event) + { + case CFE_FS_FileWriteEvent_COMPLETE: + CFE_EVS_SendEvent(CFE_ES_ERLOG2_EID, CFE_EVS_EventType_DEBUG, + "%s written:Size=%lu", + BgFilePtr->FileWrite.FileName,(unsigned long)Position); + break; + + case CFE_FS_FileWriteEvent_HEADER_WRITE_ERROR: + case CFE_FS_FileWriteEvent_RECORD_WRITE_ERROR: + CFE_EVS_SendEvent(CFE_ES_FILEWRITE_ERR_EID,CFE_EVS_EventType_ERROR, + "File write,byte cnt err,file %s,request=%u,actual=%u", + BgFilePtr->FileWrite.FileName, (int)BlockSize, (int)Status); + break; + + case CFE_FS_FileWriteEvent_CREATE_ERROR: + CFE_EVS_SendEvent(CFE_ES_ERLOG2_ERR_EID,CFE_EVS_EventType_ERROR, + "Error creating file %s, RC = %d", + BgFilePtr->FileWrite.FileName, (int)Status); + break; + + default: + /* unhandled event - ignore */ + break; + } +} /* **--------------------------------------------------------------------------------------- diff --git a/fsw/cfe-core/src/es/cfe_es_task.c b/fsw/cfe-core/src/es/cfe_es_task.c index 2aa1b1fb1..501495153 100644 --- a/fsw/cfe-core/src/es/cfe_es_task.c +++ b/fsw/cfe-core/src/es/cfe_es_task.c @@ -1614,26 +1614,38 @@ int32 CFE_ES_ClearERLogCmd(const CFE_ES_ClearERLogCmd_t *data) int32 CFE_ES_WriteERLogCmd(const CFE_ES_WriteERLogCmd_t *data) { const CFE_ES_FileNameCmd_Payload_t *CmdPtr = &data->Payload; + CFE_ES_BackgroundLogDumpGlobal_t *StatePtr; + int32 Status; + + StatePtr = &CFE_ES_TaskData.BackgroundERLogDumpState; - if (CFE_ES_TaskData.BackgroundERLogDumpState.IsPending) + /* check if pending before overwriting fields in the structure */ + if (CFE_FS_BackgroundFileDumpIsPending(&StatePtr->FileWrite)) + { + Status = CFE_STATUS_REQUEST_ALREADY_PENDING; + } + else + { + CFE_SB_MessageStringGet(CFE_ES_TaskData.BackgroundERLogDumpState.FileWrite.FileName, (char *)CmdPtr->FileName, + CFE_PLATFORM_ES_DEFAULT_ER_LOG_FILE, + sizeof(StatePtr->FileWrite.FileName), sizeof(CmdPtr->FileName)); + + Status = CFE_FS_BackgroundFileDumpRequest(&CFE_ES_TaskData.BackgroundERLogDumpState.FileWrite); + } + + if (Status != CFE_SUCCESS) { CFE_EVS_SendEvent(CFE_ES_ERLOG_PENDING_ERR_EID,CFE_EVS_EventType_ERROR, "Error log write to file %s already in progress", - CFE_ES_TaskData.BackgroundERLogDumpState.DataFileName); + StatePtr->FileWrite.FileName); /* background dump already running, consider this an error */ CFE_ES_TaskData.CommandErrorCounter++; - } + } else { - CFE_SB_MessageStringGet(CFE_ES_TaskData.BackgroundERLogDumpState.DataFileName, (char *)CmdPtr->FileName, - CFE_PLATFORM_ES_DEFAULT_ER_LOG_FILE, - sizeof(CFE_ES_TaskData.BackgroundERLogDumpState.DataFileName), sizeof(CmdPtr->FileName)); - - CFE_ES_TaskData.BackgroundERLogDumpState.IsPending = true; CFE_ES_TaskData.CommandCounter++; - CFE_ES_BackgroundWakeup(); - } + } return CFE_SUCCESS; }/* end CFE_ES_WriteERLogCmd */ diff --git a/fsw/cfe-core/src/es/cfe_es_task.h b/fsw/cfe-core/src/es/cfe_es_task.h index 9b6476c0c..ab6fd37db 100644 --- a/fsw/cfe-core/src/es/cfe_es_task.h +++ b/fsw/cfe-core/src/es/cfe_es_task.h @@ -44,6 +44,7 @@ #include "cfe_es_events.h" #include "cfe_es_msg.h" #include "cfe_es_perf.h" +#include "private/cfe_es_erlog_typedef.h" /*************************************************************************/ @@ -90,8 +91,8 @@ */ typedef struct { - volatile bool IsPending; - char DataFileName[OS_MAX_PATH_LEN]; + CFE_FS_FileWriteMetaData_t FileWrite; /**< FS state data - must be first */ + CFE_ES_ERLog_FileEntry_t EntryBuffer; /**< Temp holding area for record to write */ } CFE_ES_BackgroundLogDumpGlobal_t; /* @@ -167,7 +168,6 @@ void CFE_ES_TaskPipe(CFE_SB_Buffer_t *SBBufPtr); */ int32 CFE_ES_BackgroundInit(void); void CFE_ES_BackgroundTask(void); -void CFE_ES_BackgroundWakeup(void); void CFE_ES_BackgroundCleanup(void); /* diff --git a/fsw/cfe-core/src/fs/cfe_fs_api.c b/fsw/cfe-core/src/fs/cfe_fs_api.c index 4f83e5f13..5b48387f9 100644 --- a/fsw/cfe-core/src/fs/cfe_fs_api.c +++ b/fsw/cfe-core/src/fs/cfe_fs_api.c @@ -328,6 +328,251 @@ int32 CFE_FS_ExtractFilenameFromPath(const char *OriginalPath, char *FileNameOnl return(ReturnCode); } +/* +** CFE_FS_RunBackgroundFileDump - See API and header file for details +*/ +bool CFE_FS_RunBackgroundFileDump(uint32 ElapsedTime, void *Arg) +{ + CFE_FS_CurrentFileState_t *State; + CFE_FS_BackgroundFileDumpEntry_t *Curr; + CFE_FS_FileWriteMetaData_t *Meta; + int32 Status; + CFE_FS_Header_t FileHdr; + void *RecordPtr; + size_t RecordSize; + bool IsEOF; + + State = &CFE_FS.FileDump.Current; + Curr = NULL; + IsEOF = false; + RecordPtr = NULL; + RecordSize = 0; + + State->Credit += (ElapsedTime * CFE_FS_BACKGROUND_CREDIT_PER_SECOND) / 1000; + if (State->Credit > CFE_FS_BACKGROUND_MAX_CREDIT) + { + State->Credit = CFE_FS_BACKGROUND_MAX_CREDIT; + } + + /* + * Lock shared data. + * Not strictly necessary as the "CompleteCount" is only updated + * by this task but this helps in case the access isn't atomic. + */ + CFE_FS_LockSharedData(__func__); + + if (CFE_FS.FileDump.CompleteCount != CFE_FS.FileDump.RequestCount) + { + Curr = &CFE_FS.FileDump.Entries[CFE_FS.FileDump.CompleteCount & (CFE_FS_MAX_BACKGROUND_FILE_WRITES - 1)]; + } + + CFE_FS_UnlockSharedData(__func__); + + if (Curr == NULL) + { + return false; + } + + Meta = Curr->Meta; + + if (!OS_ObjectIdDefined(State->Fd) && Meta->IsPending) + { + /* First time processing this entry - open the file */ + Status = OS_OpenCreate(&State->Fd, Meta->FileName, OS_FILE_FLAG_CREATE | OS_FILE_FLAG_TRUNCATE, OS_WRITE_ONLY); + if(Status < 0) + { + State->Fd = OS_OBJECT_ID_UNDEFINED; + Meta->OnEvent(Meta, CFE_FS_FileWriteEvent_CREATE_ERROR, Status, 0, 0, 0); + } + else + { + CFE_FS_InitHeader(&FileHdr, Meta->Description, Meta->FileSubType); + + /* write the cFE header to the file */ + Status = CFE_FS_WriteHeader(State->Fd, &FileHdr); + if (Status != sizeof(CFE_FS_Header_t)) + { + OS_close(State->Fd); + State->Fd = OS_OBJECT_ID_UNDEFINED; + Meta->OnEvent(Meta, CFE_FS_FileWriteEvent_HEADER_WRITE_ERROR, Status, State->RecordNum, sizeof(CFE_FS_Header_t), State->FileSize); + } + else + { + State->FileSize = sizeof(CFE_FS_Header_t); + State->Credit -= sizeof(CFE_FS_Header_t); + State->RecordNum = 0; + } + } + } + + while (OS_ObjectIdDefined(State->Fd) && State->Credit > 0 && !IsEOF) + { + /* + * Getter should return false on EOF (last record), true if more data is still waiting + */ + IsEOF = Meta->GetData(Meta, State->RecordNum, &RecordPtr, &RecordSize); + + /* + * if the getter outputs a record size of 0, this means there is no data for + * this entry, but the cycle keeps going (in case of "holes" or unused table entries + * in the database). + */ + if (RecordSize > 0) + { + State->Credit -= RecordSize; + + /* + * Now write to file + */ + Status = OS_write(State->Fd,RecordPtr,RecordSize); + + if (Status != RecordSize) + { + /* end the file early (cannot set "IsEOF" as this would cause the complete event to be generated too) */ + OS_close(State->Fd); + State->Fd = OS_OBJECT_ID_UNDEFINED; + + /* generate write error event */ + Meta->OnEvent(Meta, CFE_FS_FileWriteEvent_RECORD_WRITE_ERROR, Status, State->RecordNum, RecordSize, State->FileSize); + break; + } + else + { + State->FileSize += RecordSize; + } + } + + ++State->RecordNum; + + } /* end if */ + + /* On normal EOF close the file and generate the complete event */ + if (IsEOF) + { + OS_close(State->Fd); + State->Fd = OS_OBJECT_ID_UNDEFINED; + + /* generate complete event */ + Meta->OnEvent(Meta, CFE_FS_FileWriteEvent_COMPLETE, CFE_SUCCESS, State->RecordNum, 0, State->FileSize); + } + + /* + * if the file is not open, consider this file complete, and advance the head position. + * (done this way so it also catches the case where the file failed to create, not just EOF) + */ + if (!OS_ObjectIdDefined(State->Fd)) + { + CFE_FS_LockSharedData(__func__); + + /* Wipe the entry structure, as it will be reused */ + memset(Curr, 0, sizeof(*Curr)); + ++CFE_FS.FileDump.CompleteCount; + + /* Set the "IsPending" flag to false - this indicates that the originator may re-post now */ + Meta->IsPending = false; + + CFE_FS_UnlockSharedData(__func__); + + } /* end if */ + + return !IsEOF; +} + +/* +** CFE_FS_BackgroundFileDumpRequest - See API and header file for details +*/ +int32 CFE_FS_BackgroundFileDumpRequest(CFE_FS_FileWriteMetaData_t *Meta) +{ + CFE_FS_BackgroundFileDumpEntry_t *Curr; + int32 Status; + uint32 PendingRequestCount; + + /* Pre-validate inputs */ + if (Meta == NULL) + { + return CFE_FS_BAD_ARGUMENT; + } + + /* getter and event functions must be set */ + if (Meta->GetData == NULL || Meta->OnEvent == NULL) + { + return CFE_FS_BAD_ARGUMENT; + } + + /* filename cannot be empty */ + if (Meta->FileName[0] == 0) + { + return CFE_FS_INVALID_PATH; + } + + /* request must not already be pending */ + if (Meta->IsPending) + { + return CFE_STATUS_REQUEST_ALREADY_PENDING; + } + + + CFE_FS_LockSharedData(__func__); + + PendingRequestCount = CFE_FS.FileDump.RequestCount + 1; + + /* Check if queue is full before writing to tail position */ + if (PendingRequestCount == (CFE_FS.FileDump.CompleteCount + CFE_FS_MAX_BACKGROUND_FILE_WRITES)) + { + Status = CFE_STATUS_REQUEST_ALREADY_PENDING; + } + else + { + Curr = &CFE_FS.FileDump.Entries[CFE_FS.FileDump.RequestCount & (CFE_FS_MAX_BACKGROUND_FILE_WRITES - 1)]; + + /* + * store the meta object - note this retains the pointer that was submitted + * (caller must not reuse/change this object until request is completed) + */ + Curr->Meta = Meta; + + /* + * The "IsPending" Flag will be set true whenever while this is waiting in the request queue. + * It will be set false when the file is done. + * + * The requester can check this flag to determine if/when the request is complete + */ + Meta->IsPending = true; + + /* update tail position */ + CFE_FS.FileDump.RequestCount = PendingRequestCount; + + Status = CFE_SUCCESS; + } + + CFE_FS_UnlockSharedData(__func__); + + if (Status == CFE_SUCCESS) + { + /* + * If successfully added to write queue, then wake the ES background task to get started. + * + * This may reduce the overall latency between request and completion (depending on other + * background task work). If this is the only pending job, this should get it started faster. + */ + CFE_ES_BackgroundWakeup(); + } + + return Status; +} + +/* +** CFE_FS_ExtractFilenameFromPath - See API and header file for details +*/ +bool CFE_FS_BackgroundFileDumpIsPending(const CFE_FS_FileWriteMetaData_t *Meta) +{ + if (Meta == NULL) + { + return false; + } + + return Meta->IsPending; +} /************************/ /* End of File Comment */ diff --git a/fsw/cfe-core/src/fs/cfe_fs_priv.h b/fsw/cfe-core/src/fs/cfe_fs_priv.h index 199791ada..79fdfb63d 100644 --- a/fsw/cfe-core/src/fs/cfe_fs_priv.h +++ b/fsw/cfe-core/src/fs/cfe_fs_priv.h @@ -44,11 +44,93 @@ ** Macro Definitions */ +/* + * Max Number of file write requests that can be queued + * + * This needs to be a power of two to simplify the masking/wraparound (bitwise AND). + */ +#define CFE_FS_MAX_BACKGROUND_FILE_WRITES 4 + + +/* + * Background file credit accumulation rate + * + * The background file writer will limit the total bytes written over time. This + * controls the amount of "credit" (bytes that can be written) per second + * of elapsed time. + * + * This permits a file writing rate of up to 10kbytes/sec. + */ +#define CFE_FS_BACKGROUND_CREDIT_PER_SECOND 10000 + +/* + * Maximum credit that the background write task can accumulate + * + * The background file writer will limit the total bytes written over time, and + * will accumulate credit against this limit while no writes are in progress. + * This is an upper cap on the amount of credit that can be accumulated. + * + * Without this limit, after a long period of inactivity without any file + * writes, a large credit would essentially bypass the rate limiting for + * the next file write command(s) once they are issued. + */ +#define CFE_FS_BACKGROUND_MAX_CREDIT 10000 /* ** Type Definitions */ +/* + * Background file dump entry structure + * + * This structure is stored in global memory and keeps the state + * of the file dump from one iteration to the next. + */ +typedef struct +{ + CFE_ES_AppId_t RequestorAppId; + CFE_FS_FileWriteMetaData_t *Meta; +} CFE_FS_BackgroundFileDumpEntry_t; + +typedef struct +{ + osal_id_t Fd; + int32 Credit; + uint32 RecordNum; + size_t FileSize; +} CFE_FS_CurrentFileState_t; + + +/** + * \brief Background file dump queue structure + * + * This structure is stored in global memory and keeps the state + * of the file dump from one iteration to the next. + * + * Normally when idle the "RequestCount" and "CompleteCount" are the + * same value. When an application requests a background file dump, + * the "RequestCount" is incremented accordingly, and when the background + * job finishes, the "CompleteCount" is incremented accordingly. + */ +typedef struct +{ + uint32 RequestCount; /**< Total Number of background file writes requested */ + uint32 CompleteCount; /**< Total Number of background file writes completed */ + + /** + * Data related to each background file write request + */ + CFE_FS_BackgroundFileDumpEntry_t Entries[CFE_FS_MAX_BACKGROUND_FILE_WRITES]; + + /** + * Persistent storage for the current file write + * (reused for each file) + */ + CFE_FS_CurrentFileState_t Current; + +} CFE_FS_BackgroundFileDumpState_t; + + /****************************************************************************** ** Typedef: CFE_FS_t ** @@ -59,8 +141,13 @@ typedef struct { osal_id_t SharedDataMutexId; + CFE_FS_BackgroundFileDumpState_t FileDump; + } CFE_FS_t; + +extern CFE_FS_t CFE_FS; + /* ** FS Function Prototypes */ diff --git a/fsw/cfe-core/src/inc/cfe_error.h b/fsw/cfe-core/src/inc/cfe_error.h index 872c89f03..98b82dc26 100644 --- a/fsw/cfe-core/src/inc/cfe_error.h +++ b/fsw/cfe-core/src/inc/cfe_error.h @@ -172,6 +172,15 @@ typedef int32 CFE_Status_t; */ #define CFE_STATUS_EXTERNAL_RESOURCE_FAIL ((int32)0xc8000005) +/** + * @brief Request already pending + * + * Commands or requests are already pending or the pending request + * limit has been reached. No more requests can be made until + * the current request(s) complete. + */ +#define CFE_STATUS_REQUEST_ALREADY_PENDING ((int32)0xc8000006) + /** * @brief Not Implemented * diff --git a/fsw/cfe-core/src/inc/cfe_es.h b/fsw/cfe-core/src/inc/cfe_es.h index 310dd7f30..f0a91da8b 100644 --- a/fsw/cfe-core/src/inc/cfe_es.h +++ b/fsw/cfe-core/src/inc/cfe_es.h @@ -1087,6 +1087,26 @@ void CFE_ES_ExitChildTask(void); * @{ */ +/*****************************************************************************/ +/** +** \brief Wakes up the CFE background task +** +** \par Description +** Normally the ES background task wakes up at a periodic interval. +** Whenever new background work is added, this can be used to wake the task early, +** which may reduce the delay between adding the job and the job getting processed. +** +** \par Assumptions, External Events, and Notes: +** Note the amount of work that the background task will perform is pro-rated +** based on the amount of time elapsed since the last wakeup. Waking the task +** early will not cause the background task to do more work than it otherwise +** would - it just reduces the delay before work starts initially. +** +** +******************************************************************************/ +void CFE_ES_BackgroundWakeup(void); + + /*****************************************************************************/ /** ** \brief Write a string to the cFE System Log diff --git a/fsw/cfe-core/src/inc/cfe_fs.h b/fsw/cfe-core/src/inc/cfe_fs.h index d6230ab77..91d4b98af 100644 --- a/fsw/cfe-core/src/inc/cfe_fs.h +++ b/fsw/cfe-core/src/inc/cfe_fs.h @@ -42,6 +42,66 @@ #include "common_types.h" #include "cfe_time.h" +/* + * Because FS is a library not an app, it does not have its own context or + * event IDs. The file writer runs in the context of the ES background task + * on behalf of whatever App requested the file write. + * + * This is a list of abstract events associated with background file write jobs. + * An app requesting the file write must supply a callback function to translate + * these into its own event IDs for feedback (i.e. file complete, error conditions, etc). + */ +typedef enum +{ + CFE_FS_FileWriteEvent_UNDEFINED, /* placeholder, no-op, keep as 0 */ + + CFE_FS_FileWriteEvent_COMPLETE, /**< File is completed successfully */ + CFE_FS_FileWriteEvent_CREATE_ERROR, /**< Unable to create/open file */ + CFE_FS_FileWriteEvent_HEADER_WRITE_ERROR, /**< Unable to write FS header */ + CFE_FS_FileWriteEvent_RECORD_WRITE_ERROR, /**< Unable to write data record */ + + CFE_FS_FileWriteEvent_MAX /* placeholder, no-op, keep last */ + +} CFE_FS_FileWriteEvent_t; + + +/** + * Data Getter routine provided by requester + * + * Outputs a data block. Should return true if the file is complete (last record/EOF), otherwise return false. + */ +typedef bool (*CFE_FS_FileWriteGetData_t)(void *Meta, uint32 RecordNum, void **Buffer, size_t *BufSize); + +/** + * Event generator routine provided by requester + * + * Invoked from certain points in the file write process. Implementation may invoke CFE_EVS_SendEvent() appropriately + * to inform of progress. + */ +typedef void (*CFE_FS_FileWriteOnEvent_t)(void *Meta, CFE_FS_FileWriteEvent_t Event, int32 Status, uint32 RecordNum, size_t BlockSize, size_t Position); + +/** + * \brief External Metadata/State object associated with background file writes + * + * Applications intending to schedule background file write jobs should instantiate + * this object in static/global data memory. This keeps track of the state of the + * file write request(s). + */ +typedef struct CFE_FS_FileWriteMetaData +{ + volatile bool IsPending; /**< Whether request is pending (volatile as it may be checked outside lock) */ + + char FileName[OS_MAX_PATH_LEN]; /**< Name of file to write */ + + /* Data for FS header */ + uint32 FileSubType; /**< Type of file to write (for FS header) */ + char Description[CFE_FS_HDR_DESC_MAX_LEN]; /**< Description of file (for FS header) */ + + CFE_FS_FileWriteGetData_t GetData; /**< Application callback to get a data record */ + CFE_FS_FileWriteOnEvent_t OnEvent; /**< Application callback for abstract event processing */ + +} CFE_FS_FileWriteMetaData_t; + /** @defgroup CFEAPIFSHeader cFE File Header Management APIs * @{ @@ -183,6 +243,63 @@ CFE_Status_t CFE_FS_SetTimestamp(osal_id_t FileDes, CFE_TIME_SysTime_t NewTimest ** ******************************************************************************/ CFE_Status_t CFE_FS_ExtractFilenameFromPath(const char *OriginalPath, char *FileNameOnly); + +/*****************************************************************************/ +/** +** \brief Register a background file dump request +** +** \par Description +** Puts the previously-initialized metadata into the pending request queue +** +** \par Assumptions, External Events, and Notes: +** Metadata structure should be stored in a static memory area (not on heap) as it +** must persist and be accessible by the file writer task throughout the asynchronous +** job operation. +** +** \param[inout] Meta The background file write persistent state object +** +** \return Execution status, see \ref CFEReturnCodes +** +******************************************************************************/ +int32 CFE_FS_BackgroundFileDumpRequest(CFE_FS_FileWriteMetaData_t *Meta); + +/*****************************************************************************/ +/** +** \brief Query if a background file write request is currently pending +** +** \par Description +** This returns "true" while the request is on the background work queue +** This returns "false" once the request is complete and removed from the queue. +** +** \par Assumptions, External Events, and Notes: +** None +** +** \param[inout] Meta The background file write persistent state object +** +** \return true if request is already pending, false if not +** +******************************************************************************/ +bool CFE_FS_BackgroundFileDumpIsPending(const CFE_FS_FileWriteMetaData_t *Meta); + +/*****************************************************************************/ +/** +** \brief Execute the background file write job(s) +** +** \par Description +** Runs the state machine associated with background file write requests +** +** \par Assumptions, External Events, and Notes: +** This should only be invoked as a background job from the ES background task, +** it should not be invoked directly. +** +** \param[in] ElapsedTime The amount of time passed since last invocation (ms) +** \param[in] Arg Not used/ignored +** +** \return true if jobs are pending, false if idle +** +******************************************************************************/ +bool CFE_FS_RunBackgroundFileDump(uint32 ElapsedTime, void *Arg); + /**@}*/ #endif /* _cfe_fs_ */ diff --git a/fsw/cfe-core/src/inc/cfe_sb_msg.h b/fsw/cfe-core/src/inc/cfe_sb_msg.h index e9a67517c..02bf729a5 100644 --- a/fsw/cfe-core/src/inc/cfe_sb_msg.h +++ b/fsw/cfe-core/src/inc/cfe_sb_msg.h @@ -604,11 +604,11 @@ typedef struct CFE_SB_PipeDepthStats { CFE_SB_PipeId_t PipeId;/**< \cfetlmmnemonic \SB_PDPIPEID \brief Pipe Id associated with the stats below */ - uint16 Depth;/**< \cfetlmmnemonic \SB_PDDEPTH + uint16 MaxQueueDepth;/**< \cfetlmmnemonic \SB_PDDEPTH \brief Number of messages the pipe can hold */ - uint16 InUse;/**< \cfetlmmnemonic \SB_PDINUSE + uint16 CurrentQueueDepth;/**< \cfetlmmnemonic \SB_PDINUSE \brief Number of messages currently on the pipe */ - uint16 PeakInUse;/**< \cfetlmmnemonic \SB_PDPKINUSE + uint16 PeakQueueDepth;/**< \cfetlmmnemonic \SB_PDPKINUSE \brief Peak number of messages that have been on the pipe */ uint16 Spare;/**< \cfetlmmnemonic \SB_PDSPARE \brief Spare word to ensure alignment */ diff --git a/fsw/cfe-core/src/sb/cfe_sb_api.c b/fsw/cfe-core/src/sb/cfe_sb_api.c index 1d1620a88..0e0ddc7ef 100644 --- a/fsw/cfe-core/src/sb/cfe_sb_api.c +++ b/fsw/cfe-core/src/sb/cfe_sb_api.c @@ -194,9 +194,9 @@ int32 CFE_SB_CreatePipe(CFE_SB_PipeId_t *PipeIdPtr, uint16 Depth, const char * if (Status == CFE_SUCCESS) { /* fill in the pipe table fields */ - PipeDscPtr->SysQueueId = SysQueueId; - PipeDscPtr->QueueDepth = Depth; - PipeDscPtr->AppId = AppId; + PipeDscPtr->SysQueueId = SysQueueId; + PipeDscPtr->MaxQueueDepth = Depth; + PipeDscPtr->AppId = AppId; CFE_SB_PipeDescSetUsed(PipeDscPtr, PendingPipeId); @@ -1692,10 +1692,10 @@ int32 CFE_SB_TransmitBufferFull(CFE_SB_BufferD_t *BufDscPtr, DestPtr->BuffCount++; /* used for checking MsgId2PipeLimit */ DestPtr->DestCnt++; /* used for statistics */ - ++PipeDscPtr->QueueDepth; - if (PipeDscPtr->QueueDepth >= PipeDscPtr->PeakDepth) + ++PipeDscPtr->CurrentQueueDepth; + if (PipeDscPtr->CurrentQueueDepth >= PipeDscPtr->PeakQueueDepth) { - PipeDscPtr->PeakDepth = PipeDscPtr->QueueDepth; + PipeDscPtr->PeakQueueDepth = PipeDscPtr->CurrentQueueDepth; } Status = CFE_SUCCESS; @@ -2001,9 +2001,9 @@ int32 CFE_SB_ReceiveBuffer(CFE_SB_Buffer_t **BufPtr, DestPtr->BuffCount--; } - if (PipeDscPtr->CurrentDepth > 0) + if (PipeDscPtr->CurrentQueueDepth > 0) { - --PipeDscPtr->CurrentDepth; + --PipeDscPtr->CurrentQueueDepth; } } else diff --git a/fsw/cfe-core/src/sb/cfe_sb_priv.h b/fsw/cfe-core/src/sb/cfe_sb_priv.h index 808645f64..69e5a58e6 100644 --- a/fsw/cfe-core/src/sb/cfe_sb_priv.h +++ b/fsw/cfe-core/src/sb/cfe_sb_priv.h @@ -152,10 +152,10 @@ typedef struct { uint8 Spare; CFE_ES_AppId_t AppId; osal_id_t SysQueueId; - uint16 QueueDepth; uint16 SendErrors; - uint16 CurrentDepth; - uint16 PeakDepth; + uint16 MaxQueueDepth; + uint16 CurrentQueueDepth; + uint16 PeakQueueDepth; CFE_SB_BufferD_t *LastBuffer; } CFE_SB_PipeD_t; @@ -172,6 +172,43 @@ typedef struct { } CFE_SB_MemParams_t; +/*******************************************************************************/ +/** +** \brief SB route info temporary structure +** +** This tracks the number of desinations along with destination data for 1 route. +** Each route may contain zero or more desinations (variable length). +*/ +typedef struct +{ + uint32 NumDestinations; + CFE_SB_RoutingFileEntry_t DestEntries[CFE_PLATFORM_SB_MAX_DEST_PER_PKT]; /**< Actual data written to file */ +} CFE_SB_BackgroundRouteInfoBuffer_t; + +/** + * \brief Temporary holding buffer for records being written to a file. + * + * This is shared/reused between all file types (msg map, route info, pipe info). + */ +typedef union +{ + CFE_SB_BackgroundRouteInfoBuffer_t RouteInfo; + CFE_SB_PipeInfoEntry_t PipeInfo; + CFE_SB_MsgMapFileEntry_t MsgMapInfo; +} CFE_SB_BackgroundFileBuffer_t; + +/** + * \brief SB Background file write state information + * + * Must be stored in persistent memory (e.g. global). + */ +typedef struct +{ + CFE_FS_FileWriteMetaData_t FileWrite; /**< FS state data - must be first */ + CFE_SB_BackgroundFileBuffer_t Buffer; /**< Temporary holding area for file record */ +} CFE_SB_BackgroundFileStateInfo_t; + + /****************************************************************************** ** Typedef: cfe_sb_t @@ -194,6 +231,8 @@ typedef struct CFE_SB_AllSubscriptionsTlm_t PrevSubMsg; CFE_EVS_BinFilter_t EventFilters[CFE_SB_MAX_CFG_FILE_EVENTS_TO_FILTER]; CFE_ResourceId_t LastPipeId; + + CFE_SB_BackgroundFileStateInfo_t BackgroundFile; } cfe_sb_t; @@ -263,7 +302,6 @@ int32 CFE_SB_TransmitMsgValidate(CFE_MSG_Message_t *MsgPtr, CFE_SB_MsgId_t *MsgIdPtr, CFE_MSG_Size_t *SizePtr, CFE_SBR_RouteId_t *RouteIdPtr); -int32 CFE_SB_SendRtgInfo(const char *Filename); int32 CFE_SB_SendPipeInfo(const char *Filename); int32 CFE_SB_SendMapInfo(const char *Filename); int32 CFE_SB_ZeroCopyReleaseDesc(CFE_SB_Buffer_t *Ptr2Release, CFE_SB_ZeroCopyHandle_t BufferHandle); @@ -273,7 +311,6 @@ void CFE_SB_DecrBufUseCnt(CFE_SB_BufferD_t *bd); int32 CFE_SB_ValidateMsgId(CFE_SB_MsgId_t MsgId); int32 CFE_SB_ValidatePipeId(CFE_SB_PipeId_t PipeId); void CFE_SB_IncrCmdCtr(int32 status); -void CFE_SB_FileWriteByteCntErr(const char *Filename,uint32 Requested,uint32 Actual); void CFE_SB_SetSubscriptionReporting(uint32 state); int32 CFE_SB_SendSubscriptionReport(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId, CFE_SB_Qos_t Quality); uint32 CFE_SB_RequestToSendEvent(CFE_ES_TaskId_t TaskId, uint32 Bit); @@ -472,6 +509,16 @@ static inline bool CFE_SB_PipeDescIsMatch(const CFE_SB_PipeD_t *PipeDscPtr, CFE_ bool CFE_SB_CheckPipeDescSlotUsed(CFE_ResourceId_t CheckId); +/* + * Helper functions for background file write requests (callbacks) + */ +void CFE_SB_CollectMsgMapInfo(CFE_SBR_RouteId_t RouteId, void *ArgPtr); +bool CFE_SB_WriteMsgMapInfoDataGetter(void *Meta, uint32 RecordNum, void **Buffer, size_t *BufSize); +void CFE_SB_CollectRouteInfo(CFE_SBR_RouteId_t RouteId, void *ArgPtr); +bool CFE_SB_WriteRouteInfoDataGetter(void *Meta, uint32 RecordNum, void **Buffer, size_t *BufSize); +bool CFE_SB_WritePipeInfoDataGetter(void *Meta, uint32 RecordNum, void **Buffer, size_t *BufSize); +void CFE_SB_BackgroundFileEventHandler(void *Meta, CFE_FS_FileWriteEvent_t Event, int32 Status, uint32 RecordNum, size_t BlockSize, size_t Position); + /* * External variables private to the software bus module */ diff --git a/fsw/cfe-core/src/sb/cfe_sb_task.c b/fsw/cfe-core/src/sb/cfe_sb_task.c index 5595a81df..c1873952f 100644 --- a/fsw/cfe-core/src/sb/cfe_sb_task.c +++ b/fsw/cfe-core/src/sb/cfe_sb_task.c @@ -48,16 +48,6 @@ cfe_sb_t CFE_SB; CFE_SB_Qos_t CFE_SB_Default_Qos; -/* Local structure for file writing callbacks */ -typedef struct -{ - const char *Filename; /* File name for error reporting */ - osal_id_t Fd; /* File id for writing */ - uint32 FileSize; /* File size for reporting */ - uint32 EntryCount; /* Entry count for reporting */ - int32 Status; /* File write status */ -} CFE_SB_FileWriteCallback_t; - /****************************************************************************** ** Function: CFE_SB_TaskMain() ** @@ -807,9 +797,11 @@ int32 CFE_SB_SendStatsCmd(const CFE_SB_SendSbStatsCmd_t *data) if (CFE_SB_PipeDescIsUsed(PipeDscPtr)) { PipeStatPtr->PipeId = PipeDscPtr->PipeId; - PipeStatPtr->InUse = PipeDscPtr->CurrentDepth; - PipeStatPtr->PeakInUse = PipeDscPtr->PeakDepth; - PipeStatPtr->Depth = PipeDscPtr->QueueDepth; + + /* Copy depth info */ + PipeStatPtr->CurrentQueueDepth = PipeDscPtr->CurrentQueueDepth; + PipeStatPtr->PeakQueueDepth = PipeDscPtr->PeakQueueDepth; + PipeStatPtr->MaxQueueDepth = PipeDscPtr->MaxQueueDepth; ++PipeStatPtr; --PipeStatCount; @@ -840,180 +832,79 @@ int32 CFE_SB_SendStatsCmd(const CFE_SB_SendSbStatsCmd_t *data) return CFE_SUCCESS; }/* CFE_SB_SendStatsCmd */ - -/****************************************************************************** -** Function: CFE_SB_SendRoutingInfoCmd() -** -** Purpose: -** SB internal function to handle processing of 'Send Routing Info' Cmd -** -** Arguments: -** None -** -** Return: -** None -*/ -int32 CFE_SB_SendRoutingInfoCmd(const CFE_SB_SendRoutingInfoCmd_t *data) -{ - const CFE_SB_WriteFileInfoCmd_Payload_t *ptr; - char LocalFilename[OS_MAX_PATH_LEN]; - int32 Stat; - - ptr = &data->Payload; - - CFE_SB_MessageStringGet(LocalFilename, ptr->Filename, CFE_PLATFORM_SB_DEFAULT_ROUTING_FILENAME, - sizeof(LocalFilename), sizeof(ptr->Filename)); - - Stat = CFE_SB_SendRtgInfo(LocalFilename); - CFE_SB_IncrCmdCtr(Stat); - - return CFE_SUCCESS; -}/* end CFE_SB_SendRoutingInfoCmd */ - - -/****************************************************************************** -** Function: CFE_SB_SendPipeInfoCmd() -** -** Purpose: -** SB internal function to handle processing of 'Send Pipe Info' Cmd -** -** Arguments: -** None -** -** Return: -** None -*/ -int32 CFE_SB_SendPipeInfoCmd(const CFE_SB_SendPipeInfoCmd_t *data) -{ - const CFE_SB_WriteFileInfoCmd_Payload_t *ptr; - char LocalFilename[OS_MAX_PATH_LEN]; - int32 Stat; - - ptr = &data->Payload; - - CFE_SB_MessageStringGet(LocalFilename, ptr->Filename, CFE_PLATFORM_SB_DEFAULT_PIPE_FILENAME, - sizeof(LocalFilename), sizeof(ptr->Filename)); - - Stat = CFE_SB_SendPipeInfo(LocalFilename); - CFE_SB_IncrCmdCtr(Stat); - - return CFE_SUCCESS; -}/* end CFE_SB_SendPipeInfoCmd */ - - -/****************************************************************************** -** Function: CFE_SB_SendMapInfoCmd() -** -** Purpose: -** SB internal function to handle processing of 'Send Map Info' Cmd -** -** Arguments: -** None -** -** Return: -** None -*/ -int32 CFE_SB_SendMapInfoCmd(const CFE_SB_SendMapInfoCmd_t *data) -{ - const CFE_SB_WriteFileInfoCmd_Payload_t *ptr; - char LocalFilename[OS_MAX_PATH_LEN]; - int32 Stat; - - ptr = &data->Payload; - - CFE_SB_MessageStringGet(LocalFilename, ptr->Filename, CFE_PLATFORM_SB_DEFAULT_MAP_FILENAME, - sizeof(LocalFilename), sizeof(ptr->Filename)); - - Stat = CFE_SB_SendMapInfo(LocalFilename); - - CFE_SB_IncrCmdCtr(Stat); - - return CFE_SUCCESS; -}/* end CFE_SB_SendMapInfoCmd */ - /****************************************************************************** * Local callback helper for writing routing info to a file */ -void CFE_SB_WriteRouteToFile(CFE_SBR_RouteId_t RouteId, void *ArgPtr) +void CFE_SB_CollectRouteInfo(CFE_SBR_RouteId_t RouteId, void *ArgPtr) { - struct RouteInfo - { - CFE_SB_PipeId_t PipeId; - uint8 Active; - uint16 DestCnt; - }; - - CFE_SB_FileWriteCallback_t *args; - CFE_SB_DestinationD_t *destptr; - CFE_SB_PipeD_t *pipedptr; - int32 status; - CFE_SB_RoutingFileEntry_t entry; - struct RouteInfo RouteInfo[CFE_PLATFORM_SB_MAX_DEST_PER_PKT]; - struct RouteInfo *RouteInfoPtr; - uint32 NumDest; + CFE_SB_DestinationD_t *DestPtr; + CFE_SB_PipeD_t *PipeDscPtr; + CFE_SB_MsgId_t RouteMsgId; + CFE_SB_BackgroundRouteInfoBuffer_t *RouteBufferPtr; + CFE_SB_RoutingFileEntry_t *FileEntryPtr; + CFE_ES_AppId_t DestAppId[CFE_PLATFORM_SB_MAX_DEST_PER_PKT]; + uint32 i; /* Cast arguments for local use */ - args = (CFE_SB_FileWriteCallback_t *)ArgPtr; - - NumDest = 0; + RouteBufferPtr = (CFE_SB_BackgroundRouteInfoBuffer_t *)ArgPtr; + /* Extract data from runtime info, write into the temporary buffer */ /* Data must be locked to snapshot the route info */ CFE_SB_LockSharedData(__FILE__, __LINE__); - destptr = CFE_SBR_GetDestListHeadPtr(RouteId); - entry.MsgId = CFE_SBR_GetMsgId(RouteId); - RouteInfoPtr = RouteInfo; + RouteMsgId = CFE_SBR_GetMsgId(RouteId); + RouteBufferPtr->NumDestinations = 0; - while((destptr != NULL) && NumDest < CFE_PLATFORM_SB_MAX_DEST_PER_PKT) + /* If this is a valid route, get the destinations */ + if (CFE_SB_IsValidMsgId(RouteMsgId)) { - pipedptr = CFE_SB_LocatePipeDescByID(destptr->PipeId); + DestPtr = CFE_SBR_GetDestListHeadPtr(RouteId); - /* If invalid id, continue on to next entry */ - if (CFE_SB_PipeDescIsMatch(pipedptr,destptr->PipeId)) + /* copy relevant data from the destination list into the temp buffer */ + while(DestPtr != NULL && RouteBufferPtr->NumDestinations < CFE_PLATFORM_SB_MAX_DEST_PER_PKT) { - RouteInfoPtr->PipeId = destptr->PipeId; - RouteInfoPtr->Active = destptr->Active; - RouteInfoPtr->DestCnt = destptr->DestCnt; - ++RouteInfoPtr; - ++NumDest; - } + PipeDscPtr = CFE_SB_LocatePipeDescByID(DestPtr->PipeId); - destptr = destptr->Next; + /* If invalid id, continue on to next entry */ + if (CFE_SB_PipeDescIsMatch(PipeDscPtr,DestPtr->PipeId)) + { + FileEntryPtr = &RouteBufferPtr->DestEntries[RouteBufferPtr->NumDestinations]; + + /* clear all fields in the temp buffer before re-use */ + memset(FileEntryPtr, 0, sizeof(*FileEntryPtr)); + + FileEntryPtr->PipeId = DestPtr->PipeId; + FileEntryPtr->State = DestPtr->Active; + FileEntryPtr->MsgCnt = DestPtr->DestCnt; + + /* Stash the Pipe Owner AppId - App Name is looked up later (comes from ES) */ + DestAppId[RouteBufferPtr->NumDestinations] = PipeDscPtr->AppId; + + ++RouteBufferPtr->NumDestinations; + } + + DestPtr = DestPtr->Next; + } } CFE_SB_UnlockSharedData(__FILE__, __LINE__); - RouteInfoPtr = RouteInfo; - while (NumDest > 0) + /* Go through the temp buffer and fill in the remaining info for each dest */ + FileEntryPtr = RouteBufferPtr->DestEntries; + for(i = 0; i < RouteBufferPtr->NumDestinations; ++i) { - entry.PipeId = RouteInfoPtr->PipeId; - entry.State = RouteInfoPtr->Active; - entry.MsgCnt = RouteInfoPtr->DestCnt; - - entry.AppName[0] = 0; + /* All dest entries refer to the same MsgId (based on the route) */ + FileEntryPtr->MsgId = RouteMsgId; /* - * NOTE: as long as CFE_ES_GetAppName() returns success, then it - * guarantees null termination of the output. Return code is not - * checked here (bad) but in case of error it does not seem to touch - * the buffer, therefore the initialization above will protect for now + * NOTE: as long as CFE_ES_GetAppName() is given a nonzero-length + * output buffer, it guarantees null termination of the output, even + * if the AppID is invalid - in which case it returns an empty string. */ - CFE_ES_GetAppName(entry.AppName, pipedptr->AppId, sizeof(entry.AppName)); - CFE_SB_GetPipeName(entry.PipeName, sizeof(entry.PipeName), entry.PipeId); - - status = OS_write (args->Fd, &entry, sizeof(CFE_SB_RoutingFileEntry_t)); - if(status != sizeof(CFE_SB_RoutingFileEntry_t)) - { - CFE_SB_FileWriteByteCntErr(args->Filename, sizeof(CFE_SB_RoutingFileEntry_t), status); - OS_close(args->Fd); - args->Status = CFE_SB_FILE_IO_ERR; - } + CFE_ES_GetAppName(FileEntryPtr->AppName, DestAppId[i], sizeof(FileEntryPtr->AppName)); + CFE_SB_GetPipeName(FileEntryPtr->PipeName, sizeof(FileEntryPtr->PipeName), FileEntryPtr->PipeId); - args->FileSize += status; - args->EntryCount++; - - ++RouteInfoPtr; - --NumDest; + ++FileEntryPtr; } } @@ -1058,272 +949,386 @@ int32 CFE_SB_SendSubscriptionReport(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId return Status; } +bool CFE_SB_WriteRouteInfoDataGetter(void *Meta, uint32 RecordNum, void **Buffer, size_t *BufSize) +{ + CFE_SB_BackgroundFileStateInfo_t *BgFilePtr; + CFE_SBR_Throttle_t Throttle; + + /* Cast arguments for local use */ + BgFilePtr = (CFE_SB_BackgroundFileStateInfo_t *)Meta; + + Throttle.StartIndex = RecordNum; + Throttle.MaxLoop = 1; + Throttle.NextIndex = 0; + + /* Reset NumDestinations to 0, just in case the CFE_SBR_ForEachRouteId() is a no-op */ + BgFilePtr->Buffer.RouteInfo.NumDestinations = 0; + + /* Collect info on the next route (limited to one per cycle via throttle) */ + CFE_SBR_ForEachRouteId(CFE_SB_CollectRouteInfo, &BgFilePtr->Buffer.RouteInfo, &Throttle); + + /* Pass the output of CFE_SB_CollectRouteInfo() back to be written */ + *Buffer = &BgFilePtr->Buffer.RouteInfo.DestEntries; + *BufSize = sizeof(CFE_SB_RoutingFileEntry_t) * BgFilePtr->Buffer.RouteInfo.NumDestinations; + + /* Check for EOF (last entry) - NextIndex is nonzero if more records left, zero at the end of the route table */ + return (Throttle.NextIndex == 0); +} + +void CFE_SB_BackgroundFileEventHandler(void *Meta, CFE_FS_FileWriteEvent_t Event, int32 Status, uint32 RecordNum, size_t BlockSize, size_t Position) +{ + CFE_SB_BackgroundFileStateInfo_t *BgFilePtr; + + BgFilePtr = (CFE_SB_BackgroundFileStateInfo_t *)Meta; + + /* + * Note that this runs in the context of ES background task (file writer background job) + * It does NOT run in the context of the CFE_TBL app task. + * + * Events should use CFE_EVS_SendEventWithAppID() rather than CFE_EVS_SendEvent() + * to get proper association with TBL task. + */ + switch(Event) + { + case CFE_FS_FileWriteEvent_COMPLETE: + CFE_EVS_SendEventWithAppID(CFE_SB_SND_RTG_EID,CFE_EVS_EventType_DEBUG, + CFE_SB.AppId, + "%s written:Size=%d,Entries=%d", + BgFilePtr->FileWrite.FileName, (int)Position, (int)RecordNum); + break; + + case CFE_FS_FileWriteEvent_HEADER_WRITE_ERROR: + case CFE_FS_FileWriteEvent_RECORD_WRITE_ERROR: + CFE_EVS_SendEventWithAppID(CFE_SB_FILEWRITE_ERR_EID,CFE_EVS_EventType_ERROR, + CFE_SB.AppId, + "File write,byte cnt err,file %s,request=%d,actual=%d", + BgFilePtr->FileWrite.FileName, (int)BlockSize, (int)Status); + break; + + case CFE_FS_FileWriteEvent_CREATE_ERROR: + CFE_EVS_SendEventWithAppID(CFE_SB_SND_RTG_ERR1_EID, CFE_EVS_EventType_ERROR, + CFE_SB.AppId, + "Error creating file %s, stat=0x%x", + BgFilePtr->FileWrite.FileName, (int)Status); + break; + + default: + /* unhandled event - ignore */ + break; + } +} /****************************************************************************** -** Function: CFE_SB_SendRoutingInfo() +** Function: CFE_SB_SendRoutingInfoCmd() ** ** Purpose: -** SB internal function to write the routing information to a file +** SB internal function to handle processing of 'Send Routing Info' Cmd ** ** Arguments: -** Pointer to a filename +** None ** ** Return: -** CFE_SB_FILE_IO_ERR for file I/O errors or CFE_SUCCESS +** None */ -int32 CFE_SB_SendRtgInfo(const char *Filename) +int32 CFE_SB_SendRoutingInfoCmd(const CFE_SB_SendRoutingInfoCmd_t *data) { - CFE_SB_FileWriteCallback_t args = {0}; + const CFE_SB_WriteFileInfoCmd_Payload_t *CmdPtr; + CFE_SB_BackgroundFileStateInfo_t *StatePtr; int32 Status; - CFE_FS_Header_t FileHdr; - - Status = OS_OpenCreate(&args.Fd, Filename, OS_FILE_FLAG_CREATE | OS_FILE_FLAG_TRUNCATE, OS_WRITE_ONLY); - if(Status < OS_SUCCESS) - { - CFE_EVS_SendEvent(CFE_SB_SND_RTG_ERR1_EID, CFE_EVS_EventType_ERROR, - "Error creating file %s, stat=0x%x", - Filename, (unsigned int)Status); - return CFE_SB_FILE_IO_ERR; - } - /* clear out the cfe file header fields, then populate description and subtype */ - CFE_FS_InitHeader(&FileHdr, "SB Routing Information", CFE_FS_SubType_SB_ROUTEDATA); + StatePtr = &CFE_SB.BackgroundFile; + CmdPtr = &data->Payload; - Status = CFE_FS_WriteHeader(args.Fd, &FileHdr); - if(Status != sizeof(CFE_FS_Header_t)) + /* If a routing info dump was already pending, do not overwrite the current request */ + if (!CFE_FS_BackgroundFileDumpIsPending(&StatePtr->FileWrite)) { - CFE_SB_FileWriteByteCntErr(Filename,sizeof(CFE_FS_Header_t),Status); - OS_close(args.Fd); - return CFE_SB_FILE_IO_ERR; - } + /* Reset the entire state object (just for good measure, ensure no stale data) */ + memset(StatePtr, 0, sizeof(*StatePtr)); + + /* Copy the commanded filename into local buffer to ensure size limitation and to allow for modification */ + CFE_SB_MessageStringGet(StatePtr->FileWrite.FileName, CmdPtr->Filename, CFE_PLATFORM_SB_DEFAULT_ROUTING_FILENAME, + sizeof(StatePtr->FileWrite.FileName), sizeof(CmdPtr->Filename)); - /* Initialize the reset of the nonzero callback argument elements */ - args.FileSize = Status; - args.Filename = Filename; + /* + * Fill out the remainder of meta data. + * This data is currently the same for every request + */ + StatePtr->FileWrite.FileSubType = CFE_FS_SubType_SB_ROUTEDATA; + snprintf(StatePtr->FileWrite.Description, sizeof(StatePtr->FileWrite.Description), "SB Routing Information"); - /* Write route info to file */ - CFE_SBR_ForEachRouteId(CFE_SB_WriteRouteToFile, &args, NULL); + StatePtr->FileWrite.GetData = CFE_SB_WriteRouteInfoDataGetter; + StatePtr->FileWrite.OnEvent = CFE_SB_BackgroundFileEventHandler; - if (args.Status != 0) - { - return args.Status; + Status = CFE_FS_BackgroundFileDumpRequest(&StatePtr->FileWrite); } else { - OS_close(args.Fd); - CFE_EVS_SendEvent(CFE_SB_SND_RTG_EID,CFE_EVS_EventType_DEBUG, - "%s written:Size=%d,Entries=%d", - Filename, (int)args.FileSize, (int)args.EntryCount); - return CFE_SUCCESS; + Status = CFE_STATUS_REQUEST_ALREADY_PENDING; } - -}/* end CFE_SB_SendRtgInfo */ + + if (Status != CFE_SUCCESS) + { + /* generate the same event as is generated when unable to create the file (same thing, really) */ + CFE_SB_BackgroundFileEventHandler(StatePtr, CFE_FS_FileWriteEvent_CREATE_ERROR, Status, 0, 0, 0); + } + + CFE_SB_IncrCmdCtr(Status); + + return CFE_SUCCESS; +}/* end CFE_SB_SendRoutingInfoCmd */ -/****************************************************************************** -** Function: CFE_SB_SendPipeInfo() -** -** Purpose: -** SB internal function to write the Pipe table to a file -** -** Arguments: -** Pointer to a filename -** -** Return: -** CFE_SB_FILE_IO_ERR for file I/O errors or CFE_SUCCESS -*/ -int32 CFE_SB_SendPipeInfo(const char *Filename) +bool CFE_SB_WritePipeInfoDataGetter(void *Meta, uint32 RecordNum, void **Buffer, size_t *BufSize) { - uint16 i; - osal_id_t fd; - int32 Status; - uint32 FileSize = 0; - uint32 EntryCount = 0; - CFE_FS_Header_t FileHdr; + CFE_SB_BackgroundFileStateInfo_t *BgFilePtr; + CFE_SB_PipeInfoEntry_t *PipeBufferPtr; CFE_SB_PipeD_t *PipeDscPtr; - CFE_SB_PipeInfoEntry_t FileEntry; osal_id_t SysQueueId; + bool PipeIsValid; - Status = OS_OpenCreate(&fd, Filename, OS_FILE_FLAG_CREATE | OS_FILE_FLAG_TRUNCATE, OS_WRITE_ONLY); + BgFilePtr = (CFE_SB_BackgroundFileStateInfo_t *)Meta; + PipeDscPtr = NULL; + PipeIsValid = false; - if(Status < OS_SUCCESS){ - CFE_EVS_SendEvent(CFE_SB_SND_RTG_ERR1_EID,CFE_EVS_EventType_ERROR, - "Error creating file %s, stat=0x%x", - Filename,(unsigned int)Status); - return CFE_SB_FILE_IO_ERR; - }/* end if */ + PipeBufferPtr = &BgFilePtr->Buffer.PipeInfo; - /* clear out the cfe file header fields, then populate description and subtype */ - CFE_FS_InitHeader(&FileHdr, "SB Pipe Information", CFE_FS_SubType_SB_PIPEDATA); - - Status = CFE_FS_WriteHeader(fd, &FileHdr); - if(Status != sizeof(CFE_FS_Header_t)){ - CFE_SB_FileWriteByteCntErr(Filename,sizeof(CFE_FS_Header_t),Status); - OS_close(fd); - return CFE_SB_FILE_IO_ERR; - }/* end if */ + if (RecordNum < CFE_PLATFORM_SB_MAX_PIPES) + { + PipeDscPtr = &CFE_SB.PipeTbl[RecordNum]; - FileSize = Status; + CFE_SB_LockSharedData(__FILE__,__LINE__); - /* loop through the pipe table */ - CFE_SB_LockSharedData(__FILE__,__LINE__); - PipeDscPtr = CFE_SB.PipeTbl; + PipeIsValid = CFE_SB_PipeDescIsUsed(PipeDscPtr); - for (i=0;iAppId; - FileEntry.MaxQueueDepth = PipeDscPtr->QueueDepth; - FileEntry.CurrentQueueDepth = PipeDscPtr->CurrentDepth; - FileEntry.PeakQueueDepth = PipeDscPtr->PeakDepth; - FileEntry.SendErrors = PipeDscPtr->SendErrors; - FileEntry.Opts = PipeDscPtr->Opts; + PipeBufferPtr->PipeId = CFE_SB_PipeDescGetID(PipeDscPtr); + PipeBufferPtr->AppId = PipeDscPtr->AppId; + PipeBufferPtr->Opts = PipeDscPtr->Opts; + + /* copy stats info */ + PipeBufferPtr->SendErrors = PipeDscPtr->SendErrors; + PipeBufferPtr->MaxQueueDepth = PipeDscPtr->MaxQueueDepth; + PipeBufferPtr->CurrentQueueDepth = PipeDscPtr->CurrentQueueDepth; + PipeBufferPtr->PeakQueueDepth = PipeDscPtr->PeakQueueDepth; + SysQueueId = PipeDscPtr->SysQueueId; + } + else + { + SysQueueId = OS_OBJECT_ID_UNDEFINED; + } + + CFE_SB_UnlockSharedData(__FILE__,__LINE__); + } - CFE_SB_UnlockSharedData(__FILE__,__LINE__); + if (PipeIsValid) + { + /* + * Gather data from other subsystems while unlocked. + * This might fail if the pipe is deleted simultaneously while this runs, but in + * the unlikely event that happens, the name data will simply be blank as the ID(s) + * will not validate. + */ + OS_GetResourceName(SysQueueId, PipeBufferPtr->PipeName, sizeof(PipeBufferPtr->PipeName)); + CFE_ES_GetAppName(PipeBufferPtr->AppName, PipeBufferPtr->AppId, sizeof(PipeBufferPtr->AppName)); - /* - * Gather data from other subsystems while unlocked. - * This might fail if the pipe is deleted simultaneously while this runs, but in - * the unlikely event that happens, the name data will simply be blank as the ID(s) - * will not validate. - */ - OS_GetResourceName(SysQueueId, FileEntry.PipeName, sizeof(FileEntry.PipeName)); - CFE_ES_GetAppName(FileEntry.AppName, FileEntry.AppId, sizeof(FileEntry.AppName)); + *Buffer = PipeBufferPtr; + *BufSize = sizeof(*PipeBufferPtr); + } + else + { + *Buffer = NULL; + *BufSize = 0; + } + + /* Check for EOF (last entry) */ + return (RecordNum >= (CFE_PLATFORM_SB_MAX_PIPES-1)); +} - Status = OS_write (fd, &FileEntry, sizeof(FileEntry)); - if (Status != sizeof(FileEntry)) - { - CFE_SB_FileWriteByteCntErr(Filename,sizeof(FileEntry),Status); - OS_close(fd); - return CFE_SB_FILE_IO_ERR; - }/* end if */ - FileSize += Status; - EntryCount ++; +/****************************************************************************** +** Function: CFE_SB_SendPipeInfoCmd() +** +** Purpose: +** SB internal function to handle processing of 'Send Pipe Info' Cmd +** +** Arguments: +** None +** +** Return: +** None +*/ +int32 CFE_SB_SendPipeInfoCmd(const CFE_SB_SendPipeInfoCmd_t *data) +{ + const CFE_SB_WriteFileInfoCmd_Payload_t *CmdPtr; + CFE_SB_BackgroundFileStateInfo_t *StatePtr; + int32 Status; - CFE_SB_LockSharedData(__FILE__,__LINE__); + StatePtr = &CFE_SB.BackgroundFile; + CmdPtr = &data->Payload; - }/* end if */ + /* If a pipe info dump was already pending, do not overwrite the current request */ + if (!CFE_FS_BackgroundFileDumpIsPending(&StatePtr->FileWrite)) + { + /* Reset the entire state object (just for good measure, ensure no stale data) */ + memset(StatePtr, 0, sizeof(*StatePtr)); - ++PipeDscPtr; + /* Copy the commanded filename into local buffer to ensure size limitation and to allow for modification */ + CFE_SB_MessageStringGet(StatePtr->FileWrite.FileName, CmdPtr->Filename, CFE_PLATFORM_SB_DEFAULT_PIPE_FILENAME, + sizeof(StatePtr->FileWrite.FileName), sizeof(CmdPtr->Filename)); - }/* end for */ + /* + * Fill out the remainder of meta data. + * This data is currently the same for every request + */ + StatePtr->FileWrite.FileSubType = CFE_FS_SubType_SB_PIPEDATA; + snprintf(StatePtr->FileWrite.Description, sizeof(StatePtr->FileWrite.Description), "SB Pipe Information"); - CFE_SB_UnlockSharedData(__FILE__,__LINE__); + StatePtr->FileWrite.GetData = CFE_SB_WritePipeInfoDataGetter; + StatePtr->FileWrite.OnEvent = CFE_SB_BackgroundFileEventHandler; - OS_close(fd); + Status = CFE_FS_BackgroundFileDumpRequest(&StatePtr->FileWrite); + } + else + { + Status = CFE_STATUS_REQUEST_ALREADY_PENDING; + } + + if (Status != CFE_SUCCESS) + { + /* generate the same event as is generated when unable to create the file (same thing, really) */ + CFE_SB_BackgroundFileEventHandler(StatePtr, CFE_FS_FileWriteEvent_CREATE_ERROR, Status, 0, 0, 0); + } - CFE_EVS_SendEvent(CFE_SB_SND_RTG_EID,CFE_EVS_EventType_DEBUG, - "%s written:Size=%d,Entries=%d", - Filename,(int)FileSize,(int)EntryCount); + CFE_SB_IncrCmdCtr(Status); return CFE_SUCCESS; - -}/* end CFE_SB_SendPipeInfo */ +}/* end CFE_SB_SendPipeInfoCmd */ /****************************************************************************** * Local callback helper for writing map info to a file */ -void CFE_SB_WriteMapToFile(CFE_SBR_RouteId_t RouteId, void *ArgPtr) +void CFE_SB_CollectMsgMapInfo(CFE_SBR_RouteId_t RouteId, void *ArgPtr) { - CFE_SB_FileWriteCallback_t *args; - int32 status; - CFE_SB_MsgMapFileEntry_t entry; + CFE_SB_MsgMapFileEntry_t *BufferPtr; /* Cast arguments for local use */ - args = (CFE_SB_FileWriteCallback_t *)ArgPtr; + BufferPtr = (CFE_SB_MsgMapFileEntry_t *)ArgPtr; - if(args->Status != CFE_SB_FILE_IO_ERR) - { - CFE_SB_LockSharedData(__FILE__,__LINE__); - entry.MsgId = CFE_SBR_GetMsgId(RouteId); - entry.Index = CFE_SBR_RouteIdToValue(RouteId); - CFE_SB_UnlockSharedData(__FILE__,__LINE__); + /* Extract data from runtime info, write into the temporary buffer */ + /* Data must be locked to snapshot the route info */ + CFE_SB_LockSharedData(__FILE__, __LINE__); - status = OS_write (args->Fd, &entry, sizeof(CFE_SB_MsgMapFileEntry_t)); - if(status != sizeof(CFE_SB_MsgMapFileEntry_t)) - { - CFE_SB_FileWriteByteCntErr(args->Filename, sizeof(CFE_SB_MsgMapFileEntry_t), status); - OS_close(args->Fd); - args->Status = CFE_SB_FILE_IO_ERR; - } + BufferPtr->MsgId = CFE_SBR_GetMsgId(RouteId); + BufferPtr->Index = CFE_SBR_RouteIdToValue(RouteId); + + CFE_SB_UnlockSharedData(__FILE__,__LINE__); +} + + +bool CFE_SB_WriteMsgMapInfoDataGetter(void *Meta, uint32 RecordNum, void **Buffer, size_t *BufSize) +{ + CFE_SB_BackgroundFileStateInfo_t *BgFilePtr; + CFE_SBR_Throttle_t Throttle; - args->FileSize += status; - args->EntryCount++; + /* Cast arguments for local use */ + BgFilePtr = (CFE_SB_BackgroundFileStateInfo_t *)Meta; + + Throttle.StartIndex = RecordNum; + Throttle.MaxLoop = 1; + Throttle.NextIndex = 0; + + /* Set the MsgId intially - will be overwritten with real info in CFE_SB_CollectMsgMapInfo */ + BgFilePtr->Buffer.MsgMapInfo.MsgId = CFE_SB_INVALID_MSG_ID; + + /* Collect info on the next route (limited to one per cycle via throttle) */ + CFE_SBR_ForEachRouteId(CFE_SB_CollectMsgMapInfo, &BgFilePtr->Buffer.MsgMapInfo, &Throttle); + + /* If Map was valid, pass the output of CFE_SB_CollectMsgMapInfo() back to be written */ + if (CFE_SB_IsValidMsgId(BgFilePtr->Buffer.MsgMapInfo.MsgId)) + { + *Buffer = &BgFilePtr->Buffer.MsgMapInfo; + *BufSize = sizeof(CFE_SB_MsgMapFileEntry_t); + } + else + { + *Buffer = NULL; + *BufSize = 0; } + + /* Check for EOF (last entry) - NextIndex is nonzero if more records left, zero at the end of the route table */ + return (Throttle.NextIndex == 0); } + /****************************************************************************** -** Function: CFE_SB_SendMapInfo() +** Function: CFE_SB_SendMapInfoCmd() ** ** Purpose: -** SB internal function to write the Message Map to a file +** SB internal function to handle processing of 'Send Map Info' Cmd ** ** Arguments: -** Pointer to a filename +** None ** ** Return: -** CFE_SB_FILE_IO_ERR for file I/O errors or CFE_SUCCESS +** None */ -int32 CFE_SB_SendMapInfo(const char *Filename) +int32 CFE_SB_SendMapInfoCmd(const CFE_SB_SendMapInfoCmd_t *data) { - CFE_SB_FileWriteCallback_t args = {0}; - int32 Status; - CFE_FS_Header_t FileHdr; + const CFE_SB_WriteFileInfoCmd_Payload_t *CmdPtr; + CFE_SB_BackgroundFileStateInfo_t *StatePtr; + int32 Status; - Status = OS_OpenCreate(&args.Fd, Filename, OS_FILE_FLAG_CREATE | OS_FILE_FLAG_TRUNCATE, OS_WRITE_ONLY); + StatePtr = &CFE_SB.BackgroundFile; + CmdPtr = &data->Payload; - if (Status < OS_SUCCESS) + /* If a pipe info dump was already pending, do not overwrite the current request */ + if (!CFE_FS_BackgroundFileDumpIsPending(&StatePtr->FileWrite)) { - CFE_EVS_SendEvent(CFE_SB_SND_RTG_ERR1_EID, CFE_EVS_EventType_ERROR, - "Error creating file %s, stat=0x%x", - Filename, (unsigned int)Status); - return CFE_SB_FILE_IO_ERR; - } - - /* clear out the cfe file header fields, then populate description and subtype */ - CFE_FS_InitHeader(&FileHdr, "SB Message Map Information", CFE_FS_SubType_SB_MAPDATA); + /* Reset the entire state object (just for good measure, ensure no stale data) */ + memset(StatePtr, 0, sizeof(*StatePtr)); - Status = CFE_FS_WriteHeader(args.Fd, &FileHdr); - if(Status != sizeof(CFE_FS_Header_t)) - { - CFE_SB_FileWriteByteCntErr(Filename, sizeof(CFE_FS_Header_t), Status); - OS_close(args.Fd); - return CFE_SB_FILE_IO_ERR; - } + /* Copy the commanded filename into local buffer to ensure size limitation and to allow for modification */ + CFE_SB_MessageStringGet(StatePtr->FileWrite.FileName, CmdPtr->Filename, CFE_PLATFORM_SB_DEFAULT_MAP_FILENAME, + sizeof(StatePtr->FileWrite.FileName), sizeof(CmdPtr->Filename)); - /* Initialize the reset of the nonzero callback argument elements */ - args.FileSize = Status; - args.Filename = Filename; + /* + * Fill out the remainder of meta data. + * This data is currently the same for every request + */ + StatePtr->FileWrite.FileSubType = CFE_FS_SubType_SB_MAPDATA; + snprintf(StatePtr->FileWrite.Description, sizeof(StatePtr->FileWrite.Description), "SB Map Information"); - /* Write route info to file */ - CFE_SBR_ForEachRouteId(CFE_SB_WriteMapToFile, &args, NULL); + StatePtr->FileWrite.GetData = CFE_SB_WriteMsgMapInfoDataGetter; + StatePtr->FileWrite.OnEvent = CFE_SB_BackgroundFileEventHandler; - if (args.Status != 0) - { - return args.Status; + Status = CFE_FS_BackgroundFileDumpRequest(&StatePtr->FileWrite); } else { - OS_close(args.Fd); - CFE_EVS_SendEvent(CFE_SB_SND_RTG_EID, CFE_EVS_EventType_DEBUG, - "%s written:Size=%d,Entries=%d", - Filename, (int)args.FileSize, (int)args.EntryCount); - return CFE_SUCCESS; + Status = CFE_STATUS_REQUEST_ALREADY_PENDING; } -} + + if (Status != CFE_SUCCESS) + { + /* generate the same event as is generated when unable to create the file (same thing, really) */ + CFE_SB_BackgroundFileEventHandler(StatePtr, CFE_FS_FileWriteEvent_CREATE_ERROR, Status, 0, 0, 0); + } + + CFE_SB_IncrCmdCtr(Status); + + return CFE_SUCCESS; +}/* end CFE_SB_SendMapInfoCmd */ /****************************************************************************** * Local callback helper for sending route subscriptions @@ -1445,29 +1450,6 @@ void CFE_SB_IncrCmdCtr(int32 status){ }/* end CFE_SB_IncrCmdCtr */ - - -/****************************************************************************** -** Function: CFE_SB_FileWriteByteCntErr() -** -** Purpose: -** SB internal function to report a file write error -** -** Arguments: -** -** -** Return: -** None -*/ -void CFE_SB_FileWriteByteCntErr(const char *Filename,uint32 Requested,uint32 Actual){ - - CFE_EVS_SendEvent(CFE_SB_FILEWRITE_ERR_EID,CFE_EVS_EventType_ERROR, - "File write,byte cnt err,file %s,request=%d,actual=%d", - Filename,(int)Requested,(int)Actual); - -}/* end CFE_SB_FileWriteByteCntErr() */ - - /****************************************************************************** ** Function: CFE_SB_SetSubscriptionReporting() ** diff --git a/fsw/cfe-core/src/tbl/cfe_tbl_internal.h b/fsw/cfe-core/src/tbl/cfe_tbl_internal.h index 13471f8ea..3ed8ac967 100644 --- a/fsw/cfe-core/src/tbl/cfe_tbl_internal.h +++ b/fsw/cfe-core/src/tbl/cfe_tbl_internal.h @@ -578,6 +578,15 @@ int32 CFE_TBL_SendNotificationMsg(CFE_TBL_RegistryRec_t *RegRecPtr); ******************************************************************************/ extern void CFE_TBL_ByteSwapUint32(uint32 *Uint32ToSwapPtr); +/* + * Internal helper functions for Table Registry dump + * + * These callbacks are used with the FS background write request API + * and are implemented per that specification. + */ +void CFE_TBL_DumpRegistryEventHandler(void *Meta, CFE_FS_FileWriteEvent_t Event, int32 Status, uint32 RecordNum, size_t BlockSize, size_t Position); +bool CFE_TBL_DumpRegistryGetter(void *Meta, uint32 RecordNum, void **Buffer, size_t *BufSize); + /* ** Globals specific to the TBL module diff --git a/fsw/cfe-core/src/tbl/cfe_tbl_task.h b/fsw/cfe-core/src/tbl/cfe_tbl_task.h index fa06254b3..018c972c1 100644 --- a/fsw/cfe-core/src/tbl/cfe_tbl_task.h +++ b/fsw/cfe-core/src/tbl/cfe_tbl_task.h @@ -271,6 +271,19 @@ typedef struct bool CriticalTable; /**< \brief Identifies whether table is Critical or Not */ } CFE_TBL_RegDumpRec_t; +/*******************************************************************************/ +/** \brief Table Registry Dump background state information +** +** State info for background table registry dump process and one temporary data record. +*/ +typedef struct +{ + CFE_FS_FileWriteMetaData_t FileWrite; /**< FS state data - must be first */ + + bool FileExisted; /**< Set true if the file already existed at the time of request */ + CFE_TBL_RegDumpRec_t DumpRecord; /**< Current record buffer (reused each entry) */ +} CFE_TBL_RegDumpStateInfo_t; + /*******************************************************************************/ /** \brief Table Task Global Data ** @@ -336,6 +349,11 @@ typedef struct CFE_TBL_ValidationResult_t ValidationResults[CFE_PLATFORM_TBL_MAX_NUM_VALIDATIONS]; /**< \brief Array of Table Validation Requests */ CFE_TBL_DumpControl_t DumpControlBlocks[CFE_PLATFORM_TBL_MAX_SIMULTANEOUS_LOADS]; /**< \brief Array of Dump-Only Dump Control Blocks */ + /* + * Registry dump state info (background job) + */ + CFE_TBL_RegDumpStateInfo_t RegDumpState; + } CFE_TBL_TaskData_t; diff --git a/fsw/cfe-core/src/tbl/cfe_tbl_task_cmds.c b/fsw/cfe-core/src/tbl/cfe_tbl_task_cmds.c index a608e07e0..1c6b6291a 100644 --- a/fsw/cfe-core/src/tbl/cfe_tbl_task_cmds.c +++ b/fsw/cfe-core/src/tbl/cfe_tbl_task_cmds.c @@ -1119,178 +1119,220 @@ int32 CFE_TBL_ActivateCmd(const CFE_TBL_ActivateCmd_t *data) ** NOTE: For complete prolog information, see 'cfe_tbl_task_cmds.h' ********************************************************************/ -int32 CFE_TBL_DumpRegistryCmd(const CFE_TBL_DumpRegistryCmd_t *data) +bool CFE_TBL_DumpRegistryGetter(void *Meta, uint32 RecordNum, void **Buffer, size_t *BufSize) { - CFE_TBL_CmdProcRet_t ReturnCode = CFE_TBL_INC_ERR_CTR; /* Assume failure */ - bool FileExistedPrev = false; - CFE_FS_Header_t StdFileHeader; - osal_id_t FileDescriptor; - int32 Status; - int16 RegIndex=0; - const CFE_TBL_DumpRegistryCmd_Payload_t *CmdPtr = &data->Payload; - char DumpFilename[OS_MAX_PATH_LEN]; + CFE_TBL_RegDumpStateInfo_t *StatePtr = (CFE_TBL_RegDumpStateInfo_t *)Meta; CFE_TBL_RegistryRec_t *RegRecPtr; CFE_TBL_Handle_t HandleIterator; - CFE_TBL_RegDumpRec_t DumpRecord; - int32 FileSize=0; - int32 NumEntries=0; + CFE_ES_AppId_t OwnerAppId; + bool IsValidEntry; - /* Copy the commanded filename into local buffer to ensure size limitation and to allow for modification */ - CFE_SB_MessageStringGet(DumpFilename, (char *)CmdPtr->DumpFilename, CFE_PLATFORM_TBL_DEFAULT_REG_DUMP_FILE, - sizeof(DumpFilename), sizeof(CmdPtr->DumpFilename)); - - /* Check to see if the dump file already exists */ - Status = OS_OpenCreate(&FileDescriptor, DumpFilename, OS_FILE_FLAG_NONE, OS_READ_ONLY); + IsValidEntry = false; + OwnerAppId = CFE_ES_APPID_UNDEFINED; - if (Status >= 0) + if (RecordNum < CFE_PLATFORM_TBL_MAX_NUM_TABLES) { - FileExistedPrev = true; - OS_close(FileDescriptor); - } - - /* Create a new dump file, overwriting anything that may have existed previously */ - Status = OS_OpenCreate(&FileDescriptor, DumpFilename, - OS_FILE_FLAG_CREATE | OS_FILE_FLAG_TRUNCATE, OS_WRITE_ONLY); - - if (Status >= OS_SUCCESS) - { - /* Initialize the standard cFE File Header for the Dump File */ - CFE_FS_InitHeader(&StdFileHeader, "Table Registry", CFE_FS_SubType_TBL_REG); + /* Make a pointer to simplify code look and to remove redundant indexing into registry */ + RegRecPtr = &CFE_TBL_TaskData.Registry[RecordNum]; - /* Output the Standard cFE File Header to the Dump File */ - Status = CFE_FS_WriteHeader(FileDescriptor, &StdFileHeader); - - /* Maintain statistics of amount of data written to file */ - FileSize += Status; + /* should lock registry while copying out data to ensure its in consistent state */ + CFE_TBL_LockRegistry(); - if (Status == sizeof(CFE_FS_Header_t)) + /* Check to see if the Registry entry is empty */ + if (!CFE_RESOURCEID_TEST_EQUAL(RegRecPtr->OwnerAppId, CFE_TBL_NOT_OWNED) || + (RegRecPtr->HeadOfAccessList != CFE_TBL_END_OF_LIST)) { - Status = sizeof(CFE_TBL_RegDumpRec_t); - while ((RegIndex < CFE_PLATFORM_TBL_MAX_NUM_TABLES) && (Status == sizeof(CFE_TBL_RegDumpRec_t))) + IsValidEntry = true; + OwnerAppId = RegRecPtr->OwnerAppId; + + /* Fill Registry Dump Record with relevant information */ + StatePtr->DumpRecord.Size = CFE_ES_MEMOFFSET_C(RegRecPtr->Size); + StatePtr->DumpRecord.TimeOfLastUpdate = RegRecPtr->TimeOfLastUpdate; + StatePtr->DumpRecord.LoadInProgress = RegRecPtr->LoadInProgress; + StatePtr->DumpRecord.ValidationFunc = (RegRecPtr->ValidationFuncPtr != NULL); + StatePtr->DumpRecord.TableLoadedOnce = RegRecPtr->TableLoadedOnce; + StatePtr->DumpRecord.LoadPending = RegRecPtr->LoadPending; + StatePtr->DumpRecord.DumpOnly = RegRecPtr->DumpOnly; + StatePtr->DumpRecord.DoubleBuffered = RegRecPtr->DoubleBuffered; + StatePtr->DumpRecord.FileCreateTimeSecs = RegRecPtr->Buffers[RegRecPtr->ActiveBufferIndex].FileCreateTimeSecs; + StatePtr->DumpRecord.FileCreateTimeSubSecs = RegRecPtr->Buffers[RegRecPtr->ActiveBufferIndex].FileCreateTimeSubSecs; + StatePtr->DumpRecord.Crc = RegRecPtr->Buffers[RegRecPtr->ActiveBufferIndex].Crc; + StatePtr->DumpRecord.CriticalTable = RegRecPtr->CriticalTable; + + /* Convert LoadInProgress flag into more meaningful information */ + /* When a load is in progress, identify which buffer is being used as the inactive buffer */ + if (StatePtr->DumpRecord.LoadInProgress != CFE_TBL_NO_LOAD_IN_PROGRESS) { - /* Make a pointer to simplify code look and to remove redundant indexing into registry */ - RegRecPtr = &CFE_TBL_TaskData.Registry[RegIndex]; - - /* Check to see if the Registry entry is empty */ - if (!CFE_RESOURCEID_TEST_EQUAL(RegRecPtr->OwnerAppId, CFE_TBL_NOT_OWNED) || - (RegRecPtr->HeadOfAccessList != CFE_TBL_END_OF_LIST)) + if (StatePtr->DumpRecord.DoubleBuffered) { - /* Fill Registry Dump Record with relevant information */ - DumpRecord.Size = CFE_ES_MEMOFFSET_C(RegRecPtr->Size); - DumpRecord.TimeOfLastUpdate = RegRecPtr->TimeOfLastUpdate; - DumpRecord.LoadInProgress = RegRecPtr->LoadInProgress; - DumpRecord.ValidationFunc = (RegRecPtr->ValidationFuncPtr != NULL); - DumpRecord.TableLoadedOnce = RegRecPtr->TableLoadedOnce; - DumpRecord.LoadPending = RegRecPtr->LoadPending; - DumpRecord.DumpOnly = RegRecPtr->DumpOnly; - DumpRecord.DoubleBuffered = RegRecPtr->DoubleBuffered; - DumpRecord.FileCreateTimeSecs = RegRecPtr->Buffers[RegRecPtr->ActiveBufferIndex].FileCreateTimeSecs; - DumpRecord.FileCreateTimeSubSecs = RegRecPtr->Buffers[RegRecPtr->ActiveBufferIndex].FileCreateTimeSubSecs; - DumpRecord.Crc = RegRecPtr->Buffers[RegRecPtr->ActiveBufferIndex].Crc; - DumpRecord.CriticalTable = RegRecPtr->CriticalTable; - - /* Convert LoadInProgress flag into more meaningful information */ - /* When a load is in progress, identify which buffer is being used as the inactive buffer */ - if (DumpRecord.LoadInProgress != CFE_TBL_NO_LOAD_IN_PROGRESS) - { - if (DumpRecord.DoubleBuffered) - { - /* For double buffered tables, the value of LoadInProgress, when a load is actually in progress, */ - /* should identify either buffer #0 or buffer #1. Convert these to enumerated value for ground */ - /* display. LoadInProgress = -2 means Buffer #1, LoadInProgress = -3 means Buffer #0. */ - DumpRecord.LoadInProgress = DumpRecord.LoadInProgress - 3; - } - /* For single buffered tables, the value of LoadInProgress, when a load is actually in progress, */ - /* indicates which shared buffer is allocated for the inactive buffer. Since the number of inactive */ - /* buffers is a platform configuration parameter, then 0 on up merely identifies the buffer number. */ - /* No translation is necessary for single buffered tables. */ - } - - /* Zero character arrays to remove garbage text */ - memset(DumpRecord.Name, 0, CFE_TBL_MAX_FULL_NAME_LEN); - memset(DumpRecord.LastFileLoaded, 0, OS_MAX_PATH_LEN); - memset(DumpRecord.OwnerAppName, 0, OS_MAX_API_NAME); - - strncpy(DumpRecord.Name, RegRecPtr->Name, sizeof(DumpRecord.Name)-1); - strncpy(DumpRecord.LastFileLoaded, RegRecPtr->LastFileLoaded, sizeof(DumpRecord.LastFileLoaded)-1); - - /* Walk the access descriptor list to determine the number of users */ - DumpRecord.NumUsers = 0; - HandleIterator = RegRecPtr->HeadOfAccessList; - while (HandleIterator != CFE_TBL_END_OF_LIST) - { - DumpRecord.NumUsers++; - HandleIterator = CFE_TBL_TaskData.Handles[HandleIterator].NextLink; - } - - /* Determine the name of the owning application */ - if (!CFE_RESOURCEID_TEST_EQUAL(RegRecPtr->OwnerAppId, CFE_TBL_NOT_OWNED)) - { - CFE_ES_GetAppName(DumpRecord.OwnerAppName, RegRecPtr->OwnerAppId, sizeof(DumpRecord.OwnerAppName)); - } - else - { - strncpy(DumpRecord.OwnerAppName, "--UNOWNED--", sizeof(DumpRecord.OwnerAppName)-1); - } - - /* Output Registry Dump Record to Registry Dump File */ - Status = OS_write(FileDescriptor, - &DumpRecord, - sizeof(CFE_TBL_RegDumpRec_t)); - - FileSize += Status; - NumEntries++; + /* For double buffered tables, the value of LoadInProgress, when a load is actually in progress, */ + /* should identify either buffer #0 or buffer #1. Convert these to enumerated value for ground */ + /* display. LoadInProgress = -2 means Buffer #1, LoadInProgress = -3 means Buffer #0. */ + StatePtr->DumpRecord.LoadInProgress = StatePtr->DumpRecord.LoadInProgress - 3; } - - /* Look at the next entry in the Registry */ - RegIndex++; + /* For single buffered tables, the value of LoadInProgress, when a load is actually in progress, */ + /* indicates which shared buffer is allocated for the inactive buffer. Since the number of inactive */ + /* buffers is a platform configuration parameter, then 0 on up merely identifies the buffer number. */ + /* No translation is necessary for single buffered tables. */ } + + strncpy(StatePtr->DumpRecord.Name, RegRecPtr->Name, sizeof(StatePtr->DumpRecord.Name)-1); + StatePtr->DumpRecord.Name[sizeof(StatePtr->DumpRecord.Name)-1] = 0; + + strncpy(StatePtr->DumpRecord.LastFileLoaded, RegRecPtr->LastFileLoaded, sizeof(StatePtr->DumpRecord.LastFileLoaded)-1); + StatePtr->DumpRecord.LastFileLoaded[sizeof(StatePtr->DumpRecord.LastFileLoaded)-1] = 0; - if (Status == sizeof(CFE_TBL_RegDumpRec_t)) - { - if (FileExistedPrev) - { - CFE_EVS_SendEvent(CFE_TBL_OVERWRITE_REG_DUMP_INF_EID, - CFE_EVS_EventType_DEBUG, - "Successfully overwrote '%s' with Table Registry:Size=%d,Entries=%d", - DumpFilename, (int)FileSize, (int)NumEntries); - } - else - { - CFE_EVS_SendEvent(CFE_TBL_WRITE_REG_DUMP_INF_EID, - CFE_EVS_EventType_DEBUG, - "Successfully dumped Table Registry to '%s':Size=%d,Entries=%d", - DumpFilename, (int)FileSize, (int)NumEntries); - } - - /* Increment Successful Command Counter */ - ReturnCode = CFE_TBL_INC_CMD_CTR; - } - else + /* Walk the access descriptor list to determine the number of users */ + StatePtr->DumpRecord.NumUsers = 0; + HandleIterator = RegRecPtr->HeadOfAccessList; + while (HandleIterator != CFE_TBL_END_OF_LIST) { - CFE_EVS_SendEvent(CFE_TBL_WRITE_TBL_REG_ERR_EID, - CFE_EVS_EventType_ERROR, - "Error writing Registry to '%s', Status=0x%08X", - DumpFilename, (unsigned int)Status); + StatePtr->DumpRecord.NumUsers++; + HandleIterator = CFE_TBL_TaskData.Handles[HandleIterator].NextLink; } } + + /* Unlock now - remainder of data gathering uses ES */ + CFE_TBL_UnlockRegistry(); + } + + /* + * If table record had data, then export now. + * Need to also get the App name from ES to complete the record. + */ + if (IsValidEntry) + { + /* Determine the name of the owning application */ + if (!CFE_RESOURCEID_TEST_EQUAL(OwnerAppId, CFE_TBL_NOT_OWNED)) + { + CFE_ES_GetAppName(StatePtr->DumpRecord.OwnerAppName, OwnerAppId, sizeof(StatePtr->DumpRecord.OwnerAppName)); + } else { - CFE_EVS_SendEvent(CFE_TBL_WRITE_CFE_HDR_ERR_EID, - CFE_EVS_EventType_ERROR, - "Error writing cFE File Header to '%s', Status=0x%08X", - DumpFilename, (unsigned int)Status); + strncpy(StatePtr->DumpRecord.OwnerAppName, "--UNOWNED--", sizeof(StatePtr->DumpRecord.OwnerAppName)-1); + StatePtr->DumpRecord.OwnerAppName[sizeof(StatePtr->DumpRecord.OwnerAppName)-1] = 0; } - /* We are done outputting data to the dump file. Close it. */ - OS_close(FileDescriptor); + /* export data to caller */ + *Buffer = &StatePtr->DumpRecord; + *BufSize = sizeof(StatePtr->DumpRecord); } else { - CFE_EVS_SendEvent(CFE_TBL_CREATING_DUMP_FILE_ERR_EID, - CFE_EVS_EventType_ERROR, - "Error creating dump file '%s', Status=0x%08X", - DumpFilename, (unsigned int)Status); + /* No data to write for this record */ + *BufSize = 0; + *Buffer = NULL; + } + + /* Check for EOF (last entry) */ + return (RecordNum >= (CFE_PLATFORM_TBL_MAX_NUM_TABLES-1)); +} + +void CFE_TBL_DumpRegistryEventHandler(void *Meta, CFE_FS_FileWriteEvent_t Event, int32 Status, uint32 RecordNum, size_t BlockSize, size_t Position) +{ + CFE_TBL_RegDumpStateInfo_t *StatePtr = (CFE_TBL_RegDumpStateInfo_t *)Meta; + + /* + * Note that this runs in the context of ES background task (file writer background job) + * It does NOT run in the context of the CFE_TBL app task. + * + * Events should use CFE_EVS_SendEventWithAppID() rather than CFE_EVS_SendEvent() + * to get proper association with TBL task. + */ + switch(Event) + { + case CFE_FS_FileWriteEvent_COMPLETE: + if (StatePtr->FileExisted) + { + CFE_EVS_SendEventWithAppID(CFE_TBL_OVERWRITE_REG_DUMP_INF_EID, + CFE_EVS_EventType_DEBUG, + CFE_TBL_TaskData.TableTaskAppId, + "Successfully overwrote '%s' with Table Registry:Size=%d,Entries=%d", + StatePtr->FileWrite.FileName, (int)Position, (int)RecordNum); + } + else + { + CFE_EVS_SendEventWithAppID(CFE_TBL_WRITE_REG_DUMP_INF_EID, + CFE_EVS_EventType_DEBUG, + CFE_TBL_TaskData.TableTaskAppId, + "Successfully dumped Table Registry to '%s':Size=%d,Entries=%d", + StatePtr->FileWrite.FileName, (int)Position, (int)RecordNum); + } + break; + + case CFE_FS_FileWriteEvent_RECORD_WRITE_ERROR: + CFE_EVS_SendEventWithAppID(CFE_TBL_WRITE_TBL_REG_ERR_EID, + CFE_EVS_EventType_ERROR, + CFE_TBL_TaskData.TableTaskAppId, + "Error writing Registry to '%s', Status=0x%08X", + StatePtr->FileWrite.FileName, (unsigned int)Status); + break; + + case CFE_FS_FileWriteEvent_HEADER_WRITE_ERROR: + CFE_EVS_SendEventWithAppID(CFE_TBL_WRITE_CFE_HDR_ERR_EID, + CFE_EVS_EventType_ERROR, + CFE_TBL_TaskData.TableTaskAppId, + "Error writing cFE File Header to '%s', Status=0x%08X", + StatePtr->FileWrite.FileName, (unsigned int)Status); + break; + + case CFE_FS_FileWriteEvent_CREATE_ERROR: + CFE_EVS_SendEventWithAppID(CFE_TBL_CREATING_DUMP_FILE_ERR_EID, + CFE_EVS_EventType_ERROR, + CFE_TBL_TaskData.TableTaskAppId, + "Error creating dump file '%s', Status=0x%08X", + StatePtr->FileWrite.FileName, (unsigned int)Status); + break; + + default: + /* unhandled event - ignore */ + break; + } +} + +int32 CFE_TBL_DumpRegistryCmd(const CFE_TBL_DumpRegistryCmd_t *data) +{ + CFE_TBL_CmdProcRet_t ReturnCode = CFE_TBL_INC_ERR_CTR; /* Assume failure */ + int32 Status; + const CFE_TBL_DumpRegistryCmd_Payload_t *CmdPtr = &data->Payload; + os_fstat_t FileStat; + + CFE_TBL_RegDumpStateInfo_t *StatePtr; + + StatePtr = &CFE_TBL_TaskData.RegDumpState; + + /* If a reg dump was already pending, do not overwrite the current request */ + if (!CFE_FS_BackgroundFileDumpIsPending(&StatePtr->FileWrite)) + { + /* Copy the commanded filename into local buffer to ensure size limitation and to allow for modification */ + CFE_SB_MessageStringGet(StatePtr->FileWrite.FileName, CmdPtr->DumpFilename, CFE_PLATFORM_TBL_DEFAULT_REG_DUMP_FILE, + sizeof(StatePtr->FileWrite.FileName), sizeof(CmdPtr->DumpFilename)); + + /* + * Fill out the remainder of meta data. + * This data is currently the same for every request + */ + StatePtr->FileWrite.FileSubType = CFE_FS_SubType_TBL_REG; + snprintf(StatePtr->FileWrite.Description, sizeof(StatePtr->FileWrite.Description), "Table Registry"); + + StatePtr->FileWrite.GetData = CFE_TBL_DumpRegistryGetter; + StatePtr->FileWrite.OnEvent = CFE_TBL_DumpRegistryEventHandler; + + /* + * Before submitting the background request, use OS_stat() to check if the file exists already. + * + * This is because TBL services issued a different event ID in some cases if + * it is overwriting a file vs. creating a new file. + */ + StatePtr->FileExisted = (OS_stat(StatePtr->FileWrite.FileName, &FileStat) == OS_SUCCESS); + + Status = CFE_FS_BackgroundFileDumpRequest(&StatePtr->FileWrite); + + if (Status == CFE_SUCCESS) + { + /* Increment the TBL generic command counter (successfully queued for background job) */ + ReturnCode = CFE_TBL_INC_CMD_CTR; + } } return ReturnCode; diff --git a/fsw/cfe-core/unit-test/es_UT.c b/fsw/cfe-core/unit-test/es_UT.c index 001f5d1af..5cc7e5119 100644 --- a/fsw/cfe-core/unit-test/es_UT.c +++ b/fsw/cfe-core/unit-test/es_UT.c @@ -2298,6 +2298,9 @@ void TestLibs(void) void TestERLog(void) { int Return; + void *LocalBuffer; + size_t LocalBufSize; + CFE_ES_BackgroundLogDumpGlobal_t State; UtPrintf("Begin Test Exception and Reset Log"); @@ -2335,6 +2338,51 @@ void TestERLog(void) CFE_ES_ResetDataPtr->ERLogIndex == 1, "CFE_ES_WriteToERLog", "No log entry rollover; no description; no context"); + + /* Test ER log background write functions */ + memset(&State, 0, sizeof(State)); + LocalBuffer = NULL; + LocalBufSize = 0; + + UT_SetDeferredRetcode(UT_KEY(CFE_PSP_Exception_CopyContext), 1, 128); + UtAssert_True(!CFE_ES_BackgroundERLogFileDataGetter(&State, 0, &LocalBuffer, &LocalBufSize), + "CFE_ES_BackgroundERLogFileDataGetter at start, with context"); + UtAssert_UINT32_EQ(State.EntryBuffer.ContextSize, 128); + UtAssert_NOT_NULL(LocalBuffer); + UtAssert_NONZERO(LocalBufSize); + + memset(&State.EntryBuffer, 0xEE, sizeof(State.EntryBuffer)); + UT_SetDeferredRetcode(UT_KEY(CFE_PSP_Exception_CopyContext), 1, -1); + UtAssert_True(CFE_ES_BackgroundERLogFileDataGetter(&State, CFE_PLATFORM_ES_ER_LOG_ENTRIES-1, &LocalBuffer, &LocalBufSize), + "CFE_ES_BackgroundERLogFileDataGetter at EOF, no context"); + UtAssert_ZERO(State.EntryBuffer.ContextSize); + + UtAssert_True(CFE_ES_BackgroundERLogFileDataGetter(&State, CFE_PLATFORM_ES_ER_LOG_ENTRIES, &LocalBuffer, &LocalBufSize), + "CFE_ES_BackgroundERLogFileDataGetter beyond EOF"); + UtAssert_NULL(LocalBuffer); + UtAssert_ZERO(LocalBufSize); + + + /* Test ER log background write event handling */ + UT_ClearEventHistory(); + CFE_ES_BackgroundERLogFileEventHandler(&State, CFE_FS_FileWriteEvent_COMPLETE, CFE_SUCCESS, 10, 0, 100); + UtAssert_True(UT_EventIsInHistory(CFE_ES_ERLOG2_EID), "COMPLETE: CFE_ES_ERLOG2_EID generated"); + + UT_ClearEventHistory(); + CFE_ES_BackgroundERLogFileEventHandler(&State, CFE_FS_FileWriteEvent_HEADER_WRITE_ERROR, -1, 10, 10, 100); + UtAssert_True(UT_EventIsInHistory(CFE_ES_FILEWRITE_ERR_EID), "HEADER_WRITE_ERROR: CFE_ES_FILEWRITE_ERR_EID generated"); + + UT_ClearEventHistory(); + CFE_ES_BackgroundERLogFileEventHandler(&State, CFE_FS_FileWriteEvent_RECORD_WRITE_ERROR, -1, 10, 10, 100); + UtAssert_True(UT_EventIsInHistory(CFE_ES_FILEWRITE_ERR_EID), "RECORD_WRITE_ERROR: CFE_ES_FILEWRITE_ERR_EID generated"); + + UT_ClearEventHistory(); + CFE_ES_BackgroundERLogFileEventHandler(&State, CFE_FS_FileWriteEvent_CREATE_ERROR, -1, 10, 10, 100); + UtAssert_True(UT_EventIsInHistory(CFE_ES_ERLOG2_ERR_EID), "CREATE_ERROR: CFE_ES_ERLOG2_ERR_EID generated"); + + UT_ClearEventHistory(); + CFE_ES_BackgroundERLogFileEventHandler(&State, CFE_FS_FileWriteEvent_UNDEFINED, CFE_SUCCESS, 10, 0, 100); + UtAssert_True(UT_GetNumEventsSent() == 0, "UNDEFINED: No event generated"); } @@ -2584,7 +2632,6 @@ void TestGenericPool(void) void TestTask(void) { uint32 ResetType; - uint32 UT_ContextData; osal_id_t UT_ContextTask; union { @@ -3361,11 +3408,11 @@ void TestTask(void) strncpy(CmdBuf.WriteERLogCmd.Payload.FileName, "filename", sizeof(CmdBuf.WriteERLogCmd.Payload.FileName) - 1); CmdBuf.WriteERLogCmd.Payload.FileName[sizeof(CmdBuf.WriteERLogCmd.Payload.FileName) - 1] = '\0'; - CFE_ES_TaskData.BackgroundERLogDumpState.IsPending = false; + UT_SetDefaultReturnValue(UT_KEY(CFE_FS_BackgroundFileDumpIsPending), false); UT_CallTaskPipe(CFE_ES_TaskPipe, &CmdBuf.Msg, sizeof(CmdBuf.WriteERLogCmd), UT_TPID_CFE_ES_CMD_WRITE_ER_LOG_CC); UT_Report(__FILE__, __LINE__, - CFE_ES_TaskData.BackgroundERLogDumpState.IsPending, + UT_GetStubCount(UT_KEY(CFE_FS_BackgroundFileDumpRequest)) == 1, "CFE_ES_WriteERLogCmd", "Write E&R log command; pending"); UT_Report(__FILE__, __LINE__, @@ -3373,71 +3420,25 @@ void TestTask(void) "CFE_ES_WriteERLogCmd", "Write E&R log command; no events"); - /* sending the same command a second time should fail with an event - * indicating a file write is already pending. */ + /* Failure from CFE_FS_BackgroundFileDumpRequest() should send the pending error event ID */ + UT_ClearEventHistory(); + UT_SetDeferredRetcode(UT_KEY(CFE_FS_BackgroundFileDumpRequest), 1, CFE_STATUS_REQUEST_ALREADY_PENDING); UT_CallTaskPipe(CFE_ES_TaskPipe, &CmdBuf.Msg, sizeof(CmdBuf.WriteERLogCmd), UT_TPID_CFE_ES_CMD_WRITE_ER_LOG_CC); UT_Report(__FILE__, __LINE__, UT_EventIsInHistory(CFE_ES_ERLOG_PENDING_ERR_EID), "CFE_ES_WriteERLogCmd", - "Write E&R log command; already pending event"); - - /* calling the background job when no write pending should immediately return false, no event */ - ES_ResetUnitTest(); - memset(&CFE_ES_TaskData.BackgroundERLogDumpState, 0, sizeof(CFE_ES_TaskData.BackgroundERLogDumpState)); - UT_Report(__FILE__, __LINE__, - !CFE_ES_RunERLogDump(0, &CFE_ES_TaskData.BackgroundERLogDumpState), - "CFE_ES_RunERLogDump", - "Write E&R log; not pending"); - UT_Report(__FILE__, __LINE__, - !UT_EventIsInHistory(CFE_ES_ERLOG2_EID), - "CFE_ES_WriteERLogCmd", - "Write E&R log command; no file written event"); - - /* nominal condition - still returns false, but generates event */ - ES_ResetUnitTest(); - UT_ContextData = 42; - UT_SetDataBuffer(UT_KEY(CFE_PSP_Exception_CopyContext),&UT_ContextData, sizeof(UT_ContextData), false); - CFE_ES_TaskData.BackgroundERLogDumpState.IsPending = true; - CFE_ES_RunERLogDump(0, &CFE_ES_TaskData.BackgroundERLogDumpState); - UT_Report(__FILE__, __LINE__, - !CFE_ES_TaskData.BackgroundERLogDumpState.IsPending, - "CFE_ES_RunERLogDump", - "Write E&R log; nominal, clear flag"); - UT_Report(__FILE__, __LINE__, - UT_EventIsInHistory(CFE_ES_ERLOG2_EID), - "CFE_ES_WriteERLogCmd", - "Write E&R log command; file written event"); - - /* Test writing the E&R log with an OS create failure */ - ES_ResetUnitTest(); - CFE_ES_TaskData.BackgroundERLogDumpState.IsPending = true; - UT_SetDefaultReturnValue(UT_KEY(OS_OpenCreate), OS_ERROR); - CFE_ES_RunERLogDump(0, &CFE_ES_TaskData.BackgroundERLogDumpState); - UT_Report(__FILE__, __LINE__, - UT_EventIsInHistory(CFE_ES_ERLOG2_ERR_EID), - "CFE_ES_RunERLogDump", - "Write E&R log; OS create"); - - /* Test writing the E&R log with an OS write failure */ - ES_ResetUnitTest(); - CFE_ES_TaskData.BackgroundERLogDumpState.IsPending = true; - UT_SetDefaultReturnValue(UT_KEY(OS_write), OS_ERROR); - CFE_ES_RunERLogDump(0, &CFE_ES_TaskData.BackgroundERLogDumpState); - UT_Report(__FILE__, __LINE__, - UT_EventIsInHistory(CFE_ES_FILEWRITE_ERR_EID), - "CFE_ES_RunERLogDump", - "Write E&R log; OS write"); + "Write E&R log command; already pending event (from FS)"); - /* Test writing the E&R log with a write header failure */ - ES_ResetUnitTest(); - CFE_ES_TaskData.BackgroundERLogDumpState.IsPending = true; - UT_SetDeferredRetcode(UT_KEY(CFE_FS_WriteHeader), 1, OS_ERROR); - CFE_ES_RunERLogDump(0, &CFE_ES_TaskData.BackgroundERLogDumpState); + /* Same event but pending locally */ + UT_ClearEventHistory(); + UT_SetDefaultReturnValue(UT_KEY(CFE_FS_BackgroundFileDumpIsPending), true); + UT_CallTaskPipe(CFE_ES_TaskPipe, &CmdBuf.Msg, sizeof(CmdBuf.WriteERLogCmd), + UT_TPID_CFE_ES_CMD_WRITE_ER_LOG_CC); UT_Report(__FILE__, __LINE__, - UT_EventIsInHistory(CFE_ES_FILEWRITE_ERR_EID), + UT_EventIsInHistory(CFE_ES_ERLOG_PENDING_ERR_EID), "CFE_ES_WriteERLogCmd", - "Write E&R log; write header"); + "Write E&R log command; already pending event (local)"); /* Test scan for exceptions in the PSP, should invoke a Processor Reset */ ES_ResetUnitTest(); diff --git a/fsw/cfe-core/unit-test/fs_UT.c b/fsw/cfe-core/unit-test/fs_UT.c index 4b569cb19..729b776be 100644 --- a/fsw/cfe-core/unit-test/fs_UT.c +++ b/fsw/cfe-core/unit-test/fs_UT.c @@ -47,6 +47,24 @@ const char *FS_SYSLOG_MSGS[] = "FS SharedData Mutex Give Err Stat=0x%x,App=%lu,Function=%s\n" }; +/* counts the number of times UT_FS_OnEvent() was invoked (below) */ +uint32 UT_FS_FileWriteEventCount[CFE_FS_FileWriteEvent_MAX]; + + +/* UT helper stub compatible with background file write DataGetter */ +bool UT_FS_DataGetter(void *Meta, uint32 RecordNum, void **Buffer, size_t *BufSize) +{ + UT_GetDataBuffer(UT_KEY(UT_FS_DataGetter), Buffer, BufSize, NULL); + return UT_DEFAULT_IMPL(UT_FS_DataGetter); +} + +/* UT helper stub compatible with background file write OnEvent */ +void UT_FS_OnEvent(void *Meta, CFE_FS_FileWriteEvent_t Event, int32 Status, uint32 RecordNum, size_t BlockSize, size_t Position) +{ + ++UT_FS_FileWriteEventCount[Event]; + UT_DEFAULT_IMPL(UT_FS_OnEvent); +} + /* ** Functions */ @@ -65,6 +83,8 @@ void UtTest_Setup(void) UT_ADD_TEST(Test_CFE_FS_ByteSwapUint32); UT_ADD_TEST(Test_CFE_FS_ExtractFileNameFromPath); UT_ADD_TEST(Test_CFE_FS_Private); + + UT_ADD_TEST(Test_CFE_FS_BackgroundFileDump); } /* @@ -358,3 +378,124 @@ void Test_CFE_FS_Private(void) UtPrintf("End Test Private\n"); } +void Test_CFE_FS_BackgroundFileDump(void) +{ + /* + * Test routine for: + * bool CFE_FS_RunBackgroundFileDump(uint32 ElapsedTime, void *Arg) + */ + CFE_FS_FileWriteMetaData_t State; + uint32 MyBuffer[2]; + int32 Status; + + memset(UT_FS_FileWriteEventCount, 0, sizeof(UT_FS_FileWriteEventCount)); + memset(&State, 0, sizeof(State)); + memset(&CFE_FS.FileDump, 0, sizeof(CFE_FS.FileDump)); + + /* Nominal with nothing pending - should accumulate credit */ + UtAssert_True(!CFE_FS_RunBackgroundFileDump(1, NULL), "CFE_FS_RunBackgroundFileDump() nothing pending, short delay"); + UtAssert_True(CFE_FS.FileDump.Current.Credit > 0, "Credit accumulating (%lu)", (unsigned long)CFE_FS.FileDump.Current.Credit); + UtAssert_True(CFE_FS.FileDump.Current.Credit < CFE_FS_BACKGROUND_MAX_CREDIT, "Credit not max (%lu)", (unsigned long)CFE_FS.FileDump.Current.Credit); + + UtAssert_True(!CFE_FS_RunBackgroundFileDump(100000, NULL), "CFE_FS_RunBackgroundFileDump() nothing pending, long delay"); + UtAssert_True(CFE_FS.FileDump.Current.Credit == CFE_FS_BACKGROUND_MAX_CREDIT, "Credit at max (%lu)", (unsigned long)CFE_FS.FileDump.Current.Credit); + + Status = CFE_FS_BackgroundFileDumpRequest(NULL); + UtAssert_True(Status == CFE_FS_BAD_ARGUMENT, "CFE_FS_BackgroundFileDumpRequest(NULL) (%lu) == CFE_FS_BAD_ARGUMENT", (unsigned long)Status); + Status = CFE_FS_BackgroundFileDumpRequest(&State); + UtAssert_True(Status == CFE_FS_BAD_ARGUMENT, "CFE_FS_BackgroundFileDumpRequest(&State) (%lu) == CFE_FS_BAD_ARGUMENT", (unsigned long)Status); + UtAssert_True(!CFE_FS_BackgroundFileDumpIsPending(&State), "!CFE_FS_BackgroundFileDumpIsPending(&State)"); + UtAssert_STUB_COUNT(CFE_ES_BackgroundWakeup, 0); /* confirm CFE_ES_BackgroundWakeup() was not invoked */ + + /* Set the data except file name and description */ + State.FileSubType = 2; + State.GetData = UT_FS_DataGetter; + State.OnEvent = UT_FS_OnEvent; + Status = CFE_FS_BackgroundFileDumpRequest(&State); + UtAssert_True(Status == CFE_FS_INVALID_PATH, "CFE_FS_BackgroundFileDumpRequest(&State) (%lu) == CFE_FS_INVALID_PATH", (unsigned long)Status); + UtAssert_True(!CFE_FS_BackgroundFileDumpIsPending(&State), "!CFE_FS_BackgroundFileDumpIsPending(&State)"); + UtAssert_STUB_COUNT(CFE_ES_BackgroundWakeup, 0); /* confirm CFE_ES_BackgroundWakeup() was not invoked */ + + /* Set up remainder of fields, so entry is valid */ + strncpy(State.FileName, "/ram/UT.bin", sizeof(State.FileName)); + strncpy(State.Description, "UT", sizeof(State.Description)); + + Status = CFE_FS_BackgroundFileDumpRequest(&State); + UtAssert_True(Status == CFE_SUCCESS, "CFE_FS_BackgroundFileDumpRequest() (%lu) == CFE_SUCCESS", (unsigned long)Status); + UtAssert_True(CFE_FS_BackgroundFileDumpIsPending(&State), "CFE_FS_BackgroundFileDumpIsPending(&State)"); + UtAssert_STUB_COUNT(CFE_ES_BackgroundWakeup, 1); /* confirm CFE_ES_BackgroundWakeup() was invoked */ + + /* + * Set up a fixed data buffer which will be written, + * this will write sizeof(MyBuffer) until credit is gone + */ + MyBuffer[0] = 10; + MyBuffer[1] = 20; + UT_SetDataBuffer(UT_KEY(UT_FS_DataGetter),MyBuffer,sizeof(MyBuffer), false); + UtAssert_True(CFE_FS_RunBackgroundFileDump(1, NULL), "CFE_FS_RunBackgroundFileDump() request pending nominal"); + UtAssert_STUB_COUNT(OS_OpenCreate, 1); /* confirm OS_open() was invoked */ + UtAssert_True(CFE_FS.FileDump.Current.Credit <= 0, "Credit exhausted (%lu)", (unsigned long)CFE_FS.FileDump.Current.Credit); + UtAssert_STUB_COUNT(OS_close, 0); /* confirm OS_close() was not invoked */ + + UT_SetDeferredRetcode(UT_KEY(UT_FS_DataGetter), 2, true); /* return EOF */ + UtAssert_True(!CFE_FS_RunBackgroundFileDump(100, NULL), "CFE_FS_RunBackgroundFileDump() request pending EOF"); + UtAssert_STUB_COUNT(OS_OpenCreate, 1); /* confirm OS_open() was not invoked again */ + UtAssert_STUB_COUNT(OS_close, 1); /* confirm OS_close() was invoked */ + UtAssert_UINT32_EQ(CFE_FS.FileDump.CompleteCount, CFE_FS.FileDump.RequestCount); /* request was completed */ + UtAssert_UINT32_EQ(UT_FS_FileWriteEventCount[CFE_FS_FileWriteEvent_COMPLETE], 1); /* complete event was sent */ + UtAssert_True(!CFE_FS_BackgroundFileDumpIsPending(&State), "!CFE_FS_BackgroundFileDumpIsPending(&State)"); + + UT_ResetState(UT_KEY(UT_FS_DataGetter)); + + + /* Error opening file */ + Status = CFE_FS_BackgroundFileDumpRequest(&State); + UtAssert_True(Status == CFE_SUCCESS, "CFE_FS_BackgroundFileDumpRequest() (%lu) == CFE_SUCCESS", (unsigned long)Status); + UT_SetDeferredRetcode(UT_KEY(OS_OpenCreate), 1, OS_ERROR); + + UtAssert_True(CFE_FS_RunBackgroundFileDump(100, NULL), "CFE_FS_RunBackgroundFileDump() request pending, file open error"); + UtAssert_UINT32_EQ(UT_FS_FileWriteEventCount[CFE_FS_FileWriteEvent_CREATE_ERROR], 1); /* create error event was sent */ + UtAssert_True(!CFE_FS_BackgroundFileDumpIsPending(&State), "!CFE_FS_BackgroundFileDumpIsPending(&State)"); + UtAssert_UINT32_EQ(CFE_FS.FileDump.CompleteCount, CFE_FS.FileDump.RequestCount); /* request was completed */ + + /* Error writing header */ + Status = CFE_FS_BackgroundFileDumpRequest(&State); + UtAssert_True(Status == CFE_SUCCESS, "CFE_FS_BackgroundFileDumpRequest() (%lu) == CFE_SUCCESS", (unsigned long)Status); + UT_SetDeferredRetcode(UT_KEY(OS_write), 1, OS_ERROR); + + UtAssert_True(CFE_FS_RunBackgroundFileDump(100, NULL), "CFE_FS_RunBackgroundFileDump() request pending, file write header error"); + UtAssert_UINT32_EQ(UT_FS_FileWriteEventCount[CFE_FS_FileWriteEvent_HEADER_WRITE_ERROR], 1); /* header error event was sent */ + UtAssert_True(!CFE_FS_BackgroundFileDumpIsPending(&State), "!CFE_FS_BackgroundFileDumpIsPending(&State)"); + UtAssert_UINT32_EQ(CFE_FS.FileDump.CompleteCount, CFE_FS.FileDump.RequestCount); /* request was completed */ + + /* Error writing data */ + Status = CFE_FS_BackgroundFileDumpRequest(&State); + UtAssert_True(Status == CFE_SUCCESS, "CFE_FS_BackgroundFileDumpRequest() (%lu) == CFE_SUCCESS", (unsigned long)Status); + UT_SetDeferredRetcode(UT_KEY(OS_write), 2, OS_ERROR); + UT_SetDataBuffer(UT_KEY(UT_FS_DataGetter),MyBuffer,sizeof(MyBuffer), false); + UtAssert_True(CFE_FS_RunBackgroundFileDump(100, NULL), "CFE_FS_RunBackgroundFileDump() request pending, file write data error"); + UtAssert_UINT32_EQ(UT_FS_FileWriteEventCount[CFE_FS_FileWriteEvent_RECORD_WRITE_ERROR], 1); /* record error event was sent */ + UtAssert_True(!CFE_FS_BackgroundFileDumpIsPending(&State), "!CFE_FS_BackgroundFileDumpIsPending(&State)"); + UtAssert_UINT32_EQ(CFE_FS.FileDump.CompleteCount, CFE_FS.FileDump.RequestCount); /* request was completed */ + + UT_ResetState(UT_KEY(UT_FS_DataGetter)); + + /* Request multiple file dumps, check queing logic */ + Status = CFE_FS_BackgroundFileDumpRequest(&State); + UtAssert_True(Status == CFE_SUCCESS, "CFE_FS_BackgroundFileDumpRequest() (%lu) == CFE_SUCCESS", (unsigned long)Status); + Status = CFE_FS_BackgroundFileDumpRequest(&State); + UtAssert_True(Status == CFE_STATUS_REQUEST_ALREADY_PENDING, "CFE_FS_BackgroundFileDumpRequest() (%lu) == CFE_STATUS_REQUEST_ALREADY_PENDING", (unsigned long)Status); + + do + { + State.IsPending = false; /* UT hack to fill queue - Force not pending. Real code should not do this. */ + Status = CFE_FS_BackgroundFileDumpRequest(&State); + } + while (Status == CFE_SUCCESS); + + UtAssert_True(Status == CFE_STATUS_REQUEST_ALREADY_PENDING, "CFE_FS_BackgroundFileDumpRequest() (%lu) == CFE_STATUS_REQUEST_ALREADY_PENDING", (unsigned long)Status); + UtAssert_UINT32_EQ(CFE_FS.FileDump.RequestCount, (CFE_FS.FileDump.CompleteCount + CFE_FS_MAX_BACKGROUND_FILE_WRITES - 1)); + + /* Confirm null arg handling in CFE_FS_BackgroundFileDumpIsPending() */ + UtAssert_True(!CFE_FS_BackgroundFileDumpIsPending(NULL), "!CFE_FS_BackgroundFileDumpIsPending(NULL)"); +} diff --git a/fsw/cfe-core/unit-test/fs_UT.h b/fsw/cfe-core/unit-test/fs_UT.h index 1d9fdf1c2..47b2573be 100644 --- a/fsw/cfe-core/unit-test/fs_UT.h +++ b/fsw/cfe-core/unit-test/fs_UT.h @@ -270,4 +270,17 @@ void Test_CFE_FS_Decompress(void); ******************************************************************************/ void Test_CFE_FS_GetUncompressedFile(void); +/*****************************************************************************/ +/** +** \brief Tests for FS background file dump +** +** \par Assumptions, External Events, and Notes: +** None +** +** \returns +** This function does not return a value. +** +******************************************************************************/ +void Test_CFE_FS_BackgroundFileDump(void); + #endif /* _es_ut_h_ */ diff --git a/fsw/cfe-core/unit-test/sb_UT.c b/fsw/cfe-core/unit-test/sb_UT.c index 9646017e6..f5d8ec78a 100644 --- a/fsw/cfe-core/unit-test/sb_UT.c +++ b/fsw/cfe-core/unit-test/sb_UT.c @@ -383,21 +383,16 @@ void Test_SB_Cmds(void) SB_UT_ADD_SUBTEST(Test_SB_Cmds_Noop); SB_UT_ADD_SUBTEST(Test_SB_Cmds_RstCtrs); SB_UT_ADD_SUBTEST(Test_SB_Cmds_Stats); + SB_UT_ADD_SUBTEST(Test_SB_Cmds_BackgroundFileWriteEvents); SB_UT_ADD_SUBTEST(Test_SB_Cmds_RoutingInfoDef); - SB_UT_ADD_SUBTEST(Test_SB_Cmds_RoutingInfoSpec); - SB_UT_ADD_SUBTEST(Test_SB_Cmds_RoutingInfoCreateFail); - SB_UT_ADD_SUBTEST(Test_SB_Cmds_RoutingInfoHdrFail); - SB_UT_ADD_SUBTEST(Test_SB_Cmds_RoutingInfoWriteFail); + SB_UT_ADD_SUBTEST(Test_SB_Cmds_RoutingInfoAlreadyPending); + SB_UT_ADD_SUBTEST(Test_SB_Cmds_RoutingInfoDataGetter); SB_UT_ADD_SUBTEST(Test_SB_Cmds_PipeInfoDef); - SB_UT_ADD_SUBTEST(Test_SB_Cmds_PipeInfoSpec); - SB_UT_ADD_SUBTEST(Test_SB_Cmds_PipeInfoCreateFail); - SB_UT_ADD_SUBTEST(Test_SB_Cmds_PipeInfoHdrFail); - SB_UT_ADD_SUBTEST(Test_SB_Cmds_PipeInfoWriteFail); + SB_UT_ADD_SUBTEST(Test_SB_Cmds_PipeInfoAlreadyPending); + SB_UT_ADD_SUBTEST(Test_SB_Cmds_PipeInfoDataGetter); SB_UT_ADD_SUBTEST(Test_SB_Cmds_MapInfoDef); - SB_UT_ADD_SUBTEST(Test_SB_Cmds_MapInfoSpec); - SB_UT_ADD_SUBTEST(Test_SB_Cmds_MapInfoCreateFail); - SB_UT_ADD_SUBTEST(Test_SB_Cmds_MapInfoHdrFail); - SB_UT_ADD_SUBTEST(Test_SB_Cmds_MapInfoWriteFail); + SB_UT_ADD_SUBTEST(Test_SB_Cmds_MapInfoAlreadyPending); + SB_UT_ADD_SUBTEST(Test_SB_Cmds_MapInfoDataGetter); SB_UT_ADD_SUBTEST(Test_SB_Cmds_EnRouteValParam); SB_UT_ADD_SUBTEST(Test_SB_Cmds_EnRouteNonExist); SB_UT_ADD_SUBTEST(Test_SB_Cmds_EnRouteInvParam); @@ -529,24 +524,20 @@ void Test_SB_Cmds_RoutingInfoDef(void) CFE_SB_ProcessCmdPipePkt(&SendRoutingInfo.SBBuf); - EVTCNT(9); + EVTCNT(5); EVTSENT(CFE_SB_INIT_EID); EVTSENT(CFE_SB_SUBSCRIPTION_RCVD_EID); - EVTSENT(CFE_SB_INIT_EID); - - EVTSENT(CFE_SB_SND_RTG_EID); - TEARDOWN(CFE_SB_DeletePipe(CFE_SB.CmdPipe)); } /* end Test_SB_Cmds_RoutingInfoDef */ /* -** Test send routing information command using a specified file name +** Test send routing information command with request already pending */ -void Test_SB_Cmds_RoutingInfoSpec(void) +void Test_SB_Cmds_RoutingInfoAlreadyPending(void) { union { @@ -557,34 +548,7 @@ void Test_SB_Cmds_RoutingInfoSpec(void) CFE_SB_MsgId_t MsgId = CFE_SB_ValueToMsgId(CFE_SB_CMD_MID); CFE_MSG_Size_t Size = sizeof(SendRoutingInfo.Cmd); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetFcnCode), &FcnCode, sizeof(FcnCode), false); - strncpy(SendRoutingInfo.Cmd.Payload.Filename, "RoutingTstFile", - sizeof(SendRoutingInfo.Cmd.Payload.Filename) - 1); - SendRoutingInfo.Cmd.Payload.Filename[sizeof(SendRoutingInfo.Cmd.Payload.Filename) - 1] = '\0'; - - CFE_SB_ProcessCmdPipePkt(&SendRoutingInfo.SBBuf); - - EVTCNT(1); - - EVTSENT(CFE_SB_SND_RTG_EID); - -} /* end Test_SB_Cmds_RoutingInfoSpec */ - -/* -** Test send routing information command with a file creation failure -*/ -void Test_SB_Cmds_RoutingInfoCreateFail(void) -{ - union - { - CFE_SB_Buffer_t SBBuf; - CFE_SB_SendRoutingInfoCmd_t Cmd; - } SendRoutingInfo; - CFE_MSG_FcnCode_t FcnCode = CFE_SB_SEND_ROUTING_INFO_CC; - CFE_SB_MsgId_t MsgId = CFE_SB_ValueToMsgId(CFE_SB_CMD_MID); - CFE_MSG_Size_t Size = sizeof(SendRoutingInfo.Cmd); + UT_SetDefaultReturnValue(UT_KEY(CFE_FS_BackgroundFileDumpIsPending), true); UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); @@ -593,61 +557,63 @@ void Test_SB_Cmds_RoutingInfoCreateFail(void) sizeof(SendRoutingInfo.Cmd.Payload.Filename) - 1); SendRoutingInfo.Cmd.Payload.Filename[sizeof(SendRoutingInfo.Cmd.Payload.Filename) - 1] = '\0'; - /* Make function CFE_SB_SendRtgInfo return CFE_SB_FILE_IO_ERR */ - UT_SetDefaultReturnValue(UT_KEY(OS_OpenCreate), OS_ERROR); - CFE_SB_ProcessCmdPipePkt(&SendRoutingInfo.SBBuf); EVTCNT(1); EVTSENT(CFE_SB_SND_RTG_ERR1_EID); -} /* end Test_SB_Cmds_RoutingInfoCreateFail */ - -/* -** Test send routing information command with a file header write failure -*/ -void Test_SB_Cmds_RoutingInfoHdrFail(void) -{ - UT_SetDeferredRetcode(UT_KEY(CFE_FS_WriteHeader), 1, -1); - - ASSERT_EQ(CFE_SB_SendRtgInfo("RoutingTstFile"), CFE_SB_FILE_IO_ERR); - - EVTCNT(1); - - EVTSENT(CFE_SB_FILEWRITE_ERR_EID); - -} /* end Test_SB_Cmds_RoutingInfoHdrFail */ +} /* end Test_SB_Cmds_RoutingInfoSpec */ /* -** Test send routing information command with a file write failure on -** the second write +** Test send routing information command data getter */ -void Test_SB_Cmds_RoutingInfoWriteFail(void) +void Test_SB_Cmds_RoutingInfoDataGetter(void) { - /* Make some routing info by calling CFE_SB_AppInit */ - SETUP(CFE_SB_AppInit()); - - UT_SetDeferredRetcode(UT_KEY(OS_write), 2, -1); - - ASSERT_EQ(CFE_SB_SendRtgInfo("RoutingTstFile"), CFE_SB_FILE_IO_ERR); - - EVTCNT(9); - - EVTSENT(CFE_SB_PIPE_ADDED_EID); + CFE_SB_PipeId_t PipeId1; + CFE_SB_PipeId_t PipeId2; + CFE_SB_PipeId_t PipeId3; + CFE_SB_MsgId_t MsgId0 = SB_UT_TLM_MID1; + CFE_SB_MsgId_t MsgId1 = SB_UT_TLM_MID2; + CFE_SB_MsgId_t MsgId2 = SB_UT_TLM_MID3; + CFE_SB_MsgId_t MsgId3 = SB_UT_TLM_MID4; + CFE_SB_MsgId_t MsgId4 = SB_UT_TLM_MID5; + CFE_SB_MsgId_t MsgId5 = SB_UT_TLM_MID6; + uint16 PipeDepth = 10; + void *LocalBuffer; + size_t LocalBufSize; + CFE_SB_BackgroundFileStateInfo_t State; - EVTSENT(CFE_SB_SUBSCRIPTION_RCVD_EID); + /* Create some map info */ + SETUP(CFE_SB_CreatePipe(&PipeId1, PipeDepth, "TestPipe1")); + SETUP(CFE_SB_CreatePipe(&PipeId2, PipeDepth, "TestPipe2")); + SETUP(CFE_SB_CreatePipe(&PipeId3, PipeDepth, "TestPipe3")); + SETUP(CFE_SB_Subscribe(MsgId0, PipeId1)); + SETUP(CFE_SB_Subscribe(MsgId0, PipeId2)); + SETUP(CFE_SB_Subscribe(MsgId1, PipeId1)); + SETUP(CFE_SB_Subscribe(MsgId2, PipeId3)); + SETUP(CFE_SB_Subscribe(MsgId3, PipeId3)); + SETUP(CFE_SB_Subscribe(MsgId4, PipeId3)); + SETUP(CFE_SB_Subscribe(MsgId5, PipeId2)); - EVTSENT(CFE_SB_INIT_EID); + memset(&State, 0, sizeof(State)); + LocalBuffer = NULL; + LocalBufSize = 0; - EVTSENT(CFE_SB_FILEWRITE_ERR_EID); + ASSERT_TRUE(!CFE_SB_WriteRouteInfoDataGetter(&State, 0, &LocalBuffer, &LocalBufSize)); + UtAssert_NOT_NULL(LocalBuffer); + UtAssert_NONZERO(LocalBufSize); - TEARDOWN(CFE_SB_DeletePipe(CFE_SB.CmdPipe)); + ASSERT_TRUE(CFE_SB_WriteRouteInfoDataGetter(&State, CFE_PLATFORM_SB_MAX_MSG_IDS, &LocalBuffer, &LocalBufSize)); + UtAssert_ZERO(LocalBufSize); -} /* end Test_SB_Cmds_RoutingInfoWriteFail */ + TEARDOWN(CFE_SB_DeletePipe(PipeId1)); + TEARDOWN(CFE_SB_DeletePipe(PipeId2)); + TEARDOWN(CFE_SB_DeletePipe(PipeId3)); +} /* end Test_SB_Cmds_RoutingInfoDataGetter */ /* -** Test send pipe information command using the default file name +** Test send pipe information command default / nominal path */ void Test_SB_Cmds_PipeInfoDef(void) { @@ -676,10 +642,9 @@ void Test_SB_Cmds_PipeInfoDef(void) CFE_SB_ProcessCmdPipePkt(&SendPipeInfo.SBBuf); - EVTCNT(4); + EVTCNT(3); EVTSENT(CFE_SB_PIPE_ADDED_EID); - EVTSENT(CFE_SB_SND_RTG_EID); TEARDOWN(CFE_SB_DeletePipe(PipeId1)); TEARDOWN(CFE_SB_DeletePipe(PipeId2)); @@ -688,9 +653,9 @@ void Test_SB_Cmds_PipeInfoDef(void) } /* end Test_SB_Cmds_PipeInfoDef */ /* -** Test send pipe information command using a specified file name +** Test send pipe information command when already pending */ -void Test_SB_Cmds_PipeInfoSpec(void) +void Test_SB_Cmds_PipeInfoAlreadyPending(void) { union { @@ -701,6 +666,8 @@ void Test_SB_Cmds_PipeInfoSpec(void) CFE_SB_MsgId_t MsgId = CFE_SB_ValueToMsgId(CFE_SB_CMD_MID); CFE_MSG_Size_t Size = sizeof(SendPipeInfo.Cmd); + UT_SetDefaultReturnValue(UT_KEY(CFE_FS_BackgroundFileDumpIsPending), true); + UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); UT_SetDataBuffer(UT_KEY(CFE_MSG_GetFcnCode), &FcnCode, sizeof(FcnCode), false); @@ -712,70 +679,131 @@ void Test_SB_Cmds_PipeInfoSpec(void) EVTCNT(1); - EVTSENT(CFE_SB_SND_RTG_EID); + EVTSENT(CFE_SB_SND_RTG_ERR1_EID); -} /* end Test_SB_Cmds_PipeInfoSpec */ +} /* end Test_SB_Cmds_PipeInfoAlreadyPending */ /* -** Test send pipe information command with a file creation failure +** Test send pipe information data getter */ -void Test_SB_Cmds_PipeInfoCreateFail(void) +void Test_SB_Cmds_PipeInfoDataGetter(void) { - UT_SetDefaultReturnValue(UT_KEY(OS_OpenCreate), OS_ERROR); - ASSERT_EQ(CFE_SB_SendPipeInfo("PipeTstFile"), CFE_SB_FILE_IO_ERR); + CFE_SB_PipeId_t PipeId1; + CFE_SB_PipeId_t PipeId2; + CFE_SB_PipeId_t PipeId3; + uint16 PipeDepth = 10; + void *LocalBuffer; + size_t LocalBufSize; + CFE_SB_BackgroundFileStateInfo_t State; - EVTCNT(1); + SETUP(CFE_SB_CreatePipe(&PipeId1, PipeDepth, "TestPipe1")); + SETUP(CFE_SB_CreatePipe(&PipeId2, PipeDepth, "TestPipe2")); + SETUP(CFE_SB_CreatePipe(&PipeId3, PipeDepth, "TestPipe3")); - EVTSENT(CFE_SB_SND_RTG_ERR1_EID); + memset(&State, 0, sizeof(State)); + LocalBuffer = NULL; + LocalBufSize = 0; + + /* Note that CFE_SB_CreatePipe() fills entry 1 first, so entry 0 is unused */ + ASSERT_TRUE(!CFE_SB_WritePipeInfoDataGetter(&State, 0, &LocalBuffer, &LocalBufSize)); + UtAssert_ZERO(LocalBufSize); + + ASSERT_TRUE(!CFE_SB_WritePipeInfoDataGetter(&State, 1, &LocalBuffer, &LocalBufSize)); + UtAssert_NOT_NULL(LocalBuffer); + UtAssert_NONZERO(LocalBufSize); -} /* end Test_SB_Cmds_PipeInfoCreateFail */ + ASSERT_TRUE(CFE_SB_WritePipeInfoDataGetter(&State, CFE_PLATFORM_SB_MAX_PIPES-1, &LocalBuffer, &LocalBufSize)); + UtAssert_ZERO(LocalBufSize); + + ASSERT_TRUE(CFE_SB_WritePipeInfoDataGetter(&State, CFE_PLATFORM_SB_MAX_PIPES, &LocalBuffer, &LocalBufSize)); + UtAssert_ZERO(LocalBufSize); + + TEARDOWN(CFE_SB_DeletePipe(PipeId1)); + TEARDOWN(CFE_SB_DeletePipe(PipeId2)); + TEARDOWN(CFE_SB_DeletePipe(PipeId3)); +} /* end Test_SB_Cmds_PipeInfoDataGetter */ /* -** Test send pipe information command with a file header write failure +** Test background file write event generator */ -void Test_SB_Cmds_PipeInfoHdrFail(void) +void Test_SB_Cmds_BackgroundFileWriteEvents(void) { - UT_SetDeferredRetcode(UT_KEY(CFE_FS_WriteHeader), 1, -1); - ASSERT_EQ(CFE_SB_SendPipeInfo("PipeTstFile"), CFE_SB_FILE_IO_ERR); + CFE_SB_BackgroundFileStateInfo_t State; - EVTCNT(1); + memset(&State, 0, sizeof(State)); + UT_ClearEventHistory(); + CFE_SB_BackgroundFileEventHandler(&State, CFE_FS_FileWriteEvent_COMPLETE, CFE_SUCCESS, 10, 0, 1000); + EVTSENT(CFE_SB_SND_RTG_EID); + + UT_ClearEventHistory(); + CFE_SB_BackgroundFileEventHandler(&State, CFE_FS_FileWriteEvent_RECORD_WRITE_ERROR, CFE_SUCCESS, 10, 10, 1000); EVTSENT(CFE_SB_FILEWRITE_ERR_EID); -} /* end Test_SB_Cmds_PipeInfoHdrFail */ + UT_ClearEventHistory(); + CFE_SB_BackgroundFileEventHandler(&State, CFE_FS_FileWriteEvent_HEADER_WRITE_ERROR, CFE_SUCCESS, 10, 10, 1000); + EVTSENT(CFE_SB_FILEWRITE_ERR_EID); + + UT_ClearEventHistory(); + CFE_SB_BackgroundFileEventHandler(&State, CFE_FS_FileWriteEvent_CREATE_ERROR, OS_ERROR, 10, 0, 0); + EVTSENT(CFE_SB_SND_RTG_ERR1_EID); + + UT_ClearEventHistory(); + CFE_SB_BackgroundFileEventHandler(&State, CFE_FS_FileWriteEvent_UNDEFINED, OS_ERROR, 0, 0, 0); + EVTCNT(0); + +} /* -** Test send pipe information command with a file write failure on -** the second write +** Test send map information data getter for background file write */ -void Test_SB_Cmds_PipeInfoWriteFail(void) +void Test_SB_Cmds_MapInfoDataGetter(void) { CFE_SB_PipeId_t PipeId1; CFE_SB_PipeId_t PipeId2; CFE_SB_PipeId_t PipeId3; + CFE_SB_MsgId_t MsgId0 = SB_UT_TLM_MID1; + CFE_SB_MsgId_t MsgId1 = SB_UT_TLM_MID2; + CFE_SB_MsgId_t MsgId2 = SB_UT_TLM_MID3; + CFE_SB_MsgId_t MsgId3 = SB_UT_TLM_MID4; + CFE_SB_MsgId_t MsgId4 = SB_UT_TLM_MID5; + CFE_SB_MsgId_t MsgId5 = SB_UT_TLM_MID6; uint16 PipeDepth = 10; + void *LocalBuffer; + size_t LocalBufSize; + CFE_SB_BackgroundFileStateInfo_t State; + /* Create some map info */ SETUP(CFE_SB_CreatePipe(&PipeId1, PipeDepth, "TestPipe1")); SETUP(CFE_SB_CreatePipe(&PipeId2, PipeDepth, "TestPipe2")); SETUP(CFE_SB_CreatePipe(&PipeId3, PipeDepth, "TestPipe3")); - UT_SetDeferredRetcode(UT_KEY(OS_write), 2, -1); - - ASSERT_EQ(CFE_SB_SendPipeInfo("PipeTstFile"), CFE_SB_FILE_IO_ERR); + SETUP(CFE_SB_Subscribe(MsgId0, PipeId1)); + SETUP(CFE_SB_Subscribe(MsgId0, PipeId2)); + SETUP(CFE_SB_Subscribe(MsgId1, PipeId1)); + SETUP(CFE_SB_Subscribe(MsgId2, PipeId3)); + SETUP(CFE_SB_Subscribe(MsgId3, PipeId3)); + SETUP(CFE_SB_Subscribe(MsgId4, PipeId3)); + SETUP(CFE_SB_Subscribe(MsgId5, PipeId2)); - EVTCNT(4); + memset(&State, 0, sizeof(State)); + LocalBuffer = NULL; + LocalBufSize = 0; - EVTSENT(CFE_SB_PIPE_ADDED_EID); + ASSERT_TRUE(!CFE_SB_WriteMsgMapInfoDataGetter(&State, 0, &LocalBuffer, &LocalBufSize)); + UtAssert_NOT_NULL(LocalBuffer); + UtAssert_NONZERO(LocalBufSize); - EVTSENT(CFE_SB_FILEWRITE_ERR_EID); + ASSERT_TRUE(CFE_SB_WriteMsgMapInfoDataGetter(&State, CFE_PLATFORM_SB_MAX_MSG_IDS, &LocalBuffer, &LocalBufSize)); + UtAssert_NULL(LocalBuffer); + UtAssert_ZERO(LocalBufSize); TEARDOWN(CFE_SB_DeletePipe(PipeId1)); TEARDOWN(CFE_SB_DeletePipe(PipeId2)); TEARDOWN(CFE_SB_DeletePipe(PipeId3)); - -} /* end Test_SB_Cmds_PipeInfoWriteFail */ +} /* end Test_SB_MapInfoDataGetter */ /* -** Test send map information command using the default file name +** Test send map information command nominal path */ void Test_SB_Cmds_MapInfoDef(void) { @@ -817,9 +845,7 @@ void Test_SB_Cmds_MapInfoDef(void) CFE_SB_ProcessCmdPipePkt(&SendMapInfo.SBBuf); - EVTCNT(11); - - EVTSENT(CFE_SB_SND_RTG_EID); + EVTCNT(10); EVTSENT(CFE_SB_PIPE_ADDED_EID); @@ -832,9 +858,9 @@ void Test_SB_Cmds_MapInfoDef(void) } /* end Test_SB_Cmds_MapInfoDef */ /* -** Test send map information command using a specified file name +** Test send map information command when already pending */ -void Test_SB_Cmds_MapInfoSpec(void) +void Test_SB_Cmds_MapInfoAlreadyPending(void) { union { @@ -845,6 +871,8 @@ void Test_SB_Cmds_MapInfoSpec(void) CFE_SB_MsgId_t MsgId = CFE_SB_ValueToMsgId(CFE_SB_CMD_MID); CFE_MSG_Size_t Size = sizeof(SendMapInfo.Cmd); + UT_SetDefaultReturnValue(UT_KEY(CFE_FS_BackgroundFileDumpIsPending), true); + UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); UT_SetDataBuffer(UT_KEY(CFE_MSG_GetFcnCode), &FcnCode, sizeof(FcnCode), false); @@ -856,83 +884,9 @@ void Test_SB_Cmds_MapInfoSpec(void) EVTCNT(1); - EVTSENT(CFE_SB_SND_RTG_EID); - -} /* end Test_SB_Cmds_MapInfoSpec */ - -/* -** Test send map information command with a file creation failure -*/ -void Test_SB_Cmds_MapInfoCreateFail(void) -{ - UT_SetDefaultReturnValue(UT_KEY(OS_OpenCreate), OS_ERROR); - ASSERT_EQ(CFE_SB_SendMapInfo("MapTstFile"), CFE_SB_FILE_IO_ERR); - - EVTCNT(1); - EVTSENT(CFE_SB_SND_RTG_ERR1_EID); -} /* end Test_SB_Cmds_MapInfoCreateFail */ - -/* -** Test send map information command with a file header write failure -*/ -void Test_SB_Cmds_MapInfoHdrFail(void) -{ - UT_SetDeferredRetcode(UT_KEY(CFE_FS_WriteHeader), 1, -1); - ASSERT_EQ(CFE_SB_SendMapInfo("MapTstFile"), CFE_SB_FILE_IO_ERR); - - EVTCNT(1); - - EVTSENT(CFE_SB_FILEWRITE_ERR_EID); - -} /* end Test_SB_Cmds_MapInfoHdrFail */ - -/* -** Test send map information command with a file write failure on -** the second write -*/ -void Test_SB_Cmds_MapInfoWriteFail(void) -{ - CFE_SB_PipeId_t PipeId1; - CFE_SB_PipeId_t PipeId2; - CFE_SB_PipeId_t PipeId3; - CFE_SB_MsgId_t MsgId0 = SB_UT_TLM_MID1; - CFE_SB_MsgId_t MsgId1 = SB_UT_TLM_MID2; - CFE_SB_MsgId_t MsgId2 = SB_UT_TLM_MID3; - CFE_SB_MsgId_t MsgId3 = SB_UT_TLM_MID4; - CFE_SB_MsgId_t MsgId4 = SB_UT_TLM_MID5; - CFE_SB_MsgId_t MsgId5 = SB_UT_TLM_MID6; - uint16 PipeDepth = 10; - - /* Create some map info */ - SETUP(CFE_SB_CreatePipe(&PipeId1, PipeDepth, "TestPipe1")); - SETUP(CFE_SB_CreatePipe(&PipeId2, PipeDepth, "TestPipe2")); - SETUP(CFE_SB_CreatePipe(&PipeId3, PipeDepth, "TestPipe3")); - SETUP(CFE_SB_Subscribe(MsgId0, PipeId1)); - SETUP(CFE_SB_Subscribe(MsgId0, PipeId2)); - SETUP(CFE_SB_Subscribe(MsgId1, PipeId1)); - SETUP(CFE_SB_Subscribe(MsgId2, PipeId3)); - SETUP(CFE_SB_Subscribe(MsgId3, PipeId3)); - SETUP(CFE_SB_Subscribe(MsgId4, PipeId3)); - SETUP(CFE_SB_Subscribe(MsgId5, PipeId2)); - UT_SetDeferredRetcode(UT_KEY(OS_write), 2, -1); - - ASSERT_EQ(CFE_SB_SendMapInfo("MapTstFile"), CFE_SB_FILE_IO_ERR); - - EVTCNT(11); - - EVTSENT(CFE_SB_FILEWRITE_ERR_EID); - - EVTSENT(CFE_SB_PIPE_ADDED_EID); - - EVTSENT(CFE_SB_SUBSCRIPTION_RCVD_EID); - - TEARDOWN(CFE_SB_DeletePipe(PipeId1)); - TEARDOWN(CFE_SB_DeletePipe(PipeId2)); - TEARDOWN(CFE_SB_DeletePipe(PipeId3)); - -} /* end Test_SB_Cmds_MapInfoWriteFail */ +} /* end Test_SB_Cmds_MapInfoSpec */ /* ** Test command to enable a specific route using a valid route diff --git a/fsw/cfe-core/unit-test/sb_UT.h b/fsw/cfe-core/unit-test/sb_UT.h index d2acfb22b..2fb4b2faa 100644 --- a/fsw/cfe-core/unit-test/sb_UT.h +++ b/fsw/cfe-core/unit-test/sb_UT.h @@ -365,7 +365,7 @@ void Test_SB_Cmds_Stats(void); /*****************************************************************************/ /** -** \brief Test send routing information command using the default file name +** \brief Test send routing information command default/nominal path ** ** \par Description ** This function tests the send routing information command using the @@ -381,7 +381,7 @@ void Test_SB_Cmds_RoutingInfoDef(void); /*****************************************************************************/ /** -** \brief Test send routing information command using a specified file name +** \brief Test send routing information command with request already pending ** ** \par Description ** This function tests the send routing information command using a @@ -393,15 +393,11 @@ void Test_SB_Cmds_RoutingInfoDef(void); ** \returns ** This function does not return a value. ******************************************************************************/ -void Test_SB_Cmds_RoutingInfoSpec(void); +void Test_SB_Cmds_RoutingInfoAlreadyPending(void); /*****************************************************************************/ /** -** \brief Test send routing information command with a file creation failure -** -** \par Description -** This function tests the send routing information command with a file -** creation failure. +** \brief Test routing information data getter ** ** \par Assumptions, External Events, and Notes: ** None @@ -409,7 +405,7 @@ void Test_SB_Cmds_RoutingInfoSpec(void); ** \returns ** This function does not return a value. ******************************************************************************/ -void Test_SB_Cmds_RoutingInfoCreateFail(void); +void Test_SB_Cmds_RoutingInfoDataGetter(void); /*****************************************************************************/ /** @@ -650,41 +646,7 @@ void Test_GetPipeIdByName(void); /*****************************************************************************/ /** -** \brief Test send routing information command with a file header -** write failure -** -** \par Description -** This function tests the send routing information command with a file -** header write failure. -** -** \par Assumptions, External Events, and Notes: -** None -** -** \returns -** This function does not return a value. -******************************************************************************/ -void Test_SB_Cmds_RoutingInfoHdrFail(void); - -/*****************************************************************************/ -/** -** \brief Test send routing information command with a file header write -** failure on the second write -** -** \par Description -** This function tests the send routing information command with a file -** header write failure on the second write. -** -** \par Assumptions, External Events, and Notes: -** None -** -** \returns -** This function does not return a value. -******************************************************************************/ -void Test_SB_Cmds_RoutingInfoWriteFail(void); - -/*****************************************************************************/ -/** -** \brief Test send pipe information command using the default file name +** \brief Test send pipe information command default / nominal path ** ** \par Description ** This function tests the send pipe information command using the @@ -700,11 +662,7 @@ void Test_SB_Cmds_PipeInfoDef(void); /*****************************************************************************/ /** -** \brief Test send pipe information command using a specified file name -** -** \par Description -** This function tests the send pipe information command using a -** specified file name. +** \brief Test send pipe information command when already pending ** ** \par Assumptions, External Events, and Notes: ** None @@ -712,15 +670,11 @@ void Test_SB_Cmds_PipeInfoDef(void); ** \returns ** This function does not return a value. ******************************************************************************/ -void Test_SB_Cmds_PipeInfoSpec(void); +void Test_SB_Cmds_PipeInfoAlreadyPending(void); /*****************************************************************************/ /** -** \brief Test send pipe information command with a file creation failure -** -** \par Description -** This function tests the send pipe information command with a file -** creation failure. +** \brief Test pipe information data getter ** ** \par Assumptions, External Events, and Notes: ** None @@ -728,16 +682,11 @@ void Test_SB_Cmds_PipeInfoSpec(void); ** \returns ** This function does not return a value. ******************************************************************************/ -void Test_SB_Cmds_PipeInfoCreateFail(void); +void Test_SB_Cmds_PipeInfoDataGetter(void); /*****************************************************************************/ /** -** \brief Test send pipe information command with a file header -** write failure -** -** \par Description -** This function tests the send pipe information command with a file -** header write failure. +** \brief Test background file writer event handler ** ** \par Assumptions, External Events, and Notes: ** None @@ -745,28 +694,11 @@ void Test_SB_Cmds_PipeInfoCreateFail(void); ** \returns ** This function does not return a value. ******************************************************************************/ -void Test_SB_Cmds_PipeInfoHdrFail(void); +void Test_SB_Cmds_BackgroundFileWriteEvents(void); /*****************************************************************************/ /** -** \brief Test send pipe information command with a file write failure on -** the second write -** -** \par Description -** This function tests the send pipe information command with a file -** write failure on the second write. -** -** \par Assumptions, External Events, and Notes: -** None -** -** \returns -** This function does not return a value. -******************************************************************************/ -void Test_SB_Cmds_PipeInfoWriteFail(void); - -/*****************************************************************************/ -/** -** \brief Test send map information command using the default file name +** \brief Test send map information command using the defaults / nominal path ** ** \par Description ** This function tests the send map information command using the @@ -782,7 +714,7 @@ void Test_SB_Cmds_MapInfoDef(void); /*****************************************************************************/ /** -** \brief Test send map information command using a specified file name +** \brief Test send map information command when already pending ** ** \par Description ** This function tests the send map information command using a @@ -794,48 +726,11 @@ void Test_SB_Cmds_MapInfoDef(void); ** \returns ** This function does not return a value. ******************************************************************************/ -void Test_SB_Cmds_MapInfoSpec(void); +void Test_SB_Cmds_MapInfoAlreadyPending(void); /*****************************************************************************/ /** -** \brief Test send map information command with a file creation failure -** -** \par Description -** This function tests the send map information command with a file -** creation failure. -** -** \par Assumptions, External Events, and Notes: -** None -** -** \returns -** This function does not return a value. -******************************************************************************/ -void Test_SB_Cmds_MapInfoCreateFail(void); - -/*****************************************************************************/ -/** -** \brief Test send map information command with a file header write failure -** -** \par Description -** This function tests the send map information command with a file -** header write failure. -** -** \par Assumptions, External Events, and Notes: -** None -** -** \returns -** This function does not return a value. -******************************************************************************/ -void Test_SB_Cmds_MapInfoHdrFail(void); - -/*****************************************************************************/ -/** -** \brief Test send map information command with a file write failure on -** the second write -** -** \par Description -** This function tests the send map information command with a file -** write failure on the second write. +** \brief Test map information data getter function ** ** \par Assumptions, External Events, and Notes: ** None @@ -843,7 +738,7 @@ void Test_SB_Cmds_MapInfoHdrFail(void); ** \returns ** This function does not return a value. ******************************************************************************/ -void Test_SB_Cmds_MapInfoWriteFail(void); +void Test_SB_Cmds_MapInfoDataGetter(void); /*****************************************************************************/ /** diff --git a/fsw/cfe-core/unit-test/tbl_UT.c b/fsw/cfe-core/unit-test/tbl_UT.c index 783f083ae..bba0c2706 100644 --- a/fsw/cfe-core/unit-test/tbl_UT.c +++ b/fsw/cfe-core/unit-test/tbl_UT.c @@ -1145,6 +1145,9 @@ void Test_CFE_TBL_DumpRegCmd(void) int q; CFE_TBL_DumpRegistryCmd_t DumpRegCmd; CFE_ES_AppId_t AppID; + size_t LocalSize; + void *LocalBuf; + bool IsEOF; /* Get the AppID being used for UT */ CFE_ES_GetAppID(&AppID); @@ -1157,77 +1160,122 @@ void Test_CFE_TBL_DumpRegCmd(void) CFE_TBL_TaskData.Registry[q].HeadOfAccessList = CFE_TBL_END_OF_LIST; } - /* Test with an error creating the dump file */ + /* Test command using the default dump file name (nominal path) */ UT_InitData(); - DumpRegCmd.Payload.DumpFilename[0] = '\0'; - UT_SetDefaultReturnValue(UT_KEY(OS_OpenCreate), OS_ERROR); + UT_SetDefaultReturnValue(UT_KEY(CFE_FS_BackgroundFileDumpIsPending), false); + strncpy(DumpRegCmd.Payload.DumpFilename, "X", sizeof(DumpRegCmd.Payload.DumpFilename) - 1); + DumpRegCmd.Payload.DumpFilename[sizeof(DumpRegCmd.Payload.DumpFilename) - 1] = '\0'; + UT_Report(__FILE__, __LINE__, + CFE_TBL_DumpRegistryCmd(&DumpRegCmd) == + CFE_TBL_INC_CMD_CTR, + "CFE_TBL_DumpRegistryCmd", + "Default dump file name"); + + /* Test command with the dump file already pending (max requests pending) */ + UT_SetDefaultReturnValue(UT_KEY(CFE_FS_BackgroundFileDumpIsPending), true); + UT_SetDefaultReturnValue(UT_KEY(CFE_FS_BackgroundFileDumpRequest), CFE_STATUS_REQUEST_ALREADY_PENDING); UT_Report(__FILE__, __LINE__, CFE_TBL_DumpRegistryCmd(&DumpRegCmd) == CFE_TBL_INC_ERR_CTR, "CFE_TBL_DumpRegistryCmd", - "Error creating dump file"); + "Dump file already pending (FS max requests)"); + UT_ResetState(UT_KEY(CFE_FS_BackgroundFileDumpRequest)); - /* Test with an error writing the cFE File header */ - UT_InitData(); - UT_SetDeferredRetcode(UT_KEY(CFE_FS_WriteHeader), 1, sizeof(CFE_FS_Header_t) - 1); + /* Test command with the dump file already pending (local) */ + UT_SetDefaultReturnValue(UT_KEY(CFE_FS_BackgroundFileDumpIsPending), false); + UT_SetDefaultReturnValue(UT_KEY(CFE_FS_BackgroundFileDumpRequest), CFE_STATUS_REQUEST_ALREADY_PENDING); UT_Report(__FILE__, __LINE__, CFE_TBL_DumpRegistryCmd(&DumpRegCmd) == CFE_TBL_INC_ERR_CTR, "CFE_TBL_DumpRegistryCmd", - "Error writing cFE File header"); + "Dump file already pending (local)"); + + /* Check event generators */ + UT_ClearEventHistory(); + CFE_TBL_TaskData.RegDumpState.FileExisted = true; + CFE_TBL_DumpRegistryEventHandler(&CFE_TBL_TaskData.RegDumpState, CFE_FS_FileWriteEvent_COMPLETE, CFE_SUCCESS, 10, 0, 1000); + UT_Report(__FILE__, __LINE__, + UT_EventIsInHistory(CFE_TBL_OVERWRITE_REG_DUMP_INF_EID), + "CFE_TBL_DumpRegistryEventHandler", + "Dump file created event (overwrite)"); + + UT_ClearEventHistory(); + CFE_TBL_TaskData.RegDumpState.FileExisted = false; + CFE_TBL_DumpRegistryEventHandler(&CFE_TBL_TaskData.RegDumpState, CFE_FS_FileWriteEvent_COMPLETE, CFE_SUCCESS, 10, 0, 1000); + UT_Report(__FILE__, __LINE__, + UT_EventIsInHistory(CFE_TBL_WRITE_REG_DUMP_INF_EID), + "CFE_TBL_DumpRegistryEventHandler", + "Dump file created event (new)"); + + UT_ClearEventHistory(); + CFE_TBL_DumpRegistryEventHandler(&CFE_TBL_TaskData.RegDumpState, CFE_FS_FileWriteEvent_RECORD_WRITE_ERROR, CFE_SUCCESS, 10, 10, 1000); + UT_Report(__FILE__, __LINE__, + UT_EventIsInHistory(CFE_TBL_WRITE_TBL_REG_ERR_EID), + "CFE_TBL_DumpRegistryEventHandler", + "Dump file record write error event"); + + UT_ClearEventHistory(); + CFE_TBL_DumpRegistryEventHandler(&CFE_TBL_TaskData.RegDumpState, CFE_FS_FileWriteEvent_HEADER_WRITE_ERROR, CFE_SUCCESS, 10, 10, 1000); + UT_Report(__FILE__, __LINE__, + UT_EventIsInHistory(CFE_TBL_WRITE_CFE_HDR_ERR_EID), + "CFE_TBL_DumpRegistryEventHandler", + "Dump file header write error event"); + + UT_ClearEventHistory(); + CFE_TBL_DumpRegistryEventHandler(&CFE_TBL_TaskData.RegDumpState, CFE_FS_FileWriteEvent_CREATE_ERROR, OS_ERROR, 10, 0, 0); + UT_Report(__FILE__, __LINE__, + UT_EventIsInHistory(CFE_TBL_CREATING_DUMP_FILE_ERR_EID), + "CFE_TBL_DumpRegistryEventHandler", + "Dump file created error event"); + + UT_ClearEventHistory(); + CFE_TBL_DumpRegistryEventHandler(&CFE_TBL_TaskData.RegDumpState, CFE_FS_FileWriteEvent_UNDEFINED, OS_ERROR, 0, 0, 0); + UT_Report(__FILE__, __LINE__, + UT_GetNumEventsSent() == 0, + "CFE_TBL_DumpRegistryEventHandler", + "Undefined event is ignored"); /* Test where the table is owned, the file doesn't already exist, and the * table is successfully dumped */ UT_InitData(); - UT_SetDeferredRetcode(UT_KEY(CFE_FS_WriteHeader), 10, sizeof(CFE_FS_Header_t)); CFE_TBL_TaskData.Registry[0].OwnerAppId = AppID; CFE_TBL_TaskData.Registry[0].HeadOfAccessList = CFE_TBL_END_OF_LIST; CFE_TBL_TaskData.Registry[1].OwnerAppId = CFE_TBL_NOT_OWNED; CFE_TBL_TaskData.Registry[0].LoadInProgress = CFE_TBL_NO_LOAD_IN_PROGRESS + 1; CFE_TBL_TaskData.Registry[0].DoubleBuffered = true; - UT_SetDeferredRetcode(UT_KEY(OS_OpenCreate), 1, OS_ERROR); - UT_Report(__FILE__, __LINE__, - CFE_TBL_DumpRegistryCmd(&DumpRegCmd) == - CFE_TBL_INC_CMD_CTR, - "CFE_TBL_DumpRegistryCmd", - "Table is owned, file didn't exist previously: successfully " - "dumped table"); - - /* Test where the file did exist previously and the table is successfully - * overwritten - */ - UT_InitData(); - CFE_TBL_TaskData.Registry[0].LoadInProgress = CFE_TBL_NO_LOAD_IN_PROGRESS; - UT_Report(__FILE__, __LINE__, - CFE_TBL_DumpRegistryCmd(&DumpRegCmd) == - CFE_TBL_INC_CMD_CTR, - "CFE_TBL_DumpRegistryCmd", - "File did exist previously: successfully overwritten table"); - - /* Test where the table is not owned, the OS write fails, resulting in an - * error writing to the registry - */ - UT_InitData(); - UT_SetDeferredRetcode(UT_KEY(OS_write), 1, sizeof(CFE_TBL_RegDumpRec_t) - 1); - CFE_TBL_TaskData.Registry[0].OwnerAppId = CFE_TBL_NOT_OWNED; - CFE_TBL_TaskData.Registry[0].HeadOfAccessList = 2; + LocalBuf = NULL; + LocalSize = 0; + IsEOF = CFE_TBL_DumpRegistryGetter(&CFE_TBL_TaskData.RegDumpState, 0, &LocalBuf, &LocalSize); + UT_Report(__FILE__, __LINE__, + !IsEOF, + "CFE_TBL_DumpRegistryGetter", + "Nominal, first record, not end of file"); + UtAssert_NOT_NULL(LocalBuf); + UtAssert_NONZERO(LocalSize); + + CFE_TBL_TaskData.Registry[CFE_PLATFORM_TBL_MAX_NUM_TABLES-1].OwnerAppId = CFE_TBL_NOT_OWNED; + CFE_TBL_TaskData.Registry[CFE_PLATFORM_TBL_MAX_NUM_TABLES-1].HeadOfAccessList = 2; CFE_TBL_TaskData.Handles[2].NextLink = CFE_TBL_END_OF_LIST; + LocalBuf = NULL; + LocalSize = 0; + IsEOF = CFE_TBL_DumpRegistryGetter(&CFE_TBL_TaskData.RegDumpState, CFE_PLATFORM_TBL_MAX_NUM_TABLES-1, &LocalBuf, &LocalSize); UT_Report(__FILE__, __LINE__, - CFE_TBL_DumpRegistryCmd(&DumpRegCmd) == - CFE_TBL_INC_ERR_CTR, - "CFE_TBL_DumpRegistryCmd", - "Table is not owned, OS_write fails: Error writing Registry"); + IsEOF, + "CFE_TBL_DumpRegistryGetter", + "Nominal, last record, multiple accessors, end of file"); + UtAssert_NOT_NULL(LocalBuf); + UtAssert_NONZERO(LocalSize); - /* Test using the default dump file name */ - UT_InitData(); - strncpy(DumpRegCmd.Payload.DumpFilename, "X", sizeof(DumpRegCmd.Payload.DumpFilename) - 1); - DumpRegCmd.Payload.DumpFilename[sizeof(DumpRegCmd.Payload.DumpFilename) - 1] = '\0'; + /* Test with record numb beyond EOF (should be ignored, return null) */ + IsEOF = CFE_TBL_DumpRegistryGetter(&CFE_TBL_TaskData.RegDumpState, CFE_PLATFORM_TBL_MAX_NUM_TABLES+1, &LocalBuf, &LocalSize); UT_Report(__FILE__, __LINE__, - CFE_TBL_DumpRegistryCmd(&DumpRegCmd) == - CFE_TBL_INC_CMD_CTR, - "CFE_TBL_DumpRegistryCmd", - "Default dump file name"); + IsEOF, + "CFE_TBL_DumpRegistryGetter", + "Past end of file"); + UtAssert_NULL(LocalBuf); + UtAssert_ZERO(LocalSize); + } /* diff --git a/fsw/cfe-core/ut-stubs/ut_es_stubs.c b/fsw/cfe-core/ut-stubs/ut_es_stubs.c index f10629b61..57b1bd105 100644 --- a/fsw/cfe-core/ut-stubs/ut_es_stubs.c +++ b/fsw/cfe-core/ut-stubs/ut_es_stubs.c @@ -1336,3 +1336,8 @@ int32 CFE_ES_TaskID_ToIndex(CFE_ES_TaskId_t TaskID, uint32 *Idx) return return_code; } + +void CFE_ES_BackgroundWakeup(void) +{ + UT_DEFAULT_IMPL(CFE_ES_BackgroundWakeup); +} diff --git a/fsw/cfe-core/ut-stubs/ut_fs_stubs.c b/fsw/cfe-core/ut-stubs/ut_fs_stubs.c index 126fe2d3a..f3bdaf3cd 100644 --- a/fsw/cfe-core/ut-stubs/ut_fs_stubs.c +++ b/fsw/cfe-core/ut-stubs/ut_fs_stubs.c @@ -304,3 +304,40 @@ int32 CFE_FS_ExtractFilenameFromPath(const char *OriginalPath, char *FileNameOnl return status; } + +bool CFE_FS_RunBackgroundFileDump(uint32 ElapsedTime, void *Arg) +{ + UT_Stub_RegisterContextGenericArg(UT_KEY(CFE_FS_RunBackgroundFileDump), ElapsedTime); + UT_Stub_RegisterContextGenericArg(UT_KEY(CFE_FS_RunBackgroundFileDump), Arg); + + int32 status; + + status = UT_DEFAULT_IMPL(CFE_FS_RunBackgroundFileDump); + + return status; +} + +int32 CFE_FS_BackgroundFileDumpRequest(CFE_FS_FileWriteMetaData_t *Meta) +{ + UT_Stub_RegisterContextGenericArg(UT_KEY(CFE_FS_BackgroundFileDumpRequest), Meta); + + int32 status; + + status = UT_DEFAULT_IMPL(CFE_FS_BackgroundFileDumpRequest); + + if (status == CFE_SUCCESS) + { + /* Snapshot the request, in case the UT test case wants to look */ + UT_Stub_CopyFromLocal(UT_KEY(CFE_FS_BackgroundFileDumpRequest), Meta, sizeof(*Meta)); + } + + return status; +} + +bool CFE_FS_BackgroundFileDumpIsPending(const CFE_FS_FileWriteMetaData_t *Meta) +{ + UT_Stub_RegisterContextGenericArg(UT_KEY(CFE_FS_BackgroundFileDumpIsPending), Meta); + + return UT_DEFAULT_IMPL(CFE_FS_BackgroundFileDumpIsPending); + +}