Skip to content

Commit

Permalink
[nwprov] Implement breadcrumb support (#18303)
Browse files Browse the repository at this point in the history
* [nwprov] Breadcrumb support

* Add interface to generalcommissioning cluster to touch breadcrumb

* Update

* Rename UpdateBreadcrumb
  • Loading branch information
erjiaqing authored May 18, 2022
1 parent b3c1dec commit c577b23
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -310,3 +310,16 @@ void MatterGeneralCommissioningPluginServerInitCallback()
registerAttributeAccessOverride(&gAttrAccess);
DeviceLayer::PlatformMgrImpl().AddEventHandler(OnPlatformEventHandler);
}

namespace chip {
namespace app {
namespace Clusters {
namespace GeneralCommissioning {
void SetBreadcrumb(Attributes::Breadcrumb::TypeInfo::Type breadcrumb)
{
Breadcrumb::Set(0, breadcrumb);
}
} // namespace GeneralCommissioning
} // namespace Clusters
} // namespace app
} // namespace chip
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
*
* Copyright (c) 2022 Project CHIP Authors
*
* 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.
*/

#include <app-common/zap-generated/cluster-objects.h>

namespace chip {
namespace app {
namespace Clusters {
namespace GeneralCommissioning {

void SetBreadcrumb(Attributes::Breadcrumb::TypeInfo::Type breadcrumb);

} // namespace GeneralCommissioning
} // namespace Clusters
} // namespace app
} // namespace chip
54 changes: 50 additions & 4 deletions src/app/clusters/network-commissioning/network-commissioning.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@

#include "network-commissioning.h"

#include <app-common/zap-generated/attributes/Accessors.h>
#include <app-common/zap-generated/cluster-objects.h>
#include <app/CommandHandlerInterface.h>
#include <app/InteractionModelEngine.h>
#include <app/clusters/general-commissioning-server/general-commissioning-server.h>
#include <app/util/attribute-storage.h>
#include <lib/support/SafeInt.h>
#include <lib/support/ThreadOperationalDataset.h>
Expand Down Expand Up @@ -267,12 +269,14 @@ void Instance::HandleScanNetworks(HandlerContext & ctx, const Commands::ScanNetw
ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Protocols::InteractionModel::Status::InvalidCommand);
return;
}
mAsyncCommandHandle = CommandHandler::Handle(&ctx.mCommandHandler);
mCurrentOperationBreadcrumb = req.breadcrumb;
mAsyncCommandHandle = CommandHandler::Handle(&ctx.mCommandHandler);
mpDriver.Get<WiFiDriver *>()->ScanNetworks(ssid, this);
}
else if (mFeatureFlags.Has(NetworkCommissioningFeature::kThreadNetworkInterface))
{
mAsyncCommandHandle = CommandHandler::Handle(&ctx.mCommandHandler);
mCurrentOperationBreadcrumb = req.breadcrumb;
mAsyncCommandHandle = CommandHandler::Handle(&ctx.mCommandHandler);
mpDriver.Get<ThreadDriver *>()->ScanNetworks(this);
}
else
Expand Down Expand Up @@ -362,6 +366,10 @@ void Instance::HandleAddOrUpdateWiFiNetwork(HandlerContext & ctx, const Commands
mpDriver.Get<WiFiDriver *>()->AddOrUpdateNetwork(req.ssid, req.credentials, debugText, outNetworkIndex);
FillDebugTextAndNetworkIndex(response, debugText, outNetworkIndex);
ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
if (response.networkingStatus == NetworkCommissioningStatus::kSuccess)
{
UpdateBreadcrumb(req.breadcrumb);
}
}

void Instance::HandleAddOrUpdateThreadNetwork(HandlerContext & ctx, const Commands::AddOrUpdateThreadNetwork::DecodableType & req)
Expand All @@ -381,6 +389,24 @@ void Instance::HandleAddOrUpdateThreadNetwork(HandlerContext & ctx, const Comman
mpDriver.Get<ThreadDriver *>()->AddOrUpdateNetwork(req.operationalDataset, debugText, outNetworkIndex);
FillDebugTextAndNetworkIndex(response, debugText, outNetworkIndex);
ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
if (response.networkingStatus == NetworkCommissioningStatus::kSuccess)
{
UpdateBreadcrumb(req.breadcrumb);
}
}

