Skip to content

Commit bf7e9fc

Browse files
authored
[Python] Convert DiscoverCommissionableNodes to asyncio (#34033)
* [Python] Convert DiscoverCommissionableNodes to asyncio Make the discovery of commissionable nodes Python asyncio APIs as well. This avoids blocking the event loop when using the API. The implementation is also safe to be used with the Python asyncio wait_for() function: The discovery process will be cancelled if the timeout is reached. * [Python] Adjust tests to use new DiscoverCommissionableNodes API
1 parent a7b064a commit bf7e9fc

File tree

9 files changed

+42
-34
lines changed

9 files changed

+42
-34
lines changed

src/controller/python/chip/ChipDeviceCtrl.py

+31-23
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
import logging
3939
import secrets
4040
import threading
41-
import time
4241
import typing
4342
from ctypes import (CDLL, CFUNCTYPE, POINTER, Structure, byref, c_bool, c_char, c_char_p, c_int, c_int32, c_size_t, c_uint8,
4443
c_uint16, c_uint32, c_uint64, c_void_p, create_string_buffer, pointer, py_object, resize, string_at)
@@ -729,8 +728,8 @@ def GetAddressAndPort(self, nodeid):
729728

730729
return (address.value.decode(), port.value) if error == 0 else None
731730

732-
def DiscoverCommissionableNodes(self, filterType: discovery.FilterType = discovery.FilterType.NONE, filter: typing.Any = None,
733-
stopOnFirst: bool = False, timeoutSecond: int = 5) -> typing.Union[None, CommissionableNode, typing.List[CommissionableNode]]:
731+
async def DiscoverCommissionableNodes(self, filterType: discovery.FilterType = discovery.FilterType.NONE, filter: typing.Any = None,
732+
stopOnFirst: bool = False, timeoutSecond: int = 5) -> typing.Union[None, CommissionableNode, typing.List[CommissionableNode]]:
734733
''' Discover commissionable nodes via DNS-SD with specified filters.
735734
Supported filters are:
736735
@@ -752,27 +751,36 @@ def DiscoverCommissionableNodes(self, filterType: discovery.FilterType = discove
752751
if isinstance(filter, int):
753752
filter = str(filter)
754753

755-
self._ChipStack.Call(
756-
lambda: self._dmLib.pychip_DeviceController_DiscoverCommissionableNodes(
757-
self.devCtrl, int(filterType), str(filter).encode("utf-8"))).raise_on_error()
758-
759-
if timeoutSecond != 0:
760-
if stopOnFirst:
761-
target = time.time() + timeoutSecond
762-
while time.time() < target:
763-
if self._ChipStack.Call(
764-
lambda: self._dmLib.pychip_DeviceController_HasDiscoveredCommissionableNode(self.devCtrl)):
765-
break
766-
time.sleep(0.1)
767-
else:
768-
time.sleep(timeoutSecond)
769-
770-
self._ChipStack.Call(
771-
lambda: self._dmLib.pychip_DeviceController_StopCommissionableDiscovery(self.devCtrl)).raise_on_error()
754+
# Discovery is also used during commissioning. Make sure this manual discovery
755+
# and commissioning attempts do not interfere with each other.
756+
async with self._commissioning_lock:
757+
res = await self._ChipStack.CallAsync(
758+
lambda: self._dmLib.pychip_DeviceController_DiscoverCommissionableNodes(
759+
self.devCtrl, int(filterType), str(filter).encode("utf-8")))
760+
res.raise_on_error()
772761

773-
return self.GetDiscoveredDevices()
762+
async def _wait_discovery():
763+
while not await self._ChipStack.CallAsync(
764+
lambda: self._dmLib.pychip_DeviceController_HasDiscoveredCommissionableNode(self.devCtrl)):
765+
await asyncio.sleep(0.1)
766+
return
774767

775-
def GetDiscoveredDevices(self):
768+
try:
769+
if stopOnFirst:
770+
await asyncio.wait_for(_wait_discovery(), timeoutSecond)
771+
else:
772+
await asyncio.sleep(timeoutSecond)
773+
except TimeoutError:
774+
# Expected timeout, do nothing
775+
pass
776+
finally:
777+
res = await self._ChipStack.CallAsync(
778+
lambda: self._dmLib.pychip_DeviceController_StopCommissionableDiscovery(self.devCtrl))
779+
res.raise_on_error()
780+
781+
return await self.GetDiscoveredDevices()
782+
783+
async def GetDiscoveredDevices(self):
776784
def GetDevices(devCtrl):
777785
devices = []
778786

@@ -786,7 +794,7 @@ def HandleDevice(deviceJson, deviceJsonLen):
786794
self._dmLib.pychip_DeviceController_IterateDiscoveredCommissionableNodes(devCtrl.devCtrl, HandleDevice)
787795
return devices
788796

789-
return self._ChipStack.Call(lambda: GetDevices(self))
797+
return await self._ChipStack.CallAsync(lambda: GetDevices(self))
790798

791799
def GetIPForDiscoveredDevice(self, idx, addrStr, length):
792800
self.CheckIsActive()

src/controller/python/chip/commissioning/pase.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ async def establish_session(devCtrl: ChipDeviceCtrl.ChipDeviceControllerBase, pa
4848
if isinstance(parameter, commissioning.PaseOverBLEParameters):
4949
await devCtrl.EstablishPASESessionBLE(parameter.setup_pin, parameter.discriminator, parameter.temporary_nodeid)
5050
elif isinstance(parameter, commissioning.PaseOverIPParameters):
51-
device = devCtrl.DiscoverCommissionableNodes(filterType=discovery.FilterType.LONG_DISCRIMINATOR,
52-
filter=parameter.long_discriminator, stopOnFirst=True)
51+
device = await devCtrl.DiscoverCommissionableNodes(filterType=discovery.FilterType.LONG_DISCRIMINATOR,
52+
filter=parameter.long_discriminator, stopOnFirst=True)
5353
if not device:
5454
raise ValueError("No commissionable device found")
5555
selected_address = None

src/controller/python/chip/yaml/runner.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,7 @@ def __init__(self, test_step):
712712
self.filterType, self.filter = DiscoveryCommandAction._filter_for_step(test_step)
713713

714714
async def run_action(self, dev_ctrl: ChipDeviceController) -> _ActionResult:
715-
devices = dev_ctrl.DiscoverCommissionableNodes(
715+
devices = await dev_ctrl.DiscoverCommissionableNodes(
716716
filterType=self.filterType, filter=self.filter, stopOnFirst=True, timeoutSecond=5)
717717

718718
# Devices will be a list: [CommissionableNode(), ...]

src/controller/python/test/test_scripts/base.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -210,10 +210,10 @@ def _WaitForOneDiscoveredDevice(self, timeoutSeconds: int = 2):
210210
return None
211211
return ctypes.string_at(addrStrStorage).decode("utf-8")
212212

213-
def TestDiscovery(self, discriminator: int):
213+
async def TestDiscovery(self, discriminator: int):
214214
self.logger.info(
215215
f"Discovering commissionable nodes with discriminator {discriminator}")
216-
res = self.devCtrl.DiscoverCommissionableNodes(
216+
res = await self.devCtrl.DiscoverCommissionableNodes(
217217
chip.discovery.FilterType.LONG_DISCRIMINATOR, discriminator, stopOnFirst=True, timeoutSecond=3)
218218
if not res:
219219
self.logger.info(
@@ -337,7 +337,7 @@ async def TestCommissioningWithSetupPayload(self, setupPayload: str, nodeid: int
337337

338338
async def TestOnNetworkCommissioning(self, discriminator: int, setuppin: int, nodeid: int, ip_override: str = None):
339339
self.logger.info("Testing discovery")
340-
device = self.TestDiscovery(discriminator=discriminator)
340+
device = await self.TestDiscovery(discriminator=discriminator)
341341
if not device:
342342
self.logger.info("Failed to discover any devices.")
343343
return False

src/controller/python/test/test_scripts/commissioning_test.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ async def main():
125125
nodeid=112233, paaTrustStorePath=options.paaTrustStorePath, testCommissioner=True)
126126

127127
logger.info("Testing discovery")
128-
FailIfNot(test.TestDiscovery(discriminator=options.discriminator),
128+
FailIfNot(await test.TestDiscovery(discriminator=options.discriminator),
129129
"Failed to discover any devices.")
130130

131131
FailIfNot(test.SetNetworkCommissioningParameters(dataset=TEST_THREAD_NETWORK_DATASET_TLV),

src/controller/python/test/test_scripts/failsafe_tests.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ async def main():
8888
nodeid=112233, paaTrustStorePath=options.paaTrustStorePath, testCommissioner=False)
8989

9090
logger.info("Testing discovery")
91-
FailIfNot(test.TestDiscovery(discriminator=TEST_DISCRIMINATOR),
91+
FailIfNot(await test.TestDiscovery(discriminator=TEST_DISCRIMINATOR),
9292
"Failed to discover any devices.")
9393

9494
FailIfNot(test.SetNetworkCommissioningParameters(dataset=TEST_THREAD_NETWORK_DATASET_TLV),

src/controller/python/test/test_scripts/mobile-device-test.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959

6060
async def ethernet_commissioning(test: BaseTestHelper, discriminator: int, setup_pin: int, address_override: str, device_nodeid: int):
6161
logger.info("Testing discovery")
62-
device = test.TestDiscovery(discriminator=discriminator)
62+
device = await test.TestDiscovery(discriminator=discriminator)
6363
FailIfNot(device, "Failed to discover any devices.")
6464

6565
address = device.addresses[0]

src/python_testing/TC_IDM_1_2.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ async def test_TC_IDM_1_2(self):
202202
new_fabric_admin = new_certificate_authority.NewFabricAdmin(vendorId=0xFFF1, fabricId=self.matter_test_config.fabric_id + 1)
203203
TH2 = new_fabric_admin.NewController(nodeId=112233)
204204

205-
devices = TH2.DiscoverCommissionableNodes(
205+
devices = await TH2.DiscoverCommissionableNodes(
206206
filterType=Discovery.FilterType.LONG_DISCRIMINATOR, filter=discriminator, stopOnFirst=False)
207207
# For some reason, the devices returned here aren't filtered, so filter ourselves
208208
device = next(filter(lambda d: d.commissioningMode == 2 and d.longDiscriminator == discriminator, devices))

src/python_testing/TC_OPCREDS_3_1.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ async def FindAndEstablishPase(self, longDiscriminator: int, setupPinCode: int,
3434
if dev_ctrl is None:
3535
dev_ctrl = self.default_controller
3636

37-
devices = dev_ctrl.DiscoverCommissionableNodes(
37+
devices = await dev_ctrl.DiscoverCommissionableNodes(
3838
filterType=Discovery.FilterType.LONG_DISCRIMINATOR, filter=longDiscriminator, stopOnFirst=False)
3939
# For some reason, the devices returned here aren't filtered, so filter ourselves
4040
device = next(filter(lambda d: d.commissioningMode ==

0 commit comments

Comments
 (0)