Skip to content

Commit

Permalink
Implement CommissionerControl command APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
yufengwangca committed Oct 13, 2024
1 parent d2d06b3 commit 515ff47
Show file tree
Hide file tree
Showing 6 changed files with 287 additions and 10 deletions.
2 changes: 2 additions & 0 deletions examples/fabric-admin/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ static_library("fabric-admin-utils") {
"commands/pairing/OpenCommissioningWindowCommand.h",
"commands/pairing/PairingCommand.cpp",
"commands/pairing/ToTLVCert.cpp",
"device_manager/CommissionerControl.cpp",
"device_manager/CommissionerControl.h",
"device_manager/DeviceManager.cpp",
"device_manager/DeviceManager.h",
"device_manager/DeviceSubscription.cpp",
Expand Down
1 change: 0 additions & 1 deletion examples/fabric-admin/commands/clusters/ClusterCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ class ClusterCommand : public InteractionModelCommands, public ModelCommand, pub
if (data != nullptr)
{
LogErrorOnFailure(RemoteDataModelLogger::LogCommandAsJSON(path, data));
DeviceMgr().HandleCommandResponse(path, *data);
}
}

Expand Down
142 changes: 142 additions & 0 deletions examples/fabric-admin/device_manager/CommissionerControl.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#include "CommissionerControl.h"
#include <device_manager/DeviceManager.h>

using namespace ::chip;

void CommissionerControl::Init(Controller::DeviceCommissioner & commissioner, NodeId nodeId, EndpointId endpointId)
{
ChipLogProgress(NotSpecified, "Initilize CommissionerControl");
mCommissioner = &commissioner;
mDestinationId = nodeId;
mEndpointId = endpointId;
}

CHIP_ERROR CommissionerControl::RequestCommissioningApproval(uint64_t requestId, uint16_t vendorId, uint16_t productId,
Optional<CharSpan> label)
{
VerifyOrReturnError(mCommissioner != nullptr, CHIP_ERROR_INCORRECT_STATE);

ChipLogProgress(NotSpecified, "Sending RequestCommissioningApproval to node " ChipLogFormatX64,
ChipLogValueX64(mDestinationId));

mRequestCommissioningApproval.requestID = requestId;
mRequestCommissioningApproval.vendorID = static_cast<VendorId>(vendorId);
mRequestCommissioningApproval.productID = productId;
mRequestCommissioningApproval.label = label;

CommissioneeDeviceProxy * commissioneeDeviceProxy = nullptr;
if (CHIP_NO_ERROR == mCommissioner->GetDeviceBeingCommissioned(mDestinationId, &commissioneeDeviceProxy))
{
return SendCommandForType(CommandType::kRequestCommissioningApproval, commissioneeDeviceProxy);
}

mCommandType = CommandType::kRequestCommissioningApproval;
return mCommissioner->GetConnectedDevice(mDestinationId, &mOnDeviceConnectedCallback, &mOnDeviceConnectionFailureCallback);
}

CHIP_ERROR CommissionerControl::CommissionNode(uint64_t requestId, uint16_t responseTimeoutSeconds)
{
VerifyOrReturnError(mCommissioner != nullptr, CHIP_ERROR_INCORRECT_STATE);

ChipLogProgress(NotSpecified, "Sending CommissionNode to node " ChipLogFormatX64, ChipLogValueX64(mDestinationId));

mCommissionNode.requestID = requestId;
mCommissionNode.responseTimeoutSeconds = responseTimeoutSeconds;

CommissioneeDeviceProxy * commissioneeDeviceProxy = nullptr;
if (CHIP_NO_ERROR == mCommissioner->GetDeviceBeingCommissioned(mDestinationId, &commissioneeDeviceProxy))
{
return SendCommandForType(CommandType::kCommissionNode, commissioneeDeviceProxy);
}

mCommandType = CommandType::kCommissionNode;
return mCommissioner->GetConnectedDevice(mDestinationId, &mOnDeviceConnectedCallback, &mOnDeviceConnectionFailureCallback);
}