void Instance::UpdateBreadcrumb(const Optional<uint64_t> & breadcrumb)
{
VerifyOrReturn(breadcrumb.HasValue());
GeneralCommissioning::SetBreadcrumb(breadcrumb.Value());
}

void Instance::CommitSavedBreadcrumb()
{
// We rejected the command when there is another ongoing command, so mCurrentOperationBreadcrumb reflects the breadcrumb
// argument in the only background command.
UpdateBreadcrumb(mCurrentOperationBreadcrumb);
mCurrentOperationBreadcrumb.ClearValue();
}

void Instance::HandleRemoveNetwork(HandlerContext & ctx, const Commands::RemoveNetwork::DecodableType & req)
Expand All @@ -399,6 +425,10 @@ void Instance::HandleRemoveNetwork(HandlerContext & ctx, const Commands::RemoveN
response.networkingStatus = mpWirelessDriver->RemoveNetwork(req.networkID, debugText, outNetworkIndex);
FillDebugTextAndNetworkIndex(response, debugText, outNetworkIndex);
ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
if (response.networkingStatus == NetworkCommissioningStatus::kSuccess)
{
UpdateBreadcrumb(req.breadcrumb);
}
}

void Instance::HandleConnectNetwork(HandlerContext & ctx, const Commands::ConnectNetwork::DecodableType & req)
Expand All @@ -414,8 +444,8 @@ void Instance::HandleConnectNetwork(HandlerContext & ctx, const Commands::Connec

mConnectingNetworkIDLen = static_cast<uint8_t>(req.networkID.size());
memcpy(mConnectingNetworkID, req.networkID.data(), mConnectingNetworkIDLen);

mAsyncCommandHandle = CommandHandler::Handle(&ctx.mCommandHandler);
mAsyncCommandHandle = CommandHandler::Handle(&ctx.mCommandHandler);
mCurrentOperationBreadcrumb = req.breadcrumb;
mpWirelessDriver->ConnectNetwork(req.networkID, this);
}

Expand All @@ -431,6 +461,10 @@ void Instance::HandleReorderNetwork(HandlerContext & ctx, const Commands::Reorde
response.networkingStatus = mpWirelessDriver->ReorderNetwork(req.networkID, req.networkIndex, debugText);
FillDebugTextAndNetworkIndex(response, debugText, req.networkIndex);
ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
if (response.networkingStatus == NetworkCommissioningStatus::kSuccess)
{
UpdateBreadcrumb(req.breadcrumb);
}
}

void Instance::OnResult(Status commissioningError, CharSpan debugText, int32_t interfaceStatus)
Expand Down Expand Up @@ -467,6 +501,10 @@ void Instance::OnResult(Status commissioningError, CharSpan debugText, int32_t i
mLastNetworkingStatusValue.SetNonNull(commissioningError);

commandHandle->AddResponse(mPath, response);
if (commissioningError == NetworkCommissioningStatus::kSuccess)
{
CommitSavedBreadcrumb();
}
}

void Instance::OnFinished(Status status, CharSpan debugText, ThreadScanResponseIterator * networks)
Expand Down Expand Up @@ -529,6 +567,10 @@ void Instance::OnFinished(Status status, CharSpan debugText, ThreadScanResponseI
{
ChipLogError(Zcl, "Failed to encode response: %s", err.AsString());
}
if (status == NetworkCommissioningStatus::kSuccess)
{
CommitSavedBreadcrumb();
}
networks->Release();
}

