From 101eac0bc124620f15b2fd93619b7b464e90697f Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 9 Dec 2024 10:19:11 -0800 Subject: [PATCH] [mle] add mechanism to track current attach duration (#10923) This commit adds a new mechanism to track the current attach time (the time duration since device was last attached to a Thread mesh). This duration indicates how long the device has been connected, regardless of its role(child, router, or leader). A related public OT API and CLI command are also added. This information can be used for debugging purposes and internally within the OT core to wait for the device to stabilize before enabling certain behaviors. --- include/openthread/instance.h | 2 +- include/openthread/thread.h | 17 +++++++++++++++++ src/cli/README.md | 15 +++++++++++++++ src/cli/cli.cpp | 31 +++++++++++++++++++++++++++++++ src/core/api/thread_api.cpp | 7 +++++++ src/core/thread/mle.cpp | 23 ++++++++++++++++++++++- src/core/thread/mle.hpp | 20 ++++++++++++-------- 7 files changed, 105 insertions(+), 10 deletions(-) diff --git a/include/openthread/instance.h b/include/openthread/instance.h index d277ec8d97b..d23454c901e 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -52,7 +52,7 @@ extern "C" { * * @note This number versions both OpenThread platform and user APIs. */ -#define OPENTHREAD_API_VERSION (465) +#define OPENTHREAD_API_VERSION (466) /** * @addtogroup api-instance diff --git a/include/openthread/thread.h b/include/openthread/thread.h index 6c5bf2b5e25..1491e1b465b 100644 --- a/include/openthread/thread.h +++ b/include/openthread/thread.h @@ -931,6 +931,23 @@ const otMleCounters *otThreadGetMleCounters(otInstance *aInstance); */ void otThreadResetMleCounters(otInstance *aInstance); +/** + * Gets the current attach duration (number of seconds since the device last attached). + * + * Requires the `OPENTHREAD_CONFIG_UPTIME_ENABLE` feature. + * + * If the device is not currently attached, zero will be returned. + * + * Unlike the role-tracking variables in `otMleCounters`, which track the cumulative time the device is in each role, + * this function tracks the time since the last successful attachment, indicating how long the device has been + * connected to the Thread mesh (regardless of its role, whether acting as a child, router, or leader). + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns The number of seconds since last attached. + */ +uint32_t otThreadGetCurrentAttachDuration(otInstance *aInstance); + /** * Pointer is called every time an MLE Parent Response message is received. * diff --git a/src/cli/README.md b/src/cli/README.md index 9fc8a7dd2af..a463e2b05ad 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -21,6 +21,7 @@ Done ## OpenThread Command List +- [attachtime](#attachtime) - [ba](#ba) - [bbr](#bbr) - [br](README_BR.md) @@ -137,6 +138,20 @@ Done ## OpenThread Command Details +### attachtime + +Prints the attach time (duration since device was last attached). + +Requires `OPENTHREAD_CONFIG_UPTIME_ENABLE`. + +Duration is formatted as `{hh}:{mm}:{ss}` for hours, minutes, and seconds if it is less than one day. If the duration is longer than one day, the format is `{dd}d.{hh}:{mm}:{ss}`. + +```bash +> attachtime +01:38:25 +Done +``` + ### bbr Show current Primary Backbone Router information for Thread 1.2 device. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 708d68ecb72..dfbed9121cd 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -364,6 +364,34 @@ otError Interpreter::SetUserCommands(const otCliCommand *aCommands, uint8_t aLen #if OPENTHREAD_FTD || OPENTHREAD_MTD +#if OPENTHREAD_CONFIG_UPTIME_ENABLE +/** + * @cli attachtime + * @code + * attachtime + * 01:38:25 + * Done + * @endcode + * @par + * Prints the current attach time (duration since device was last attached). Requires `OPENTHREAD_CONFIG_UPTIME_ENABLE`. + * Duration is formatted as `{hh}:{mm}:{ss}` for hours, minutes, and seconds if it is less than one day. If the + * duration is longer than one day, the format is `{dd}d.{hh}:{mm}:{ss}`. + */ +template <> otError Interpreter::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + char string[OT_DURATION_STRING_SIZE]; + + VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + + otConvertDurationInSecondsToString(otThreadGetCurrentAttachDuration(GetInstancePtr()), string, sizeof(string)); + OutputLine("%s", string); + +exit: + return error; +} +#endif + #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE template <> otError Interpreter::Process(Arg aArgs[]) { return mHistory.Process(aArgs); } #endif @@ -8425,6 +8453,9 @@ otError Interpreter::ProcessCommand(Arg aArgs[]) static constexpr Command kCommands[] = { #if OPENTHREAD_FTD || OPENTHREAD_MTD +#if OPENTHREAD_CONFIG_UPTIME_ENABLE + CmdEntry("attachtime"), +#endif #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE CmdEntry("ba"), #endif diff --git a/src/core/api/thread_api.cpp b/src/core/api/thread_api.cpp index b1bf11f0f07..0f89e0afa22 100644 --- a/src/core/api/thread_api.cpp +++ b/src/core/api/thread_api.cpp @@ -475,6 +475,13 @@ const otMleCounters *otThreadGetMleCounters(otInstance *aInstance) void otThreadResetMleCounters(otInstance *aInstance) { AsCoreType(aInstance).Get().ResetCounters(); } +#if OPENTHREAD_CONFIG_UPTIME_ENABLE +uint32_t otThreadGetCurrentAttachDuration(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().GetCurrentAttachDuration(); +} +#endif + #if OPENTHREAD_CONFIG_MLE_PARENT_RESPONSE_CALLBACK_API_ENABLE void otThreadRegisterParentResponseCallback(otInstance *aInstance, otThreadParentResponseCallback aCallback, diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index ce9234783e0..79960795dad 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -240,6 +240,15 @@ void Mle::Stop(StopMode aMode) } } +const Counters &Mle::GetCounters(void) +{ +#if OPENTHREAD_CONFIG_UPTIME_ENABLE + UpdateRoleTimeCounters(mRole); +#endif + + return mCounters; +} + void Mle::ResetCounters(void) { ClearAllBytes(mCounters); @@ -249,6 +258,12 @@ void Mle::ResetCounters(void) } #if OPENTHREAD_CONFIG_UPTIME_ENABLE + +uint32_t Mle::GetCurrentAttachDuration(void) const +{ + return IsAttached() ? Uptime::MsecToSec(Get().GetUptime()) - mLastAttachTime : 0; +} + void Mle::UpdateRoleTimeCounters(DeviceRole aRole) { uint64_t currentUptimeMsec = Get().GetUptime(); @@ -277,7 +292,8 @@ void Mle::UpdateRoleTimeCounters(DeviceRole aRole) break; } } -#endif + +#endif // OPENTHREAD_CONFIG_UPTIME_ENABLE void Mle::SetRole(DeviceRole aRole) { @@ -288,6 +304,11 @@ void Mle::SetRole(DeviceRole aRole) LogNote("Role %s -> %s", RoleToString(oldRole), RoleToString(mRole)); #if OPENTHREAD_CONFIG_UPTIME_ENABLE + if ((oldRole == kRoleDetached) && IsAttached()) + { + mLastAttachTime = Uptime::MsecToSec(Get().GetUptime()); + } + UpdateRoleTimeCounters(oldRole); #endif diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index 6d9652f4df2..a24f71e0d00 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -598,19 +598,22 @@ class Mle : public InstanceLocator, private NonCopyable * * @returns A reference to the MLE counters. */ - const Counters &GetCounters(void) - { -#if OPENTHREAD_CONFIG_UPTIME_ENABLE - UpdateRoleTimeCounters(mRole); -#endif - return mCounters; - } + const Counters &GetCounters(void); /** * Resets the MLE counters. */ void ResetCounters(void); +#if OPENTHREAD_CONFIG_UPTIME_ENABLE + /** + * Determines the current attach duration (number of seconds since the device last attached). + * + * @returns Current attach duration in seconds. + */ + uint32_t GetCurrentAttachDuration(void) const; +#endif + #if OPENTHREAD_CONFIG_MLE_PARENT_RESPONSE_CALLBACK_API_ENABLE /** * Registers the client callback that is called when processing an MLE Parent Response message. @@ -1477,10 +1480,11 @@ class Mle : public InstanceLocator, private NonCopyable #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE uint32_t mCslTimeout; #endif - uint64_t mAlternateTimestamp; #if OPENTHREAD_CONFIG_UPTIME_ENABLE + uint32_t mLastAttachTime; uint64_t mLastUpdatedTimestamp; #endif + uint64_t mAlternateTimestamp; LeaderData mLeaderData; Parent mParent;