mirror of
https://github.com/espressif/openthread.git
synced 2026-07-31 16:17:47 +00:00
[link-metrics] add blocking CLI query command and enhance tests (#8108)
This commit updates CLI `linkmetrics query` command to allow it to be used in blocking mode (wait for response to query). This is then used in different tests to validate the `LinkMetrics` module behavior. In particular, `v1_2_test_single_probe` and `v1_2_LowPower_7_2_01_ForwardTrackingSeries` are updated to validate that the received query report message is correctly parsed.
This commit is contained in:
+31
-13
@@ -135,6 +135,9 @@ Interpreter::Interpreter(Instance *aInstance, otCliOutputCallback aCallback, voi
|
||||
#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
|
||||
@@ -3467,12 +3470,11 @@ void Interpreter::HandleLinkMetricsReport(const otIp6Address * aAddress,
|
||||
|
||||
void Interpreter::PrintLinkMetricsValue(const otLinkMetricsValues *aMetricsValues)
|
||||
{
|
||||
const char kLinkMetricsTypeCount[] = "(Count/Summation)";
|
||||
const char kLinkMetricsTypeAverage[] = "(Exponential Moving Average)";
|
||||
static const char kLinkMetricsTypeAverage[] = "(Exponential Moving Average)";
|
||||
|
||||
if (aMetricsValues->mMetrics.mPduCount)
|
||||
{
|
||||
OutputLine(" - PDU Counter: %d %s", aMetricsValues->mPduCountValue, kLinkMetricsTypeCount);
|
||||
OutputLine(" - PDU Counter: %d (Count/Summation)", aMetricsValues->mPduCountValue);
|
||||
}
|
||||
|
||||
if (aMetricsValues->mMetrics.mLqi)
|
||||
@@ -3506,6 +3508,12 @@ void Interpreter::HandleLinkMetricsReport(const otIp6Address * aAddress,
|
||||
{
|
||||
OutputLine("Link Metrics Report, status: %s", LinkMetricsStatusToStr(aStatus));
|
||||
}
|
||||
|
||||
if (mLinkMetricsQueryInProgress)
|
||||
{
|
||||
mLinkMetricsQueryInProgress = false;
|
||||
OutputResult(OT_ERROR_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress, uint8_t aStatus, void *aContext)
|
||||
@@ -3580,28 +3588,37 @@ template <> otError Interpreter::Process<Cmd("linkmetrics")>(Arg aArgs[])
|
||||
if (aArgs[0] == "query")
|
||||
{
|
||||
otIp6Address address;
|
||||
bool isSingle;
|
||||
bool blocking;
|
||||
uint8_t seriesId;
|
||||
otLinkMetrics linkMetrics;
|
||||
|
||||
SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
|
||||
|
||||
if (aArgs[2] == "single")
|
||||
{
|
||||
VerifyOrExit(!aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
||||
isSingle = true;
|
||||
SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[3]));
|
||||
error = otLinkMetricsQuery(GetInstancePtr(), &address, /* aSeriesId */ 0, &linkMetrics,
|
||||
&Interpreter::HandleLinkMetricsReport, this);
|
||||
}
|
||||
else if (aArgs[2] == "forward")
|
||||
{
|
||||
uint8_t seriesId;
|
||||
|
||||
isSingle = false;
|
||||
SuccessOrExit(error = aArgs[3].ParseAsUint8(seriesId));
|
||||
error = otLinkMetricsQuery(GetInstancePtr(), &address, seriesId, nullptr,
|
||||
&Interpreter::HandleLinkMetricsReport, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
error = OT_ERROR_INVALID_ARGS;
|
||||
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")
|
||||
@@ -3629,6 +3646,8 @@ otError Interpreter::ParseLinkMetricsFlags(otLinkMetrics &aLinkMetrics, const Ar
|
||||
{
|
||||
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++)
|
||||
@@ -3713,8 +3732,8 @@ otError Interpreter::ProcessLinkMetricsMgmt(Arg aArgs[])
|
||||
|
||||
if (!clear)
|
||||
{
|
||||
VerifyOrExit(!aArgs[4].IsEmpty() && aArgs[5].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
||||
SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[4]));
|
||||
VerifyOrExit(aArgs[5].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
||||
}
|
||||
|
||||
error = otLinkMetricsConfigForwardTrackingSeries(GetInstancePtr(), &address, seriesId, seriesFlags,
|
||||
@@ -3735,7 +3754,6 @@ otError Interpreter::ProcessLinkMetricsMgmt(Arg aArgs[])
|
||||
else if (aArgs[2] == "register")
|
||||
{
|
||||
enhAckFlags = OT_LINK_METRICS_ENH_ACK_REGISTER;
|
||||
VerifyOrExit(!aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
|
||||
SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[3]));
|
||||
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||
if (aArgs[4] == "r")
|
||||
|
||||
@@ -547,6 +547,10 @@ 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>()`
|
||||
|
||||
@@ -2861,15 +2861,38 @@ 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)
|
||||
def link_metrics_query_single_probe(self, dst_addr: str, linkmetrics_flags: str, block: str = ""):
|
||||
cmd = 'linkmetrics query %s single %s %s' % (dst_addr, linkmetrics_flags, block)
|
||||
self.send_command(cmd)
|
||||
self._expect_done()
|
||||
self.simulator.go(5)
|
||||
return self._parse_linkmetrics_query_result(self._expect_command_output())
|
||||
|
||||
def link_metrics_query_forward_tracking_series(self, dst_addr: str, series_id: int):
|
||||
cmd = 'linkmetrics query %s forward %d' % (dst_addr, series_id)
|
||||
def link_metrics_query_forward_tracking_series(self, dst_addr: str, series_id: int, block: str = ""):
|
||||
cmd = 'linkmetrics query %s forward %d %s' % (dst_addr, series_id, block)
|
||||
self.send_command(cmd)
|
||||
self._expect_done()
|
||||
self.simulator.go(5)
|
||||
return self._parse_linkmetrics_query_result(self._expect_command_output())
|
||||
|
||||
def _parse_linkmetrics_query_result(self, lines):
|
||||
"""Parse link metrics query result"""
|
||||
|
||||
# Exmaple of command output:
|
||||
# ['Received Link Metrics Report from: fe80:0:0:0:146e:a00:0:1',
|
||||
# '- PDU Counter: 1 (Count/Summation)',
|
||||
# '- LQI: 0 (Exponential Moving Average)',
|
||||
# '- Margin: 80 (dB) (Exponential Moving Average)',
|
||||
# '- RSSI: -20 (dBm) (Exponential Moving Average)']
|
||||
#
|
||||
# Or 'Link Metrics Report, status: {status}'
|
||||
|
||||
result = {}
|
||||
for line in lines:
|
||||
if line.startswith('- '):
|
||||
k, v = line[2:].split(': ')
|
||||
result[k] = v.split(' ')[0]
|
||||
elif line.startswith('Link Metrics Report, status: '):
|
||||
result['Status'] = line[29:]
|
||||
return result
|
||||
|
||||
def link_metrics_mgmt_req_enhanced_ack_based_probing(self,
|
||||
dst_addr: str,
|
||||
|
||||
@@ -111,9 +111,10 @@ class LowPower_7_2_01_ForwardTrackingSeries(thread_cert.TestCase):
|
||||
self.simulator.go(30)
|
||||
|
||||
# Step 7 - SED_1 sends an MLE Data Request to retrieve aggregated Forward Series Results
|
||||
self.nodes[SED_1].link_metrics_query_forward_tracking_series(leader_addr, SERIES_ID)
|
||||
result = self.nodes[SED_1].link_metrics_query_forward_tracking_series(leader_addr, SERIES_ID, 'block')
|
||||
self.assertIn("PDU Counter", result)
|
||||
|
||||
self.simulator.go(5)
|
||||
#self.simulator.go(5)
|
||||
|
||||
# Step 9 - SED_1 clears the Forward Tracking Series
|
||||
# Forward Series Flags = 0x00:
|
||||
@@ -144,9 +145,8 @@ class LowPower_7_2_01_ForwardTrackingSeries(thread_cert.TestCase):
|
||||
self.simulator.go(1)
|
||||
|
||||
# Step 19 - SSED_1 sends an MLE Data Request to retrieve aggregated Forward Series Results
|
||||
self.nodes[SSED_1].link_metrics_query_forward_tracking_series(leader_addr, SERIES_ID_2)
|
||||
|
||||
self.simulator.go(5)
|
||||
result = self.nodes[SSED_1].link_metrics_query_forward_tracking_series(leader_addr, SERIES_ID_2, 'block')
|
||||
self.assertIn("Margin", result)
|
||||
|
||||
# Step 21 - SSED_1 clears the Forward Series Link Metrics
|
||||
# Forward Series Flags = 0x00:
|
||||
@@ -157,9 +157,9 @@ class LowPower_7_2_01_ForwardTrackingSeries(thread_cert.TestCase):
|
||||
self.simulator.go(5)
|
||||
|
||||
# Step 23 - SSED_1 sends an MLE Data Request to retrieve aggregated Forward Series Results
|
||||
self.nodes[SSED_1].link_metrics_query_forward_tracking_series(leader_addr, SERIES_ID_2)
|
||||
|
||||
self.simulator.go(5)
|
||||
result = self.nodes[SSED_1].link_metrics_query_forward_tracking_series(leader_addr, SERIES_ID_2, 'block')
|
||||
self.assertIn('Status', result)
|
||||
self.assertEqual(result['Status'], 'Series ID not recognized')
|
||||
|
||||
def verify(self, pv):
|
||||
pkts = pv.pkts
|
||||
|
||||
@@ -70,40 +70,48 @@ class SSED_SingleProbe(thread_cert.TestCase):
|
||||
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)
|
||||
result = self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'p', 'block')
|
||||
self.assertIn('PDU Counter', result)
|
||||
self.assertEqual(len(result), 1)
|
||||
|
||||
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)
|
||||
result = self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'q', 'block')
|
||||
self.assertIn('LQI', result)
|
||||
self.assertEqual(len(result), 1)
|
||||
|
||||
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)
|
||||
result = self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'm', 'block')
|
||||
self.assertIn('Margin', result)
|
||||
self.assertEqual(len(result), 1)
|
||||
|
||||
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)
|
||||
result = self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'r', 'block')
|
||||
self.assertIn('RSSI', result)
|
||||
self.assertEqual(len(result), 1)
|
||||
|
||||
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)
|
||||
result = self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'pqmr', 'block')
|
||||
self.assertIn('PDU Counter', result)
|
||||
self.assertIn('LQI', result)
|
||||
self.assertIn('Margin', result)
|
||||
self.assertIn('RSSI', result)
|
||||
self.assertEqual(len(result), 4)
|
||||
|
||||
leader_messages = self.simulator.get_messages_sent_by(LEADER)
|
||||
msg = leader_messages.next_mle_message(mle.CommandType.DATA_RESPONSE)
|
||||
|
||||
Reference in New Issue
Block a user