From 550e7f7dd349c24e234ab0881f5c13e667f05741 Mon Sep 17 00:00:00 2001 From: Joseph Hickey Date: Fri, 16 Jun 2023 16:18:05 -0400 Subject: [PATCH] Fix #2378, refactor SB to support additional use cases Cleans up the internal SB implementation so it can better support future enhancements such as message integrity, additional header fields and timestamping. --- modules/sb/fsw/src/cfe_sb_api.c | 844 +++--------------------- modules/sb/fsw/src/cfe_sb_buf.c | 1 - modules/sb/fsw/src/cfe_sb_priv.c | 1060 +++++++++++++++++++++++++++++- modules/sb/fsw/src/cfe_sb_priv.h | 597 ++++++++++++++--- modules/sb/ut-coverage/sb_UT.c | 1017 ++++++++++++++++------------ modules/sb/ut-coverage/sb_UT.h | 43 +- 6 files changed, 2289 insertions(+), 1273 deletions(-) diff --git a/modules/sb/fsw/src/cfe_sb_api.c b/modules/sb/fsw/src/cfe_sb_api.c index bc4280ece..89a1e8caf 100644 --- a/modules/sb/fsw/src/cfe_sb_api.c +++ b/modules/sb/fsw/src/cfe_sb_api.c @@ -1282,464 +1282,6 @@ int32 CFE_SB_UnsubscribeFull(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId, uint8 return Status; } -/*---------------------------------------------------------------- - * - * Implemented per public API - * See description in header file for argument/return detail - * - *-----------------------------------------------------------------*/ -CFE_Status_t CFE_SB_TransmitMsg(const CFE_MSG_Message_t *MsgPtr, bool IsOrigination) -{ - int32 Status; - CFE_MSG_Size_t Size = 0; - CFE_SB_MsgId_t MsgId = CFE_SB_INVALID_MSG_ID; - CFE_ES_TaskId_t TskId; - char FullName[(OS_MAX_API_NAME * 2)]; - CFE_SB_BufferD_t *BufDscPtr; - CFE_SBR_RouteId_t RouteId; - uint16 PendingEventID; - - PendingEventID = 0; - BufDscPtr = NULL; - RouteId = CFE_SBR_INVALID_ROUTE_ID; - - Status = CFE_SB_TransmitMsgValidate(MsgPtr, &MsgId, &Size, &RouteId); - - CFE_SB_LockSharedData(__func__, __LINE__); - - if (Status == CFE_SUCCESS && CFE_SBR_IsValidRouteId(RouteId)) - { - /* Get buffer - note this pre-initializes the returned buffer with - * a use count of 1, which refers to this task as it fills the buffer. */ - BufDscPtr = CFE_SB_GetBufferFromPool(Size); - if (BufDscPtr == NULL) - { - PendingEventID = CFE_SB_GET_BUF_ERR_EID; - Status = CFE_SB_BUF_ALOC_ERR; - } - } - - /* - * Increment the MsgSendErrorCounter only if there was a real error, - * such as a validation issue or failure to allocate a buffer. - * - * (This should NOT be done if simply no route) - */ - if (Status != CFE_SUCCESS) - { - CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter++; - } - - CFE_SB_UnlockSharedData(__func__, __LINE__); - - /* - * If a buffer was obtained above, then copy the content into it - * and broadcast it to all subscribers in the route. - * - * Note - if there is no route / no subscribers, the "Status" will - * be CFE_SUCCESS because CFE_SB_TransmitMsgValidate() succeeded, - * but there will be no buffer because CFE_SBR_IsValidRouteId() returned - * false. - * - * But if the descriptor is non-null it means the message is valid and - * there is a route to send it to. - */ - if (BufDscPtr != NULL) - { - /* Copy actual message content into buffer and set its metadata */ - memcpy(&BufDscPtr->Content, MsgPtr, Size); - BufDscPtr->MsgId = MsgId; - BufDscPtr->ContentSize = Size; - BufDscPtr->NeedsUpdate = IsOrigination; - CFE_MSG_GetType(MsgPtr, &BufDscPtr->ContentType); - - /* - * This routine will use best-effort to send to all subscribers, - * increment the buffer use count for every successful delivery, - * and send an event/increment counter for any unsuccessful delivery. - */ - CFE_SB_BroadcastBufferToRoute(BufDscPtr, RouteId); - - /* - * The broadcast function consumes the buffer, so it should not be - * accessed in this function anymore - */ - BufDscPtr = NULL; - } - - if (PendingEventID == CFE_SB_GET_BUF_ERR_EID) - { - /* Get task id for events and Sender Info*/ - CFE_ES_GetTaskID(&TskId); - - if (CFE_SB_RequestToSendEvent(TskId, CFE_SB_GET_BUF_ERR_EID_BIT) == CFE_SB_GRANTED) - { - CFE_EVS_SendEventWithAppID(CFE_SB_GET_BUF_ERR_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Send Err:Request for Buffer Failed. MsgId 0x%x,app %s,size %d", - (unsigned int)CFE_SB_MsgIdToValue(MsgId), CFE_SB_GetAppTskName(TskId, FullName), - (int)Size); - - /* clear the bit so the task may send this event again */ - CFE_SB_FinishSendEvent(TskId, CFE_SB_GET_BUF_ERR_EID_BIT); - } - } - - return Status; -} - -/*---------------------------------------------------------------- - * - * Application-scope internal function - * See description in header file for argument/return detail - * - *-----------------------------------------------------------------*/ -int32 CFE_SB_TransmitMsgValidate(const CFE_MSG_Message_t *MsgPtr, CFE_SB_MsgId_t *MsgIdPtr, CFE_MSG_Size_t *SizePtr, - CFE_SBR_RouteId_t *RouteIdPtr) -{ - CFE_ES_TaskId_t TskId; - char FullName[(OS_MAX_API_NAME * 2)]; - uint16 PendingEventID; - int32 Status; - - PendingEventID = 0; - Status = CFE_SUCCESS; - - /* check input parameter */ - if (MsgPtr == NULL) - { - PendingEventID = CFE_SB_SEND_BAD_ARG_EID; - Status = CFE_SB_BAD_ARGUMENT; - } - - if (Status == CFE_SUCCESS) - { - CFE_MSG_GetMsgId(MsgPtr, MsgIdPtr); - - /* validate the msgid in the message */ - if (!CFE_SB_IsValidMsgId(*MsgIdPtr)) - { - PendingEventID = CFE_SB_SEND_INV_MSGID_EID; - Status = CFE_SB_BAD_ARGUMENT; - } - } - - if (Status == CFE_SUCCESS) - { - CFE_MSG_GetSize(MsgPtr, SizePtr); - - /* Verify the size of the pkt is < or = the mission defined max */ - if (*SizePtr > CFE_MISSION_SB_MAX_SB_MSG_SIZE) - { - PendingEventID = CFE_SB_MSG_TOO_BIG_EID; - Status = CFE_SB_MSG_TOO_BIG; - } - } - - if (Status == CFE_SUCCESS) - { - /* check the route, which should be done while locked */ - CFE_SB_LockSharedData(__func__, __LINE__); - - /* Get the routing id */ - *RouteIdPtr = CFE_SBR_GetRouteId(*MsgIdPtr); - - /* if there have been no subscriptions for this pkt, */ - /* increment the dropped pkt cnt, send event and return success */ - if (!CFE_SBR_IsValidRouteId(*RouteIdPtr)) - { - CFE_SB_Global.HKTlmMsg.Payload.NoSubscribersCounter++; - PendingEventID = CFE_SB_SEND_NO_SUBS_EID; - } - - CFE_SB_UnlockSharedData(__func__, __LINE__); - } - - if (PendingEventID != 0) - { - /* get task id for events */ - CFE_ES_GetTaskID(&TskId); - - switch (PendingEventID) - { - case CFE_SB_SEND_BAD_ARG_EID: - if (CFE_SB_RequestToSendEvent(TskId, CFE_SB_SEND_BAD_ARG_EID_BIT) == CFE_SB_GRANTED) - { - CFE_EVS_SendEventWithAppID(CFE_SB_SEND_BAD_ARG_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Send Err:Bad input argument,Arg 0x%lx,App %s", (unsigned long)MsgPtr, - CFE_SB_GetAppTskName(TskId, FullName)); - - /* clear the bit so the task may send this event again */ - CFE_SB_FinishSendEvent(TskId, CFE_SB_SEND_BAD_ARG_EID_BIT); - } - break; - - case CFE_SB_SEND_INV_MSGID_EID: - if (CFE_SB_RequestToSendEvent(TskId, CFE_SB_SEND_INV_MSGID_EID_BIT) == CFE_SB_GRANTED) - { - CFE_EVS_SendEventWithAppID(CFE_SB_SEND_INV_MSGID_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Send Err:Invalid MsgId(0x%x)in msg,App %s", - (unsigned int)CFE_SB_MsgIdToValue(*MsgIdPtr), - CFE_SB_GetAppTskName(TskId, FullName)); - - /* clear the bit so the task may send this event again */ - CFE_SB_FinishSendEvent(TskId, CFE_SB_SEND_INV_MSGID_EID_BIT); - } - break; - - case CFE_SB_MSG_TOO_BIG_EID: - if (CFE_SB_RequestToSendEvent(TskId, CFE_SB_MSG_TOO_BIG_EID_BIT) == CFE_SB_GRANTED) - { - CFE_EVS_SendEventWithAppID(CFE_SB_MSG_TOO_BIG_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Send Err:Msg Too Big MsgId=0x%x,app=%s,size=%d,MaxSz=%d", - (unsigned int)CFE_SB_MsgIdToValue(*MsgIdPtr), - CFE_SB_GetAppTskName(TskId, FullName), (int)*SizePtr, - CFE_MISSION_SB_MAX_SB_MSG_SIZE); - - /* clear the bit so the task may send this event again */ - CFE_SB_FinishSendEvent(TskId, CFE_SB_MSG_TOO_BIG_EID_BIT); - } - break; - - case CFE_SB_SEND_NO_SUBS_EID: - /* Determine if event can be sent without causing recursive event problem */ - if (CFE_SB_RequestToSendEvent(TskId, CFE_SB_SEND_NO_SUBS_EID_BIT) == CFE_SB_GRANTED) - { - CFE_EVS_SendEventWithAppID(CFE_SB_SEND_NO_SUBS_EID, CFE_EVS_EventType_INFORMATION, - CFE_SB_Global.AppId, "No subscribers for MsgId 0x%x,sender %s", - (unsigned int)CFE_SB_MsgIdToValue(*MsgIdPtr), - CFE_SB_GetAppTskName(TskId, FullName)); - - /* clear the bit so the task may send this event again */ - CFE_SB_FinishSendEvent(TskId, CFE_SB_SEND_NO_SUBS_EID_BIT); - } - break; - } - } - - return Status; -} - -/*---------------------------------------------------------------- - * - * Application-scope internal function - * See description in header file for argument/return detail - * - *-----------------------------------------------------------------*/ -void CFE_SB_BroadcastBufferToRoute(CFE_SB_BufferD_t *BufDscPtr, CFE_SBR_RouteId_t RouteId) -{ - CFE_ES_AppId_t AppId; - CFE_ES_TaskId_t TskId; - CFE_SB_DestinationD_t *DestPtr; - CFE_SB_PipeD_t * PipeDscPtr; - CFE_SB_EventBuf_t SBSndErr; - int32 OsStatus; - uint32 i; - char FullName[(OS_MAX_API_NAME * 2)]; - char PipeName[OS_MAX_API_NAME]; - bool IsAcceptable; - - SBSndErr.EvtsToSnd = 0; - IsAcceptable = true; - - /* get app id for loopback testing */ - CFE_ES_GetAppID(&AppId); - - /* get task id for events and Sender Info*/ - CFE_ES_GetTaskID(&TskId); - - /* take semaphore to prevent a task switch during processing */ - CFE_SB_LockSharedData(__func__, __LINE__); - - /* For an invalid route / no subscribers this whole logic can be skipped */ - if (CFE_SBR_IsValidRouteId(RouteId)) - { - /* Set the seq count if requested (while locked) before actually sending */ - if (BufDscPtr->NeedsUpdate) - { - CFE_SBR_IncrementSequenceCounter(RouteId); - - CFE_MSG_SetSequenceCount(&BufDscPtr->Content.Msg, CFE_SBR_GetSequenceCounter(RouteId)); - - /* Update all MSG headers based on the current sequence */ - CFE_MSG_OriginationAction(&BufDscPtr->Content.Msg, BufDscPtr->ContentSize, &IsAcceptable); - - /* Clear the flag, just in case */ - BufDscPtr->NeedsUpdate = false; - } - - /* Send the packet to all destinations */ - for (DestPtr = CFE_SBR_GetDestListHeadPtr(RouteId); DestPtr != NULL; DestPtr = DestPtr->Next) - { - if (DestPtr->Active == CFE_SB_ACTIVE) /* destination is active */ - { - PipeDscPtr = CFE_SB_LocatePipeDescByID(DestPtr->PipeId); - } - else - { - PipeDscPtr = NULL; - } - - if (!CFE_SB_PipeDescIsMatch(PipeDscPtr, DestPtr->PipeId)) - { - continue; - } - - if ((PipeDscPtr->Opts & CFE_SB_PIPEOPTS_IGNOREMINE) != 0 && - CFE_RESOURCEID_TEST_EQUAL(PipeDscPtr->AppId, AppId)) - { - continue; - } - - /* if Msg limit exceeded, log event, increment counter */ - /* and go to next destination */ - if (DestPtr->BuffCount >= DestPtr->MsgId2PipeLim) - { - SBSndErr.EvtBuf[SBSndErr.EvtsToSnd].PipeId = DestPtr->PipeId; - SBSndErr.EvtBuf[SBSndErr.EvtsToSnd].EventId = CFE_SB_MSGID_LIM_ERR_EID; - SBSndErr.EvtsToSnd++; - CFE_SB_Global.HKTlmMsg.Payload.MsgLimitErrorCounter++; - PipeDscPtr->SendErrors++; - - continue; - } - - /* - ** Write the buffer descriptor to the queue of the pipe. If the write - ** failed, log info and increment the pipe's error counter. - */ - OsStatus = OS_QueuePut(PipeDscPtr->SysQueueId, &BufDscPtr, sizeof(BufDscPtr), 0); - - if (OsStatus == OS_SUCCESS) - { - /* The queue now holds a ref to the buffer, so increment its ref count. */ - CFE_SB_IncrBufUseCnt(BufDscPtr); - - DestPtr->BuffCount++; /* used for checking MsgId2PipeLimit */ - DestPtr->DestCnt++; /* used for statistics */ - ++PipeDscPtr->CurrentQueueDepth; - if (PipeDscPtr->CurrentQueueDepth >= PipeDscPtr->PeakQueueDepth) - { - PipeDscPtr->PeakQueueDepth = PipeDscPtr->CurrentQueueDepth; - } - } - else - { - SBSndErr.EvtBuf[SBSndErr.EvtsToSnd].PipeId = DestPtr->PipeId; - if (OsStatus == OS_QUEUE_FULL) - { - SBSndErr.EvtBuf[SBSndErr.EvtsToSnd].EventId = CFE_SB_Q_FULL_ERR_EID; - CFE_SB_Global.HKTlmMsg.Payload.PipeOverflowErrorCounter++; - } - else - { - /* Unexpected error while writing to queue. */ - SBSndErr.EvtBuf[SBSndErr.EvtsToSnd].EventId = CFE_SB_Q_WR_ERR_EID; - SBSndErr.EvtBuf[SBSndErr.EvtsToSnd].OsStatus = OsStatus; - CFE_SB_Global.HKTlmMsg.Payload.InternalErrorCounter++; - } - SBSndErr.EvtsToSnd++; - PipeDscPtr->SendErrors++; - } /*end if */ - - } /* end loop over destinations */ - } - - /* - * If any specific delivery issues occurred, also increment the - * general error count before releasing the lock. - */ - if (SBSndErr.EvtsToSnd > 0) - { - CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter++; - } - - /* - * Remove this from whatever list it was in - * - * If it was a singleton/new buffer this has no effect. - * If it was a zero-copy buffer this removes it from the ZeroCopyList. - */ - CFE_SB_TrackingListRemove(&BufDscPtr->Link); - - /* clear the AppID field in case it was a zero copy buffer, - * as it is no longer owned by that app after broadcasting */ - BufDscPtr->AppId = CFE_ES_APPID_UNDEFINED; - - /* track the buffer as an in-transit message */ - CFE_SB_TrackingListAdd(&CFE_SB_Global.InTransitList, &BufDscPtr->Link); - - /* - ** Decrement the buffer UseCount and free buffer if cnt=0. This decrement is done - ** because the use cnt is initialized to 1 in CFE_SB_GetBufferFromPool. - ** Initializing the count to 1 (as opposed to zero) and decrementing it here are - ** done to ensure the buffer gets released when there are destinations that have - ** been disabled via ground command. - */ - CFE_SB_DecrBufUseCnt(BufDscPtr); - - /* release the semaphore */ - CFE_SB_UnlockSharedData(__func__, __LINE__); - - /* send an event for each pipe write error that may have occurred */ - for (i = 0; i < SBSndErr.EvtsToSnd; i++) - { - if (SBSndErr.EvtBuf[i].EventId == CFE_SB_MSGID_LIM_ERR_EID) - { - /* Determine if event can be sent without causing recursive event problem */ - if (CFE_SB_RequestToSendEvent(TskId, CFE_SB_MSGID_LIM_ERR_EID_BIT) == CFE_SB_GRANTED) - { - CFE_SB_GetPipeName(PipeName, sizeof(PipeName), SBSndErr.EvtBuf[i].PipeId); - - CFE_ES_PerfLogEntry(CFE_MISSION_SB_MSG_LIM_PERF_ID); - CFE_ES_PerfLogExit(CFE_MISSION_SB_MSG_LIM_PERF_ID); - - CFE_EVS_SendEventWithAppID(CFE_SB_MSGID_LIM_ERR_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Msg Limit Err,MsgId 0x%x,pipe %s,sender %s", - (unsigned int)CFE_SB_MsgIdToValue(BufDscPtr->MsgId), PipeName, - CFE_SB_GetAppTskName(TskId, FullName)); - - /* clear the bit so the task may send this event again */ - CFE_SB_FinishSendEvent(TskId, CFE_SB_MSGID_LIM_ERR_EID_BIT); - } - } - else if (SBSndErr.EvtBuf[i].EventId == CFE_SB_Q_FULL_ERR_EID) - { - /* Determine if event can be sent without causing recursive event problem */ - if (CFE_SB_RequestToSendEvent(TskId, CFE_SB_Q_FULL_ERR_EID_BIT) == CFE_SB_GRANTED) - { - CFE_SB_GetPipeName(PipeName, sizeof(PipeName), SBSndErr.EvtBuf[i].PipeId); - - CFE_ES_PerfLogEntry(CFE_MISSION_SB_PIPE_OFLOW_PERF_ID); - CFE_ES_PerfLogExit(CFE_MISSION_SB_PIPE_OFLOW_PERF_ID); - - CFE_EVS_SendEventWithAppID(CFE_SB_Q_FULL_ERR_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Pipe Overflow,MsgId 0x%x,pipe %s,sender %s", - (unsigned int)CFE_SB_MsgIdToValue(BufDscPtr->MsgId), PipeName, - CFE_SB_GetAppTskName(TskId, FullName)); - - /* clear the bit so the task may send this event again */ - CFE_SB_FinishSendEvent(TskId, CFE_SB_Q_FULL_ERR_EID_BIT); - } - } - else - { - /* Determine if event can be sent without causing recursive event problem */ - if (CFE_SB_RequestToSendEvent(TskId, CFE_SB_Q_WR_ERR_EID_BIT) == CFE_SB_GRANTED) - { - CFE_SB_GetPipeName(PipeName, sizeof(PipeName), SBSndErr.EvtBuf[i].PipeId); - - CFE_EVS_SendEventWithAppID(CFE_SB_Q_WR_ERR_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Pipe Write Err,MsgId 0x%x,pipe %s,sender %s,stat %ld", - (unsigned int)CFE_SB_MsgIdToValue(BufDscPtr->MsgId), PipeName, - CFE_SB_GetAppTskName(TskId, FullName), (long)(SBSndErr.EvtBuf[i].OsStatus)); - - /* clear the bit so the task may send this event again */ - CFE_SB_FinishSendEvent(TskId, CFE_SB_Q_WR_ERR_EID_BIT); - } - } - } -} - /*---------------------------------------------------------------- * * Implemented per public API @@ -1748,251 +1290,41 @@ void CFE_SB_BroadcastBufferToRoute(CFE_SB_BufferD_t *BufDscPtr, CFE_SBR_RouteId_ *-----------------------------------------------------------------*/ CFE_Status_t CFE_SB_ReceiveBuffer(CFE_SB_Buffer_t **BufPtr, CFE_SB_PipeId_t PipeId, int32 TimeOut) { - int32 Status; - int32 OsStatus; - CFE_SB_BufferD_t * BufDscPtr; - size_t BufDscSize; - CFE_SB_PipeD_t * PipeDscPtr; - CFE_SB_DestinationD_t *DestPtr; - CFE_SBR_RouteId_t RouteId; - CFE_ES_TaskId_t TskId; - uint16 PendingEventID; - osal_id_t SysQueueId; - int32 SysTimeout; - char FullName[(OS_MAX_API_NAME * 2)]; - - PendingEventID = 0; - Status = CFE_SUCCESS; - SysTimeout = OS_PEND; - SysQueueId = OS_OBJECT_ID_UNDEFINED; - PipeDscPtr = NULL; - BufDscPtr = NULL; - DestPtr = NULL; - BufDscSize = 0; - OsStatus = OS_SUCCESS; - - /* - * Check input args and see if any are bad, which require - * a "BAD_ARG_EID" to be generated. - * - * Also translate the timeout here. Timeouts greater than 0 - * may be passed to OSAL directly, but the two fixed constants - * CFE_SB_PEND_FOREVER and CFE_SB_POLL are checked explicitly, - * to maintain API independence - even though the values are - * currently defined the same. - */ + CFE_SB_ReceiveTxn_State_t TxnBuf; + CFE_SB_MessageTxn_State_t *Txn; - if (BufPtr == NULL) - { - PendingEventID = CFE_SB_RCV_BAD_ARG_EID; - Status = CFE_SB_BAD_ARGUMENT; - } - else if (TimeOut > 0) - { - /* time outs greater than 0 can be passed to OSAL directly */ - SysTimeout = TimeOut; - } - else if (TimeOut == CFE_SB_POLL) - { - SysTimeout = OS_CHECK; - } - else if (TimeOut != CFE_SB_PEND_FOREVER) - { - /* any other timeout value is invalid */ - PendingEventID = CFE_SB_RCV_BAD_ARG_EID; - Status = CFE_SB_BAD_ARGUMENT; - } + Txn = CFE_SB_ReceiveTxn_Init(&TxnBuf, BufPtr); - /* If OK, then lock and pull relevant info from Pipe Descriptor */ - if (Status == CFE_SUCCESS) + if (CFE_SB_MessageTxn_IsOK(Txn)) { - CFE_SB_LockSharedData(__func__, __LINE__); - - PipeDscPtr = CFE_SB_LocatePipeDescByID(PipeId); - - /* If the pipe does not exist or PipeId is out of range... */ - if (!CFE_SB_PipeDescIsMatch(PipeDscPtr, PipeId)) - { - PendingEventID = CFE_SB_BAD_PIPEID_EID; - Status = CFE_SB_BAD_ARGUMENT; - } - else - { - /* Grab the queue ID */ - SysQueueId = PipeDscPtr->SysQueueId; - - /* - * Un-reference any previous buffer from the last call. - * - * NOTE: This is historical behavior where apps call CFE_SB_ReceiveBuffer() - * in the loop within the app's main task. There is currently no separate - * API to "free" or unreference a buffer that was returned from SB. - * - * Instead, each time this function is invoked, it is implicitly interpreted - * as an indication that the caller is done with the previous buffer. - * - * Unfortunately this prevents pipe IDs from being serviced/shared across - * multiple child tasks in a worker pattern design. This may be changed - * in a future version of CFE to decouple these actions, to allow for - * multiple workers to service the same pipe. - */ - if (PipeDscPtr->LastBuffer != NULL) - { - /* Decrement the Buffer Use Count, which will Free buffer if it becomes 0 */ - CFE_SB_DecrBufUseCnt(PipeDscPtr->LastBuffer); - PipeDscPtr->LastBuffer = NULL; - } - } - - CFE_SB_UnlockSharedData(__func__, __LINE__); + CFE_SB_MessageTxn_SetTimeout(Txn, TimeOut); } - /* - * If everything validated, then proceed to get a buffer from the queue. - * This must be done OUTSIDE the SB lock, as this call likely blocks. - */ - if (Status == CFE_SUCCESS) + if (CFE_SB_MessageTxn_IsOK(Txn)) { - /* Read the buffer descriptor address from the queue. */ - OsStatus = OS_QueueGet(SysQueueId, &BufDscPtr, sizeof(BufDscPtr), &BufDscSize, SysTimeout); + CFE_SB_ReceiveTxn_SetPipeId(Txn, PipeId); /* - * translate the return value - - * - * CFE functions have their own set of RC values should not directly return OSAL codes - * The size should always match. If it does not, then generate CFE_SB_Q_RD_ERR_EID. + * Set the verify flag true by default - + * in the default impl verify is a no-op that always succeeds. + * If an actual implementation is provided, it will be used here. */ - if (OsStatus == OS_SUCCESS && BufDscPtr != NULL && BufDscSize == sizeof(BufDscPtr)) - { - /* Pass through */ - } - else if (OsStatus == OS_QUEUE_EMPTY) - { - /* normal if using CFE_SB_POLL */ - Status = CFE_SB_NO_MESSAGE; - } - else if (OsStatus == OS_QUEUE_TIMEOUT) - { - /* normal if using a nonzero timeout */ - Status = CFE_SB_TIME_OUT; - } - else - { - /* off-nominal condition, report an error event */ - PendingEventID = CFE_SB_Q_RD_ERR_EID; - Status = CFE_SB_PIPE_RD_ERR; - } + CFE_SB_MessageTxn_SetEndpoint(Txn, true); } - /* Now re-lock to store the buffer in the pipe descriptor */ - CFE_SB_LockSharedData(__func__, __LINE__); - - if (Status == CFE_SUCCESS) + if (BufPtr != NULL) { /* - * NOTE: This uses the same PipeDscPtr that was found earlier. - * Technically it is possible that the pipe was changed between now and then, - * but the current PipeID definition doesn't really allow this to be detected. + * Note - the API should qualify the parameter as "const", but this is + * kept non-const for backward compatibility. Callers should never write to + * the returned buffer, it is const in practice. */ - if (CFE_SB_PipeDescIsMatch(PipeDscPtr, PipeId)) - { - /* - ** Load the pipe tables 'CurrentBuff' with the buffer descriptor - ** ptr corresponding to the message just read. This is done so that - ** the buffer can be released on the next receive call for this pipe. - ** - ** This counts as a new reference as it is being stored in the PipeDsc - */ - CFE_SB_IncrBufUseCnt(BufDscPtr); - PipeDscPtr->LastBuffer = BufDscPtr; - - /* - * Also set the Receivers pointer to the address of the actual message - * (currently this is "borrowing" the ref above, not its own ref) - */ - *BufPtr = &BufDscPtr->Content; - - /* get pointer to destination to be used in decrementing msg limit cnt*/ - RouteId = CFE_SBR_GetRouteId(BufDscPtr->MsgId); - DestPtr = CFE_SB_GetDestPtr(RouteId, PipeId); - - /* - ** DestPtr would be NULL if the msg is unsubscribed to while it is on - ** the pipe. The BuffCount may be zero if the msg is unsubscribed to and - ** then resubscribed to while it is on the pipe. Both of these cases are - ** considered nominal and are handled by the code below. - */ - if (DestPtr != NULL && DestPtr->BuffCount > 0) - { - DestPtr->BuffCount--; - } - - if (PipeDscPtr->CurrentQueueDepth > 0) - { - --PipeDscPtr->CurrentQueueDepth; - } - } - else - { - /* should send the bad pipe ID event here too */ - PendingEventID = CFE_SB_BAD_PIPEID_EID; - Status = CFE_SB_PIPE_RD_ERR; - } - - /* Always decrement the use count, for the ref that was in the queue */ - CFE_SB_DecrBufUseCnt(BufDscPtr); - } - - /* Before unlocking, increment relevant error counter if needed */ - if (Status != CFE_SUCCESS && Status != CFE_SB_NO_MESSAGE && Status != CFE_SB_TIME_OUT) - { - if (PendingEventID == CFE_SB_RCV_BAD_ARG_EID || PendingEventID == CFE_SB_BAD_PIPEID_EID) - { - ++CFE_SB_Global.HKTlmMsg.Payload.MsgReceiveErrorCounter; - } - else - { - /* For any other unexpected error (e.g. CFE_SB_Q_RD_ERR_EID) */ - ++CFE_SB_Global.HKTlmMsg.Payload.InternalErrorCounter; - } + *BufPtr = (CFE_SB_Buffer_t *)CFE_SB_ReceiveTxn_Execute(Txn); } - CFE_SB_UnlockSharedData(__func__, __LINE__); - - /* Now actually send the event, after unlocking (do not call EVS with SB locked) */ - if (PendingEventID != 0) - { - /* get task id for events */ - CFE_ES_GetTaskID(&TskId); - - switch (PendingEventID) - { - case CFE_SB_Q_RD_ERR_EID: - CFE_EVS_SendEventWithAppID(CFE_SB_Q_RD_ERR_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Pipe Read Err,pipe %lu,app %s,stat %ld", CFE_RESOURCEID_TO_ULONG(PipeId), - CFE_SB_GetAppTskName(TskId, FullName), (long)OsStatus); - break; - case CFE_SB_RCV_BAD_ARG_EID: - CFE_EVS_SendEventWithAppID(CFE_SB_RCV_BAD_ARG_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Rcv Err:Bad Input Arg:BufPtr 0x%lx,pipe %lu,t/o %d,app %s", - (unsigned long)BufPtr, CFE_RESOURCEID_TO_ULONG(PipeId), (int)TimeOut, - CFE_SB_GetAppTskName(TskId, FullName)); - break; - case CFE_SB_BAD_PIPEID_EID: - CFE_EVS_SendEventWithAppID(CFE_SB_BAD_PIPEID_EID, CFE_EVS_EventType_ERROR, CFE_SB_Global.AppId, - "Rcv Err:PipeId %lu does not exist,app %s", CFE_RESOURCEID_TO_ULONG(PipeId), - CFE_SB_GetAppTskName(TskId, FullName)); - break; - } - } + CFE_SB_MessageTxn_ReportEvents(Txn); - /* If not successful, set the output pointer to NULL */ - if (Status != CFE_SUCCESS && BufPtr != NULL) - { - *BufPtr = NULL; - } - - return Status; + return CFE_SB_MessageTxn_GetStatus(Txn); } /*---------------------------------------------------------------- @@ -2055,13 +1387,14 @@ CFE_SB_Buffer_t *CFE_SB_AllocateMessageBuffer(size_t MsgSize) /*---------------------------------------------------------------- * - * Application-scope internal function + * Implemented per public API * See description in header file for argument/return detail * *-----------------------------------------------------------------*/ -int32 CFE_SB_ZeroCopyBufferValidate(CFE_SB_Buffer_t *BufPtr, CFE_SB_BufferD_t **BufDscPtr) +CFE_Status_t CFE_SB_ReleaseMessageBuffer(CFE_SB_Buffer_t *BufPtr) { - cpuaddr BufDscAddr; + CFE_SB_BufferD_t *BufDscPtr; + int32 Status; /* * Sanity Check that the pointers are not NULL @@ -2071,23 +1404,19 @@ int32 CFE_SB_ZeroCopyBufferValidate(CFE_SB_Buffer_t *BufPtr, CFE_SB_BufferD_t ** return CFE_SB_BAD_ARGUMENT; } - /* - * Calculate descriptor pointer from buffer pointer - - * The buffer is just a member (offset) in the descriptor - */ - BufDscAddr = (cpuaddr)BufPtr - offsetof(CFE_SB_BufferD_t, Content); - *BufDscPtr = (CFE_SB_BufferD_t *)BufDscAddr; + CFE_SB_LockSharedData(__func__, __LINE__); - /* - * Check that the descriptor is actually a "zero copy" type, - */ - if (!CFE_RESOURCEID_TEST_DEFINED((*BufDscPtr)->AppId)) + Status = CFE_SB_ZeroCopyBufferValidate(BufPtr, &BufDscPtr); + if (Status == CFE_SUCCESS) { - return CFE_SB_BUFFER_INVALID; + /* Clear the ownership app ID and decrement use count (may also free) */ + BufDscPtr->AppId = CFE_ES_APPID_UNDEFINED; + CFE_SB_DecrBufUseCnt(BufDscPtr); } - /* Basic sanity check passed */ - return CFE_SUCCESS; + CFE_SB_UnlockSharedData(__func__, __LINE__); + + return Status; } /*---------------------------------------------------------------- @@ -2096,25 +1425,31 @@ int32 CFE_SB_ZeroCopyBufferValidate(CFE_SB_Buffer_t *BufPtr, CFE_SB_BufferD_t ** * See description in header file for argument/return detail * *-----------------------------------------------------------------*/ -CFE_Status_t CFE_SB_ReleaseMessageBuffer(CFE_SB_Buffer_t *BufPtr) +CFE_Status_t CFE_SB_TransmitBuffer(CFE_SB_Buffer_t *BufPtr, bool IsOrigination) { - CFE_SB_BufferD_t *BufDscPtr; - int32 Status; + CFE_SB_TransmitTxn_State_t TxnBuf; + CFE_SB_MessageTxn_State_t *Txn; - Status = CFE_SB_ZeroCopyBufferValidate(BufPtr, &BufDscPtr); + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, BufPtr); - CFE_SB_LockSharedData(__func__, __LINE__); + /* In this context, the user should have set the the size and MsgId in the content */ + if (CFE_SB_MessageTxn_IsOK(Txn)) + { + CFE_SB_TransmitTxn_SetupFromMsg(Txn, &BufPtr->Msg); + } - if (Status == CFE_SUCCESS) + if (CFE_SB_MessageTxn_IsOK(Txn)) { - /* Clear the ownership app ID and decrement use count (may also free) */ - BufDscPtr->AppId = CFE_ES_APPID_UNDEFINED; - CFE_SB_DecrBufUseCnt(BufDscPtr); + /* Save passed-in parameters */ + CFE_SB_MessageTxn_SetEndpoint(Txn, IsOrigination); + + CFE_SB_TransmitTxn_Execute(Txn, BufPtr); } - CFE_SB_UnlockSharedData(__func__, __LINE__); + /* send an event for each pipe write error that may have occurred */ + CFE_SB_MessageTxn_ReportEvents(Txn); - return Status; + return CFE_SB_MessageTxn_GetStatus(Txn); } /*---------------------------------------------------------------- @@ -2123,52 +1458,63 @@ CFE_Status_t CFE_SB_ReleaseMessageBuffer(CFE_SB_Buffer_t *BufPtr) * See description in header file for argument/return detail * *-----------------------------------------------------------------*/ -CFE_Status_t CFE_SB_TransmitBuffer(CFE_SB_Buffer_t *BufPtr, bool IsOrigination) +CFE_Status_t CFE_SB_TransmitMsg(const CFE_MSG_Message_t *MsgPtr, bool IsOrigination) { - int32 Status; - CFE_SB_BufferD_t *BufDscPtr; - CFE_SBR_RouteId_t RouteId; + CFE_SB_TransmitTxn_State_t TxnBuf; + CFE_SB_MessageTxn_State_t *Txn; + CFE_SB_Buffer_t * BufPtr; - Status = CFE_SB_ZeroCopyBufferValidate(BufPtr, &BufDscPtr); + BufPtr = NULL; + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, MsgPtr); - if (Status == CFE_SUCCESS) + /* In this context, the user should have set the the size and MsgId in the content */ + if (CFE_SB_MessageTxn_IsOK(Txn)) { - /* Validate the content and get the MsgId, store it in the descriptor */ - Status = CFE_SB_TransmitMsgValidate(&BufPtr->Msg, &BufDscPtr->MsgId, &BufDscPtr->ContentSize, &RouteId); + CFE_SB_TransmitTxn_SetupFromMsg(Txn, MsgPtr); + } - /* - * Broadcast the message if validation succeeded. - * - * Note that for the case of no subscribers, the validation returns CFE_SUCCESS - * but the actual route ID may be invalid. This is OK and considered normal- - * the validation will increment the NoSubscribers count, but we should NOT - * increment the MsgSendErrorCounter here - it is not really a sending error to - * have no subscribers. CFE_SB_BroadcastBufferToRoute() will not send to - * anything if the route is not valid (benign). - */ - if (Status == CFE_SUCCESS) + if (CFE_SB_MessageTxn_IsOK(Txn)) + { + /* Get buffer - note this pre-initializes the returned buffer with + * a use count of 1, which refers to this task as it fills the buffer. */ + BufPtr = CFE_SB_AllocateMessageBuffer(CFE_SB_MessageTxn_GetContentSize(Txn)); + if (BufPtr == NULL) { - BufDscPtr->NeedsUpdate = IsOrigination; - CFE_MSG_GetType(&BufPtr->Msg, &BufDscPtr->ContentType); - - /* Now broadcast the message, which consumes the buffer */ - CFE_SB_BroadcastBufferToRoute(BufDscPtr, RouteId); - - /* - * IMPORTANT - the descriptor might be freed at any time after this, - * so the descriptor should not be accessed again after this point. - */ - BufDscPtr = NULL; + CFE_SB_MessageTxn_SetEventAndStatus(Txn, CFE_SB_GET_BUF_ERR_EID, CFE_SB_BUF_ALOC_ERR); } } - if (Status != CFE_SUCCESS) + /* + * If a buffer was obtained above, then copy the content into it + * and broadcast it to all subscribers in the route. + * + * Note - if there is no route / no subscribers, the "Status" will + * be CFE_SUCCESS because CFE_SB_TransmitMsgValidate() succeeded, + * but there will be no buffer because CFE_SBR_IsValidRouteId() returned + * false. + * + * But if the descriptor is non-null it means the message is valid and + * there is a route to send it to. + */ + if (CFE_SB_MessageTxn_IsOK(Txn)) { - /* Increment send error counter for validation failure */ - CFE_SB_LockSharedData(__func__, __LINE__); - CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter++; - CFE_SB_UnlockSharedData(__func__, __LINE__); + /* Copy actual message content into buffer */ + memcpy(&BufPtr->Msg, MsgPtr, CFE_SB_MessageTxn_GetContentSize(Txn)); + + /* Save passed-in parameters */ + CFE_SB_MessageTxn_SetEndpoint(Txn, IsOrigination); + + CFE_SB_TransmitTxn_Execute(Txn, BufPtr); + + /* + * The broadcast function consumes the buffer, so it should not be + * accessed in this function anymore + */ + BufPtr = NULL; } - return Status; + /* send an event for each pipe write error that may have occurred */ + CFE_SB_MessageTxn_ReportEvents(Txn); + + return CFE_SB_MessageTxn_GetStatus(Txn); } diff --git a/modules/sb/fsw/src/cfe_sb_buf.c b/modules/sb/fsw/src/cfe_sb_buf.c index 1f5dd0156..5853cefb6 100644 --- a/modules/sb/fsw/src/cfe_sb_buf.c +++ b/modules/sb/fsw/src/cfe_sb_buf.c @@ -128,7 +128,6 @@ CFE_SB_BufferD_t *CFE_SB_GetBufferFromPool(size_t MaxMsgSize) bd = (CFE_SB_BufferD_t *)addr; memset(bd, 0, CFE_SB_BUFFERD_CONTENT_OFFSET); - bd->MsgId = CFE_SB_INVALID_MSG_ID; bd->UseCount = 1; bd->AllocatedSize = AllocSize; diff --git a/modules/sb/fsw/src/cfe_sb_priv.c b/modules/sb/fsw/src/cfe_sb_priv.c index a48f77481..169bc1361 100644 --- a/modules/sb/fsw/src/cfe_sb_priv.c +++ b/modules/sb/fsw/src/cfe_sb_priv.c @@ -291,10 +291,15 @@ char *CFE_SB_GetAppTskName(CFE_ES_TaskId_t TaskId, char *FullName) * See description in header file for argument/return detail * *-----------------------------------------------------------------*/ -uint32 CFE_SB_RequestToSendEvent(CFE_ES_TaskId_t TaskId, uint32 Bit) +uint32 CFE_SB_RequestToSendEvent(CFE_ES_TaskId_t TaskId, int32 Bit) { uint32 Indx; + if (Bit < 0) + { + return CFE_SB_GRANTED; + } + if (CFE_ES_TaskID_ToIndex(TaskId, &Indx) != CFE_SUCCESS) { return CFE_SB_DENIED; @@ -318,10 +323,15 @@ uint32 CFE_SB_RequestToSendEvent(CFE_ES_TaskId_t TaskId, uint32 Bit) * See description in header file for argument/return detail * *-----------------------------------------------------------------*/ -void CFE_SB_FinishSendEvent(CFE_ES_TaskId_t TaskId, uint32 Bit) +void CFE_SB_FinishSendEvent(CFE_ES_TaskId_t TaskId, int32 Bit) { uint32 Indx; + if (Bit < 0) + { + return; + } + if (CFE_ES_TaskID_ToIndex(TaskId, &Indx) != CFE_SUCCESS) { return; @@ -470,3 +480,1049 @@ int32 CFE_SB_ZeroCopyReleaseAppId(CFE_ES_AppId_t AppId) return CFE_SUCCESS; } + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +int32 CFE_SB_ZeroCopyBufferValidate(CFE_SB_Buffer_t *BufPtr, CFE_SB_BufferD_t **BufDscPtr) +{ + cpuaddr BufDscAddr; + + /* + * Calculate descriptor pointer from buffer pointer - + * The buffer is just a member (offset) in the descriptor + */ + BufDscAddr = (cpuaddr)BufPtr - offsetof(CFE_SB_BufferD_t, Content); + *BufDscPtr = (CFE_SB_BufferD_t *)BufDscAddr; + + /* + * Check that the descriptor is actually a "zero copy" type, + */ + if (!CFE_RESOURCEID_TEST_DEFINED((*BufDscPtr)->AppId)) + { + return CFE_SB_BUFFER_INVALID; + } + + /* Basic sanity check passed */ + return CFE_SUCCESS; +} + +/****************************************************************** + * + * MESSAGE TRANSACTION IMPLEMENTATION FUNCTIONS + * + ******************************************************************/ + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_Init(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_PipeSetEntry_t *PipeSet, uint16 MaxPipes, + const void *RefMemPtr) +{ + memset(TxnPtr, 0, offsetof(CFE_SB_TransmitTxn_State_t, DestSet)); + TxnPtr->PipeSet = PipeSet; + TxnPtr->MaxPipes = MaxPipes; + + /* The reference pointer is kept just for (potential) reporting in events, etc. + * It is not dereferenced from here. */ + TxnPtr->RefMemPtr = RefMemPtr; +} + +/*---------------------------------------------------------------- + * + * Local Helper function + * Not invoked outside of this unit + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_SetTimeout(CFE_SB_MessageTxn_State_t *TxnPtr, int32 Timeout) +{ + TxnPtr->UserTimeoutParam = Timeout; + if (Timeout > 0) + { + /* Convert to an absolute timeout */ + TxnPtr->TimeoutMode = CFE_SB_MessageTxn_TimeoutMode_TIMED; + CFE_PSP_GetTime(&TxnPtr->AbsTimeout); + TxnPtr->AbsTimeout = OS_TimeAdd(TxnPtr->AbsTimeout, OS_TimeFromTotalMilliseconds(Timeout)); + } + else if (Timeout == CFE_SB_POLL) + { + TxnPtr->TimeoutMode = CFE_SB_MessageTxn_TimeoutMode_POLL; + } + else if (Timeout == CFE_SB_PEND_FOREVER) + { + TxnPtr->TimeoutMode = CFE_SB_MessageTxn_TimeoutMode_PEND; + } + else + { + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, CFE_SB_RCV_BAD_ARG_EID, CFE_SB_BAD_ARGUMENT); + } +} + +/*---------------------------------------------------------------- + * + * Local Helper function + * Not invoked outside of this unit + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_GetEventDetails(const CFE_SB_MessageTxn_State_t *TxnPtr, const CFE_SB_PipeSetEntry_t *ContextPtr, + uint16 EventId, CFE_ES_TaskId_t TskId, char *EvtMsg, size_t EvtMsgSize, + CFE_EVS_EventType_Enum_t *EventType, int32 *ReqBit) +{ + char FullName[(OS_MAX_API_NAME * 2)]; + char PipeName[OS_MAX_API_NAME]; + int32 LocalOsStatus; + + if (ContextPtr == NULL) + { + LocalOsStatus = INT32_MIN; /* should not be used; do not alias any actual OS status */ + } + else + { + LocalOsStatus = ContextPtr->OsStatus; + if (ContextPtr->PendingEventId == CFE_SB_BAD_PIPEID_EID) + { + /* do not attempt to map to a pipe name if reporting a bad pipeID event - use its ID */ + snprintf(PipeName, sizeof(PipeName), "%lu", CFE_RESOURCEID_TO_ULONG(ContextPtr->PipeId)); + } + else + { + CFE_SB_GetPipeName(PipeName, sizeof(PipeName), ContextPtr->PipeId); + } + } + + switch (EventId) + { + case CFE_SB_SEND_BAD_ARG_EID: + *ReqBit = CFE_SB_SEND_BAD_ARG_EID_BIT; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Send Err:Bad input argument,Arg 0x%lx,App %s", + (unsigned long)TxnPtr->RefMemPtr, CFE_SB_GetAppTskName(TskId, FullName)); + break; + + case CFE_SB_SEND_INV_MSGID_EID: + *ReqBit = CFE_SB_SEND_INV_MSGID_EID_BIT; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Send Err:Invalid MsgId(0x%x)in msg,App %s", + (unsigned int)CFE_SB_MsgIdToValue(TxnPtr->RoutingMsgId), CFE_SB_GetAppTskName(TskId, FullName)); + break; + + case CFE_SB_MSG_TOO_BIG_EID: + *ReqBit = CFE_SB_MSG_TOO_BIG_EID_BIT; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Send Err:Msg Too Big MsgId=0x%x,app=%s,size=%d,MaxSz=%d", + (unsigned int)CFE_SB_MsgIdToValue(TxnPtr->RoutingMsgId), CFE_SB_GetAppTskName(TskId, FullName), + (int)TxnPtr->ContentSize, CFE_MISSION_SB_MAX_SB_MSG_SIZE); + break; + + case CFE_SB_SEND_NO_SUBS_EID: + *ReqBit = CFE_SB_SEND_NO_SUBS_EID_BIT; + *EventType = CFE_EVS_EventType_INFORMATION; + + snprintf(EvtMsg, EvtMsgSize, "No subscribers for MsgId 0x%x,sender %s", + (unsigned int)CFE_SB_MsgIdToValue(TxnPtr->RoutingMsgId), CFE_SB_GetAppTskName(TskId, FullName)); + break; + + case CFE_SB_GET_BUF_ERR_EID: + *ReqBit = CFE_SB_GET_BUF_ERR_EID_BIT; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Send Err:Request for Buffer Failed. MsgId 0x%x,app %s,size %d", + (unsigned int)CFE_SB_MsgIdToValue(TxnPtr->RoutingMsgId), CFE_SB_GetAppTskName(TskId, FullName), + (int)TxnPtr->ContentSize); + break; + + case CFE_SB_MSGID_LIM_ERR_EID: + *ReqBit = CFE_SB_MSGID_LIM_ERR_EID_BIT; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Msg Limit Err,MsgId 0x%x,pipe %s,sender %s", + (unsigned int)CFE_SB_MsgIdToValue(TxnPtr->RoutingMsgId), PipeName, + CFE_SB_GetAppTskName(TskId, FullName)); + break; + + case CFE_SB_Q_FULL_ERR_EID: + *ReqBit = CFE_SB_Q_FULL_ERR_EID_BIT; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Pipe Overflow,MsgId 0x%x,pipe %s,sender %s", + (unsigned int)CFE_SB_MsgIdToValue(TxnPtr->RoutingMsgId), PipeName, + CFE_SB_GetAppTskName(TskId, FullName)); + break; + + case CFE_SB_Q_WR_ERR_EID: + *ReqBit = CFE_SB_Q_WR_ERR_EID_BIT; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Pipe Write Err,MsgId 0x%x,pipe %s,sender %s,stat %ld", + (unsigned int)CFE_SB_MsgIdToValue(TxnPtr->RoutingMsgId), PipeName, + CFE_SB_GetAppTskName(TskId, FullName), (long)LocalOsStatus); + break; + + case CFE_SB_Q_RD_ERR_EID: + *ReqBit = -1; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Pipe Read Err,pipe %s,app %s,stat %ld", PipeName, + CFE_SB_GetAppTskName(TskId, FullName), (long)LocalOsStatus); + break; + case CFE_SB_RCV_BAD_ARG_EID: + *ReqBit = -1; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Rcv Err:Bad Input Arg:BufPtr 0x%lx,pipe %s,t/o %d,app %s", + (unsigned long)TxnPtr->RefMemPtr, PipeName, (int)TxnPtr->UserTimeoutParam, + CFE_SB_GetAppTskName(TskId, FullName)); + break; + case CFE_SB_BAD_PIPEID_EID: + *ReqBit = -1; + *EventType = CFE_EVS_EventType_ERROR; + + snprintf(EvtMsg, EvtMsgSize, "Rcv Err:PipeId %s does not exist,app %s", PipeName, + CFE_SB_GetAppTskName(TskId, FullName)); + break; + + default: + EvtMsg[0] = 0; + *ReqBit = 0; + *EventType = 0; + break; + } +} + +/*---------------------------------------------------------------- + * + * Local Helper function + * Not invoked outside of this unit + * + *-----------------------------------------------------------------*/ +bool CFE_SB_MessageTxn_ReportSingleEvent(const CFE_SB_MessageTxn_State_t *TxnPtr, + const CFE_SB_PipeSetEntry_t *ContextPtr, uint16 EventId) +{ + CFE_ES_TaskId_t TskId; + CFE_EVS_EventType_Enum_t EventType; + char Message[CFE_MISSION_EVS_MAX_MESSAGE_LENGTH]; + int32 ReqBit; + + /* get task id for events and Sender Info*/ + CFE_ES_GetTaskID(&TskId); + + CFE_SB_MessageTxn_GetEventDetails(TxnPtr, ContextPtr, EventId, TskId, Message, sizeof(Message), &EventType, + &ReqBit); + + if (EventType > 0 && CFE_SB_RequestToSendEvent(TskId, ReqBit) == CFE_SB_GRANTED) + { + CFE_EVS_SendEventWithAppID(EventId, EventType, CFE_SB_Global.AppId, "%s", Message); + + /* clear the bit so the task may send this event again */ + CFE_SB_FinishSendEvent(TskId, ReqBit); + } + + /* If the event type was an error, this should increment the general error counter */ + return (EventType >= CFE_EVS_EventType_ERROR); +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_ReportEvents(const CFE_SB_MessageTxn_State_t *TxnPtr) +{ + uint32 i; + uint32 NumErrors; + bool IsError; + + NumErrors = 0; + + if (TxnPtr->TransactionEventId != 0) + { + IsError = CFE_SB_MessageTxn_ReportSingleEvent(TxnPtr, NULL, TxnPtr->TransactionEventId); + if (IsError) + { + ++NumErrors; + } + } + + for (i = 0; i < TxnPtr->NumPipes; ++i) + { + if (TxnPtr->PipeSet[i].PendingEventId != 0) + { + IsError = + CFE_SB_MessageTxn_ReportSingleEvent(TxnPtr, &TxnPtr->PipeSet[i], TxnPtr->PipeSet[i].PendingEventId); + if (IsError) + { + ++NumErrors; + } + } + } + + if (NumErrors > 0) + { + /* + * Increment the error only if there was a real error, + * such as a validation issue or failure to allocate a buffer. + * + * Note that transmit side just has one error counter, whereas + * receive side has two - these differeniate between a bad passed-in + * arg vs some other internal error such as queue access. + */ + CFE_SB_LockSharedData(__func__, __LINE__); + + if (TxnPtr->IsTransmit) + { + CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter++; + } + else if (TxnPtr->Status == CFE_SB_BAD_ARGUMENT) + { + ++CFE_SB_Global.HKTlmMsg.Payload.MsgReceiveErrorCounter; + } + else + { + /* For any other unexpected error (e.g. CFE_SB_Q_RD_ERR_EID) */ + ++CFE_SB_Global.HKTlmMsg.Payload.InternalErrorCounter; + } + + CFE_SB_UnlockSharedData(__func__, __LINE__); + } +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_SetEventAndStatus(CFE_SB_MessageTxn_State_t *TxnPtr, uint16 EventId, CFE_Status_t Status) +{ + if (TxnPtr->TransactionEventId == 0) + { + TxnPtr->TransactionEventId = EventId; + } + if (TxnPtr->Status == CFE_SUCCESS) + { + TxnPtr->Status = Status; + } +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_SetRoutingMsgId(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_MsgId_t RoutingMsgId) +{ + /* validate the msgid in the message */ + if (!CFE_SB_IsValidMsgId(RoutingMsgId)) + { + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, CFE_SB_SEND_INV_MSGID_EID, CFE_SB_BAD_ARGUMENT); + } + else + { + TxnPtr->RoutingMsgId = RoutingMsgId; + } +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_SetContentSize(CFE_SB_MessageTxn_State_t *TxnPtr, size_t ContentSize) +{ + if (ContentSize > CFE_MISSION_SB_MAX_SB_MSG_SIZE) + { + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, CFE_SB_MSG_TOO_BIG_EID, CFE_SB_MSG_TOO_BIG); + } + else + { + TxnPtr->ContentSize = ContentSize; + } +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_TransmitTxn_SetupFromMsg(CFE_SB_MessageTxn_State_t *TxnPtr, const CFE_MSG_Message_t *MsgPtr) +{ + CFE_Status_t Status; + CFE_MSG_Size_t MsgSize; + CFE_SB_MsgId_t MsgId; + + if (CFE_SB_MessageTxn_IsOK(TxnPtr)) + { + /* In this context, the user should have set the the size and MsgId in the content */ + Status = CFE_MSG_GetMsgId(MsgPtr, &MsgId); + if (Status == CFE_SUCCESS) + { + CFE_SB_MessageTxn_SetRoutingMsgId(TxnPtr, MsgId); + } + else + { + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, CFE_SB_SEND_BAD_ARG_EID, Status); + } + } + + if (CFE_SB_MessageTxn_IsOK(TxnPtr)) + { + Status = CFE_MSG_GetSize(MsgPtr, &MsgSize); + if (Status == CFE_SUCCESS) + { + CFE_SB_MessageTxn_SetContentSize(TxnPtr, MsgSize); + } + else + { + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, CFE_SB_SEND_BAD_ARG_EID, Status); + } + } +} + +/****************************************************************** + * + * TRANSMIT TRANSACTION IMPLEMENTATION FUNCTIONS + * + ******************************************************************/ + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_SB_MessageTxn_State_t *CFE_SB_TransmitTxn_Init(CFE_SB_TransmitTxn_State_t *TxnPtr, const void *RefMemPtr) +{ + CFE_SB_MessageTxn_Init(&TxnPtr->MessageTxn_State, TxnPtr->DestSet, CFE_PLATFORM_SB_MAX_DEST_PER_PKT, RefMemPtr); + TxnPtr->MessageTxn_State.IsTransmit = true; + + /* No matter what, the mem pointer from the caller should not be NULL */ + if (RefMemPtr == NULL) + { + CFE_SB_MessageTxn_SetEventAndStatus(&TxnPtr->MessageTxn_State, CFE_SB_SEND_BAD_ARG_EID, CFE_SB_BAD_ARGUMENT); + } + + return &TxnPtr->MessageTxn_State; +} + +/*---------------------------------------------------------------- + * + * Local Helper function + * Not invoked outside of this unit + * + *-----------------------------------------------------------------*/ +void CFE_SB_TransmitTxn_FindDestinations(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_BufferD_t *BufDscPtr) +{ + CFE_SB_PipeD_t * PipeDscPtr; + CFE_SB_DestinationD_t *DestPtr; + CFE_SB_PipeSetEntry_t *ContextPtr; + CFE_ES_AppId_t AppId; + bool IsAcceptable; + CFE_Status_t Status; + + /* + * get app id for loopback testing - + * This is only used if one or more of the destinations has its "IGNOREMINE" option set, + * but it should NOT be gotten while locked. So since we do not know (yet) if we need it, + * it is better to get it and not need it than need it and not have it. + */ + CFE_ES_GetAppID(&AppId); + + /* take semaphore to prevent a task switch during processing */ + CFE_SB_LockSharedData(__func__, __LINE__); + + /* Get the routing id */ + BufDscPtr->DestRouteId = CFE_SBR_GetRouteId(TxnPtr->RoutingMsgId); + + /* For an invalid route / no subscribers this whole logic can be skipped */ + if (CFE_SBR_IsValidRouteId(BufDscPtr->DestRouteId)) + { + /* If this is the origination, then update the message content (while locked) before actually sending */ + if (TxnPtr->IsEndpoint) + { + CFE_SBR_IncrementSequenceCounter(BufDscPtr->DestRouteId); + + /* Set the sequence count from the route */ + Status = + CFE_MSG_SetSequenceCount(&BufDscPtr->Content.Msg, CFE_SBR_GetSequenceCounter(BufDscPtr->DestRouteId)); + } + + /* Send the packet to all destinations */ + DestPtr = CFE_SBR_GetDestListHeadPtr(BufDscPtr->DestRouteId); + while (DestPtr != NULL && TxnPtr->NumPipes < TxnPtr->MaxPipes) + { + ContextPtr = NULL; + + if (DestPtr->Active == CFE_SB_ACTIVE) /* destination is active */ + { + PipeDscPtr = CFE_SB_LocatePipeDescByID(DestPtr->PipeId); + } + else + { + PipeDscPtr = NULL; + } + + if (CFE_SB_PipeDescIsMatch(PipeDscPtr, DestPtr->PipeId)) + { + if ((PipeDscPtr->Opts & CFE_SB_PIPEOPTS_IGNOREMINE) == 0 || + !CFE_RESOURCEID_TEST_EQUAL(PipeDscPtr->AppId, AppId)) + { + ContextPtr = &TxnPtr->PipeSet[TxnPtr->NumPipes]; + ++TxnPtr->NumPipes; + } + } + + if (ContextPtr != NULL) + { + memset(ContextPtr, 0, sizeof(*ContextPtr)); + + ContextPtr->PipeId = DestPtr->PipeId; + ContextPtr->SysQueueId = PipeDscPtr->SysQueueId; + + /* if Msg limit exceeded, log event, increment counter */ + /* and go to next destination */ + if (DestPtr->BuffCount >= DestPtr->MsgId2PipeLim) + { + ContextPtr->PendingEventId = CFE_SB_MSGID_LIM_ERR_EID; + ++CFE_SB_Global.HKTlmMsg.Payload.MsgLimitErrorCounter; + ++PipeDscPtr->SendErrors; + ++TxnPtr->NumPipeErrs; + } + else + { + CFE_SB_IncrBufUseCnt(BufDscPtr); + ++DestPtr->BuffCount; + + ++PipeDscPtr->CurrentQueueDepth; + if (PipeDscPtr->CurrentQueueDepth > PipeDscPtr->PeakQueueDepth) + { + PipeDscPtr->PeakQueueDepth = PipeDscPtr->CurrentQueueDepth; + } + } + } + + DestPtr = DestPtr->Next; + } + } + else + { + /* if there have been no subscriptions for this pkt, */ + /* increment the dropped pkt cnt, send event and return success */ + CFE_SB_Global.HKTlmMsg.Payload.NoSubscribersCounter++; + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, CFE_SB_SEND_NO_SUBS_EID, CFE_SUCCESS); + } + + /* + * Remove this from whatever list it was in + * + * If it was a singleton/new buffer this has no effect. + * If it was a zero-copy buffer this removes it from the ZeroCopyList. + */ + CFE_SB_TrackingListRemove(&BufDscPtr->Link); + + /* clear the AppID field in case it was a zero copy buffer, + * as it is no longer owned by that app after broadcasting */ + BufDscPtr->AppId = CFE_ES_APPID_UNDEFINED; + + /* track the buffer as an in-transit message */ + CFE_SB_TrackingListAdd(&CFE_SB_Global.InTransitList, &BufDscPtr->Link); + + CFE_SB_UnlockSharedData(__func__, __LINE__); + + /* + * Lastly, if this is the origination point, now that all headers should + * have known values (including sequence) - invoke the mission-specific + * message origination action. This may update timestamps and/or compute + * any required error control fields. + */ + if (CFE_SB_MessageTxn_IsOK(TxnPtr) && TxnPtr->IsEndpoint) + { + /* Update any other system-specific MSG headers based on the current sequence */ + Status = CFE_MSG_OriginationAction(&BufDscPtr->Content.Msg, BufDscPtr->AllocatedSize, &IsAcceptable); + if (Status != CFE_SUCCESS || !IsAcceptable) + { + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, CFE_SB_SEND_MESSAGE_INTEGRITY_FAIL_EID, Status); + } + } +} + +/*---------------------------------------------------------------- + * + * Local Helper function + * Not invoked outside of this unit + * + *-----------------------------------------------------------------*/ +int32 CFE_SB_MessageTxn_GetOsTimeout(const CFE_SB_MessageTxn_State_t *TxnPtr) +{ + int32 OsTimeout; + OS_time_t TimeNow; + + switch (TxnPtr->TimeoutMode) + { + case CFE_SB_MessageTxn_TimeoutMode_PEND: + OsTimeout = OS_PEND; + break; + case CFE_SB_MessageTxn_TimeoutMode_TIMED: + CFE_PSP_GetTime(&TimeNow); + OsTimeout = OS_TimeGetTotalMilliseconds(OS_TimeSubtract(TxnPtr->AbsTimeout, TimeNow)); + if (OsTimeout < 0) + { + /* timeout has already expired, so all remaining pipes should be CHECK only */ + OsTimeout = OS_CHECK; + } + break; + case CFE_SB_MessageTxn_TimeoutMode_POLL: + default: + OsTimeout = OS_CHECK; + break; + } + + return OsTimeout; +} + +/*---------------------------------------------------------------- + * + * Local Helper function + * Not invoked outside of this unit + * + *-----------------------------------------------------------------*/ +bool CFE_SB_TransmitTxn_PipeHandler(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_PipeSetEntry_t *ContextPtr, void *Arg) +{ + CFE_SB_DestinationD_t *DestPtr; + CFE_SB_PipeD_t * PipeDscPtr; + CFE_SB_BufferD_t * BufDscPtr; + + BufDscPtr = Arg; + + /* + * Write the buffer descriptor to the queue of the pipe. Note that + * accounting for depth and buffer limits was already done as part + * of "FindDestinations" assuming this write will be successful - which + * is the expected/typical result here. + */ + ContextPtr->OsStatus = + OS_QueuePut(ContextPtr->SysQueueId, &BufDscPtr, sizeof(BufDscPtr), CFE_SB_MessageTxn_GetOsTimeout(TxnPtr)); + + /* + * If it succeeded, nothing else to do. But if it fails then we must undo the + * optimistic depth accounting done earlier. + */ + if (ContextPtr->OsStatus != OS_SUCCESS) + { + ++TxnPtr->NumPipeErrs; + + CFE_SB_LockSharedData(__func__, __LINE__); + + if (ContextPtr->OsStatus == OS_QUEUE_FULL) + { + ContextPtr->PendingEventId = CFE_SB_Q_FULL_ERR_EID; + CFE_SB_Global.HKTlmMsg.Payload.PipeOverflowErrorCounter++; + } + else + { + /* Unexpected error while writing to queue. */ + ContextPtr->PendingEventId = CFE_SB_Q_WR_ERR_EID; + CFE_SB_Global.HKTlmMsg.Payload.InternalErrorCounter++; + } + + PipeDscPtr = CFE_SB_LocatePipeDescByID(ContextPtr->PipeId); + if (CFE_SB_PipeDescIsMatch(PipeDscPtr, ContextPtr->PipeId) && PipeDscPtr->CurrentQueueDepth > 0) + { + --PipeDscPtr->CurrentQueueDepth; + } + + DestPtr = CFE_SB_GetDestPtr(BufDscPtr->DestRouteId, ContextPtr->PipeId); + if (DestPtr != NULL && DestPtr->BuffCount > 0) + { + DestPtr->BuffCount--; + } + + CFE_SB_DecrBufUseCnt(BufDscPtr); + + CFE_SB_UnlockSharedData(__func__, __LINE__); + } + + /* always keep going when sending (broadcast) */ + return true; +} + +/*---------------------------------------------------------------- + * + * Local Helper function + * Not invoked outside of this unit + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_ProcessPipes(CFE_SB_MessageTxn_PipeHandler_t HandlerFunc, CFE_SB_MessageTxn_State_t *TxnPtr, + void *Arg) +{ + uint32 i; + CFE_SB_PipeSetEntry_t *ContextPtr; + bool should_continue; + + should_continue = true; + for (i = 0; should_continue && i < TxnPtr->NumPipes; ++i) + { + ContextPtr = &TxnPtr->PipeSet[i]; + + if (ContextPtr->PendingEventId == 0) + { + should_continue = HandlerFunc(TxnPtr, ContextPtr, Arg); + } + } +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_TransmitTxn_Execute(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_Buffer_t *BufPtr) +{ + int32 Status; + CFE_SB_BufferD_t *BufDscPtr; + + /* Sanity check on the input buffer - if this doesn't work, stop now */ + Status = CFE_SB_ZeroCopyBufferValidate(BufPtr, &BufDscPtr); + if (Status != CFE_SUCCESS) + { + /* There is currently no event defined for this */ + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, 0, Status); + return; + } + + /* Save passed-in routing parameters into the descriptor */ + BufDscPtr->ContentSize = CFE_SB_MessageTxn_GetContentSize(TxnPtr); + BufDscPtr->MsgId = CFE_SB_MessageTxn_GetRoutingMsgId(TxnPtr); + + /* Convert the route to a set of pipes/destinations */ + CFE_SB_TransmitTxn_FindDestinations(TxnPtr, BufDscPtr); + + /* Note the above function always succeeds - even if no pipes are subscribed, + * the transaction will simply have 0 pipes and this next call becomes a no-op */ + CFE_SB_MessageTxn_ProcessPipes(CFE_SB_TransmitTxn_PipeHandler, TxnPtr, BufDscPtr); + + /* + * Decrement the buffer UseCount - This means that the caller + * should not use the buffer anymore after this call. + */ + CFE_SB_LockSharedData(__func__, __LINE__); + CFE_SB_DecrBufUseCnt(BufDscPtr); + CFE_SB_UnlockSharedData(__func__, __LINE__); +} + +/****************************************************************** + * + * RECEIVE TRANSACTION IMPLEMENTATION FUNCTIONS + * + ******************************************************************/ + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_SB_MessageTxn_State_t *CFE_SB_ReceiveTxn_Init(CFE_SB_ReceiveTxn_State_t *TxnPtr, const void *RefMemPtr) +{ + CFE_SB_MessageTxn_Init(&TxnPtr->MessageTxn_State, &TxnPtr->Source, 1, RefMemPtr); + TxnPtr->MessageTxn_State.IsTransmit = false; + + /* No matter what, the mem pointer from the caller should not be NULL */ + /* note that the event ID is different between send and recv */ + if (RefMemPtr == NULL) + { + CFE_SB_MessageTxn_SetEventAndStatus(&TxnPtr->MessageTxn_State, CFE_SB_RCV_BAD_ARG_EID, CFE_SB_BAD_ARGUMENT); + } + + return &TxnPtr->MessageTxn_State; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_MessageTxn_SetEndpoint(CFE_SB_MessageTxn_State_t *TxnPtr, bool IsEndpoint) +{ + TxnPtr->IsEndpoint = IsEndpoint; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_SB_ReceiveTxn_SetPipeId(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_PipeId_t PipeId) +{ + CFE_SB_PipeD_t * PipeDscPtr; + CFE_SB_PipeSetEntry_t *ContextPtr; + + /* For now, there is just one of these */ + ContextPtr = TxnPtr->PipeSet; + memset(ContextPtr, 0, sizeof(*ContextPtr)); + ContextPtr->PipeId = PipeId; + TxnPtr->NumPipes = 1; + + PipeDscPtr = CFE_SB_LocatePipeDescByID(PipeId); + + CFE_SB_LockSharedData(__func__, __LINE__); + + /* If the pipe does not exist or PipeId is out of range... */ + if (!CFE_SB_PipeDescIsMatch(PipeDscPtr, PipeId)) + { + ContextPtr->PendingEventId = CFE_SB_BAD_PIPEID_EID; + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, 0, CFE_SB_BAD_ARGUMENT); + ++TxnPtr->NumPipeErrs; + } + else + { + ContextPtr->SysQueueId = PipeDscPtr->SysQueueId; + + /* + * Un-reference any previous buffer from the last call. + * + * NOTE: This is historical behavior where apps call CFE_SB_ReceiveBuffer() + * in the loop within the app's main task. There is currently no separate + * API to "free" or unreference a buffer that was returned from SB. + * + * Instead, each time this function is invoked, it is implicitly interpreted + * as an indication that the caller is done with the previous buffer. + * + * Unfortunately this prevents pipe IDs from being serviced/shared across + * multiple child tasks in a worker pattern design. This may be changed + * in a future version of CFE to decouple these actions, to allow for + * multiple workers to service the same pipe. + */ + if (PipeDscPtr->LastBuffer != NULL) + { + /* Decrement the Buffer Use Count, which will Free buffer if it becomes 0 */ + CFE_SB_DecrBufUseCnt(PipeDscPtr->LastBuffer); + PipeDscPtr->LastBuffer = NULL; + } + } + + CFE_SB_UnlockSharedData(__func__, __LINE__); +} + +/*---------------------------------------------------------------- + * + * Local Helper function + * Not invoked outside of this unit + * + *-----------------------------------------------------------------*/ +void CFE_SB_ReceiveTxn_ExportReference(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_PipeSetEntry_t *ContextPtr, + CFE_SB_BufferD_t *BufDscPtr, CFE_SB_BufferD_t **ParentBufDscPtrP) +{ + CFE_SB_PipeD_t * PipeDscPtr; + CFE_SB_DestinationD_t *DestPtr; + + PipeDscPtr = CFE_SB_LocatePipeDescByID(ContextPtr->PipeId); + + /* Now re-lock to store the buffer in the pipe descriptor */ + CFE_SB_LockSharedData(__func__, __LINE__); + + /* + * NOTE: This uses the same PipeDscPtr that was found earlier. + * But it has to be revalidated because its theoretically possible + * the pipe got deleted between now and then. + */ + if (CFE_SB_PipeDescIsMatch(PipeDscPtr, ContextPtr->PipeId)) + { + /* + ** Load the pipe tables 'CurrentBuff' with the buffer descriptor + ** ptr corresponding to the message just read. This is done so that + ** the buffer can be released on the next receive call for this pipe. + ** + ** This counts as a new reference as it is being stored in the PipeDsc + */ + CFE_SB_IncrBufUseCnt(BufDscPtr); + PipeDscPtr->LastBuffer = BufDscPtr; + + /* + * Also set the Receivers pointer to the address of the actual message + * (currently this is "borrowing" the ref above, not its own ref) + */ + *ParentBufDscPtrP = BufDscPtr; + + /* get pointer to destination to be used in decrementing msg limit cnt*/ + DestPtr = CFE_SB_GetDestPtr(BufDscPtr->DestRouteId, ContextPtr->PipeId); + + /* + ** DestPtr would be NULL if the msg is unsubscribed to while it is on + ** the pipe. The BuffCount may be zero if the msg is unsubscribed to and + ** then resubscribed to while it is on the pipe. Both of these cases are + ** considered nominal and are handled by the code below. + */ + if (DestPtr != NULL && DestPtr->BuffCount > 0) + { + DestPtr->BuffCount--; + } + + if (PipeDscPtr->CurrentQueueDepth > 0) + { + --PipeDscPtr->CurrentQueueDepth; + } + } + else + { + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, 0, CFE_SB_PIPE_RD_ERR); + + /* should send the bad pipe ID event here too */ + ContextPtr->PendingEventId = CFE_SB_BAD_PIPEID_EID; + } + + /* Always decrement the use count, for the ref that was in the queue */ + CFE_SB_DecrBufUseCnt(BufDscPtr); + + CFE_SB_UnlockSharedData(__func__, __LINE__); +} + +/*---------------------------------------------------------------- + * + * Local Helper function + * Not invoked outside of this unit + * + *-----------------------------------------------------------------*/ +bool CFE_SB_ReceiveTxn_PipeHandler(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_PipeSetEntry_t *ContextPtr, void *Arg) +{ + CFE_SB_BufferD_t * BufDscPtr; + CFE_SB_BufferD_t **ParentBufDscPtrP; + size_t BufDscSize; + + ParentBufDscPtrP = Arg; + + /* Read the buffer descriptor address from the queue. */ + ContextPtr->OsStatus = OS_QueueGet(ContextPtr->SysQueueId, &BufDscPtr, sizeof(BufDscPtr), &BufDscSize, + CFE_SB_MessageTxn_GetOsTimeout(TxnPtr)); + + /* + * translate the return value - + * + * CFE functions have their own set of RC values should not directly return OSAL codes + * The size should always match. If it does not, then generate CFE_SB_Q_RD_ERR_EID. + */ + + if (ContextPtr->OsStatus == OS_SUCCESS && BufDscPtr != NULL && BufDscSize == sizeof(BufDscPtr)) + { + CFE_SB_ReceiveTxn_ExportReference(TxnPtr, ContextPtr, BufDscPtr, ParentBufDscPtrP); + } + else + { + *ParentBufDscPtrP = NULL; + + if (ContextPtr->OsStatus == OS_QUEUE_EMPTY) + { + /* normal if using CFE_SB_POLL */ + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, 0, CFE_SB_NO_MESSAGE); + } + else if (ContextPtr->OsStatus == OS_QUEUE_TIMEOUT) + { + /* normal if using a nonzero timeout */ + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, 0, CFE_SB_TIME_OUT); + } + else + { + /* off-nominal condition, report an error event */ + CFE_SB_MessageTxn_SetEventAndStatus(TxnPtr, 0, CFE_SB_PIPE_RD_ERR); + ContextPtr->PendingEventId = CFE_SB_Q_RD_ERR_EID; + } + } + + /* Read ops only process one pipe */ + return false; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +const CFE_SB_Buffer_t *CFE_SB_ReceiveTxn_Execute(CFE_SB_MessageTxn_State_t *TxnPtr) +{ + CFE_SB_BufferD_t * BufDscPtr; + const CFE_SB_Buffer_t *Result; + bool IsAcceptable; + CFE_Status_t Status; + + Result = NULL; + + while (CFE_SB_MessageTxn_IsOK(TxnPtr)) + { + BufDscPtr = NULL; + + /* + * Read from the pipe(s). Currently this is just one but someday could + * become more than one e.g. a high-pri and a low-pri queue. + */ + CFE_SB_MessageTxn_ProcessPipes(CFE_SB_ReceiveTxn_PipeHandler, TxnPtr, &BufDscPtr); + + /* If nothing received, then quit */ + if (BufDscPtr == NULL) + { + TxnPtr->RoutingMsgId = CFE_SB_INVALID_MSG_ID; + TxnPtr->ContentSize = 0; + Result = NULL; + break; + } + + if (TxnPtr->IsEndpoint) + { + Status = CFE_MSG_VerificationAction(&BufDscPtr->Content.Msg, BufDscPtr->AllocatedSize, &IsAcceptable); + if (Status != CFE_SUCCESS) + { + /* This typically should not happen - only if VerificationAction got bad arguments */ + IsAcceptable = false; + } + } + else + { + /* If no verification being done at this stage - consider everything "good" */ + IsAcceptable = true; + } + + if (IsAcceptable) + { + /* + * Replicate the buffer descriptor MsgId and ContentSize in the transaction. + * + * This does not use the functions that check the values because that will + * send an event and set an error status if they are bad. But on the recv side, + * it is already here - whatever we got should be returned. Error checking for + * size and MsgId should have been done on the transmit side, so they should never + * be bad at this point. + */ + TxnPtr->RoutingMsgId = BufDscPtr->MsgId; + TxnPtr->ContentSize = BufDscPtr->ContentSize; + Result = &BufDscPtr->Content; + break; + } + + /* Report an event indicating the buffer is being dropped */ + CFE_SB_MessageTxn_ReportSingleEvent(TxnPtr, TxnPtr->PipeSet, CFE_SB_RCV_MESSAGE_INTEGRITY_FAIL_EID); + + /* + * Also need to re-set the PipeId for proper accounting. This buffer will be dropped, + * and this decrements the use count and removes it from the LastBuffer pointer in the + * Pipe Descriptor + */ + CFE_SB_ReceiveTxn_SetPipeId(TxnPtr, TxnPtr->PipeSet->PipeId); + } + + return Result; +} diff --git a/modules/sb/fsw/src/cfe_sb_priv.h b/modules/sb/fsw/src/cfe_sb_priv.h index 737682936..ee87cf09a 100644 --- a/modules/sb/fsw/src/cfe_sb_priv.h +++ b/modules/sb/fsw/src/cfe_sb_priv.h @@ -124,16 +124,18 @@ typedef struct CFE_SB_BufferD CFE_SB_BufferLink_t Link; /**< Links for inclusion in the tracking lists */ /** - * Actual MsgId of the content, cached here to avoid repeat - * calls into CFE_MSG API during traversal/delivery of the message. + * MsgId that should be used for routing/delivering the message. Traditionally, + * this is the same as the MsgId inside the content, but cached for easier access. + * However, the user may also choose to route messages explicitly and thus this + * message ID could differ from the one inside the message content. * - * MsgId is set for buffers which contain actual data in transit. AppId is unset - * while in transit, as it may be sent to multiple apps. + * MsgId is set for buffers in transit. AppId is unset while in transit, as it + * may be sent to multiple apps. * * During zero copy buffer initial allocation, the MsgId is not known at this time * and should be set to the invalid msg ID. */ - CFE_SB_MsgId_t MsgId; + CFE_SBR_RouteId_t DestRouteId; /** * Current owner of the buffer, if owned by a single app. @@ -146,11 +148,10 @@ typedef struct CFE_SB_BufferD */ CFE_ES_AppId_t AppId; - size_t AllocatedSize; /**< Total size of this descriptor (including descriptor itself) */ - size_t ContentSize; /**< Actual size of message content currently stored in the buffer */ - CFE_MSG_Type_t ContentType; /**< Type of message content currently stored in the buffer */ + CFE_SB_MsgId_t MsgId; - bool NeedsUpdate; /**< If message should get its header fields automatically updated */ + size_t AllocatedSize; /**< Total size of this descriptor (including descriptor itself) */ + size_t ContentSize; /**< Actual size of message content currently stored in the buffer */ uint16 UseCount; /**< Number of active references to this buffer in the system */ @@ -168,10 +169,10 @@ typedef struct CFE_SB_BufferD typedef struct { CFE_SB_PipeId_t PipeId; - uint8 Opts; - uint8 Spare; CFE_ES_AppId_t AppId; osal_id_t SysQueueId; + uint8 Opts; + uint8 Spare; uint16 SendErrors; uint16 MaxQueueDepth; uint16 CurrentQueueDepth; @@ -259,29 +260,82 @@ typedef struct } CFE_SB_Global_t; /****************************************************************************** -** Typedef: CFE_SB_SendErrEventBuf_t -** -** Purpose: -** This structure is used to store event information during a send. -*/ + * Typedef: CFE_SB_PipeSetEntry_t + */ typedef struct { - uint32 EventId; - int32 OsStatus; CFE_SB_PipeId_t PipeId; -} CFE_SB_SendErrEventBuf_t; + osal_id_t SysQueueId; + uint16 PendingEventId; + int32 OsStatus; +} CFE_SB_PipeSetEntry_t; -/****************************************************************************** -** Typedef: CFE_SB_EventBuf_t -** -** Purpose: -** This structure is used to store event information during a send. -*/ +typedef enum +{ + CFE_SB_MessageTxn_TimeoutMode_POLL, /**< Transaction should not block */ + CFE_SB_MessageTxn_TimeoutMode_PEND, /**< Transaction should block indefinitely */ + CFE_SB_MessageTxn_TimeoutMode_TIMED /**< Transaction should block for a limited amount of time */ +} CFE_SB_MessageTxn_TimeoutMode_t; + +/** + * \brief Tracks the status of a message/buffer transmit transaction + * + * This includes the list of destinations to send to, and the status of each, + * as well as any pending event IDs from the transaction setup. + * + */ +typedef struct +{ + bool IsTransmit; + bool IsEndpoint; + + uint16 NumPipes; + uint16 MaxPipes; + uint16 NumPipeErrs; + uint16 TransactionEventId; + + CFE_SB_MessageTxn_TimeoutMode_t TimeoutMode; + int32_t UserTimeoutParam; + OS_time_t AbsTimeout; + + CFE_Status_t Status; + size_t ContentSize; + CFE_SB_MsgId_t RoutingMsgId; + + const void *RefMemPtr; + + CFE_SB_PipeSetEntry_t *PipeSet; +} CFE_SB_MessageTxn_State_t; + +/** + * \brief Tracks the status of a message/buffer transmit transaction + * + * This includes the list of destinations to send to, and the status of each, + * as well as any pending event IDs from the transaction setup. + * + */ +typedef struct +{ + CFE_SB_MessageTxn_State_t MessageTxn_State; + + CFE_SB_PipeSetEntry_t DestSet[CFE_PLATFORM_SB_MAX_DEST_PER_PKT]; +} CFE_SB_TransmitTxn_State_t; + +/** + * \brief Tracks the status of a message/buffer transmit transaction + * + * This includes the list of destinations to send to, and the status of each, + * as well as any pending event IDs from the transaction setup. + * + */ typedef struct { - uint32 EvtsToSnd; - CFE_SB_SendErrEventBuf_t EvtBuf[CFE_PLATFORM_SB_MAX_DEST_PER_PKT]; -} CFE_SB_EventBuf_t; + CFE_SB_MessageTxn_State_t MessageTxn_State; + + CFE_SB_PipeSetEntry_t Source; +} CFE_SB_ReceiveTxn_State_t; + +typedef bool (*CFE_SB_MessageTxn_PipeHandler_t)(CFE_SB_MessageTxn_State_t *, CFE_SB_PipeSetEntry_t *, void *); /* ** Software Bus Function Prototypes @@ -410,20 +464,6 @@ int32 CFE_SB_UnsubscribeWithAppId(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId, */ int32 CFE_SB_UnsubscribeFull(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId, uint8 Scope, CFE_ES_AppId_t AppId); -/*---------------------------------------------------------------------------------------*/ -/** - * \brief Internal routine to validate a transmit message before sending - * - * \param[in] MsgPtr Pointer to the message to validate - * \param[out] MsgIdPtr Message Id of message - * \param[out] SizePtr Size of message - * \param[out] RouteIdPtr Route ID of the message (invalid if none) - * - * \return Execution status, see \ref CFEReturnCodes - */ -int32 CFE_SB_TransmitMsgValidate(const CFE_MSG_Message_t *MsgPtr, CFE_SB_MsgId_t *MsgIdPtr, CFE_MSG_Size_t *SizePtr, - CFE_SBR_RouteId_t *RouteIdPtr); - /*---------------------------------------------------------------------------------------*/ /** * Release all zero-copy buffers associated with the given app ID. @@ -513,7 +553,7 @@ int32 CFE_SB_SendSubscriptionReport(CFE_SB_MsgId_t MsgId, CFE_SB_PipeId_t PipeId * * @returns grant/deny status */ -uint32 CFE_SB_RequestToSendEvent(CFE_ES_TaskId_t TaskId, uint32 Bit); +uint32 CFE_SB_RequestToSendEvent(CFE_ES_TaskId_t TaskId, int32 Bit); /*---------------------------------------------------------------------------------------*/ /** @@ -521,7 +561,7 @@ uint32 CFE_SB_RequestToSendEvent(CFE_ES_TaskId_t TaskId, uint32 Bit); * * This should be called after a successful CFE_SB_RequestToSendEvent() */ -void CFE_SB_FinishSendEvent(CFE_ES_TaskId_t TaskId, uint32 Bit); +void CFE_SB_FinishSendEvent(CFE_ES_TaskId_t TaskId, int32 Bit); /*---------------------------------------------------------------------------------------*/ /** @@ -619,41 +659,6 @@ CFE_SB_BufferD_t *CFE_SB_GetBufferFromPool(size_t MaxMsgSize); */ void CFE_SB_ReturnBufferToPool(CFE_SB_BufferD_t *bd); -/*---------------------------------------------------------------------------------------*/ -/** - * \brief Broadcast a SB buffer descriptor to all destinations in route - * - * Internal routine that implements the logic of transmitting a message buffer - * to all destinations subscribed in the SB route. - * - * As this function will broadcast the message to any number of destinations (0-many), - * and some may be successful and some may fail, the status cannot be expressed - * in any single error code, so this does not return any status. - * - * Instead, this routine handles all potential outcomes on its own, and does - * not expect the caller to handle any delivery issues. Also note that the general - * design pattern of the software bus is a "send and forget" model where the sender does - * not know (or care) what entities are subscribed to the data being generated. - * - * - For any undeliverable destination (limit, OSAL error, etc), a proper event is generated. - * - For any successful queueing, the buffer use count is incremented - * - * The caller is expected to hold a reference (use count) of the buffer prior to invoking - * this routine, representing itself, which is then consumed by this routine. - * - * \note _This call will "consume" the buffer by decrementing the buffer use count_ after - * broadcasting the message to all subscribed pipes. - * - * The caller should not access the buffer again after calling this function, as it may - * be deallocated at any time. If the caller wishes to continue accessing the buffer, - * it should explicitly increment the use count before calling this, which will prevent - * deallocation. - * - * \param[in] BufDscPtr Pointer to the buffer descriptor to broadcast - * \param[in] RouteId Route to send to - */ -void CFE_SB_BroadcastBufferToRoute(CFE_SB_BufferD_t *BufDscPtr, CFE_SBR_RouteId_t RouteId); - /*---------------------------------------------------------------------------------------*/ /** * \brief Perform basic sanity check on the Zero Copy handle @@ -745,6 +750,440 @@ CFE_SB_DestinationD_t *CFE_SB_GetDestPtr(CFE_SBR_RouteId_t RouteId, CFE_SB_PipeI **/ size_t CFE_SB_MsgHdrSize(const CFE_MSG_Message_t *MsgPtr); +/* + * Message Transmit/Receive Transaction implementation functions + */ + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Set up transaction initial state + * + * Helper function to initialize the common parts of a transmit/receive transaction + * + * \param[out] TxnPtr Transaction object to initialize + * \param[inout] PipeSet Pointer to pipe entry buffer + * \param[in] MaxPipes Number of entries in PipeSet + * \param[in] RefMemPtr User-supplied Opaque object pointer for event logging + */ +void CFE_SB_MessageTxn_Init(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_PipeSetEntry_t *PipeSet, uint16 MaxPipes, + const void *RefMemPtr); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Check transaction current state + * + * Checks if the transaction is in a good state. + * + * \param[in] TxnPtr Transaction object to check + * \returns Boolean indicating transaction condition + * \retval true if the transaction is OK + * \retval false if the transaction has an error + */ +static inline bool CFE_SB_MessageTxn_IsOK(const CFE_SB_MessageTxn_State_t *TxnPtr) +{ + return (TxnPtr->Status == CFE_SUCCESS && TxnPtr->TransactionEventId == 0); +} + + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Get transaction content size + * + * Obtains the size of the object/message being passed in this transaction + * + * \param[in] TxnPtr Transaction object + * \returns Size of message/object being passed + */ +static inline size_t CFE_SB_MessageTxn_GetContentSize(const CFE_SB_MessageTxn_State_t *TxnPtr) +{ + return TxnPtr->ContentSize; +} + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Get transaction MsgId used for routing + * + * Obtains the MsgId used for routing of the object/message being passed in this transaction + * + * \note This MsgId may be different than the one embedded within the message + * + * \param[in] TxnPtr Transaction object + * \returns MsgId used for routing of message/object being passed + */ +static inline CFE_SB_MsgId_t CFE_SB_MessageTxn_GetRoutingMsgId(CFE_SB_MessageTxn_State_t *TxnPtr) +{ + return TxnPtr->RoutingMsgId; +} + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Get transaction status code + * + * Obtains the CFE status code associated with the transaction + * + * \param[in] TxnPtr Transaction object + * \returns CFE Status code + */ +static inline CFE_Status_t CFE_SB_MessageTxn_GetStatus(CFE_SB_MessageTxn_State_t *TxnPtr) +{ + return TxnPtr->Status; +} + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Set transaction status code and event + * + * If an off-nominal condition occurs, this routine sets the specified status and/or event code + * that should be returned to the caller to indicate that condition. + * + * A value of 0 (which is an alias to #CFE_SUCCESS for status codes) can be passed for either + * parameter if there is no defined code for the particular condition. + * + * The transaction object stores the first non-zero value for event ID or status code. + * + * \param[inout] TxnPtr Transaction object + * \param[in] EventId The event ID to set + * \param[in] Status The status code to set + */ +void CFE_SB_MessageTxn_SetEventAndStatus(CFE_SB_MessageTxn_State_t *TxnPtr, uint16 EventId, CFE_Status_t Status); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Set transaction MsgId used for routing + * + * Configures the MsgId used for routing of the object/message being passed in this transaction + * + * \note This MsgId may be different than the one embedded within the message + * + * \param[inout] TxnPtr Transaction object + * \param[in] RoutingMsgId MsgId used for routing of message/object being passed + */ +void CFE_SB_MessageTxn_SetRoutingMsgId(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_MsgId_t RoutingMsgId); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Set transaction content size + * + * Configures the size of the object/message being passed in this transaction + * + * \param[inout] TxnPtr Transaction object + * \param[in] ContentSize Size of message/object being passed + */ +void CFE_SB_MessageTxn_SetContentSize(CFE_SB_MessageTxn_State_t *TxnPtr, size_t ContentSize); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Configure transaction time out + * + * Configures the relative timeout (in milliseconds) for this transaction. This is based + * on sampling the PSP clock at the time this function is invoked, and adding the relative + * time to it. + * + * This should be called early in the transaction configuration process. + * + * \param[inout] TxnPtr Transaction object + * \param[in] Timeout Timeout for message/object transaction + */ +void CFE_SB_MessageTxn_SetTimeout(CFE_SB_MessageTxn_State_t *TxnPtr, int32 Timeout); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Configure message endpoint (origination/termination) processing + * + * For a transmit transaction this enables origination processing. If configured as + * true, then the CFE_MSG_OriginationAction() function is invoked prior to sending the message. + * The specific action(s) depend on the MSG module implementation, and may include: + * - Updating the sequence number + * - Updating the message timestamp + * - Calculating/Appending any applicable CRC, checksum, or other error control field + * - Any other system-specific message header updates + * + * For a receive transaction, this enables message verification. If configured as + * true, then the CFE_MSG_VerificationAction() function will be invoked before the message buffer + * is returned to the caller. The verification criteria depends on the MSG implementation, but + * is typically expected to be the inverse of the origination processing. That is, items that + * were calculated or updated as part of the origination process can be verified during this step. + * If verification fails, then an event will be logged, the message will be dropped, and the + * implementation will get the next message in the queue (if any). + * + * \note The receive verification function in the default MSG implementation always returns + * success, thus should be backward compatible with previous CFE versions that did not perform + * any verification at the SB level, leaving it up to the individual app to check. To make use + * of this feature, the implementer must also provide an alternate implemenation of + * CFE_MSG_VerificationAction() that performs the desired checks for their particular use-case. + * + * \sa CFE_MSG_OriginationAction() + * \sa CFE_MSG_VerificationAction() + * + * \param[inout] TxnPtr Transaction object + * \param[in] IsEndpoint Pass as "true" to enable endpoint (origination/termination) message processing + */ +void CFE_SB_MessageTxn_SetEndpoint(CFE_SB_MessageTxn_State_t *TxnPtr, bool IsEndpoint); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Helper function to get the OSAL timeout to use + * + * Returns the relative timeout (in milliseconds) for any pending queue operation(s). + * This reflects the amount of time left from the initial call to CFE_SB_MessageTxn_SetTimeout(). + * + * If the original timeout was to PEND, this always returns -1 (OS_PEND). + * If the original timeout was to POLL, this always returns 0 (OS_CHECK). + * If the original timeout was some nonzero value, this returns the time left, in milliseconds, + * based on the current time as obtained from the PSP. + * + * \param[in] TxnPtr Transaction object + * \returns Timeout to use for OSAL routines that may block + */ +int32 CFE_SB_MessageTxn_GetOsTimeout(const CFE_SB_MessageTxn_State_t *TxnPtr); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Facilitates reporting of a single event related to a transmit transaction + * + * Internal routine that assembles the following information about how an event should + * be reported: + * + * - Complete message text (human-readable) + * - The type of event (information, error, etc) + * - The loop-detection bit to use (request to send/clear to send) + * + * \param[inout] TxnPtr Transaction object + * \param[in] TskId Calling task identifier + * \param[in] EventId Event to report + * \param[out] EvtMsg Message buffer + * \param[in] EvtMsgSize Size of EvtMsg buffer + * \param[in] ContextPtr Context information (may be NULL for general transaction events) + * \param[out] EventType Buffer to store event type + * \param[out] ReqBit Buffer to store request/loop detect bit + */ +void CFE_SB_MessageTxn_GetEventDetails(const CFE_SB_MessageTxn_State_t *TxnPtr, const CFE_SB_PipeSetEntry_t *ContextPtr, + uint16 EventId, CFE_ES_TaskId_t TskId, char *EvtMsg, size_t EvtMsgSize, + CFE_EVS_EventType_Enum_t *EventType, int32 *ReqBit); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Reports a single event related to a transmit transaction + * + * Internal routine that performs the basic flow of sending a single event ID related + * to a transmit transaction. This uses CFE_SB_TransmitTxn_GetEventDetails() to determine + * how the event should be reported, then checks/sets the Request bit (loop detect), sends the + * event, then clears the request bit. + * + * \param[in] TxnPtr Transaction object + * \param[in] ContextPtr Context information (may be NULL for general transaction events) + * \param[in] EventId Event to report + * + * \returns Boolean indicating if the event constitutes a transaction failure + * \retval true if the transmit transaction failed as a result (event is an error) + * \retval false if this was informational (event is benign, e.g. no subscribers) + */ +bool CFE_SB_MessageTxn_ReportSingleEvent(const CFE_SB_MessageTxn_State_t *TxnPtr, + const CFE_SB_PipeSetEntry_t *ContextPtr, uint16 EventId); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Implements all reporting and accouting associated with a transmit transaction + * + * Internal routine that sends all events and increments any telemetry counters associated + * with the transaction. This should be called as the last step of a transmit operation. + * + * \param[in] TxnPtr Transaction object + */ +void CFE_SB_MessageTxn_ReportEvents(const CFE_SB_MessageTxn_State_t *TxnPtr); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Process all pipes identified in the transaction object + * + * The provided HandlerFunc will be invoked for every item in the PipeSet within the transaction + * object. The handler function can stop the loop by returning "false". + * + * \param[in] HandlerFunc Handler function to call + * \param[inout] TxnPtr Transaction object, passed through to handler + * \param[inout] Arg Opaque argument to pass to handler + */ +void CFE_SB_MessageTxn_ProcessPipes(CFE_SB_MessageTxn_PipeHandler_t HandlerFunc, CFE_SB_MessageTxn_State_t *TxnPtr, + void *Arg); + +/* + * Receive Transaction implementation/helper functions + * These functions are specific to the receive-side operation + */ + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Initialize a receive transaction + * + * This should be the first function called to initialize a transaction state object + * that has been newly allocated. The object will be put into a safe initial state. + * + * The RefMemPtr should refer to the object/buffer that the user intends to receive from the + * software bus. This is kept as part of the transaction mainly for logging/event purposes, + * the address may be reported in events if the transaction is unsuccessful. + * + * \param[out] TxnPtr Transaction object to initialize + * \param[in] RefMemPtr Pointer to user object/buffer being received (opaque) + */ +CFE_SB_MessageTxn_State_t *CFE_SB_ReceiveTxn_Init(CFE_SB_ReceiveTxn_State_t *TxnPtr, const void *RefMemPtr); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Sets the Pipe ID to read for a receive transaction + * + * Currently, receive transactions only deal with a single pipe. This sets the pipe ID to + * read to receive the object. + * + * \param[inout] TxnPtr Transaction object + * \param[in] PipeId Pipe ID to read from + */ +void CFE_SB_ReceiveTxn_SetPipeId(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_PipeId_t PipeId); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Pipe handler function for receive transactions + * + * Helper function to implement reading of a pipe during a receive transaction. This + * is only used via CFE_SB_MessageTxn_ProcessPipes(), but declared here so it can be unit + * tested. + * + * This implements the logic of transmitting a message buffer to all destinations that were + * identified via CFE_SB_TransmitTxn_FindDestinations(). + * + * As this function will broadcast the message to any number of destinations (0-many), + * and some may be successful and some may fail, this function always returns true + * to continue the loop inside CFE_SB_MessageTxn_ProcessPipes(). + * + * The status/result of every queue write operation is kept as part of the transaction + * status object. Event reporting related those operations is deferred to the + * CFE_SB_TransmitTxn_ReportEvents() function, which should be invoked at the end + * of the transaction to report any delivery issues. + * + * The caller is expected to hold a reference (use count) of the buffer prior to invoking + * this routine, representing itself, plus the number of active destinations. + * + * \note If any queue write operation is _not_ successful, the use count will be decremented. + * \sa CFE_SB_MessageTxn_ProcessPipes + * + * \param[inout] TxnPtr Transaction object + * \param[in] ContextPtr Pointer to pipe entry within transaction + * \param[inout] Arg Opaque argument for API, should be a CFE_SB_BufferD_t** + * \returns always false to stop the parent loop (receive transactions only read a single pipe) + */ +bool CFE_SB_ReceiveTxn_PipeHandler(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_PipeSetEntry_t *ContextPtr, void *Arg); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Executes a receive transaction + * + * Implements reading of the pipe(s) and exporting information such as the content pointer, content size, + * and message ID. + * + * \param[inout] TxnPtr Transaction object + * \returns Pointer to buffer that was read + * \retval NULL if no message was read (e.g. if a timeout occurred or polling an empty queue) + */ +const CFE_SB_Buffer_t *CFE_SB_ReceiveTxn_Execute(CFE_SB_MessageTxn_State_t *TxnPtr); + +/* + * Transmit Transaction implementation/helper functions + * These functions are specific to the transmit-side operation + */ + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Initialize a transmit transaction + * + * This should be the first function called to initialize a transaction state object + * that has been newly allocated. The object will be put into a safe initial state. + * + * The RefMemPtr should refer to the object/buffer that the user intends to send on the + * software bus. This is kept as part of the transaction mainly for logging/event purposes, + * the address may be reported in events if the transaction is unsuccessful. + * + * \param[out] TxnPtr Transaction object to initialize + * \param[in] RefMemPtr Pointer to user object/buffer being transmitted (opaque) + */ +CFE_SB_MessageTxn_State_t *CFE_SB_TransmitTxn_Init(CFE_SB_TransmitTxn_State_t *TxnPtr, const void *RefMemPtr); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Set up a transmit transaction from a CFE MSG object + * + * Extracts the routing information - specifically MsgId and Size - from the message content. + * + * Software Bus transmit operations typically operate based on the MsgId and Size information + * that the application stores the message header prior to transmission. This function implements + * that traditional behavior by pulling this information out of the message and caching it in + * the transaction state object for use during routing and delivery of the object. + * + * The information is extracted from the message using the CFE MSG API calls. + * + * \param[inout] TxnPtr Transaction object + * \param[in] MsgPtr Pointer to message being transmitted + */ +void CFE_SB_TransmitTxn_SetupFromMsg(CFE_SB_MessageTxn_State_t *TxnPtr, const CFE_MSG_Message_t *MsgPtr); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Find the complete set of destination pipes for the given transaction + * + * Looks up the routing information (via the SBR subsystem/module) and collects the set of + * active destinations (Pipe IDs) that the message will need to be broadcast to. + * + * If no destinations are found, then this sets the transaction status to generate a + * NO SUBSCRIBERS event, but it does not actually send the event from here. + * + * \sa CFE_SB_TransmitTxn_ReportEvents() + * + * \note This also increments the buffer use count for every destination found, in anticipation + * of writing the buffer address into the underlying queue. If the subsequent write is not + * actually successful, then the count must be decremented accordingly, to keep the reference + * counts correct. + * + * \param[inout] TxnPtr Transaction object + * \param[inout] BufDscPtr Buffer descriptor that is pending broadcast + */ +void CFE_SB_TransmitTxn_FindDestinations(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_BufferD_t *BufDscPtr); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Pipe handler function for transmit transactions + * + * Helper function to implement reading of a pipe during a transmit transaction. This + * is only used via CFE_SB_MessageTxn_ProcessPipes(), but declared here so it can be unit + * tested. + * + * \sa CFE_SB_MessageTxn_ProcessPipes + * + * \param[inout] TxnPtr Transaction object + * \param[in] ContextPtr Pointer to pipe entry within transaction + * \param[inout] Arg Opaque argument for API, should be a CFE_SB_BufferD_t* + * \returns always true to continue the parent loop (transmit transactions process all pipes) + */ +bool CFE_SB_TransmitTxn_PipeHandler(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_PipeSetEntry_t *ContextPtr, void *Arg); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Executes the transmit transaction + * + * Internal routine that finds all active destinations for the route and broadcasts the + * buffer to those destinations. Any errors or off-nominal events that occur will be stored + * in the transaction object for deferred reporting via CFE_SB_TransmitTxn_ReportEvents(). + * + * \note Upon successful operation, this function decrements the use count of the buffer. + * The calling routine is expected to hold one reference to the buffer being transmitted, which + * is then consumed by this call. If the caller intends to retain a reference to the buffer, + * the use count must be incremented prior to invoking this function. + * + * However, if this returns a non-successful status code then the use count will _not_ be + * decremented - but this can generally only fail if the passed-in buffer does not validate. + * + * \param[inout] TxnPtr Transaction object + * \param[inout] BufPtr Buffer object that is pending to be broadcast + */ +void CFE_SB_TransmitTxn_Execute(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_Buffer_t *BufPtr); + /* * Software Bus Message Handler Function prototypes */ diff --git a/modules/sb/ut-coverage/sb_UT.c b/modules/sb/ut-coverage/sb_UT.c index 571c95a9e..8d7bb9b7a 100644 --- a/modules/sb/ut-coverage/sb_UT.c +++ b/modules/sb/ut-coverage/sb_UT.c @@ -390,7 +390,7 @@ void Test_SB_Main_RcvErr(void) UT_SetDeferredRetcode(UT_KEY(OS_QueueGet), 1, -1); CFE_SB_TaskMain(); - CFE_UtAssert_EVENTCOUNT(6); + CFE_UtAssert_EVENTCOUNT(7); CFE_UtAssert_EVENTSENT(CFE_SB_INIT_EID); @@ -2883,6 +2883,591 @@ void Test_Unsubscribe_GetDestPtr(void) CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(TestPipe2)); } +void Test_TransmitTxn_Init(void) +{ + /* Test case for: + * void CFE_SB_TransmitTxn_Init(CFE_SB_TransmitTxn_State_t *TxnPtr, const void *RefMemPtr); + */ + CFE_SB_TransmitTxn_State_t TxnBuf; + CFE_SB_MessageTxn_State_t *Txn; + uint32 MyData; + + /* Call to ensure coverage */ + memset(&Txn, 0xEE, sizeof(Txn)); + MyData = 123; + UtAssert_ADDRESS_EQ(Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &MyData), &TxnBuf); + + /* This should have cleared everything */ + UtAssert_ADDRESS_EQ(Txn->RefMemPtr, &MyData); + UtAssert_ZERO(Txn->NumPipes); + UtAssert_ZERO(Txn->NumPipeErrs); + UtAssert_ZERO(Txn->TransactionEventId); + UtAssert_ZERO(Txn->TimeoutMode); + UtAssert_ZERO(Txn->ContentSize); + UtAssert_BOOL_FALSE(Txn->IsEndpoint); +} + +void Test_MessageTxn_SetEventAndStatus(void) +{ + /* Test function for: + * void CFE_SB_MessageTxn_SetEventAndStatus(CFE_SB_MessageTxn_State_t *TxnPtr, + * uint16 EventId, CFE_Status_t Status) + */ + CFE_SB_MessageTxn_State_t Txn; + + /* + * although this function is used throughout the other tests, this is needed to target + * certain branches/paths that don't get executed. Namely - this should only store + * the first error/event that occurs. + */ + + /* Set only an event ID first, then attempt to set both */ + memset(&Txn, 0, sizeof(Txn)); + + UtAssert_BOOL_TRUE(CFE_SB_MessageTxn_IsOK(&Txn)); + + CFE_SB_MessageTxn_SetEventAndStatus(&Txn, CFE_SB_MSG_TOO_BIG_EID, CFE_SUCCESS); + UtAssert_UINT16_EQ(Txn.TransactionEventId, CFE_SB_MSG_TOO_BIG_EID); + UtAssert_INT32_EQ(Txn.Status, CFE_SUCCESS); + UtAssert_BOOL_FALSE(CFE_SB_MessageTxn_IsOK(&Txn)); + + CFE_SB_MessageTxn_SetEventAndStatus(&Txn, CFE_SB_SEND_INV_MSGID_EID, CFE_SB_BAD_ARGUMENT); + UtAssert_UINT16_EQ(Txn.TransactionEventId, CFE_SB_MSG_TOO_BIG_EID); + UtAssert_INT32_EQ(Txn.Status, CFE_SB_BAD_ARGUMENT); + UtAssert_BOOL_FALSE(CFE_SB_MessageTxn_IsOK(&Txn)); + + /* Set only a status code first, then attempt to set both */ + memset(&Txn, 0, sizeof(Txn)); + + CFE_SB_MessageTxn_SetEventAndStatus(&Txn, 0, CFE_SB_BUF_ALOC_ERR); + UtAssert_UINT16_EQ(Txn.TransactionEventId, 0); + UtAssert_INT32_EQ(Txn.Status, CFE_SB_BUF_ALOC_ERR); + UtAssert_BOOL_FALSE(CFE_SB_MessageTxn_IsOK(&Txn)); + + CFE_SB_MessageTxn_SetEventAndStatus(&Txn, CFE_SB_SEND_INV_MSGID_EID, CFE_SB_BAD_ARGUMENT); + UtAssert_UINT16_EQ(Txn.TransactionEventId, CFE_SB_SEND_INV_MSGID_EID); + UtAssert_INT32_EQ(Txn.Status, CFE_SB_BUF_ALOC_ERR); + UtAssert_BOOL_FALSE(CFE_SB_MessageTxn_IsOK(&Txn)); +} + +void Test_MessageTxn_Timeout(void) +{ + /* Test case for: + * void CFE_SB_MessageTxn_SetTimeout(CFE_SB_MessageTxn_State_t *TxnPtr, int32 Timeout); + * int32 CFE_SB_MessageTxn_GetOsTimeout(const CFE_SB_MessageTxn_State_t *TxnPtr); + */ + CFE_SB_MessageTxn_State_t Txn; + OS_time_t TestTime; + + memset(&Txn, 0, sizeof(Txn)); + + UtAssert_VOIDCALL(CFE_SB_MessageTxn_SetTimeout(&Txn, CFE_SB_POLL)); + UtAssert_INT32_EQ(CFE_SB_MessageTxn_GetOsTimeout(&Txn), OS_CHECK); + + UtAssert_VOIDCALL(CFE_SB_MessageTxn_SetTimeout(&Txn, CFE_SB_PEND_FOREVER)); + UtAssert_INT32_EQ(CFE_SB_MessageTxn_GetOsTimeout(&Txn), OS_PEND); + + TestTime = OS_TimeFromTotalSeconds(1000000000); + UT_SetDataBuffer(UT_KEY(CFE_PSP_GetTime), &TestTime, sizeof(TestTime), false); + UtAssert_VOIDCALL(CFE_SB_MessageTxn_SetTimeout(&Txn, 100)); + + UT_SetDataBuffer(UT_KEY(CFE_PSP_GetTime), &TestTime, sizeof(TestTime), false); + UtAssert_INT32_EQ(CFE_SB_MessageTxn_GetOsTimeout(&Txn), 100); + + TestTime = OS_TimeAdd(TestTime, OS_TimeFromTotalMilliseconds(10)); + UT_SetDataBuffer(UT_KEY(CFE_PSP_GetTime), &TestTime, sizeof(TestTime), false); + UtAssert_INT32_EQ(CFE_SB_MessageTxn_GetOsTimeout(&Txn), 90); + + TestTime = OS_TimeAdd(TestTime, OS_TimeFromTotalSeconds(1)); + UT_SetDataBuffer(UT_KEY(CFE_PSP_GetTime), &TestTime, sizeof(TestTime), false); + UtAssert_INT32_EQ(CFE_SB_MessageTxn_GetOsTimeout(&Txn), OS_CHECK); + + UtAssert_VOIDCALL(CFE_SB_MessageTxn_SetTimeout(&Txn, -1000)); + UtAssert_INT32_EQ(Txn.Status, CFE_SB_BAD_ARGUMENT); +} + +void Test_MessageTxn_SetupFromMsg(void) +{ + /* Test case for: + * CFE_Status_t CFE_SB_TransmitTxn_SetupFromMsg(CFE_SB_MessageTxn_State_t *TxnPtr, const CFE_MSG_Message_t + * *MsgPtr); + */ + CFE_SB_MessageTxn_State_t Txn; + CFE_MSG_Message_t Msg; + CFE_SB_MsgId_t MsgId; + CFE_MSG_Size_t MsgSize; + + memset(&Msg, 0, sizeof(Msg)); + + /* Transaction already failed case (no-op) */ + memset(&Txn, 0, sizeof(Txn)); + Txn.Status = -20; + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_SetupFromMsg(&Txn, &Msg)); + UtAssert_INT32_EQ(Txn.Status, -20); + + /* CFE_MSG_GetMsgId() fail case */ + memset(&Txn, 0, sizeof(Txn)); + UT_SetDeferredRetcode(UT_KEY(CFE_MSG_GetMsgId), 1, -10); + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_SetupFromMsg(&Txn, &Msg)); + UtAssert_INT32_EQ(Txn.Status, -10); + UtAssert_UINT32_EQ(Txn.TransactionEventId, CFE_SB_SEND_BAD_ARG_EID); + + /* Invalid MsgId case */ + memset(&Txn, 0, sizeof(Txn)); + MsgId = CFE_SB_INVALID_MSG_ID; + UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_SetupFromMsg(&Txn, &Msg)); + UtAssert_INT32_EQ(Txn.Status, CFE_SB_BAD_ARGUMENT); + UtAssert_UINT32_EQ(Txn.TransactionEventId, CFE_SB_SEND_INV_MSGID_EID); + + /* CFE_MSG_GetSize() fail case */ + memset(&Txn, 0, sizeof(Txn)); + MsgId = SB_UT_TLM_MID; + UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); + UT_SetDeferredRetcode(UT_KEY(CFE_MSG_GetSize), 1, -11); + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_SetupFromMsg(&Txn, &Msg)); + UtAssert_INT32_EQ(Txn.Status, -11); + UtAssert_UINT32_EQ(Txn.TransactionEventId, CFE_SB_SEND_BAD_ARG_EID); + + /* Message too big case */ + memset(&Txn, 0, sizeof(Txn)); + MsgId = SB_UT_TLM_MID; + UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); + MsgSize = 1 + CFE_MISSION_SB_MAX_SB_MSG_SIZE; + UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &MsgSize, sizeof(MsgSize), false); + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_SetupFromMsg(&Txn, &Msg)); + UtAssert_INT32_EQ(Txn.Status, CFE_SB_MSG_TOO_BIG); + UtAssert_UINT32_EQ(Txn.TransactionEventId, CFE_SB_MSG_TOO_BIG_EID); + + /* Nominal case */ + memset(&Txn, 0, sizeof(Txn)); + MsgId = SB_UT_TLM_MID; + UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); + MsgSize = sizeof(Msg); + UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &MsgSize, sizeof(MsgSize), false); + + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_SetupFromMsg(&Txn, &Msg)); + UtAssert_INT32_EQ(Txn.Status, CFE_SUCCESS); + UtAssert_ZERO(Txn.TransactionEventId); + CFE_UtAssert_MSGID_EQ(Txn.RoutingMsgId, SB_UT_TLM_MID); + UtAssert_UINT32_EQ(Txn.ContentSize, sizeof(Msg)); +} + +void Test_TransmitTxn_FindDestinations(void) +{ + /* Test case for: + * void CFE_SB_TransmitTxn_FindDestinations(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_BufferD_t *BufDscPtr); + */ + + CFE_SB_TransmitTxn_State_t TxnBuf; + CFE_SB_MessageTxn_State_t *Txn; + CFE_SB_BufferD_t BufDsc; + CFE_SB_PipeD_t * PipeDscPtr; + CFE_SB_PipeId_t PipeId = CFE_SB_INVALID_PIPE; + CFE_SB_MsgId_t MsgId = SB_UT_TLM_MID; + CFE_SBR_RouteId_t RouteId; + CFE_SB_DestinationD_t * DestPtr; + + memset(&BufDsc, 0, sizeof(BufDsc)); + CFE_SB_TrackingListReset(&BufDsc.Link); /* so tracking list ops work */ + + CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, 2, "TestPipe")); + CFE_UtAssert_SETUP(CFE_SB_SubscribeFull(MsgId, PipeId, CFE_SB_DEFAULT_QOS, 2, CFE_SB_MSG_GLOBAL)); + PipeDscPtr = CFE_SB_LocatePipeDescByID(PipeId); + RouteId = CFE_SBR_GetRouteId(MsgId); + DestPtr = CFE_SB_GetDestPtr(RouteId, PipeId); + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &BufDsc.Content); + PipeDscPtr->PeakQueueDepth = 1; + + CFE_SB_Global.HKTlmMsg.Payload.NoSubscribersCounter = 0; + + /* No subscriber case */ + Txn->RoutingMsgId = CFE_SB_INVALID_MSG_ID; + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_FindDestinations(Txn, &BufDsc)); + UtAssert_UINT32_EQ(CFE_SB_Global.HKTlmMsg.Payload.NoSubscribersCounter, 1); + UtAssert_UINT32_EQ(Txn->TransactionEventId, CFE_SB_SEND_NO_SUBS_EID); + UtAssert_UINT32_EQ(BufDsc.UseCount, 0); + + /* Nominal Case 1 */ + memset(&BufDsc, 0, sizeof(BufDsc)); + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &BufDsc.Content); + CFE_SB_TrackingListReset(&BufDsc.Link); /* so tracking list ops work */ + Txn->RoutingMsgId = MsgId; + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_FindDestinations(Txn, &BufDsc)); + UtAssert_UINT32_EQ(Txn->NumPipes, 1); + CFE_UtAssert_RESOURCEID_EQ(Txn->PipeSet[0].PipeId, PipeId); + UtAssert_UINT32_EQ(Txn->PipeSet[0].PendingEventId, 0); + UtAssert_UINT32_EQ(BufDsc.UseCount, 1); + UtAssert_UINT32_EQ(DestPtr->BuffCount, 1); + UtAssert_UINT32_EQ(PipeDscPtr->CurrentQueueDepth, 1); + UtAssert_UINT32_EQ(PipeDscPtr->PeakQueueDepth, 1); + UtAssert_UINT32_EQ(PipeDscPtr->SendErrors, 0); + UtAssert_UINT32_EQ(Txn->NumPipeErrs, 0); + + /* Nominal Case 2 */ + memset(&BufDsc, 0, sizeof(BufDsc)); + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &BufDsc.Content); + CFE_SB_TrackingListReset(&BufDsc.Link); /* so tracking list ops work */ + Txn->RoutingMsgId = MsgId; + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_FindDestinations(Txn, &BufDsc)); + UtAssert_UINT32_EQ(Txn->NumPipes, 1); + CFE_UtAssert_RESOURCEID_EQ(Txn->PipeSet[0].PipeId, PipeId); + UtAssert_UINT32_EQ(Txn->PipeSet[0].PendingEventId, 0); + UtAssert_UINT32_EQ(BufDsc.UseCount, 1); + UtAssert_UINT32_EQ(DestPtr->BuffCount, 2); + UtAssert_UINT32_EQ(PipeDscPtr->CurrentQueueDepth, 2); + UtAssert_UINT32_EQ(PipeDscPtr->PeakQueueDepth, 2); + UtAssert_UINT32_EQ(PipeDscPtr->SendErrors, 0); + UtAssert_UINT32_EQ(Txn->NumPipeErrs, 0); + + /* MsgLim Error Case */ + memset(&BufDsc, 0, sizeof(BufDsc)); + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &BufDsc.Content); + CFE_SB_TrackingListReset(&BufDsc.Link); /* so tracking list ops work */ + Txn->RoutingMsgId = MsgId; + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_FindDestinations(Txn, &BufDsc)); + UtAssert_UINT32_EQ(Txn->NumPipes, 1); + CFE_UtAssert_RESOURCEID_EQ(Txn->PipeSet[0].PipeId, PipeId); + UtAssert_UINT32_EQ(Txn->PipeSet[0].PendingEventId, CFE_SB_MSGID_LIM_ERR_EID); + UtAssert_ZERO(BufDsc.UseCount); + UtAssert_UINT32_EQ(DestPtr->BuffCount, 2); + UtAssert_UINT32_EQ(PipeDscPtr->CurrentQueueDepth, 2); + UtAssert_UINT32_EQ(PipeDscPtr->PeakQueueDepth, 2); + UtAssert_UINT32_EQ(PipeDscPtr->SendErrors, 1); + UtAssert_UINT32_EQ(Txn->NumPipeErrs, 1); + DestPtr->BuffCount = 0; + + /* Destination Inactive Case */ + memset(&BufDsc, 0, sizeof(BufDsc)); + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &BufDsc.Content); + CFE_SB_TrackingListReset(&BufDsc.Link); /* so tracking list ops work */ + Txn->RoutingMsgId = MsgId; + DestPtr->Active = CFE_SB_INACTIVE; + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_FindDestinations(Txn, &BufDsc)); + UtAssert_ZERO(Txn->NumPipes); + UtAssert_UINT32_EQ(BufDsc.UseCount, 0); + UtAssert_UINT32_EQ(DestPtr->BuffCount, 0); + DestPtr->Active = CFE_SB_ACTIVE; + + /* Pipe "Ignore Mine" Option Case w/Matching AppID */ + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &BufDsc.Content); + Txn->RoutingMsgId = MsgId; + PipeDscPtr->Opts |= CFE_SB_PIPEOPTS_IGNOREMINE; + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_FindDestinations(Txn, &BufDsc)); + UtAssert_ZERO(Txn->NumPipes); + UtAssert_UINT32_EQ(BufDsc.UseCount, 0); + UtAssert_UINT32_EQ(DestPtr->BuffCount, 0); + + /* Pipe "Ignore Mine" Option Case w/Non-Matching AppID */ + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &BufDsc.Content); + Txn->RoutingMsgId = MsgId; + PipeDscPtr->AppId = CFE_ES_APPID_UNDEFINED; + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_FindDestinations(Txn, &BufDsc)); + UtAssert_UINT32_EQ(Txn->NumPipes, 1); + UtAssert_UINT32_EQ(BufDsc.UseCount, 1); + UtAssert_UINT32_EQ(DestPtr->BuffCount, 1); + CFE_ES_GetAppID(&PipeDscPtr->AppId); + PipeDscPtr->Opts &= ~CFE_SB_PIPEOPTS_IGNOREMINE; + + /* DestPtr List too long - this emulates a hypothetical bug in SBR allowing list to grow too long */ + /* Hack to make it infinite length */ + DestPtr->Next = DestPtr; + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &BufDsc.Content); + Txn->RoutingMsgId = MsgId; + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_FindDestinations(Txn, &BufDsc)); + UtAssert_UINT32_EQ(Txn->NumPipes, CFE_PLATFORM_SB_MAX_DEST_PER_PKT); + + CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); +} + +void Test_TransmitTxn_PipeHandler(void) +{ + /* Test function for: + * bool CFE_SB_TransmitTxn_PipeHandler(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_PipeSetEntry_t *ContextPtr, void + * *Arg); + */ + + CFE_SB_TransmitTxn_State_t TxnBuf; + CFE_SB_MessageTxn_State_t *Txn; + CFE_SB_PipeId_t PipeId = CFE_SB_INVALID_PIPE; + CFE_SB_MsgId_t MsgId = SB_UT_TLM_MID; + CFE_SB_BufferD_t SBBufD; + + memset(&SBBufD, 0, sizeof(SBBufD)); + memset(&TxnBuf, 0, sizeof(TxnBuf)); + CFE_SB_TrackingListReset(&SBBufD.Link); /* so tracking list ops work */ + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &SBBufD.Content); + + CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, 3, "TestPipe")); + CFE_UtAssert_SETUP(CFE_SB_Subscribe(MsgId, PipeId)); + + SBBufD.DestRouteId = CFE_SBR_GetRouteId(MsgId); + + CFE_SB_LocatePipeDescByID(PipeId)->CurrentQueueDepth = 1; + CFE_SB_GetDestPtr(SBBufD.DestRouteId, PipeId)->BuffCount = 1; + + Txn->NumPipes = 6; + Txn->NumPipeErrs = 1; + Txn->PipeSet[0].PipeId = PipeId; + Txn->PipeSet[1].PipeId = PipeId; + Txn->PipeSet[2].PipeId = PipeId; + Txn->PipeSet[2].PendingEventId = 1; + Txn->PipeSet[3].PipeId = PipeId; + Txn->PipeSet[4].PipeId = PipeId; + Txn->PipeSet[5].PipeId = SB_UT_ALTERNATE_INVALID_PIPEID; + + UT_SetDeferredRetcode(UT_KEY(OS_QueuePut), 2, OS_QUEUE_FULL); + UT_SetDeferredRetcode(UT_KEY(OS_QueuePut), 1, OS_ERROR); + UT_SetDeferredRetcode(UT_KEY(OS_QueuePut), 1, OS_ERROR); + UT_SetDeferredRetcode(UT_KEY(OS_QueuePut), 1, OS_ERROR); + + UtAssert_VOIDCALL(CFE_SB_MessageTxn_ProcessPipes(CFE_SB_TransmitTxn_PipeHandler, Txn, &SBBufD)); + UtAssert_STUB_COUNT(OS_QueuePut, 5); + UtAssert_UINT32_EQ(Txn->NumPipeErrs, 5); + UtAssert_UINT32_EQ(Txn->PipeSet[0].PendingEventId, 0); + UtAssert_UINT32_EQ(Txn->PipeSet[1].PendingEventId, CFE_SB_Q_FULL_ERR_EID); + UtAssert_UINT32_EQ(Txn->PipeSet[2].PendingEventId, 1); + UtAssert_UINT32_EQ(Txn->PipeSet[3].PendingEventId, CFE_SB_Q_WR_ERR_EID); + UtAssert_UINT32_EQ(Txn->PipeSet[4].PendingEventId, CFE_SB_Q_WR_ERR_EID); + UtAssert_UINT32_EQ(Txn->PipeSet[5].PendingEventId, CFE_SB_Q_WR_ERR_EID); + + CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); +} + +void Test_TransmitTxn_Execute(void) +{ + /* Test case for: + * CFE_Status_t CFE_SB_TransmitTxn_Execute(CFE_SB_MessageTxn_State_t *TxnPtr, CFE_SB_Buffer_t *BufPtr); + */ + CFE_SB_BufferD_t SBBufD; + CFE_SB_TransmitTxn_State_t TxnBuf; + CFE_SB_MessageTxn_State_t *Txn; + CFE_ES_AppId_t MyAppId; + CFE_SB_PipeId_t PipeId = CFE_SB_INVALID_PIPE; + CFE_SB_MsgId_t MsgId = SB_UT_TLM_MID; + + memset(&SBBufD, 0, sizeof(SBBufD)); + memset(&TxnBuf, 0, sizeof(TxnBuf)); + CFE_SB_TrackingListReset(&SBBufD.Link); /* so tracking list ops work */ + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &SBBufD.Content); + CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, 2, "TestPipe")); + CFE_UtAssert_SETUP(CFE_ES_GetAppID(&MyAppId)); + + /* no subs - this should still keep status as SUCCESS but trigger CFE_SB_SEND_NO_SUBS_EID event */ + SBBufD.AppId = MyAppId; + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_Execute(Txn, &SBBufD.Content)); + UtAssert_STUB_COUNT(OS_QueuePut, 0); + UtAssert_UINT32_EQ(Txn->TransactionEventId, CFE_SB_SEND_NO_SUBS_EID); + UtAssert_INT32_EQ(Txn->Status, CFE_SUCCESS); + UtAssert_BOOL_FALSE(CFE_RESOURCEID_TEST_DEFINED(SBBufD.AppId)); + + /* add a subscriber - nominal case */ + CFE_UtAssert_SETUP(CFE_SB_SubscribeFull(MsgId, PipeId, CFE_SB_DEFAULT_QOS, 2, CFE_SB_MSG_GLOBAL)); + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &SBBufD.Content); + Txn->RoutingMsgId = MsgId; + SBBufD.AppId = MyAppId; + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_Execute(Txn, &SBBufD.Content)); + UtAssert_BOOL_TRUE(CFE_SB_MessageTxn_IsOK(Txn)); + UtAssert_STUB_COUNT(OS_QueuePut, 1); + UtAssert_BOOL_FALSE(CFE_RESOURCEID_TEST_DEFINED(SBBufD.AppId)); + + /* error writing to the pipe - this also still keeps status as SUCCESS but trigger CFE_SB_Q_WR_ERR_EID */ + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &SBBufD.Content); + Txn->RoutingMsgId = MsgId; + SBBufD.AppId = MyAppId; + UT_SetDeferredRetcode(UT_KEY(OS_QueuePut), 1, OS_ERROR); + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_Execute(Txn, &SBBufD.Content)); + UtAssert_BOOL_TRUE(CFE_SB_MessageTxn_IsOK(Txn)); + UtAssert_UINT32_EQ(Txn->PipeSet[0].PendingEventId, CFE_SB_Q_WR_ERR_EID); + UtAssert_STUB_COUNT(OS_QueuePut, 2); + UtAssert_BOOL_FALSE(CFE_RESOURCEID_TEST_DEFINED(SBBufD.AppId)); + + /* Validation fail */ + Txn = CFE_SB_TransmitTxn_Init(&TxnBuf, &SBBufD.Content); + UtAssert_VOIDCALL(CFE_SB_TransmitTxn_Execute(Txn, &SBBufD.Content)); + UtAssert_BOOL_FALSE(CFE_SB_MessageTxn_IsOK(Txn)); + UtAssert_INT32_EQ(Txn->Status, CFE_SB_BUFFER_INVALID); + UtAssert_STUB_COUNT(OS_QueuePut, 2); + + CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); +} + +void Test_MessageTxn_GetEventDetails(void) +{ + /* Test case for: + * void CFE_SB_MessageTxn_GetEventDetails(const CFE_SB_MessageTxn_State_t *TxnPtr, CFE_ES_TaskId_t TskId, uint16 + * EventId,char *EvtMsg, size_t EvtMsgSize, const CFE_SB_PipeSetEntry_t *ContextPtr,CFE_EVS_EventType_Enum_t + * *EventType, int32 *ReqBit); + */ + + CFE_SB_MessageTxn_State_t Txn; + CFE_SB_PipeSetEntry_t Entry; + CFE_ES_TaskId_t MyTskId; + char EvtMsg[64]; + CFE_EVS_EventType_Enum_t EvType; + int32 EvReqBit; + CFE_SB_PipeId_t PipeId; + + CFE_UtAssert_SETUP(CFE_ES_GetTaskID(&MyTskId)); + CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, 3, "TestPipe")); + CFE_SB_MessageTxn_Init(&Txn, &Entry, 1, &PipeId); + EvReqBit = 0; + EvType = 0; + Entry.PipeId = PipeId; + + memset(EvtMsg, 0xAA, sizeof(EvtMsg)); + UtAssert_VOIDCALL( + CFE_SB_MessageTxn_GetEventDetails(&Txn, NULL, 0, MyTskId, EvtMsg, sizeof(EvtMsg), &EvType, &EvReqBit)); + UtAssert_ZERO(EvtMsg[0]); + UtAssert_ZERO(EvReqBit); + + memset(EvtMsg, 0xAA, sizeof(EvtMsg)); + UtAssert_VOIDCALL(CFE_SB_MessageTxn_GetEventDetails(&Txn, NULL, CFE_SB_SEND_BAD_ARG_EID, MyTskId, EvtMsg, + sizeof(EvtMsg), &EvType, &EvReqBit)); + UtAssert_NONZERO(EvtMsg[0]); + UtAssert_NOT_NULL(memchr(EvtMsg, 0, sizeof(EvtMsg))); + UtAssert_UINT32_EQ(EvReqBit, CFE_SB_SEND_BAD_ARG_EID_BIT); + + memset(EvtMsg, 0xAA, sizeof(EvtMsg)); + UtAssert_VOIDCALL(CFE_SB_MessageTxn_GetEventDetails(&Txn, NULL, CFE_SB_SEND_INV_MSGID_EID, MyTskId, EvtMsg, + sizeof(EvtMsg), &EvType, &EvReqBit)); + UtAssert_NONZERO(EvtMsg[0]); + UtAssert_UINT32_EQ(EvReqBit, CFE_SB_SEND_INV_MSGID_EID_BIT); + UtAssert_NOT_NULL(memchr(EvtMsg, 0, sizeof(EvtMsg))); + + memset(EvtMsg, 0xAA, sizeof(EvtMsg)); + UtAssert_VOIDCALL(CFE_SB_MessageTxn_GetEventDetails(&Txn, NULL, CFE_SB_MSG_TOO_BIG_EID, MyTskId, EvtMsg, + sizeof(EvtMsg), &EvType, &EvReqBit)); + UtAssert_NONZERO(EvtMsg[0]); + UtAssert_NOT_NULL(memchr(EvtMsg, 0, sizeof(EvtMsg))); + UtAssert_UINT32_EQ(EvReqBit, CFE_SB_MSG_TOO_BIG_EID_BIT); + + memset(EvtMsg, 0xAA, sizeof(EvtMsg)); + UtAssert_VOIDCALL(CFE_SB_MessageTxn_GetEventDetails(&Txn, NULL, CFE_SB_SEND_NO_SUBS_EID, MyTskId, EvtMsg, + sizeof(EvtMsg), &EvType, &EvReqBit)); + UtAssert_NONZERO(EvtMsg[0]); + UtAssert_NOT_NULL(memchr(EvtMsg, 0, sizeof(EvtMsg))); + UtAssert_UINT32_EQ(EvReqBit, CFE_SB_SEND_NO_SUBS_EID_BIT); + + memset(EvtMsg, 0xAA, sizeof(EvtMsg)); + UtAssert_VOIDCALL(CFE_SB_MessageTxn_GetEventDetails(&Txn, &Entry, CFE_SB_GET_BUF_ERR_EID, MyTskId, EvtMsg, + sizeof(EvtMsg), &EvType, &EvReqBit)); + UtAssert_NONZERO(EvtMsg[0]); + UtAssert_NOT_NULL(memchr(EvtMsg, 0, sizeof(EvtMsg))); + UtAssert_UINT32_EQ(EvReqBit, CFE_SB_GET_BUF_ERR_EID_BIT); + + memset(EvtMsg, 0xAA, sizeof(EvtMsg)); + UtAssert_VOIDCALL(CFE_SB_MessageTxn_GetEventDetails(&Txn, &Entry, CFE_SB_MSGID_LIM_ERR_EID, MyTskId, EvtMsg, + sizeof(EvtMsg), &EvType, &EvReqBit)); + UtAssert_NONZERO(EvtMsg[0]); + UtAssert_NOT_NULL(memchr(EvtMsg, 0, sizeof(EvtMsg))); + UtAssert_UINT32_EQ(EvReqBit, CFE_SB_MSGID_LIM_ERR_EID_BIT); + + memset(EvtMsg, 0xAA, sizeof(EvtMsg)); + UtAssert_VOIDCALL(CFE_SB_MessageTxn_GetEventDetails(&Txn, &Entry, CFE_SB_Q_FULL_ERR_EID, MyTskId, EvtMsg, + sizeof(EvtMsg), &EvType, &EvReqBit)); + UtAssert_NONZERO(EvtMsg[0]); + UtAssert_NOT_NULL(memchr(EvtMsg, 0, sizeof(EvtMsg))); + UtAssert_UINT32_EQ(EvReqBit, CFE_SB_Q_FULL_ERR_EID_BIT); + + memset(EvtMsg, 0xAA, sizeof(EvtMsg)); + UtAssert_VOIDCALL(CFE_SB_MessageTxn_GetEventDetails(&Txn, NULL, CFE_SB_Q_WR_ERR_EID, MyTskId, EvtMsg, + sizeof(EvtMsg), &EvType, &EvReqBit)); + UtAssert_NONZERO(EvtMsg[0]); + UtAssert_NOT_NULL(memchr(EvtMsg, 0, sizeof(EvtMsg))); + UtAssert_UINT32_EQ(EvReqBit, CFE_SB_Q_WR_ERR_EID_BIT); + + CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); +} + +void Test_MessageTxn_ReportSingleEvent(void) +{ + /* Test case for: + * bool CFE_SB_MessageTxn_ReportSingleEvent(uint16 EventId, const CFE_SB_MessageTxn_State_t *TxnPtr, const + * CFE_SB_PipeSetEntry_t *ContextPtr); + */ + CFE_SB_PipeSetEntry_t Entry; + CFE_SB_MessageTxn_State_t Txn; + CFE_SB_PipeId_t PipeId; + CFE_ES_TaskId_t MyTskId; + + memset(&Txn, 0, sizeof(Txn)); + memset(&Entry, 0, sizeof(Entry)); + CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, 3, "TestPipe")); + CFE_UtAssert_SETUP(CFE_ES_GetTaskID(&MyTskId)); + Txn.NumPipes = 1; + Txn.PipeSet = &Entry; + Entry.PipeId = PipeId; + + /* Event ID == 0 is reserved for no event, nothing to report */ + UT_ClearEventHistory(); + UtAssert_BOOL_FALSE(CFE_SB_MessageTxn_ReportSingleEvent(&Txn, NULL, 0)); + CFE_UtAssert_EVENTCOUNT(0); + + /* No subscribers should be an information event (not an error) */ + UT_ClearEventHistory(); + UtAssert_BOOL_FALSE(CFE_SB_MessageTxn_ReportSingleEvent(&Txn, NULL, CFE_SB_SEND_NO_SUBS_EID)); + CFE_UtAssert_EVENTCOUNT(1); + CFE_UtAssert_EVENTSENT(CFE_SB_SEND_NO_SUBS_EID); + + /* Queue write failure should be an error */ + /* Note that with context != NULL, it looks up the pipe name, which generates a debug event */ + UT_ClearEventHistory(); + UtAssert_BOOL_TRUE(CFE_SB_MessageTxn_ReportSingleEvent(&Txn, &Entry, CFE_SB_Q_WR_ERR_EID)); + CFE_UtAssert_EVENTCOUNT(2); + CFE_UtAssert_EVENTSENT(CFE_SB_Q_WR_ERR_EID); + CFE_UtAssert_EVENTSENT(CFE_SB_GETPIPENAME_EID); + + /* Check that the loop-avoidance works */ + CFE_SB_RequestToSendEvent(MyTskId, CFE_SB_MSG_TOO_BIG_EID_BIT); + UT_ClearEventHistory(); + UtAssert_BOOL_TRUE(CFE_SB_MessageTxn_ReportSingleEvent(&Txn, NULL, CFE_SB_MSG_TOO_BIG_EID)); + CFE_UtAssert_EVENTCOUNT(0); + CFE_SB_FinishSendEvent(MyTskId, CFE_SB_MSG_TOO_BIG_EID_BIT); + + CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); +} + +void Test_MessageTxn_ReportEvents(void) +{ + /* Test case for: + * void CFE_SB_MessageTxn_ReportEvents(const CFE_SB_MessageTxn_State_t *TxnPtr); + */ + CFE_SB_PipeSetEntry_t PipeSetEntry; + CFE_SB_MessageTxn_State_t Txn; + + memset(&Txn, 0, sizeof(Txn)); + Txn.PipeSet = &PipeSetEntry; + Txn.NumPipes = 1; + Txn.MaxPipes = 1; + + /* nominal */ + UtAssert_VOIDCALL(CFE_SB_MessageTxn_ReportEvents(&Txn)); + UtAssert_ZERO(CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter); + + /* with an event at the transaction level, known to be an error */ + Txn.TransactionEventId = CFE_SB_MSG_TOO_BIG_EID; + Txn.IsTransmit = true; + UtAssert_VOIDCALL(CFE_SB_MessageTxn_ReportEvents(&Txn)); + UtAssert_UINT32_EQ(CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter, 1); + + /* with some undefined/unknown event at the transaction level, not an error */ + Txn.TransactionEventId = 0xFFFF; + UtAssert_VOIDCALL(CFE_SB_MessageTxn_ReportEvents(&Txn)); + UtAssert_UINT32_EQ(CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter, 1); + + /* with an event at the pipe level, known to be an error */ + Txn.TransactionEventId = 0; + Txn.PipeSet[0].PendingEventId = CFE_SB_Q_FULL_ERR_EID; + UtAssert_VOIDCALL(CFE_SB_MessageTxn_ReportEvents(&Txn)); + UtAssert_UINT32_EQ(CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter, 2); + + /* with some undefined/unknown event at the pipe level, not an error */ + Txn.PipeSet[0].PendingEventId = 0xFFFF; + UtAssert_VOIDCALL(CFE_SB_MessageTxn_ReportEvents(&Txn)); + UtAssert_UINT32_EQ(CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter, 2); +} + /* ** Function for calling SB send message API test functions */ @@ -2901,10 +3486,19 @@ void Test_TransmitMsg_API(void) SB_UT_ADD_SUBTEST(Test_TransmitBuffer_NoIncrement); SB_UT_ADD_SUBTEST(Test_TransmitMsg_ZeroCopyBufferValidate); SB_UT_ADD_SUBTEST(Test_TransmitMsg_DisabledDestination); - SB_UT_ADD_SUBTEST(Test_BroadcastBufferToRoute); - SB_UT_ADD_SUBTEST(Test_TransmitMsgValidate_MaxMsgSizePlusOne); - SB_UT_ADD_SUBTEST(Test_TransmitMsgValidate_NoSubscribers); - SB_UT_ADD_SUBTEST(Test_TransmitMsgValidate_InvalidMsgId); + + SB_UT_ADD_SUBTEST(Test_MessageTxn_SetEventAndStatus); + SB_UT_ADD_SUBTEST(Test_MessageTxn_SetupFromMsg); + SB_UT_ADD_SUBTEST(Test_MessageTxn_Timeout); + SB_UT_ADD_SUBTEST(Test_MessageTxn_GetEventDetails); + SB_UT_ADD_SUBTEST(Test_MessageTxn_ReportSingleEvent); + SB_UT_ADD_SUBTEST(Test_MessageTxn_ReportEvents); + + SB_UT_ADD_SUBTEST(Test_TransmitTxn_Init); + SB_UT_ADD_SUBTEST(Test_TransmitTxn_FindDestinations); + SB_UT_ADD_SUBTEST(Test_TransmitTxn_PipeHandler); + SB_UT_ADD_SUBTEST(Test_TransmitTxn_Execute); + SB_UT_ADD_SUBTEST(Test_AllocateMessageBuffer); SB_UT_ADD_SUBTEST(Test_ReleaseMessageBuffer); } @@ -3304,9 +3898,6 @@ void Test_TransmitMsg_ZeroCopyBufferValidate(void) * descriptor which is NOT from CFE_SB_AllocateMessageBuffer(). */ memset(&BadZeroCpyBuf, 0, sizeof(BadZeroCpyBuf)); - /* Null Buffer => BAD_ARGUMENT */ - UtAssert_INT32_EQ(CFE_SB_ZeroCopyBufferValidate(NULL, &BufDscPtr), CFE_SB_BAD_ARGUMENT); - /* Non-null buffer pointer but Non Zero-Copy => CFE_SB_BUFFER_INVALID */ UtAssert_INT32_EQ(CFE_SB_ZeroCopyBufferValidate(&BadZeroCpyBuf.Content, &BufDscPtr), CFE_SB_BUFFER_INVALID); @@ -3505,113 +4096,12 @@ void Test_TransmitMsg_DisabledDestination(void) CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); } -/* -** Test successful CFE_SB_BroadcastBufferToRoute -*/ -void Test_BroadcastBufferToRoute(void) -{ - CFE_SB_PipeId_t PipeId = CFE_SB_INVALID_PIPE; - CFE_SB_MsgId_t MsgId = SB_UT_TLM_MID; - CFE_SB_BufferD_t SBBufD; - int32 PipeDepth; - CFE_SBR_RouteId_t RouteId; - - memset(&SBBufD, 0, sizeof(SBBufD)); - SBBufD.MsgId = MsgId; - CFE_SB_TrackingListReset(&SBBufD.Link); - - PipeDepth = 2; - CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, PipeDepth, "TestPipe")); - CFE_UtAssert_SETUP(CFE_SB_Subscribe(MsgId, PipeId)); - - RouteId = CFE_SBR_GetRouteId(MsgId); - - /* No return from this function - it handles all errors */ - CFE_SB_BroadcastBufferToRoute(&SBBufD, RouteId); - - CFE_UtAssert_EVENTCOUNT(2); - UT_ClearEventHistory(); - - /* Calling this with invalid route ID is essentially a no-op, called for coverage */ - CFE_SB_BroadcastBufferToRoute(&SBBufD, CFE_SBR_INVALID_ROUTE_ID); - - CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); -} - -/* -** Test response to sending a message with the message size larger than allowed -*/ -void Test_TransmitMsgValidate_MaxMsgSizePlusOne(void) +void UT_CFE_MSG_Verify_CustomHandler(void *UserObj, UT_EntryKey_t FuncKey, const UT_StubContext_t *Context) { - CFE_SB_MsgId_t MsgId = SB_UT_TLM_MID; - CFE_SB_MsgId_t MsgIdRtn; - SB_UT_Test_Tlm_t TlmPkt; - CFE_MSG_Size_t Size = CFE_MISSION_SB_MAX_SB_MSG_SIZE + 1; - CFE_MSG_Size_t SizeRtn = 0; - CFE_SBR_RouteId_t RouteIdRtn; - - memset(&TlmPkt, 0, sizeof(TlmPkt)); - - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); - - UtAssert_INT32_EQ(CFE_SB_TransmitMsgValidate(CFE_MSG_PTR(TlmPkt.TelemetryHeader), &MsgIdRtn, &SizeRtn, &RouteIdRtn), - CFE_SB_MSG_TOO_BIG); - CFE_UtAssert_MSGID_EQ(MsgIdRtn, MsgId); - UtAssert_INT32_EQ(SizeRtn, Size); + bool *IsAcceptable = UT_Hook_GetArgValueByName(Context, "IsAcceptable", bool *); - CFE_UtAssert_EVENTCOUNT(1); - - CFE_UtAssert_EVENTSENT(CFE_SB_MSG_TOO_BIG_EID); -} - -/* -** Test response to sending a message which has no subscribers -*/ -void Test_TransmitMsgValidate_NoSubscribers(void) -{ - CFE_SB_MsgId_t MsgId = SB_UT_TLM_MID; - CFE_SB_MsgId_t MsgIdRtn; - SB_UT_Test_Tlm_t TlmPkt; - CFE_MSG_Size_t Size = sizeof(TlmPkt); - CFE_MSG_Size_t SizeRtn = 0; - CFE_SBR_RouteId_t RouteIdRtn = CFE_SBR_INVALID_ROUTE_ID; - - memset(&TlmPkt, 0, sizeof(TlmPkt)); - - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); - - CFE_UtAssert_SUCCESS( - CFE_SB_TransmitMsgValidate(CFE_MSG_PTR(TlmPkt.TelemetryHeader), &MsgIdRtn, &SizeRtn, &RouteIdRtn)); - CFE_UtAssert_MSGID_EQ(MsgIdRtn, MsgId); - UtAssert_INT32_EQ(SizeRtn, Size); - UtAssert_BOOL_FALSE(CFE_SBR_IsValidRouteId(RouteIdRtn)); - - CFE_UtAssert_EVENTCOUNT(1); - - CFE_UtAssert_EVENTSENT(CFE_SB_SEND_NO_SUBS_EID); -} - -/* -** Test response to sending a message which has an invalid Msg ID -*/ -void Test_TransmitMsgValidate_InvalidMsgId(void) -{ - CFE_SB_MsgId_t MsgId = CFE_SB_INVALID_MSG_ID; - CFE_SB_MsgId_t MsgIdRtn; - SB_UT_Test_Tlm_t TlmPkt; - CFE_MSG_Size_t SizeRtn; - CFE_SBR_RouteId_t RouteIdRtn; - - memset(&TlmPkt, 0, sizeof(TlmPkt)); - - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - - UtAssert_INT32_EQ(CFE_SB_TransmitMsgValidate(CFE_MSG_PTR(TlmPkt.TelemetryHeader), &MsgIdRtn, &SizeRtn, &RouteIdRtn), - CFE_SB_BAD_ARGUMENT); - CFE_UtAssert_EVENTCOUNT(1); - CFE_UtAssert_EVENTSENT(CFE_SB_SEND_INV_MSGID_EID); + /* Return alternating false/true (this is needed to avoid an endless loop) */ + *IsAcceptable = (UT_GetStubCount(FuncKey) & 1) == 0; } /* @@ -3630,17 +4120,18 @@ void Test_ReceiveBuffer_API(void) static void SB_UT_PipeIdModifyHandler(void *UserObj, UT_EntryKey_t FuncKey, const UT_StubContext_t *Context) { - void * data = UT_Hook_GetArgValueByName(Context, "data", void *); + CFE_SB_BufferD_t ** OutData = UT_Hook_GetArgValueByName(Context, "data", void *); size_t * size_copied = UT_Hook_GetArgValueByName(Context, "size_copied", size_t *); int32 status; - static SB_UT_Test_Tlm_t FakeTlmPkt; - SB_UT_Test_Tlm_t ** OutData; + static CFE_SB_BufferD_t LocalBuf; CFE_SB_PipeD_t * PipeDscPtr = UserObj; - OutData = data; - *OutData = &FakeTlmPkt; - *size_copied = sizeof(*OutData); - status = OS_SUCCESS; + memset(&LocalBuf, 0, sizeof(LocalBuf)); + CFE_SB_TrackingListReset(&LocalBuf.Link); + LocalBuf.UseCount = 1; + *OutData = &LocalBuf; + *size_copied = sizeof(*OutData); + status = OS_SUCCESS; UT_Stub_SetReturnValue(FuncKey, status); /* Modify the PipeID so it fails to match */ @@ -3700,8 +4191,8 @@ void Test_ReceiveBuffer_InvalidPipeId(void) UT_SetHandlerFunction(UT_KEY(OS_QueueGet), SB_UT_PipeIdModifyHandler, PipeDscPtr); UtAssert_INT32_EQ(CFE_SB_ReceiveBuffer(&SBBufPtr, PipeId, CFE_SB_POLL), CFE_SB_PIPE_RD_ERR); CFE_UtAssert_EVENTSENT(CFE_SB_BAD_PIPEID_EID); - UtAssert_UINT8_EQ(CFE_SB_Global.HKTlmMsg.Payload.MsgReceiveErrorCounter, 2); - UtAssert_UINT8_EQ(CFE_SB_Global.HKTlmMsg.Payload.InternalErrorCounter, 0); + UtAssert_UINT8_EQ(CFE_SB_Global.HKTlmMsg.Payload.MsgReceiveErrorCounter, 1); + UtAssert_UINT8_EQ(CFE_SB_Global.HKTlmMsg.Payload.InternalErrorCounter, 1); UT_SetHandlerFunction(UT_KEY(OS_QueueGet), NULL, NULL); /* restore the PipeID so it can be deleted */ @@ -3793,7 +4284,7 @@ void Test_ReceiveBuffer_PipeReadError(void) UT_SetDeferredRetcode(UT_KEY(OS_QueueGet), 1, OS_ERROR); UtAssert_INT32_EQ(CFE_SB_ReceiveBuffer(&SBBufPtr, PipeId, CFE_SB_PEND_FOREVER), CFE_SB_PIPE_RD_ERR); - CFE_UtAssert_EVENTCOUNT(2); + CFE_UtAssert_EVENTCOUNT(3); CFE_UtAssert_EVENTSENT(CFE_SB_Q_RD_ERR_EID); @@ -4187,11 +4678,6 @@ void Test_SB_SpecialCases(void) SB_UT_ADD_SUBTEST(Test_PutDestBlk_ErrLogic); SB_UT_ADD_SUBTEST(Test_CFE_SB_Buffers); SB_UT_ADD_SUBTEST(Test_CFE_SB_BadPipeInfo); - SB_UT_ADD_SUBTEST(Test_SB_TransmitMsgPaths_Nominal); - SB_UT_ADD_SUBTEST(Test_SB_TransmitMsgPaths_LimitErr); - SB_UT_ADD_SUBTEST(Test_SB_TransmitMsgPaths_FullErr); - SB_UT_ADD_SUBTEST(Test_SB_TransmitMsgPaths_WriteErr); - SB_UT_ADD_SUBTEST(Test_SB_TransmitMsgPaths_IgnoreOpt); SB_UT_ADD_SUBTEST(Test_ReceiveBuffer_UnsubResubPath); SB_UT_ADD_SUBTEST(Test_MessageString); } @@ -4361,281 +4847,6 @@ void Test_CFE_SB_BadPipeInfo(void) CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); } -/* -** Test send housekeeping information command -*/ -void Test_SB_TransmitMsgPaths_Nominal(void) -{ - union - { - CFE_SB_Buffer_t SBBuf; - CFE_SB_SendHkCmd_t SendHkCmd; - } Housekeeping; - CFE_SB_MsgId_t MsgId; - CFE_SB_PipeId_t PipeId = CFE_SB_INVALID_PIPE; - SB_UT_Test_Tlm_t TlmPkt; - int32 PipeDepth = 2; - CFE_MSG_Size_t Size; - CFE_MSG_Type_t Type; - - memset(&Housekeeping, 0, sizeof(Housekeeping)); - memset(&TlmPkt, 0, sizeof(TlmPkt)); - - /* Set up for dispatch FIRST */ - UT_SetupBasicMsgDispatch(&UT_TPID_CFE_SB_SEND_HK, sizeof(Housekeeping.SendHkCmd), false); - - /* For internal send message call */ - MsgId = CFE_SB_ValueToMsgId(CFE_SB_HK_TLM_MID); - Size = sizeof(CFE_SB_Global.HKTlmMsg); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); - - /* Repress sending the no subscriptions event and process request */ - CFE_SB_Global.HKTlmMsg.Payload.NoSubscribersCounter = 0; - CFE_SB_Global.StopRecurseFlags[1] |= CFE_BIT(CFE_SB_SEND_NO_SUBS_EID_BIT); - CFE_SB_ProcessCmdPipePkt(&Housekeeping.SBBuf); - - /* The no subs event should not be in history but count should increment */ - CFE_UtAssert_EVENTNOTSENT(CFE_SB_SEND_NO_SUBS_EID); - UtAssert_INT32_EQ(CFE_SB_Global.HKTlmMsg.Payload.NoSubscribersCounter, 1); - - /* Repress get buffer error */ - CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter = 0; - CFE_SB_Global.StopRecurseFlags[1] |= CFE_BIT(CFE_SB_GET_BUF_ERR_EID_BIT); - - /* Set up for dispatch FIRST */ - UT_SetupBasicMsgDispatch(&UT_TPID_CFE_SB_SEND_HK, sizeof(Housekeeping.SendHkCmd), false); - - /* For internal send message call */ - MsgId = CFE_SB_ValueToMsgId(CFE_SB_HK_TLM_MID); - Size = sizeof(CFE_SB_Global.HKTlmMsg); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); - - UT_SetDeferredRetcode(UT_KEY(CFE_ES_GetPoolBuf), 1, CFE_ES_ERR_MEM_BLOCK_SIZE); - CFE_SB_ProcessCmdPipePkt(&Housekeeping.SBBuf); - UtAssert_INT32_EQ(CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter, 0); - - CFE_UtAssert_EVENTNOTSENT(CFE_SB_GET_BUF_ERR_EID); - - CFE_UtAssert_EVENTCOUNT(0); - - CFE_SB_Global.StopRecurseFlags[1] = 0; - - /* Create a message ID with the command bit set and disable reporting */ - MsgId = SB_UT_CMD_MID; - Size = sizeof(TlmPkt); - Type = CFE_MSG_Type_Cmd; - CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, PipeDepth, "TestPipe")); - - /* Will fail because of deferred CFE_ES_GetPoolBuf failure return */ - UtAssert_INT32_EQ(CFE_SB_Subscribe(MsgId, PipeId), CFE_SB_BUF_ALOC_ERR); - - 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_GetType), &Type, sizeof(Type), false); - - CFE_UtAssert_SUCCESS(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmPkt.TelemetryHeader), true)); - - CFE_UtAssert_EVENTCOUNT(3); - - /* - * Test Additional paths within CFE_SB_TransmitMsgValidate that skip sending events to avoid a loop - * For all of these they should skip sending the event but still increment the MsgSendErrorCounter - */ - - /* CFE_SB_MSG_TOO_BIG_EID loop filter */ - CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter = 0; - Size = CFE_MISSION_SB_MAX_SB_MSG_SIZE + 1; - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetSize), &Size, sizeof(Size), false); - CFE_SB_Global.StopRecurseFlags[1] |= CFE_BIT(CFE_SB_MSG_TOO_BIG_EID_BIT); - CFE_SB_TransmitMsg(CFE_MSG_PTR(Housekeeping.SBBuf), true); - CFE_UtAssert_EVENTNOTSENT(CFE_SB_MSG_TOO_BIG_EID); - UtAssert_UINT32_EQ(CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter, 1); - - /* CFE_SB_SEND_INV_MSGID_EID loop filter */ - CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter = 0; - MsgId = CFE_SB_INVALID_MSG_ID; - UT_SetDataBuffer(UT_KEY(CFE_MSG_GetMsgId), &MsgId, sizeof(MsgId), false); - CFE_SB_Global.StopRecurseFlags[1] |= CFE_BIT(CFE_SB_SEND_INV_MSGID_EID_BIT); - CFE_SB_TransmitMsg(CFE_MSG_PTR(Housekeeping.SBBuf), true); - CFE_UtAssert_EVENTNOTSENT(CFE_SB_SEND_INV_MSGID_EID); - UtAssert_UINT32_EQ(CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter, 1); - - /* CFE_SB_SEND_BAD_ARG_EID loop filter */ - CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter = 0; - CFE_SB_Global.StopRecurseFlags[1] |= CFE_BIT(CFE_SB_SEND_BAD_ARG_EID_BIT); - CFE_SB_TransmitMsg(NULL, true); - CFE_UtAssert_EVENTNOTSENT(CFE_SB_SEND_BAD_ARG_EID); - UtAssert_UINT32_EQ(CFE_SB_Global.HKTlmMsg.Payload.MsgSendErrorCounter, 1); - - CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); -} - -void Test_SB_TransmitMsgPaths_LimitErr(void) -{ - CFE_SB_MsgId_t MsgId; - CFE_SB_PipeId_t PipeId = CFE_SB_INVALID_PIPE; - SB_UT_Test_Tlm_t TlmPkt; - int32 PipeDepth = 2; - CFE_MSG_Type_t Type = CFE_MSG_Type_Tlm; - CFE_MSG_Size_t Size = sizeof(TlmPkt); - - memset(&TlmPkt, 0, sizeof(TlmPkt)); - - /* Test inhibiting sending a "message ID limit error" message */ - MsgId = SB_UT_TLM_MID; - CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, PipeDepth, "MsgLimTestPipe")); - - /* Set maximum allowed messages on the pipe at one time to 1 */ - CFE_UtAssert_SETUP(CFE_SB_SubscribeEx(MsgId, PipeId, CFE_SB_DEFAULT_QOS, 1)); - - 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_GetType), &Type, sizeof(Type), false); - - /* First send should pass */ - CFE_UtAssert_SUCCESS(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmPkt.TelemetryHeader), 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_GetType), &Type, sizeof(Type), false); - - CFE_SB_Global.StopRecurseFlags[1] |= CFE_BIT(CFE_SB_MSGID_LIM_ERR_EID_BIT); - CFE_UtAssert_SUCCESS(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmPkt.TelemetryHeader), true)); - CFE_SB_Global.StopRecurseFlags[1] = 0; - - CFE_UtAssert_EVENTNOTSENT(CFE_SB_MSGID_LIM_ERR_EID); - - CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); -} - -void Test_SB_TransmitMsgPaths_FullErr(void) -{ - CFE_SB_MsgId_t MsgId; - CFE_SB_PipeId_t PipeId = CFE_SB_INVALID_PIPE; - SB_UT_Test_Tlm_t TlmPkt; - int32 PipeDepth = 2; - CFE_MSG_Type_t Type = CFE_MSG_Type_Tlm; - CFE_MSG_Size_t Size = sizeof(TlmPkt); - - memset(&TlmPkt, 0, sizeof(TlmPkt)); - - /* Test inhibiting sending a "pipe full" message */ - MsgId = SB_UT_TLM_MID; - CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, PipeDepth, "PipeFullTestPipe")); - CFE_UtAssert_SETUP(CFE_SB_Subscribe(MsgId, PipeId)); - - 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_GetType), &Type, sizeof(Type), false); - - /* This send should pass */ - CFE_UtAssert_SUCCESS(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmPkt.TelemetryHeader), 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_GetType), &Type, sizeof(Type), false); - - /* Tell the QueuePut stub to return OS_QUEUE_FULL on its next call */ - UT_SetDeferredRetcode(UT_KEY(OS_QueuePut), 1, OS_QUEUE_FULL); - CFE_SB_Global.StopRecurseFlags[1] |= CFE_BIT(CFE_SB_Q_FULL_ERR_EID_BIT); - CFE_UtAssert_SUCCESS(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmPkt.TelemetryHeader), true)); - CFE_SB_Global.StopRecurseFlags[1] = 0; - - CFE_UtAssert_EVENTNOTSENT(CFE_SB_Q_FULL_ERR_EID_BIT); - - CFE_UtAssert_EVENTCOUNT(2); - - CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); -} - -void Test_SB_TransmitMsgPaths_WriteErr(void) -{ - CFE_SB_MsgId_t MsgId; - CFE_SB_PipeId_t PipeId = CFE_SB_INVALID_PIPE; - SB_UT_Test_Tlm_t TlmPkt; - int32 PipeDepth = 2; - CFE_MSG_Type_t Type = CFE_MSG_Type_Tlm; - CFE_MSG_Size_t Size = sizeof(TlmPkt); - - memset(&TlmPkt, 0, sizeof(TlmPkt)); - - /* Test inhibiting sending a "pipe write error" message */ - MsgId = SB_UT_TLM_MID; - CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, PipeDepth, "TestPipe")); - CFE_UtAssert_SETUP(CFE_SB_Subscribe(MsgId, PipeId)); - UT_SetDeferredRetcode(UT_KEY(OS_QueuePut), 1, OS_ERROR); - - 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_GetType), &Type, sizeof(Type), false); - - CFE_SB_Global.StopRecurseFlags[1] |= CFE_BIT(CFE_SB_Q_WR_ERR_EID_BIT); - CFE_UtAssert_SUCCESS(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmPkt.TelemetryHeader), true)); - CFE_SB_Global.StopRecurseFlags[1] = 0; - - 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_GetType), &Type, sizeof(Type), false); - - CFE_UtAssert_SUCCESS(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmPkt.TelemetryHeader), true)); - - CFE_UtAssert_EVENTCOUNT(2); - - CFE_UtAssert_EVENTNOTSENT(CFE_SB_Q_WR_ERR_EID); - - CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); -} - -void Test_SB_TransmitMsgPaths_IgnoreOpt(void) -{ - CFE_SB_MsgId_t MsgId; - CFE_SB_PipeId_t PipeId = CFE_SB_INVALID_PIPE; - SB_UT_Test_Tlm_t TlmPkt; - int32 PipeDepth = 2; - CFE_MSG_Type_t Type = CFE_MSG_Type_Tlm; - CFE_MSG_Size_t Size = sizeof(TlmPkt); - CFE_SB_PipeD_t * PipeDscPtr; - CFE_ES_AppId_t AppId; - - memset(&TlmPkt, 0, sizeof(TlmPkt)); - - /* Setup Test skipping sending to a pipe when the pipe option is set to ignore */ - MsgId = SB_UT_TLM_MID; - CFE_UtAssert_SETUP(CFE_SB_CreatePipe(&PipeId, PipeDepth, "SkipPipe")); - CFE_UtAssert_SETUP(CFE_SB_Subscribe(MsgId, PipeId)); - CFE_UtAssert_SETUP(CFE_SB_SetPipeOpts(PipeId, CFE_SB_PIPEOPTS_IGNOREMINE)); - - 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_GetType), &Type, sizeof(Type), false); - - /* Test skipping this pipe and the send should pass */ - CFE_UtAssert_SUCCESS(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmPkt.TelemetryHeader), true)); - UtAssert_STUB_COUNT(OS_QueuePut, 0); - - /* Set up and send again without matching ApId and it should transmit */ - 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_GetType), &Type, sizeof(Type), false); - PipeDscPtr = CFE_SB_LocatePipeDescByID(PipeId); - AppId = PipeDscPtr->AppId; - PipeDscPtr->AppId = CFE_ES_APPID_UNDEFINED; - - /* Also hit case where not the peak depth */ - PipeDscPtr->PeakQueueDepth += 2; - CFE_UtAssert_SUCCESS(CFE_SB_TransmitMsg(CFE_MSG_PTR(TlmPkt.TelemetryHeader), true)); - UtAssert_STUB_COUNT(OS_QueuePut, 1); - - /* Set AppId back so it can be deleted */ - PipeDscPtr->AppId = AppId; - - CFE_UtAssert_TEARDOWN(CFE_SB_SetPipeOpts(PipeId, 0)); - CFE_UtAssert_TEARDOWN(CFE_SB_DeletePipe(PipeId)); -} - /* ** Test receiving a message response to an unsubscribing to message, then ** resubscribing to it while it's in the pipe diff --git a/modules/sb/ut-coverage/sb_UT.h b/modules/sb/ut-coverage/sb_UT.h index bf72d1055..88b6ac950 100644 --- a/modules/sb/ut-coverage/sb_UT.h +++ b/modules/sb/ut-coverage/sb_UT.h @@ -2047,7 +2047,7 @@ void Test_TransmitMsg_DisabledDestination(void); /*****************************************************************************/ /** -** \brief Test CFE_SB_BroadcastBufferToRoute +** \brief Test CFE_SB_TransmitTxn_BroadcastToRoute ** ** \par Description ** This function tests broadcasting a message buffer with the metadata. @@ -2058,23 +2058,7 @@ void Test_TransmitMsg_DisabledDestination(void); ** \returns ** This function does not return a value. ******************************************************************************/ -void Test_BroadcastBufferToRoute(void); - -/*****************************************************************************/ -/** -** \brief Test response to sending a message which has no subscribers -** -** \par Description -** This function tests the response to sending a message which has no -** subscribers. -** -** \par Assumptions, External Events, and Notes: -** None -** -** \returns -** This function does not return a value. -******************************************************************************/ -void Test_TransmitMsgValidate_NoSubscribers(void); +void Test_TransmitTxn_PipeHandler(void); /*****************************************************************************/ /** @@ -2090,7 +2074,7 @@ void Test_TransmitMsgValidate_NoSubscribers(void); ** \returns ** This function does not return a value. ******************************************************************************/ -void Test_TransmitMsgValidate_InvalidMsgId(void); +void Test_TransmitTxn_SetupFromMsg_InvalidMsgId(void); /*****************************************************************************/ /** @@ -2107,7 +2091,7 @@ void Test_TransmitMsgValidate_InvalidMsgId(void); ** \returns ** This function does not return a value. ******************************************************************************/ -void Test_TransmitMsgValidate_MaxMsgSizePlusOne(void); +void Test_TransmitTxn_SetupFromMsg_MaxMsgSizePlusOne(void); /*****************************************************************************/ /** @@ -2457,25 +2441,6 @@ void Test_CFE_SB_Buffers(void); ******************************************************************************/ void Test_CFE_SB_BadPipeInfo(void); -/*****************************************************************************/ -/** -** \brief Test TransmitMsgFull function paths -** -** \par Description -** This function tests branch paths in the TransmitMsgFull function. -** -** \par Assumptions, External Events, and Notes: -** None -** -** \returns -** This function does not return a value. -******************************************************************************/ -void Test_SB_TransmitMsgPaths_Nominal(void); -void Test_SB_TransmitMsgPaths_LimitErr(void); -void Test_SB_TransmitMsgPaths_FullErr(void); -void Test_SB_TransmitMsgPaths_WriteErr(void); -void Test_SB_TransmitMsgPaths_IgnoreOpt(void); - /*****************************************************************************/ /** ** \brief Test ReceiveBuffer function unsubscribe/resubscribe path