mirror of
https://github.com/espressif/openthread.git
synced 2026-08-11 13:17:48 +00:00
[thci] add Thread 1.2 Certification fixes (#7122)
Several fixes for Thread 1.2 Certification, including: - getMAC - setPollingRate - joinCommissioned - MGMT_ACTIVE_SET - setCSLtout - LinkMetricsMgmtReq - send_udp - setCSLsuspension - set_max_addrs_per_child - getMlrLogs - mldv2_query - mdns_query - powerDown - powerUp
This commit is contained in:
@@ -78,6 +78,7 @@ if TESTHARNESS_VERSION == TESTHARNESS_1_2:
|
||||
from IThci import IThci
|
||||
|
||||
ZEPHYR_PREFIX = 'ot '
|
||||
"""CLI prefix used for OpenThread commands in Zephyr systems"""
|
||||
|
||||
LINESEPX = re.compile(r'\r\n|\n')
|
||||
"""regex: used to split lines"""
|
||||
@@ -956,7 +957,7 @@ class OpenThreadTHCI(object):
|
||||
macAddr64 = self.__executeCommand('eui64')[0]
|
||||
elif bType == MacType.HashMac:
|
||||
macAddr64 = self.__executeCommand('joiner id')[0]
|
||||
elif TESTHARNESS_VERSION == TESTHARNESS_1_2 and bType == MacType.EthMac:
|
||||
elif TESTHARNESS_VERSION == TESTHARNESS_1_2 and bType == MacType.EthMac and self.IsBorderRouter:
|
||||
return self._deviceGetEtherMac()
|
||||
else:
|
||||
macAddr64 = self.__executeCommand('extaddr')[0]
|
||||
@@ -1233,6 +1234,8 @@ class OpenThreadTHCI(object):
|
||||
print('join as SSED')
|
||||
mode = '-'
|
||||
self.setCSLperiod(self.cslPeriod)
|
||||
self.setCSLtout(self.ssedTimeout)
|
||||
self.setCSLsuspension(False)
|
||||
elif eRoleId == Thread_Device_Role.EndDevice:
|
||||
print('join as end device')
|
||||
mode = 'rn'
|
||||
@@ -1549,6 +1552,7 @@ class OpenThreadTHCI(object):
|
||||
self.activetimestamp = ModuleHelper.Default_ActiveTimestamp
|
||||
# self.sedPollingRate = ModuleHelper.Default_Harness_SED_Polling_Rate
|
||||
self.__sedPollPeriod = 3 * 1000 # in milliseconds
|
||||
self.ssedTimeout = 30 # in seconds
|
||||
self.cslPeriod = 500 # in milliseconds
|
||||
self.deviceRole = None
|
||||
self.provisioningUrl = ''
|
||||
@@ -1618,6 +1622,10 @@ class OpenThreadTHCI(object):
|
||||
print(iPollingRate)
|
||||
|
||||
if self.__sedPollPeriod != iPollingRate:
|
||||
if not iPollingRate:
|
||||
iPollingRate = 0xFFFF # T5.2.1, disable polling
|
||||
elif iPollingRate < 1:
|
||||
iPollingRate = 1 # T9.2.13
|
||||
self.__sedPollPeriod = iPollingRate
|
||||
|
||||
# apply immediately
|
||||
@@ -2473,6 +2481,13 @@ class OpenThreadTHCI(object):
|
||||
False: fail to start joiner
|
||||
"""
|
||||
self.log("joinCommissioned on channel %s", self.getChannel())
|
||||
|
||||
if self.deviceRole in [
|
||||
Thread_Device_Role.Leader,
|
||||
Thread_Device_Role.Router,
|
||||
Thread_Device_Role.REED,
|
||||
]:
|
||||
self.__setRouterSelectionJitter(1)
|
||||
self.__executeCommand('ifconfig up')
|
||||
strPSKd = self.__normalizePSKd(strPSKd)
|
||||
cmd = 'joiner start %s %s' % (strPSKd, self.provisioningUrl)
|
||||
@@ -2493,8 +2508,11 @@ class OpenThreadTHCI(object):
|
||||
|
||||
self.sleep(1)
|
||||
|
||||
self.setMAC(self.mac)
|
||||
self.__executeCommand('thread start')
|
||||
self.sleep(30)
|
||||
self.wait_for_attach_to_the_network(expected_role=self.deviceRole,
|
||||
timeout=self.NETWORK_ATTACHMENT_TIMEOUT,
|
||||
raise_assert=True)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@@ -2509,6 +2527,7 @@ class OpenThreadTHCI(object):
|
||||
rawLogs = self.logThread.get()
|
||||
ProcessedLogs = []
|
||||
payload = []
|
||||
|
||||
while not rawLogs.empty():
|
||||
rawLogEach = rawLogs.get()
|
||||
print(rawLogEach)
|
||||
@@ -2754,7 +2773,10 @@ class OpenThreadTHCI(object):
|
||||
cmd += pskc
|
||||
|
||||
if listSecurityPolicy is not None:
|
||||
cmd += '0c03'
|
||||
if self.DeviceCapability == DevCapb.V1_1:
|
||||
cmd += '0c03'
|
||||
else:
|
||||
cmd += '0c04'
|
||||
|
||||
rotationTime = 0
|
||||
policyBits = 0
|
||||
@@ -2780,7 +2802,11 @@ class OpenThreadTHCI(object):
|
||||
else:
|
||||
# new passing way listSecurityPolicy=[3600, 0b11001111]
|
||||
rotationTime = listSecurityPolicy[0]
|
||||
policyBits = listSecurityPolicy[1]
|
||||
# bit order
|
||||
if self.DeviceCapability != DevCapb.V1_1:
|
||||
policyBits = listSecurityPolicy[2] << 8 | listSecurityPolicy[1]
|
||||
else:
|
||||
policyBits = listSecurityPolicy[1]
|
||||
|
||||
policy = str(hex(rotationTime))[2:]
|
||||
|
||||
@@ -2789,7 +2815,12 @@ class OpenThreadTHCI(object):
|
||||
|
||||
cmd += policy
|
||||
|
||||
cmd += str(hex(policyBits))[2:]
|
||||
flags0 = ('%x' % (policyBits & 0x00ff)).ljust(2, '0')
|
||||
cmd += flags0
|
||||
|
||||
if self.DeviceCapability != DevCapb.V1_1:
|
||||
flags1 = ('%x' % ((policyBits & 0xff00) >> 8)).ljust(2, '0')
|
||||
cmd += flags1
|
||||
|
||||
if xCommissioningSessionId is not None:
|
||||
cmd += '0b02'
|
||||
@@ -3157,8 +3188,9 @@ class OpenThreadTHCI(object):
|
||||
# Low power THCI
|
||||
@API
|
||||
def setCSLtout(self, tout=30):
|
||||
self.ssedTimeout = tout
|
||||
self.log('call setCSLtout')
|
||||
cmd = 'csl timeout %u' % tout
|
||||
cmd = 'csl timeout %u' % self.ssedTimeout
|
||||
print(cmd)
|
||||
return self.__executeCommand(cmd)[-1] == 'Done'
|
||||
|
||||
@@ -3185,8 +3217,8 @@ class OpenThreadTHCI(object):
|
||||
return self.__executeCommand(cmd)[-1] == 'Done'
|
||||
|
||||
@staticmethod
|
||||
def getForwardSeriesFlagsFromHexStr(flags):
|
||||
hexFlags = int(flags, 16)
|
||||
def getForwardSeriesFlagsFromHexOrStr(flags):
|
||||
hexFlags = int(flags, 16) if isinstance(flags, str) else flags
|
||||
strFlags = ''
|
||||
if hexFlags == 0:
|
||||
strFlags = 'X'
|
||||
@@ -3210,19 +3242,31 @@ class OpenThreadTHCI(object):
|
||||
0x0a: 'm',
|
||||
0x0b: 'r',
|
||||
}
|
||||
return metricsFlagMap.get(metrics, '?')
|
||||
metricsReservedFlagMap = {0x11: 'q', 0x12: 'm', 0x13: 'r'}
|
||||
if metricsFlagMap.get(metrics):
|
||||
return metricsFlagMap.get(metrics), False
|
||||
elif metricsReservedFlagMap.get(metrics):
|
||||
return metricsReservedFlagMap.get(metrics), True
|
||||
else:
|
||||
logging.warning("Not found flag mapping for given metrics: {}".format(metrics))
|
||||
return '', False
|
||||
|
||||
@staticmethod
|
||||
def getMetricsFlagsFromHexStr(metrics):
|
||||
strMetrics = ''
|
||||
reserved_flag = ''
|
||||
|
||||
if metrics.startswith('0x'):
|
||||
metrics = metrics[2:]
|
||||
hexMetricsArray = bytearray.fromhex(metrics)
|
||||
|
||||
strMetrics = ''
|
||||
for metric in hexMetricsArray:
|
||||
strMetrics += OpenThreadTHCI.mapMetricsHexToChar(metric)
|
||||
metric_flag, has_reserved_flag = OpenThreadTHCI.mapMetricsHexToChar(metric)
|
||||
strMetrics += metric_flag
|
||||
if has_reserved_flag:
|
||||
reserved_flag = ' r'
|
||||
|
||||
return strMetrics
|
||||
return strMetrics + reserved_flag
|
||||
|
||||
@API
|
||||
def LinkMetricsSingleReq(self, dst_addr, metrics):
|
||||
@@ -3236,18 +3280,13 @@ class OpenThreadTHCI(object):
|
||||
self.log('call LinkMetricsMgmtReq')
|
||||
cmd = 'linkmetrics mgmt %s ' % dst_addr
|
||||
if type_ == 'FWD':
|
||||
cmd += 'forward %d %s' % (series_id, self.getForwardSeriesFlagsFromHexStr(flags))
|
||||
cmd += 'forward %d %s' % (series_id, self.getForwardSeriesFlagsFromHexOrStr(flags))
|
||||
if flags != 0:
|
||||
cmd += ' %s' % (self.getMetricsFlagsFromHexStr(metrics))
|
||||
elif type_ == 'ENH':
|
||||
cmd += 'enhanced-ack'
|
||||
if flags != 0:
|
||||
cmd += ' register'
|
||||
metricsFlags = self.getMetricsFlagsFromHexStr(metrics)
|
||||
if '?' in metricsFlags:
|
||||
cmd += ' %s r' % metricsFlags.replace('?', '')
|
||||
else:
|
||||
cmd += ' %s' % metricsFlags
|
||||
cmd += ' register %s' % (self.getMetricsFlagsFromHexStr(metrics))
|
||||
else:
|
||||
cmd += ' clear'
|
||||
print(cmd)
|
||||
@@ -3299,6 +3338,7 @@ class OpenThreadTHCI(object):
|
||||
assert payload is not None, 'payload should not be none'
|
||||
assert interface == 0, "non-BR must send UDP to Thread interface"
|
||||
self.__udpOpen()
|
||||
time.sleep(0.5)
|
||||
cmd = 'udp send %s %s -x %s' % (destination, port, payload)
|
||||
print(cmd)
|
||||
return self.__executeCommand(cmd)[-1] == 'Done'
|
||||
@@ -3307,6 +3347,13 @@ class OpenThreadTHCI(object):
|
||||
if not self.__isUdpOpened:
|
||||
cmd = 'udp open'
|
||||
self.__executeCommand(cmd)
|
||||
|
||||
# Bind to RLOC address and first dynamic port
|
||||
rlocAddr = self.getRloc()
|
||||
|
||||
cmd = 'udp bind %s 49152' % rlocAddr
|
||||
self.__executeCommand(cmd)
|
||||
|
||||
self.__isUdpOpened = True
|
||||
|
||||
@API
|
||||
@@ -3327,13 +3374,13 @@ class OpenThreadTHCI(object):
|
||||
def setCSLsuspension(self, suspend):
|
||||
self.log('call setCSLsuspension')
|
||||
if suspend:
|
||||
self.__setPollPeriod(1000000)
|
||||
self.__setPollPeriod(240 * 1000)
|
||||
else:
|
||||
self.__setPollPeriod(self.__sedPollPeriod)
|
||||
self.__setPollPeriod(int(0.9 * self.ssedTimeout * 1000))
|
||||
|
||||
@API
|
||||
def set_max_addrs_per_child(self, num):
|
||||
cmd = 'childipmax %d' % int(num)
|
||||
cmd = 'childip max %d' % int(num)
|
||||
print(cmd)
|
||||
self.__executeCommand(cmd)
|
||||
|
||||
@@ -3446,6 +3493,7 @@ class OpenThreadTHCI(object):
|
||||
else:
|
||||
raise
|
||||
|
||||
@API
|
||||
def deregisterMulticast(self, sAddr):
|
||||
"""
|
||||
Unsubscribe to a given IPv6 address.
|
||||
@@ -3459,6 +3507,10 @@ class OpenThreadTHCI(object):
|
||||
self.externalCommissioner.MLR(sAddr, 0)
|
||||
return True
|
||||
|
||||
@API
|
||||
def getMlrLogs(self):
|
||||
return self.externalCommissioner.getMlrLogs()
|
||||
|
||||
@API
|
||||
def migrateNetwork(self, channel=None, net_name=None):
|
||||
"""migrate to another Thread Partition 'net_name' (could be None)
|
||||
@@ -3520,19 +3572,22 @@ class OpenThreadTHCI(object):
|
||||
|
||||
def __discoverDeviceCapability(self):
|
||||
"""Discover device capability according to version"""
|
||||
self.DeviceCapability = DevCapb.NotSpecified
|
||||
|
||||
if self.IsBorderRouter:
|
||||
self.DeviceCapability = DevCapb.C_BBR | DevCapb.C_Host | DevCapb.C_Comm
|
||||
self.log("Setting capability of BR {}: DevCapb.C_BBR | DevCapb.C_Host | DevCapb.C_Comm".format(self))
|
||||
self.DeviceCapability = (DevCapb.C_BBR | DevCapb.C_Host | DevCapb.C_Comm)
|
||||
else:
|
||||
# Get Thread stack version to distinguish device capability.
|
||||
thver = self.__executeCommand('thread version')[0]
|
||||
|
||||
if thver in ['1.2', '3']:
|
||||
self.DeviceCapability = (DevCapb.C_FFD | DevCapb.C_RFD | DevCapb.L_AIO)
|
||||
self.log("Setting capability of {}: DevCapb.L_AIO | DevCapb.C_FFD | DevCapb.C_RFD".format(self))
|
||||
self.DeviceCapability = (DevCapb.L_AIO | DevCapb.C_FFD | DevCapb.C_RFD)
|
||||
elif thver in ['1.1', '2']:
|
||||
self.log("Setting capability of {}: DevCapb.V1_1".format(self))
|
||||
self.DeviceCapability = DevCapb.V1_1
|
||||
else:
|
||||
self.log("Capability not specified for {}".format(self))
|
||||
self.DeviceCapability = DevCapb.NotSpecified
|
||||
assert False, thver
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -34,9 +34,9 @@ import logging
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import ipaddress
|
||||
|
||||
import serial
|
||||
from GRLLibs.UtilityModules.ModuleHelper import ModuleHelper
|
||||
from IThci import IThci
|
||||
from THCI.OpenThread import OpenThreadTHCI, watched, API
|
||||
|
||||
@@ -154,6 +154,13 @@ class SerialHandle:
|
||||
|
||||
self.bash('stty cols 256')
|
||||
|
||||
def log(self, fmt, *args):
|
||||
try:
|
||||
msg = fmt % args
|
||||
print('%s - %s - %s' % (self.port, time.strftime('%b %d %H:%M:%S'), msg))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def close(self):
|
||||
self.__handle.close()
|
||||
|
||||
@@ -304,6 +311,8 @@ class OpenThread_BR(OpenThreadTHCI, IThci):
|
||||
def _deviceAfterReset(self):
|
||||
self.__dumpSyslog()
|
||||
self.__truncateSyslog()
|
||||
if not self.IsHost:
|
||||
self.bash('sudo service otbr-agent restart')
|
||||
|
||||
@API
|
||||
def setupHost(self, setDua=False):
|
||||
@@ -345,11 +354,18 @@ class OpenThread_BR(OpenThreadTHCI, IThci):
|
||||
if interface == 1:
|
||||
ifname = 'eth0'
|
||||
else:
|
||||
print('invalid interface')
|
||||
return
|
||||
raise AssertionError('Invalid interface set to send UDP: {} '
|
||||
'Available interface options: 0 - Thread; 1 - Ethernet'.format(interface))
|
||||
cmd = 'sudo /home/pi/reference-device/send_udp.py %s %s %s %s' % (ifname, dst, port, payload)
|
||||
print(cmd)
|
||||
self.bash(cmd)
|
||||
|
||||
cmd = 'sudo /home/pi/ot-br-posix/script/reference-device/send_udp.py %s %s %s %s' % (ifname, dst, port,
|
||||
payload)
|
||||
@API
|
||||
def mldv2_query(self):
|
||||
ifname = 'eth0'
|
||||
dst = 'ff02::1'
|
||||
|
||||
cmd = 'sudo /home/pi/reference-device/send_mld_query.py %s %s' % (ifname, dst)
|
||||
print(cmd)
|
||||
self.bash(cmd)
|
||||
|
||||
@@ -465,14 +481,18 @@ class OpenThread_BR(OpenThreadTHCI, IThci):
|
||||
continue
|
||||
|
||||
addr = line[1].split('/')[0]
|
||||
addr = ModuleHelper.GetFullIpv6Address(addr).lower()
|
||||
addr = str(ipaddress.IPv6Address(addr.decode()).exploded)
|
||||
globalAddrs.append(addr)
|
||||
|
||||
if not filterByPrefix:
|
||||
return globalAddrs[0]
|
||||
else:
|
||||
if filterByPrefix[-2:] != '::':
|
||||
filterByPrefix = '%s::' % filterByPrefix
|
||||
prefix = ipaddress.IPv6Network((filterByPrefix + '/64').decode())
|
||||
for fullIp in globalAddrs:
|
||||
if fullIp.startswith(filterByPrefix):
|
||||
address = ipaddress.IPv6Address(fullIp.decode())
|
||||
if address in prefix:
|
||||
return fullIp
|
||||
|
||||
def _cliReadLine(self):
|
||||
@@ -524,7 +544,7 @@ interface eth0
|
||||
AdvAutonomous on;
|
||||
AdvRouterAddr on;
|
||||
};
|
||||
|
||||
|
||||
prefix fd00:7d03:7d03:7d03::/64
|
||||
{
|
||||
AdvOnLink on;
|
||||
@@ -586,3 +606,22 @@ EOF"
|
||||
output = self.bash_unwatched('sudo grep "otbr-agent" /var/log/syslog')
|
||||
for line in output:
|
||||
self.log('%s', line)
|
||||
|
||||
@API
|
||||
def mdns_query(self):
|
||||
print('mdns_query')
|
||||
self.bash('dig -p 5353 @ff02::fb _meshcop._udp.local ptr')
|
||||
|
||||
# Override powerDown
|
||||
@API
|
||||
def powerDown(self):
|
||||
print('powerDown')
|
||||
self.bash('sudo service otbr-agent stop')
|
||||
super(OpenThread_BR, self).powerDown()
|
||||
|
||||
# Override powerUp
|
||||
@API
|
||||
def powerUp(self):
|
||||
print('powerUp')
|
||||
self.bash('sudo service otbr-agent start')
|
||||
super(OpenThread_BR, self).powerUp()
|
||||
|
||||
Reference in New Issue
Block a user