mirror of
https://github.com/espressif/openthread.git
synced 2026-08-01 00:27:47 +00:00
[link-metrics] implement link metrics manager (#9375)
This commit implements a new module LinkMetricsManager, which utilizes the Link Metrics feature to get the Link Metrics data from neighboring devices. The commit also adds a few tests to the module: - Unit Test: tests/unit/test_link_metrics_manager.cpp, will be run in `unit-tests` in `unit.yml`. - Expect Test: tests/scripts/expect/v1_2-linkmetricsmgr.exp, will be run in `expects` in `simulation-1.2.yml`. - Simulation Test: tests/scripts/thread-cert/v1_2_LowPower_test_link_metrics_manager.py, will be run in `packet-verification-low-power` in `simulation-1.2.yml`.
This commit is contained in:
@@ -207,6 +207,7 @@ ot_option(OT_IP6_FRAGM OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE "ipv6 fragment
|
||||
ot_option(OT_JAM_DETECTION OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE "jam detection")
|
||||
ot_option(OT_JOINER OPENTHREAD_CONFIG_JOINER_ENABLE "joiner")
|
||||
ot_option(OT_LINK_METRICS_INITIATOR OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE "link metrics initiator")
|
||||
ot_option(OT_LINK_METRICS_MANAGER OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE "link metrics manager")
|
||||
ot_option(OT_LINK_METRICS_SUBJECT OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE "link metrics subject")
|
||||
ot_option(OT_LINK_RAW OPENTHREAD_CONFIG_LINK_RAW_ENABLE "link raw service")
|
||||
ot_option(OT_LOG_LEVEL_DYNAMIC OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE "dynamic log level control")
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
#define OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_JOINER_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_LINK_RAW_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_LOG_LEVEL OT_LOG_LEVEL_INFO
|
||||
#define OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE 1
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
#define OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_JOINER_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_LINK_RAW_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_LOG_LEVEL OT_LOG_LEVEL_INFO
|
||||
#define OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE 0
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
#define OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE 0
|
||||
#define OPENTHREAD_CONFIG_JOINER_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE 0
|
||||
#define OPENTHREAD_CONFIG_LINK_RAW_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_LOG_LEVEL OT_LOG_LEVEL_INFO
|
||||
#define OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE 0
|
||||
|
||||
@@ -53,7 +53,7 @@ extern "C" {
|
||||
* @note This number versions both OpenThread platform and user APIs.
|
||||
*
|
||||
*/
|
||||
#define OPENTHREAD_API_VERSION (355)
|
||||
#define OPENTHREAD_API_VERSION (356)
|
||||
|
||||
/**
|
||||
* @addtogroup api-instance
|
||||
|
||||
@@ -249,6 +249,31 @@ otError otLinkMetricsSendLinkProbe(otInstance *aInstance,
|
||||
uint8_t aSeriesId,
|
||||
uint8_t aLength);
|
||||
|
||||
/**
|
||||
* Enable or disable Link Metrics Manager.
|
||||
*
|
||||
* @param[in] aInstance A pointer to an OpenThread instance.
|
||||
* @param[in] aEnable A boolean indicating to enable or disable.
|
||||
*
|
||||
*/
|
||||
void otLinkMetricsManagerSetEnabled(otInstance *aInstance, bool aEnable);
|
||||
|
||||
/**
|
||||
* Get Link Metrics data of a neighbor by its extended address.
|
||||
*
|
||||
* @param[in] aInstance A pointer to an OpenThread instance.
|
||||
* @param[in] aExtAddress A pointer to the Mac extended address of the Probing Subject.
|
||||
* @param[out] aLinkMetricsValues A pointer to the Link Metrics values of the subject.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Successfully got the Link Metrics data.
|
||||
* @retval OT_ERROR_INVALID_ARGS The arguments are invalid.
|
||||
* @retval OT_ERROR_NOT_FOUND No neighbor with the given extended address is found.
|
||||
*
|
||||
*/
|
||||
otError otLinkMetricsManagerGetMetricsValueByExtAddr(otInstance *aInstance,
|
||||
const otExtAddress *aExtAddress,
|
||||
otLinkMetricsValues *aLinkMetricsValues);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*
|
||||
|
||||
@@ -129,6 +129,7 @@ build_simulation()
|
||||
options+=("-DOT_CSL_RECEIVER=ON")
|
||||
options+=("-DOT_LINK_METRICS_INITIATOR=ON")
|
||||
options+=("-DOT_LINK_METRICS_SUBJECT=ON")
|
||||
options+=("-DOT_LINK_METRICS_MANAGER=ON")
|
||||
fi
|
||||
|
||||
if [[ ${ot_extra_options[*]+x} ]]; then
|
||||
@@ -162,6 +163,9 @@ build_posix()
|
||||
if [[ ${version} != "1.1" ]]; then
|
||||
options+=("-DOT_DUA=ON")
|
||||
options+=("-DOT_MLR=ON")
|
||||
options+=("-DOT_LINK_METRICS_INITIATOR=ON")
|
||||
options+=("-DOT_LINK_METRICS_SUBJECT=ON")
|
||||
options+=("-DOT_LINK_METRICS_MANAGER=ON")
|
||||
fi
|
||||
|
||||
if [[ ${FULL_LOGS} == 1 ]]; then
|
||||
|
||||
@@ -66,6 +66,7 @@ Done
|
||||
- [leaderdata](#leaderdata)
|
||||
- [leaderweight](#leaderweight)
|
||||
- [linkmetrics](#linkmetrics-mgmt-ipaddr-enhanced-ack-clear)
|
||||
- [linkmetricsmgr](#linkmetricsmgr-disable)
|
||||
- [locate](#locate)
|
||||
- [log](#log-filename-filename)
|
||||
- [mac](#mac-retries-direct)
|
||||
@@ -1877,6 +1878,41 @@ Done
|
||||
- RSSI: -18 (dBm) (Exponential Moving Average)
|
||||
```
|
||||
|
||||
### linkmetricsmgr disable
|
||||
|
||||
Disable the Link Metrics Manager.
|
||||
|
||||
`OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE` is required.
|
||||
|
||||
```bash
|
||||
> linkmetricsmgr disable
|
||||
Done
|
||||
```
|
||||
|
||||
### linkmetricsmgr enable
|
||||
|
||||
Enable the Link Metrics Manager.
|
||||
|
||||
`OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE` is required.
|
||||
|
||||
```bash
|
||||
> linkmetricsmgr enable
|
||||
Done
|
||||
```
|
||||
|
||||
### linkmetricsmgr show
|
||||
|
||||
Display the Link Metrics data of all subjects. The subjects are identified by its extended address.
|
||||
|
||||
`OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE` is required.
|
||||
|
||||
```bash
|
||||
|
||||
> linkmetricsmgr show
|
||||
ExtAddr:827aa7f7f63e1234, LinkMargin:80, Rssi:-20
|
||||
Done
|
||||
```
|
||||
|
||||
### locate
|
||||
|
||||
Gets the current state (`In Progress` or `Idle`) of anycast locator.
|
||||
|
||||
@@ -3863,6 +3863,72 @@ exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
|
||||
template <> otError Interpreter::Process<Cmd("linkmetricsmgr")>(Arg aArgs[])
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
||||
|
||||
/**
|
||||
* @cli linkmetricsmgr (enable,disable)
|
||||
* @code
|
||||
* linkmetricmgr enable
|
||||
* Done
|
||||
* @endcode
|
||||
* @code
|
||||
* linkmetricmgr disable
|
||||
* Done
|
||||
* @endcode
|
||||
* @cparam linkmetricsmgr @ca{enable|disable}
|
||||
* @par api_copy
|
||||
* #otLinkMetricsManagerSetEnabled
|
||||
*
|
||||
*/
|
||||
if (ProcessEnableDisable(aArgs, otLinkMetricsManagerSetEnabled) == OT_ERROR_NONE)
|
||||
{
|
||||
}
|
||||
/**
|
||||
* @cli linkmetricsmgr show
|
||||
* @code
|
||||
* linkmetricsmgr show
|
||||
* ExtAddr:827aa7f7f63e1234, LinkMargin:80, Rssi:-20
|
||||
* Done
|
||||
* @endcode
|
||||
* @par api_copy
|
||||
* #otLinkMetricsManagerGetMetricsValueByExtAddr
|
||||
*
|
||||
*/
|
||||
else if (aArgs[0] == "show")
|
||||
{
|
||||
otNeighborInfoIterator iterator = OT_NEIGHBOR_INFO_ITERATOR_INIT;
|
||||
otNeighborInfo neighborInfo;
|
||||
|
||||
while (otThreadGetNextNeighborInfo(GetInstancePtr(), &iterator, &neighborInfo) == OT_ERROR_NONE)
|
||||
{
|
||||
otLinkMetricsValues linkMetricsValues;
|
||||
|
||||
if (otLinkMetricsManagerGetMetricsValueByExtAddr(GetInstancePtr(), &neighborInfo.mExtAddress,
|
||||
&linkMetricsValues) != OT_ERROR_NONE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
OutputFormat("ExtAddr:");
|
||||
OutputExtAddress(neighborInfo.mExtAddress);
|
||||
OutputLine(", LinkMargin:%u, Rssi:%d", linkMetricsValues.mLinkMarginValue, linkMetricsValues.mRssiValue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error = OT_ERROR_INVALID_COMMAND;
|
||||
}
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
#endif // OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
|
||||
|
||||
otError Interpreter::ParseLinkMetricsFlags(otLinkMetrics &aLinkMetrics, const Arg &aFlags)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
@@ -8352,6 +8418,9 @@ otError Interpreter::ProcessCommand(Arg aArgs[])
|
||||
#endif
|
||||
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
|
||||
CmdEntry("linkmetrics"),
|
||||
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
|
||||
CmdEntry("linkmetricsmgr"),
|
||||
#endif
|
||||
#endif
|
||||
#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
|
||||
CmdEntry("locate"),
|
||||
|
||||
@@ -706,6 +706,8 @@ openthread_core_files = [
|
||||
"utils/history_tracker.hpp",
|
||||
"utils/jam_detector.cpp",
|
||||
"utils/jam_detector.hpp",
|
||||
"utils/link_metrics_manager.cpp",
|
||||
"utils/link_metrics_manager.hpp",
|
||||
"utils/mesh_diag.cpp",
|
||||
"utils/mesh_diag.hpp",
|
||||
"utils/otns.cpp",
|
||||
@@ -792,6 +794,7 @@ source_set("libopenthread_core_config") {
|
||||
"config/history_tracker.h",
|
||||
"config/ip6.h",
|
||||
"config/joiner.h",
|
||||
"config/link_metrics_manager.h",
|
||||
"config/link_quality.h",
|
||||
"config/link_raw.h",
|
||||
"config/logging.h",
|
||||
|
||||
@@ -244,6 +244,7 @@ set(COMMON_SOURCES
|
||||
utils/heap.cpp
|
||||
utils/history_tracker.cpp
|
||||
utils/jam_detector.cpp
|
||||
utils/link_metrics_manager.cpp
|
||||
utils/mesh_diag.cpp
|
||||
utils/otns.cpp
|
||||
utils/parse_cmdline.cpp
|
||||
|
||||
@@ -54,7 +54,6 @@ otError otLinkMetricsQuery(otInstance *aInstance,
|
||||
AsCoreTypePtr(aLinkMetricsFlags));
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
|
||||
otError otLinkMetricsConfigForwardTrackingSeries(otInstance *aInstance,
|
||||
const otIp6Address *aDestination,
|
||||
uint8_t aSeriesId,
|
||||
@@ -98,6 +97,26 @@ otError otLinkMetricsSendLinkProbe(otInstance *aInstance,
|
||||
|
||||
return initiator.SendLinkProbe(AsCoreType(aDestination), aSeriesId, aLength);
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
|
||||
void otLinkMetricsManagerSetEnabled(otInstance *aInstance, bool aEnable)
|
||||
{
|
||||
AsCoreType(aInstance).Get<Utils::LinkMetricsManager>().SetEnabled(aEnable);
|
||||
}
|
||||
|
||||
otError otLinkMetricsManagerGetMetricsValueByExtAddr(otInstance *aInstance,
|
||||
const otExtAddress *aExtAddress,
|
||||
otLinkMetricsValues *aLinkMetricsValues)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
VerifyOrExit(aExtAddress != nullptr && aLinkMetricsValues != nullptr, error = OT_ERROR_INVALID_ARGS);
|
||||
|
||||
error = AsCoreType(aInstance).Get<Utils::LinkMetricsManager>().GetLinkMetricsValueByExtAddr(
|
||||
AsCoreType(aExtAddress), AsCoreType(aLinkMetricsValues));
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
|
||||
|
||||
@@ -216,6 +216,9 @@ Instance::Instance(void)
|
||||
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
|
||||
, mHistoryTracker(*this)
|
||||
#endif
|
||||
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
|
||||
, mLinkMetricsManager(*this)
|
||||
#endif
|
||||
#if (OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE || OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE) && OPENTHREAD_FTD
|
||||
, mDatasetUpdater(*this)
|
||||
#endif
|
||||
|
||||
@@ -127,6 +127,7 @@
|
||||
#include "utils/heap.hpp"
|
||||
#include "utils/history_tracker.hpp"
|
||||
#include "utils/jam_detector.hpp"
|
||||
#include "utils/link_metrics_manager.hpp"
|
||||
#include "utils/mesh_diag.hpp"
|
||||
#include "utils/ping_sender.hpp"
|
||||
#include "utils/slaac_address.hpp"
|
||||
@@ -610,6 +611,10 @@ private:
|
||||
Utils::HistoryTracker mHistoryTracker;
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
|
||||
Utils::LinkMetricsManager mLinkMetricsManager;
|
||||
#endif
|
||||
|
||||
#if (OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE || OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE) && OPENTHREAD_FTD
|
||||
MeshCoP::DatasetUpdater mDatasetUpdater;
|
||||
#endif
|
||||
@@ -901,6 +906,10 @@ template <> inline Utils::MeshDiag &Instance::Get(void) { return mMeshDiag; }
|
||||
template <> inline Utils::HistoryTracker &Instance::Get(void) { return mHistoryTracker; }
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
|
||||
template <> inline Utils::LinkMetricsManager &Instance::Get(void) { return mLinkMetricsManager; }
|
||||
#endif
|
||||
|
||||
#if (OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE || OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE) && OPENTHREAD_FTD
|
||||
template <> inline MeshCoP::DatasetUpdater &Instance::Get(void) { return mDatasetUpdater; }
|
||||
#endif
|
||||
|
||||
@@ -186,6 +186,9 @@ void Notifier::EmitEvents(void)
|
||||
// being published (if needed).
|
||||
Get<NetworkData::Publisher>().HandleNotifierEvents(events);
|
||||
#endif
|
||||
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
|
||||
Get<Utils::LinkMetricsManager>().HandleNotifierEvents(events);
|
||||
#endif
|
||||
|
||||
for (ExternalCallback &callback : mExternalCallbacks)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2023, The OpenThread Authors.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* This file includes compile-time configurations for Link Metrics Manager.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_LINK_METRICS_MANAGER_H_
|
||||
#define CONFIG_LINK_METRICS_MANAGER_H_
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
|
||||
*
|
||||
* Define as 1 to enable Link Metrics Manager support.
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
|
||||
#define OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ON_BY_DEFAULT
|
||||
*
|
||||
* Define as 1 to make Link Metrics Manager function on by default.
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ON_BY_DEFAULT
|
||||
#define OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ON_BY_DEFAULT 0
|
||||
#endif
|
||||
|
||||
#endif // CONFIG_LINK_METRICS_MANAGER_H_
|
||||
@@ -92,6 +92,7 @@
|
||||
#include "config/history_tracker.h"
|
||||
#include "config/ip6.h"
|
||||
#include "config/joiner.h"
|
||||
#include "config/link_metrics_manager.h"
|
||||
#include "config/link_quality.h"
|
||||
#include "config/link_raw.h"
|
||||
#include "config/logging.h"
|
||||
|
||||
@@ -75,6 +75,7 @@ namespace ot {
|
||||
*/
|
||||
|
||||
class SupervisionListener;
|
||||
class UnitTester;
|
||||
|
||||
/**
|
||||
* @namespace ot::Mle
|
||||
@@ -111,6 +112,7 @@ class Mle : public InstanceLocator, private NonCopyable
|
||||
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
|
||||
friend class ot::LinkMetrics::Initiator;
|
||||
#endif
|
||||
friend class ot::UnitTester;
|
||||
|
||||
public:
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,377 @@
|
||||
/*
|
||||
* Copyright (c) 2023, The OpenThread Authors.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "link_metrics_manager.hpp"
|
||||
|
||||
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
|
||||
|
||||
#include "common/as_core_type.hpp"
|
||||
#include "common/error.hpp"
|
||||
#include "common/locator_getters.hpp"
|
||||
#include "common/log.hpp"
|
||||
#include "common/notifier.hpp"
|
||||
#include "thread/mle.hpp"
|
||||
#include "thread/neighbor_table.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace Utils {
|
||||
|
||||
RegisterLogModule("LinkMetricsMgr");
|
||||
|
||||
/**
|
||||
* @addtogroup utils-link-metrics-manager
|
||||
*
|
||||
* @brief
|
||||
* This module includes definitions for Link Metrics Manager.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
LinkMetricsManager::LinkMetricsManager(Instance &aInstance)
|
||||
: InstanceLocator(aInstance)
|
||||
, mTimer(aInstance)
|
||||
, mEnabled(OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ON_BY_DEFAULT)
|
||||
{
|
||||
}
|
||||
|
||||
void LinkMetricsManager::SetEnabled(bool aEnable)
|
||||
{
|
||||
VerifyOrExit(mEnabled != aEnable);
|
||||
mEnabled = aEnable;
|
||||
|
||||
if (mEnabled)
|
||||
{
|
||||
Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
Error LinkMetricsManager::GetLinkMetricsValueByExtAddr(const Mac::ExtAddress &aExtAddress,
|
||||
LinkMetrics::MetricsValues &aMetricsValues)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
Subject *subject;
|
||||
|
||||
subject = mSubjectList.FindMatching(aExtAddress);
|
||||
VerifyOrExit(subject != nullptr, error = kErrorNotFound);
|
||||
|
||||
aMetricsValues.mLinkMarginValue = subject->mData.mLinkMargin;
|
||||
aMetricsValues.mRssiValue = subject->mData.mRssi;
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
void LinkMetricsManager::Start(void)
|
||||
{
|
||||
LinkMetrics::Initiator &initiator = Get<LinkMetrics::Initiator>();
|
||||
|
||||
VerifyOrExit(mEnabled && Get<Mle::Mle>().IsAttached());
|
||||
|
||||
initiator.SetMgmtResponseCallback(HandleMgmtResponse, this);
|
||||
initiator.SetEnhAckProbingCallback(HandleEnhAckIe, this);
|
||||
|
||||
mTimer.Start(kTimeBeforeStartMilliSec);
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void LinkMetricsManager::Stop(void)
|
||||
{
|
||||
LinkMetrics::Initiator &initiator = Get<LinkMetrics::Initiator>();
|
||||
|
||||
mTimer.Stop();
|
||||
|
||||
initiator.SetMgmtResponseCallback(nullptr, nullptr);
|
||||
initiator.SetEnhAckProbingCallback(nullptr, nullptr);
|
||||
|
||||
UnregisterAllSubjects();
|
||||
ReleaseAllSubjects();
|
||||
}
|
||||
|
||||
void LinkMetricsManager::Update(void)
|
||||
{
|
||||
UpdateSubjects();
|
||||
UpdateLinkMetricsStates();
|
||||
}
|
||||
|
||||
// This method updates the Link Metrics Subject in the subject list. It adds new neighbors to the list.
|
||||
void LinkMetricsManager::UpdateSubjects(void)
|
||||
{
|
||||
Neighbor::Info neighborInfo;
|
||||
otNeighborInfoIterator iterator = OT_NEIGHBOR_INFO_ITERATOR_INIT;
|
||||
|
||||
while (Get<NeighborTable>().GetNextNeighborInfo(iterator, neighborInfo) == kErrorNone)
|
||||
{
|
||||
// if not in the subject list, allocate and add
|
||||
if (!mSubjectList.ContainsMatching(AsCoreType(&neighborInfo.mExtAddress)))
|
||||
{
|
||||
Subject *subject = mPool.Allocate();
|
||||
|
||||
VerifyOrExit(subject != nullptr);
|
||||
|
||||
subject->Clear();
|
||||
subject->mExtAddress = AsCoreType(&neighborInfo.mExtAddress);
|
||||
IgnoreError(mSubjectList.Add(*subject));
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
// This method updates the state and take corresponding actions for all subjects and removes stale subjects.
|
||||
void LinkMetricsManager::UpdateLinkMetricsStates(void)
|
||||
{
|
||||
LinkedList<Subject> staleSubjects;
|
||||
|
||||
mSubjectList.RemoveAllMatching(*this, staleSubjects);
|
||||
|
||||
while (!staleSubjects.IsEmpty())
|
||||
{
|
||||
Subject *subject = staleSubjects.Pop();
|
||||
|
||||
mPool.Free(*subject);
|
||||
}
|
||||
}
|
||||
|
||||
void LinkMetricsManager::UnregisterAllSubjects(void)
|
||||
{
|
||||
for (Subject &subject : mSubjectList)
|
||||
{
|
||||
IgnoreError(subject.UnregisterEap(GetInstance()));
|
||||
}
|
||||
}
|
||||
|
||||
void LinkMetricsManager::ReleaseAllSubjects(void)
|
||||
{
|
||||
while (!mSubjectList.IsEmpty())
|
||||
{
|
||||
Subject *subject = mSubjectList.Pop();
|
||||
|
||||
mPool.Free(*subject);
|
||||
}
|
||||
}
|
||||
|
||||
void LinkMetricsManager::HandleNotifierEvents(Events aEvents)
|
||||
{
|
||||
if (aEvents.Contains(kEventThreadRoleChanged))
|
||||
{
|
||||
if (Get<Mle::Mle>().IsAttached())
|
||||
{
|
||||
Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LinkMetricsManager::HandleTimer(void)
|
||||
{
|
||||
if (Get<Mle::Mle>().IsAttached())
|
||||
{
|
||||
Update();
|
||||
mTimer.Start(kStateUpdateIntervalMilliSec);
|
||||
}
|
||||
}
|
||||
|
||||
void LinkMetricsManager::HandleMgmtResponse(const otIp6Address *aAddress, otLinkMetricsStatus aStatus, void *aContext)
|
||||
{
|
||||
static_cast<LinkMetricsManager *>(aContext)->HandleMgmtResponse(aAddress, aStatus);
|
||||
}
|
||||
|
||||
void LinkMetricsManager::HandleMgmtResponse(const otIp6Address *aAddress, otLinkMetricsStatus aStatus)
|
||||
{
|
||||
Mac::ExtAddress extAddress;
|
||||
Subject *subject;
|
||||
Neighbor *neighbor;
|
||||
|
||||
AsCoreType(aAddress).GetIid().ConvertToExtAddress(extAddress);
|
||||
neighbor = Get<NeighborTable>().FindNeighbor(extAddress);
|
||||
VerifyOrExit(neighbor != nullptr);
|
||||
|
||||
subject = mSubjectList.FindMatching(extAddress);
|
||||
VerifyOrExit(subject != nullptr);
|
||||
|
||||
switch (MapEnum(aStatus))
|
||||
{
|
||||
case LinkMetrics::Status::kStatusSuccess:
|
||||
subject->mState = SubjectState::kActive;
|
||||
break;
|
||||
default:
|
||||
subject->mState = SubjectState::kNotConfigured;
|
||||
break;
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void LinkMetricsManager::HandleEnhAckIe(otShortAddress aShortAddress,
|
||||
const otExtAddress *aExtAddress,
|
||||
const otLinkMetricsValues *aMetricsValues,
|
||||
void *aContext)
|
||||
{
|
||||
static_cast<LinkMetricsManager *>(aContext)->HandleEnhAckIe(aShortAddress, aExtAddress, aMetricsValues);
|
||||
}
|
||||
|
||||
void LinkMetricsManager::HandleEnhAckIe(otShortAddress aShortAddress,
|
||||
const otExtAddress *aExtAddress,
|
||||
const otLinkMetricsValues *aMetricsValues)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aShortAddress);
|
||||
|
||||
Error error = kErrorNone;
|
||||
Subject *subject = mSubjectList.FindMatching(AsCoreType(aExtAddress));
|
||||
|
||||
VerifyOrExit(subject != nullptr, error = kErrorNotFound);
|
||||
|
||||
VerifyOrExit(subject->mState == SubjectState::kActive || subject->mState == SubjectState::kRenewing);
|
||||
subject->mLastUpdateTime = TimerMilli::GetNow();
|
||||
|
||||
VerifyOrExit(aMetricsValues->mMetrics.mRssi && aMetricsValues->mMetrics.mLinkMargin, error = kErrorInvalidArgs);
|
||||
|
||||
subject->mData.mRssi = aMetricsValues->mRssiValue;
|
||||
subject->mData.mLinkMargin = aMetricsValues->mLinkMarginValue;
|
||||
|
||||
exit:
|
||||
if (error == kErrorInvalidArgs)
|
||||
{
|
||||
LogWarn("Metrics received are unexpected!");
|
||||
}
|
||||
}
|
||||
|
||||
// This special Match method is used for "iterating over list while removing some items"
|
||||
bool LinkMetricsManager::Subject::Matches(const LinkMetricsManager &aLinkMetricsMgr)
|
||||
{
|
||||
Error error = UpdateState(aLinkMetricsMgr.GetInstance());
|
||||
|
||||
return error == kErrorUnknownNeighbor || error == kErrorNotCapable;
|
||||
}
|
||||
|
||||
Error LinkMetricsManager::Subject::ConfigureEap(Instance &aInstance)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
Neighbor *neighbor = aInstance.Get<NeighborTable>().FindNeighbor(mExtAddress);
|
||||
Ip6::Address destination;
|
||||
LinkMetrics::EnhAckFlags enhAckFlags = LinkMetrics::kEnhAckRegister;
|
||||
LinkMetrics::Metrics metricsFlags;
|
||||
|
||||
VerifyOrExit(neighbor != nullptr, error = kErrorUnknownNeighbor);
|
||||
destination.SetToLinkLocalAddress(neighbor->GetExtAddress());
|
||||
|
||||
metricsFlags.Clear();
|
||||
metricsFlags.mLinkMargin = 1;
|
||||
metricsFlags.mRssi = 1;
|
||||
error =
|
||||
aInstance.Get<LinkMetrics::Initiator>().SendMgmtRequestEnhAckProbing(destination, enhAckFlags, &metricsFlags);
|
||||
|
||||
exit:
|
||||
if (error == kErrorNone)
|
||||
{
|
||||
mState = (mState == SubjectState::kActive) ? SubjectState::kRenewing : SubjectState::kConfiguring;
|
||||
mAttempts++;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
Error LinkMetricsManager::Subject::UnregisterEap(Instance &aInstance)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
Neighbor *neighbor = aInstance.Get<NeighborTable>().FindNeighbor(mExtAddress);
|
||||
Ip6::Address destination;
|
||||
LinkMetrics::EnhAckFlags enhAckFlags = LinkMetrics::kEnhAckClear;
|
||||
|
||||
VerifyOrExit(neighbor != nullptr, error = kErrorUnknownNeighbor);
|
||||
destination.SetToLinkLocalAddress(neighbor->GetExtAddress());
|
||||
|
||||
error = aInstance.Get<LinkMetrics::Initiator>().SendMgmtRequestEnhAckProbing(destination, enhAckFlags, nullptr);
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
Error LinkMetricsManager::Subject::UpdateState(Instance &aInstance)
|
||||
{
|
||||
bool shouldConfigure = false;
|
||||
uint32_t pastTimeMs;
|
||||
Error error = kErrorNone;
|
||||
|
||||
switch (mState)
|
||||
{
|
||||
case kNotConfigured:
|
||||
case kConfiguring:
|
||||
case kRenewing:
|
||||
if (mAttempts >= kConfigureLinkMetricsMaxAttempts)
|
||||
{
|
||||
mState = kNotSupported;
|
||||
}
|
||||
else
|
||||
{
|
||||
shouldConfigure = true;
|
||||
}
|
||||
break;
|
||||
case kActive:
|
||||
pastTimeMs = TimerMilli::GetNow() - mLastUpdateTime;
|
||||
if (pastTimeMs >= kStateUpdateIntervalMilliSec)
|
||||
{
|
||||
shouldConfigure = true;
|
||||
}
|
||||
break;
|
||||
case kNotSupported:
|
||||
ExitNow(error = kErrorNotCapable);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (shouldConfigure)
|
||||
{
|
||||
error = ConfigureEap(aInstance);
|
||||
}
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*
|
||||
*/
|
||||
|
||||
} // namespace Utils
|
||||
} // namespace ot
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright (c) 2023, The OpenThread Authors.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef LINK_METRICS_MANAGER_HPP_
|
||||
#define LINK_METRICS_MANAGER_HPP_
|
||||
|
||||
#include "openthread-core-config.h"
|
||||
|
||||
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
|
||||
|
||||
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE == 0
|
||||
#error \
|
||||
"OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE can only be used when OPENTHREAD_CONFIG_LINK_METRICS_INITIATOR_ENABLE is true"
|
||||
#endif
|
||||
|
||||
#include <openthread/link_metrics.h>
|
||||
|
||||
#include "common/clearable.hpp"
|
||||
#include "common/linked_list.hpp"
|
||||
#include "common/locator.hpp"
|
||||
#include "common/non_copyable.hpp"
|
||||
#include "common/notifier.hpp"
|
||||
#include "common/pool.hpp"
|
||||
#include "common/time.hpp"
|
||||
#include "common/timer.hpp"
|
||||
#include "mac/mac_types.hpp"
|
||||
#include "thread/link_metrics_types.hpp"
|
||||
|
||||
namespace ot {
|
||||
class UnitTester;
|
||||
namespace Utils {
|
||||
|
||||
/**
|
||||
* @addtogroup utils-link-metrics-manager
|
||||
*
|
||||
* @brief
|
||||
* This module includes definitions for Link Metrics Manager.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* Link Metrics Manager feature utilizes the Enhanced-ACK Based
|
||||
* Probing (abbreviated as "EAP" below) to get the Link Metrics
|
||||
* data of neighboring devices. It is a user of the Link Metrics
|
||||
* feature.
|
||||
*
|
||||
* The feature works as follow:
|
||||
* - Start/Stop
|
||||
* The switch `OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ON_BY_DEFAULT`
|
||||
* controls enabling/disabling this feature by default. The feature
|
||||
* will only start to work after it joins a Thread network.
|
||||
*
|
||||
* A CLI interface is provided to enable/disable this feature.
|
||||
*
|
||||
* Once enabled, it will regularly check current neighbors (all
|
||||
* devices in the neighbor table, including 'Child' and 'Router')
|
||||
* and configure the probing with them if haven't done that.
|
||||
* If disabled, it will clear the configuration with its subjects
|
||||
* and the local data.
|
||||
*
|
||||
* - Maintenance
|
||||
* The manager will regularly check the status of each subject. If
|
||||
* it finds that the link metrics data for one subject hasn't been
|
||||
* updated for `kStateUpdateIntervalMilliSec`, it will configure
|
||||
* EAP with the subject again.
|
||||
* The manager may find that some subject (neighbor) no longer
|
||||
* exist when trying to configure EAP. It will remove the stale
|
||||
* subject then.
|
||||
*
|
||||
* - Show data
|
||||
* An OT API is provided to get the link metrics data of any
|
||||
* subject (neighbor) by its extended address. In production, this
|
||||
* data may be fetched by some other means like RPC.
|
||||
*
|
||||
*/
|
||||
|
||||
class LinkMetricsManager : public InstanceLocator, private NonCopyable
|
||||
{
|
||||
friend class ot::Notifier;
|
||||
friend class ot::UnitTester;
|
||||
|
||||
struct LinkMetricsData
|
||||
{
|
||||
uint8_t mLqi; ///< Link Quality Indicator. Value range: [0, 255].
|
||||
int8_t mRssi; ///< Receive Signal Strength Indicator. Value range: [-128, 0].
|
||||
uint8_t mLinkMargin; ///< Link Margin. The relative signal strength is recorded as
|
||||
///< db above the local noise floor. Value range: [0, 130].
|
||||
};
|
||||
|
||||
enum SubjectState : uint8_t
|
||||
{
|
||||
kNotConfigured = 0,
|
||||
kConfiguring,
|
||||
kActive,
|
||||
kRenewing,
|
||||
kNotSupported,
|
||||
};
|
||||
|
||||
struct Subject : LinkedListEntry<Subject>, Clearable<Subject>
|
||||
{
|
||||
Mac::ExtAddress mExtAddress; ///< Use the extended address to identify the neighbor.
|
||||
SubjectState mState; ///< Current State of the Subject
|
||||
uint8_t mAttempts; ///< The count of attempt that has been made to
|
||||
///< configure EAP
|
||||
TimeMilli mLastUpdateTime; ///< The time `mData` was updated last time
|
||||
LinkMetricsData mData;
|
||||
|
||||
Subject *mNext;
|
||||
|
||||
bool Matches(const Mac::ExtAddress &aExtAddress) const { return mExtAddress == aExtAddress; }
|
||||
bool Matches(const LinkMetricsManager &aLinkMetricsMgr);
|
||||
|
||||
Error ConfigureEap(Instance &aInstance);
|
||||
Error UnregisterEap(Instance &aInstance);
|
||||
Error UpdateState(Instance &aInstance);
|
||||
};
|
||||
|
||||
public:
|
||||
/**
|
||||
* Initializes a `LinkMetricsManager` object.
|
||||
*
|
||||
* @param[in] aInstance A reference to the OpenThread instance.
|
||||
*
|
||||
*/
|
||||
explicit LinkMetricsManager(Instance &aInstance);
|
||||
|
||||
/**
|
||||
* Enable/Disable the LinkMetricsManager feature.
|
||||
*
|
||||
* @param[in] aEnable A boolean to indicate enable or disable.
|
||||
*
|
||||
*/
|
||||
void SetEnabled(bool aEnable);
|
||||
|
||||
/**
|
||||
* Get Link Metrics data of subject by the extended address.
|
||||
*
|
||||
* @param[in] aExtAddress A reference to the extended address of the subject.
|
||||
* @param[out] aMetricsValues A reference to the MetricsValues object to place the result.
|
||||
*
|
||||
* @retval kErrorNone Successfully got the metrics value.
|
||||
* @retval kErrorInvalidArgs The arguments are invalid.
|
||||
* @retval kNotFound No neighbor with the given extended address is found.
|
||||
*
|
||||
*/
|
||||
Error GetLinkMetricsValueByExtAddr(const Mac::ExtAddress &aExtAddress, LinkMetrics::MetricsValues &aMetricsValues);
|
||||
|
||||
private:
|
||||
static constexpr uint16_t kTimeBeforeStartMilliSec = 5000;
|
||||
static constexpr uint32_t kStateUpdateIntervalMilliSec = 150000;
|
||||
static constexpr uint8_t kConfigureLinkMetricsMaxAttempts = 3;
|
||||
#if OPENTHREAD_FTD
|
||||
static constexpr uint8_t kMaximumSubjectToTrack = 128;
|
||||
#elif OPENTHREAD_MTD
|
||||
static constexpr uint8_t kMaximumSubjectToTrack = 1;
|
||||
#endif
|
||||
|
||||
void Start(void);
|
||||
void Stop(void);
|
||||
void Update(void);
|
||||
void UpdateSubjects(void);
|
||||
void UpdateLinkMetricsStates(void);
|
||||
void UnregisterAllSubjects(void);
|
||||
void ReleaseAllSubjects(void);
|
||||
|
||||
void HandleNotifierEvents(Events aEvents);
|
||||
void HandleTimer(void);
|
||||
static void HandleMgmtResponse(const otIp6Address *aAddress, otLinkMetricsStatus aStatus, void *aContext);
|
||||
void HandleMgmtResponse(const otIp6Address *aAddress, otLinkMetricsStatus aStatus);
|
||||
static void HandleEnhAckIe(otShortAddress aShortAddress,
|
||||
const otExtAddress *aExtAddress,
|
||||
const otLinkMetricsValues *aMetricsValues,
|
||||
void *aContext);
|
||||
void HandleEnhAckIe(otShortAddress aShortAddress,
|
||||
const otExtAddress *aExtAddress,
|
||||
const otLinkMetricsValues *aMetricsValues);
|
||||
|
||||
using LinkMetricsMgrTimer = TimerMilliIn<LinkMetricsManager, &LinkMetricsManager::HandleTimer>;
|
||||
|
||||
Pool<Subject, kMaximumSubjectToTrack> mPool;
|
||||
LinkedList<Subject> mSubjectList;
|
||||
LinkMetricsMgrTimer mTimer;
|
||||
bool mEnabled;
|
||||
};
|
||||
|
||||
/**
|
||||
* @}
|
||||
*
|
||||
*/
|
||||
|
||||
} // namespace Utils
|
||||
} // namespace ot
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
|
||||
|
||||
#endif // LINK_METRICS_MANAGER_HPP_
|
||||
Executable
+60
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/expect -f
|
||||
#
|
||||
# Copyright (c) 2023, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
|
||||
source "tests/scripts/expect/_common.exp"
|
||||
source "tests/scripts/expect/_multinode.exp"
|
||||
|
||||
setup_two_nodes
|
||||
|
||||
switch_node 1
|
||||
send "linkmetricsmgr enable\n"
|
||||
expect_line "Done"
|
||||
|
||||
sleep 10
|
||||
|
||||
switch_node 2
|
||||
set addr [get_ipaddr mleid]
|
||||
|
||||
switch_node 1
|
||||
send "ping $addr\n"
|
||||
expect "16 bytes from $addr: icmp_seq=1"
|
||||
send "linkmetricsmgr show\n"
|
||||
expect -re {ExtAddr:([0-9a-f]){16}, LinkMargin:\d+, Rssi:\-?\d+}
|
||||
expect "Done"
|
||||
|
||||
sleep 5
|
||||
|
||||
send "linkmetricsmgr disable\n"
|
||||
expect_line "Done"
|
||||
send "linkmetricsmgr show\n"
|
||||
expect_line "Done"
|
||||
send "linkmetricsmgr xxx\n"
|
||||
expect_line "InvalidCommand"
|
||||
|
||||
dispose_all
|
||||
@@ -3257,6 +3257,12 @@ class NodeImpl:
|
||||
self.send_command(cmd)
|
||||
self._expect_done()
|
||||
|
||||
def link_metrics_mgr_set_enabled(self, enable: bool):
|
||||
op_str = "enable" if enable else "disable"
|
||||
cmd = f'linkmetricsmgr {op_str}'
|
||||
self.send_command(cmd)
|
||||
self._expect_done()
|
||||
|
||||
def send_address_notification(self, dst: str, target: str, mliid: str):
|
||||
cmd = f'fake /a/an {dst} {target} {mliid}'
|
||||
self.send_command(cmd)
|
||||
|
||||
@@ -0,0 +1,271 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2023, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
|
||||
import unittest
|
||||
|
||||
from config import ADDRESS_TYPE
|
||||
from mle import LinkMetricsSubTlvType, TlvType
|
||||
from pktverify import consts
|
||||
from pktverify.null_field import nullField
|
||||
from pktverify.packet_verifier import PacketVerifier
|
||||
|
||||
import config
|
||||
import thread_cert
|
||||
|
||||
LEADER = 1
|
||||
ROUTER = 2
|
||||
CHILD = 3
|
||||
"""
|
||||
Acronyms:
|
||||
- EAP - Enhanced-ACK Based Probing
|
||||
|
||||
Test Process:
|
||||
1. Initiate a leader and a router at the beginning.
|
||||
2. Enable Link Metrics Manager on leader.
|
||||
3. Wait a few seconds.
|
||||
- At this moment, leader should have configured EAP successfully at router.
|
||||
4. Instruct the leader ping the router.
|
||||
- Enhanced ACK sent by router should have Thread IE containing Link Metrics data.
|
||||
5. Add a child into the network. The child should attach to the leader.
|
||||
6. Wait 150 seconds.
|
||||
- At this moment, leader should have configured EAP successfully at the child.
|
||||
7. Instruct both the router and the child ping the leader.
|
||||
- Enhanced ACK sent by router and child should have Thread IE containing Link Metrics data.
|
||||
8. Shutdown the child.
|
||||
9. Wait 300 seconds.
|
||||
- The leader should not send a Link Management Request to the child.
|
||||
- The leader should have sent a Link Management Request to the router and get a response.
|
||||
10. Disable Link Metrics Manager on leader.
|
||||
- The leader shonld send a Link Management Request to the router to unregister EAP.
|
||||
"""
|
||||
|
||||
|
||||
class LowPower_test_LinkMetricsManager(thread_cert.TestCase):
|
||||
TOPOLOGY = {
|
||||
LEADER: {
|
||||
'version': '1.2',
|
||||
'name': 'LEADER',
|
||||
'mode': 'rdn',
|
||||
'allowlist': [ROUTER, CHILD],
|
||||
},
|
||||
ROUTER: {
|
||||
'version': '1.2',
|
||||
'name': 'ROUTER',
|
||||
'mode': 'rdn',
|
||||
'allowlist': [LEADER],
|
||||
},
|
||||
CHILD: {
|
||||
'version': '1.2',
|
||||
'name': 'CHILD',
|
||||
'mode': 'r',
|
||||
'allowlist': [LEADER],
|
||||
}
|
||||
}
|
||||
"""All nodes are created with default configurations"""
|
||||
|
||||
def test(self):
|
||||
self.nodes[LEADER].start()
|
||||
self.simulator.go(config.LEADER_STARTUP_DELAY)
|
||||
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
|
||||
|
||||
self.nodes[ROUTER].start()
|
||||
self.simulator.go(config.ROUTER_STARTUP_DELAY)
|
||||
self.assertEqual(self.nodes[ROUTER].get_state(), 'router')
|
||||
|
||||
leader_addr = self.nodes[LEADER].get_ip6_address(ADDRESS_TYPE.LINK_LOCAL)
|
||||
router_addr = self.nodes[ROUTER].get_ip6_address(ADDRESS_TYPE.LINK_LOCAL)
|
||||
|
||||
# Step 2 - Enable Link Metrics Manager on leader.
|
||||
self.nodes[LEADER].link_metrics_mgr_set_enabled(True)
|
||||
|
||||
self.simulator.go(10)
|
||||
|
||||
# Step 4 - Instruct the leader ping the router.
|
||||
self.nodes[LEADER].ping(router_addr)
|
||||
|
||||
# Step 5 - Add a child into the network.
|
||||
self.nodes[CHILD].start()
|
||||
self.simulator.go(10)
|
||||
self.assertEqual(self.nodes[CHILD].get_state(), 'child')
|
||||
child_addr = self.nodes[CHILD].get_ip6_address(ADDRESS_TYPE.LINK_LOCAL)
|
||||
|
||||
# Step 6 - Wait the leader to configure EAP at the child.
|
||||
self.simulator.go(150)
|
||||
|
||||
# Step 7 - Instruct both the router and the child ping the leader.
|
||||
self.nodes[LEADER].ping(router_addr)
|
||||
self.nodes[LEADER].ping(child_addr)
|
||||
|
||||
# Step 8 - Shutdown the child.
|
||||
self.nodes[CHILD].stop()
|
||||
|
||||
# Step 9 - Wait the leader to refresh EAP.
|
||||
self.simulator.go(300)
|
||||
|
||||
# Step 10 - Disable Link Metrics Manager on leader.
|
||||
self.nodes[LEADER].link_metrics_mgr_set_enabled(False)
|
||||
|
||||
def verify(self, pv):
|
||||
pkts = pv.pkts
|
||||
pv.summary.show()
|
||||
LEADER = pv.vars['LEADER']
|
||||
ROUTER = pv.vars['ROUTER']
|
||||
CHILD = pv.vars['CHILD']
|
||||
|
||||
# Step 3 - Leader enables IEEE 802.15.4-2015 Enhanced ACK based Probing by sending a Link Metrics Management
|
||||
# Request to the Router
|
||||
# MLE Link Metrics Management TLV Payload:
|
||||
# - Enhanced ACK Configuration Sub-TLV
|
||||
# -- Enh-ACK Flags = 1 (register a configuration)
|
||||
# -- Concatenation of Link Metric Type ID Flags = 0x00:
|
||||
# --- Item1: (0)(0)(001)(010) = 0x0a
|
||||
# ---- E = 0
|
||||
# ---- L = 0
|
||||
# ---- Type/Average Enum = 1 (Exponential Moving Avg)
|
||||
# ---- Metrics Enum = 2 (Link Margin)
|
||||
pkts.filter_wpan_src64(LEADER) \
|
||||
.filter_wpan_dst64(ROUTER) \
|
||||
.filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \
|
||||
.filter(lambda p: consts.LM_ENHANCED_ACK_CONFIGURATION_SUB_TLV in p.mle.tlv.link_sub_tlv) \
|
||||
.filter(lambda p: p.mle.tlv.link_enh_ack_flags == consts.LINK_METRICS_ENH_ACK_PROBING_REGISTER) \
|
||||
.filter(lambda p: p.mle.tlv.link_requested_type_id_flags == '0a0b') \
|
||||
.must_next()
|
||||
|
||||
# Step 3 - The Router MUST send a Link Metrics Management Response to Leader containing the following TLVs:
|
||||
# - MLE LInk Metrics Management TLV
|
||||
# -- Link Metrics Status Sub-TLV = 0 (Success)
|
||||
pkts.filter_wpan_src64(ROUTER) \
|
||||
.filter_wpan_dst64(LEADER) \
|
||||
.filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_RESPONSE) \
|
||||
.filter(lambda p: p.mle.tlv.link_status_sub_tlv == consts.LINK_METRICS_STATUS_SUCCESS) \
|
||||
.must_next()
|
||||
|
||||
# Step 4 - The Enhanced Ack sent by both Router should contain Thread specific IE
|
||||
pkt = pkts.filter_wpan_src64(LEADER) \
|
||||
.filter_wpan_dst64(ROUTER) \
|
||||
.filter_ping_request() \
|
||||
.must_next()
|
||||
ack_seq_no = pkt.wpan.seq_no
|
||||
pkts.filter_wpan_ack() \
|
||||
.filter_wpan_seq(ack_seq_no) \
|
||||
.filter(lambda p: p.wpan.payload_ie.vendor.oui == consts.THREAD_IEEE_802154_COMPANY_ID) \
|
||||
.must_next()
|
||||
|
||||
# Step 6 - Leader enables IEEE 802.15.4-2015 Enhanced ACK based Probing by sending a Link Metrics Management
|
||||
# Request to the Child
|
||||
# MLE Link Metrics Management TLV Payload:
|
||||
# - Enhanced ACK Configuration Sub-TLV
|
||||
# -- Enh-ACK Flags = 1 (register a configuration)
|
||||
# -- Concatenation of Link Metric Type ID Flags = 0x00:
|
||||
# --- Item1: (0)(0)(001)(010) = 0x0a
|
||||
# ---- E = 0
|
||||
# ---- L = 0
|
||||
# ---- Type/Average Enum = 1 (Exponential Moving Avg)
|
||||
# ---- Metrics Enum = 2 (Link Margin)
|
||||
pkts.filter_wpan_src64(LEADER) \
|
||||
.filter_wpan_dst64(CHILD) \
|
||||
.filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \
|
||||
.filter(lambda p: consts.LM_ENHANCED_ACK_CONFIGURATION_SUB_TLV in p.mle.tlv.link_sub_tlv) \
|
||||
.filter(lambda p: p.mle.tlv.link_enh_ack_flags == consts.LINK_METRICS_ENH_ACK_PROBING_REGISTER) \
|
||||
.filter(lambda p: p.mle.tlv.link_requested_type_id_flags == '0a0b') \
|
||||
.must_next()
|
||||
|
||||
# Step 6 - The Child MUST send a Link Metrics Management Response to Leader containing the following TLVs:
|
||||
# - MLE LInk Metrics Management TLV
|
||||
# -- Link Metrics Status Sub-TLV = 0 (Success)
|
||||
pkts.filter_wpan_src64(CHILD) \
|
||||
.filter_wpan_dst64(LEADER) \
|
||||
.filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_RESPONSE) \
|
||||
.filter(lambda p: p.mle.tlv.link_status_sub_tlv == consts.LINK_METRICS_STATUS_SUCCESS) \
|
||||
.must_next()
|
||||
|
||||
# Step 7 - The Enhanced Ack sent by both Router and Child should contain Thread specific IE
|
||||
pkt1 = pkts.filter_wpan_src64(LEADER) \
|
||||
.filter_wpan_dst64(ROUTER) \
|
||||
.filter_ping_request() \
|
||||
.must_next()
|
||||
ack_seq_no = pkt1.wpan.seq_no
|
||||
pkts.filter_wpan_ack() \
|
||||
.filter_wpan_seq(ack_seq_no) \
|
||||
.filter(lambda p: p.wpan.payload_ie.vendor.oui == consts.THREAD_IEEE_802154_COMPANY_ID) \
|
||||
.must_next()
|
||||
|
||||
pkt2 = pkts.filter_wpan_src64(LEADER) \
|
||||
.filter_wpan_dst64(CHILD) \
|
||||
.filter_ping_request() \
|
||||
.must_next()
|
||||
ack_seq_no = pkt2.wpan.seq_no
|
||||
pkts.filter_wpan_ack() \
|
||||
.filter_wpan_seq(ack_seq_no) \
|
||||
.filter(lambda p: p.wpan.payload_ie.vendor.oui == consts.THREAD_IEEE_802154_COMPANY_ID) \
|
||||
.must_next()
|
||||
|
||||
# Step 9 - Leader refreshes IEEE 802.15.4-2015 Enhanced ACK based Probing by sending a Link Metrics Management
|
||||
# Request to the Router
|
||||
# MLE Link Metrics Management TLV Payload:
|
||||
# - Enhanced ACK Configuration Sub-TLV
|
||||
# -- Enh-ACK Flags = 1 (register a configuration)
|
||||
# -- Concatenation of Link Metric Type ID Flags = 0x00:
|
||||
# --- Item1: (0)(0)(001)(010) = 0x0a
|
||||
# ---- E = 0
|
||||
# ---- L = 0
|
||||
# ---- Type/Average Enum = 1 (Exponential Moving Avg)
|
||||
# ---- Metrics Enum = 2 (Link Margin)
|
||||
pkts.filter_wpan_src64(LEADER) \
|
||||
.filter_wpan_dst64(ROUTER) \
|
||||
.filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \
|
||||
.filter(lambda p: consts.LM_ENHANCED_ACK_CONFIGURATION_SUB_TLV in p.mle.tlv.link_sub_tlv) \
|
||||
.filter(lambda p: p.mle.tlv.link_enh_ack_flags == consts.LINK_METRICS_ENH_ACK_PROBING_REGISTER) \
|
||||
.filter(lambda p: p.mle.tlv.link_requested_type_id_flags == '0a0b') \
|
||||
.must_next()
|
||||
|
||||
# Step 9 - The Router MUST send a Link Metrics Management Response to Leader containing the following TLVs:
|
||||
# - MLE LInk Metrics Management TLV
|
||||
# -- Link Metrics Status Sub-TLV = 0 (Success)
|
||||
pkts.filter_wpan_src64(ROUTER) \
|
||||
.filter_wpan_dst64(LEADER) \
|
||||
.filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_RESPONSE) \
|
||||
.filter(lambda p: p.mle.tlv.link_status_sub_tlv == consts.LINK_METRICS_STATUS_SUCCESS) \
|
||||
.must_next()
|
||||
|
||||
# Step 10 - Leader unregisters IEEE 802.15.4-2015 Enhanced ACK based Probing by sending a Link Metrics Management
|
||||
# Request to the Router
|
||||
# MLE Link Metrics Management TLV Payload:
|
||||
# - Enhanced ACK Configuration Sub-TLV
|
||||
# -- Enh-ACK Flags = 0 (unregister a configuration)
|
||||
pkts.filter_wpan_src64(LEADER) \
|
||||
.filter_wpan_dst64(ROUTER) \
|
||||
.filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \
|
||||
.filter(lambda p: consts.LM_ENHANCED_ACK_CONFIGURATION_SUB_TLV in p.mle.tlv.link_sub_tlv) \
|
||||
.filter(lambda p: p.mle.tlv.link_enh_ack_flags == consts.LINK_METRICS_ENH_ACK_PROBING_CLEAR) \
|
||||
.must_next()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -538,6 +538,27 @@ target_link_libraries(ot-test-ip-address
|
||||
|
||||
add_test(NAME ot-test-ip-address COMMAND ot-test-ip-address)
|
||||
|
||||
add_executable(ot-test-link-metrics-manager
|
||||
test_link_metrics_manager.cpp
|
||||
)
|
||||
|
||||
target_include_directories(ot-test-link-metrics-manager
|
||||
PRIVATE
|
||||
${COMMON_INCLUDES}
|
||||
)
|
||||
|
||||
target_compile_options(ot-test-link-metrics-manager
|
||||
PRIVATE
|
||||
${COMMON_COMPILE_OPTIONS}
|
||||
)
|
||||
|
||||
target_link_libraries(ot-test-link-metrics-manager
|
||||
PRIVATE
|
||||
${COMMON_LIBS}
|
||||
)
|
||||
|
||||
add_test(NAME ot-test-link-metrics-manager COMMAND ot-test-link-metrics-manager)
|
||||
|
||||
add_executable(ot-test-link-quality
|
||||
test_link_quality.cpp
|
||||
)
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (c) 2023, The OpenThread Authors.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "test_platform.h"
|
||||
|
||||
#include <openthread/config.h>
|
||||
|
||||
#include "test_util.h"
|
||||
#include "common/array.hpp"
|
||||
#include "common/code_utils.hpp"
|
||||
#include "common/instance.hpp"
|
||||
#include "mac/mac_types.hpp"
|
||||
#include "thread/child_table.hpp"
|
||||
|
||||
namespace ot {
|
||||
|
||||
static ot::Instance *sInstance;
|
||||
|
||||
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
static uint32_t sNow = 10000;
|
||||
|
||||
extern "C" {
|
||||
|
||||
uint32_t otPlatAlarmMilliGetNow(void) { return sNow; }
|
||||
|
||||
} // extern "C"
|
||||
|
||||
struct TestChild
|
||||
{
|
||||
Child::State mState;
|
||||
otExtAddress mExtAddress;
|
||||
uint16_t mThreadVersion;
|
||||
};
|
||||
|
||||
class UnitTester
|
||||
{
|
||||
public:
|
||||
static void TestLinkMetricsManager(void);
|
||||
|
||||
private:
|
||||
static void SetTestLinkMetricsValues(otLinkMetricsValues &aLinkMetricsValues,
|
||||
uint8_t aLinkMarginValue,
|
||||
int8_t aRssiValue);
|
||||
|
||||
static const TestChild mTestChildList[];
|
||||
};
|
||||
|
||||
const TestChild UnitTester::mTestChildList[] = {
|
||||
{
|
||||
Child::kStateValid,
|
||||
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x16}},
|
||||
ot::kThreadVersion1p2,
|
||||
},
|
||||
{
|
||||
Child::kStateValid,
|
||||
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x17}},
|
||||
ot::kThreadVersion1p2,
|
||||
},
|
||||
{
|
||||
Child::kStateParentRequest,
|
||||
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x18}},
|
||||
ot::kThreadVersion1p2,
|
||||
},
|
||||
};
|
||||
|
||||
void UnitTester::SetTestLinkMetricsValues(otLinkMetricsValues &aLinkMetricsValues,
|
||||
uint8_t aLinkMarginValue,
|
||||
int8_t aRssiValue)
|
||||
{
|
||||
aLinkMetricsValues.mMetrics.mLinkMargin = true;
|
||||
aLinkMetricsValues.mMetrics.mRssi = true;
|
||||
|
||||
aLinkMetricsValues.mLinkMarginValue = aLinkMarginValue;
|
||||
aLinkMetricsValues.mRssiValue = aRssiValue;
|
||||
}
|
||||
|
||||
void UnitTester::TestLinkMetricsManager(void)
|
||||
{
|
||||
ChildTable *childTable;
|
||||
const uint16_t testListLength = GetArrayLength(mTestChildList);
|
||||
Ip6::Address linkLocalAddr;
|
||||
LinkMetricsManager::Subject *subject1 = nullptr;
|
||||
LinkMetricsManager::Subject *subject2 = nullptr;
|
||||
LinkMetricsManager *linkMetricsMgr = nullptr;
|
||||
otLinkMetricsValues linkMetricsValues;
|
||||
otShortAddress anyShortAddress = 0x1234;
|
||||
|
||||
sInstance = testInitInstance();
|
||||
VerifyOrQuit(sInstance != nullptr);
|
||||
|
||||
childTable = &sInstance->Get<ChildTable>();
|
||||
|
||||
sInstance->Get<Mle::Mle>().SetRole(Mle::kRoleRouter);
|
||||
// Add the child entries from test list
|
||||
for (uint16_t i = 0; i < testListLength; i++)
|
||||
{
|
||||
Child *child;
|
||||
|
||||
child = childTable->GetNewChild();
|
||||
VerifyOrQuit(child != nullptr, "GetNewChild() failed");
|
||||
|
||||
child->SetState(mTestChildList[i].mState);
|
||||
child->SetExtAddress((AsCoreType(&mTestChildList[i].mExtAddress)));
|
||||
child->SetVersion(mTestChildList[i].mThreadVersion);
|
||||
}
|
||||
|
||||
linkMetricsMgr = &sInstance->Get<LinkMetricsManager>();
|
||||
linkMetricsMgr->SetEnabled(true);
|
||||
// Update the subjects for the first time.
|
||||
linkMetricsMgr->UpdateSubjects();
|
||||
|
||||
// Expect there are 2 subjects in NotConfigured state.
|
||||
{
|
||||
uint16_t index = 0;
|
||||
for (LinkMetricsManager::Subject &subject : linkMetricsMgr->mSubjectList)
|
||||
{
|
||||
VerifyOrQuit(subject.mExtAddress == AsCoreType(&mTestChildList[1 - index].mExtAddress));
|
||||
VerifyOrQuit(subject.mState == LinkMetricsManager::SubjectState::kNotConfigured);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
subject1 = linkMetricsMgr->mSubjectList.FindMatching(AsCoreType(&mTestChildList[0].mExtAddress));
|
||||
subject2 = linkMetricsMgr->mSubjectList.FindMatching(AsCoreType(&mTestChildList[1].mExtAddress));
|
||||
VerifyOrQuit(subject1 != nullptr);
|
||||
VerifyOrQuit(subject2 != nullptr);
|
||||
|
||||
// Update the state of the subjects
|
||||
linkMetricsMgr->UpdateLinkMetricsStates();
|
||||
// Expect the 2 subjects are in Configuring state.
|
||||
for (LinkMetricsManager::Subject &subject : linkMetricsMgr->mSubjectList)
|
||||
{
|
||||
VerifyOrQuit(subject.mState == LinkMetricsManager::SubjectState::kConfiguring);
|
||||
}
|
||||
|
||||
// subject1 received a response with a success status code
|
||||
linkLocalAddr.SetToLinkLocalAddress(AsCoreType(&mTestChildList[0].mExtAddress));
|
||||
linkMetricsMgr->HandleMgmtResponse(&linkLocalAddr, MapEnum(LinkMetrics::Status::kStatusSuccess));
|
||||
VerifyOrQuit(subject1->mState == LinkMetricsManager::SubjectState::kActive);
|
||||
|
||||
// subject1 received Enhanced ACK IE and updated the link metrics data
|
||||
{
|
||||
constexpr uint8_t kTestLinkMargin = 100;
|
||||
constexpr int8_t kTestRssi = -30;
|
||||
SetTestLinkMetricsValues(linkMetricsValues, kTestLinkMargin, kTestRssi);
|
||||
linkMetricsMgr->HandleEnhAckIe(anyShortAddress, &mTestChildList[0].mExtAddress, &linkMetricsValues);
|
||||
VerifyOrQuit(subject1->mData.mLinkMargin == kTestLinkMargin);
|
||||
VerifyOrQuit(subject1->mData.mRssi == kTestRssi);
|
||||
}
|
||||
|
||||
// subject2 didn't receive any responses after a few attempts and marked as UnSupported.
|
||||
for (uint8_t i = 0; i < LinkMetricsManager::kConfigureLinkMetricsMaxAttempts; i++)
|
||||
{
|
||||
linkMetricsMgr->Update();
|
||||
}
|
||||
VerifyOrQuit(subject2->mState == LinkMetricsManager::SubjectState::kNotSupported);
|
||||
|
||||
// neighbor2 is removed and subject2 should also be removed.
|
||||
Child *child2 = childTable->FindChild(subject2->mExtAddress, Child::kInStateValid);
|
||||
child2->SetState(Child::kStateInvalid);
|
||||
linkMetricsMgr->Update();
|
||||
subject2 = linkMetricsMgr->mSubjectList.FindMatching(AsCoreType(&mTestChildList[1].mExtAddress));
|
||||
VerifyOrQuit(subject2 == nullptr);
|
||||
|
||||
// subject1 still existed
|
||||
subject1 = linkMetricsMgr->mSubjectList.FindMatching(AsCoreType(&mTestChildList[0].mExtAddress));
|
||||
VerifyOrQuit(subject1 != nullptr);
|
||||
|
||||
// Make subject1 renew
|
||||
sNow += LinkMetricsManager::kStateUpdateIntervalMilliSec + 1;
|
||||
linkMetricsMgr->Update();
|
||||
VerifyOrQuit(subject1->mState == LinkMetricsManager::SubjectState::kRenewing);
|
||||
|
||||
// subject1 got Enh-ACK IE when in kRenewing state
|
||||
sNow += 1;
|
||||
linkMetricsMgr->HandleEnhAckIe(anyShortAddress, &mTestChildList[0].mExtAddress, &linkMetricsValues);
|
||||
VerifyOrQuit(subject1->mLastUpdateTime == TimeMilli(sNow));
|
||||
|
||||
// subject1 got response and become active again
|
||||
linkMetricsMgr->HandleMgmtResponse(&linkLocalAddr, MapEnum(LinkMetrics::Status::kStatusSuccess));
|
||||
VerifyOrQuit(subject1->mState == LinkMetricsManager::SubjectState::kActive);
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
|
||||
|
||||
} // namespace ot
|
||||
|
||||
int main(void)
|
||||
{
|
||||
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
|
||||
ot::UnitTester::TestLinkMetricsManager();
|
||||
#endif
|
||||
printf("\nAll tests passed.\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user