[low-power] implement link metrics - single probe (#5481)

This commit is the first part in link metrics. It implements single
probe function.

An overview of major changes:
- Add link metrics apis which allow apps to send a single probe and
  set report callback.
- Add cli commands for calling link metrics api and related README
  doc.
- Add a new module link_metrics to implement the query process and
  handle the report.
- Add related tlv types.
- Add simple script test to run single probe process.
This commit is contained in:
Li Cao
2020-09-25 09:50:09 -07:00
committed by GitHub
parent 88c976e3f7
commit dc94758e84
36 changed files with 1591 additions and 9 deletions
+2
View File
@@ -164,6 +164,7 @@ LOCAL_SRC_FILES := \
src/core/api/jam_detection_api.cpp \
src/core/api/joiner_api.cpp \
src/core/api/link_api.cpp \
src/core/api/link_metrics_api.cpp \
src/core/api/link_raw_api.cpp \
src/core/api/logging_api.cpp \
src/core/api/message_api.cpp \
@@ -255,6 +256,7 @@ LOCAL_SRC_FILES := \
src/core/thread/energy_scan_server.cpp \
src/core/thread/indirect_sender.cpp \
src/core/thread/key_manager.cpp \
src/core/thread/link_metrics.cpp \
src/core/thread/link_quality.cpp \
src/core/thread/lowpan.cpp \
src/core/thread/mesh_forwarder.cpp \
+5
View File
@@ -177,6 +177,11 @@ if(OT_LINK_RAW)
target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_LINK_RAW_ENABLE=1")
endif()
option(OT_LINK_METRICS "enable link metrics")
if (OT_LINK_METRICS)
target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE=1")
endif()
option(OT_LOG_LEVEL_DYNAMIC "enable dynamic log level control")
if(OT_LOG_LEVEL_DYNAMIC)
target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE=1")
+3
View File
@@ -150,6 +150,9 @@ if (openthread_enable_core_config_args) {
# Enable legacy network support
openthread_config_legacy_enable = false
# Enable link metrics
openthread_config_link_metrics_enable = false
# Enable link raw service
openthread_config_link_raw_enable = false
+4
View File
@@ -202,6 +202,10 @@ ifeq ($(LINK_RAW),1)
COMMONCFLAGS += -DOPENTHREAD_CONFIG_LINK_RAW_ENABLE=1
endif
ifeq ($(LINK_METRICS),1)
COMMONCFLAGS += -DOPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE=1
endif
ifneq ($(LOG_OUTPUT),)
COMMONCFLAGS += -DOPENTHREAD_CONFIG_LOG_OUTPUT=OPENTHREAD_CONFIG_LOG_OUTPUT_$(LOG_OUTPUT)
endif
+1
View File
@@ -60,6 +60,7 @@ openthread_headers = \
openthread/jam_detection.h \
openthread/joiner.h \
openthread/link.h \
openthread/link_metrics.h \
openthread/link_raw.h \
openthread/logging.h \
openthread/message.h \
+127
View File
@@ -0,0 +1,127 @@
/*
* Copyright (c) 2020, 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
* @brief
* This file defines the OpenThread Link Metrics API.
*/
#ifndef LINK_METRICS_H_
#define LINK_METRICS_H_
#include "openthread-core-config.h"
#include <openthread/ip6.h>
#include <openthread/message.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @addtogroup linkmetrics Link Metrics
*
* @brief
* This module includes functions that control Link Metrics protocol.
*
* @{
*
*/
/*
* This structure represents what metrics are specified to query.
*
*/
typedef struct otLinkMetrics
{
bool mPduCount : 1;
bool mLqi : 1;
bool mLinkMargin : 1;
bool mRssi : 1;
} otLinkMetrics;
/*
* This structure represents the result (value) for a Link Metrics query.
*
*/
typedef struct otLinkMetricsValues
{
otLinkMetrics mMetrics; ///< Specifies which metrics values are present/included.
uint32_t mPduCountValue; ///< The value of Pdu Count.
uint8_t mLqiValue; ///< The value LQI.
uint8_t mLinkMarginValue; ///< The value of Link Margin.
int8_t mRssiValue; ///< The value of Rssi.
} otLinkMetricsValues;
/**
* This function pointer is called when a Link Metrics report is received.
*
* @param[in] aSource A pointer to the source address.
* @param[in] aMetricsValues A pointer to the Link Metrics values (the query result).
* @param[in] aContext A pointer to application-specific context.
*
*/
typedef void (*otLinkMetricsReportCallback)(const otIp6Address * aSource,
const otLinkMetricsValues *aMetricsValues,
void * aContext);
/**
* This function sends an MLE Data Request to query Link Metrics.
*
* It could be either Single Probe or Forward Tracking Series.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aDestination A pointer to the destination address.
* @param[in] aSeriesId The Series ID to query about, 0 for Single Probe.
* @param[in] aLinkMetricsFlags A pointer to flags specifying what metrics to query.
* @param[in] aCallback A pointer to a function that is called when Link Metrics report is received.
* @param[in] aCallbackContext A pointer to application-specific context.
*
* @retval OT_ERROR_NONE Successfully sent a Link Metrics query message.
* @retval OT_ERROR_NO_BUFS Insufficient buffers to generate the MLE Data Request message.
*
*/
otError otLinkMetricsQuery(otInstance * aInstance,
const otIp6Address * aDestination,
uint8_t aSeriesId,
const otLinkMetrics * aLinkMetricsFlags,
otLinkMetricsReportCallback aCallback,
void * aCallbackContext);
/**
* @}
*
*/
#ifdef __cplusplus
} // extern "C"
#endif
#endif // LINK_METRICS_H_
+1
View File
@@ -67,6 +67,7 @@ build_simulation()
if [[ ${version} == "1.2" ]]; then
options+=("-DOT_CSL_RECEIVER=ON")
options+=("-DOT_LINK_METRICS=ON")
fi
if [[ ${ot_extra_options[*]+x} ]]; then
+22
View File
@@ -57,6 +57,7 @@ Done
- [leaderdata](#leaderdata)
- [leaderpartitionid](#leaderpartitionid)
- [leaderweight](#leaderweight)
- [linkmetrics](#linkmetrics-query-ipaddr-single-pqmr)
- [linkquality](#linkquality-extaddr)
- [log](#log-filename-filename)
- [mac](#mac-retries-direct)
@@ -1066,6 +1067,27 @@ Set the Thread Leader Weight.
Done
```
### linkmetrics query \<ipaddr\> single [pqmr]
Perform a Link Metrics query (Single Probe).
- ipaddr: Peer address.
- pqmr: This specifies what metrics to query.
- p: Layer 2 Number of PDUs received.
- q: Layer 2 LQI.
- m: Link Margin.
- r: RSSI.
```bash
> linkmetrics query fe80:0:0:0:3092:f334:1455:1ad2 single qmr
Done
> Received Link Metrics Report from: fe80:0:0:0:3092:f334:1455:1ad2
- LQI: 76 (Exponential Moving Average)
- Margin: 82 (dB) (Exponential Moving Average)
- RSSI: -18 (dBm) (Exponential Moving Average)
```
### linkquality \<extaddr\>
Get the link quality on the link to a given extended address.
+106
View File
@@ -75,6 +75,9 @@
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
#include <openthread/backbone_router_ftd.h>
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
#include <openthread/link_metrics.h>
#endif
#endif
#include "cli_dataset.hpp"
@@ -1899,6 +1902,109 @@ exit:
}
#endif // OPENTHREAD_FTD
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
void Interpreter::HandleLinkMetricsReport(const otIp6Address * aAddress,
const otLinkMetricsValues *aMetricsValues,
void * aContext)
{
static_cast<Interpreter *>(aContext)->HandleLinkMetricsReport(aAddress, aMetricsValues);
}
void Interpreter::HandleLinkMetricsReport(const otIp6Address *aAddress, const otLinkMetricsValues *aMetricsValues)
{
const char kLinkMetricsTypeCount[] = "(Count/Summation)";
const char kLinkMetricsTypeAverage[] = "(Exponential Moving Average)";
OutputFormat("Received Link Metrics Report from: ");
OutputIp6Address(*aAddress);
OutputLine("");
if (aMetricsValues->mMetrics.mPduCount)
{
OutputLine(" - PDU Counter: %d %s", aMetricsValues->mPduCountValue, kLinkMetricsTypeCount);
}
if (aMetricsValues->mMetrics.mLqi)
{
OutputLine(" - LQI: %d %s", aMetricsValues->mLqiValue, kLinkMetricsTypeAverage);
}
if (aMetricsValues->mMetrics.mLinkMargin)
{
OutputLine(" - Margin: %d (dB) %s", aMetricsValues->mLinkMarginValue, kLinkMetricsTypeAverage);
}
if (aMetricsValues->mMetrics.mRssi)
{
OutputLine(" - RSSI: %d (dBm) %s", aMetricsValues->mRssiValue, kLinkMetricsTypeAverage);
}
}
otError Interpreter::ProcessLinkMetrics(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_INVALID_COMMAND;
VerifyOrExit(aArgsLength >= 1, OT_NOOP);
if (strcmp(aArgs[0], "query") == 0)
{
error = ProcessLinkMetricsQuery(aArgsLength - 1, aArgs + 1);
}
exit:
return error;
}
otError Interpreter::ProcessLinkMetricsQuery(uint8_t aArgsLength, char *aArgs[])
{
otError error = OT_ERROR_INVALID_ARGS;
otIp6Address address;
otLinkMetrics linkMetrics;
long seriesId = 0;
VerifyOrExit(aArgsLength >= 2, OT_NOOP);
SuccessOrExit(error = otIp6AddressFromString(aArgs[0], &address));
memset(&linkMetrics, 0, sizeof(otLinkMetrics));
if (strcmp(aArgs[1], "single") == 0)
{
VerifyOrExit(aArgsLength == 3, OT_NOOP);
for (char *arg = aArgs[2]; *arg != '\0'; arg++)
{
switch (*arg)
{
case 'p':
linkMetrics.mPduCount = 1;
break;
case 'q':
linkMetrics.mLqi = 1;
break;
case 'm':
linkMetrics.mLinkMargin = 1;
break;
case 'r':
linkMetrics.mRssi = 1;
break;
default:
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
}
error = otLinkMetricsQuery(mInstance, &address, static_cast<uint8_t>(seriesId), &linkMetrics,
&Interpreter::HandleLinkMetricsReport, this);
}
exit:
return error;
}
#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
#if OPENTHREAD_FTD
otError Interpreter::ProcessPskc(uint8_t aArgsLength, char *aArgs[])
{
+15
View File
@@ -370,6 +370,10 @@ private:
otError ProcessLeaderWeight(uint8_t aArgsLength, char *aArgs[]);
#endif
otError ProcessMasterKey(uint8_t aArgsLength, char *aArgs[]);
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
otError ProcessLinkMetrics(uint8_t aArgsLength, char *aArgs[]);
otError ProcessLinkMetricsQuery(uint8_t aArgsLength, char *aArgs[]);
#endif
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
otError ProcessMlr(uint8_t aArgsLength, char *aArgs[]);
@@ -530,6 +534,14 @@ private:
#if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
void HandleSntpResponse(uint64_t aTime, otError aResult);
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
static void HandleLinkMetricsReport(const otIp6Address * aAddress,
const otLinkMetricsValues *aMetricsValues,
void * aContext);
void HandleLinkMetricsReport(const otIp6Address *aAddress, const otLinkMetricsValues *aMetricsValues);
#endif
static Interpreter &GetOwner(OwnerLocator &aOwnerLocator);
static void HandleDiscoveryRequest(const otThreadDiscoveryRequestInfo *aInfo, void *aContext)
@@ -626,6 +638,9 @@ private:
#if OPENTHREAD_FTD
{"leaderpartitionid", &Interpreter::ProcessLeaderPartitionId},
{"leaderweight", &Interpreter::ProcessLeaderWeight},
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
{"linkmetrics", &Interpreter::ProcessLinkMetrics},
#endif
{"log", &Interpreter::ProcessLog},
{"mac", &Interpreter::ProcessMac},
+8
View File
@@ -160,6 +160,10 @@ if (openthread_enable_core_config_args) {
defines += [ "OPENTHREAD_CONFIG_LEGACY_ENABLE=1" ]
}
if (openthread_config_link_metrics_enable) {
defines += [ "OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE=1" ]
}
if (openthread_config_link_raw_enable) {
defines += [ "OPENTHREAD_CONFIG_LINK_RAW_ENABLE=1" ]
}
@@ -305,6 +309,7 @@ openthread_core_files = [
"api/jam_detection_api.cpp",
"api/joiner_api.cpp",
"api/link_api.cpp",
"api/link_metrics_api.cpp",
"api/link_raw_api.cpp",
"api/logging_api.cpp",
"api/message_api.cpp",
@@ -500,6 +505,9 @@ openthread_core_files = [
"thread/indirect_sender_frame_context.hpp",
"thread/key_manager.cpp",
"thread/key_manager.hpp",
"thread/link_metrics.cpp",
"thread/link_metrics.hpp",
"thread/link_metrics_tlvs.hpp",
"thread/link_quality.cpp",
"thread/link_quality.hpp",
"thread/lowpan.cpp",
+2
View File
@@ -88,6 +88,7 @@ set(COMMON_SOURCES
api/jam_detection_api.cpp
api/joiner_api.cpp
api/link_api.cpp
api/link_metrics_api.cpp
api/link_raw_api.cpp
api/logging_api.cpp
api/message_api.cpp
@@ -184,6 +185,7 @@ set(COMMON_SOURCES
thread/energy_scan_server.cpp
thread/indirect_sender.cpp
thread/key_manager.cpp
thread/link_metrics.cpp
thread/link_quality.cpp
thread/lowpan.cpp
thread/mesh_forwarder.cpp
+4
View File
@@ -130,6 +130,7 @@ SOURCES_COMMON = \
api/jam_detection_api.cpp \
api/joiner_api.cpp \
api/link_api.cpp \
api/link_metrics_api.cpp \
api/link_raw_api.cpp \
api/logging_api.cpp \
api/message_api.cpp \
@@ -226,6 +227,7 @@ SOURCES_COMMON = \
thread/energy_scan_server.cpp \
thread/indirect_sender.cpp \
thread/key_manager.cpp \
thread/link_metrics.cpp \
thread/link_quality.cpp \
thread/lowpan.cpp \
thread/mesh_forwarder.cpp \
@@ -451,6 +453,8 @@ HEADERS_COMMON = \
thread/indirect_sender.hpp \
thread/indirect_sender_frame_context.hpp \
thread/key_manager.hpp \
thread/link_metrics.hpp \
thread/link_metrics_tlvs.hpp \
thread/link_quality.hpp \
thread/lowpan.hpp \
thread/mesh_forwarder.hpp \
+60
View File
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2020, 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 implements the OpenThread Link Metrics API.
*/
#include "openthread-core-config.h"
#include <openthread/link_metrics.h>
#include "common/instance.hpp"
#include "net/ip6_address.hpp"
using namespace ot;
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
otError otLinkMetricsQuery(otInstance * aInstance,
const otIp6Address * aDestination,
uint8_t aSeriesId,
const otLinkMetrics * aLinkMetricsFlags,
otLinkMetricsReportCallback aCallback,
void * aCallbackContext)
{
OT_ASSERT(aDestination != nullptr && aLinkMetricsFlags != nullptr);
static_cast<Instance *>(aInstance)->Get<LinkMetrics>().SetLinkMetricsReportCallback(aCallback, aCallbackContext);
return static_cast<Instance *>(aInstance)->Get<LinkMetrics>().LinkMetricsQuery(
static_cast<const Ip6::Address &>(*aDestination), aSeriesId, *aLinkMetricsFlags);
}
#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
+11
View File
@@ -86,6 +86,10 @@
#include "backbone_router/bbr_local.hpp"
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
#include "thread/link_metrics.hpp"
#endif
#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
@@ -789,6 +793,13 @@ template <> inline DuaManager &Instance::Get(void)
}
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
template <> inline LinkMetrics &Instance::Get(void)
{
return mThreadNetif.mLinkMetrics;
}
#endif
#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
#if OPENTHREAD_CONFIG_OTNS_ENABLE
+3
View File
@@ -620,6 +620,9 @@ void Message::SetLinkInfo(const ThreadLinkInfo &aLinkInfo)
SetLinkSecurityEnabled(aLinkInfo.mLinkSecurity);
SetPanId(aLinkInfo.mPanId);
AddRss(aLinkInfo.mRss);
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
AddLqi(aLinkInfo.mLqi);
#endif
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
SetTimeSyncSeq(aLinkInfo.mTimeSyncSeq);
SetNetworkTimeOffset(aLinkInfo.mNetworkTimeOffset);
+32
View File
@@ -151,6 +151,9 @@ struct MessageMetadata
uint16_t mLength; ///< Number of bytes within the message.
uint16_t mOffset; ///< A byte offset within the message.
RssAverager mRssAverager; ///< The averager maintaining the received signal strength (RSS) average.
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
LqiAverager mLqiAverager; ///< The averager maintaining the Link quality indicator (LQI) average.
#endif
ChildMask mChildMask; ///< A ChildMask to indicate which sleepy children need to receive this.
uint16_t mMeshDest; ///< Used for unicast non-link-local messages.
@@ -883,6 +886,35 @@ public:
*/
const RssAverager &GetRssAverager(void) const { return GetMetadata().mRssAverager; }
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
/**
* This method updates the average LQI (Link Quality Indicator) associated with the message.
*
* The given LQI value would be added to the average. Note that a message can be composed of multiple 802.15.4
* frame fragments each received with a different signal strength.
*
* @param[in] aLQI A new LQI value (has no unit) to be added to average.
*
*/
void AddLqi(uint8_t aLqi) { GetMetadata().mLqiAverager.Add(aLqi); }
/**
* This method returns the average LQI (Link Quality Indicator) associated with the message.
*
* @returns The current average LQI value (in dBm) or OT_RADIO_LQI_NONE if no average is available.
*
*/
uint8_t GetAverageLqi(void) const { return GetMetadata().mLqiAverager.GetAverage(); }
/**
* This mehod returns the count of frames counted so far.
*
* @retruns The count of frames that have been counted.
*
*/
uint8_t GetPsduCount(void) const { return GetMetadata().mLqiAverager.GetCount(); }
#endif
/**
* This method sets the message's link info properties (PAN ID, link security, RSS) from a given `ThreadLinkInfo`.
*
+10
View File
@@ -240,4 +240,14 @@
#define OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH 0
#endif
/**
* @def OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
*
* Define as 1 to enable Link Metrics feature.
*
*/
#ifndef OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
#define OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE 0
#endif
#endif // CONFIG_MLE_H_
@@ -523,6 +523,12 @@
#endif
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
#if (OPENTHREAD_CONFIG_THREAD_VERSION < OT_THREAD_VERSION_1_2)
#error "Thread 1.2 or higher version is required for OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE"
#endif
#endif
#if OPENTHREAD_CONFIG_DUA_ENABLE && (OPENTHREAD_CONFIG_THREAD_VERSION < OT_THREAD_VERSION_1_2)
#error "Thread 1.2 or higher version is required for OPENTHREAD_CONFIG_DUA_ENABLE"
#endif
+385
View File
@@ -0,0 +1,385 @@
/*
* Copyright (c) 2020, 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 definitions for Thread Link Metrics.
*/
#include "link_metrics.hpp"
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
#include "common/code_utils.hpp"
#include "common/instance.hpp"
#include "common/locator-getters.hpp"
#include "common/logging.hpp"
#include "link_metrics_tlvs.hpp"
namespace ot {
LinkMetrics::LinkMetrics(Instance &aInstance)
: InstanceLocator(aInstance)
, mLinkMetricsReportCallback(nullptr)
, mLinkMetricsReportCallbackContext(nullptr)
{
}
otError LinkMetrics::LinkMetricsQuery(const Ip6::Address & aDestination,
uint8_t aSeriesId,
const otLinkMetrics &aLinkMetricsFlags)
{
otError error;
LinkMetricsTypeIdFlags typeIdFlags[kMaxTypeIdFlags];
uint8_t typeIdFlagsCount = TypeIdFlagsFromLinkMetricsFlags(typeIdFlags, aLinkMetricsFlags);
error = SendLinkMetricsQuery(aDestination, aSeriesId, typeIdFlags, typeIdFlagsCount);
return error;
}
otError LinkMetrics::AppendLinkMetricsReport(Message &aMessage, const Message &aRequestMessage)
{
otError error = OT_ERROR_NONE;
Tlv tlv;
uint8_t queryId;
bool hasQueryId = false;
uint8_t length = 0;
uint16_t startOffset = aMessage.GetLength();
uint16_t offset;
uint16_t endOffset;
otLinkMetrics linkMetrics;
memset(&linkMetrics, 0, sizeof(linkMetrics));
SuccessOrExit(error = Tlv::FindTlvValueOffset(aRequestMessage, Mle::Tlv::Type::kLinkMetricsQuery, offset,
endOffset)); // `endOffset` is used to store tlv length here
endOffset = offset + endOffset;
while (offset < endOffset)
{
VerifyOrExit(aRequestMessage.Read(offset, sizeof(tlv), &tlv) == sizeof(tlv), error = OT_ERROR_PARSE);
switch (tlv.GetType())
{
case kLinkMetricsQueryId:
SuccessOrExit(error = Tlv::ReadUint8Tlv(aRequestMessage, offset, queryId));
hasQueryId = true;
break;
case kLinkMetricsQueryOptions:
for (uint16_t index = offset + sizeof(tlv), endIndex = static_cast<uint16_t>(offset + tlv.GetSize());
index < endIndex; index += sizeof(LinkMetricsTypeIdFlags))
{
LinkMetricsTypeIdFlags typeIdFlags;
VerifyOrExit(aRequestMessage.Read(index, sizeof(typeIdFlags), &typeIdFlags) == sizeof(typeIdFlags),
error = OT_ERROR_PARSE);
switch (typeIdFlags.GetRawValue())
{
case kTypeIdFlagPdu:
VerifyOrExit(!linkMetrics.mPduCount, error = OT_ERROR_PARSE);
linkMetrics.mPduCount = true;
break;
case kTypeIdFlagLqi:
VerifyOrExit(!linkMetrics.mLqi, error = OT_ERROR_PARSE);
linkMetrics.mLqi = true;
break;
case kTypeIdFlagLinkMargin:
VerifyOrExit(!linkMetrics.mLinkMargin, error = OT_ERROR_PARSE);
linkMetrics.mLinkMargin = true;
break;
case kTypeIdFlagRssi:
VerifyOrExit(!linkMetrics.mRssi, error = OT_ERROR_PARSE);
linkMetrics.mRssi = true;
break;
default:
if (typeIdFlags.IsExtendedFlagSet())
{
index += sizeof(uint8_t); // Skip the additional second flags byte.
}
break;
}
}
break;
default:
break;
}
offset += tlv.GetSize();
}
VerifyOrExit(hasQueryId, error = OT_ERROR_PARSE);
// Link Metrics Report TLV
tlv.SetType(Mle::Tlv::kLinkMetricsReport);
SuccessOrExit(error = aMessage.Append(&tlv, sizeof(tlv)));
if (queryId == 0)
{
SuccessOrExit(error = AppendSingleProbeLinkMetricsReport(aMessage, length, linkMetrics, aRequestMessage));
}
else
{
ExitNow(error = OT_ERROR_NOT_IMPLEMENTED);
}
tlv.SetLength(length);
aMessage.Write(startOffset, sizeof(tlv), &tlv);
exit:
otLogDebgMle("AppendLinkMetricsReport, error:%s", otThreadErrorToString(error));
return error;
}
void LinkMetrics::HandleLinkMetricsReport(const Message & aMessage,
uint16_t aOffset,
uint16_t aLength,
const Ip6::Address &aAddress)
{
otLinkMetricsValues metricsValues;
uint8_t metricsRawValue;
uint16_t pos = aOffset;
uint16_t endPos = aOffset + aLength;
Tlv tlv;
LinkMetricsTypeIdFlags typeIdFlags;
VerifyOrExit(mLinkMetricsReportCallback != nullptr, OT_NOOP);
memset(&metricsValues, 0, sizeof(metricsValues));
otLogDebgMle("Received Link Metrics Report");
while (pos < endPos)
{
VerifyOrExit(aMessage.Read(pos, sizeof(Tlv), &tlv) == sizeof(Tlv), OT_NOOP);
VerifyOrExit(tlv.GetType() == kLinkMetricsReportSub, OT_NOOP);
pos += sizeof(Tlv);
VerifyOrExit(pos + tlv.GetLength() <= endPos, OT_NOOP);
aMessage.Read(pos, sizeof(LinkMetricsTypeIdFlags), &typeIdFlags);
if (typeIdFlags.IsExtendedFlagSet())
{
pos += tlv.GetLength(); // Skip the whole sub-TLV if `E` flag is set
continue;
}
pos += sizeof(LinkMetricsTypeIdFlags);
switch (typeIdFlags.GetRawValue())
{
case kTypeIdFlagPdu:
metricsValues.mMetrics.mPduCount = true;
aMessage.Read(pos, sizeof(uint32_t), &metricsValues.mPduCountValue);
pos += sizeof(uint32_t);
otLogDebgMle(" - PDU Counter: %d (Count/Summation)", metricsValues.mPduCountValue);
break;
case kTypeIdFlagLqi:
metricsValues.mMetrics.mLqi = true;
aMessage.Read(pos, sizeof(uint8_t), &metricsValues.mLqiValue);
pos += sizeof(uint8_t);
otLogDebgMle(" - LQI: %d (Exponential Moving Average)", metricsValues.mLqiValue);
break;
case kTypeIdFlagLinkMargin:
metricsValues.mMetrics.mLinkMargin = true;
aMessage.Read(pos, sizeof(uint8_t), &metricsRawValue);
metricsValues.mLinkMarginValue =
metricsRawValue * 130 / 255; // Reverse operation for linear scale, map from [0, 255] to [0, 130]
pos += sizeof(uint8_t);
otLogDebgMle(" - Margin: %d (dB) (Exponential Moving Average)", metricsValues.mLinkMarginValue);
break;
case kTypeIdFlagRssi:
metricsValues.mMetrics.mRssi = true;
aMessage.Read(pos, sizeof(uint8_t), &metricsRawValue);
metricsValues.mRssiValue =
metricsRawValue * 130 / 255 - 130; // Reverse operation for linear scale, map from [0, 255] to [-130, 0]
pos += sizeof(uint8_t);
otLogDebgMle(" - RSSI: %d (dBm) (Exponential Moving Average)", metricsValues.mRssiValue);
break;
default:
break;
}
}
mLinkMetricsReportCallback(&aAddress, &metricsValues, mLinkMetricsReportCallbackContext);
exit:
return;
}
void LinkMetrics::SetLinkMetricsReportCallback(otLinkMetricsReportCallback aCallback, void *aCallbackContext)
{
mLinkMetricsReportCallback = aCallback;
mLinkMetricsReportCallbackContext = aCallbackContext;
}
otError LinkMetrics::SendLinkMetricsQuery(const Ip6::Address & aDestination,
uint8_t aSeriesId,
const LinkMetricsTypeIdFlags *aTypeIdFlags,
uint8_t aTypeIdFlagsCount)
{
otError error = OT_ERROR_NONE;
LinkMetricsQueryOptionsTlv linkMetricsQueryOptionsTlv;
uint8_t length = 0;
static const uint8_t tlvs[] = {Mle::Tlv::kLinkMetricsReport};
uint8_t buf[sizeof(Tlv) * 3 + sizeof(uint8_t) +
sizeof(LinkMetricsTypeIdFlags) *
kMaxTypeIdFlags]; // LinkMetricsQuery Tlv + LinkMetricsQueryId sub-TLV (value-length: 1 byte) +
// LinkMetricsQueryOptions sub-TLV (value-length: `kMaxTypeIdFlags` bytes)
Tlv *tlv = reinterpret_cast<Tlv *>(buf);
Tlv subTlv;
// Link Metrics Query TLV
tlv->SetType(Mle::Tlv::kLinkMetricsQuery);
length += sizeof(Tlv);
// Link Metrics Query ID sub-TLV
subTlv.SetType(kLinkMetricsQueryId);
subTlv.SetLength(sizeof(uint8_t));
memcpy(buf + length, &subTlv, sizeof(subTlv));
length += sizeof(subTlv);
memcpy(buf + length, &aSeriesId, sizeof(aSeriesId));
length += sizeof(aSeriesId);
// Link Metrics Query Options sub-TLV
if (aTypeIdFlagsCount > 0)
{
linkMetricsQueryOptionsTlv.Init();
linkMetricsQueryOptionsTlv.SetLength(aTypeIdFlagsCount * sizeof(LinkMetricsTypeIdFlags));
memcpy(buf + length, &linkMetricsQueryOptionsTlv, sizeof(linkMetricsQueryOptionsTlv));
length += sizeof(linkMetricsQueryOptionsTlv);
memcpy(buf + length, aTypeIdFlags, linkMetricsQueryOptionsTlv.GetLength());
length += linkMetricsQueryOptionsTlv.GetLength();
}
// Set Length for Link Metrics Report TLV
tlv->SetLength(length - sizeof(Tlv));
SuccessOrExit(error = Get<Mle::MleRouter>().SendDataRequest(aDestination, tlvs, sizeof(tlvs), 0, buf, length));
exit:
return error;
}
otError LinkMetrics::AppendSingleProbeLinkMetricsReport(Message & aMessage,
uint8_t & aLength,
const otLinkMetrics &aLinkMetrics,
const Message & aRequestMessage)
{
otError error = OT_ERROR_NONE;
LinkMetricsReportSubTlv metric;
aLength = 0;
// Link Metrics Report sub-TLVs
if (aLinkMetrics.mPduCount)
{
metric.Init();
metric.SetMetricsTypeId(kTypeIdFlagPdu);
metric.SetMetricsValue32(aRequestMessage.GetPsduCount());
SuccessOrExit(error = aMessage.Append(&metric, metric.GetSize()));
aLength += metric.GetSize();
}
if (aLinkMetrics.mLqi)
{
metric.Init();
metric.SetMetricsTypeId(kTypeIdFlagLqi);
metric.SetMetricsValue8(aRequestMessage.GetAverageLqi()); // IEEE 802.15.4 LQI is in scale 0-255
SuccessOrExit(error = aMessage.Append(&metric, metric.GetSize()));
aLength += metric.GetSize();
}
if (aLinkMetrics.mLinkMargin)
{
metric.Init();
metric.SetMetricsTypeId(kTypeIdFlagLinkMargin);
metric.SetMetricsValue8(
LinkQualityInfo::ConvertRssToLinkMargin(Get<Mac::Mac>().GetNoiseFloor(), aRequestMessage.GetAverageRss()) *
255 / 130); // Linear scale Link Margin from [0, 130] to [0, 255]
SuccessOrExit(error = aMessage.Append(&metric, metric.GetSize()));
aLength += metric.GetSize();
}
if (aLinkMetrics.mRssi)
{
metric.Init();
metric.SetMetricsTypeId(kTypeIdFlagRssi);
metric.SetMetricsValue8((aRequestMessage.GetAverageRss() + 130) * 255 /
130); // Linear scale rss from [-130, 0] to [0, 255]
SuccessOrExit(error = aMessage.Append(&metric, metric.GetSize()));
aLength += metric.GetSize();
}
exit:
return error;
}
uint8_t LinkMetrics::TypeIdFlagsFromLinkMetricsFlags(LinkMetricsTypeIdFlags *aTypeIdFlags,
const otLinkMetrics & aLinkMetricsFlags)
{
uint8_t count = 0;
if (aLinkMetricsFlags.mPduCount)
{
aTypeIdFlags[count++].SetRawValue(kTypeIdFlagPdu);
}
if (aLinkMetricsFlags.mLqi)
{
aTypeIdFlags[count++].SetRawValue(kTypeIdFlagLqi);
}
if (aLinkMetricsFlags.mLinkMargin)
{
aTypeIdFlags[count++].SetRawValue(kTypeIdFlagLinkMargin);
}
if (aLinkMetricsFlags.mRssi)
{
aTypeIdFlags[count++].SetRawValue(kTypeIdFlagRssi);
}
return count;
}
} // namespace ot
#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
+163
View File
@@ -0,0 +1,163 @@
/*
* Copyright (c) 2020, 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 definitions for Thread Link Metrics query and management.
*/
#ifndef LINK_METRICS_HPP_
#define LINK_METRICS_HPP_
#include "openthread-core-config.h"
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
#include <openthread/ip6.h>
#include "common/code_utils.hpp"
#include "common/locator.hpp"
#include "link_metrics_tlvs.hpp"
#include "topology.hpp"
namespace ot {
/**
* @addtogroup core-link-metrics
*
* @brief
* This module includes definitions for Thread Link Metrics query and management.
*
* @{
*/
class LinkMetrics : public InstanceLocator
{
public:
/**
* This constructor initializes an instance of the LinkMetrics class.
*
* @param[in] aInstance A reference to the OpenThread interface.
*
*/
explicit LinkMetrics(Instance &aInstance);
/**
* This function sends an MLE Data Request containing Link Metrics Query TLV to query Link Metrics data.
*
* It could be either a Single Probe or a Forward Tracking Series.
*
* @param[in] aDestination A reference to the IPv6 address of the destination.
* @param[in] aSeriesId The Series ID to query, 0 for single probe.
* @param[in] aLinkMetricsFlags A reference to flags specifying what metrics to query.
*
* @retval OT_ERROR_NONE Successfully sent a Link Metrics query message.
* @retval OT_ERROR_NO_BUFS Insufficient buffers to generate the MLE Data Request message.
* @retval OT_ERROR_INVALID_ARGS TypeIdFlags are not valid or exceed the count limit.
*
*/
otError LinkMetricsQuery(const Ip6::Address & aDestination,
uint8_t aSeriesId,
const otLinkMetrics &aLinkMetricsFlags);
/**
* This method appends a Link Metrics Report to a message according to the Link Metrics query.
*
* @param[out] aMessage A reference to the message to append report.
* @param[in] aRequestMessage A reference to the message of the Data Request.
*
* @retval OT_ERROR_NONE Successfully appended the Thread Discovery TLV.
* @retval OT_ERROR_PARSE Cannot parse query sub TLV successfully.
* @retval OT_ERROR_INVALID_ARGS QueryId is invalid or any Type ID is invalid.
*
*/
otError AppendLinkMetricsReport(Message &aMessage, const Message &aRequestMessage);
/**
* This method handles the received Link Metrics report contained in @p aMessage.
*
* @param[in] aMessage A reference to the message.
* @param[in] aOffset The offset in bytes where the metrics report sub-TLVs start.
* @param[in] aLength The length of the metrics report sub-TLVs in bytes.
* @param[in] aAddress A reference to the source address of the message.
*
*/
void HandleLinkMetricsReport(const Message & aMessage,
uint16_t aOffset,
uint16_t aLength,
const Ip6::Address &aAddress);
/**
* This method registers a callback to handle Link Metrics report received.
*
* @param[in] aCallback A pointer to a function that is called when link probing report is received
* @param[in] aCallbackContext A pointer to application-specific context.
*
*/
void SetLinkMetricsReportCallback(otLinkMetricsReportCallback aCallback, void *aCallbackContext);
private:
enum
{
kMaxTypeIdFlags = 4,
kTypeIdFlagPdu =
0x40, ///< 0x0_1_000_000 -> 0x40 ==> L bit set, type = 0 (count/summation), metric-enum = 0 (PDU rxed)
kTypeIdFlagLqi = 0x09, ///< 0x0_0_001_001 -> 0x00 ==> L bit not set, type = 1 (exp ave), metric-enum = 1 (LQI)
kTypeIdFlagLinkMargin =
0x0a, ///< 0x0_0_001_010 -> 0x00 ==> L bit not set, type = 1 (exp ave), metric-enum = 2 (Link Margin)
kTypeIdFlagRssi = 0x0b, ///< 0x0_0_001_011 -> 0x00 ==> L bit not set, type = 1 (exp ave), metric-enum = 3 (RSSI)
};
otLinkMetricsReportCallback mLinkMetricsReportCallback;
void * mLinkMetricsReportCallbackContext;
otError SendLinkMetricsQuery(const Ip6::Address & aDestination,
uint8_t aSeriesId,
const LinkMetricsTypeIdFlags *aTypeIdFlags,
uint8_t aTypeIdFlagsCount);
otError AppendSingleProbeLinkMetricsReport(Message & aMessage,
uint8_t & aLength,
const otLinkMetrics &aLinkMetrics,
const Message & aRequestMessage);
static uint8_t TypeIdFlagsFromLinkMetricsFlags(LinkMetricsTypeIdFlags *aTypeIdFlags,
const otLinkMetrics & aLinkMetricsFlags);
};
/**
* @}
*/
} // namespace ot
#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
#endif // LINK_METRICS_HPP
+337
View File
@@ -0,0 +1,337 @@
/*
* Copyright (c) 2020, 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 definitions for generating and processing Link Metrics TLVs.
*
*/
#ifndef LINK_METRICS_TLVS_HPP_
#define LINK_METRICS_TLVS_HPP_
#include <openthread/link_metrics.h>
#include "common/encoding.hpp"
#include "common/message.hpp"
#include "common/tlvs.hpp"
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
namespace ot {
/**
* Link Metrics parameters
*
*/
enum
{
kLinkMetricsMaxTypeIdFlags = 4, ///< Max count of Link Metrics Type ID Flags.
};
/**
* Link Metrics Sub-TLV types
*
*/
enum Type
{
kLinkMetricsReportSub = 0, ///< Link Metrics Report Sub-TLV
kLinkMetricsQueryId = 1, ///< Link Metrics Query ID Sub-TLV
kLinkMetricsQueryOptions = 2, ///< Link Metrics Query Options Sub-TLV
kForwardProbingRegistration = 3, ///< Forward Probing Registration Sub-TLV
kReverseProbingRegistration = 4, ///< Reverse Probing Registration Sub-TLV
kLinkMetricsStatus = 5, ///< Link Metrics Status Sub-TLV
kSeriesTrackingCapabilities = 6, ///< Series Tracking Capabilities Sub-TLV
kEnhancedACKConfiguration = 7, ///< Enhanced ACK Configuration Sub-TLV
};
/**
* This class implements Link Metrics Type Id Flags generation and parsing.
*
*/
OT_TOOL_PACKED_BEGIN
class LinkMetricsTypeIdFlags
{
public:
/**
* Default constructor.
*
*/
LinkMetricsTypeIdFlags(void) {}
/**
* Constructor for implicit cast from `uint8_t` to `LinkMetricsTypeIdFlags`.
*
*/
LinkMetricsTypeIdFlags(uint8_t typeIdFlags)
: mTypeIdFlags(typeIdFlags)
{
}
/**
* This method init the Type Id value
*
*/
void Init(void) { mTypeIdFlags = 0; }
/**
* This method clears the Extended flag.
*
*/
void ClearExtendedFlag(void) { mTypeIdFlags &= ~kExtendedFlag; }
/**
* This method sets the Extended flag, indicating an additional second flags byte after the current 1-byte flags.
* MUST NOT set in Thread 1.2.1.
*
*/
void SetExtendedFlag(void) { mTypeIdFlags |= kExtendedFlag; }
/**
* This method indicates whether or not the Extended flag is set.
*
* @retval true The Extended flag is set.
* @retval false The Extended flag is not set.
*
*/
bool IsExtendedFlagSet(void) const { return (mTypeIdFlags & kExtendedFlag) != 0; }
/**
* This method clears value length flag.
*
*/
void ClearLengthFlag(void) { mTypeIdFlags &= ~kLengthFlag; }
/**
* This method sets the value length flag.
*
*/
void SetLengthFlag(void) { mTypeIdFlags |= kLengthFlag; }
/**
* This method indicates whether or not the value length flag is set.
*
* @retval true The value length flag is set, extended value length (4 bytes)
* @retval false The value length flag is not set, short value length (1 byte)
*
*/
bool IsLengthFlagSet(void) const { return (mTypeIdFlags & kLengthFlag) != 0; }
/**
* This method sets the Type/Average Enum.
*
* @param[in] aTypeEnum Type/Average Enum.
*
*/
void SetTypeEnum(uint8_t aTypeEnum)
{
mTypeIdFlags = (mTypeIdFlags & ~kTypeEnumMask) | ((aTypeEnum << kTypeEnumOffset) & kTypeEnumMask);
}
/**
* This method returns the Type/Average Enum.
*
* @returns The Type/Average Enum.
*
*/
uint8_t GetTypeEnum(void) const { return (mTypeIdFlags & kTypeEnumMask) >> kTypeEnumOffset; }
/**
* This method sets the Metric Enum.
*
* @param[in] aMetricEnum Metric Enum.
*
*/
void SetMetricEnum(uint8_t aMetricEnum)
{
mTypeIdFlags = (mTypeIdFlags & ~kMetricEnumMask) | ((aMetricEnum << kMetricEnumOffset) & kMetricEnumMask);
}
/**
* This method returns the Metric Enum.
*
* @returns The Metric Enum.
*
*/
uint8_t GetMetricEnum(void) const { return (mTypeIdFlags & kMetricEnumMask) >> kMetricEnumOffset; }
/**
* This method returns the raw value of the entire TypeIdFlags.
*
* @returns The raw value of TypeIdFlags.
*
*/
uint8_t GetRawValue(void) const { return mTypeIdFlags; }
/**
* This method sets the raw value of the entire TypeIdFlags.
*
* @param[in] aTypeIdFlags The value of entire TypeIdFlags.
*
*/
void SetRawValue(uint8_t aTypeIdFlags) { mTypeIdFlags = aTypeIdFlags; }
private:
enum
{
kLengthFlag = 1 << 6,
kExtendedFlag = 1 << 7,
kTypeEnumOffset = 3,
kTypeEnumMask = 7 << kTypeEnumOffset,
kMetricEnumOffset = 0,
kMetricEnumMask = 7 << kMetricEnumOffset,
};
uint8_t mTypeIdFlags;
} OT_TOOL_PACKED_END;
/**
* This class implements Link Metrics Report Sub-TLV generation and parsing.
*
*/
OT_TOOL_PACKED_BEGIN
class LinkMetricsReportSubTlv : public Tlv
{
public:
/**
* This method initializes the TLV.
*
*/
void Init(void)
{
SetType(kLinkMetricsReportSub);
SetLength(sizeof(*this) - sizeof(Tlv));
}
/**
* This method indicates whether or not the TLV appears to be well-formed.
*
* @retval true The TLV appears to be well-formed.
* @retval false The TLV does not appear to be well-formed.
*
*/
bool IsValid(void) const { return GetLength() >= sizeof(LinkMetricsTypeIdFlags) + sizeof(uint8_t); }
/**
* This method returns the Link Metrics Type ID.
*
* @returns The Link Metrics Type ID.
*
*/
LinkMetricsTypeIdFlags GetMetricsTypeId(void) const { return mMetricsTypeId; }
/**
* This method sets the Link Metrics Type ID.
*
* @param[in] aMetricsTypeID The Link Metrics Type ID to set.
*
*/
void SetMetricsTypeId(LinkMetricsTypeIdFlags aMetricsTypeId)
{
mMetricsTypeId = aMetricsTypeId;
if (!aMetricsTypeId.IsLengthFlagSet())
{
SetLength(sizeof(*this) - sizeof(Tlv) - sizeof(uint32_t) + sizeof(uint8_t)); // The value is 1 byte long
}
}
/**
* This method returns the metric value in 8 bits.
*
* @returns The metric value.
*
*/
uint8_t GetMetricsValue8(void) const { return mMetricsValue.m8; }
/**
* This method returns the metric value in 32 bits.
*
* @returns The metric value.
*
*/
uint32_t GetMetricsValue32(void) const { return mMetricsValue.m32; }
/**
* This method sets the metric value (8 bits).
*
* @param[in] aMetricsValue Metrics value.
*
*/
void SetMetricsValue8(uint8_t aMetricsValue) { mMetricsValue.m8 = aMetricsValue; }
/**
* This method sets the metric value (32 bits).
*
* @param[in] aMetricsValue Metrics value.
*
*/
void SetMetricsValue32(uint32_t aMetricsValue) { mMetricsValue.m32 = aMetricsValue; }
private:
LinkMetricsTypeIdFlags mMetricsTypeId;
union
{
uint8_t m8;
uint32_t m32;
} mMetricsValue;
} OT_TOOL_PACKED_END;
/**
* This class implements Link Metrics Query Options Sub-TLV generation and parsing.
*
*/
OT_TOOL_PACKED_BEGIN
class LinkMetricsQueryOptionsTlv : public Tlv
{
public:
/**
* This method initializes the TLV.
*
*/
void Init(void)
{
SetType(kLinkMetricsQueryOptions);
SetLength(0);
}
/**
* This method indicates whether or not the TLV appears to be well-formed.
*
* @retval TRUE If the TLV appears to be well-formed.
* @retval FALSE If the TLV does not appear to be well-formed.
*
*/
bool IsValid(void) const { return GetLength() >= sizeof(LinkMetricsTypeIdFlags); }
} OT_TOOL_PACKED_END;
} // namespace ot
#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
#endif // LINK_METRICS_TLVS_HPP_
+19
View File
@@ -121,6 +121,25 @@ exit:
return string;
}
void LqiAverager::Reset(void)
{
mCount = 0;
mAverage = 0;
}
void LqiAverager::Add(uint8_t aLqi)
{
uint8_t count;
if (mCount < UINT8_MAX)
{
mCount++;
}
count = OT_MIN((1 << kCoeffBitShift), mCount);
mAverage = static_cast<uint8_t>(((mAverage * (count - 1)) + aLqi) / count);
}
void LinkQualityInfo::Clear(void)
{
mRssAverager.Reset();
+49
View File
@@ -211,6 +211,55 @@ private:
uint16_t mCount : 5; // Number of RSS values added to averager so far (limited to 2^kCoeffBitShift-1).
};
/**
* This class implements a Link Quality Indicator (LQI) averager.
*
* It maintains the exponential moving average value of LQI.
*
*/
class LqiAverager
{
public:
/**
* This method resets the averager and clears the average value.
*
*/
void Reset(void);
/**
* This method adds a link quality indicator (LQI) value to the average.
*
* @param[in] aLqi Link Quality Indicator value to be added to the average.
*
*/
void Add(uint8_t aLqi);
/**
* This method returns the current average link quality value maintained by the averager.
*
* @returns The current average value.
*
*/
uint8_t GetAverage(void) const { return mAverage; }
/**
* This method returns the count of frames calculated so far.
*
* @returns The count of frames calculated.
*
*/
uint8_t GetCount(void) const { return mCount; }
private:
enum
{
kCoeffBitShift = 3, ///< Coefficient used for exponentially weighted filter (1 << kCoeffBitShift).
};
uint8_t mAverage; ///< The average link quality indicator value.
uint8_t mCount; ///< Number of LQI values added to averager so far.
};
/**
* This class encapsulates/stores all relevant information about quality of a link, including average received signal
* strength (RSS), last RSS, link margin, and link quality.
+3
View File
@@ -1042,6 +1042,9 @@ void MeshForwarder::HandleFragment(const uint8_t * aFrame,
message->Write(message->GetOffset(), aFrameLength, aFrame);
message->MoveOffset(aFrameLength);
message->AddRss(aLinkInfo.GetRss());
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
message->AddLqi(aLinkInfo.GetLqi());
#endif
message->SetTimeout(kReassemblyTimeout);
}
+23 -1
View File
@@ -2035,8 +2035,13 @@ exit:
otError Mle::SendDataRequest(const Ip6::Address &aDestination,
const uint8_t * aTlvs,
uint8_t aTlvsLength,
uint16_t aDelay)
uint16_t aDelay,
const uint8_t * aExtraTlvs,
uint8_t aExtraTlvsLength)
{
OT_UNUSED_VARIABLE(aExtraTlvs);
OT_UNUSED_VARIABLE(aExtraTlvsLength);
otError error = OT_ERROR_NONE;
Message *message;
@@ -2046,6 +2051,11 @@ otError Mle::SendDataRequest(const Ip6::Address &aDestination,
SuccessOrExit(error = AppendActiveTimestamp(*message));
SuccessOrExit(error = AppendPendingTimestamp(*message));
if (aExtraTlvs != nullptr && aExtraTlvsLength > 0)
{
SuccessOrExit(error = message->Append(aExtraTlvs, aExtraTlvsLength));
}
if (aDelay)
{
SuccessOrExit(error = AddDelayedResponse(*message, aDestination, aDelay));
@@ -2801,11 +2811,23 @@ exit:
void Mle::HandleDataResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, const Neighbor *aNeighbor)
{
otError error;
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
uint16_t metricsReportValueOffset;
uint16_t length;
#endif
Log(kMessageReceive, kTypeDataResponse, aMessageInfo.GetPeerAddr());
VerifyOrExit(aNeighbor && aNeighbor->IsStateValid(), error = OT_ERROR_SECURITY);
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
if (Tlv::FindTlvValueOffset(aMessage, Tlv::kLinkMetricsReport, metricsReportValueOffset, length) == OT_ERROR_NONE)
{
Get<LinkMetrics>().HandleLinkMetricsReport(aMessage, metricsReportValueOffset, length,
aMessageInfo.GetPeerAddr());
}
#endif
error = HandleLeaderData(aMessage, aMessageInfo);
if (mDataRequestState == kDataRequestNone && !IsRxOnWhenIdle())
+13 -5
View File
@@ -43,6 +43,7 @@
#include "meshcop/joiner_router.hpp"
#include "meshcop/meshcop.hpp"
#include "net/udp6.hpp"
#include "thread/link_metrics.hpp"
#include "thread/mle_tlvs.hpp"
#include "thread/mle_types.hpp"
#include "thread/neighbor_table.hpp"
@@ -92,6 +93,9 @@ class Mle : public InstanceLocator
{
friend class DiscoverScanner;
friend class ot::Notifier;
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
friend class ot::LinkMetrics;
#endif
public:
/**
@@ -1288,10 +1292,12 @@ protected:
/**
* This method generates an MLE Data Request message.
*
* @param[in] aDestination A reference to the IPv6 address of the destination.
* @param[in] aTlvs A pointer to requested TLV types.
* @param[in] aTlvsLength The number of TLV types in @p aTlvs.
* @param[in] aDelay Delay in milliseconds before the Data Request message is sent.
* @param[in] aDestination A reference to the IPv6 address of the destination.
* @param[in] aTlvs A pointer to requested TLV types.
* @param[in] aTlvsLength The number of TLV types in @p aTlvs.
* @param[in] aDelay Delay in milliseconds before the Data Request message is sent.
* @param[in] aExtraTlvs A pointer to extra TLVs.
* @param[in] aExtraTlvsLength Length of extra TLVs.
*
* @retval OT_ERROR_NONE Successfully generated an MLE Data Request message.
* @retval OT_ERROR_NO_BUFS Insufficient buffers to generate the MLE Data Request message.
@@ -1300,7 +1306,9 @@ protected:
otError SendDataRequest(const Ip6::Address &aDestination,
const uint8_t * aTlvs,
uint8_t aTlvsLength,
uint16_t aDelay);
uint16_t aDelay,
const uint8_t * aExtraTlvs = nullptr,
uint8_t aExtraTlvsLength = 0);
/**
* This method generates an MLE Child Update Request message.
+12 -2
View File
@@ -2723,7 +2723,7 @@ void MleRouter::HandleDataRequest(const Message & aMessage,
tlvs[numTlvs++] = Tlv::kPendingDataset;
}
SendDataResponse(aMessageInfo.GetPeerAddr(), tlvs, numTlvs, 0);
SendDataResponse(aMessageInfo.GetPeerAddr(), tlvs, numTlvs, 0, &aMessage);
exit:
LogProcessError(kTypeDataRequest, error);
@@ -3210,8 +3210,11 @@ exit:
void MleRouter::SendDataResponse(const Ip6::Address &aDestination,
const uint8_t * aTlvs,
uint8_t aTlvsLength,
uint16_t aDelay)
uint16_t aDelay,
const Message * aRequestMessage)
{
OT_UNUSED_VARIABLE(aRequestMessage);
otError error = OT_ERROR_NONE;
Message * message = nullptr;
Neighbor *neighbor;
@@ -3248,6 +3251,13 @@ void MleRouter::SendDataResponse(const Ip6::Address &aDestination,
case Tlv::kPendingDataset:
SuccessOrExit(error = AppendPendingDataset(*message));
break;
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
case Tlv::kLinkMetricsReport:
OT_ASSERT(aRequestMessage != nullptr);
SuccessOrExit(error = Get<LinkMetrics>().AppendLinkMetricsReport(*message, *aRequestMessage));
break;
#endif
}
}
+5 -1
View File
@@ -615,7 +615,11 @@ private:
const uint8_t * aTlvs,
uint8_t aTlvsLength,
const Challenge & aChallenge);
void SendDataResponse(const Ip6::Address &aDestination, const uint8_t *aTlvs, uint8_t aTlvsLength, uint16_t aDelay);
void SendDataResponse(const Ip6::Address &aDestination,
const uint8_t * aTlvs,
uint8_t aTlvsLength,
uint16_t aDelay,
const Message * aRequestMessage = nullptr);
otError SendDiscoveryResponse(const Ip6::Address &aDestination, uint16_t aPanId);
void SetStateRouter(uint16_t aRloc16);
+3
View File
@@ -41,6 +41,7 @@
#include "common/tlvs.hpp"
#include "meshcop/timestamp.hpp"
#include "net/ip6_address.hpp"
#include "thread/link_metrics_tlvs.hpp"
#include "thread/mle_types.hpp"
namespace ot {
@@ -103,6 +104,8 @@ public:
kDiscovery = 26, ///< Thread Discovery TLV
kCslChannel = 80, ///< CSL Channel TLV
kCslTimeout = 85, ///< CSL Timeout TLV
kLinkMetricsQuery = 87, ///< Link Metrics Query TLV
kLinkMetricsReport = 89, ///< Link Metrics Report TLV
/**
* Applicable/Required only when time synchronization service
+3
View File
@@ -126,6 +126,9 @@ ThreadNetif::ThreadNetif(Instance &aInstance)
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
, mTimeSync(aInstance)
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
, mLinkMetrics(aInstance)
#endif
{
}
+4
View File
@@ -84,6 +84,7 @@
#include "thread/discover_scanner.hpp"
#include "thread/energy_scan_server.hpp"
#include "thread/key_manager.hpp"
#include "thread/link_metrics.hpp"
#include "thread/mesh_forwarder.hpp"
#include "thread/mle.hpp"
#include "thread/mle_router.hpp"
@@ -266,6 +267,9 @@ private:
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
TimeSync mTimeSync;
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE
LinkMetrics mLinkMetrics;
#endif
};
/**
+2
View File
@@ -257,6 +257,8 @@ def create_default_mle_tlvs_factories():
mle.TlvType.TIME_REQUEST: mle.TimeRequestFactory(),
mle.TlvType.TIME_PARAMETER: mle.TimeParameterFactory(),
mle.TlvType.THREAD_DISCOVERY: create_default_mle_tlv_thread_discovery_factory(),
mle.TlvType.LINK_METRICS_QUERY: mle.LinkMetricsQueryFactory(),
mle.TlvType.LINK_METRICS_REPORT: mle.LinkMetricsReportFactory(),
}
+28
View File
@@ -89,6 +89,8 @@ class TlvType(IntEnum):
THREAD_DISCOVERY = 26
CSL_CHANNEL = 80
CSL_SYNCHRONIZED_TIMEOUT = 85
LINK_METRICS_QUERY = 87
LINK_METRICS_REPORT = 89
TIME_REQUEST = 252
TIME_PARAMETER = 253
@@ -1102,6 +1104,32 @@ class TimeParameterFactory:
return TimeParameter()
class LinkMetricsQuery:
# TODO: Not implemented yet
def __init__(self):
print("LinkMetricsQuery is not implemented yet.")
class LinkMetricsQueryFactory:
def parse(self, data, message_info):
return LinkMetricsQuery()
class LinkMetricsReport:
# TODO: Not implemented yet
def __init__(self):
print("LinkMetricsReport is not implemented yet.")
class LinkMetricsReportFactory:
def parse(self, data, message_info):
return LinkMetricsReport()
class MleCommand(object):
def __init__(self, _type, tlvs):
+5
View File
@@ -1866,6 +1866,11 @@ class NodeImpl:
return router_table
def link_metrics_query_single_probe(self, dst_addr: str, linkmetrics_flags: str):
cmd = 'linkmetrics query %s single %s' % (dst_addr, linkmetrics_flags)
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)
+115
View File
@@ -0,0 +1,115 @@
#!/usr/bin/env python3
#
# Copyright (c) 2020, 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
import mle
import thread_cert
LEADER = 1
SSED_1 = 2
CSL_PERIOD = 500 * 6.25 # 500ms
CSL_TIMEOUT = 30 # 30s
class SSED_SingleProbe(thread_cert.TestCase):
TOPOLOGY = {
LEADER: {
'version': '1.2',
},
SSED_1: {
'version': '1.2',
'mode': 's',
},
}
"""All nodes are created with default configurations"""
def test(self):
self.nodes[SSED_1].set_csl_period(CSL_PERIOD)
self.nodes[SSED_1].set_csl_timeout(CSL_TIMEOUT)
self.nodes[LEADER].start()
self.simulator.go(5)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[SSED_1].start()
self.simulator.go(7)
self.assertEqual(self.nodes[SSED_1].get_state(), 'child')
leader_addr = self.nodes[LEADER].get_linklocal()
leader_messages = self.simulator.get_messages_sent_by(LEADER)
# SSED_1 sends a Single Probe Link Metrics for L2 PDU count using MLE Data Request
self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'p')
self.simulator.go(5)
leader_messages = self.simulator.get_messages_sent_by(LEADER)
msg = leader_messages.next_mle_message(mle.CommandType.DATA_RESPONSE)
msg.assertMleMessageContainsTlv(mle.LinkMetricsReport)
# SSED_1 sends a Single Probe Link Metrics for L2 LQI using MLE Data Request
self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'q')
self.simulator.go(5)
leader_messages = self.simulator.get_messages_sent_by(LEADER)
msg = leader_messages.next_mle_message(mle.CommandType.DATA_RESPONSE)
msg.assertMleMessageContainsTlv(mle.LinkMetricsReport)
# SSED_1 sends a Single Probe Link Metrics for Link Margin using MLE Data Request
self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'm')
self.simulator.go(5)
leader_messages = self.simulator.get_messages_sent_by(LEADER)
msg = leader_messages.next_mle_message(mle.CommandType.DATA_RESPONSE)
msg.assertMleMessageContainsTlv(mle.LinkMetricsReport)
# SSED_1 sends a Single Probe Link Metrics for Link Margin using MLE Data Request
self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'r')
self.simulator.go(5)
leader_messages = self.simulator.get_messages_sent_by(LEADER)
msg = leader_messages.next_mle_message(mle.CommandType.DATA_RESPONSE)
msg.assertMleMessageContainsTlv(mle.LinkMetricsReport)
# SSED_1 sends a Single Probe Link Metrics for all metrics using MLE Data Request
self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'pqmr')
self.simulator.go(5)
leader_messages = self.simulator.get_messages_sent_by(LEADER)
msg = leader_messages.next_mle_message(mle.CommandType.DATA_RESPONSE)
msg.assertMleMessageContainsTlv(mle.LinkMetricsReport)
self.assertTrue(self.nodes[LEADER].ping(self.nodes[SSED_1].get_rloc()))
if __name__ == '__main__':
unittest.main()