diff --git a/include/openthread/types.h b/include/openthread/types.h index abd1be67f..78a2ddefd 100644 --- a/include/openthread/types.h +++ b/include/openthread/types.h @@ -895,6 +895,8 @@ typedef enum /** * This structure holds diagnostic information for a neighboring Thread node * + * `mFrameErrorRate` and `mMessageErrorRate` require `OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING` feature to be + * enabled. */ typedef struct { @@ -906,6 +908,8 @@ typedef struct uint8_t mLinkQualityIn; ///< Link Quality In int8_t mAverageRssi; ///< Average RSSI int8_t mLastRssi; ///< Last observed RSSI + uint16_t mFrameErrorRate; ///< Frame error rate (0xffff->100%). Requires error tracking feature. + uint16_t mMessageErrorRate; ///< (IPv6) msg error rate (0xffff->100%). Requires error tracking feature. bool mRxOnWhenIdle : 1; ///< rx-on-when-idle bool mSecureDataRequest : 1; ///< Secure Data Requests bool mFullFunction : 1; ///< Full Function Device @@ -920,6 +924,9 @@ typedef int16_t otNeighborInfoIterator; ///< Used to iterate through neighbor /** * This structure holds diagnostic information for a Thread Child * + * `mFrameErrorRate` and `mMessageErrorRate` require `OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING` feature to be + * enabled. + * */ typedef struct { @@ -932,6 +939,8 @@ typedef struct uint8_t mLinkQualityIn; ///< Link Quality In int8_t mAverageRssi; ///< Average RSSI int8_t mLastRssi; ///< Last observed RSSI + uint16_t mFrameErrorRate; ///< Frame error rate (0xffff->100%). Requires error tracking feature. + uint16_t mMessageErrorRate; ///< (IPv6) msg error rate (0xffff->100%). Requires error tracking feature. bool mRxOnWhenIdle : 1; ///< rx-on-when-idle bool mSecureDataRequest : 1; ///< Secure Data Requests bool mFullFunction : 1; ///< Full Function Device diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp index 636d27543..be88ac872 100644 --- a/src/core/common/message.hpp +++ b/src/core/common/message.hpp @@ -113,6 +113,7 @@ struct MessageInfo bool mLinkSecurity : 1; ///< Indicates whether or not link security is enabled. uint8_t mPriority : 2; ///< Identifies the message priority level (lower value is higher priority). bool mInPriorityQ : 1; ///< Indicates whether the message is queued in normal or priority queue. + bool mTxSuccess : 1; ///< Indicates whether the direct tx of the message was successful. }; /** @@ -602,6 +603,24 @@ public: */ void SetDirectTransmission(void) { mBuffer.mHead.mInfo.mDirectTx = true; } + /** + * This methods indicates whether the direct transmission of message was successful. + * + * @retval TRUE If direct transmission of message was successful (all fragments were delivered and acked). + * @retval FALSE If direct transmission of message failed (at least one fragment failed). + * + */ + bool GetTxSuccess(void) const { return mBuffer.mHead.mInfo.mTxSuccess; } + + /** + * This methods sets whether the direct transmission of message was successful. + * + * @param[in] aTxSuccess TRUE if the direct transmission is successful, FALSE otherwise (i.e., at least one + * fragment transmission failed). + * + */ + void SetTxSuccess(bool aTxSuccess) { mBuffer.mHead.mInfo.mTxSuccess = aTxSuccess; } + /** * This method indicates whether or not link security is enabled for the message. * diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index d5d7f6250..22be25cab 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -1077,6 +1077,8 @@ void Mac::HandleTransmitDone(otRadioFrame *aFrame, otRadioFrame *aAckFrame, otEr bool framePending = false; bool ccaSuccess = true; Address dstAddr; + bool ackRequested; + Neighbor *neighbor; // Stop the ack timer. @@ -1127,6 +1129,40 @@ void Mac::HandleTransmitDone(otRadioFrame *aFrame, otRadioFrame *aAckFrame, otEr mCsmaAttempts = 0; + sendFrame.GetDstAddr(dstAddr); + neighbor = GetNetif().GetMle().GetNeighbor(dstAddr); + ackRequested = sendFrame.GetAckRequest(); + +#if OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING + + // Record frame transmission success/failure state (for a neighbor). + + if ((neighbor != NULL) && ackRequested) + { + bool frameTxSuccess = true; + + // CCA or abort errors are excluded from frame tx error + // rate tracking, since when they occur, the frame is + // not actually sent over the air. + + switch (aError) + { + case OT_ERROR_NO_ACK: + frameTxSuccess = false; + + // Fall through + + case OT_ERROR_NONE: + neighbor->GetLinkInfo().AddFrameTxStatus(frameTxSuccess); + break; + + default: + break; + } + } + +#endif // OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING + // Determine whether to re-transmit the frame. mTransmitAttempts++; @@ -1154,12 +1190,9 @@ void Mac::HandleTransmitDone(otRadioFrame *aFrame, otRadioFrame *aAckFrame, otEr // Process the ack frame for "frame pending". - sendFrame.GetDstAddr(dstAddr); - - if (aError == OT_ERROR_NONE && sendFrame.GetAckRequest() && aAckFrame != NULL) + if (aError == OT_ERROR_NONE && ackRequested && aAckFrame != NULL) { Frame &ackFrame = *static_cast(aAckFrame); - Neighbor *neighbor = GetNetif().GetMle().GetNeighbor(dstAddr); framePending = ackFrame.GetFramePending(); @@ -1187,7 +1220,7 @@ void Mac::HandleTransmitDone(otRadioFrame *aFrame, otRadioFrame *aAckFrame, otEr mCounters.mTxUnicast++; } - if (sendFrame.GetAckRequest()) + if (ackRequested) { mCounters.mTxAckRequested++; diff --git a/src/core/openthread-core-default-config.h b/src/core/openthread-core-default-config.h index fc9e54488..0f4a78b9e 100644 --- a/src/core/openthread-core-default-config.h +++ b/src/core/openthread-core-default-config.h @@ -946,6 +946,49 @@ #define OPENTHREAD_CONFIG_CCA_FAILURE_RATE_AVERAGING_WINDOW 512 #endif +/** + * @def OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING + * + * Define as 1 to enable transmission error rate tracking (for both MAC frames and IPv6 messages) + * + * When enabled, OpenThread will track average error rate of MAC frame transmissions and IPv6 message error rate for + * every neighbor. + * + */ +#ifndef OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING +#define OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_FRAME_TX_ERR_RATE_AVERAGING_WINDOW + * + * Applicable only if error rate tracking is enabled (i.e., `OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING` is set). + * + * OpenThread's MAC implementation maintains the average error rate of MAC frame transmissions per neighbor. This + * parameter specifies the window (in terms of number of frames/sample) over which the average error rate is maintained. + * Practically, the average value can be considered as the percentage of failed (no ack) MAC frame transmissions over + * (approximately) last AVERAGING_WINDOW frame transmission attempts to a specific neighbor. + * + */ +#ifndef OPENTHREAD_CONFIG_FRAME_TX_ERR_RATE_AVERAGING_WINDOW +#define OPENTHREAD_CONFIG_FRAME_TX_ERR_RATE_AVERAGING_WINDOW 128 +#endif + +/** + * @def OPENTHREAD_CONFIG_IPV6_TX_ERR_RATE_AVERAGING_WINDOW + * + * Applicable only if error rate tracking is enabled (i.e., `OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING` is set). + * + * OpenThread maintains the average error rate of IPv6 messages per neighbor. This parameter specifies the + * window (in terms of number of messages) over which the average error rate is maintained. Practically, the average + * value can be considered as the percentage of failed (no ack) messages over (approximately) last AVERAGING_WINDOW + * IPv6 messages sent to a specific neighbor. + * + */ +#ifndef OPENTHREAD_CONFIG_IPV6_TX_ERR_RATE_AVERAGING_WINDOW +#define OPENTHREAD_CONFIG_IPV6_TX_ERR_RATE_AVERAGING_WINDOW 128 +#endif + /** * @def OPENTHREAD_CONFIG_CHANNEL_MONITOR_SAMPLE_INTERVAL * diff --git a/src/core/thread/link_quality.cpp b/src/core/thread/link_quality.cpp index 3a86db202..f89e5c54d 100644 --- a/src/core/thread/link_quality.cpp +++ b/src/core/thread/link_quality.cpp @@ -50,14 +50,14 @@ static const char *const kDigitsString[8] = void SuccessRateTracker::AddSample(bool aSuccess, uint16_t aWeight) { - uint32_t oldAverage = mSuccessRate; - uint32_t newValue = (aSuccess) ? kMaxRateValue : 0; + uint32_t oldAverage = mFailureRate; + uint32_t newValue = (aSuccess) ? 0 : kMaxRateValue; uint32_t n = aWeight; // `n/2` is added to the sum to ensure rounding the value to the nearest integer when dividing by `n` // (e.g., 1.2 -> 1, 3.5 -> 4). - mSuccessRate = static_cast(((oldAverage * (n - 1)) + newValue + (n / 2)) / n); + mFailureRate = static_cast(((oldAverage * (n - 1)) + newValue + (n / 2)) / n); } void RssAverager::Reset(void) diff --git a/src/core/thread/link_quality.hpp b/src/core/thread/link_quality.hpp index e758e3988..1a5998034 100644 --- a/src/core/thread/link_quality.hpp +++ b/src/core/thread/link_quality.hpp @@ -71,13 +71,13 @@ public: * After initialization the tracker starts with success rate 100% (failure rate 0%). * */ - SuccessRateTracker(void): mSuccessRate(kMaxRateValue) { } + SuccessRateTracker(void): mFailureRate(0) { } /** * This method resets the tracker to its initialized state, setting success rate to 100%. * */ - void Reset(void) { mSuccessRate = kMaxRateValue; } + void Reset(void) { mFailureRate = 0; } /** * This method adds a sample (success or failure) to `SuccessRateTracker`. @@ -94,7 +94,7 @@ public: * @retval the average failure rate `[0-kMaxRateValue]` with `kMaxRateValue` corresponding to 100%. * */ - uint16_t GetFailureRate(void) const { return kMaxRateValue - mSuccessRate; } + uint16_t GetFailureRate(void) const { return mFailureRate; } /** * This method returns the average success rate. @@ -102,7 +102,7 @@ public: * @retval the average success rate as [0-kMaxRateValue] with `kMaxRateValue` corresponding to 100%. * */ - uint16_t GetSuccessRate(void) const { return mSuccessRate; } + uint16_t GetSuccessRate(void) const { return kMaxRateValue - mFailureRate; } private: enum @@ -110,7 +110,7 @@ private: kDefaultWeight = 64, }; - uint16_t mSuccessRate; + uint16_t mFailureRate; }; /** @@ -121,8 +121,6 @@ private: */ class RssAverager { - friend class LinkQualityInfo; - public: enum { @@ -212,9 +210,8 @@ private: // Member variables fit into two bytes. - uint16_t mAverage : 11; // The raw average signal strength value (stored as RSS times precision multiple). - uint8_t mCount : 3; // Number of RSS values added to averager so far (limited to 2^kCoeffBitShift-1). - uint8_t mLinkQuality : 2; // Used by friend class LinkQualityInfo to store LinkQuality (0-3) value. + uint16_t mAverage : 11; // The raw average signal strength value (stored as RSS times precision multiple). + uint16_t mCount : 5; // Number of RSS values added to averager so far (limited to 2^kCoeffBitShift-1). }; /** @@ -307,7 +304,7 @@ public: * @returns The current link quality value (value 0-3 as per Thread specification). * */ - uint8_t GetLinkQuality(void) const { return mRssAverager.mLinkQuality; } + uint8_t GetLinkQuality(void) const { return mLinkQuality; } /** * Returns the most recent RSS value. @@ -317,6 +314,58 @@ public: */ int8_t GetLastRss(void) const { return mLastRss; } +#if OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING + + /** + * This method adds a MAC frame transmission status (success/failure) and updates the frame tx error rate. + * + * @param[in] aTxStatus Success/Failure of MAC frame transmission (`true` -> success, `false` -> failure). + * + */ + void AddFrameTxStatus(bool aTxStatus) { + mFrameErrorRate.AddSample(aTxStatus, OPENTHREAD_CONFIG_FRAME_TX_ERR_RATE_AVERAGING_WINDOW); + } + + /** + * This method adds a message transmission status (success/failure) and updates the message error rate. + * + * @param[in] aTxStatus Success/Failure of message (`true` -> success, `false` -> message tx failed). + * A larger (IPv6) message may be fragmented and sent as multiple MAC frames. The message + * transmission is considered a failure, if any of its fragments fail after all MAC retry + * attempts. + * + */ + void AddMessageTxStatus(bool aTxStatus) { + mMessageErrorRate.AddSample(aTxStatus, OPENTHREAD_CONFIG_IPV6_TX_ERR_RATE_AVERAGING_WINDOW); + } + + /** + * This method returns the MAC frame transmission error rate for the link. + * + * The rate is maintained over a window of (roughly) last `OPENTHREAD_CONFIG_FRAME_TX_ERR_RATE_AVERAGING_WINDOW` + * frame transmissions. + * + * @returns The error rate with maximum value `0xffff` corresponding to 100% failure rate. + * + */ + uint16_t GetFrameErrorRate(void) const { return mFrameErrorRate.GetFailureRate(); } + + /** + * This method returns the message error rate for the link. + * + * The rate is maintained over a window of (roughly) last `OPENTHREAD_CONFIG_IPV6_TX_ERR_RATE_AVERAGING_WINDOW` + * (IPv6) messages. + * + * Note that a larger (IPv6) message can be fragmented and sent as multiple MAC frames. The message transmission is + * considered a failure, if any of its fragments fail after all MAC retry attempts. + * + * @returns The error rate with maximum value `0xffff` corresponding to 100% failure rate. + * + */ + uint16_t GetMessageErrorRate(void) const { return mMessageErrorRate.GetFailureRate(); } + +#endif // OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING + /** * This method converts a received signal strength value to a link margin value. * @@ -366,22 +415,22 @@ private: { // Constants for obtaining link quality from link margin: - kThreshold3 = 20, // Link margin threshold for quality 3 link. - kThreshold2 = 10, // Link margin threshold for quality 2 link. - kThreshold1 = 2, // Link margin threshold for quality 1 link. - kHysteresisThreshold = 2, // Link margin hysteresis threshold. + kThreshold3 = 20, ///< Link margin threshold for quality 3 link. + kThreshold2 = 10, ///< Link margin threshold for quality 2 link. + kThreshold1 = 2, ///< Link margin threshold for quality 1 link. + kHysteresisThreshold = 2, ///< Link margin hysteresis threshold. // constants for test: - kLinkQuality3LinkMargin = 50, ///< link margin for Link Quality 3 (21 - 255) - kLinkQuality2LinkMargin = 15, ///< link margin for Link Quality 3 (21 - 255) - kLinkQuality1LinkMargin = 5, ///< link margin for Link Quality 3 (21 - 255) - kLinkQuality0LinkMargin = 0, ///< link margin for Link Quality 3 (21 - 255) + kLinkQuality3LinkMargin = 50, ///< link margin for Link Quality 3 (21 - 255) + kLinkQuality2LinkMargin = 15, ///< link margin for Link Quality 3 (21 - 255) + kLinkQuality1LinkMargin = 5, ///< link margin for Link Quality 3 (21 - 255) + kLinkQuality0LinkMargin = 0, ///< link margin for Link Quality 3 (21 - 255) kNoLinkQuality = 0xff, // Used to indicate that there is no previous/last link quality. }; - void SetLinkQuality(uint8_t aLinkQuality) { mRssAverager.mLinkQuality = aLinkQuality; } + void SetLinkQuality(uint8_t aLinkQuality) { mLinkQuality = aLinkQuality; } /* Static private method to calculate the link quality from a given link margin while taking into account the last * link quality value and adding the hysteresis value to the thresholds. If there is no previous value for link @@ -391,7 +440,12 @@ private: static uint8_t CalculateLinkQuality(uint8_t aLinkMargin, uint8_t aLastLinkQuality); RssAverager mRssAverager; + uint8_t mLinkQuality; int8_t mLastRss; +#if OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING + SuccessRateTracker mFrameErrorRate; + SuccessRateTracker mMessageErrorRate; +#endif }; /** diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index 5c03148d7..cf7926719 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -187,6 +187,11 @@ void MeshForwarder::ScheduleTransmissionTask(void) if ((mSendMessage = GetDirectTransmission()) != NULL) { + if (mSendMessage->GetOffset() == 0) + { + mSendMessage->SetTxSuccess(true); + } + mSendMessageMaxMacTxAttempts = Mac::kDirectFrameMacTxAttempts; GetNetif().GetMac().SendFrameRequest(mMacSender); ExitNow(); @@ -962,18 +967,22 @@ void MeshForwarder::HandleSentFrame(Mac::Frame &aFrame, otError aError) if (mSendMessage->GetDirectTransmission()) { + if (aError != OT_ERROR_NONE) + { + // If the transmission of any fragment frame fails, + // the overall message transmission is considered + // as failed + + mSendMessage->SetTxSuccess(false); #if OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE - if (aError != OT_ERROR_NONE) - { // We set the NextOffset to end of message to avoid sending // any remaining fragments in the message. mMessageNextOffset = mSendMessage->GetLength(); - } - #endif + } if (mMessageNextOffset < mSendMessage->GetLength()) { @@ -983,6 +992,11 @@ void MeshForwarder::HandleSentFrame(Mac::Frame &aFrame, otError aError) { mSendMessage->ClearDirectTransmission(); mSendMessage->SetOffset(0); + + if (neighbor != NULL) + { + neighbor->GetLinkInfo().AddMessageTxStatus(mSendMessage->GetTxSuccess()); + } } if (mSendMessage->GetSubType() == Message::kSubTypeMleDiscoverRequest) diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp index f52018e0f..a03757d4e 100644 --- a/src/core/thread/mesh_forwarder_ftd.cpp +++ b/src/core/thread/mesh_forwarder_ftd.cpp @@ -477,6 +477,7 @@ Message *MeshForwarder::GetIndirectTransmission(Child &aChild) aChild.SetIndirectMessage(message); aChild.SetIndirectFragmentOffset(0); aChild.ResetIndirectTxAttempts(); + aChild.SetIndirectTxSuccess(true); if (message != NULL) { @@ -644,6 +645,7 @@ void MeshForwarder::HandleSentFrameToChild(const Mac::Frame &aFrame, otError aEr } child->ResetIndirectTxAttempts(); + child->SetIndirectTxSuccess(false); #if OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE // We set the NextOffset to end of message, since there is no need to @@ -670,6 +672,7 @@ void MeshForwarder::HandleSentFrameToChild(const Mac::Frame &aFrame, otError aEr { child->SetIndirectFragmentOffset(0); child->SetIndirectMessage(NULL); + child->GetLinkInfo().AddMessageTxStatus(child->GetIndirectTxSuccess()); // Enable short source address matching after the first indirect // message transmission attempt to the child. We intentionally do diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 0ea60b878..8f57f1fe2 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -3949,11 +3949,16 @@ otError MleRouter::GetChildInfo(Child &aChild, otChildInfo &aChildInfo) aChildInfo.mAverageRssi = aChild.GetLinkInfo().GetAverageRss(); aChildInfo.mLastRssi = aChild.GetLinkInfo().GetLastRss(); - aChildInfo.mRxOnWhenIdle = aChild.IsRxOnWhenIdle(); - aChildInfo.mSecureDataRequest = aChild.IsSecureDataRequest(); - aChildInfo.mFullFunction = aChild.IsFullThreadDevice(); - aChildInfo.mFullNetworkData = aChild.IsFullNetworkData(); - aChildInfo.mIsStateRestoring = aChild.IsStateRestoring(); +#if OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING + aChildInfo.mFrameErrorRate = aChild.GetLinkInfo().GetFrameErrorRate(); + aChildInfo.mMessageErrorRate = aChild.GetLinkInfo().GetMessageErrorRate(); +#endif + + aChildInfo.mRxOnWhenIdle = aChild.IsRxOnWhenIdle(); + aChildInfo.mSecureDataRequest = aChild.IsSecureDataRequest(); + aChildInfo.mFullFunction = aChild.IsFullThreadDevice(); + aChildInfo.mFullNetworkData = aChild.IsFullNetworkData(); + aChildInfo.mIsStateRestoring = aChild.IsStateRestoring(); exit: return error; @@ -4049,6 +4054,10 @@ exit: aNeighInfo.mLinkQualityIn = neighbor->GetLinkInfo().GetLinkQuality(); aNeighInfo.mAverageRssi = neighbor->GetLinkInfo().GetAverageRss(); aNeighInfo.mLastRssi = neighbor->GetLinkInfo().GetLastRss(); +#if OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING + aNeighInfo.mFrameErrorRate = neighbor->GetLinkInfo().GetFrameErrorRate(); + aNeighInfo.mMessageErrorRate = neighbor->GetLinkInfo().GetMessageErrorRate(); +#endif aNeighInfo.mRxOnWhenIdle = neighbor->IsRxOnWhenIdle(); aNeighInfo.mSecureDataRequest = neighbor->IsSecureDataRequest(); aNeighInfo.mFullFunction = neighbor->IsFullThreadDevice(); diff --git a/src/core/thread/topology.hpp b/src/core/thread/topology.hpp index 34116d84c..c426f4bbd 100644 --- a/src/core/thread/topology.hpp +++ b/src/core/thread/topology.hpp @@ -585,6 +585,22 @@ public: */ void SetIndirectFragmentOffset(uint16_t aFragmentOffset) { mIndirectFragmentOffset = aFragmentOffset; } + /** + * This method gets the transmission status (success/failure) of the indirect transmission. + * + * @returns The transmission status of indirect transmission, `true` indicating success, `false` indicating failure. + * + */ + bool GetIndirectTxSuccess(void) const { return mIndirectTxSuccess; } + + /** + * This method sets the transmission status (success/failure) of the indirect transmission. + * + * @param[in] aTxStatus The transmission status, `true` indicating success, `false` indicating failure. + * + */ + void SetIndirectTxSuccess(bool aTxStatus) { mIndirectTxSuccess = aTxStatus; } + /** * This method gets the IEEE 802.15.4 Key ID to use for indirect retransmissions. * @@ -778,7 +794,8 @@ private: uint32_t mIndirectFrameCounter; ///< Frame counter for current indirect message (used fore retx). Message *mIndirectMessage; ///< Current indirect message. - uint16_t mIndirectFragmentOffset; ///< 6LoWPAN fragment offset for the indirect message. + uint16_t mIndirectFragmentOffset : 15; ///< 6LoWPAN fragment offset for the indirect message. + bool mIndirectTxSuccess : 1; ///< Indicates tx success/failure of current indirect message. uint8_t mIndirectKeyId; ///< Key Id for current indirect message (used for retx). uint8_t mIndirectTxAttempts; ///< Number of data poll triggered tx attempts. uint8_t mIndirectDsn; ///< MAC level Data Sequence Number (DSN) for retx attempts.