From 0471a926ca1abb2c80d44ccfa64775ebe282cbd6 Mon Sep 17 00:00:00 2001 From: Song Guo Date: Thu, 9 Dec 2021 17:59:18 +0800 Subject: [PATCH] Update python test script --- .../network-commissioning.cpp | 28 +-- .../network-commissioning.h | 9 + .../python/chip/clusters/ClusterObjects.py | 2 +- .../python/test/test_scripts/base.py | 29 --- .../test/test_scripts/mobile-device-test.py | 9 +- .../test_scripts/network_commissioning.py | 179 ++++++++++++++++++ .../Linux/ConnectivityManagerImpl.cpp | 18 +- 7 files changed, 207 insertions(+), 67 deletions(-) create mode 100644 src/controller/python/test/test_scripts/network_commissioning.py diff --git a/src/app/clusters/network-commissioning/network-commissioning.cpp b/src/app/clusters/network-commissioning/network-commissioning.cpp index 6764f1445402e9..762b22667700cf 100644 --- a/src/app/clusters/network-commissioning/network-commissioning.cpp +++ b/src/app/clusters/network-commissioning/network-commissioning.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include @@ -48,7 +47,6 @@ CHIP_ERROR InstanceBase::Register() { ReturnErrorOnFailure(chip::app::InteractionModelEngine::GetInstance()->RegisterCommandHandler(this)); VerifyOrReturnError(registerAttributeAccessOverride(this), CHIP_ERROR_INCORRECT_STATE); - VerifyOrReturnError(DeviceLayer::PlatformMgrImpl().AddEventHandler(_OnCommissioningComplete, reinterpret_cast(this))); return CHIP_NO_ERROR; } @@ -127,7 +125,7 @@ CHIP_ERROR InstanceBase::Read(const ConcreteReadAttributePath & aPath, Attribute case Attributes::LastNetworkingStatus::Id: return aEncoder.Encode(NetworkCommissioningStatus::kSuccess); case Attributes::LastNetworkID::Id: - return aEncoder.Encode(ByteSpan()); + return aEncoder.Encode(CharSpan()); case Attributes::LastConnectErrorValue::Id: return aEncoder.Encode(Attributes::LastConnectErrorValue::TypeInfo::Type(0)); @@ -539,30 +537,6 @@ void InstanceBase::OnScanFinished(CHIP_ERROR err, CharSpan debugText, const Span commandHandle->AddResponseData(mPath, response); } -static InstanceBase::_OnCommissioningComplete(const DeviceLayer::ChipDeviceEvent * event, intptr_t arg) -{ - InstanceBase * this_ = reinterpret_cast(arg); - VerifyOrReturn(event->Type == DeviceEventType::kCommissioningComplete); - this_->OnCommissioningComplete(event->CommissioningComplete.status); -} - -void InstanceBase::OnCommissioningComplete(CHIP_ERROR err) -{ - if (err == CHIP_NO_ERROR) - { - ChipLogDetail(Zcl, "Commissioning complete, persist network credentails to platform driver."); - // TODO: Implement this - if (network->mNetworkType == NetworkType::kWiFi) - { - mpWiFiDelegate->StoreNetworks(Span()); - } - else if (network->mNetworkType == NetworkType::kThread) - { - mpThreadDelegate->StoreNetworks(Span()); - } - } -} - } // namespace NetworkCommissioning } // namespace Clusters } // namespace app diff --git a/src/app/clusters/network-commissioning/network-commissioning.h b/src/app/clusters/network-commissioning/network-commissioning.h index 414b4519dc04e3..7d7709dcc30b0b 100644 --- a/src/app/clusters/network-commissioning/network-commissioning.h +++ b/src/app/clusters/network-commissioning/network-commissioning.h @@ -61,12 +61,19 @@ class InstanceBase : public CommandHandlerInterface, } mData; }; + /** + * Register will register the network commissioning instance to the attribute and command dispatching route. + */ + CHIP_ERROR Register(); + // CommandHandlerInterface void InvokeCommand(HandlerContext & ctx) override; + // AttributeAccessInterface CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; CHIP_ERROR Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder) override; + // Actual handlers of the commands void HandleScanNetworks(HandlerContext & ctx, const Commands::ScanNetworks::DecodableType & req); void HandleAddOrUpdateWiFiNetwork(HandlerContext & ctx, const Commands::AddOrUpdateWiFiNetwork::DecodableType & req); void HandleAddOrUpdateThreadNetwork(HandlerContext & ctx, const Commands::AddOrUpdateThreadNetwork::DecodableType & req); @@ -98,6 +105,8 @@ class InstanceBase : public CommandHandlerInterface, private: void OnConnectResult(app::Clusters::NetworkCommissioning::NetworkCommissioningStatus commissioningError, CharSpan errorText, int32_t networkRelatedAssociationStatus); + + // Some handlers for attribute reads. CHIP_ERROR HandleReadNetworks(AttributeValueEncoder & aEncoder); CHIP_ERROR HandleReadScanMaxTimeSeconds(AttributeValueEncoder & aEncoder); CHIP_ERROR HandleReadConnectMaxTimeSeconds(AttributeValueEncoder & aEncoder); diff --git a/src/controller/python/chip/clusters/ClusterObjects.py b/src/controller/python/chip/clusters/ClusterObjects.py index d1f46eace96e88..7b00bcad58cca1 100644 --- a/src/controller/python/chip/clusters/ClusterObjects.py +++ b/src/controller/python/chip/clusters/ClusterObjects.py @@ -140,7 +140,7 @@ def TagDictToLabelDict(self, debugPath: str, tlvData: Dict[int, Any]) -> Dict[st for tag, value in tlvData.items(): descriptor = self.GetFieldByTag(tag) if not descriptor: - # We do not have enough infomation for this field. + # We do not have enough information for this field. ret[tag] = value continue diff --git a/src/controller/python/test/test_scripts/base.py b/src/controller/python/test/test_scripts/base.py index 695ca48cc66821..f910e0404c0703 100644 --- a/src/controller/python/test/test_scripts/base.py +++ b/src/controller/python/test/test_scripts/base.py @@ -150,35 +150,6 @@ def TestCloseSession(self, nodeid: int): f"Failed to close sessions with device {nodeid}: {ex}") return False - def TestNetworkCommissioning(self, nodeid: int, endpoint: int, group: int, dataset: str, network_id: str): - self.logger.info("Commissioning network to device {}".format(nodeid)) - try: - (err, resp) = self.devCtrl.ZCLSend("NetworkCommissioning", "AddOrUpdateThreadNetwork", nodeid, endpoint, group, { - "operationalDataset": bytes.fromhex(dataset), - "breadcrumb": 0}, blocking=True) - self.logger.info(f"Received response: {resp}") - if resp.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess: - self.logger.exception("Failed to add Thread network.") - return False - except Exception as ex: - self.logger.exception( - "Failed to send AddOrUpdateThreadNetwork command") - return False - self.logger.info( - "Send ConnectNetwork command to device {}".format(nodeid)) - try: - self.devCtrl.ZCLSend("NetworkCommissioning", "ConnectNetwork", nodeid, endpoint, group, { - "networkID": bytes.fromhex(network_id), - "breadcrumb": 0}, blocking=True) - self.logger.info(f"Received response: {resp}") - if resp.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess: - self.logger.exception("Failed to enable Thread network.") - return False - except Exception as ex: - self.logger.exception("Failed to send ConnectNetwork command") - return False - return True - def TestOnOffCluster(self, nodeid: int, endpoint: int, group: int): self.logger.info( "Sending On/Off commands to device {} endpoint {}".format(nodeid, endpoint)) diff --git a/src/controller/python/test/test_scripts/mobile-device-test.py b/src/controller/python/test/test_scripts/mobile-device-test.py index 1f542cdc11c1c9..25f4c2ea5ce45d 100755 --- a/src/controller/python/test/test_scripts/mobile-device-test.py +++ b/src/controller/python/test/test_scripts/mobile-device-test.py @@ -22,7 +22,8 @@ import sys from optparse import OptionParser from base import TestFail, TestTimeout, BaseTestHelper, FailIfNot, logger -from cluster_objects import ClusterObjectTests +from cluster_objects import NODE_ID, ClusterObjectTests +from network_commissioning import NetworkCommissioningTests import asyncio # The thread network dataset tlv for testing, splited into T-L-V. @@ -93,11 +94,7 @@ def main(): "Failed to resolve nodeid") logger.info("Testing network commissioning") - FailIfNot(test.TestNetworkCommissioning(nodeid=1, - endpoint=ENDPOINT_ID, - group=GROUP_ID, - dataset=TEST_THREAD_NETWORK_DATASET_TLV, - network_id=TEST_THREAD_NETWORK_ID), + FailIfNot(asyncio.run(NetworkCommissioningTests(devCtrl=test.devCtrl, nodeid=1).run(testThread=True)), "Failed to finish network commissioning") logger.info("Testing on off cluster") diff --git a/src/controller/python/test/test_scripts/network_commissioning.py b/src/controller/python/test/test_scripts/network_commissioning.py new file mode 100644 index 00000000000000..ab2f024614b161 --- /dev/null +++ b/src/controller/python/test/test_scripts/network_commissioning.py @@ -0,0 +1,179 @@ +# +# Copyright (c) 2021 Project CHIP Authors +# All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import chip.clusters as Clusters +import logging +from chip.clusters.Attribute import AttributePath, AttributeReadResult, AttributeStatus, ValueDecodeFailure +import chip.interaction_model +import asyncio + +logger = logging.getLogger('NetworkCommissioning') +logger.setLevel(logging.INFO) + +NODE_ID = 1 +THREAD_ENDPOINT_ID = 0 +WIFI_ENDPOINT_ID = 1 + +TEST_THREAD_NETWORK_DATASET_TLVS = [bytes.fromhex("0e080000000000010000" + + "000300000c" + + "35060004001fffe0" + + "0208fedcba9876543210" + + "0708fd00000000001234" + + "0510ffeeddccbbaa99887766554433221100" + + "030e54657374696e674e6574776f726b" + + "0102d252" + + "041081cb3b2efa781cc778397497ff520fa50c0302a0ff"), + # End of first TLV + ] +# Network id, for the thread network, current a const value, will be changed to XPANID of the thread network. +TEST_THREAD_NETWORK_IDS = [ + bytes.fromhex("fedcba9876543210"), +] + + +class NetworkCommissioningTests: + def __init__(self, devCtrl, nodeid): + self._devCtrl = devCtrl + self._nodeid = nodeid + + def log_interface_basic_info(self, values): + logger.info(f"The interface supports {values.maxNetworks} networks.") + logger.info( + f"ScanNetworks should take no more than {values.scanMaxTimeSeconds} seconds.") + logger.info( + f"ConnectNetwork should take no more than {values.connectMaxTimeSeconds} seconds.") + logger.info( + f"The feature map of this endpoint is {values.featureMap}.") + + async def test_wifi(self): + logger.info(f"Get basic information of the endpoint") + res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[ + (WIFI_ENDPOINT_ID, + Clusters.NetworkCommissioning.Attributes.ConnectMaxTimeSeconds), + (WIFI_ENDPOINT_ID, + Clusters.NetworkCommissioning.Attributes.ScanMaxTimeSeconds), + (WIFI_ENDPOINT_ID, Clusters.NetworkCommissioning.Attributes.MaxNetworks), + (WIFI_ENDPOINT_ID, Clusters.NetworkCommissioning.Attributes.FeatureMap)], + returnClusterObject=True) + self.log_interface_basic_info( + res[WIFI_ENDPOINT_ID][Clusters.NetworkCommissioning]) + logger.info(f"Finished getting basic information of the endpoint") + + # Scan networks + logger.info(f"Scan networks") + req = Clusters.NetworkCommissioning.Commands.ScanNetworks( + ssid=b'', breadcrumb=0) + res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=WIFI_ENDPOINT_ID, payload=req) + logger.info(f"Received response: {res}") + if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess: + raise AssertionError(f"Unexpected result: {res.networkingStatus}") + + # Add first network + logger.info(f"Adding first test network") + req = Clusters.NetworkCommissioning.Commands.AddOrUpdateWiFiNetwork( + ssid=b"TestSSID", credentials=b"TestPass", breadcrumb=0) + res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=WIFI_ENDPOINT_ID, payload=req) + logger.info(f"Received response: {res}") + if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess: + raise AssertionError(f"Unexpected result: {res.networkingStatus}") + + logger.info(f"Check network list") + res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[(WIFI_ENDPOINT_ID, Clusters.NetworkCommissioning.Attributes.Networks)], returnClusterObject=True) + networkList = res[THREAD_ENDPOINT_ID][Clusters.NetworkCommissioning].networks + logger.info(f"Got network list: {networkList}") + if len(networkList) != 2: + raise AssertionError( + f"Unexpected result: expect 1 networks, but {len(networkList)} networks received") + if networkList[0].networkID != b"TestSSID": + raise AssertionError( + f"Unexpected result: first network ID should be 'TestSSID' got {networkList[0].networkID}") + + logger.info(f"Connect to a network") + req = Clusters.NetworkCommissioning.Commands.ConnectNetwork( + networkID=b'TestSSID', breadcrumb=0) + res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=WIFI_ENDPOINT_ID, payload=req) + logger.info(f"Got response: {res}") + if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess: + raise AssertionError(f"Unexpected result: {res.networkingStatus}") + logger.info(f"Device connected to a network.") + + async def test_thread(self): + logger.info(f"Get basic information of the endpoint") + res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[ + (THREAD_ENDPOINT_ID, + Clusters.NetworkCommissioning.Attributes.ConnectMaxTimeSeconds), + (THREAD_ENDPOINT_ID, + Clusters.NetworkCommissioning.Attributes.ScanMaxTimeSeconds), + (THREAD_ENDPOINT_ID, Clusters.NetworkCommissioning.Attributes.MaxNetworks), + (THREAD_ENDPOINT_ID, Clusters.NetworkCommissioning.Attributes.FeatureMap)], + returnClusterObject=True) + self.log_interface_basic_info( + res[THREAD_ENDPOINT_ID][Clusters.NetworkCommissioning]) + logger.info(f"Finished getting basic information of the endpoint") + + # Scan networks + logger.info(f"Scan networks") + req = Clusters.NetworkCommissioning.Commands.ScanNetworks( + ssid=b'', breadcrumb=0) + res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=THREAD_ENDPOINT_ID, payload=req) + logger.info(f"Received response: {res}") + if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess: + raise AssertionError(f"Unexpected result: {res.networkingStatus}") + + # Add first network + logger.info(f"Adding first test network") + req = Clusters.NetworkCommissioning.Commands.AddOrUpdateThreadNetwork( + operationalDataset=TEST_THREAD_NETWORK_DATASET_TLVS[0], breadcrumb=0) + res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=THREAD_ENDPOINT_ID, payload=req) + logger.info(f"Received response: {res}") + if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess: + raise AssertionError(f"Unexpected result: {res.networkingStatus}") + + logger.info(f"Check network list") + res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[(THREAD_ENDPOINT_ID, Clusters.NetworkCommissioning.Attributes.Networks)], returnClusterObject=True) + networkList = res[THREAD_ENDPOINT_ID][Clusters.NetworkCommissioning].networks + logger.info(f"Got network list: {networkList}") + if len(networkList) != 1: + raise AssertionError( + f"Unexpected result: expect 1 networks, but {len(networkList.Data.value)} networks received") + if networkList[0].networkID != TEST_THREAD_NETWORK_IDS[0]: + raise AssertionError( + f"Unexpected result: first network ID should be {TEST_THREAD_NETWORK_IDS[0]} got {networkList[0].networkID}") + + logger.info(f"Connect to a network") + req = Clusters.NetworkCommissioning.Commands.ConnectNetwork( + networkID=TEST_THREAD_NETWORK_IDS[0], breadcrumb=0) + res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=THREAD_ENDPOINT_ID, payload=req) + logger.info(f"Got response: {res}") + if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess: + raise AssertionError(f"Unexpected result: {res.networkingStatus}") + logger.info(f"Device connected to a network.") + + async def run(self, testThread=True, testWiFi=False): + if testThread: + try: + await self.test_thread() + except Exception as ex: + logger.exception(ex) + return False + if testWiFi: + try: + await self.test_wifi() + except Exception as ex: + logger.exception(ex) + return False + return True diff --git a/src/platform/Linux/ConnectivityManagerImpl.cpp b/src/platform/Linux/ConnectivityManagerImpl.cpp index a536c2f299bcea..4e8b23e7e270fb 100644 --- a/src/platform/Linux/ConnectivityManagerImpl.cpp +++ b/src/platform/Linux/ConnectivityManagerImpl.cpp @@ -1123,6 +1123,13 @@ CHIP_ERROR ConnectivityManagerImpl::StartWiFiScan(ByteSpan ssid, WiFiNetworkComm std::lock_guard lock(mWpaSupplicantMutex); VerifyOrReturnError(mWpaSupplicant.iface != nullptr, CHIP_ERROR_INCORRECT_STATE); + if (wpa_fi_w1_wpa_supplicant1_interface_get_scanning(mWpaSupplicant.iface)) + { + // If we are already scanning, skip it + mpNetworkCommissioningCallback = callback; + return CHIP_NO_ERROR; + } + CHIP_ERROR ret = CHIP_NO_ERROR; GError * err = nullptr; GVariant * args = nullptr; @@ -1338,10 +1345,13 @@ void ConnectivityManagerImpl::_OnWpaInterfaceScanDone(GObject * source_object, G scanResults.push_back(scanResult); } - mpNetworkCommissioningCallback->OnScanFinished( - CHIP_NO_ERROR, CharSpan(), - Span(scanResults.data(), - scanResults.size())); + if (mpNetworkCommissioningCallback != nullptr) + { + mpNetworkCommissioningCallback->OnScanFinished( + CHIP_NO_ERROR, CharSpan(), + Span(scanResults.data(), + scanResults.size())); + } }); g_strfreev(oldBsss);