void CommissionerControl::OnResponse(app::CommandSender * client, const app::ConcreteCommandPath & path,
const app::StatusIB & status, TLV::TLVReader * data)
{
ChipLogProgress(NotSpecified, "CommissionerControl: OnResponse.");

CHIP_ERROR error = status.ToChipError();
if (CHIP_NO_ERROR != error)
{
ChipLogError(NotSpecified, "Response Failure: %s", ErrorStr(error));
return;
}

if (data != nullptr)
{
DeviceMgr().HandleCommandResponse(path, *data);
}
}

void CommissionerControl::OnError(const app::CommandSender * client, CHIP_ERROR error)
{
// Handle the error, then reset mCommandSender
ChipLogProgress(NotSpecified, "CommissionerControl: OnError: Error: %s", ErrorStr(error));
mCommandSender.reset();
}

void CommissionerControl::OnDone(app::CommandSender * client)
{
ChipLogProgress(NotSpecified, "CommissionerControl: OnDone.");

switch (mCommandType)
{
case CommandType::kRequestCommissioningApproval:
ChipLogProgress(NotSpecified, "CommissionerControl: Command RequestCommissioningApproval has been successfully processed.");
break;

case CommandType::kCommissionNode:
ChipLogProgress(NotSpecified, "CommissionerControl: Command CommissionNode has been successfully processed.");
break;

default:
ChipLogError(NotSpecified, "CommissionerControl: Unknown or unhandled command type in OnDone.");
break;
}

// Reset command type to undefined after processing is done
mCommandType = CommandType::kUndefined;

// Ensure that mCommandSender is cleaned up after it is done
mCommandSender.reset();
}

CHIP_ERROR CommissionerControl::SendCommandForType(CommandType commandType, DeviceProxy * device)
{
switch (commandType)
{
case CommandType::kRequestCommissioningApproval:
return SendCommand(device, mEndpointId, app::Clusters::CommissionerControl::Id,
app::Clusters::CommissionerControl::Commands::RequestCommissioningApproval::Id,
mRequestCommissioningApproval);
case CommandType::kCommissionNode:
return SendCommand(device, mEndpointId, app::Clusters::CommissionerControl::Id,
app::Clusters::CommissionerControl::Commands::CommissionNode::Id, mCommissionNode);
default:
return CHIP_ERROR_INVALID_ARGUMENT;
}
}

void CommissionerControl::OnDeviceConnectedFn(void * context, Messaging::ExchangeManager & exchangeMgr,
const SessionHandle & sessionHandle)
{
CommissionerControl * self = reinterpret_cast<CommissionerControl *>(context);
VerifyOrReturn(self != nullptr, ChipLogError(NotSpecified, "OnDeviceConnectedFn: context is null"));

OperationalDeviceProxy device(&exchangeMgr, sessionHandle);

CHIP_ERROR err = self->SendCommandForType(self->mCommandType, &device);
;
LogErrorOnFailure(err);
}

void CommissionerControl::OnDeviceConnectionFailureFn(void * context, const ScopedNodeId & peerId, CHIP_ERROR err)
{
LogErrorOnFailure(err);

CommissionerControl * self = reinterpret_cast<CommissionerControl *>(context);
VerifyOrReturn(self != nullptr, ChipLogError(NotSpecified, "OnDeviceConnectionFailureFn: context is null"));
}
122 changes: 122 additions & 0 deletions examples/fabric-admin/device_manager/CommissionerControl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright (c) 2024 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.
*
*/

#pragma once

#include <app/CommandSender.h>
#include <controller/CHIPDeviceController.h>

