Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fabric-Admin] Implement CommissionerControl command APIs #36057

Merged
merged 10 commits into from
Oct 17, 2024
2 changes: 2 additions & 0 deletions examples/fabric-admin/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ static_library("fabric-admin-utils") {
"commands/pairing/ToTLVCert.cpp",
"device_manager/BridgeSubscription.cpp",
"device_manager/BridgeSubscription.h",
"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
141 changes: 141 additions & 0 deletions examples/fabric-admin/device_manager/CommissionerControl.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#include "CommissionerControl.h"
#include <device_manager/DeviceManager.h>

using namespace ::chip;

void CommissionerControl::Init(Controller::DeviceCommissioner & commissioner, NodeId nodeId, EndpointId endpointId)
{
// Ensure that mCommissioner is not already initialized
VerifyOrDie(mCommissioner == nullptr);

ChipLogProgress(NotSpecified, "Initilize CommissionerControl");
yufengwangca marked this conversation as resolved.
Show resolved Hide resolved
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;

if (label.HasValue())
{
VerifyOrReturnError(label.Value().size() <= kMaxDeviceLabelLength, CHIP_ERROR_BUFFER_TOO_SMALL);
memcpy(mLabelBuffer, label.Value().data(), label.Value().size());
yufengwangca marked this conversation as resolved.
Show resolved Hide resolved
mRequestCommissioningApproval.label = Optional<Span<const char>>(CharSpan(mLabelBuffer, label.Value().size()));
}

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;

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));
}

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);
if (err != CHIP_NO_ERROR)
{
ChipLogError(NotSpecified, "Failed to send CommissionerControl command.");
self->OnDone(nullptr);
}
}

void CommissionerControl::OnDeviceConnectionFailureFn(void * context, const ScopedNodeId & peerId, CHIP_ERROR err)
{
LogErrorOnFailure(err);
CommissionerControl * self = reinterpret_cast<CommissionerControl *>(context);
VerifyOrReturn(self != nullptr, ChipLogError(NotSpecified, "OnDeviceConnectedFn: context is null"));
self->OnDone(nullptr);
}
125 changes: 125 additions & 0 deletions examples/fabric-admin/device_manager/CommissionerControl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* 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 sending CHIP commands related to commissioning, including sending
* commissioning approval requests and commissioning nodes.
*
* The class acts as a command sender and implements the `chip::app::CommandSender::Callback` interface
* to handle responses, errors, and completion events for the commands it sends. It relies on external
* CCTRL delegate and server mechanisms to manage the overall protocol and state transitions, including
* processing the CommissioningRequestResult and invoking CommissionNode.
*/
class CommissionerControl : public chip::app::CommandSender::Callback
yufengwangca marked this conversation as resolved.
Show resolved Hide resolved
{
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:
static constexpr uint16_t kMaxDeviceLabelLength = 64;

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);
ReturnErrorOnFailure(mCommandSender->AddRequestData(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;
char mLabelBuffer[kMaxDeviceLabelLength];

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;
};
Loading
Loading