diff --git a/README.md b/README.md index f7e5fb10c..0b985bd14 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,25 @@ The detailed cFE user's guide can be viewed at + ### Development Build: 6.8.0-rc1+dev348 - Corrects reference to PSP header file location. Build now succesfully completes the build succeeds again when using `add_psp_module()` in custom CMakeLists file. diff --git a/cmake/arch_build.cmake b/cmake/arch_build.cmake index e364d0f12..ec964ae59 100644 --- a/cmake/arch_build.cmake +++ b/cmake/arch_build.cmake @@ -474,7 +474,9 @@ function(process_arch SYSVAR) add_subdirectory(cmake/target ${TGTNAME}) foreach(INSTFILE ${${TGTNAME}_FILELIST}) - if(EXISTS ${MISSION_DEFS}/${TGTNAME}_${INSTFILE}) + if(EXISTS ${MISSION_DEFS}/${TGTNAME}/${INSTFILE}) + set(FILESRC ${MISSION_DEFS}/${TGTNAME}/${INSTFILE}) + elseif(EXISTS ${MISSION_DEFS}/${TGTNAME}_${INSTFILE}) set(FILESRC ${MISSION_DEFS}/${TGTNAME}_${INSTFILE}) elseif(EXISTS ${MISSION_DEFS}/${INSTFILE}) set(FILESRC ${MISSION_DEFS}/${INSTFILE}) @@ -482,6 +484,9 @@ function(process_arch SYSVAR) set(FILESRC) endif() if (FILESRC) + # In case the file is a symlink, follow it to get to the actual file + get_filename_component(FILESRC "${FILESRC}" REALPATH) + message("NOTE: Selected ${FILESRC} as source for ${INSTFILE} on ${TGTNAME}") install(FILES ${FILESRC} DESTINATION ${TGTNAME}/${INSTALL_SUBDIR} RENAME ${INSTFILE}) else(FILESRC) message("WARNING: Install file ${INSTFILE} for ${TGTNAME} not found") diff --git a/cmake/sample_defs/cpu1_platform_cfg.h b/cmake/sample_defs/cpu1_platform_cfg.h index e45d64055..431c9960f 100644 --- a/cmake/sample_defs/cpu1_platform_cfg.h +++ b/cmake/sample_defs/cpu1_platform_cfg.h @@ -901,7 +901,7 @@ ** The length of each string, including the NULL terminator cannot exceed the ** #OS_MAX_PATH_LEN value. */ -#define CFE_PLATFORM_ES_DEFAULT_TASK_LOG_FILE "/ram/cfe_es_task_info.log" +#define CFE_PLATFORM_ES_DEFAULT_TASK_LOG_FILE "/ram/cfe_es_taskinfo.log" /** ** \cfeescfg Default System Log Filename diff --git a/cmake/sample_defs/sample_mission_cfg.h b/cmake/sample_defs/sample_mission_cfg.h index 09a12ea17..c6a241853 100644 --- a/cmake/sample_defs/sample_mission_cfg.h +++ b/cmake/sample_defs/sample_mission_cfg.h @@ -47,7 +47,6 @@ ** messages sent. If the pkt length field indicates the message is larger ** than this define, SB sends an event and rejects the send. ** -** ** \par Limits ** This parameter has a lower limit of 6 (CCSDS primary header size). There ** are no restrictions on the upper limit however, the maximum message size is @@ -247,6 +246,7 @@ ** portion of a Full CDS Name of the following form: ** "ApplicationName.CDSName" ** +** This length does not need to include an extra character for NULL termination. ** ** \par Limits ** This value should be kept as a multiple of 4, to maintain alignment of @@ -264,6 +264,8 @@ ** Indicates the maximum length (in characters) of the formatted text ** string portion of an event message ** +** This length does not need to include an extra character for NULL termination. +** ** \par Limits ** Not Applicable */ @@ -299,6 +301,8 @@ ** ('TblName') portion of a Full Table Name of the following ** form: "ApplicationName.TblName" ** +** This length does not need to include an extra character for NULL termination. +** ** \par Limits ** This value should be kept as a multiple of 4, to maintain alignment of ** any possible neighboring fields without implicit padding. @@ -531,6 +535,8 @@ ** ** This affects only the layout of command/telemetry messages and table definitions; ** internal allocation may use the platform-specific OS_MAX_PATH_LEN value. +** +** This length must include an extra character for NULL termination. ** ** \par Limits ** All CPUs within the same SB domain (mission) and ground tools must share the @@ -557,6 +563,8 @@ ** This affects only the layout of command/telemetry messages and table definitions; ** internal allocation may use the platform-specific OS_MAX_FILE_LEN value. ** +** This length must include an extra character for NULL termination. +** ** \par Limits ** All CPUs within the same SB domain (mission) and ground tools must share the ** same definition. @@ -582,6 +590,8 @@ ** This affects only the layout of command/telemetry messages and table definitions; ** internal allocation may use the platform-specific OS_MAX_API_LEN value. ** +** This length must include an extra character for NULL termination. +** ** \par Limits ** All CPUs within the same SB domain (mission) must share the same definition ** Note this affects the size of messages, so it must not cause any message diff --git a/fsw/cfe-core/src/es/cfe_es_api.c b/fsw/cfe-core/src/es/cfe_es_api.c index 916264a1d..54304aecb 100644 --- a/fsw/cfe-core/src/es/cfe_es_api.c +++ b/fsw/cfe-core/src/es/cfe_es_api.c @@ -1015,15 +1015,15 @@ int32 CFE_ES_GetAppInfo(CFE_ES_AppInfo_t *AppInfo, CFE_ES_AppId_t AppId) AppInfo->ResourceId = CFE_RESOURCEID_UNWRAP(AppId); /* make into a generic resource ID */ AppInfo->Type = AppRecPtr->Type; + strncpy(AppInfo->Name, CFE_ES_AppRecordGetName(AppRecPtr), sizeof(AppInfo->Name)-1); + CFE_ES_CopyModuleBasicInfo(&AppRecPtr->StartParams.BasicInfo, AppInfo); - CFE_ES_CopyModuleStatusInfo(&AppRecPtr->ModuleInfo, AppInfo); + CFE_ES_CopyModuleStatusInfo(&AppRecPtr->LoadStatus, AppInfo); - AppInfo->StackSize = AppRecPtr->StartParams.StackSize; AppInfo->ExceptionAction = AppRecPtr->StartParams.ExceptionAction; - AppInfo->Priority = AppRecPtr->StartParams.Priority; AppInfo->MainTaskId = AppRecPtr->MainTaskId; - ModuleId = AppRecPtr->ModuleInfo.ModuleId; + ModuleId = AppRecPtr->LoadStatus.ModuleId; /* ** Calculate the number of child tasks @@ -1042,6 +1042,10 @@ int32 CFE_ES_GetAppInfo(CFE_ES_AppInfo_t *AppInfo, CFE_ES_AppId_t AppId) strncpy(AppInfo->MainTaskName, TaskRecPtr->TaskName, sizeof(AppInfo->MainTaskName) - 1); AppInfo->MainTaskName[sizeof(AppInfo->MainTaskName) - 1] = '\0'; + + AppInfo->StackSize = TaskRecPtr->StartParams.StackSize; + AppInfo->Priority = TaskRecPtr->StartParams.Priority; + } else { @@ -1105,10 +1109,12 @@ int32 CFE_ES_GetLibInfo(CFE_ES_AppInfo_t *LibInfo, CFE_ES_LibId_t LibId) LibInfo->ResourceId = CFE_RESOURCEID_UNWRAP(LibId); /* make into generic ID */ LibInfo->Type = CFE_ES_AppType_LIBRARY; - CFE_ES_CopyModuleBasicInfo(&LibRecPtr->BasicInfo, LibInfo); - CFE_ES_CopyModuleStatusInfo(&LibRecPtr->ModuleInfo, LibInfo); + strncpy(LibInfo->Name, CFE_ES_LibRecordGetName(LibRecPtr), sizeof(LibInfo->Name)-1); - ModuleId = LibRecPtr->ModuleInfo.ModuleId; + CFE_ES_CopyModuleBasicInfo(&LibRecPtr->LoadParams, LibInfo); + CFE_ES_CopyModuleStatusInfo(&LibRecPtr->LoadStatus, LibInfo); + + ModuleId = LibRecPtr->LoadStatus.ModuleId; Status = CFE_SUCCESS; } @@ -1243,108 +1249,90 @@ int32 CFE_ES_CreateChildTask(CFE_ES_TaskId_t *TaskIdPtr, CFE_ES_TaskPriority_Atom_t Priority, uint32 Flags) { + int32 ReturnCode; + CFE_ES_AppRecord_t * AppRecPtr; + CFE_ES_AppId_t ParentAppId; + CFE_ES_TaskId_t SelfTaskId; + CFE_ES_TaskStartParams_t Params; - int32 Result; - CFE_ES_AppRecord_t *AppRecPtr; - CFE_ES_TaskRecord_t *TaskRecPtr; - int32 ReturnCode; - CFE_ES_TaskId_t SelfTaskId; - CFE_ES_TaskId_t LocalChildTaskId; - osal_id_t OsalId; + ParentAppId = CFE_ES_APPID_UNDEFINED; - /* - ** Validate some of the arguments - */ - if ( TaskIdPtr == NULL ) - { - if (TaskName == NULL) - { - CFE_ES_WriteToSysLog("CFE_ES_CreateChildTask: Task Id and Name Pointer Parameters are NULL.\n"); - ReturnCode = CFE_ES_BAD_ARGUMENT; - } - else - { - CFE_ES_WriteToSysLog("CFE_ES_CreateChildTask: Task Id Pointer Parameter is NULL for Task '%s'.\n",TaskName); - ReturnCode = CFE_ES_BAD_ARGUMENT; - } - } - else if ( TaskName == NULL ) - { - CFE_ES_WriteToSysLog("CFE_ES_CreateChildTask: TaskName Parameter is NULL\n"); - ReturnCode = CFE_ES_BAD_ARGUMENT; - } - else if ( FunctionPtr == NULL ) - { - CFE_ES_WriteToSysLog("CFE_ES_CreateChildTask: Function Pointer Parameter is NULL for Task '%s'\n",TaskName); - ReturnCode = CFE_ES_BAD_ARGUMENT; - } - else - { - - CFE_ES_LockSharedData(__func__,__LINE__); + memset(&Params, 0, sizeof(Params)); + Params.Priority = Priority; + Params.StackSize = StackSize; - /* - ** Get the App Record of the calling Application - */ - AppRecPtr = CFE_ES_GetAppRecordByContext(); - if (AppRecPtr == NULL) - { - CFE_ES_SysLogWrite_Unsync("CFE_ES_CreateChildTask: Invalid calling context when creating Task '%s'\n",TaskName); - ReturnCode = CFE_ES_ERR_RESOURCEID_NOT_VALID; - } - else /* else AppId is valid */ - { - /* - ** First, Make sure the Calling Task is a cFE Main task. - ** TaskID must be the same as the Parent Task ID. - */ - OsalId = OS_TaskGetId(); - SelfTaskId = CFE_ES_TaskId_FromOSAL(OsalId); - if ( CFE_RESOURCEID_TEST_EQUAL(SelfTaskId, AppRecPtr->MainTaskId) ) - { - /* - ** Step 2: Create the new task using the OS API call - */ - Result = OS_TaskCreate(&OsalId, TaskName, FunctionPtr, StackPtr, - StackSize, Priority, OS_FP_ENABLED ); - - /* - ** Step 3: Record the task information in the task table - */ - if ( Result == OS_SUCCESS ) - { - LocalChildTaskId = CFE_ES_TaskId_FromOSAL(OsalId); - TaskRecPtr = CFE_ES_LocateTaskRecordByID(LocalChildTaskId); + /* + ** Validate some of the arguments + */ + if (TaskIdPtr == NULL) + { + if (TaskName == NULL) + { + CFE_ES_WriteToSysLog("CFE_ES_CreateChildTask: Task Id and Name Pointer Parameters are NULL.\n"); + ReturnCode = CFE_ES_BAD_ARGUMENT; + } + else + { + CFE_ES_WriteToSysLog("CFE_ES_CreateChildTask: Task Id Pointer Parameter is NULL for Task '%s'.\n", + TaskName); + ReturnCode = CFE_ES_BAD_ARGUMENT; + } + } + else if (TaskName == NULL) + { + CFE_ES_WriteToSysLog("CFE_ES_CreateChildTask: TaskName Parameter is NULL\n"); + ReturnCode = CFE_ES_BAD_ARGUMENT; + } + else if (FunctionPtr == NULL) + { + CFE_ES_WriteToSysLog("CFE_ES_CreateChildTask: Function Pointer Parameter is NULL for Task '%s'\n", TaskName); + ReturnCode = CFE_ES_BAD_ARGUMENT; + } + else + { + /* + ** First, Make sure the Calling Task is a cFE Main task. + ** TaskID must be the same as the Parent Task ID. + */ + SelfTaskId = CFE_ES_TaskId_FromOSAL(OS_TaskGetId()); - CFE_ES_TaskRecordSetUsed(TaskRecPtr, CFE_RESOURCEID_UNWRAP(LocalChildTaskId)); - TaskRecPtr->AppId = CFE_ES_AppRecordGetID(AppRecPtr); - strncpy(TaskRecPtr->TaskName,TaskName,sizeof(TaskRecPtr->TaskName) - 1); - TaskRecPtr->TaskName[sizeof(TaskRecPtr->TaskName) - 1] = '\0'; - CFE_ES_Global.RegisteredTasks++; + CFE_ES_LockSharedData(__func__, __LINE__); - *TaskIdPtr = CFE_ES_TaskRecordGetID(TaskRecPtr); - ReturnCode = CFE_SUCCESS; - } - else - { - CFE_ES_SysLogWrite_Unsync("CFE_ES_CreateChildTask: Error calling OS_TaskCreate for Task '%s' RC = 0x%08X\n",TaskName,(unsigned int)Result); - ReturnCode = CFE_ES_ERR_CHILD_TASK_CREATE; - } - } - else - { - CFE_ES_SysLogWrite_Unsync("CFE_ES_CreateChildTask: Error: Cannot call from a Child Task (for Task '%s').\n",TaskName); + /* + ** Get the App Record of the calling Application + */ + AppRecPtr = CFE_ES_GetAppRecordByContext(); + if (AppRecPtr == NULL) + { + CFE_ES_SysLogWrite_Unsync("CFE_ES_CreateChildTask: Invalid calling context when creating Task '%s'\n", + TaskName); + ReturnCode = CFE_ES_ERR_RESOURCEID_NOT_VALID; + } + else if (!CFE_RESOURCEID_TEST_EQUAL(SelfTaskId, AppRecPtr->MainTaskId)) + { + CFE_ES_SysLogWrite_Unsync("CFE_ES_CreateChildTask: Error: Cannot call from a Child Task (for Task '%s').\n", + TaskName); ReturnCode = CFE_ES_ERR_CHILD_TASK_CREATE; + } + else + { + ParentAppId = CFE_ES_AppRecordGetID(AppRecPtr); + ReturnCode = CFE_SUCCESS; + } /* end If AppID is valid */ - } /* end if Calling task is a main task */ - - }/* end If AppID is valid */ + CFE_ES_UnlockSharedData(__func__, __LINE__); - CFE_ES_UnlockSharedData(__func__,__LINE__); + } /* end if parameter checking */ - } /* end if parameter checking */ + /* + ** Step 2: Create the new task if the parameter validation succeeded + */ + if (ReturnCode == CFE_SUCCESS) + { + ReturnCode = CFE_ES_StartAppTask(TaskIdPtr, TaskName, FunctionPtr, &Params, ParentAppId); + } - return(ReturnCode); + return (ReturnCode); } /* End of CFE_ES_CreateChildTask() */ diff --git a/fsw/cfe-core/src/es/cfe_es_apps.c b/fsw/cfe-core/src/es/cfe_es_apps.c index 8e825dfb3..fddb1203b 100644 --- a/fsw/cfe-core/src/es/cfe_es_apps.c +++ b/fsw/cfe-core/src/es/cfe_es_apps.c @@ -263,104 +263,98 @@ void CFE_ES_StartApplications(uint32 ResetType, const char *StartFilePath ) */ int32 CFE_ES_ParseFileEntry(const char **TokenList, uint32 NumTokens) { - const char *FileName; - const char *AppName; - const char *EntryPoint; - const char *EntryType; - unsigned long PriorityIn; - unsigned long StackSizeIn; - unsigned long ExceptionActionIn; - union - { + const char * ModuleName; + const char * EntryType; + unsigned long ParsedValue; + union + { CFE_ES_AppId_t AppId; CFE_ES_LibId_t LibId; - } IdBuf; - int32 CreateStatus = CFE_ES_ERR_APP_CREATE; + } IdBuf; + int32 CreateStatus = CFE_ES_ERR_APP_CREATE; + CFE_ES_AppStartParams_t ParamBuf; - /* - ** Check to see if the correct number of items were parsed - */ - if ( NumTokens < 8 ) - { - CFE_ES_WriteToSysLog("ES Startup: Invalid ES Startup file entry: %u\n",(unsigned int)NumTokens); - return (CreateStatus); - } + /* + ** Check to see if the correct number of items were parsed + */ + if (NumTokens < 8) + { + CFE_ES_WriteToSysLog("ES Startup: Invalid ES Startup file entry: %u\n", (unsigned int)NumTokens); + return (CreateStatus); + } - EntryType = TokenList[0]; - FileName = TokenList[1]; - EntryPoint = TokenList[2]; - AppName = TokenList[3]; + /* Get pointers to specific tokens that are simple strings used as-is */ + EntryType = TokenList[0]; + ModuleName = TokenList[3]; - /* - * NOTE: In previous CFE versions the sscanf() function was used to convert - * these string values into integers. This approach of using the pre-tokenized strings - * and strtoul() is safer but the side effect is that it will also be more "permissive" in - * what is accepted vs. rejected by this function. - * - * For instance if the startup script contains "123xyz", this will be converted to the value - * 123 instead of triggering a validation failure as it would have in CFE <= 6.5.0. - * - * This permissive parsing should not be relied upon, as it may become more strict again in - * future CFE revisions. - * - * Also note that this uses "unsigned long" as that is the defined output type of strtoul(). - * It will be converted to the correct type later. - */ - PriorityIn = strtoul(TokenList[4], NULL, 0); - StackSizeIn = strtoul(TokenList[5], NULL, 0); - ExceptionActionIn = strtoul(TokenList[7], NULL, 0); + /* + * Other tokens will need to be scrubbed/converted. + * Both Libraries and Apps use File Name (1) and Symbol Name (2) fields so copy those now + */ + memset(&ParamBuf, 0, sizeof(ParamBuf)); + strncpy(ParamBuf.BasicInfo.FileName, TokenList[1], sizeof(ParamBuf.BasicInfo.FileName) - 1); + strncpy(ParamBuf.BasicInfo.InitSymbolName, TokenList[2], sizeof(ParamBuf.BasicInfo.InitSymbolName) - 1); - if(strcmp(EntryType,"CFE_APP")==0) - { - CFE_ES_WriteToSysLog("ES Startup: Loading file: %s, APP: %s\n", - FileName, AppName); + if (strcmp(EntryType, "CFE_APP") == 0) + { + CFE_ES_WriteToSysLog("ES Startup: Loading file: %s, APP: %s\n", ParamBuf.BasicInfo.FileName, ModuleName); - /* - ** Validate Some parameters - ** Exception action should be 0 ( Restart App ) or - ** 1 ( Processor reset ). If it's non-zero, assume it means - ** reset CPU. - */ - if ( ExceptionActionIn > CFE_ES_ExceptionAction_RESTART_APP ) - { - ExceptionActionIn = CFE_ES_ExceptionAction_PROC_RESTART; - } + /* + * Priority and Exception action have limited ranges, which is checked here + * Task priority cannot be bigger than OS_MAX_TASK_PRIORITY + */ + ParsedValue = strtoul(TokenList[4], NULL, 0); + if (ParsedValue > OS_MAX_TASK_PRIORITY) + { + ParamBuf.MainTaskInfo.Priority = OS_MAX_TASK_PRIORITY; + } + else + { + /* convert parsed value to correct type */ + ParamBuf.MainTaskInfo.Priority = (CFE_ES_TaskPriority_Atom_t)ParsedValue; + } - /* - * Task priority cannot be bigger than OS_MAX_TASK_PRIORITY - */ - if ( PriorityIn > OS_MAX_TASK_PRIORITY ) - { - PriorityIn = OS_MAX_TASK_PRIORITY; - } + /* No specific upper/lower limit for stack size - will pass value through */ + ParamBuf.MainTaskInfo.StackSize = strtoul(TokenList[5], NULL, 0); - /* - ** Now create the application - */ - CreateStatus = CFE_ES_AppCreate(&IdBuf.AppId, FileName, - EntryPoint, AppName, - PriorityIn, - StackSizeIn, - ExceptionActionIn); - } - else if(strcmp(EntryType,"CFE_LIB")==0) - { - CFE_ES_WriteToSysLog("ES Startup: Loading shared library: %s\n",FileName); - /* - ** Now load the library - */ - CreateStatus = CFE_ES_LoadLibrary(&IdBuf.LibId, FileName, - EntryPoint, AppName); + /* + ** Validate Some parameters + ** Exception action should be 0 ( Restart App ) or + ** 1 ( Processor reset ). If it's non-zero, assume it means + ** reset CPU. + */ + ParsedValue = strtoul(TokenList[7], NULL, 0); + if (ParsedValue > CFE_ES_ExceptionAction_RESTART_APP) + { + ParamBuf.ExceptionAction = CFE_ES_ExceptionAction_PROC_RESTART; + } + else + { + /* convert parsed value to correct type */ + ParamBuf.ExceptionAction = (CFE_ES_ExceptionAction_Enum_t)ParsedValue; + } - } - else - { - CFE_ES_WriteToSysLog("ES Startup: Unexpected EntryType %s in startup file.\n",EntryType); - } + /* + ** Now create the application + */ + CreateStatus = CFE_ES_AppCreate(&IdBuf.AppId, ModuleName, &ParamBuf); + } + else if (strcmp(EntryType, "CFE_LIB") == 0) + { + CFE_ES_WriteToSysLog("ES Startup: Loading shared library: %s\n", ParamBuf.BasicInfo.FileName); - return (CreateStatus); + /* + ** Now load the library + */ + CreateStatus = CFE_ES_LoadLibrary(&IdBuf.LibId, ModuleName, &ParamBuf.BasicInfo); + } + else + { + CFE_ES_WriteToSysLog("ES Startup: Unexpected EntryType %s in startup file.\n", EntryType); + } + return (CreateStatus); } /* @@ -373,21 +367,21 @@ int32 CFE_ES_ParseFileEntry(const char **TokenList, uint32 NumTokens) ** **------------------------------------------------------------------------------------- */ -int32 CFE_ES_LoadModule(CFE_ResourceId_t ResourceId, const CFE_ES_ModuleLoadParams_t* LoadParams, CFE_ES_ModuleLoadStatus_t *LoadStatus) +int32 CFE_ES_LoadModule(CFE_ResourceId_t ParentResourceId, const char *ModuleName, const CFE_ES_ModuleLoadParams_t* LoadParams, CFE_ES_ModuleLoadStatus_t *LoadStatus) { osal_id_t ModuleId; - cpuaddr StartAddr; + cpuaddr InitSymbolAddress; int32 ReturnCode; int32 StatusCode; uint32 LoadFlags; LoadFlags = 0; - StartAddr = 0; + InitSymbolAddress = 0; ReturnCode = CFE_SUCCESS; if (LoadParams->FileName[0] != 0) { - switch(CFE_ResourceId_GetBase(ResourceId)) + switch(CFE_ResourceId_GetBase(ParentResourceId)) { case CFE_ES_APPID_BASE: /* @@ -417,7 +411,7 @@ int32 CFE_ES_LoadModule(CFE_ResourceId_t ResourceId, const CFE_ES_ModuleLoadPara * Load the module via OSAL. */ StatusCode = OS_ModuleLoad ( &ModuleId, - LoadParams->Name, + ModuleName, LoadParams->FileName, LoadFlags ); @@ -437,13 +431,13 @@ int32 CFE_ES_LoadModule(CFE_ResourceId_t ResourceId, const CFE_ES_ModuleLoadPara /* * If the Load was OK, then lookup the address of the entry point */ - if (ReturnCode == CFE_SUCCESS && LoadParams->EntryPoint[0] != 0) + if (ReturnCode == CFE_SUCCESS && LoadParams->InitSymbolName[0] != 0) { - StatusCode = OS_ModuleSymbolLookup(ModuleId, &StartAddr, LoadParams->EntryPoint); + StatusCode = OS_ModuleSymbolLookup(ModuleId, &InitSymbolAddress, LoadParams->InitSymbolName); if (StatusCode != OS_SUCCESS) { CFE_ES_WriteToSysLog("ES Startup: Could not find symbol:%s. EC = 0x%08X\n", - LoadParams->EntryPoint, (unsigned int)StatusCode); + LoadParams->InitSymbolName, (unsigned int)StatusCode); ReturnCode = CFE_STATUS_EXTERNAL_RESOURCE_FAIL; } } @@ -452,7 +446,7 @@ int32 CFE_ES_LoadModule(CFE_ResourceId_t ResourceId, const CFE_ES_ModuleLoadPara { /* store the data in the app record after successful load+lookup */ LoadStatus->ModuleId = ModuleId; - LoadStatus->EntryAddress = StartAddr; + LoadStatus->InitSymbolAddress = InitSymbolAddress; } else if (OS_ObjectIdDefined(ModuleId)) { @@ -462,7 +456,7 @@ int32 CFE_ES_LoadModule(CFE_ResourceId_t ResourceId, const CFE_ES_ModuleLoadPara if ( StatusCode != OS_SUCCESS ) /* There's not much we can do except notify */ { CFE_ES_WriteToSysLog("ES Startup: Failed to unload: %s. EC = 0x%08X\n", - LoadParams->Name, (unsigned int)StatusCode); + ModuleName, (unsigned int)StatusCode); } } @@ -471,7 +465,7 @@ int32 CFE_ES_LoadModule(CFE_ResourceId_t ResourceId, const CFE_ES_ModuleLoadPara /* **------------------------------------------------------------------------------------- -** Name: CFE_ES_GetAppEntryPoint +** Name: CFE_ES_GetTaskFunction ** ** Helper function to act as the intermediate entry point of an app ** This is to support starting apps before having a fully completed entry in the @@ -480,9 +474,11 @@ int32 CFE_ES_LoadModule(CFE_ResourceId_t ResourceId, const CFE_ES_ModuleLoadPara ** **------------------------------------------------------------------------------------- */ -int32 CFE_ES_GetAppEntryPoint(osal_task_entry *FuncPtr) +int32 CFE_ES_GetTaskFunction(CFE_ES_TaskEntryFuncPtr_t *FuncPtr) { - CFE_ES_AppRecord_t *AppRecPtr; + CFE_ES_TaskRecord_t *TaskRecPtr; + CFE_ES_AppId_t AppId; + CFE_ES_TaskEntryFuncPtr_t EntryFunc; int32 ReturnCode; int32 Timeout; @@ -491,18 +487,23 @@ int32 CFE_ES_GetAppEntryPoint(osal_task_entry *FuncPtr) */ ReturnCode = CFE_ES_ERR_APP_REGISTER; Timeout = CFE_PLATFORM_ES_STARTUP_SCRIPT_TIMEOUT_MSEC; + AppId = CFE_ES_APPID_UNDEFINED; + EntryFunc = NULL; while(true) { OS_TaskDelay(CFE_PLATFORM_ES_STARTUP_SYNC_POLL_MSEC); CFE_ES_LockSharedData(__func__,__LINE__); - AppRecPtr = CFE_ES_GetAppRecordByContext(); - if (AppRecPtr != NULL) + TaskRecPtr = CFE_ES_GetTaskRecordByContext(); + if (TaskRecPtr != NULL) { - AppRecPtr->AppState = CFE_ES_AppState_EARLY_INIT; - *FuncPtr = (osal_task_entry)AppRecPtr->ModuleInfo.EntryAddress; - ReturnCode = CFE_SUCCESS; + AppId = TaskRecPtr->AppId; + EntryFunc = TaskRecPtr->EntryFunc; + if (CFE_RESOURCEID_TEST_DEFINED(AppId) && EntryFunc != 0) + { + ReturnCode = CFE_SUCCESS; + } } CFE_ES_UnlockSharedData(__func__,__LINE__); @@ -515,12 +516,18 @@ int32 CFE_ES_GetAppEntryPoint(osal_task_entry *FuncPtr) Timeout -= CFE_PLATFORM_ES_STARTUP_SYNC_POLL_MSEC; } + /* output function address to caller */ + if (FuncPtr != NULL) + { + *FuncPtr = EntryFunc; + } + return (ReturnCode); } /* **------------------------------------------------------------------------------------- -** Name: CFE_ES_AppEntryPoint +** Name: CFE_ES_TaskEntryPoint ** ** Helper function to act as the intermediate entry point of an app ** This is to support starting apps before having a fully completed entry in the @@ -529,11 +536,11 @@ int32 CFE_ES_GetAppEntryPoint(osal_task_entry *FuncPtr) ** **------------------------------------------------------------------------------------- */ -void CFE_ES_AppEntryPoint(void) +void CFE_ES_TaskEntryPoint(void) { - osal_task_entry RealEntryFunc; + CFE_ES_TaskEntryFuncPtr_t RealEntryFunc; - if (CFE_ES_GetAppEntryPoint(&RealEntryFunc) == CFE_SUCCESS && + if (CFE_ES_GetTaskFunction(&RealEntryFunc) == CFE_SUCCESS && RealEntryFunc != NULL) { (*RealEntryFunc)(); @@ -556,7 +563,7 @@ void CFE_ES_AppEntryPoint(void) ** **------------------------------------------------------------------------------------- */ -int32 CFE_ES_StartAppTask(const CFE_ES_AppStartParams_t* StartParams, CFE_ES_AppId_t RefAppId, CFE_ES_TaskId_t *TaskIdPtr) +int32 CFE_ES_StartAppTask(CFE_ES_TaskId_t *TaskIdPtr, const char *TaskName, CFE_ES_TaskEntryFuncPtr_t EntryFunc, const CFE_ES_TaskStartParams_t* Params, CFE_ES_AppId_t ParentAppId) { CFE_ES_TaskRecord_t *TaskRecPtr; osal_id_t OsalTaskId; @@ -565,15 +572,15 @@ int32 CFE_ES_StartAppTask(const CFE_ES_AppStartParams_t* StartParams, CFE_ES_App int32 ReturnCode; /* - ** Create the primary task for the newly loaded task + * Create the primary task for the newly loaded task */ - StatusCode = OS_TaskCreate(&OsalTaskId, /* task id */ - StartParams->BasicInfo.Name, /* task name */ - CFE_ES_AppEntryPoint, /* task function pointer */ - OSAL_TASK_STACK_ALLOCATE, /* stack pointer (allocate) */ - StartParams->StackSize, /* stack size */ - StartParams->Priority, /* task priority */ - OS_FP_ENABLED); /* task options */ + StatusCode = OS_TaskCreate(&OsalTaskId, /* task id */ + TaskName, /* task name matches app name for main task */ + CFE_ES_TaskEntryPoint, /* task function pointer */ + OSAL_TASK_STACK_ALLOCATE, /* stack pointer (allocate) */ + Params->StackSize, /* stack size */ + Params->Priority, /* task priority */ + OS_FP_ENABLED); /* task options */ CFE_ES_LockSharedData(__func__,__LINE__); @@ -598,13 +605,17 @@ int32 CFE_ES_StartAppTask(const CFE_ES_AppStartParams_t* StartParams, CFE_ES_App */ memset(TaskRecPtr, 0, sizeof(*TaskRecPtr)); - TaskRecPtr->AppId = RefAppId; - strncpy(TaskRecPtr->TaskName, StartParams->BasicInfo.Name, sizeof(TaskRecPtr->TaskName)-1); + TaskRecPtr->AppId = ParentAppId; + TaskRecPtr->EntryFunc = EntryFunc; + TaskRecPtr->StartParams = *Params; + + strncpy(TaskRecPtr->TaskName, TaskName, sizeof(TaskRecPtr->TaskName)-1); TaskRecPtr->TaskName[sizeof(TaskRecPtr->TaskName)-1] = 0; + CFE_ES_TaskRecordSetUsed(TaskRecPtr, CFE_RESOURCEID_UNWRAP(LocalTaskId)); /* - ** Increment the registered Task count. + * Increment the registered Task count. */ CFE_ES_Global.RegisteredTasks++; ReturnCode = CFE_SUCCESS; @@ -613,7 +624,7 @@ int32 CFE_ES_StartAppTask(const CFE_ES_AppStartParams_t* StartParams, CFE_ES_App else { CFE_ES_SysLogWrite_Unsync("ES Startup: AppCreate Error: TaskCreate %s Failed. EC = 0x%08X!\n", - StartParams->BasicInfo.Name,(unsigned int)StatusCode); + TaskName,(unsigned int)StatusCode); ReturnCode = CFE_STATUS_EXTERNAL_RESOURCE_FAIL; *TaskIdPtr = CFE_ES_TASKID_UNDEFINED; } @@ -634,28 +645,22 @@ int32 CFE_ES_StartAppTask(const CFE_ES_AppStartParams_t* StartParams, CFE_ES_App ** **--------------------------------------------------------------------------------------- */ -int32 CFE_ES_AppCreate(CFE_ES_AppId_t *ApplicationIdPtr, - const char *FileName, - const char *EntryPointName, - const char *AppName, - CFE_ES_TaskPriority_Atom_t Priority, - size_t StackSize, - CFE_ES_ExceptionAction_Enum_t ExceptionAction) +int32 CFE_ES_AppCreate(CFE_ES_AppId_t *ApplicationIdPtr, const char *AppName, const CFE_ES_AppStartParams_t *Params) { CFE_Status_t Status; - CFE_ES_TaskId_t MainTaskId; CFE_ES_AppRecord_t *AppRecPtr; CFE_ResourceId_t PendingResourceId; /* - * The FileName must not be NULL + * The AppName must not be NULL */ - if (FileName == NULL || AppName == NULL) + if (AppName == NULL || Params == NULL) { return CFE_ES_BAD_ARGUMENT; } - if (strlen(AppName) >= OS_MAX_API_NAME) + /* Confirm name will fit inside the record */ + if (memchr(AppName,0,sizeof(AppRecPtr->AppName)) == NULL) { return CFE_ES_BAD_ARGUMENT; } @@ -705,27 +710,19 @@ int32 CFE_ES_AppCreate(CFE_ES_AppId_t *ApplicationIdPtr, /* Fully clear the entry, just in case of stale data */ memset(AppRecPtr, 0, sizeof(*AppRecPtr)); + /* Store the app name from passed-in value */ + strncpy(AppRecPtr->AppName, AppName, sizeof(AppRecPtr->AppName)-1); + + AppRecPtr->Type = CFE_ES_AppType_EXTERNAL; + /* * Fill out the parameters in the StartParams sub-structure + * + * This contains all relevant info, including file name, entry point, + * main task info, etc. which is required to start the app now + * or in a future restart/reload request. */ - AppRecPtr->Type = CFE_ES_AppType_EXTERNAL; - strncpy(AppRecPtr->StartParams.BasicInfo.Name, AppName, - sizeof(AppRecPtr->StartParams.BasicInfo.Name)-1); - AppRecPtr->StartParams.BasicInfo.Name[sizeof(AppRecPtr->StartParams.BasicInfo.Name)-1] = '\0'; - strncpy(AppRecPtr->StartParams.BasicInfo.FileName, FileName, - sizeof(AppRecPtr->StartParams.BasicInfo.FileName)-1); - AppRecPtr->StartParams.BasicInfo.FileName[sizeof(AppRecPtr->StartParams.BasicInfo.FileName)-1] = '\0'; - if (EntryPointName != NULL && strcmp(EntryPointName, "NULL") != 0) - { - strncpy(AppRecPtr->StartParams.BasicInfo.EntryPoint, EntryPointName, - sizeof(AppRecPtr->StartParams.BasicInfo.EntryPoint)-1); - AppRecPtr->StartParams.BasicInfo.EntryPoint[ - sizeof(AppRecPtr->StartParams.BasicInfo.EntryPoint)-1] = '\0'; - } - - AppRecPtr->StartParams.StackSize = StackSize; - AppRecPtr->StartParams.ExceptionAction = ExceptionAction; - AppRecPtr->StartParams.Priority = Priority; + AppRecPtr->StartParams = *Params; /* * Fill out the Task State info @@ -753,18 +750,19 @@ int32 CFE_ES_AppCreate(CFE_ES_AppId_t *ApplicationIdPtr, /* * Load the module based on StartParams configured above. */ - Status = CFE_ES_LoadModule(PendingResourceId, &AppRecPtr->StartParams.BasicInfo, &AppRecPtr->ModuleInfo); + Status = CFE_ES_LoadModule(PendingResourceId, AppName, &AppRecPtr->StartParams.BasicInfo, &AppRecPtr->LoadStatus); /* * If the Load was OK, then complete the initialization */ if (Status == CFE_SUCCESS) { - Status = CFE_ES_StartAppTask(&AppRecPtr->StartParams, CFE_ES_APPID_C(PendingResourceId), &MainTaskId); - } - else - { - MainTaskId = CFE_ES_TASKID_UNDEFINED; + Status = CFE_ES_StartAppTask(&AppRecPtr->MainTaskId, /* Task ID (output) stored in App Record as main task */ + AppName, /* Main Task name matches app name */ + (CFE_ES_TaskEntryFuncPtr_t)AppRecPtr->LoadStatus + .InitSymbolAddress, /* Init Symbol is main task entry point */ + &AppRecPtr->StartParams.MainTaskInfo, /* Main task parameters */ + CFE_ES_APPID_C(PendingResourceId)); /* Parent App ID */ } /* @@ -779,7 +777,6 @@ int32 CFE_ES_AppCreate(CFE_ES_AppId_t *ApplicationIdPtr, * important - set the ID to its proper value * which turns this into a real/valid table entry */ - AppRecPtr->MainTaskId = MainTaskId; CFE_ES_AppRecordSetUsed(AppRecPtr, PendingResourceId); /* @@ -812,10 +809,7 @@ int32 CFE_ES_AppCreate(CFE_ES_AppId_t *ApplicationIdPtr, ** **--------------------------------------------------------------------------------------- */ -int32 CFE_ES_LoadLibrary(CFE_ES_LibId_t *LibraryIdPtr, - const char *FileName, - const char *EntryPointName, - const char *LibName) +int32 CFE_ES_LoadLibrary(CFE_ES_LibId_t *LibraryIdPtr, const char *LibName, const CFE_ES_ModuleLoadParams_t *Params) { CFE_ES_LibraryEntryFuncPtr_t FunctionPointer; CFE_ES_LibRecord_t * LibSlotPtr; @@ -823,14 +817,15 @@ int32 CFE_ES_LoadLibrary(CFE_ES_LibId_t *LibraryIdPtr, CFE_ResourceId_t PendingResourceId; /* - * The FileName must not be NULL + * The LibName must not be NULL */ - if (FileName == NULL || LibName == NULL) + if (LibName == NULL || Params == NULL) { return CFE_ES_BAD_ARGUMENT; } - if (strlen(LibName) >= OS_MAX_API_NAME) + /* Confirm name will fit inside the record */ + if (memchr(LibName,0,sizeof(LibSlotPtr->LibName)) == NULL) { return CFE_ES_BAD_ARGUMENT; } @@ -889,18 +884,10 @@ int32 CFE_ES_LoadLibrary(CFE_ES_LibId_t *LibraryIdPtr, /* * Fill out the parameters in the AppStartParams sub-structure */ - strncpy(LibSlotPtr->BasicInfo.Name, LibName, - sizeof(LibSlotPtr->BasicInfo.Name)-1); - LibSlotPtr->BasicInfo.Name[sizeof(LibSlotPtr->BasicInfo.Name)-1] = '\0'; - strncpy(LibSlotPtr->BasicInfo.FileName, FileName, - sizeof(LibSlotPtr->BasicInfo.FileName)-1); - LibSlotPtr->BasicInfo.FileName[sizeof(LibSlotPtr->BasicInfo.FileName)-1] = '\0'; - if (EntryPointName != NULL && strcmp(EntryPointName, "NULL") != 0) - { - strncpy(LibSlotPtr->BasicInfo.EntryPoint, EntryPointName, - sizeof(LibSlotPtr->BasicInfo.EntryPoint)-1); - LibSlotPtr->BasicInfo.EntryPoint[sizeof(LibSlotPtr->BasicInfo.EntryPoint)-1] = '\0'; - } + strncpy(LibSlotPtr->LibName, LibName, + sizeof(LibSlotPtr->LibName)-1); + LibSlotPtr->LibName[sizeof(LibSlotPtr->LibName)-1] = '\0'; + LibSlotPtr->LoadParams = *Params; CFE_ES_LibRecordSetUsed(LibSlotPtr, CFE_RESOURCEID_RESERVED); CFE_ES_Global.LastLibId = PendingResourceId; @@ -923,10 +910,10 @@ int32 CFE_ES_LoadLibrary(CFE_ES_LibId_t *LibraryIdPtr, /* * Load the module based on StartParams configured above. */ - Status = CFE_ES_LoadModule(PendingResourceId, &LibSlotPtr->BasicInfo, &LibSlotPtr->ModuleInfo); + Status = CFE_ES_LoadModule(PendingResourceId, LibName, &LibSlotPtr->LoadParams, &LibSlotPtr->LoadStatus); if (Status == CFE_SUCCESS) { - FunctionPointer = (CFE_ES_LibraryEntryFuncPtr_t)LibSlotPtr->ModuleInfo.EntryAddress; + FunctionPointer = (CFE_ES_LibraryEntryFuncPtr_t)LibSlotPtr->LoadStatus.InitSymbolAddress; if (FunctionPointer != NULL) { Status = (*FunctionPointer)(CFE_ES_LIBID_C(PendingResourceId)); @@ -1112,7 +1099,8 @@ void CFE_ES_ProcessControlRequest(CFE_ES_AppId_t AppId) { CFE_ES_AppRecord_t *AppRecPtr; uint32 PendingControlReq; - CFE_ES_AppStartParams_t OrigStartParams; + CFE_ES_AppStartParams_t RestartParams; + char OrigAppName[OS_MAX_API_NAME]; CFE_Status_t CleanupStatus; CFE_Status_t StartupStatus; CFE_ES_AppId_t NewAppId; @@ -1129,8 +1117,11 @@ void CFE_ES_ProcessControlRequest(CFE_ES_AppId_t AppId) StartupStatus = CFE_SUCCESS; PendingControlReq = 0; NewAppId = CFE_ES_APPID_UNDEFINED; + OrigAppName[0] = 0; + memset(&RestartParams, 0, sizeof(RestartParams)); + AppRecPtr = CFE_ES_LocateAppRecordByID(AppId); - memset(&OrigStartParams, 0, sizeof(OrigStartParams)); + /* @@ -1144,7 +1135,15 @@ void CFE_ES_ProcessControlRequest(CFE_ES_AppId_t AppId) if (CFE_ES_AppRecordIsMatch(AppRecPtr, AppId)) { PendingControlReq = AppRecPtr->ControlReq.AppControlRequest; - OrigStartParams = AppRecPtr->StartParams; + strncpy(OrigAppName, AppRecPtr->AppName, sizeof(OrigAppName)-1); + OrigAppName[sizeof(OrigAppName)-1] = 0; + + /* If a restart was requested, copy the parameters to re-use in new app */ + if ( PendingControlReq == CFE_ES_RunStatus_SYS_RESTART || + PendingControlReq == CFE_ES_RunStatus_SYS_RELOAD ) + { + RestartParams = AppRecPtr->StartParams; + } } CFE_ES_UnlockSharedData(__func__,__LINE__); @@ -1174,13 +1173,7 @@ void CFE_ES_ProcessControlRequest(CFE_ES_AppId_t AppId) if ( PendingControlReq == CFE_ES_RunStatus_SYS_RESTART || PendingControlReq == CFE_ES_RunStatus_SYS_RELOAD ) { - StartupStatus = CFE_ES_AppCreate(&NewAppId, - OrigStartParams.BasicInfo.FileName, - OrigStartParams.BasicInfo.EntryPoint, - OrigStartParams.BasicInfo.Name, - OrigStartParams.Priority, - OrigStartParams.StackSize, - OrigStartParams.ExceptionAction); + StartupStatus = CFE_ES_AppCreate(&NewAppId, OrigAppName, &RestartParams); } /* @@ -1328,7 +1321,7 @@ void CFE_ES_ProcessControlRequest(CFE_ES_AppId_t AppId) } CFE_EVS_SendEvent(EventID, EventType, "%s Application %s %s", - ReqName, OrigStartParams.BasicInfo.Name, MessageDetail); + ReqName, OrigAppName, MessageDetail); } } /* End Function */ @@ -1379,7 +1372,7 @@ int32 CFE_ES_CleanUpApp(CFE_ES_AppId_t AppId) * * (this will be OS_OBJECT_ID_UNDEFINED if it was not loaded dynamically) */ - ModuleId = AppRecPtr->ModuleInfo.ModuleId; + ModuleId = AppRecPtr->LoadStatus.ModuleId; } /* @@ -1765,11 +1758,7 @@ int32 CFE_ES_CleanupTaskResources(CFE_ES_TaskId_t TaskId) */ void CFE_ES_CopyModuleBasicInfo(const CFE_ES_ModuleLoadParams_t *ParamsPtr, CFE_ES_AppInfo_t *AppInfoPtr) { - strncpy(AppInfoPtr->Name, ParamsPtr->Name, - sizeof(AppInfoPtr->Name)-1); - AppInfoPtr->Name[sizeof(AppInfoPtr->Name)-1] = '\0'; - - strncpy(AppInfoPtr->EntryPoint, ParamsPtr->EntryPoint, + strncpy(AppInfoPtr->EntryPoint, ParamsPtr->InitSymbolName, sizeof(AppInfoPtr->EntryPoint) - 1); AppInfoPtr->EntryPoint[sizeof(AppInfoPtr->EntryPoint) - 1] = '\0'; @@ -1790,7 +1779,7 @@ void CFE_ES_CopyModuleBasicInfo(const CFE_ES_ModuleLoadParams_t *ParamsPtr, CFE_ */ void CFE_ES_CopyModuleStatusInfo(const CFE_ES_ModuleLoadStatus_t *StatusPtr, CFE_ES_AppInfo_t *AppInfoPtr) { - AppInfoPtr->StartAddress = CFE_ES_MEMADDRESS_C(StatusPtr->EntryAddress); + AppInfoPtr->StartAddress = CFE_ES_MEMADDRESS_C(StatusPtr->InitSymbolAddress); } /* diff --git a/fsw/cfe-core/src/es/cfe_es_apps.h b/fsw/cfe-core/src/es/cfe_es_apps.h index abe038378..2e6bf558d 100644 --- a/fsw/cfe-core/src/es/cfe_es_apps.h +++ b/fsw/cfe-core/src/es/cfe_es_apps.h @@ -77,8 +77,7 @@ typedef struct */ typedef struct { - char Name[OS_MAX_API_NAME]; - char EntryPoint[OS_MAX_API_NAME]; + char InitSymbolName[OS_MAX_API_NAME]; char FileName[OS_MAX_PATH_LEN]; } CFE_ES_ModuleLoadParams_t; @@ -92,19 +91,31 @@ typedef struct */ typedef struct { - cpuaddr EntryAddress; osal_id_t ModuleId; + cpuaddr InitSymbolAddress; } CFE_ES_ModuleLoadStatus_t; +/* +** CFE_ES_TaskStartParams_t contains basic details about a CFE task +** +** This information needs to be specified when starting a task and is +** stored as part of the task record for future reference. +*/ +typedef struct +{ + size_t StackSize; + CFE_ES_TaskPriority_Atom_t Priority; + +} CFE_ES_TaskStartParams_t; + /* -** CFE_ES_AppStartParams_t is a structure of information used when an application is -** created in the system. +** CFE_ES_AppStartParams_t contains basic details about a CFE app. ** ** This is an extension of the CFE_ES_ModuleLoadParams_t which adds information -** about the task stack size and priority. It is only used for apps, as libraries +** about the main task and exception action. It is only used for apps, as libraries ** do not have a task associated. */ typedef struct @@ -112,30 +123,28 @@ typedef struct /* * Basic (static) information about the module */ - CFE_ES_ModuleLoadParams_t BasicInfo; + CFE_ES_ModuleLoadParams_t BasicInfo; - /* - * Extra information the pertains to applications only, not libraries. - */ - size_t StackSize; - CFE_ES_TaskPriority_Atom_t Priority; - CFE_ES_ExceptionAction_Enum_t ExceptionAction; + CFE_ES_TaskStartParams_t MainTaskInfo; + CFE_ES_ExceptionAction_Enum_t ExceptionAction; } CFE_ES_AppStartParams_t; + /* ** CFE_ES_AppRecord_t is an internal structure used to keep track of ** CFE Applications that are active in the system. */ typedef struct { - CFE_ES_AppId_t AppId; /* The actual AppID of this entry, or undefined */ - CFE_ES_AppState_Enum_t AppState; /* Is the app running, or stopped, or waiting? */ - CFE_ES_AppType_Enum_t Type; /* The type of App: CORE or EXTERNAL */ - CFE_ES_AppStartParams_t StartParams; /* The start parameters for an App */ - CFE_ES_ModuleLoadStatus_t ModuleInfo; /* Runtime module information */ - CFE_ES_ControlReq_t ControlReq; /* The Control Request Record for External cFE Apps */ - CFE_ES_TaskId_t MainTaskId; /* The Application's Main Task ID */ + CFE_ES_AppId_t AppId; /* The actual AppID of this entry, or undefined */ + char AppName[OS_MAX_API_NAME]; /* The name of the app */ + CFE_ES_AppState_Enum_t AppState; /* Is the app running, or stopped, or waiting? */ + CFE_ES_AppType_Enum_t Type; /* The type of App: CORE or EXTERNAL */ + CFE_ES_AppStartParams_t StartParams; /* The start parameters for an App */ + CFE_ES_ModuleLoadStatus_t LoadStatus; /* Runtime module information */ + CFE_ES_ControlReq_t ControlReq; /* The Control Request Record for External cFE Apps */ + CFE_ES_TaskId_t MainTaskId; /* The Application's Main Task ID */ } CFE_ES_AppRecord_t; @@ -146,11 +155,12 @@ typedef struct */ typedef struct { - CFE_ES_TaskId_t TaskId; /* The actual TaskID of this entry, or undefined */ - CFE_ES_AppId_t AppId; /* The parent Application's App ID */ - uint32 ExecutionCounter; /* The execution counter for the Child task */ - char TaskName[OS_MAX_API_NAME]; /* Task Name */ - + CFE_ES_TaskId_t TaskId; /* The actual TaskID of this entry, or undefined */ + char TaskName[OS_MAX_API_NAME]; /* Task Name */ + CFE_ES_AppId_t AppId; /* The parent Application's App ID */ + CFE_ES_TaskStartParams_t StartParams; /* The start parameters for the task */ + CFE_ES_TaskEntryFuncPtr_t EntryFunc; /* Task entry function */ + uint32 ExecutionCounter; /* The execution counter for the task */ } CFE_ES_TaskRecord_t; @@ -160,9 +170,11 @@ typedef struct */ typedef struct { - CFE_ES_LibId_t LibId; /* The actual LibID of this entry, or undefined */ - CFE_ES_ModuleLoadParams_t BasicInfo; /* Basic (static) information about the module */ - CFE_ES_ModuleLoadStatus_t ModuleInfo; /* Runtime information about the module */ + CFE_ES_LibId_t LibId; /* The actual LibID of this entry, or undefined */ + char LibName[OS_MAX_API_NAME]; /* Library Name */ + CFE_ES_ModuleLoadParams_t LoadParams; /* Basic (static) information about the module */ + CFE_ES_ModuleLoadStatus_t LoadStatus; /* Runtime information about the module */ + } CFE_ES_LibRecord_t; /* @@ -198,7 +210,7 @@ int32 CFE_ES_ParseFileEntry(const char **TokenList, uint32 NumTokens); ** This only loads the code and looks up relevent runtime information. ** It does not start any tasks. */ -int32 CFE_ES_LoadModule(CFE_ResourceId_t ResourceId, const CFE_ES_ModuleLoadParams_t* LoadParams, CFE_ES_ModuleLoadStatus_t *LoadStatus); +int32 CFE_ES_LoadModule(CFE_ResourceId_t ParentResourceId, const char *ModuleName, const CFE_ES_ModuleLoadParams_t* LoadParams, CFE_ES_ModuleLoadStatus_t *LoadStatus); /* ** Internal function to determine the entry point of an app. @@ -206,37 +218,29 @@ int32 CFE_ES_LoadModule(CFE_ResourceId_t ResourceId, const CFE_ES_ModuleLoadPara ** then this delays until the app is completely configured and the entry point is ** confirmed to be valid. */ -int32 CFE_ES_GetAppEntryPoint(osal_task_entry *FuncPtr); +int32 CFE_ES_GetTaskFunction(CFE_ES_TaskEntryFuncPtr_t *FuncPtr); /* -** Intermediate entry point of an app. Determines the actual +** Intermediate entry point of all tasks. Determines the actual ** entry point from the global data structures. */ -void CFE_ES_AppEntryPoint(void); +void CFE_ES_TaskEntryPoint(void); /* -** Internal function to start the main task of an app. +** Internal function to start a task associated with an app. */ -int32 CFE_ES_StartAppTask(const CFE_ES_AppStartParams_t* StartParams, CFE_ES_AppId_t RefAppId, CFE_ES_TaskId_t *TaskIdPtr); +int32 CFE_ES_StartAppTask(CFE_ES_TaskId_t *TaskIdPtr, const char *TaskName, CFE_ES_TaskEntryFuncPtr_t EntryFunc, const CFE_ES_TaskStartParams_t* Params, CFE_ES_AppId_t ParentAppId); /* ** Internal function to create/start a new cFE app ** based on the parameters passed in */ -int32 CFE_ES_AppCreate(CFE_ES_AppId_t *ApplicationIdPtr, - const char *FileName, - const char *EntryPointName, - const char *AppName, - CFE_ES_TaskPriority_Atom_t Priority, - size_t StackSize, - CFE_ES_ExceptionAction_Enum_t ExceptionAction); +int32 CFE_ES_AppCreate(CFE_ES_AppId_t *ApplicationIdPtr, const char *AppName, const CFE_ES_AppStartParams_t *Params); + /* ** Internal function to load a a new cFE shared Library */ -int32 CFE_ES_LoadLibrary(CFE_ES_LibId_t *LibraryIdPtr, - const char *FileName, - const char *EntryPointName, - const char *LibName); +int32 CFE_ES_LoadLibrary(CFE_ES_LibId_t *LibraryIdPtr, const char *LibName, const CFE_ES_ModuleLoadParams_t *Params); /* ** Scan the Application Table for actions to take @@ -249,9 +253,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_objtab.c b/fsw/cfe-core/src/es/cfe_es_objtab.c index 88bca7540..18c4c3287 100644 --- a/fsw/cfe-core/src/es/cfe_es_objtab.c +++ b/fsw/cfe-core/src/es/cfe_es_objtab.c @@ -129,7 +129,7 @@ CFE_ES_ObjectTable_t CFE_ES_ObjectTable[CFE_PLATFORM_ES_OBJECT_TABLE_SIZE] = { .ObjectType = CFE_ES_CORE_TASK, .ObjectName = "CFE_EVS", - .FuncPtrUnion.MainAppPtr = CFE_EVS_TaskMain, + .FuncPtrUnion.MainTaskPtr = CFE_EVS_TaskMain, .ObjectPriority = CFE_PLATFORM_EVS_START_TASK_PRIORITY, .ObjectSize = CFE_PLATFORM_EVS_START_TASK_STACK_SIZE }, @@ -139,7 +139,7 @@ CFE_ES_ObjectTable_t CFE_ES_ObjectTable[CFE_PLATFORM_ES_OBJECT_TABLE_SIZE] = { .ObjectType = CFE_ES_CORE_TASK, .ObjectName = "CFE_SB", - .FuncPtrUnion.MainAppPtr = CFE_SB_TaskMain, + .FuncPtrUnion.MainTaskPtr = CFE_SB_TaskMain, .ObjectPriority = CFE_PLATFORM_SB_START_TASK_PRIORITY, .ObjectSize = CFE_PLATFORM_SB_START_TASK_STACK_SIZE }, @@ -149,7 +149,7 @@ CFE_ES_ObjectTable_t CFE_ES_ObjectTable[CFE_PLATFORM_ES_OBJECT_TABLE_SIZE] = { .ObjectType = CFE_ES_CORE_TASK, .ObjectName = "CFE_ES", - .FuncPtrUnion.MainAppPtr = CFE_ES_TaskMain, + .FuncPtrUnion.MainTaskPtr = CFE_ES_TaskMain, .ObjectPriority = CFE_PLATFORM_ES_START_TASK_PRIORITY, .ObjectSize = CFE_PLATFORM_ES_START_TASK_STACK_SIZE }, @@ -159,7 +159,7 @@ CFE_ES_ObjectTable_t CFE_ES_ObjectTable[CFE_PLATFORM_ES_OBJECT_TABLE_SIZE] = { .ObjectType = CFE_ES_CORE_TASK, .ObjectName = "CFE_TIME", - .FuncPtrUnion.MainAppPtr = CFE_TIME_TaskMain, + .FuncPtrUnion.MainTaskPtr = CFE_TIME_TaskMain, .ObjectPriority = CFE_PLATFORM_TIME_START_TASK_PRIORITY, .ObjectSize = CFE_PLATFORM_TIME_START_TASK_STACK_SIZE }, @@ -169,7 +169,7 @@ CFE_ES_ObjectTable_t CFE_ES_ObjectTable[CFE_PLATFORM_ES_OBJECT_TABLE_SIZE] = { .ObjectType = CFE_ES_CORE_TASK, .ObjectName = "CFE_TBL", - .FuncPtrUnion.MainAppPtr = CFE_TBL_TaskMain, + .FuncPtrUnion.MainTaskPtr = CFE_TBL_TaskMain, .ObjectPriority = CFE_PLATFORM_TBL_START_TASK_PRIORITY, .ObjectSize = CFE_PLATFORM_TBL_START_TASK_STACK_SIZE }, diff --git a/fsw/cfe-core/src/es/cfe_es_resource.h b/fsw/cfe-core/src/es/cfe_es_resource.h index 9cbaa840d..efdb8280a 100644 --- a/fsw/cfe-core/src/es/cfe_es_resource.h +++ b/fsw/cfe-core/src/es/cfe_es_resource.h @@ -173,7 +173,7 @@ static inline bool CFE_ES_AppRecordIsMatch(const CFE_ES_AppRecord_t *AppRecPtr, */ static inline const char* CFE_ES_AppRecordGetName(const CFE_ES_AppRecord_t *AppRecPtr) { - return AppRecPtr->StartParams.BasicInfo.Name; + return AppRecPtr->AppName; } @@ -266,7 +266,7 @@ static inline bool CFE_ES_LibRecordIsMatch(const CFE_ES_LibRecord_t *LibRecPtr, */ static inline const char* CFE_ES_LibRecordGetName(const CFE_ES_LibRecord_t *LibRecPtr) { - return LibRecPtr->BasicInfo.Name; + return LibRecPtr->LibName; } diff --git a/fsw/cfe-core/src/es/cfe_es_start.c b/fsw/cfe-core/src/es/cfe_es_start.c index 5fcf246e7..da0538c4c 100644 --- a/fsw/cfe-core/src/es/cfe_es_start.c +++ b/fsw/cfe-core/src/es/cfe_es_start.c @@ -743,7 +743,6 @@ void CFE_ES_CreateObjects(void) uint16 i; CFE_ES_AppRecord_t *AppRecPtr; CFE_ResourceId_t PendingAppId; - CFE_ES_TaskId_t PendingTaskId; CFE_ES_WriteToSysLog("ES Startup: Starting Object Creation calls.\n"); @@ -767,15 +766,15 @@ void CFE_ES_CreateObjects(void) ** Fill out the parameters in the AppStartParams sub-structure */ AppRecPtr->Type = CFE_ES_AppType_CORE; - strncpy(AppRecPtr->StartParams.BasicInfo.Name, CFE_ES_ObjectTable[i].ObjectName, - sizeof(AppRecPtr->StartParams.BasicInfo.Name)-1); - AppRecPtr->StartParams.BasicInfo.Name[sizeof(AppRecPtr->StartParams.BasicInfo.Name)-1] = '\0'; + + strncpy(AppRecPtr->AppName, CFE_ES_ObjectTable[i].ObjectName, + sizeof(AppRecPtr->AppName)-1); + AppRecPtr->AppName[sizeof(AppRecPtr->AppName)-1] = '\0'; /* FileName and EntryPoint is not valid for core apps */ - AppRecPtr->StartParams.StackSize = CFE_ES_ObjectTable[i].ObjectSize; + AppRecPtr->StartParams.MainTaskInfo.StackSize = CFE_ES_ObjectTable[i].ObjectSize; + AppRecPtr->StartParams.MainTaskInfo.Priority = CFE_ES_ObjectTable[i].ObjectPriority; AppRecPtr->StartParams.ExceptionAction = CFE_ES_ExceptionAction_PROC_RESTART; - AppRecPtr->StartParams.Priority = CFE_ES_ObjectTable[i].ObjectPriority; - AppRecPtr->ModuleInfo.EntryAddress = (cpuaddr)CFE_ES_ObjectTable[i].FuncPtrUnion.VoidPtr; /* ** Fill out the Task State info @@ -798,7 +797,11 @@ void CFE_ES_CreateObjects(void) ** Start the core app main task ** (core apps are already in memory - no loading needed) */ - ReturnCode = CFE_ES_StartAppTask(&AppRecPtr->StartParams, CFE_ES_APPID_C(PendingAppId), &PendingTaskId); + ReturnCode = CFE_ES_StartAppTask(&AppRecPtr->MainTaskId, + AppRecPtr->AppName, + CFE_ES_ObjectTable[i].FuncPtrUnion.MainTaskPtr, + &AppRecPtr->StartParams.MainTaskInfo, + CFE_ES_APPID_C(PendingAppId)); /* * Finalize data in the app table entry, which must be done under lock. @@ -809,7 +812,6 @@ void CFE_ES_CreateObjects(void) if ( ReturnCode == OS_SUCCESS ) { - AppRecPtr->MainTaskId = PendingTaskId; CFE_ES_AppRecordSetUsed(AppRecPtr, PendingAppId); /* diff --git a/fsw/cfe-core/src/es/cfe_es_start.h b/fsw/cfe-core/src/es/cfe_es_start.h index e98990cc8..119df0a5a 100644 --- a/fsw/cfe-core/src/es/cfe_es_start.h +++ b/fsw/cfe-core/src/es/cfe_es_start.h @@ -62,12 +62,11 @@ */ typedef int32 (*CFE_ES_EarlyInitFuncPtr_t)(void); /**< \brief Req'd prototype of Early Init Functions */ -typedef void (*CFE_ES_MainAppFuncPtr_t)(void); /**< \brief Req'd prototype of Application Main Functions */ typedef union { CFE_ES_EarlyInitFuncPtr_t FunctionPtr; - CFE_ES_MainAppFuncPtr_t MainAppPtr; + CFE_ES_TaskEntryFuncPtr_t MainTaskPtr; void *VoidPtr; } CFE_ES_FuncPtrUnion_t; diff --git a/fsw/cfe-core/src/es/cfe_es_task.c b/fsw/cfe-core/src/es/cfe_es_task.c index 3f7696876..75bae1b7e 100644 --- a/fsw/cfe-core/src/es/cfe_es_task.c +++ b/fsw/cfe-core/src/es/cfe_es_task.c @@ -849,19 +849,18 @@ int32 CFE_ES_StartAppCmd(const CFE_ES_StartAppCmd_t *data) int32 FilenameLen; int32 AppEntryLen; int32 AppNameLen; - size_t AppStackSize; - char LocalFile[OS_MAX_PATH_LEN]; - char LocalEntryPt[OS_MAX_API_NAME]; char LocalAppName[OS_MAX_API_NAME]; + CFE_ES_AppStartParams_t StartParams; + /* Create local copies of all input strings and ensure null termination */ - FilenameLen = CFE_SB_MessageStringGet(LocalFile, (char *)cmd->AppFileName, NULL, - sizeof(LocalFile), sizeof(cmd->AppFileName)); + FilenameLen = CFE_SB_MessageStringGet(StartParams.BasicInfo.FileName, cmd->AppFileName, NULL, + sizeof(StartParams.BasicInfo.FileName), sizeof(cmd->AppFileName)); - AppEntryLen = CFE_SB_MessageStringGet(LocalEntryPt, (char *)cmd->AppEntryPoint, NULL, - sizeof(LocalEntryPt), sizeof(cmd->AppEntryPoint)); + AppEntryLen = CFE_SB_MessageStringGet(StartParams.BasicInfo.InitSymbolName, cmd->AppEntryPoint, NULL, + sizeof(StartParams.BasicInfo.InitSymbolName), sizeof(cmd->AppEntryPoint)); - AppNameLen = CFE_SB_MessageStringGet(LocalAppName, (char *)cmd->Application, NULL, + AppNameLen = CFE_SB_MessageStringGet(LocalAppName, cmd->Application, NULL, sizeof(LocalAppName), sizeof(cmd->Application)); /* @@ -872,19 +871,19 @@ int32 CFE_ES_StartAppCmd(const CFE_ES_StartAppCmd_t *data) CFE_ES_TaskData.CommandErrorCounter++; CFE_EVS_SendEvent(CFE_ES_START_INVALID_FILENAME_ERR_EID, CFE_EVS_EventType_ERROR, "CFE_ES_StartAppCmd: invalid filename: %s", - LocalFile); + StartParams.BasicInfo.FileName); } else if (AppEntryLen <= 0) { CFE_ES_TaskData.CommandErrorCounter++; CFE_EVS_SendEvent(CFE_ES_START_INVALID_ENTRY_POINT_ERR_EID, CFE_EVS_EventType_ERROR, - "CFE_ES_StartAppCmd: App Entry Point is NULL."); + "CFE_ES_StartAppCmd: App Entry Point is empty."); } else if (AppNameLen <= 0) { CFE_ES_TaskData.CommandErrorCounter++; CFE_EVS_SendEvent(CFE_ES_START_NULL_APP_NAME_ERR_EID, CFE_EVS_EventType_ERROR, - "CFE_ES_StartAppCmd: App Name is NULL."); + "CFE_ES_StartAppCmd: App Name is empty."); } else if (cmd->Priority > OS_MAX_PRIORITY) { @@ -906,22 +905,20 @@ int32 CFE_ES_StartAppCmd(const CFE_ES_StartAppCmd_t *data) /* If stack size was provided, use it, otherwise use default. */ if (cmd->StackSize == 0) { - AppStackSize = CFE_PLATFORM_ES_DEFAULT_STACK_SIZE; + StartParams.MainTaskInfo.StackSize = CFE_PLATFORM_ES_DEFAULT_STACK_SIZE; } else { - AppStackSize = cmd->StackSize; + StartParams.MainTaskInfo.StackSize = cmd->StackSize; } + StartParams.MainTaskInfo.Priority = cmd->Priority; + StartParams.ExceptionAction = cmd->ExceptionAction; + /* ** Invoke application loader/startup function. */ - Result = CFE_ES_AppCreate(&AppID, LocalFile, - LocalEntryPt, - LocalAppName, - cmd->Priority, - AppStackSize, - cmd->ExceptionAction); + Result = CFE_ES_AppCreate(&AppID, LocalAppName, &StartParams); /* ** Send appropriate event message @@ -931,14 +928,14 @@ int32 CFE_ES_StartAppCmd(const CFE_ES_StartAppCmd_t *data) CFE_ES_TaskData.CommandCounter++; CFE_EVS_SendEvent(CFE_ES_START_INF_EID, CFE_EVS_EventType_INFORMATION, "Started %s from %s, AppID = %lu", - LocalAppName, LocalFile, CFE_RESOURCEID_TO_ULONG(AppID)); + LocalAppName, StartParams.BasicInfo.FileName, CFE_RESOURCEID_TO_ULONG(AppID)); } else { CFE_ES_TaskData.CommandErrorCounter++; CFE_EVS_SendEvent(CFE_ES_START_ERR_EID, CFE_EVS_EventType_ERROR, "Failed to start %s from %s, RC = 0x%08X", - LocalAppName, LocalFile, (unsigned int)Result); + LocalAppName, StartParams.BasicInfo.FileName, (unsigned int)Result); } } /* End if -- command parameter validation */ @@ -1612,26 +1609,51 @@ 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 + { + /* Reset the entire state object (just for good measure, ensure no stale data) */ + memset(StatePtr, 0, sizeof(*StatePtr)); + + /* + * Fill out the remainder of meta data. + * This data is currently the same for every request + */ + StatePtr->FileWrite.FileSubType = CFE_FS_SubType_ES_ERLOG; + snprintf(StatePtr->FileWrite.Description, sizeof(StatePtr->FileWrite.Description), CFE_ES_ER_LOG_DESC); + + StatePtr->FileWrite.GetData = CFE_ES_BackgroundERLogFileDataGetter; + StatePtr->FileWrite.OnEvent = CFE_ES_BackgroundERLogFileEventHandler; + + CFE_SB_MessageStringGet(StatePtr->FileWrite.FileName, CmdPtr->FileName, + CFE_PLATFORM_ES_DEFAULT_ER_LOG_FILE, + sizeof(StatePtr->FileWrite.FileName), sizeof(CmdPtr->FileName)); + + Status = CFE_FS_BackgroundFileDumpRequest(&StatePtr->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 479799d80..fb8d69cf6 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; /* @@ -164,7 +165,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..7638472e9 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_Global.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_Global.FileDump.CompleteCount != CFE_FS_Global.FileDump.RequestCount) + { + Curr = &CFE_FS_Global.FileDump.Entries[CFE_FS_Global.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_Global.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_Global.FileDump.RequestCount + 1; + + /* Check if queue is full before writing to tail position */ + if (PendingRequestCount == (CFE_FS_Global.FileDump.CompleteCount + CFE_FS_MAX_BACKGROUND_FILE_WRITES)) + { + Status = CFE_STATUS_REQUEST_ALREADY_PENDING; + } + else + { + Curr = &CFE_FS_Global.FileDump.Entries[CFE_FS_Global.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_Global.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 c4772bad5..c2826b466 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_Global_t ** @@ -59,8 +141,13 @@ typedef struct { osal_id_t SharedDataMutexId; + CFE_FS_BackgroundFileDumpState_t FileDump; + } CFE_FS_Global_t; + +extern CFE_FS_Global_t CFE_FS_Global; + /* ** 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 499fe397f..2e00165bf 100644 --- a/fsw/cfe-core/src/inc/cfe_es.h +++ b/fsw/cfe-core/src/inc/cfe_es.h @@ -140,11 +140,18 @@ */ /* -** Child Task Main Function Prototype +** Entry Function Prototypes */ -typedef void (*CFE_ES_ChildTaskMainFuncPtr_t)(void); /**< \brief Required Prototype of Child Task Main Functions */ +typedef void (*CFE_ES_TaskEntryFuncPtr_t)(void); /**< \brief Required Prototype of Task Main Functions */ typedef int32 (*CFE_ES_LibraryEntryFuncPtr_t)(CFE_ES_LibId_t LibId); /**< \brief Required Prototype of Library Initialization Functions */ +/** + * \brief Compatible typedef for ES child task entry point. + * + * All ES task functions (main + child) use the same entry point type. + */ +typedef CFE_ES_TaskEntryFuncPtr_t CFE_ES_ChildTaskMainFuncPtr_t; + /** * @brief Type for the stack pointer of tasks. * @@ -1105,6 +1112,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 a9ed6f0e5..e504f4b37 100644 --- a/fsw/cfe-core/src/inc/cfe_sb_msg.h +++ b/fsw/cfe-core/src/inc/cfe_sb_msg.h @@ -591,11 +591,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/inc/cfe_version.h b/fsw/cfe-core/src/inc/cfe_version.h index f7bd98e01..94cb45b7c 100644 --- a/fsw/cfe-core/src/inc/cfe_version.h +++ b/fsw/cfe-core/src/inc/cfe_version.h @@ -35,7 +35,7 @@ /* Development Build Macro Definitions */ -#define CFE_BUILD_NUMBER 348 /*!< Development Build: Number of commits since baseline */ +#define CFE_BUILD_NUMBER 365 /*!< Development Build: Number of commits since baseline */ #define CFE_BUILD_BASELINE "v6.8.0-rc1" /*!< Development Build: git tag that is the base for the current development */ /* Version Macro Definitions */ diff --git a/fsw/cfe-core/src/sb/cfe_sb_api.c b/fsw/cfe-core/src/sb/cfe_sb_api.c index 772a84e53..8a1b33a98 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 23abe02ec..d01ab0b5d 100644 --- a/fsw/cfe-core/src/sb/cfe_sb_priv.h +++ b/fsw/cfe-core/src/sb/cfe_sb_priv.h @@ -149,10 +149,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; @@ -169,6 +169,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_Global_t @@ -192,6 +229,8 @@ typedef struct CFE_EVS_BinFilter_t EventFilters[CFE_SB_MAX_CFG_FILE_EVENTS_TO_FILTER]; CFE_SB_Qos_t Default_Qos; CFE_ResourceId_t LastPipeId; + + CFE_SB_BackgroundFileStateInfo_t BackgroundFile; } CFE_SB_Global_t; @@ -261,9 +300,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_WriteRtgInfo(const char *Filename); -int32 CFE_SB_WritePipeInfo(const char *Filename); -int32 CFE_SB_WriteMapInfo(const char *Filename); int32 CFE_SB_ZeroCopyReleaseDesc(CFE_SB_Buffer_t *Ptr2Release, CFE_SB_ZeroCopyHandle_t BufferHandle); int32 CFE_SB_ZeroCopyReleaseAppId(CFE_ES_AppId_t AppId); void CFE_SB_IncrBufUseCnt(CFE_SB_BufferD_t *bd); @@ -271,7 +307,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); @@ -470,6 +505,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 623b5d205..5c24e5fe6 100644 --- a/fsw/cfe-core/src/sb/cfe_sb_task.c +++ b/fsw/cfe-core/src/sb/cfe_sb_task.c @@ -806,9 +806,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; @@ -839,156 +841,79 @@ int32 CFE_SB_SendStatsCmd(const CFE_SB_SendSbStatsCmd_t *data) return CFE_SUCCESS; }/* CFE_SB_SendStatsCmd */ - -/****************************************************************************** - * \brief SB internal function to handle processing of 'Write Routing Info' Cmd - * - * \param[in] data Pointer to command structure - * - * \return Execution status, see \ref CFEReturnCodes - */ -int32 CFE_SB_WriteRoutingInfoCmd(const CFE_SB_WriteRoutingInfoCmd_t *data) -{ - char LocalFilename[OS_MAX_PATH_LEN]; - int32 Stat; - - CFE_SB_MessageStringGet(LocalFilename, data->Payload.Filename, CFE_PLATFORM_SB_DEFAULT_ROUTING_FILENAME, - sizeof(LocalFilename), sizeof(data->Payload.Filename)); - - Stat = CFE_SB_WriteRtgInfo(LocalFilename); - CFE_SB_IncrCmdCtr(Stat); - - return CFE_SUCCESS; -} - - -/****************************************************************************** - * \brief SB internal function to handle processing of 'Write Pipe Info' Cmd - * - * \param[in] data Pointer to command structure - * - * \return Execution status, see \ref CFEReturnCodes - */ -int32 CFE_SB_WritePipeInfoCmd(const CFE_SB_WritePipeInfoCmd_t *data) -{ - char LocalFilename[OS_MAX_PATH_LEN]; - int32 Stat; - - CFE_SB_MessageStringGet(LocalFilename, data->Payload.Filename, CFE_PLATFORM_SB_DEFAULT_PIPE_FILENAME, - sizeof(LocalFilename), sizeof(data->Payload.Filename)); - - Stat = CFE_SB_WritePipeInfo(LocalFilename); - CFE_SB_IncrCmdCtr(Stat); - - return CFE_SUCCESS; -} - - -/****************************************************************************** - * \brief SB internal function to handle processing of 'Write Map Info' Cmd - * - * \param[in] data Pointer to command structure - * - * \return Execution status, see \ref CFEReturnCodes - */ -int32 CFE_SB_WriteMapInfoCmd(const CFE_SB_WriteMapInfoCmd_t *data) -{ - char LocalFilename[OS_MAX_PATH_LEN]; - int32 Stat; - - CFE_SB_MessageStringGet(LocalFilename, data->Payload.Filename, CFE_PLATFORM_SB_DEFAULT_MAP_FILENAME, - sizeof(LocalFilename), sizeof(data->Payload.Filename)); - - Stat = CFE_SB_WriteMapInfo(LocalFilename); - - CFE_SB_IncrCmdCtr(Stat); - - return CFE_SUCCESS; -} - /****************************************************************************** * 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; } } @@ -1033,169 +958,257 @@ 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_Global.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_Global.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_Global.AppId, + "Error creating file %s, stat=0x%x", + BgFilePtr->FileWrite.FileName, (int)Status); + break; + + default: + /* unhandled event - ignore */ + break; + } +} /****************************************************************************** - * \brief SB internal function to write the routing information to a file + * \brief SB internal function to handle processing of 'Write Routing Info' Cmd * - * \param[in] Filename Pointer the file name to write + * \param[in] data Pointer to command structure * * \return Execution status, see \ref CFEReturnCodes - * \retval #CFE_SUCCESS \copybrief CFE_SUCCESS - * \retval #CFE_SB_FILE_IO_ERR \copybrief CFE_SB_FILE_IO_ERR */ -int32 CFE_SB_WriteRtgInfo(const char *Filename) +int32 CFE_SB_WriteRoutingInfoCmd(const CFE_SB_WriteRoutingInfoCmd_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_Global.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)); - /* Initialize the reset of the nonzero callback argument elements */ - args.FileSize = Status; - args.Filename = Filename; + /* 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)); - /* Write route info to file */ - CFE_SBR_ForEachRouteId(CFE_SB_WriteRouteToFile, &args, NULL); + /* + * 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"); - if (args.Status != 0) - { - return args.Status; + StatePtr->FileWrite.GetData = CFE_SB_WriteRouteInfoDataGetter; + StatePtr->FileWrite.OnEvent = CFE_SB_BackgroundFileEventHandler; + + 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_WriteRoutingInfoCmd */ -/****************************************************************************** - * \brief SB internal function to write the Pipe table to a file - * - * \param[in] Filename Pointer the file name to write - * - * \return Execution status, see \ref CFEReturnCodes - * \retval #CFE_SUCCESS \copybrief CFE_SUCCESS - * \retval #CFE_SB_FILE_IO_ERR \copybrief CFE_SB_FILE_IO_ERR - */ -int32 CFE_SB_WritePipeInfo(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_Global.PipeTbl[RecordNum]; - FileSize = Status; + CFE_SB_LockSharedData(__FILE__,__LINE__); - /* loop through the pipe table */ - CFE_SB_LockSharedData(__FILE__,__LINE__); - PipeDscPtr = CFE_SB_Global.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 ++; +/****************************************************************************** + * \brief SB internal function to handle processing of 'Write Pipe Info' Cmd + * + * \param[in] data Pointer to command structure + * + * \return Execution status, see \ref CFEReturnCodes + */ +int32 CFE_SB_WritePipeInfoCmd(const CFE_SB_WritePipeInfoCmd_t *data) +{ + const CFE_SB_WriteFileInfoCmd_Payload_t *CmdPtr; + CFE_SB_BackgroundFileStateInfo_t *StatePtr; + int32 Status; - CFE_SB_LockSharedData(__FILE__,__LINE__); + StatePtr = &CFE_SB_Global.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; @@ -1204,91 +1217,112 @@ int32 CFE_SB_WritePipeInfo(const char *Filename) /****************************************************************************** * 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; + + /* 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; - args->FileSize += status; - args->EntryCount++; + /* 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); } + /****************************************************************************** - * \brief SB internal function to write the Message Map to a file + * \brief SB internal function to handle processing of 'Write Map Info' Cmd * - * \param[in] Filename Pointer the file name to write + * \param[in] data Pointer to command structure * * \return Execution status, see \ref CFEReturnCodes - * \retval #CFE_SUCCESS \copybrief CFE_SUCCESS - * \retval #CFE_SB_FILE_IO_ERR \copybrief CFE_SB_FILE_IO_ERR */ -int32 CFE_SB_WriteMapInfo(const char *Filename) +int32 CFE_SB_WriteMapInfoCmd(const CFE_SB_WriteMapInfoCmd_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_Global.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; - } + /* Reset the entire state object (just for good measure, ensure no stale data) */ + memset(StatePtr, 0, sizeof(*StatePtr)); - /* 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); + /* 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)); - 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; - } - - /* 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_WriteMapInfoCmd */ /****************************************************************************** * Local callback helper for sending route subscriptions @@ -1410,29 +1444,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 21cbfaee2..7fbdb25c3 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 6ad6611f0..e17ddf804 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_Global_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 21952d79e..5ac2b95d0 100644 --- a/fsw/cfe-core/src/tbl/cfe_tbl_task_cmds.c +++ b/fsw/cfe-core/src/tbl/cfe_tbl_task_cmds.c @@ -1114,183 +1114,237 @@ int32 CFE_TBL_ActivateCmd(const CFE_TBL_ActivateCmd_t *data) /******************************************************************* ** -** CFE_TBL_DumpRegistryCmd() -- Process Dump Table Registry to file Command Message +** CFE_TBL_DumpRegistryGetter() -- Helper function for dumping table registry ** -** 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; - - /* 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)); + CFE_ES_AppId_t OwnerAppId; + bool IsValidEntry; - /* 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); - } + /* Make a pointer to simplify code look and to remove redundant indexing into registry */ + RegRecPtr = &CFE_TBL_Global.Registry[RecordNum]; - /* 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); + /* should lock registry while copying out data to ensure its in consistent state */ + CFE_TBL_LockRegistry(); - 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); + /* 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)) + { + 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) + { + if (StatePtr->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. */ + StatePtr->DumpRecord.LoadInProgress = StatePtr->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. */ + } + + 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; - /* 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; + /* 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) + { + StatePtr->DumpRecord.NumUsers++; + HandleIterator = CFE_TBL_Global.Handles[HandleIterator].NextLink; + } + } - if (Status == sizeof(CFE_FS_Header_t)) + /* 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)) { - Status = sizeof(CFE_TBL_RegDumpRec_t); - while ((RegIndex < CFE_PLATFORM_TBL_MAX_NUM_TABLES) && (Status == sizeof(CFE_TBL_RegDumpRec_t))) - { - /* Make a pointer to simplify code look and to remove redundant indexing into registry */ - RegRecPtr = &CFE_TBL_Global.Registry[RegIndex]; + CFE_ES_GetAppName(StatePtr->DumpRecord.OwnerAppName, OwnerAppId, sizeof(StatePtr->DumpRecord.OwnerAppName)); + } + else + { + strncpy(StatePtr->DumpRecord.OwnerAppName, "--UNOWNED--", sizeof(StatePtr->DumpRecord.OwnerAppName)-1); + StatePtr->DumpRecord.OwnerAppName[sizeof(StatePtr->DumpRecord.OwnerAppName)-1] = 0; + } - /* 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)) - { - /* 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_Global.Handles[HandleIterator].NextLink; - } + /* export data to caller */ + *Buffer = &StatePtr->DumpRecord; + *BufSize = sizeof(StatePtr->DumpRecord); + } + else + { + /* No data to write for this record */ + *BufSize = 0; + *Buffer = NULL; + } - /* 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); - } + /* Check for EOF (last entry) */ + return (RecordNum >= (CFE_PLATFORM_TBL_MAX_NUM_TABLES-1)); +} - /* Output Registry Dump Record to Registry Dump File */ - Status = OS_write(FileDescriptor, - &DumpRecord, - sizeof(CFE_TBL_RegDumpRec_t)); - - FileSize += Status; - NumEntries++; - } +/******************************************************************* +** +** CFE_TBL_DumpRegistryEventHandler() -- Helper function for dumping table registry +** +********************************************************************/ - /* Look at the next entry in the Registry */ - RegIndex++; - } +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; - if (Status == sizeof(CFE_TBL_RegDumpRec_t)) + /* + * 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) { - 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; + CFE_EVS_SendEventWithAppID(CFE_TBL_OVERWRITE_REG_DUMP_INF_EID, + CFE_EVS_EventType_DEBUG, + CFE_TBL_Global.TableTaskAppId, + "Successfully overwrote '%s' with Table Registry:Size=%d,Entries=%d", + StatePtr->FileWrite.FileName, (int)Position, (int)RecordNum); } else { - 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); + CFE_EVS_SendEventWithAppID(CFE_TBL_WRITE_REG_DUMP_INF_EID, + CFE_EVS_EventType_DEBUG, + CFE_TBL_Global.TableTaskAppId, + "Successfully dumped Table Registry to '%s':Size=%d,Entries=%d", + StatePtr->FileWrite.FileName, (int)Position, (int)RecordNum); } - } - else - { - CFE_EVS_SendEvent(CFE_TBL_WRITE_CFE_HDR_ERR_EID, + break; + + case CFE_FS_FileWriteEvent_RECORD_WRITE_ERROR: + CFE_EVS_SendEventWithAppID(CFE_TBL_WRITE_TBL_REG_ERR_EID, + CFE_EVS_EventType_ERROR, + CFE_TBL_Global.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_Global.TableTaskAppId, "Error writing cFE File Header to '%s', Status=0x%08X", - DumpFilename, (unsigned int)Status); - } + 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_Global.TableTaskAppId, + "Error creating dump file '%s', Status=0x%08X", + StatePtr->FileWrite.FileName, (unsigned int)Status); + break; + + default: + /* unhandled event - ignore */ + break; + } +} - /* We are done outputting data to the dump file. Close it. */ - OS_close(FileDescriptor); - } - else +/******************************************************************* +** +** CFE_TBL_DumpRegistryCmd() -- Process Dump Table Registry to file Command Message +** +** NOTE: For complete prolog information, see 'cfe_tbl_task_cmds.h' +********************************************************************/ + +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_Global.RegDumpState; + + /* If a reg dump was already pending, do not overwrite the current request */ + if (!CFE_FS_BackgroundFileDumpIsPending(&StatePtr->FileWrite)) { - 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); + /* 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 629abd399..118dc6346 100644 --- a/fsw/cfe-core/unit-test/es_UT.c +++ b/fsw/cfe-core/unit-test/es_UT.c @@ -275,6 +275,51 @@ CFE_ResourceId_t ES_UT_MakeCDSIdForIndex(uint32 ArrayIdx) return CFE_ResourceId_FromInteger(ArrayIdx + CFE_ES_CDSBLOCKID_BASE); } +/* + * A local stub that can serve as the user function for testing ES tasks + */ +void ES_UT_TaskFunction(void) +{ + UT_DEFAULT_IMPL(ES_UT_TaskFunction); +} + +/* + * Helper function to assemble basic bits of info into the "CFE_ES_ModuleLoadParams_t" struct + */ +void ES_UT_SetupModuleLoadParams(CFE_ES_ModuleLoadParams_t *Params, const char *FileName, const char *EntryName) +{ + char Empty = 0; + + if (FileName == NULL) + { + FileName = &Empty; + } + + if (EntryName == NULL) + { + EntryName = &Empty; + } + + strncpy(Params->FileName, FileName, sizeof(Params->FileName)); + strncpy(Params->InitSymbolName, EntryName, sizeof(Params->InitSymbolName)); +} + +/* + * Helper function to assemble basic bits of info into the "CFE_ES_AppStartParams_t" struct + */ +void ES_UT_SetupAppStartParams(CFE_ES_AppStartParams_t *Params, const char *FileName, const char *EntryName, + size_t StackSize, CFE_ES_TaskPriority_Atom_t Priority, + CFE_ES_ExceptionAction_Enum_t ExceptionAction) +{ + ES_UT_SetupModuleLoadParams(&Params->BasicInfo, FileName, EntryName); + Params->MainTaskInfo.StackSize = StackSize; + Params->MainTaskInfo.Priority = Priority; + Params->ExceptionAction = ExceptionAction; +} + + + + /* * Helper function to setup a single app ID in the given state, along with * a main task ID. A pointer to the App and Task record is output so the @@ -306,11 +351,9 @@ void ES_UT_SetupSingleAppId(CFE_ES_AppType_Enum_t AppType, CFE_ES_AppState_Enum_ if (AppName) { - strncpy(LocalAppPtr->StartParams.BasicInfo.Name, AppName, - sizeof(LocalAppPtr->StartParams.BasicInfo.Name)-1); - LocalAppPtr->StartParams.BasicInfo.Name[sizeof(LocalAppPtr->StartParams.BasicInfo.Name)-1] = 0; - strncpy(LocalTaskPtr->TaskName, AppName, - sizeof(LocalTaskPtr->TaskName)-1); + strncpy(LocalAppPtr->AppName, AppName, sizeof(LocalAppPtr->AppName)-1); + LocalAppPtr->AppName[sizeof(LocalAppPtr->AppName)-1] = 0; + strncpy(LocalTaskPtr->TaskName, AppName, sizeof(LocalTaskPtr->TaskName)-1); LocalTaskPtr->TaskName[sizeof(LocalTaskPtr->TaskName)-1] = 0; } @@ -332,7 +375,7 @@ void ES_UT_SetupSingleAppId(CFE_ES_AppType_Enum_t AppType, CFE_ES_AppState_Enum_ ++CFE_ES_Global.RegisteredExternalApps; OS_ModuleLoad(&UtOsalId, NULL, NULL, 0); - LocalAppPtr->ModuleInfo.ModuleId = UtOsalId; + LocalAppPtr->LoadStatus.ModuleId = UtOsalId; } ++CFE_ES_Global.RegisteredTasks; } @@ -391,9 +434,9 @@ void ES_UT_SetupSingleLibId(const char *LibName, CFE_ES_LibRecord_t **OutLibRec) if (LibName) { - strncpy(LocalLibPtr->BasicInfo.Name, LibName, - sizeof(LocalLibPtr->BasicInfo.Name)-1); - LocalLibPtr->BasicInfo.Name[sizeof(LocalLibPtr->BasicInfo.Name)-1] = 0; + strncpy(LocalLibPtr->LibName, LibName, + sizeof(LocalLibPtr->LibName)-1); + LocalLibPtr->LibName[sizeof(LocalLibPtr->LibName)-1] = 0; } if (OutLibRec) @@ -1168,6 +1211,7 @@ void TestApps(void) CFE_ES_AppRecord_t *UtAppRecPtr; CFE_ES_MemPoolRecord_t *UtPoolRecPtr; char NameBuffer[OS_MAX_API_NAME+5]; + CFE_ES_AppStartParams_t StartParams; UtPrintf("Begin Test Apps"); @@ -1271,25 +1315,23 @@ void TestApps(void) /* Test application loading and creation with a task creation failure */ ES_ResetUnitTest(); UT_SetDefaultReturnValue(UT_KEY(OS_TaskCreate), OS_ERROR); - Return = CFE_ES_AppCreate(&AppId, + ES_UT_SetupAppStartParams(&StartParams, "ut/filename", "EntryPoint", - "AppName", 170, 4096, 1); + Return = CFE_ES_AppCreate(&AppId, + "AppName", + &StartParams); UtAssert_INT32_EQ(Return, CFE_STATUS_EXTERNAL_RESOURCE_FAIL); UtAssert_NONZERO(UT_PrintfIsInHistory(UT_OSP_MESSAGES[UT_OSP_APP_CREATE])); - /* Test application creation with NULL file name */ + /* Test application creation with NULL parameters */ ES_ResetUnitTest(); Return = CFE_ES_AppCreate(&AppId, - NULL, - "EntryPoint", "AppName", - 170, - 4096, - 1); + NULL); UT_Report(__FILE__, __LINE__, Return == CFE_ES_BAD_ARGUMENT, "CFE_ES_AppCreate", @@ -1298,13 +1340,15 @@ void TestApps(void) /* Test application creation with name too long */ memset(NameBuffer, 'x', sizeof(NameBuffer)-1); NameBuffer[sizeof(NameBuffer)-1] = 0; - Return = CFE_ES_AppCreate(&AppId, + ES_UT_SetupAppStartParams(&StartParams, "ut/filename.x", "EntryPoint", - NameBuffer, 170, 4096, 1); + Return = CFE_ES_AppCreate(&AppId, + NameBuffer, + &StartParams); UT_Report(__FILE__, __LINE__, Return == CFE_ES_BAD_ARGUMENT, "CFE_ES_AppCreate", @@ -1313,26 +1357,30 @@ void TestApps(void) /* Test successful application loading and creation */ ES_ResetUnitTest(); - Return = CFE_ES_AppCreate(&AppId, + ES_UT_SetupAppStartParams(&StartParams, "ut/filename.x", "EntryPoint", - "AppName", 170, 8192, 1); + Return = CFE_ES_AppCreate(&AppId, + "AppName", + &StartParams); UT_Report(__FILE__, __LINE__, Return == CFE_SUCCESS, "CFE_ES_AppCreate", "Application load/create; successful"); /* Test application loading of the same name again */ - Return = CFE_ES_AppCreate(&AppId, + ES_UT_SetupAppStartParams(&StartParams, "ut/filename.x", "EntryPoint", - "AppName", 170, 8192, 1); + Return = CFE_ES_AppCreate(&AppId, + "AppName", + &StartParams); UT_Report(__FILE__, __LINE__, Return == CFE_ES_ERR_DUPLICATE_NAME, "CFE_ES_AppCreate", @@ -1341,26 +1389,30 @@ void TestApps(void) /* Test application loading and creation where the file cannot be loaded */ UT_InitData(); UT_SetDeferredRetcode(UT_KEY(OS_ModuleLoad), 1, -1); - Return = CFE_ES_AppCreate(&AppId, + ES_UT_SetupAppStartParams(&StartParams, "ut/filename.x", "EntryPoint", - "AppName2", 170, 8192, 1); + Return = CFE_ES_AppCreate(&AppId, + "AppName2", + &StartParams); UtAssert_INT32_EQ(Return, CFE_STATUS_EXTERNAL_RESOURCE_FAIL); UtAssert_NONZERO(UT_PrintfIsInHistory(UT_OSP_MESSAGES[UT_OSP_EXTRACT_FILENAME_UT55])); /* Test application loading and creation where all app slots are taken */ ES_ResetUnitTest(); UT_SetDefaultReturnValue(UT_KEY(CFE_ResourceId_FindNext), OS_ERROR); - Return = CFE_ES_AppCreate(&AppId, + ES_UT_SetupAppStartParams(&StartParams, "ut/filename.x", "EntryPoint", - "AppName", 170, 8192, 1); + Return = CFE_ES_AppCreate(&AppId, + "AppName", + &StartParams); UT_Report(__FILE__, __LINE__, Return == CFE_ES_NO_RESOURCE_IDS_AVAILABLE && UT_PrintfIsInHistory(UT_OSP_MESSAGES[UT_OSP_NO_FREE_APP_SLOTS]), @@ -1378,13 +1430,15 @@ void TestApps(void) */ ES_ResetUnitTest(); UT_SetDeferredRetcode(UT_KEY(OS_ModuleSymbolLookup), 1, -1); - Return = CFE_ES_AppCreate(&AppId, + ES_UT_SetupAppStartParams(&StartParams, "ut/filename.x", "EntryPoint", - "AppName", 170, 8192, 1); + Return = CFE_ES_AppCreate(&AppId, + "AppName", + &StartParams); UtAssert_INT32_EQ(Return, CFE_STATUS_EXTERNAL_RESOURCE_FAIL); UtAssert_NONZERO(UT_PrintfIsInHistory(UT_OSP_MESSAGES[UT_OSP_CANNOT_FIND_SYMBOL])); @@ -1395,13 +1449,15 @@ void TestApps(void) ES_ResetUnitTest(); UT_SetDeferredRetcode(UT_KEY(OS_ModuleSymbolLookup), 1, -1); UT_SetDeferredRetcode(UT_KEY(OS_ModuleUnload), 1, -1); - Return = CFE_ES_AppCreate(&AppId, + ES_UT_SetupAppStartParams(&StartParams, "ut/filename.x", "EntryPoint", - "AppName", 170, 8192, 1); + Return = CFE_ES_AppCreate(&AppId, + "AppName", + &StartParams); UtAssert_INT32_EQ(Return, CFE_STATUS_EXTERNAL_RESOURCE_FAIL); UtAssert_NONZERO(UT_PrintfIsInHistory(UT_OSP_MESSAGES[UT_OSP_CANNOT_FIND_SYMBOL])); UtAssert_NONZERO(UT_PrintfIsInHistory(UT_OSP_MESSAGES[UT_OSP_MODULE_UNLOAD_FAILED])); @@ -1487,15 +1543,12 @@ void TestApps(void) /* Test a successful control action request to exit an application */ ES_ResetUnitTest(); ES_UT_SetupSingleAppId(CFE_ES_AppType_EXTERNAL, CFE_ES_AppState_RUNNING, NULL, &UtAppRecPtr, NULL); - strncpy(UtAppRecPtr->StartParams.BasicInfo.FileName, - "/ram/Filename", sizeof(UtAppRecPtr->StartParams.BasicInfo.FileName) - 1); - UtAppRecPtr->StartParams.BasicInfo.FileName[sizeof(UtAppRecPtr->StartParams.BasicInfo.FileName) - 1] = '\0'; - strncpy(UtAppRecPtr->StartParams.BasicInfo.EntryPoint, - "NotNULL", sizeof(UtAppRecPtr->StartParams.BasicInfo.EntryPoint) - 1); - UtAppRecPtr->StartParams.BasicInfo.EntryPoint[sizeof(UtAppRecPtr->StartParams.BasicInfo.EntryPoint) - 1] = '\0'; - UtAppRecPtr->StartParams.Priority = 255; - UtAppRecPtr->StartParams.StackSize = 8192; - UtAppRecPtr->StartParams.ExceptionAction = 0; + ES_UT_SetupAppStartParams(&UtAppRecPtr->StartParams, + "/ram/Filename", + "NotNULL", + 8192, + 255, + 0); UtAppRecPtr->ControlReq.AppControlRequest = CFE_ES_RunStatus_APP_EXIT; AppId = CFE_ES_AppRecordGetID(UtAppRecPtr); @@ -1556,7 +1609,7 @@ void TestApps(void) ES_UT_SetupSingleAppId(CFE_ES_AppType_EXTERNAL, CFE_ES_AppState_RUNNING, NULL, &UtAppRecPtr, NULL); UtAppRecPtr->ControlReq.AppControlRequest = CFE_ES_RunStatus_SYS_RESTART; - OS_ModuleLoad(&UtAppRecPtr->ModuleInfo.ModuleId, NULL, NULL, 0); + OS_ModuleLoad(&UtAppRecPtr->LoadStatus.ModuleId, NULL, NULL, 0); UT_SetDefaultReturnValue(UT_KEY(OS_TaskCreate), OS_ERROR); AppId = CFE_ES_AppRecordGetID(UtAppRecPtr); CFE_ES_ProcessControlRequest(AppId); @@ -1587,7 +1640,7 @@ void TestApps(void) ES_UT_SetupSingleAppId(CFE_ES_AppType_EXTERNAL, CFE_ES_AppState_RUNNING, NULL, &UtAppRecPtr, NULL); UtAppRecPtr->ControlReq.AppControlRequest = CFE_ES_RunStatus_SYS_RELOAD; - OS_ModuleLoad(&UtAppRecPtr->ModuleInfo.ModuleId, NULL, NULL, 0); + OS_ModuleLoad(&UtAppRecPtr->LoadStatus.ModuleId, NULL, NULL, 0); UT_SetDefaultReturnValue(UT_KEY(OS_TaskCreate), OS_ERROR); AppId = CFE_ES_AppRecordGetID(UtAppRecPtr); CFE_ES_ProcessControlRequest(AppId); @@ -1601,15 +1654,13 @@ void TestApps(void) */ ES_ResetUnitTest(); ES_UT_SetupSingleAppId(CFE_ES_AppType_EXTERNAL, CFE_ES_AppState_RUNNING, NULL, &UtAppRecPtr, NULL); - strncpy(UtAppRecPtr->StartParams.BasicInfo.FileName, - "/ram/FileName", sizeof(UtAppRecPtr->StartParams.BasicInfo.FileName) - 1); - UtAppRecPtr->StartParams.BasicInfo.FileName[sizeof(UtAppRecPtr->StartParams.BasicInfo.FileName) - 1] = '\0'; - strncpy(UtAppRecPtr->StartParams.BasicInfo.EntryPoint, "NULL", - sizeof(UtAppRecPtr->StartParams.BasicInfo.EntryPoint) - 1); - UtAppRecPtr->StartParams.BasicInfo.EntryPoint[sizeof(UtAppRecPtr->StartParams.BasicInfo.EntryPoint) - 1] = '\0'; - UtAppRecPtr->StartParams.Priority = 255; - UtAppRecPtr->StartParams.StackSize = 8192; - UtAppRecPtr->StartParams.ExceptionAction = 0; + ES_UT_SetupAppStartParams(&UtAppRecPtr->StartParams, + "/ram/FileName", + "NULL", + 8192, + 255, + 0); + UtAppRecPtr->ControlReq.AppControlRequest = CFE_ES_RunStatus_APP_ERROR; AppId = CFE_ES_AppRecordGetID(UtAppRecPtr); @@ -1637,15 +1688,12 @@ void TestApps(void) /* Test a successful control action request to stop an application */ ES_ResetUnitTest(); ES_UT_SetupSingleAppId(CFE_ES_AppType_EXTERNAL, CFE_ES_AppState_RUNNING, NULL, &UtAppRecPtr, NULL); - strncpy(UtAppRecPtr->StartParams.BasicInfo.FileName, - "/ram/FileName", sizeof(UtAppRecPtr->StartParams.BasicInfo.FileName) - 1); - UtAppRecPtr->StartParams.BasicInfo.FileName[sizeof(UtAppRecPtr->StartParams.BasicInfo.FileName) - 1] = '\0'; - strncpy(UtAppRecPtr->StartParams.BasicInfo.EntryPoint, "NULL", - sizeof(UtAppRecPtr->StartParams.BasicInfo.EntryPoint) - 1); - UtAppRecPtr->StartParams.BasicInfo.EntryPoint[sizeof(UtAppRecPtr->StartParams.BasicInfo.EntryPoint) - 1] = '\0'; - UtAppRecPtr->StartParams.Priority = 255; - UtAppRecPtr->StartParams.StackSize = 8192; - UtAppRecPtr->StartParams.ExceptionAction = 0; + ES_UT_SetupAppStartParams(&UtAppRecPtr->StartParams, + "/ram/FileName", + "NULL", + 8192, + 255, + 0); UtAppRecPtr->ControlReq.AppControlRequest = CFE_ES_RunStatus_SYS_DELETE; AppId = CFE_ES_AppRecordGetID(UtAppRecPtr); @@ -1658,15 +1706,12 @@ void TestApps(void) /* Test a successful control action request to restart an application */ ES_ResetUnitTest(); ES_UT_SetupSingleAppId(CFE_ES_AppType_EXTERNAL, CFE_ES_AppState_RUNNING, NULL, &UtAppRecPtr, NULL); - strncpy(UtAppRecPtr->StartParams.BasicInfo.FileName, - "/ram/FileName", sizeof(UtAppRecPtr->StartParams.BasicInfo.FileName) - 1); - UtAppRecPtr->StartParams.BasicInfo.FileName[sizeof(UtAppRecPtr->StartParams.BasicInfo.FileName) - 1] = '\0'; - strncpy(UtAppRecPtr->StartParams.BasicInfo.EntryPoint, "NULL", - sizeof(UtAppRecPtr->StartParams.BasicInfo.EntryPoint) - 1); - UtAppRecPtr->StartParams.BasicInfo.EntryPoint[sizeof(UtAppRecPtr->StartParams.BasicInfo.EntryPoint) - 1] = '\0'; - UtAppRecPtr->StartParams.Priority = 255; - UtAppRecPtr->StartParams.StackSize = 8192; - UtAppRecPtr->StartParams.ExceptionAction = 0; + ES_UT_SetupAppStartParams(&UtAppRecPtr->StartParams, + "/ram/FileName", + "NULL", + 8192, + 255, + 0); UtAppRecPtr->ControlReq.AppControlRequest = CFE_ES_RunStatus_SYS_RESTART; AppId = CFE_ES_AppRecordGetID(UtAppRecPtr); @@ -1679,15 +1724,12 @@ void TestApps(void) /* Test a successful control action request to reload an application */ ES_ResetUnitTest(); ES_UT_SetupSingleAppId(CFE_ES_AppType_EXTERNAL, CFE_ES_AppState_RUNNING, NULL, &UtAppRecPtr, NULL); - strncpy(UtAppRecPtr->StartParams.BasicInfo.FileName, - "/ram/FileName", sizeof(UtAppRecPtr->StartParams.BasicInfo.FileName) -1); - UtAppRecPtr->StartParams.BasicInfo.FileName[sizeof(UtAppRecPtr->StartParams.BasicInfo.FileName) -1] = '\0'; - strncpy(UtAppRecPtr->StartParams.BasicInfo.EntryPoint, "NULL", - sizeof(UtAppRecPtr->StartParams.BasicInfo.EntryPoint) - 1); - UtAppRecPtr->StartParams.BasicInfo.EntryPoint[sizeof(UtAppRecPtr->StartParams.BasicInfo.EntryPoint) - 1] = '\0'; - UtAppRecPtr->StartParams.Priority = 255; - UtAppRecPtr->StartParams.StackSize = 8192; - UtAppRecPtr->StartParams.ExceptionAction = 0; + ES_UT_SetupAppStartParams(&UtAppRecPtr->StartParams, + "/ram/FileName", + "NULL", + 8192, + 255, + 0); UtAppRecPtr->ControlReq.AppControlRequest = CFE_ES_RunStatus_SYS_RELOAD; AppId = CFE_ES_AppRecordGetID(UtAppRecPtr); @@ -1702,15 +1744,12 @@ void TestApps(void) */ ES_ResetUnitTest(); ES_UT_SetupSingleAppId(CFE_ES_AppType_EXTERNAL, CFE_ES_AppState_RUNNING, NULL, &UtAppRecPtr, NULL); - strncpy(UtAppRecPtr->StartParams.BasicInfo.FileName, - "/ram/FileName", sizeof(UtAppRecPtr->StartParams.BasicInfo.FileName) - 1); - UtAppRecPtr->StartParams.BasicInfo.FileName[sizeof(UtAppRecPtr->StartParams.BasicInfo.FileName) - 1] = '\0'; - strncpy(UtAppRecPtr->StartParams.BasicInfo.EntryPoint, "NULL", - sizeof(UtAppRecPtr->StartParams.BasicInfo.EntryPoint) - 1); - UtAppRecPtr->StartParams.BasicInfo.EntryPoint[sizeof(UtAppRecPtr->StartParams.BasicInfo.EntryPoint) - 1] = '\0'; - UtAppRecPtr->StartParams.Priority = 255; - UtAppRecPtr->StartParams.StackSize = 8192; - UtAppRecPtr->StartParams.ExceptionAction = 0; + ES_UT_SetupAppStartParams(&UtAppRecPtr->StartParams, + "/ram/FileName", + "NULL", + 8192, + 255, + 0); UtAppRecPtr->ControlReq.AppControlRequest = CFE_ES_RunStatus_SYS_EXCEPTION; AppId = CFE_ES_AppRecordGetID(UtAppRecPtr); @@ -1781,7 +1820,7 @@ void TestApps(void) ES_UT_SetupSingleAppId(CFE_ES_AppType_EXTERNAL, CFE_ES_AppState_RUNNING, NULL, &UtAppRecPtr, NULL); ES_UT_SetupForOSCleanup(); - OS_ModuleLoad(&UtAppRecPtr->ModuleInfo.ModuleId, NULL, NULL, 0); + OS_ModuleLoad(&UtAppRecPtr->LoadStatus.ModuleId, NULL, NULL, 0); UT_SetDefaultReturnValue(UT_KEY(OS_TaskDelete), OS_ERROR); UT_SetDefaultReturnValue(UT_KEY(OS_close), OS_ERROR); AppId = CFE_ES_AppRecordGetID(UtAppRecPtr); @@ -2156,36 +2195,29 @@ void TestResourceID(void) void TestLibs(void) { CFE_ES_LibRecord_t *UtLibRecPtr; - char LongLibraryName[sizeof(UtLibRecPtr->BasicInfo.Name)+1]; + char LongLibraryName[sizeof(UtLibRecPtr->LibName)+1]; CFE_ES_LibId_t Id; int32 Return; + CFE_ES_ModuleLoadParams_t LoadParams; /* Test shared library loading and initialization where the initialization * routine returns an error */ ES_ResetUnitTest(); UT_SetDummyFuncRtn(-444); - Return = CFE_ES_LoadLibrary(&Id, - "filename", - "EntryPoint", - "LibName"); + ES_UT_SetupModuleLoadParams(&LoadParams, "filename", "entrypt"); + Return = CFE_ES_LoadLibrary(&Id, "LibName", &LoadParams); UtAssert_INT32_EQ(Return, -444); UtAssert_NONZERO(UT_PrintfIsInHistory(UT_OSP_MESSAGES[UT_OSP_SHARED_LIBRARY_INIT])); /* Test Load library returning an error on a null pointer argument */ - Return = CFE_ES_LoadLibrary(&Id, - NULL, - "EntryPoint", - "LibName"); + Return = CFE_ES_LoadLibrary(&Id, "LibName", NULL); UT_Report(__FILE__, __LINE__, Return == CFE_ES_BAD_ARGUMENT, "CFE_ES_LoadLibrary", "Load shared library bad argument (NULL filename)"); - Return = CFE_ES_LoadLibrary(&Id, - "filename", - "EntryPoint", - NULL); + Return = CFE_ES_LoadLibrary(&Id, NULL, &LoadParams); UT_Report(__FILE__, __LINE__, Return == CFE_ES_BAD_ARGUMENT, "CFE_ES_LoadLibrary", @@ -2194,10 +2226,7 @@ void TestLibs(void) /* Test Load library returning an error on a too long library name */ memset(LongLibraryName, 'a', sizeof(LongLibraryName)-1); LongLibraryName[sizeof(LongLibraryName)-1] = '\0'; - Return = CFE_ES_LoadLibrary(&Id, - "filename", - "EntryPoint", - &LongLibraryName[0]); + Return = CFE_ES_LoadLibrary(&Id, LongLibraryName, &LoadParams); UT_Report(__FILE__, __LINE__, Return == CFE_ES_BAD_ARGUMENT, "CFE_ES_LoadLibrary", @@ -2206,10 +2235,7 @@ void TestLibs(void) /* Test successful shared library loading and initialization */ UT_InitData(); UT_SetDummyFuncRtn(OS_SUCCESS); - Return = CFE_ES_LoadLibrary(&Id, - "/cf/apps/tst_lib.bundle", - "TST_LIB_Init", - "TST_LIB"); + Return = CFE_ES_LoadLibrary(&Id, "TST_LIB", &LoadParams); UT_Report(__FILE__, __LINE__, Return == CFE_SUCCESS, "CFE_ES_LoadLibrary", @@ -2220,10 +2246,7 @@ void TestLibs(void) UtAssert_True(CFE_ES_LibRecordIsUsed(UtLibRecPtr), "CFE_ES_LoadLibrary() record used"); /* Try loading same library again, should return the DUPLICATE code */ - Return = CFE_ES_LoadLibrary(&Id, - "/cf/apps/tst_lib.bundle", - "TST_LIB_Init", - "TST_LIB"); + Return = CFE_ES_LoadLibrary(&Id, "TST_LIB", &LoadParams); UT_Report(__FILE__, __LINE__, Return == CFE_ES_ERR_DUPLICATE_NAME, "CFE_ES_LoadLibrary", @@ -2236,10 +2259,7 @@ void TestLibs(void) */ ES_ResetUnitTest(); UT_SetDeferredRetcode(UT_KEY(OS_ModuleLoad), 1, -1); - Return = CFE_ES_LoadLibrary(&Id, - "/cf/apps/tst_lib.bundle", - "TST_LIB_Init", - "TST_LIB"); + Return = CFE_ES_LoadLibrary(&Id, "TST_LIB", &LoadParams); UtAssert_INT32_EQ(Return, CFE_STATUS_EXTERNAL_RESOURCE_FAIL); /* Test shared library loading and initialization where the library @@ -2247,10 +2267,7 @@ void TestLibs(void) */ ES_ResetUnitTest(); UT_SetDeferredRetcode(UT_KEY(OS_ModuleSymbolLookup), 1, -1); - Return = CFE_ES_LoadLibrary(&Id, - "/cf/apps/tst_lib.bundle", - "TST_LIB_Init", - "TST_LIB"); + Return = CFE_ES_LoadLibrary(&Id, "TST_LIB", &LoadParams); UtAssert_INT32_EQ(Return, CFE_STATUS_EXTERNAL_RESOURCE_FAIL); /* Test shared library loading and initialization where the library @@ -2259,10 +2276,8 @@ void TestLibs(void) ES_ResetUnitTest(); UT_SetDefaultReturnValue(UT_KEY(OS_remove), OS_ERROR); /* for coverage of error path */ UT_SetDefaultReturnValue(UT_KEY(dummy_function), -555); - Return = CFE_ES_LoadLibrary(&Id, - "/cf/apps/tst_lib.bundle", - "dummy_function", - "TST_LIB"); + ES_UT_SetupModuleLoadParams(&LoadParams, "filename", "dummy_function"); + Return = CFE_ES_LoadLibrary(&Id, "TST_LIB", &LoadParams); UT_Report(__FILE__, __LINE__, Return == -555, "CFE_ES_LoadLibrary", @@ -2273,10 +2288,7 @@ void TestLibs(void) */ ES_ResetUnitTest(); UT_SetDefaultReturnValue(UT_KEY(CFE_ResourceId_FindNext), OS_ERROR); - Return = CFE_ES_LoadLibrary(&Id, - "filename", - "EntryPoint", - "LibName"); + Return = CFE_ES_LoadLibrary(&Id, "LibName", &LoadParams); UT_Report(__FILE__, __LINE__, Return == CFE_ES_NO_RESOURCE_IDS_AVAILABLE && UT_PrintfIsInHistory(UT_OSP_MESSAGES[UT_OSP_LIBRARY_SLOTS]), @@ -2306,6 +2318,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"); @@ -2343,6 +2358,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"); } @@ -2592,7 +2652,6 @@ void TestGenericPool(void) void TestTask(void) { uint32 ResetType; - uint32 UT_ContextData; osal_id_t UT_ContextTask; union { @@ -3430,11 +3489,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__, @@ -3442,71 +3501,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"); + "Write E&R log command; already pending event (from FS)"); - /* 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"); - - /* 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(); @@ -4870,7 +4883,7 @@ void TestAPI(void) 400, 0); UT_Report(__FILE__, __LINE__, - Return == CFE_ES_ERR_CHILD_TASK_CREATE, + Return == CFE_STATUS_EXTERNAL_RESOURCE_FAIL, "CFE_ES_ChildTaskCreate", "OS task create failed"); @@ -4962,6 +4975,26 @@ void TestAPI(void) Return == CFE_SUCCESS, "CFE_ES_CreateChildTask", "Create child task successful"); + /* Test common entry point */ + ES_ResetUnitTest(); + + /* + * Without no app/task set up the entry point will not be found. + * There is no return value to check here, it just will not do anything. + */ + CFE_ES_TaskEntryPoint(); + + /* Now set up an app+task */ + ES_UT_SetupSingleAppId(CFE_ES_AppType_EXTERNAL, CFE_ES_AppState_RUNNING, NULL, &UtAppRecPtr, &UtTaskRecPtr); + + /* Test with app/task set up but no entry point defined */ + CFE_ES_TaskEntryPoint(); + + /* Finally set entry point, nominal mode */ + UtTaskRecPtr->EntryFunc = ES_UT_TaskFunction; + CFE_ES_TaskEntryPoint(); + UtAssert_STUB_COUNT(ES_UT_TaskFunction, 1); + /* Test deleting a child task using a main task's ID */ ES_ResetUnitTest(); ES_UT_SetupSingleAppId(CFE_ES_AppType_EXTERNAL, CFE_ES_AppState_RUNNING, "UT", NULL, &UtTaskRecPtr); diff --git a/fsw/cfe-core/unit-test/fs_UT.c b/fsw/cfe-core/unit-test/fs_UT.c index 4b569cb19..00335e0dd 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_Global.FileDump, 0, sizeof(CFE_FS_Global.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_Global.FileDump.Current.Credit > 0, "Credit accumulating (%lu)", (unsigned long)CFE_FS_Global.FileDump.Current.Credit); + UtAssert_True(CFE_FS_Global.FileDump.Current.Credit < CFE_FS_BACKGROUND_MAX_CREDIT, "Credit not max (%lu)", (unsigned long)CFE_FS_Global.FileDump.Current.Credit); + + UtAssert_True(!CFE_FS_RunBackgroundFileDump(100000, NULL), "CFE_FS_RunBackgroundFileDump() nothing pending, long delay"); + UtAssert_True(CFE_FS_Global.FileDump.Current.Credit == CFE_FS_BACKGROUND_MAX_CREDIT, "Credit at max (%lu)", (unsigned long)CFE_FS_Global.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_Global.FileDump.Current.Credit <= 0, "Credit exhausted (%lu)", (unsigned long)CFE_FS_Global.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_Global.FileDump.CompleteCount, CFE_FS_Global.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_Global.FileDump.CompleteCount, CFE_FS_Global.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_Global.FileDump.CompleteCount, CFE_FS_Global.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_Global.FileDump.CompleteCount, CFE_FS_Global.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_Global.FileDump.RequestCount, (CFE_FS_Global.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 3e041c35d..e6afc7715 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); @@ -528,24 +523,20 @@ void Test_SB_Cmds_RoutingInfoDef(void) CFE_SB_ProcessCmdPipePkt(&WriteRoutingInfo.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_Global.CmdPipe)); } /* end Test_SB_Cmds_RoutingInfoDef */ /* -** Test write routing information command using a specified file name +** Test write routing information command with request already pending */ -void Test_SB_Cmds_RoutingInfoSpec(void) +void Test_SB_Cmds_RoutingInfoAlreadyPending(void) { union { @@ -556,34 +547,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(WriteRoutingInfo.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(WriteRoutingInfo.Cmd.Payload.Filename, "RoutingTstFile", - sizeof(WriteRoutingInfo.Cmd.Payload.Filename) - 1); - WriteRoutingInfo.Cmd.Payload.Filename[sizeof(WriteRoutingInfo.Cmd.Payload.Filename) - 1] = '\0'; - - CFE_SB_ProcessCmdPipePkt(&WriteRoutingInfo.SBBuf); - - EVTCNT(1); - - EVTSENT(CFE_SB_SND_RTG_EID); - -} /* end Test_SB_Cmds_RoutingInfoSpec */ - -/* -** Test write routing information command with a file creation failure -*/ -void Test_SB_Cmds_RoutingInfoCreateFail(void) -{ - union - { - CFE_SB_Buffer_t SBBuf; - CFE_SB_WriteRoutingInfoCmd_t Cmd; - } WriteRoutingInfo; - CFE_MSG_FcnCode_t FcnCode = CFE_SB_WRITE_ROUTING_INFO_CC; - CFE_SB_MsgId_t MsgId = CFE_SB_ValueToMsgId(CFE_SB_CMD_MID); - CFE_MSG_Size_t Size = sizeof(WriteRoutingInfo.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); @@ -592,61 +556,63 @@ void Test_SB_Cmds_RoutingInfoCreateFail(void) sizeof(WriteRoutingInfo.Cmd.Payload.Filename) - 1); WriteRoutingInfo.Cmd.Payload.Filename[sizeof(WriteRoutingInfo.Cmd.Payload.Filename) - 1] = '\0'; - /* Make function CFE_SB_WriteRtgInfo return CFE_SB_FILE_IO_ERR */ - UT_SetDefaultReturnValue(UT_KEY(OS_OpenCreate), OS_ERROR); - CFE_SB_ProcessCmdPipePkt(&WriteRoutingInfo.SBBuf); EVTCNT(1); EVTSENT(CFE_SB_SND_RTG_ERR1_EID); -} /* end Test_SB_Cmds_RoutingInfoCreateFail */ - -/* -** Test write 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_WriteRtgInfo("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 write 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_WriteRtgInfo("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_Global.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 write pipe information command using the default file name +** Test send pipe information command default / nominal path */ void Test_SB_Cmds_PipeInfoDef(void) { @@ -675,10 +641,9 @@ void Test_SB_Cmds_PipeInfoDef(void) CFE_SB_ProcessCmdPipePkt(&WritePipeInfo.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)); @@ -687,9 +652,9 @@ void Test_SB_Cmds_PipeInfoDef(void) } /* end Test_SB_Cmds_PipeInfoDef */ /* -** Test write pipe information command using a specified file name +** Test write pipe information command when already pending */ -void Test_SB_Cmds_PipeInfoSpec(void) +void Test_SB_Cmds_PipeInfoAlreadyPending(void) { union { @@ -700,6 +665,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(WritePipeInfo.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); @@ -711,70 +678,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 write pipe information command with a file creation failure +** Test write 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_WritePipeInfo("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 write 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_WritePipeInfo("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 write pipe information command with a file write failure on -** the second write +** Test write pipe 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_WritePipeInfo("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 write map information command using the default file name +** Test write map information command nominal path */ void Test_SB_Cmds_MapInfoDef(void) { @@ -816,9 +844,7 @@ void Test_SB_Cmds_MapInfoDef(void) CFE_SB_ProcessCmdPipePkt(&WriteMapInfo.SBBuf); - EVTCNT(11); - - EVTSENT(CFE_SB_SND_RTG_EID); + EVTCNT(10); EVTSENT(CFE_SB_PIPE_ADDED_EID); @@ -831,9 +857,9 @@ void Test_SB_Cmds_MapInfoDef(void) } /* end Test_SB_Cmds_MapInfoDef */ /* -** Test write map information command using a specified file name +** Test write map information command when already pending */ -void Test_SB_Cmds_MapInfoSpec(void) +void Test_SB_Cmds_MapInfoAlreadyPending(void) { union { @@ -844,6 +870,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(WriteMapInfo.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); @@ -855,83 +883,9 @@ void Test_SB_Cmds_MapInfoSpec(void) EVTCNT(1); - EVTSENT(CFE_SB_SND_RTG_EID); - -} /* end Test_SB_Cmds_MapInfoSpec */ - -/* -** Test write 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_WriteMapInfo("MapTstFile"), CFE_SB_FILE_IO_ERR); - - EVTCNT(1); - EVTSENT(CFE_SB_SND_RTG_ERR1_EID); -} /* end Test_SB_Cmds_MapInfoCreateFail */ - -/* -** Test write 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_WriteMapInfo("MapTstFile"), CFE_SB_FILE_IO_ERR); - - EVTCNT(1); - - EVTSENT(CFE_SB_FILEWRITE_ERR_EID); - -} /* end Test_SB_Cmds_MapInfoHdrFail */ - -/* -** Test write 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_WriteMapInfo("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 7ebff942d..2f09ea386 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 a26d524c5..156c560a6 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_Global.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_Global.RegDumpState.FileExisted = true; + CFE_TBL_DumpRegistryEventHandler(&CFE_TBL_Global.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_Global.RegDumpState.FileExisted = false; + CFE_TBL_DumpRegistryEventHandler(&CFE_TBL_Global.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_Global.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_Global.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_Global.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_Global.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_Global.Registry[0].OwnerAppId = AppID; CFE_TBL_Global.Registry[0].HeadOfAccessList = CFE_TBL_END_OF_LIST; CFE_TBL_Global.Registry[1].OwnerAppId = CFE_TBL_NOT_OWNED; CFE_TBL_Global.Registry[0].LoadInProgress = CFE_TBL_NO_LOAD_IN_PROGRESS + 1; CFE_TBL_Global.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_Global.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_Global.Registry[0].OwnerAppId = CFE_TBL_NOT_OWNED; - CFE_TBL_Global.Registry[0].HeadOfAccessList = 2; + LocalBuf = NULL; + LocalSize = 0; + IsEOF = CFE_TBL_DumpRegistryGetter(&CFE_TBL_Global.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_Global.Registry[CFE_PLATFORM_TBL_MAX_NUM_TABLES-1].OwnerAppId = CFE_TBL_NOT_OWNED; + CFE_TBL_Global.Registry[CFE_PLATFORM_TBL_MAX_NUM_TABLES-1].HeadOfAccessList = 2; CFE_TBL_Global.Handles[2].NextLink = CFE_TBL_END_OF_LIST; + LocalBuf = NULL; + LocalSize = 0; + IsEOF = CFE_TBL_DumpRegistryGetter(&CFE_TBL_Global.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_Global.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); + +} diff --git a/fsw/cfe-core/ut-stubs/ut_tbl_stubs.c b/fsw/cfe-core/ut-stubs/ut_tbl_stubs.c index f9df4806a..f75ae4a35 100644 --- a/fsw/cfe-core/ut-stubs/ut_tbl_stubs.c +++ b/fsw/cfe-core/ut-stubs/ut_tbl_stubs.c @@ -132,7 +132,7 @@ int32 CFE_TBL_GetAddress (void **TblPtr, CFE_TBL_Handle_t TblHandle) int32 ForceValue; status = UT_DEFAULT_IMPL(CFE_TBL_GetAddress); - if (status >= 0 && !UT_Stub_CheckForceFail(UT_KEY(CFE_TBL_GetAddress), &ForceValue)) + if (status >= 0 && !UT_Stub_CheckDefaultReturnValue(UT_KEY(CFE_TBL_GetAddress), &ForceValue)) { UT_Stub_CopyToLocal(UT_KEY(CFE_TBL_GetAddress), (uint8 *)TblPtr, sizeof(void*)); }