Expand Down Expand Up @@ -588,6 +630,10 @@ void Instance::OnFinished(Status status, CharSpan debugText, WiFiScanResponseIte
{
ChipLogError(Zcl, "Failed to encode response: %s", err.AsString());
}
if (status == NetworkCommissioningStatus::kSuccess)
{
CommitSavedBreadcrumb();
}
if (networks != nullptr)
{
networks->Release();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,15 @@ class Instance : public CommandHandlerInterface,
uint8_t mLastNetworkID[DeviceLayer::NetworkCommissioning::kMaxNetworkIDLen];
uint8_t mLastNetworkIDLen = 0;

Optional<uint64_t> mCurrentOperationBreadcrumb;

// Commits the breadcrumb value saved in mCurrentOperationBreadcrumb to the breadcrumb attribute in GeneralCommissioning
// cluster. Will set mCurrentOperationBreadcrumb to NullOptional.
void CommitSavedBreadcrumb();

// Sets the breadcrumb attribute in GeneralCommissioning cluster, no-op when breadcrumbValue is NullOptional.
void UpdateBreadcrumb(const Optional<uint64_t> & breadcrumbValue);

// Actual handlers of the commands
void HandleScanNetworks(HandlerContext & ctx, const Commands::ScanNetworks::DecodableType & req);
void HandleAddOrUpdateWiFiNetwork(HandlerContext & ctx, const Commands::AddOrUpdateWiFiNetwork::DecodableType & req);
Expand Down
37 changes: 29 additions & 8 deletions src/controller/python/test/test_scripts/network_commissioning.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from chip.clusters.Types import NullValue
import chip.interaction_model
import asyncio
import random

import base

Expand Down Expand Up @@ -55,6 +56,18 @@ class NetworkCommissioningTests:
def __init__(self, devCtrl, nodeid):
self._devCtrl = devCtrl
self._nodeid = nodeid
self._last_breadcrumb = random.randint(1, 1 << 48)

async def must_verify_breadcrumb(self):
res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[(0, Clusters.GeneralCommissioning.Attributes.Breadcrumb)], returnClusterObject=True)
if self._last_breadcrumb is not None:
if self._last_breadcrumb != res[0][Clusters.GeneralCommissioning].breadcrumb:
raise AssertionError(
f"Breadcrumb attribute mismatch! Expect {self._last_breadcrumb} got {res[0][Clusters.GeneralCommissioning].breadcrumb}")

def with_breadcrumb(self) -> int:
self._last_breadcrumb += 1
return self._last_breadcrumb

def log_interface_basic_info(self, values):
logger.info(f"The interface supports {values.maxNetworks} networks.")
Expand Down Expand Up @@ -129,11 +142,12 @@ async def test_wifi(self, endpointId):
# Scan networks
logger.info(f"Scan networks")
req = Clusters.NetworkCommissioning.Commands.ScanNetworks(
ssid=b'', breadcrumb=0)
ssid=b'', breadcrumb=self.with_breadcrumb())
res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=endpointId, payload=req)
logger.info(f"Received response: {res}")
if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess:
raise AssertionError(f"Unexpected result: {res.networkingStatus}")
await self.must_verify_breadcrumb()

# Arm the failsafe before making network config changes
logger.info(f"Arming the failsafe")
Expand All @@ -149,24 +163,26 @@ async def test_wifi(self, endpointId):
if len(networkList) != 0:
logger.info(f"Removing existing network")
req = Clusters.NetworkCommissioning.Commands.RemoveNetwork(
networkID=networkList[0].networkID, breadcrumb=0)
networkID=networkList[0].networkID, breadcrumb=self.with_breadcrumb())
res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=endpointId, payload=req)
logger.info(f"Received response: {res}")
if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess:
raise AssertionError(
f"Unexpected result: {res.networkingStatus}")
await self.must_verify_breadcrumb()

# Add first network
logger.info(f"Adding first test network")
req = Clusters.NetworkCommissioning.Commands.AddOrUpdateWiFiNetwork(
ssid=TEST_WIFI_SSID.encode(), credentials=TEST_WIFI_PASS.encode(), breadcrumb=0)
ssid=TEST_WIFI_SSID.encode(), credentials=TEST_WIFI_PASS.encode(), breadcrumb=self.with_breadcrumb())
res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=endpointId, payload=req)
logger.info(f"Received response: {res}")
if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess:
raise AssertionError(f"Unexpected result: {res.networkingStatus}")
if res.networkIndex != 0:
raise AssertionError(
f"Unexpected result: {res.networkIndex} (should be 0)")
await self.must_verify_breadcrumb()

