[cli] make linkmetrics cli as a separate module (#9619)

This commit is contained in:
Li Cao
2023-11-22 01:48:27 +08:00
committed by GitHub
parent 30c94db4a4
commit 1d4e6571f9
6 changed files with 670 additions and 486 deletions
+2
View File
@@ -49,6 +49,8 @@ openthread_cli_sources = [
"cli_history.hpp",
"cli_joiner.cpp",
"cli_joiner.hpp",
"cli_link_metrics.cpp",
"cli_link_metrics.hpp",
"cli_mac_filter.cpp",
"cli_mac_filter.hpp",
"cli_network_data.cpp",
+1
View File
@@ -42,6 +42,7 @@ set(COMMON_SOURCES
cli_dns.cpp
cli_history.cpp
cli_joiner.cpp
cli_link_metrics.cpp
cli_mac_filter.cpp
cli_network_data.cpp
cli_output.cpp
+4 -447
View File
@@ -67,9 +67,6 @@
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
#include <openthread/backbone_router_ftd.h>
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
#include <openthread/link_metrics.h>
#endif
#endif
#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
#include <openthread/channel_manager.h>
@@ -148,12 +145,12 @@ Interpreter::Interpreter(Instance *aInstance, otCliOutputCallback aCallback, voi
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
, mHistory(aInstance, *this)
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
, mLinkMetrics(aInstance, *this)
#endif
#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
, mLocateInProgress(false)
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
, mLinkMetricsQueryInProgress(false)
#endif
#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
{
#if (OPENTHREAD_FTD || OPENTHREAD_MTD) && OPENTHREAD_CONFIG_CLI_REGISTER_IP6_RECV_CALLBACK
@@ -3673,241 +3670,8 @@ exit:
#endif // OPENTHREAD_FTD
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
void Interpreter::HandleLinkMetricsReport(const otIp6Address *aAddress,
const otLinkMetricsValues *aMetricsValues,
otLinkMetricsStatus aStatus,
void *aContext)
{
static_cast<Interpreter *>(aContext)->HandleLinkMetricsReport(aAddress, aMetricsValues, aStatus);
}
void Interpreter::PrintLinkMetricsValue(const otLinkMetricsValues *aMetricsValues)
{
static const char kLinkMetricsTypeAverage[] = "(Exponential Moving Average)";
if (aMetricsValues->mMetrics.mPduCount)
{
OutputLine(" - PDU Counter: %lu (Count/Summation)", ToUlong(aMetricsValues->mPduCountValue));
}
if (aMetricsValues->mMetrics.mLqi)
{
OutputLine(" - LQI: %u %s", aMetricsValues->mLqiValue, kLinkMetricsTypeAverage);
}
if (aMetricsValues->mMetrics.mLinkMargin)
{
OutputLine(" - Margin: %u (dB) %s", aMetricsValues->mLinkMarginValue, kLinkMetricsTypeAverage);
}
if (aMetricsValues->mMetrics.mRssi)
{
OutputLine(" - RSSI: %d (dBm) %s", aMetricsValues->mRssiValue, kLinkMetricsTypeAverage);
}
}
void Interpreter::HandleLinkMetricsReport(const otIp6Address *aAddress,
const otLinkMetricsValues *aMetricsValues,
otLinkMetricsStatus aStatus)
{
OutputFormat("Received Link Metrics Report from: ");
OutputIp6AddressLine(*aAddress);
if (aMetricsValues != nullptr)
{
PrintLinkMetricsValue(aMetricsValues);
}
else
{
OutputLine("Link Metrics Report, status: %s", LinkMetricsStatusToStr(aStatus));
}
if (mLinkMetricsQueryInProgress)
{
mLinkMetricsQueryInProgress = false;
OutputResult(OT_ERROR_NONE);
}
}
void Interpreter::HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress,
otLinkMetricsStatus aStatus,
void *aContext)
{
static_cast<Interpreter *>(aContext)->HandleLinkMetricsMgmtResponse(aAddress, aStatus);
}
void Interpreter::HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress, otLinkMetricsStatus aStatus)
{
OutputFormat("Received Link Metrics Management Response from: ");
OutputIp6AddressLine(*aAddress);
OutputLine("Status: %s", LinkMetricsStatusToStr(aStatus));
}
void Interpreter::HandleLinkMetricsEnhAckProbingIe(otShortAddress aShortAddress,
const otExtAddress *aExtAddress,
const otLinkMetricsValues *aMetricsValues,
void *aContext)
{
static_cast<Interpreter *>(aContext)->HandleLinkMetricsEnhAckProbingIe(aShortAddress, aExtAddress, aMetricsValues);
}
void Interpreter::HandleLinkMetricsEnhAckProbingIe(otShortAddress aShortAddress,
const otExtAddress *aExtAddress,
const otLinkMetricsValues *aMetricsValues)
{
OutputFormat("Received Link Metrics data in Enh Ack from neighbor, short address:0x%02x , extended address:",
aShortAddress);
OutputExtAddressLine(*aExtAddress);
if (aMetricsValues != nullptr)
{
PrintLinkMetricsValue(aMetricsValues);
}
}
const char *Interpreter::LinkMetricsStatusToStr(otLinkMetricsStatus aStatus)
{
static const char *const kStatusStrings[] = {
"Success", // (0) OT_LINK_METRICS_STATUS_SUCCESS
"Cannot support new series", // (1) OT_LINK_METRICS_STATUS_CANNOT_SUPPORT_NEW_SERIES
"Series ID already registered", // (2) OT_LINK_METRICS_STATUS_SERIESID_ALREADY_REGISTERED
"Series ID not recognized", // (3) OT_LINK_METRICS_STATUS_SERIESID_NOT_RECOGNIZED
"No matching series ID", // (4) OT_LINK_METRICS_STATUS_NO_MATCHING_FRAMES_RECEIVED
};
const char *str = "Unknown error";
static_assert(0 == OT_LINK_METRICS_STATUS_SUCCESS, "STATUS_SUCCESS is incorrect");
static_assert(1 == OT_LINK_METRICS_STATUS_CANNOT_SUPPORT_NEW_SERIES, "CANNOT_SUPPORT_NEW_SERIES is incorrect");
static_assert(2 == OT_LINK_METRICS_STATUS_SERIESID_ALREADY_REGISTERED, "SERIESID_ALREADY_REGISTERED is incorrect");
static_assert(3 == OT_LINK_METRICS_STATUS_SERIESID_NOT_RECOGNIZED, "SERIESID_NOT_RECOGNIZED is incorrect");
static_assert(4 == OT_LINK_METRICS_STATUS_NO_MATCHING_FRAMES_RECEIVED, "NO_MATCHING_FRAMES_RECEIVED is incorrect");
if (aStatus < OT_ARRAY_LENGTH(kStatusStrings))
{
str = kStatusStrings[aStatus];
}
else if (aStatus == OT_LINK_METRICS_STATUS_OTHER_ERROR)
{
str = "Other error";
}
return str;
}
template <> otError Interpreter::Process<Cmd("linkmetrics")>(Arg aArgs[])
{
otError error = OT_ERROR_INVALID_COMMAND;
if (aArgs[0] == "query")
{
otIp6Address address;
bool isSingle;
bool blocking;
uint8_t seriesId;
otLinkMetrics linkMetrics;
SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
/**
* @cli linkmetrics query single
* @code
* 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)
* @endcode
* @cparam linkmetrics query @ca{peer-ipaddr} single [@ca{pqmr}]
* - `peer-ipaddr`: Peer address.
* - [`p`, `q`, `m`, and `r`] map to #otLinkMetrics.
* - `p`: Layer 2 Number of PDUs received.
* - `q`: Layer 2 LQI.
* - `m`: Link Margin.
* - `r`: RSSI.
* @par
* Perform a Link Metrics query (Single Probe).
* @sa otLinkMetricsQuery
*/
if (aArgs[2] == "single")
{
isSingle = true;
SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[3]));
}
/**
* @cli linkmetrics query forward
* @code
* linkmetrics query fe80:0:0:0:3092:f334:1455:1ad2 forward 1
* Done
* > Received Link Metrics Report from: fe80:0:0:0:3092:f334:1455:1ad2
* - PDU Counter: 2 (Count/Summation)
* - LQI: 76 (Exponential Moving Average)
* - Margin: 82 (dB) (Exponential Moving Average)
* - RSSI: -18 (dBm) (Exponential Moving Average)
* @endcode
* @cparam linkmetrics query @ca{peer-ipaddr} forward @ca{series-id}
* - `peer-ipaddr`: Peer address.
* - `series-id`: The Series ID.
* @par
* Perform a Link Metrics query (Forward Tracking Series).
* @sa otLinkMetricsQuery
*/
else if (aArgs[2] == "forward")
{
isSingle = false;
SuccessOrExit(error = aArgs[3].ParseAsUint8(seriesId));
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
blocking = (aArgs[4] == "block");
SuccessOrExit(error = otLinkMetricsQuery(GetInstancePtr(), &address, isSingle ? 0 : seriesId,
isSingle ? &linkMetrics : nullptr, HandleLinkMetricsReport, this));
if (blocking)
{
mLinkMetricsQueryInProgress = true;
error = OT_ERROR_PENDING;
}
}
else if (aArgs[0] == "mgmt")
{
error = ProcessLinkMetricsMgmt(aArgs + 1);
}
/**
* @cli linkmetrics probe
* @code
* linkmetrics probe fe80:0:0:0:3092:f334:1455:1ad2 1 10
* Done
* @endcode
* @cparam linkmetrics probe @ca{peer-ipaddr} @ca{series-id} @ca{length}
* - `peer-ipaddr`: Peer address.
* - `series-id`: The Series ID for which this Probe message targets.
* - `length`: The length of the Probe message. A valid range is [0, 64].
* @par api_copy
* #otLinkMetricsSendLinkProbe
*/
else if (aArgs[0] == "probe")
{
otIp6Address address;
uint8_t seriesId;
uint8_t length;
SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
SuccessOrExit(error = aArgs[2].ParseAsUint8(seriesId));
SuccessOrExit(error = aArgs[3].ParseAsUint8(length));
error = otLinkMetricsSendLinkProbe(GetInstancePtr(), &address, seriesId, length);
}
exit:
return error;
}
template <> otError Interpreter::Process<Cmd("linkmetrics")>(Arg aArgs[]) { return mLinkMetrics.Process(aArgs); }
#if OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
template <> otError Interpreter::Process<Cmd("linkmetricsmgr")>(Arg aArgs[])
@@ -3975,213 +3739,6 @@ exit:
}
#endif // OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE
otError Interpreter::ParseLinkMetricsFlags(otLinkMetrics &aLinkMetrics, const Arg &aFlags)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(!aFlags.IsEmpty(), error = OT_ERROR_INVALID_ARGS);
memset(&aLinkMetrics, 0, sizeof(aLinkMetrics));
for (const char *arg = aFlags.GetCString(); *arg != '\0'; arg++)
{
switch (*arg)
{
case 'p':
aLinkMetrics.mPduCount = true;
break;
case 'q':
aLinkMetrics.mLqi = true;
break;
case 'm':
aLinkMetrics.mLinkMargin = true;
break;
case 'r':
aLinkMetrics.mRssi = true;
break;
default:
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
}
exit:
return error;
}
otError Interpreter::ProcessLinkMetricsMgmt(Arg aArgs[])
{
otError error;
otIp6Address address;
otLinkMetricsSeriesFlags seriesFlags;
bool clear = false;
SuccessOrExit(error = aArgs[0].ParseAsIp6Address(address));
memset(&seriesFlags, 0, sizeof(otLinkMetricsSeriesFlags));
/**
* @cli linkmetrics mgmt forward
* @code
* linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 forward 1 dra pqmr
* Done
* > Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2
* Status: SUCCESS
* @endcode
* @cparam linkmetrics mgmt @ca{peer-ipaddr} forward @ca{series-id} [@ca{ldraX}][@ca{pqmr}]
* - `peer-ipaddr`: Peer address.
* - `series-id`: The Series ID.
* - [`l`, `d`, `r`, and `a`] map to #otLinkMetricsSeriesFlags. `X` represents none of the
* `otLinkMetricsSeriesFlags`, and stops the accounting and removes the series.
* - `l`: MLE Link Probe.
* - `d`: MAC Data.
* - `r`: MAC Data Request.
* - `a`: MAC Ack.
* - `X`: Can only be used without any other flags.
* - [`p`, `q`, `m`, and `r`] map to #otLinkMetricsValues.
* - `p`: Layer 2 Number of PDUs received.
* - `q`: Layer 2 LQI.
* - `m`: Link Margin.
* - `r`: RSSI.
* @par api_copy
* #otLinkMetricsConfigForwardTrackingSeries
*/
if (aArgs[1] == "forward")
{
uint8_t seriesId;
otLinkMetrics linkMetrics;
memset(&linkMetrics, 0, sizeof(otLinkMetrics));
SuccessOrExit(error = aArgs[2].ParseAsUint8(seriesId));
VerifyOrExit(!aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
for (const char *arg = aArgs[3].GetCString(); *arg != '\0'; arg++)
{
switch (*arg)
{
case 'l':
seriesFlags.mLinkProbe = true;
break;
case 'd':
seriesFlags.mMacData = true;
break;
case 'r':
seriesFlags.mMacDataRequest = true;
break;
case 'a':
seriesFlags.mMacAck = true;
break;
case 'X':
VerifyOrExit(arg == aArgs[3].GetCString() && *(arg + 1) == '\0' && aArgs[4].IsEmpty(),
error = OT_ERROR_INVALID_ARGS); // Ensure the flags only contain 'X'
clear = true;
break;
default:
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
}
if (!clear)
{
SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[4]));
VerifyOrExit(aArgs[5].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
}
error = otLinkMetricsConfigForwardTrackingSeries(GetInstancePtr(), &address, seriesId, seriesFlags,
clear ? nullptr : &linkMetrics,
&Interpreter::HandleLinkMetricsMgmtResponse, this);
}
else if (aArgs[1] == "enhanced-ack")
{
otLinkMetricsEnhAckFlags enhAckFlags;
otLinkMetrics linkMetrics;
otLinkMetrics *pLinkMetrics = &linkMetrics;
/**
* @cli linkmetrics mgmt enhanced-ack clear
* @code
* linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack clear
* Done
* > Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2
* Status: Success
* @endcode
* @cparam linkmetrics mgmt @ca{peer-ipaddr} enhanced-ack clear
* `peer-ipaddr` should be the Link Local address of the neighboring device.
* @par
* Sends a Link Metrics Management Request to clear an Enhanced-ACK Based Probing.
* @sa otLinkMetricsConfigEnhAckProbing
*/
if (aArgs[2] == "clear")
{
enhAckFlags = OT_LINK_METRICS_ENH_ACK_CLEAR;
pLinkMetrics = nullptr;
}
/**
* @cli linkmetrics mgmt enhanced-ack register
* @code
* linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack register qm
* Done
* > Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2
* Status: Success
* @endcode
* @code
* > linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack register qm r
* Done
* > Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2
* Status: Cannot support new series
* @endcode
* @cparam linkmetrics mgmt @ca{peer-ipaddr} enhanced-ack register [@ca{qmr}][@ca{r}]
* [`q`, `m`, and `r`] map to #otLinkMetricsValues. Per spec 4.11.3.4.4.6, you can
* only use a maximum of two options at once, for example `q`, or `qm`.
* - `q`: Layer 2 LQI.
* - `m`: Link Margin.
* - `r`: RSSI.
* .
* The additional `r` is optional and only used for reference devices. When this option
* is specified, Type/Average Enum of each Type Id Flags is set to reserved. This is
* used to verify that the Probing Subject correctly handles invalid Type Id Flags, and
* only available when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled.
* @par
* Sends a Link Metrics Management Request to register an Enhanced-ACK Based Probing.
* @sa otLinkMetricsConfigEnhAckProbing
*/
else if (aArgs[2] == "register")
{
enhAckFlags = OT_LINK_METRICS_ENH_ACK_REGISTER;
SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[3]));
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
if (aArgs[4] == "r")
{
linkMetrics.mReserved = true;
}
#endif
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
error = otLinkMetricsConfigEnhAckProbing(GetInstancePtr(), &address, enhAckFlags, pLinkMetrics,
&Interpreter::HandleLinkMetricsMgmtResponse, this,
&Interpreter::HandleLinkMetricsEnhAckProbingIe, this);
}
else
{
error = OT_ERROR_INVALID_ARGS;
}
exit:
return error;
}
#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
+5 -39
View File
@@ -65,6 +65,7 @@
#include "cli/cli_dns.hpp"
#include "cli/cli_history.hpp"
#include "cli/cli_joiner.hpp"
#include "cli/cli_link_metrics.hpp"
#include "cli/cli_mac_filter.hpp"
#include "cli/cli_network_data.hpp"
#include "cli/cli_output.hpp"
@@ -113,6 +114,7 @@ class Interpreter : public OutputImplementer, public Output
friend class Commissioner;
friend class Dns;
friend class Joiner;
friend class LinkMetrics;
friend class NetworkData;
friend class SrpClient;
friend class SrpServer;
@@ -412,12 +414,6 @@ private:
#if OPENTHREAD_FTD
void OutputEidCacheEntry(const otCacheEntryInfo &aEntry);
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
otError ProcessLinkMetricsQuery(Arg aArgs[]);
otError ProcessLinkMetricsMgmt(Arg aArgs[]);
otError ProcessLinkMetricsProbe(Arg aArgs[]);
otError ParseLinkMetricsFlags(otLinkMetrics &aLinkMetrics, const Arg &aFlags);
#endif
#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
static void HandleLocateResult(void *aContext,
otError aError,
@@ -507,35 +503,6 @@ private:
#if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
void HandleSntpResponse(uint64_t aTime, otError aResult);
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
void PrintLinkMetricsValue(const otLinkMetricsValues *aMetricsValues);
static void HandleLinkMetricsReport(const otIp6Address *aAddress,
const otLinkMetricsValues *aMetricsValues,
otLinkMetricsStatus aStatus,
void *aContext);
void HandleLinkMetricsReport(const otIp6Address *aAddress,
const otLinkMetricsValues *aMetricsValues,
otLinkMetricsStatus aStatus);
static void HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress,
otLinkMetricsStatus aStatus,
void *aContext);
void HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress, otLinkMetricsStatus aStatus);
static void HandleLinkMetricsEnhAckProbingIe(otShortAddress aShortAddress,
const otExtAddress *aExtAddress,
const otLinkMetricsValues *aMetricsValues,
void *aContext);
void HandleLinkMetricsEnhAckProbingIe(otShortAddress aShortAddress,
const otExtAddress *aExtAddress,
const otLinkMetricsValues *aMetricsValues);
const char *LinkMetricsStatusToStr(otLinkMetricsStatus aStatus);
#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
static void HandleDetachGracefullyResult(void *aContext);
void HandleDetachGracefullyResult(void);
@@ -624,6 +591,9 @@ private:
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
History mHistory;
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
LinkMetrics mLinkMetrics;
#endif
#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
@@ -632,10 +602,6 @@ private:
#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
bool mLocateInProgress : 1;
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
bool mLinkMetricsQueryInProgress : 1;
#endif
};
// Specializations of `FormatStringFor<ValueType>()`
+530
View File
@@ -0,0 +1,530 @@
/*
* 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 implements the CLI interpreter for Link Metrics function.
*/
#include "cli_link_metrics.hpp"
#include <openthread/link_metrics.h>
#include "cli/cli.hpp"
#include "cli/cli_output.hpp"
#include "common/code_utils.hpp"
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
namespace ot {
namespace Cli {
LinkMetrics::LinkMetrics(otInstance *aInstance, OutputImplementer &aOutputImplementer)
: Output(aInstance, aOutputImplementer)
, mLinkMetricsQueryInProgress(false)
{
}
template <> otError LinkMetrics::Process<Cmd("query")>(Arg aArgs[])
{
otError error = OT_ERROR_NONE;
otIp6Address address;
bool isSingle;
bool blocking;
uint8_t seriesId;
otLinkMetrics linkMetrics;
SuccessOrExit(error = aArgs[0].ParseAsIp6Address(address));
/**
* @cli linkmetrics query single
* @code
* 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)
* @endcode
* @cparam linkmetrics query @ca{peer-ipaddr} single [@ca{pqmr}]
* - `peer-ipaddr`: Peer address.
* - [`p`, `q`, `m`, and `r`] map to #otLinkMetrics.
* - `p`: Layer 2 Number of PDUs received.
* - `q`: Layer 2 LQI.
* - `m`: Link Margin.
* - `r`: RSSI.
* @par
* Perform a Link Metrics query (Single Probe).
* @sa otLinkMetricsQuery
*/
if (aArgs[1] == "single")
{
isSingle = true;
SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[2]));
}
/**
* @cli linkmetrics query forward
* @code
* linkmetrics query fe80:0:0:0:3092:f334:1455:1ad2 forward 1
* Done
* > Received Link Metrics Report from: fe80:0:0:0:3092:f334:1455:1ad2
* - PDU Counter: 2 (Count/Summation)
* - LQI: 76 (Exponential Moving Average)
* - Margin: 82 (dB) (Exponential Moving Average)
* - RSSI: -18 (dBm) (Exponential Moving Average)
* @endcode
* @cparam linkmetrics query @ca{peer-ipaddr} forward @ca{series-id}
* - `peer-ipaddr`: Peer address.
* - `series-id`: The Series ID.
* @par
* Perform a Link Metrics query (Forward Tracking Series).
* @sa otLinkMetricsQuery
*/
else if (aArgs[1] == "forward")
{
isSingle = false;
SuccessOrExit(error = aArgs[2].ParseAsUint8(seriesId));
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
blocking = (aArgs[3] == "block");
SuccessOrExit(error = otLinkMetricsQuery(GetInstancePtr(), &address, isSingle ? 0 : seriesId,
isSingle ? &linkMetrics : nullptr, HandleLinkMetricsReport, this));
if (blocking)
{
mLinkMetricsQueryInProgress = true;
error = OT_ERROR_PENDING;
}
exit:
return error;
}
template <> otError LinkMetrics::Process<Cmd("mgmt")>(Arg aArgs[])
{
otError error;
otIp6Address address;
otLinkMetricsSeriesFlags seriesFlags;
bool clear = false;
SuccessOrExit(error = aArgs[0].ParseAsIp6Address(address));
memset(&seriesFlags, 0, sizeof(otLinkMetricsSeriesFlags));
/**
* @cli linkmetrics mgmt forward
* @code
* linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 forward 1 dra pqmr
* Done
* > Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2
* Status: SUCCESS
* @endcode
* @cparam linkmetrics mgmt @ca{peer-ipaddr} forward @ca{series-id} [@ca{ldraX}][@ca{pqmr}]
* - `peer-ipaddr`: Peer address.
* - `series-id`: The Series ID.
* - [`l`, `d`, `r`, and `a`] map to #otLinkMetricsSeriesFlags. `X` represents none of the
* `otLinkMetricsSeriesFlags`, and stops the accounting and removes the series.
* - `l`: MLE Link Probe.
* - `d`: MAC Data.
* - `r`: MAC Data Request.
* - `a`: MAC Ack.
* - `X`: Can only be used without any other flags.
* - [`p`, `q`, `m`, and `r`] map to #otLinkMetricsValues.
* - `p`: Layer 2 Number of PDUs received.
* - `q`: Layer 2 LQI.
* - `m`: Link Margin.
* - `r`: RSSI.
* @par api_copy
* #otLinkMetricsConfigForwardTrackingSeries
*/
if (aArgs[1] == "forward")
{
uint8_t seriesId;
otLinkMetrics linkMetrics;
memset(&linkMetrics, 0, sizeof(otLinkMetrics));
SuccessOrExit(error = aArgs[2].ParseAsUint8(seriesId));
VerifyOrExit(!aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
for (const char *arg = aArgs[3].GetCString(); *arg != '\0'; arg++)
{
switch (*arg)
{
case 'l':
seriesFlags.mLinkProbe = true;
break;
case 'd':
seriesFlags.mMacData = true;
break;
case 'r':
seriesFlags.mMacDataRequest = true;
break;
case 'a':
seriesFlags.mMacAck = true;
break;
case 'X':
VerifyOrExit(arg == aArgs[3].GetCString() && *(arg + 1) == '\0' && aArgs[4].IsEmpty(),
error = OT_ERROR_INVALID_ARGS); // Ensure the flags only contain 'X'
clear = true;
break;
default:
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
}
if (!clear)
{
SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[4]));
VerifyOrExit(aArgs[5].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
}
error = otLinkMetricsConfigForwardTrackingSeries(GetInstancePtr(), &address, seriesId, seriesFlags,
clear ? nullptr : &linkMetrics,
&LinkMetrics::HandleLinkMetricsMgmtResponse, this);
}
else if (aArgs[1] == "enhanced-ack")
{
otLinkMetricsEnhAckFlags enhAckFlags;
otLinkMetrics linkMetrics;
otLinkMetrics *pLinkMetrics = &linkMetrics;
/**
* @cli linkmetrics mgmt enhanced-ack clear
* @code
* linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack clear
* Done
* > Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2
* Status: Success
* @endcode
* @cparam linkmetrics mgmt @ca{peer-ipaddr} enhanced-ack clear
* `peer-ipaddr` should be the Link Local address of the neighboring device.
* @par
* Sends a Link Metrics Management Request to clear an Enhanced-ACK Based Probing.
* @sa otLinkMetricsConfigEnhAckProbing
*/
if (aArgs[2] == "clear")
{
enhAckFlags = OT_LINK_METRICS_ENH_ACK_CLEAR;
pLinkMetrics = nullptr;
}
/**
* @cli linkmetrics mgmt enhanced-ack register
* @code
* linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack register qm
* Done
* > Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2
* Status: Success
* @endcode
* @code
* > linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack register qm r
* Done
* > Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2
* Status: Cannot support new series
* @endcode
* @cparam linkmetrics mgmt @ca{peer-ipaddr} enhanced-ack register [@ca{qmr}][@ca{r}]
* [`q`, `m`, and `r`] map to #otLinkMetricsValues. Per spec 4.11.3.4.4.6, you can
* only use a maximum of two options at once, for example `q`, or `qm`.
* - `q`: Layer 2 LQI.
* - `m`: Link Margin.
* - `r`: RSSI.
* .
* The additional `r` is optional and only used for reference devices. When this option
* is specified, Type/Average Enum of each Type Id Flags is set to reserved. This is
* used to verify that the Probing Subject correctly handles invalid Type Id Flags, and
* only available when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled.
* @par
* Sends a Link Metrics Management Request to register an Enhanced-ACK Based Probing.
* @sa otLinkMetricsConfigEnhAckProbing
*/
else if (aArgs[2] == "register")
{
enhAckFlags = OT_LINK_METRICS_ENH_ACK_REGISTER;
SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[3]));
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
if (aArgs[4] == "r")
{
linkMetrics.mReserved = true;
}
#endif
}
else
{
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
error = otLinkMetricsConfigEnhAckProbing(GetInstancePtr(), &address, enhAckFlags, pLinkMetrics,
&LinkMetrics::HandleLinkMetricsMgmtResponse, this,
&LinkMetrics::HandleLinkMetricsEnhAckProbingIe, this);
}
else
{
error = OT_ERROR_INVALID_ARGS;
}
exit:
return error;
}
template <> otError LinkMetrics::Process<Cmd("probe")>(Arg aArgs[])
{
/**
* @cli linkmetrics probe
* @code
* linkmetrics probe fe80:0:0:0:3092:f334:1455:1ad2 1 10
* Done
* @endcode
* @cparam linkmetrics probe @ca{peer-ipaddr} @ca{series-id} @ca{length}
* - `peer-ipaddr`: Peer address.
* - `series-id`: The Series ID for which this Probe message targets.
* - `length`: The length of the Probe message. A valid range is [0, 64].
* @par api_copy
* #otLinkMetricsSendLinkProbe
*/
otError error = OT_ERROR_NONE;
otIp6Address address;
uint8_t seriesId;
uint8_t length;
SuccessOrExit(error = aArgs[0].ParseAsIp6Address(address));
SuccessOrExit(error = aArgs[1].ParseAsUint8(seriesId));
SuccessOrExit(error = aArgs[2].ParseAsUint8(length));
error = otLinkMetricsSendLinkProbe(GetInstancePtr(), &address, seriesId, length);
exit:
return error;
}
otError LinkMetrics::Process(Arg aArgs[])
{
#define CmdEntry(aCommandString) \
{ \
aCommandString, &LinkMetrics::Process<Cmd(aCommandString)> \
}
static constexpr Command kCommands[] = {
CmdEntry("mgmt"),
CmdEntry("probe"),
CmdEntry("query"),
};
static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
otError error = OT_ERROR_INVALID_COMMAND;
const Command *command;
if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
{
OutputCommandTable(kCommands);
ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
}
command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
VerifyOrExit(command != nullptr);
error = (this->*command->mHandler)(aArgs + 1);
exit:
return error;
}
otError LinkMetrics::ParseLinkMetricsFlags(otLinkMetrics &aLinkMetrics, const Arg &aFlags)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(!aFlags.IsEmpty(), error = OT_ERROR_INVALID_ARGS);
memset(&aLinkMetrics, 0, sizeof(aLinkMetrics));
for (const char *arg = aFlags.GetCString(); *arg != '\0'; arg++)
{
switch (*arg)
{
case 'p':
aLinkMetrics.mPduCount = true;
break;
case 'q':
aLinkMetrics.mLqi = true;
break;
case 'm':
aLinkMetrics.mLinkMargin = true;
break;
case 'r':
aLinkMetrics.mRssi = true;
break;
default:
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
}
exit:
return error;
}
void LinkMetrics::HandleLinkMetricsReport(const otIp6Address *aAddress,
const otLinkMetricsValues *aMetricsValues,
otLinkMetricsStatus aStatus,
void *aContext)
{
static_cast<LinkMetrics *>(aContext)->HandleLinkMetricsReport(aAddress, aMetricsValues, aStatus);
}
void LinkMetrics::PrintLinkMetricsValue(const otLinkMetricsValues *aMetricsValues)
{
static const char kLinkMetricsTypeAverage[] = "(Exponential Moving Average)";
if (aMetricsValues->mMetrics.mPduCount)
{
OutputLine(" - PDU Counter: %lu (Count/Summation)", ToUlong(aMetricsValues->mPduCountValue));
}
if (aMetricsValues->mMetrics.mLqi)
{
OutputLine(" - LQI: %u %s", aMetricsValues->mLqiValue, kLinkMetricsTypeAverage);
}
if (aMetricsValues->mMetrics.mLinkMargin)
{
OutputLine(" - Margin: %u (dB) %s", aMetricsValues->mLinkMarginValue, kLinkMetricsTypeAverage);
}
if (aMetricsValues->mMetrics.mRssi)
{
OutputLine(" - RSSI: %d (dBm) %s", aMetricsValues->mRssiValue, kLinkMetricsTypeAverage);
}
}
void LinkMetrics::HandleLinkMetricsReport(const otIp6Address *aAddress,
const otLinkMetricsValues *aMetricsValues,
otLinkMetricsStatus aStatus)
{
OutputFormat("Received Link Metrics Report from: ");
OutputIp6AddressLine(*aAddress);
if (aMetricsValues != nullptr)
{
PrintLinkMetricsValue(aMetricsValues);
}
else
{
OutputLine("Link Metrics Report, status: %s", LinkMetricsStatusToStr(aStatus));
}
if (mLinkMetricsQueryInProgress)
{
mLinkMetricsQueryInProgress = false;
OutputResult(OT_ERROR_NONE);
}
}
void LinkMetrics::HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress,
otLinkMetricsStatus aStatus,
void *aContext)
{
static_cast<LinkMetrics *>(aContext)->HandleLinkMetricsMgmtResponse(aAddress, aStatus);
}
void LinkMetrics::HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress, otLinkMetricsStatus aStatus)
{
OutputFormat("Received Link Metrics Management Response from: ");
OutputIp6AddressLine(*aAddress);
OutputLine("Status: %s", LinkMetricsStatusToStr(aStatus));
}
void LinkMetrics::HandleLinkMetricsEnhAckProbingIe(otShortAddress aShortAddress,
const otExtAddress *aExtAddress,
const otLinkMetricsValues *aMetricsValues,
void *aContext)
{
static_cast<LinkMetrics *>(aContext)->HandleLinkMetricsEnhAckProbingIe(aShortAddress, aExtAddress, aMetricsValues);
}
void LinkMetrics::HandleLinkMetricsEnhAckProbingIe(otShortAddress aShortAddress,
const otExtAddress *aExtAddress,
const otLinkMetricsValues *aMetricsValues)
{
OutputFormat("Received Link Metrics data in Enh Ack from neighbor, short address:0x%02x , extended address:",
aShortAddress);
OutputExtAddressLine(*aExtAddress);
if (aMetricsValues != nullptr)
{
PrintLinkMetricsValue(aMetricsValues);
}
}
const char *LinkMetrics::LinkMetricsStatusToStr(otLinkMetricsStatus aStatus)
{
static const char *const kStatusStrings[] = {
"Success", // (0) OT_LINK_METRICS_STATUS_SUCCESS
"Cannot support new series", // (1) OT_LINK_METRICS_STATUS_CANNOT_SUPPORT_NEW_SERIES
"Series ID already registered", // (2) OT_LINK_METRICS_STATUS_SERIESID_ALREADY_REGISTERED
"Series ID not recognized", // (3) OT_LINK_METRICS_STATUS_SERIESID_NOT_RECOGNIZED
"No matching series ID", // (4) OT_LINK_METRICS_STATUS_NO_MATCHING_FRAMES_RECEIVED
};
const char *str = "Unknown error";
static_assert(0 == OT_LINK_METRICS_STATUS_SUCCESS, "STATUS_SUCCESS is incorrect");
static_assert(1 == OT_LINK_METRICS_STATUS_CANNOT_SUPPORT_NEW_SERIES, "CANNOT_SUPPORT_NEW_SERIES is incorrect");
static_assert(2 == OT_LINK_METRICS_STATUS_SERIESID_ALREADY_REGISTERED, "SERIESID_ALREADY_REGISTERED is incorrect");
static_assert(3 == OT_LINK_METRICS_STATUS_SERIESID_NOT_RECOGNIZED, "SERIESID_NOT_RECOGNIZED is incorrect");
static_assert(4 == OT_LINK_METRICS_STATUS_NO_MATCHING_FRAMES_RECEIVED, "NO_MATCHING_FRAMES_RECEIVED is incorrect");
if (aStatus < OT_ARRAY_LENGTH(kStatusStrings))
{
str = kStatusStrings[aStatus];
}
else if (aStatus == OT_LINK_METRICS_STATUS_OTHER_ERROR)
{
str = "Other error";
}
return str;
}
void LinkMetrics::OutputResult(otError aError) { Interpreter::GetInterpreter().OutputResult(aError); }
} // namespace Cli
} // namespace ot
#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
+128
View File
@@ -0,0 +1,128 @@
/*
* 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 contains definitions for the CLI interpreter for Link Metrics function.
*/
#ifndef CLI_LINK_METRICS_HPP_
#define CLI_LINK_METRICS_HPP_
#include "openthread-core-config.h"
#include <openthread/link_metrics.h>
#include "cli/cli_output.hpp"
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
namespace ot {
namespace Cli {
/**
* Implements the Link Metrics CLI interpreter.
*
*/
class LinkMetrics : private Output
{
public:
typedef Utils::CmdLineParser::Arg Arg;
/**
* Constructor
*
* @param[in] aInstance The OpenThread Instance.
* @param[in] aOutputImplementer An `OutputImplementer`.
*
*/
LinkMetrics(otInstance *aInstance, OutputImplementer &aOutputImplementer);
/**
* Processes a CLI sub-command.
*
* @param[in] aArgs An array of command line arguments.
*
* @retval OT_ERROR_NONE Successfully executed the CLI command.
* @retval OT_ERROR_PENDING The CLI command was successfully started but final result is pending.
* @retval OT_ERROR_INVALID_COMMAND Invalid or unknown CLI command.
* @retval OT_ERROR_INVALID_ARGS Invalid arguments.
* @retval ... Error during execution of the CLI command.
*
*/
otError Process(Arg aArgs[]);
private:
static constexpr uint8_t kIndentSize = 4;
using Command = CommandEntry<LinkMetrics>;
template <CommandId kCommandId> otError Process(Arg aArgs[]);
otError ParseLinkMetricsFlags(otLinkMetrics &aLinkMetrics, const Arg &aFlags);
void PrintLinkMetricsValue(const otLinkMetricsValues *aMetricsValues);
static void HandleLinkMetricsReport(const otIp6Address *aAddress,
const otLinkMetricsValues *aMetricsValues,
otLinkMetricsStatus aStatus,
void *aContext);
void HandleLinkMetricsReport(const otIp6Address *aAddress,
const otLinkMetricsValues *aMetricsValues,
otLinkMetricsStatus aStatus);
static void HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress,
otLinkMetricsStatus aStatus,
void *aContext);
void HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress, otLinkMetricsStatus aStatus);
static void HandleLinkMetricsEnhAckProbingIe(otShortAddress aShortAddress,
const otExtAddress *aExtAddress,
const otLinkMetricsValues *aMetricsValues,
void *aContext);
void HandleLinkMetricsEnhAckProbingIe(otShortAddress aShortAddress,
const otExtAddress *aExtAddress,
const otLinkMetricsValues *aMetricsValues);
const char *LinkMetricsStatusToStr(otLinkMetricsStatus aStatus);
void OutputResult(otError aError);
bool mLinkMetricsQueryInProgress;
};
} // namespace Cli
} // namespace ot
#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
#endif // CLI_LINK_METRICS_HPP_