/**
* @class CommissionerControl
* @brief This class handles commissioning control operations using CHIP commands, including sending
* commissioning approval requests and commissioning nodes.
*
* The class implements the `chip::app::CommandSender::Callback` interface to handle responses, errors,
* and completion events for the commands it sends. It allows sending commands related to commissioning
* and tracks the status of the operations.
*/
class CommissionerControl : public chip::app::CommandSender::Callback
{
public:
CommissionerControl() :
mOnDeviceConnectedCallback(OnDeviceConnectedFn, this), mOnDeviceConnectionFailureCallback(OnDeviceConnectionFailureFn, this)
{}

/**
* @brief Initializes the CommissionerControl with a DeviceCommissioner, NodeId, and EndpointId.
*
* @param commissioner The DeviceCommissioner to use for the commissioning process.
* @param nodeId The node ID of the remote fabric bridge.
* @param endpointId The endpoint on which to send CommissionerControl commands.
*/
void Init(chip::Controller::DeviceCommissioner & commissioner, chip::NodeId nodeId, chip::EndpointId endpointId);

/**
* @brief Sends a RequestCommissioningApproval command to the device.
*
* @param requestId The unique request ID.
* @param vendorId The vendor ID of the device.
* @param productId The product ID of the device.
* @param label Optional label for the device.
* @return CHIP_ERROR CHIP_NO_ERROR on success, or an appropriate error code on failure.
*/
CHIP_ERROR RequestCommissioningApproval(uint64_t requestId, uint16_t vendorId, uint16_t productId,
chip::Optional<chip::CharSpan> label);
/**
* @brief Sends a CommissionNode command to the device.
*
* @param requestId The unique request ID.
* @param responseTimeoutSeconds Timeout for the response in seconds.
* @return CHIP_ERROR CHIP_NO_ERROR on success, or an appropriate error code on failure.
*/
CHIP_ERROR CommissionNode(uint64_t requestId, uint16_t responseTimeoutSeconds);

/////////// CommandSender Callback Interface /////////
virtual void OnResponse(chip::app::CommandSender * client, const chip::app::ConcreteCommandPath & path,
const chip::app::StatusIB & status, chip::TLV::TLVReader * data) override;

virtual void OnError(const chip::app::CommandSender * client, CHIP_ERROR error) override;

virtual void OnDone(chip::app::CommandSender * client) override;

private:
enum class CommandType : uint8_t
{
kUndefined = 0,
kRequestCommissioningApproval = 1,
kCommissionNode = 2,
};

template <class T>
CHIP_ERROR SendCommand(chip::DeviceProxy * device, chip::EndpointId endpointId, chip::ClusterId clusterId,
chip::CommandId commandId, const T & value)
{
chip::app::CommandPathParams commandPath = { endpointId, clusterId, commandId,
(chip::app::CommandPathFlags::kEndpointIdValid) };
mCommandSender = std::make_unique<chip::app::CommandSender>(this, device->GetExchangeManager(), false, false,
device->GetSecureSession().Value()->AllowsLargePayload());

VerifyOrReturnError(mCommandSender != nullptr, CHIP_ERROR_NO_MEMORY);

chip::app::CommandSender::AddRequestDataParameters addRequestDataParams(chip::NullOptional);
// Using TestOnly AddRequestData to allow for an intentionally malformed request for server validation testing.
ReturnErrorOnFailure(mCommandSender->TestOnlyAddRequestDataNoTimedCheck(commandPath, value, addRequestDataParams));
ReturnErrorOnFailure(mCommandSender->SendCommandRequest(device->GetSecureSession().Value()));

return CHIP_NO_ERROR;
}

CHIP_ERROR SendCommandForType(CommandType commandType, chip::DeviceProxy * device);

static void OnDeviceConnectedFn(void * context, chip::Messaging::ExchangeManager & exchangeMgr,
const chip::SessionHandle & sessionHandle);
static void OnDeviceConnectionFailureFn(void * context, const chip::ScopedNodeId & peerId, CHIP_ERROR error);

// Private data members
chip::Controller::DeviceCommissioner * mCommissioner = nullptr;
std::unique_ptr<chip::app::CommandSender> mCommandSender;
chip::NodeId mDestinationId = chip::kUndefinedNodeId;
chip::EndpointId mEndpointId = chip::kRootEndpointId;
CommandType mCommandType = CommandType::kUndefined;

chip::Callback::Callback<chip::OnDeviceConnected> mOnDeviceConnectedCallback;
chip::Callback::Callback<chip::OnDeviceConnectionFailure> mOnDeviceConnectionFailureCallback;

chip::app::Clusters::CommissionerControl::Commands::RequestCommissioningApproval::Type mRequestCommissioningApproval;
chip::app::Clusters::CommissionerControl::Commands::CommissionNode::Type mCommissionNode;
};
26 changes: 18 additions & 8 deletions examples/fabric-admin/device_manager/DeviceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ void DeviceManager::HandleReadSupportedDeviceCategories(TLV::TLVReader & data)
if (value.Has(CommissionerControl::SupportedDeviceCategoryBitmap::kFabricSynchronization))
{
ChipLogProgress(NotSpecified, "Remote Fabric-Bridge supports Fabric Synchronization, start reverse commissioning.");
mCommissionerControl.Init(PairingManager::Instance().CurrentCommissioner(), mRemoteBridgeNodeId, kAggregatorEndpointId);
RequestCommissioningApproval();
}
}
Expand All @@ -258,12 +259,17 @@ void DeviceManager::RequestCommissioningApproval()
uint16_t vendorId = static_cast<uint16_t>(CHIP_DEVICE_CONFIG_DEVICE_VENDOR_ID);
uint16_t productId = static_cast<uint16_t>(CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_ID);