logger.info(f"Check network list")
res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[(endpointId, Clusters.NetworkCommissioning.Attributes.Networks)], returnClusterObject=True)
Expand All @@ -181,12 +197,13 @@ async def test_wifi(self, endpointId):

logger.info(f"Connect to a network")
req = Clusters.NetworkCommissioning.Commands.ConnectNetwork(
networkID=TEST_WIFI_SSID.encode(), breadcrumb=0)
networkID=TEST_WIFI_SSID.encode(), breadcrumb=self.with_breadcrumb())
res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=endpointId, 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.")
await self.must_verify_breadcrumb()

# Disarm the failsafe
logger.info(f"Disarming the failsafe")
Expand Down Expand Up @@ -259,11 +276,12 @@ async def test_thread(self, endpointId):
# Scan networks
logger.info(f"Scan networks")
req = Clusters.NetworkCommissioning.Commands.ScanNetworks(
ssid=b'', breadcrumb=0)
ssid=b'', breadcrumb=self.with_breadcrumb())
res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=endpointId, payload=req)
logger.info(f"Received response: {res}")
if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess:
raise AssertionError(f"Unexpected result: {res.networkingStatus}")
await self.must_verify_breadcrumb()

# Arm the failsafe before making network config changes
logger.info(f"Arming the failsafe")
Expand All @@ -279,24 +297,26 @@ async def test_thread(self, endpointId):
if len(networkList) != 0:
logger.info(f"Removing existing network")
req = Clusters.NetworkCommissioning.Commands.RemoveNetwork(
networkID=networkList[0].networkID, breadcrumb=0)
networkID=networkList[0].networkID, breadcrumb=self.with_breadcrumb())
res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=endpointId, payload=req)
logger.info(f"Received response: {res}")
if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess:
raise AssertionError(
f"Unexpected result: {res.networkingStatus}")
await self.must_verify_breadcrumb()

# Add first network
logger.info(f"Adding first test network")
req = Clusters.NetworkCommissioning.Commands.AddOrUpdateThreadNetwork(
operationalDataset=TEST_THREAD_NETWORK_DATASET_TLVS[0], breadcrumb=0)
operationalDataset=TEST_THREAD_NETWORK_DATASET_TLVS[0], breadcrumb=self.with_breadcrumb())
res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=endpointId, payload=req)
logger.info(f"Received response: {res}")
if res.networkingStatus != Clusters.NetworkCommissioning.Enums.NetworkCommissioningStatus.kSuccess:
raise AssertionError(f"Unexpected result: {res.networkingStatus}")
if res.networkIndex != 0:
raise AssertionError(
f"Unexpected result: {res.networkIndex} (should be 0)")
await self.must_verify_breadcrumb()

logger.info(f"Check network list")
res = await self._devCtrl.ReadAttribute(nodeid=self._nodeid, attributes=[(endpointId, Clusters.NetworkCommissioning.Attributes.Networks)], returnClusterObject=True)
Expand All @@ -311,12 +331,13 @@ async def test_thread(self, endpointId):

logger.info(f"Connect to a network")
req = Clusters.NetworkCommissioning.Commands.ConnectNetwork(
networkID=TEST_THREAD_NETWORK_IDS[0], breadcrumb=0)
networkID=TEST_THREAD_NETWORK_IDS[0], breadcrumb=self.with_breadcrumb())
res = await self._devCtrl.SendCommand(nodeid=self._nodeid, endpoint=endpointId, 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.")
await self.must_verify_breadcrumb()

# Disarm the failsafe
logger.info(f"Disarming the failsafe")
Expand Down

0 comments on commit c577b23

Please sign in to comment.