Skip to content

Commit

Permalink
Merge pull request nasa#2617 from nasa/integration-candidate
Browse files Browse the repository at this point in the history
cFE Integration candidate: Equuleus-rc1+dev21
  • Loading branch information
dzbaker authored Dec 10, 2024
2 parents f87ab0e + bf05cc8 commit 2f0e1a2
Show file tree
Hide file tree
Showing 11 changed files with 76 additions and 79 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## Development Build: equuleus-rc1+dev219
- Consistent use of CFE_EVS_EventType_Enum_t for EventType
- Combine redundant switch blocks to simplify CFE_ES_CleanupObjectCallback
- Unloading module after failed app start
- Simplify and clarify EVS_AddLog logic
- See <https://github.com/nasa/cFE/pull/2308>, <https://github.com/nasa/cFE/pull/2612>, <https://github.com/nasa/cFE/pull/2616>, and <https://github.com/nasa/cFE/pull/2309>

## Development Build: equuleus-rc1+dev209
- Remove redundant status check in CFE_ES_RegisterCDSEx()
- See <https://github.com/nasa/cFE/pull/2329>
Expand Down
2 changes: 1 addition & 1 deletion modules/cfe_assert/src/cfe_assert_runner.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ bool CFE_Assert_Status_DeferredCheck(CFE_Status_t Status, UtAssert_CaseType_t Ca

void CFE_Assert_StatusReport(uint8 MessageType, const char *Prefix, const char *OutputMessage)
{
uint16 EventType;
CFE_EVS_EventType_Enum_t EventType;

switch (MessageType)
{
Expand Down
11 changes: 6 additions & 5 deletions modules/core_api/fsw/inc/cfe_evs.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ CFE_Status_t CFE_EVS_Register(const void *Filters, uint16 NumEventFilters, uint1
** \sa #CFE_EVS_SendEventWithAppID, #CFE_EVS_SendTimedEvent
**
**/
CFE_Status_t CFE_EVS_SendEvent(uint16 EventID, uint16 EventType, const char *Spec, ...) OS_PRINTF(3, 4);
CFE_Status_t CFE_EVS_SendEvent(uint16 EventID, CFE_EVS_EventType_Enum_t EventType, const char *Spec, ...)
OS_PRINTF(3, 4);

/**
** \brief Generate a software event given the specified Application ID.
Expand Down Expand Up @@ -201,8 +202,8 @@ CFE_Status_t CFE_EVS_SendEvent(uint16 EventID, uint16 EventType, const char *Spe
** \sa #CFE_EVS_SendEvent, #CFE_EVS_SendTimedEvent
**
**/
CFE_Status_t CFE_EVS_SendEventWithAppID(uint16 EventID, uint16 EventType, CFE_ES_AppId_t AppID, const char *Spec, ...)
OS_PRINTF(4, 5);
CFE_Status_t CFE_EVS_SendEventWithAppID(uint16 EventID, CFE_EVS_EventType_Enum_t EventType, CFE_ES_AppId_t AppID,
const char *Spec, ...) OS_PRINTF(4, 5);

/**
** \brief Generate a software event with a specific time tag.
Expand Down Expand Up @@ -251,8 +252,8 @@ CFE_Status_t CFE_EVS_SendEventWithAppID(uint16 EventID, uint16 EventType, CFE_ES
** \sa #CFE_EVS_SendEvent, #CFE_EVS_SendEventWithAppID
**
**/
CFE_Status_t CFE_EVS_SendTimedEvent(CFE_TIME_SysTime_t Time, uint16 EventID, uint16 EventType, const char *Spec, ...)
OS_PRINTF(4, 5);
CFE_Status_t CFE_EVS_SendTimedEvent(CFE_TIME_SysTime_t Time, uint16 EventID, CFE_EVS_EventType_Enum_t EventType,
const char *Spec, ...) OS_PRINTF(4, 5);
/**@}*/

/** @defgroup CFEAPIEVSResetFilter cFE Reset Event Filter APIs
Expand Down
2 changes: 1 addition & 1 deletion modules/core_api/fsw/inc/cfe_version.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
#define CFE_VERSION_H

/* Development Build Macro Definitions */
#define CFE_BUILD_NUMBER 209 /**< @brief Development: Number of development git commits since CFE_BUILD_BASELINE */
#define CFE_BUILD_NUMBER 219 /**< @brief Development: Number of development git commits since CFE_BUILD_BASELINE */
#define CFE_BUILD_BASELINE "equuleus-rc1" /**< @brief Development: Reference git tag for build number */
#define CFE_BUILD_DEV_CYCLE "equuleus-rc2" /**< @brief Development: Release name for current development cycle */
#define CFE_BUILD_CODENAME "Equuleus" /**< @brief: Development: Code name for the current build */
Expand Down
14 changes: 8 additions & 6 deletions modules/core_api/ut-stubs/src/cfe_evs_stubs.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,14 @@ CFE_Status_t CFE_EVS_ResetFilter(uint16 EventID)
* Generated stub function for CFE_EVS_SendEvent()
* ----------------------------------------------------
*/
CFE_Status_t CFE_EVS_SendEvent(uint16 EventID, uint16 EventType, const char *Spec, ...)
CFE_Status_t CFE_EVS_SendEvent(uint16 EventID, CFE_EVS_EventType_Enum_t EventType, const char *Spec, ...)
{
va_list UtStub_ArgList;

UT_GenStub_SetupReturnBuffer(CFE_EVS_SendEvent, CFE_Status_t);

UT_GenStub_AddParam(CFE_EVS_SendEvent, uint16, EventID);
UT_GenStub_AddParam(CFE_EVS_SendEvent, uint16, EventType);
UT_GenStub_AddParam(CFE_EVS_SendEvent, CFE_EVS_EventType_Enum_t, EventType);
UT_GenStub_AddParam(CFE_EVS_SendEvent, const char *, Spec);

va_start(UtStub_ArgList, Spec);
Expand All @@ -106,14 +106,15 @@ CFE_Status_t CFE_EVS_SendEvent(uint16 EventID, uint16 EventType, const char *Spe
* Generated stub function for CFE_EVS_SendEventWithAppID()
* ----------------------------------------------------
*/
CFE_Status_t CFE_EVS_SendEventWithAppID(uint16 EventID, uint16 EventType, CFE_ES_AppId_t AppID, const char *Spec, ...)
CFE_Status_t CFE_EVS_SendEventWithAppID(uint16 EventID, CFE_EVS_EventType_Enum_t EventType, CFE_ES_AppId_t AppID,
const char *Spec, ...)
{
va_list UtStub_ArgList;

UT_GenStub_SetupReturnBuffer(CFE_EVS_SendEventWithAppID, CFE_Status_t);

UT_GenStub_AddParam(CFE_EVS_SendEventWithAppID, uint16, EventID);
UT_GenStub_AddParam(CFE_EVS_SendEventWithAppID, uint16, EventType);
UT_GenStub_AddParam(CFE_EVS_SendEventWithAppID, CFE_EVS_EventType_Enum_t, EventType);
UT_GenStub_AddParam(CFE_EVS_SendEventWithAppID, CFE_ES_AppId_t, AppID);
UT_GenStub_AddParam(CFE_EVS_SendEventWithAppID, const char *, Spec);

Expand All @@ -129,15 +130,16 @@ CFE_Status_t CFE_EVS_SendEventWithAppID(uint16 EventID, uint16 EventType, CFE_ES
* Generated stub function for CFE_EVS_SendTimedEvent()
* ----------------------------------------------------
*/
CFE_Status_t CFE_EVS_SendTimedEvent(CFE_TIME_SysTime_t Time, uint16 EventID, uint16 EventType, const char *Spec, ...)
CFE_Status_t CFE_EVS_SendTimedEvent(CFE_TIME_SysTime_t Time, uint16 EventID, CFE_EVS_EventType_Enum_t EventType,
const char *Spec, ...)
{
va_list UtStub_ArgList;

UT_GenStub_SetupReturnBuffer(CFE_EVS_SendTimedEvent, CFE_Status_t);

UT_GenStub_AddParam(CFE_EVS_SendTimedEvent, CFE_TIME_SysTime_t, Time);
UT_GenStub_AddParam(CFE_EVS_SendTimedEvent, uint16, EventID);
UT_GenStub_AddParam(CFE_EVS_SendTimedEvent, uint16, EventType);
UT_GenStub_AddParam(CFE_EVS_SendTimedEvent, CFE_EVS_EventType_Enum_t, EventType);
UT_GenStub_AddParam(CFE_EVS_SendTimedEvent, const char *, Spec);

va_start(UtStub_ArgList, Spec);
Expand Down
80 changes: 35 additions & 45 deletions modules/es/fsw/src/cfe_es_apps.c
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,8 @@ void CFE_ES_StartApplications(uint32 ResetType, const char *StartFilePath)
*-----------------------------------------------------------------*/
int32 CFE_ES_ParseFileEntry(const char **TokenList, uint32 NumTokens)
{
const char * ModuleName;
const char * EntryType;
const char *ModuleName;
const char *EntryType;
unsigned long ParsedValue;
union
{
Expand Down Expand Up @@ -495,7 +495,7 @@ int32 CFE_ES_LoadModule(CFE_ResourceId_t ParentResourceId, const char *ModuleNam
*-----------------------------------------------------------------*/
int32 CFE_ES_GetTaskFunction(CFE_ES_TaskEntryFuncPtr_t *FuncPtr)
{
CFE_ES_TaskRecord_t * TaskRecPtr;
CFE_ES_TaskRecord_t *TaskRecPtr;
CFE_ES_TaskEntryFuncPtr_t EntryFunc;
int32 ReturnCode;
int32 Timeout;
Expand Down Expand Up @@ -656,6 +656,7 @@ int32 CFE_ES_StartAppTask(CFE_ES_TaskId_t *TaskIdPtr, const char *TaskName, CFE_
int32 CFE_ES_AppCreate(CFE_ES_AppId_t *ApplicationIdPtr, const char *AppName, const CFE_ES_AppStartParams_t *Params)
{
CFE_Status_t Status;
CFE_Status_t CleanupStatus;
CFE_ES_AppRecord_t *AppRecPtr;
CFE_ResourceId_t PendingResourceId = CFE_RESOURCEID_UNDEFINED;

Expand Down Expand Up @@ -799,6 +800,16 @@ int32 CFE_ES_AppCreate(CFE_ES_AppId_t *ApplicationIdPtr, const char *AppName, co
/*
* Set the table entry back to free
*/
if (OS_ObjectIdDefined(AppRecPtr->LoadStatus.ModuleId))
{
CleanupStatus = OS_ModuleUnload(AppRecPtr->LoadStatus.ModuleId);
if (CleanupStatus != OS_SUCCESS)
{
CFE_ES_WriteToSysLog("%s: Module (ID:0x%08lX) Unload failed. RC=%ld\n", __func__,
OS_ObjectIdToInteger(AppRecPtr->LoadStatus.ModuleId), (long)CleanupStatus);
}
}

CFE_ES_AppRecordSetFree(AppRecPtr);
PendingResourceId = CFE_RESOURCEID_UNDEFINED;
}
Expand All @@ -819,7 +830,7 @@ int32 CFE_ES_AppCreate(CFE_ES_AppId_t *ApplicationIdPtr, const char *AppName, co
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;
CFE_ES_LibRecord_t *LibSlotPtr;
int32 Status;
CFE_ResourceId_t PendingResourceId;

Expand Down Expand Up @@ -974,7 +985,7 @@ bool CFE_ES_RunAppTableScan(uint32 ElapsedTime, void *Arg)
{
CFE_ES_AppTableScanState_t *State = (CFE_ES_AppTableScanState_t *)Arg;
uint32 i;
CFE_ES_AppRecord_t * AppPtr;
CFE_ES_AppRecord_t *AppPtr;
CFE_ES_AppId_t AppTimeoutList[CFE_PLATFORM_ES_MAX_APPLICATIONS];
uint32 NumAppTimeouts;

Expand Down Expand Up @@ -1092,14 +1103,14 @@ bool CFE_ES_RunAppTableScan(uint32 ElapsedTime, void *Arg)
*-----------------------------------------------------------------*/
void CFE_ES_ProcessControlRequest(CFE_ES_AppId_t AppId)
{
CFE_ES_AppRecord_t * AppRecPtr;
CFE_ES_AppRecord_t *AppRecPtr;
uint32 PendingControlReq;
CFE_ES_AppStartParams_t RestartParams;
char OrigAppName[OS_MAX_API_NAME];
CFE_Status_t CleanupStatus;
CFE_Status_t StartupStatus;
CFE_ES_AppId_t NewAppId;
const char * ReqName;
const char *ReqName;
char MessageDetail[48];
uint16 EventID;
CFE_EVS_EventType_Enum_t EventType;
Expand Down Expand Up @@ -1325,8 +1336,8 @@ int32 CFE_ES_CleanUpApp(CFE_ES_AppId_t AppId)
osal_id_t ModuleId;
uint32 NumTasks;
uint32 NumPools;
CFE_ES_AppRecord_t * AppRecPtr;
CFE_ES_TaskRecord_t * TaskRecPtr;
CFE_ES_AppRecord_t *AppRecPtr;
CFE_ES_TaskRecord_t *TaskRecPtr;
CFE_ES_MemPoolRecord_t *MemPoolRecPtr;

NumTasks = 0;
Expand Down Expand Up @@ -1565,6 +1576,7 @@ void CFE_ES_CleanupObjectCallback(osal_id_t ObjectId, void *arg)
int32 OsStatus;
osal_objtype_t ObjType;
bool ObjIsValid;
CFE_Status_t ErrorStatus = CFE_ES_APP_CLEANUP_ERR; // Set default cFE error status

CleanState = (CFE_ES_CleanupState_t *)arg;
ObjIsValid = true;
Expand All @@ -1573,22 +1585,28 @@ void CFE_ES_CleanupObjectCallback(osal_id_t ObjectId, void *arg)
switch (ObjType)
{
case OS_OBJECT_TYPE_OS_TASK:
OsStatus = OS_TaskDelete(ObjectId);
OsStatus = OS_TaskDelete(ObjectId);
ErrorStatus = CFE_ES_ERR_CHILD_TASK_DELETE;
break;
case OS_OBJECT_TYPE_OS_QUEUE:
OsStatus = OS_QueueDelete(ObjectId);
OsStatus = OS_QueueDelete(ObjectId);
ErrorStatus = CFE_ES_QUEUE_DELETE_ERR;
break;
case OS_OBJECT_TYPE_OS_BINSEM:
OsStatus = OS_BinSemDelete(ObjectId);
OsStatus = OS_BinSemDelete(ObjectId);
ErrorStatus = CFE_ES_BIN_SEM_DELETE_ERR;
break;
case OS_OBJECT_TYPE_OS_COUNTSEM:
OsStatus = OS_CountSemDelete(ObjectId);
OsStatus = OS_CountSemDelete(ObjectId);
ErrorStatus = CFE_ES_COUNT_SEM_DELETE_ERR;
break;
case OS_OBJECT_TYPE_OS_MUTEX:
OsStatus = OS_MutSemDelete(ObjectId);
OsStatus = OS_MutSemDelete(ObjectId);
ErrorStatus = CFE_ES_MUT_SEM_DELETE_ERR;
break;
case OS_OBJECT_TYPE_OS_TIMECB:
OsStatus = OS_TimerDelete(ObjectId);
OsStatus = OS_TimerDelete(ObjectId);
ErrorStatus = CFE_ES_TIMER_DELETE_ERR;
break;
case OS_OBJECT_TYPE_OS_STREAM:
OsStatus = OS_close(ObjectId);
Expand All @@ -1615,36 +1633,8 @@ void CFE_ES_CleanupObjectCallback(osal_id_t ObjectId, void *arg)
OS_ObjectIdToInteger(ObjectId), (long)OsStatus);
if (CleanState->OverallStatus == CFE_SUCCESS)
{
/*
* Translate any OS failures into the appropriate CFE_ES return codes
* (Some object types have special return codes, depending on what type
* of object failed to delete)
*/
switch (ObjType)
{
case OS_OBJECT_TYPE_OS_TASK:
CleanState->OverallStatus = CFE_ES_ERR_CHILD_TASK_DELETE;
break;
case OS_OBJECT_TYPE_OS_QUEUE:
CleanState->OverallStatus = CFE_ES_QUEUE_DELETE_ERR;
break;
case OS_OBJECT_TYPE_OS_BINSEM:
CleanState->OverallStatus = CFE_ES_BIN_SEM_DELETE_ERR;
break;
case OS_OBJECT_TYPE_OS_COUNTSEM:
CleanState->OverallStatus = CFE_ES_COUNT_SEM_DELETE_ERR;
break;
case OS_OBJECT_TYPE_OS_MUTEX:
CleanState->OverallStatus = CFE_ES_MUT_SEM_DELETE_ERR;
break;
case OS_OBJECT_TYPE_OS_TIMECB:
CleanState->OverallStatus = CFE_ES_TIMER_DELETE_ERR;
break;
default:
/* generic failure */
CleanState->OverallStatus = CFE_ES_APP_CLEANUP_ERR;
break;
}
// Save the object-type-specific error status that was set earlier in the switch statement
CleanState->OverallStatus = ErrorStatus;
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions modules/evs/config/default_cfe_evs_msgdefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,10 +222,10 @@ typedef struct CFE_EVS_HousekeepingTlm_Payload
typedef struct CFE_EVS_PacketID
{
char AppName[CFE_MISSION_MAX_API_LEN]; /**< \cfetlmmnemonic \EVS_APPNAME
\brief Application name */
\brief Application name */
uint16 EventID; /**< \cfetlmmnemonic \EVS_EVENTID
\brief Numerical event identifier */
uint16 EventType; /**< \cfetlmmnemonic \EVS_EVENTTYPE
CFE_EVS_EventType_Enum_t EventType; /**< \cfetlmmnemonic \EVS_EVENTTYPE
\brief Numerical event type identifier */
uint32 SpacecraftID; /**< \cfetlmmnemonic \EVS_SCID
\brief Spacecraft identifier */
Expand Down
8 changes: 5 additions & 3 deletions modules/evs/fsw/src/cfe_evs.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ CFE_Status_t CFE_EVS_Register(const void *Filters, uint16 NumEventFilters, uint1
* See description in header file for argument/return detail
*
*-----------------------------------------------------------------*/
CFE_Status_t CFE_EVS_SendEvent(uint16 EventID, uint16 EventType, const char *Spec, ...)
CFE_Status_t CFE_EVS_SendEvent(uint16 EventID, CFE_EVS_EventType_Enum_t EventType, const char *Spec, ...)
{
int32 Status;
CFE_ES_AppId_t AppID;
Expand Down Expand Up @@ -166,7 +166,8 @@ CFE_Status_t CFE_EVS_SendEvent(uint16 EventID, uint16 EventType, const char *Spe
* See description in header file for argument/return detail
*
*-----------------------------------------------------------------*/
CFE_Status_t CFE_EVS_SendEventWithAppID(uint16 EventID, uint16 EventType, CFE_ES_AppId_t AppID, const char *Spec, ...)
CFE_Status_t CFE_EVS_SendEventWithAppID(uint16 EventID, CFE_EVS_EventType_Enum_t EventType, CFE_ES_AppId_t AppID,
const char *Spec, ...)
{
int32 Status = CFE_SUCCESS;
CFE_TIME_SysTime_t Time;
Expand Down Expand Up @@ -215,7 +216,8 @@ CFE_Status_t CFE_EVS_SendEventWithAppID(uint16 EventID, uint16 EventType, CFE_ES
* See description in header file for argument/return detail
*
*-----------------------------------------------------------------*/
CFE_Status_t CFE_EVS_SendTimedEvent(CFE_TIME_SysTime_t Time, uint16 EventID, uint16 EventType, const char *Spec, ...)
CFE_Status_t CFE_EVS_SendTimedEvent(CFE_TIME_SysTime_t Time, uint16 EventID, CFE_EVS_EventType_Enum_t EventType,
const char *Spec, ...)
{
int32 Status;
CFE_ES_AppId_t AppID;
Expand Down
15 changes: 5 additions & 10 deletions modules/evs/fsw/src/cfe_evs_log.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,15 @@ void EVS_AddLog(CFE_EVS_LongEventTlm_t *EVS_PktPtr)
/* Serialize access to event log control variables */
OS_MutSemTake(CFE_EVS_Global.EVS_SharedDataMutexID);

if ((CFE_EVS_Global.EVS_LogPtr->LogFullFlag == true) &&
(CFE_EVS_Global.EVS_LogPtr->LogMode == CFE_EVS_LogMode_DISCARD))
if (CFE_EVS_Global.EVS_LogPtr->LogFullFlag == true)
{
/* If log is full and in discard mode, just count the event */
CFE_EVS_Global.EVS_LogPtr->LogOverflowCounter++;
}
else
{
if (CFE_EVS_Global.EVS_LogPtr->LogFullFlag == true)
{
/* If log is full and in wrap mode, count it and store it */
CFE_EVS_Global.EVS_LogPtr->LogOverflowCounter++;
}

/* If the log is not full, _or_ if it is in OVERWRITE mode, add the event to the log */
if ((CFE_EVS_Global.EVS_LogPtr->LogFullFlag == false) ||
(CFE_EVS_Global.EVS_LogPtr->LogMode == CFE_EVS_LogMode_OVERWRITE))
{
/* Copy the event data to the next available entry in the log */
memcpy(&CFE_EVS_Global.EVS_LogPtr->LogEntry[CFE_EVS_Global.EVS_LogPtr->Next], EVS_PktPtr, sizeof(*EVS_PktPtr));

Expand Down
6 changes: 3 additions & 3 deletions modules/evs/fsw/src/cfe_evs_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ int32 EVS_NotRegistered(EVS_AppData_t *AppDataPtr, CFE_ES_AppId_t CallerID)
* See description in header file for argument/return detail
*
*-----------------------------------------------------------------*/
bool EVS_IsFiltered(EVS_AppData_t *AppDataPtr, uint16 EventID, uint16 EventType)
bool EVS_IsFiltered(EVS_AppData_t *AppDataPtr, uint16 EventID, CFE_EVS_EventType_Enum_t EventType)
{
EVS_BinFilter_t *FilterPtr;
bool Filtered = false;
Expand Down Expand Up @@ -449,7 +449,7 @@ void EVS_DisableTypes(EVS_AppData_t *AppDataPtr, uint8 BitMask)
* See description in header file for argument/return detail
*
*-----------------------------------------------------------------*/
void EVS_GenerateEventTelemetry(EVS_AppData_t *AppDataPtr, uint16 EventID, uint16 EventType,
void EVS_GenerateEventTelemetry(EVS_AppData_t *AppDataPtr, uint16 EventID, CFE_EVS_EventType_Enum_t EventType,
const CFE_TIME_SysTime_t *TimeStamp, const char *MsgSpec, va_list ArgPtr)
{
CFE_EVS_LongEventTlm_t LongEventTlm; /* The "long" flavor is always generated, as this is what is logged */
Expand Down Expand Up @@ -592,7 +592,7 @@ void EVS_OutputPort(uint8 PortNum, char *Message)
* See description in header file for argument/return detail
*
*-----------------------------------------------------------------*/
int32 EVS_SendEvent(uint16 EventID, uint16 EventType, const char *Spec, ...)
int32 EVS_SendEvent(uint16 EventID, CFE_EVS_EventType_Enum_t EventType, const char *Spec, ...)
{
CFE_TIME_SysTime_t Time;
va_list Ptr;
Expand Down
Loading

0 comments on commit 2f0e1a2

Please sign in to comment.