StringBuilder<kMaxCommandSize> commandBuilder;
commandBuilder.Add("commissionercontrol request-commissioning-approval ");
commandBuilder.AddFormat("%lu %u %u %lu %d", requestId, vendorId, productId, mRemoteBridgeNodeId, kAggregatorEndpointId);
CHIP_ERROR error = mCommissionerControl.RequestCommissioningApproval(requestId, vendorId, productId, NullOptional);

if (error != CHIP_NO_ERROR)
{
ChipLogError(NotSpecified,
"Failed to request commissioning-approval to the remote bridge (NodeId: %lu). Error: %" CHIP_ERROR_FORMAT,
mRemoteBridgeNodeId, error.Format());
return;
}

mRequestId = requestId;
PushCommand(commandBuilder.c_str());
}

void DeviceManager::HandleCommissioningRequestResult(TLV::TLVReader & data)
Expand Down Expand Up @@ -397,11 +403,15 @@ void DeviceManager::SendCommissionNodeRequest(uint64_t requestId, uint16_t respo
{
ChipLogProgress(NotSpecified, "Request the Commissioner Control Server to begin commissioning a previously approved request.");

StringBuilder<kMaxCommandSize> commandBuilder;
commandBuilder.Add("commissionercontrol commission-node ");
commandBuilder.AddFormat("%lu %u %lu %d", requestId, responseTimeoutSeconds, mRemoteBridgeNodeId, kAggregatorEndpointId);
CHIP_ERROR error = mCommissionerControl.CommissionNode(requestId, responseTimeoutSeconds);

PushCommand(commandBuilder.c_str());
if (error != CHIP_NO_ERROR)
{
ChipLogError(NotSpecified,
"Failed to send CommissionNode command to the remote bridge (NodeId: %lu). Error: %" CHIP_ERROR_FORMAT,
mRemoteBridgeNodeId, error.Format());
return;
}
}

void DeviceManager::HandleReverseOpenCommissioningWindow(TLV::TLVReader & data)
Expand Down
4 changes: 3 additions & 1 deletion examples/fabric-admin/device_manager/DeviceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
#pragma once

#include <app-common/zap-generated/cluster-objects.h>
#include <device_manager/CommissionerControl.h>
#include <device_manager/PairingManager.h>
#include <platform/CHIPDeviceLayer.h>

#include <set>

constexpr uint32_t kDefaultSetupPinCode = 20202021;
Expand Down Expand Up @@ -209,6 +209,8 @@ class DeviceManager : public PairingDelegate
bool mAutoSyncEnabled = false;
bool mInitialized = false;
uint64_t mRequestId = 0;

CommissionerControl mCommissionerControl;
};

/**
Expand Down

0 comments on commit 515ff47

Please sign in to comment.