Skip to content

Commit

Permalink
[Fabric-Admin] Refactor to use API methods instead of PushCommand (3/…
Browse files Browse the repository at this point in the history
…3) (#36033)

* [Fabric-Admin] Implement the bridge subscription API

* Restyle

* Address review comments

* Reset the subscription state only in OnDone

* Keep subscription
  • Loading branch information
yufengwangca authored and pull[bot] committed Oct 18, 2024
1 parent eb8ad83 commit 1128818
Show file tree
Hide file tree
Showing 6 changed files with 250 additions and 20 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/BridgeSubscription.cpp",
"device_manager/BridgeSubscription.h",
"device_manager/DeviceManager.cpp",
"device_manager/DeviceManager.h",
"device_manager/DeviceSubscription.cpp",
Expand Down
2 changes: 0 additions & 2 deletions examples/fabric-admin/commands/clusters/ReportCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,4 @@ void ReportCommand::OnEventData(const app::EventHeader & eventHeader, TLV::TLVRe
}

LogErrorOnFailure(RemoteDataModelLogger::LogEventAsJSON(eventHeader, data));

DeviceMgr().HandleEventData(eventHeader, *data);
}
159 changes: 159 additions & 0 deletions examples/fabric-admin/device_manager/BridgeSubscription.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/*
* 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.
*
*/

#include "BridgeSubscription.h"
#include <device_manager/DeviceManager.h>

using namespace ::chip;
using namespace ::chip::app;
using chip::app::ReadClient;

namespace {

constexpr uint16_t kSubscribeMinInterval = 0;
constexpr uint16_t kSubscribeMaxInterval = 60;

void OnDeviceConnectedWrapper(void * context, Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle)
{
reinterpret_cast<BridgeSubscription *>(context)->OnDeviceConnected(exchangeMgr, sessionHandle);
}

void OnDeviceConnectionFailureWrapper(void * context, const ScopedNodeId & peerId, CHIP_ERROR error)
{
reinterpret_cast<BridgeSubscription *>(context)->OnDeviceConnectionFailure(peerId, error);
}

} // namespace

BridgeSubscription::BridgeSubscription() :
mOnDeviceConnectedCallback(OnDeviceConnectedWrapper, this),
mOnDeviceConnectionFailureCallback(OnDeviceConnectionFailureWrapper, this)
{}

CHIP_ERROR BridgeSubscription::StartSubscription(Controller::DeviceController & controller, NodeId nodeId, EndpointId endpointId)
{
assertChipStackLockedByCurrentThread();

VerifyOrDie(!subscriptionStarted); // Ensure it's not called multiple times.

// Mark as started
subscriptionStarted = true;

mEndpointId = endpointId;

CHIP_ERROR err = controller.GetConnectedDevice(nodeId, &mOnDeviceConnectedCallback, &mOnDeviceConnectionFailureCallback);
if (err != CHIP_NO_ERROR)
{
ChipLogError(NotSpecified, "Failed to connect to remote fabric sync bridge %" CHIP_ERROR_FORMAT, err.Format());
}
return err;
}

void BridgeSubscription::OnAttributeData(const ConcreteDataAttributePath & path, TLV::TLVReader * data, const StatusIB & status)
{
if (!status.IsSuccess())
{
ChipLogError(NotSpecified, "Response Failure: %" CHIP_ERROR_FORMAT, status.ToChipError().Format());
return;
}

if (data == nullptr)
{
ChipLogError(NotSpecified, "Response Failure: No Data");
return;
}

DeviceMgr().HandleAttributeData(path, *data);
}

void BridgeSubscription::OnEventData(const app::EventHeader & eventHeader, TLV::TLVReader * data, const app::StatusIB * status)
{
if (status != nullptr)
{
CHIP_ERROR error = status->ToChipError();
if (CHIP_NO_ERROR != error)
{
ChipLogError(NotSpecified, "Response Failure: %" CHIP_ERROR_FORMAT, error.Format());
return;
}
}

if (data == nullptr)
{
ChipLogError(NotSpecified, "Response Failure: No Data");
return;
}

DeviceMgr().HandleEventData(eventHeader, *data);
}

void BridgeSubscription::OnError(CHIP_ERROR error)
{
ChipLogProgress(NotSpecified, "Error on remote fabric sync bridge subscription: %" CHIP_ERROR_FORMAT, error.Format());
}

void BridgeSubscription::OnDone(ReadClient * apReadClient)
{
mClient.reset();
ChipLogProgress(NotSpecified, "The remote fabric sync bridge subscription is terminated");

// Reset the subscription state to allow retry
subscriptionStarted = false;

// TODO:(#36092) Fabric-Admin should attempt to re-subscribe when the subscription to the remote bridge is terminated.
}

void BridgeSubscription::OnDeviceConnected(Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle)
{
mClient = std::make_unique<ReadClient>(app::InteractionModelEngine::GetInstance(), &exchangeMgr /* echangeMgr */,
*this /* callback */, ReadClient::InteractionType::Subscribe);
VerifyOrDie(mClient);

AttributePathParams readPaths[1];
readPaths[0] = AttributePathParams(mEndpointId, Clusters::Descriptor::Id, Clusters::Descriptor::Attributes::PartsList::Id);

EventPathParams eventPaths[1];
eventPaths[0] = EventPathParams(mEndpointId, Clusters::CommissionerControl::Id,
Clusters::CommissionerControl::Events::CommissioningRequestResult::Id);
eventPaths[0].mIsUrgentEvent = true;

ReadPrepareParams readParams(sessionHandle);

readParams.mpAttributePathParamsList = readPaths;
readParams.mAttributePathParamsListSize = 1;
readParams.mpEventPathParamsList = eventPaths;
readParams.mEventPathParamsListSize = 1;
readParams.mMinIntervalFloorSeconds = kSubscribeMinInterval;
readParams.mMaxIntervalCeilingSeconds = kSubscribeMaxInterval;
readParams.mKeepSubscriptions = true;

CHIP_ERROR err = mClient->SendRequest(readParams);

if (err != CHIP_NO_ERROR)
{
ChipLogError(NotSpecified, "Failed to issue subscription to the Descriptor Cluster of the remote bridged device.");
OnDone(nullptr);
return;
}
}

void BridgeSubscription::OnDeviceConnectionFailure(const ScopedNodeId & peerId, CHIP_ERROR error)
{
ChipLogError(NotSpecified, "BridgeSubscription failed to connect to " ChipLogFormatX64, ChipLogValueX64(peerId.GetNodeId()));
OnDone(nullptr);
}
77 changes: 77 additions & 0 deletions examples/fabric-admin/device_manager/BridgeSubscription.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* 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/ReadClient.h>
#include <controller/CHIPDeviceController.h>

#include <memory>
#include <optional>

/**
* @brief Class used to subscribe to attributes and events from the remote bridged device.
*
* The Descriptor Cluster contains attributes such as the Parts List, which provides a list
* of endpoints or devices that are part of a composite device or bridge. The CommissionerControl
* Cluster generates events related to commissioning requests, which can be monitored to track
* device commissioning status.
*
* When subscribing to attributes and events of a bridged device from another fabric, the class:
* - Establishes a secure session with the device (if needed) via CASE (Chip over
* Authenticated Session Establishment) session.
* - Subscribes to the specified attributes in the Descriptor Cluster (e.g., Parts List) and
* events in the CommissionerControl Cluster (e.g., CommissioningRequestResult) of the remote
* device on the specified node and endpoint.
* - Invokes the provided callback upon successful or unsuccessful subscription, allowing
* further handling of data or errors.
*
* This class also implements the necessary callbacks to handle attribute data reports, event data,
* errors, and session establishment procedures.
*/
class BridgeSubscription : public chip::app::ReadClient::Callback
{
public:
BridgeSubscription();

CHIP_ERROR StartSubscription(chip::Controller::DeviceController & controller, chip::NodeId nodeId, chip::EndpointId endpointId);

///////////////////////////////////////////////////////////////
// ReadClient::Callback implementation
///////////////////////////////////////////////////////////////
void OnAttributeData(const chip::app::ConcreteDataAttributePath & path, chip::TLV::TLVReader * data,
const chip::app::StatusIB & status) override;
void OnEventData(const chip::app::EventHeader & eventHeader, chip::TLV::TLVReader * data,
const chip::app::StatusIB * status) override;
void OnError(CHIP_ERROR error) override;
void OnDone(chip::app::ReadClient * apReadClient) override;

///////////////////////////////////////////////////////////////
// callbacks for CASE session establishment
///////////////////////////////////////////////////////////////
void OnDeviceConnected(chip::Messaging::ExchangeManager & exchangeMgr, const chip::SessionHandle & sessionHandle);
void OnDeviceConnectionFailure(const chip::ScopedNodeId & peerId, CHIP_ERROR error);

private:
std::unique_ptr<chip::app::ReadClient> mClient;

chip::Callback::Callback<chip::OnDeviceConnected> mOnDeviceConnectedCallback;
chip::Callback::Callback<chip::OnDeviceConnectionFailure> mOnDeviceConnectionFailureCallback;
chip::EndpointId mEndpointId;
bool subscriptionStarted = false;
};
27 changes: 9 additions & 18 deletions examples/fabric-admin/device_manager/DeviceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ namespace {
constexpr EndpointId kAggregatorEndpointId = 1;
constexpr uint16_t kWindowTimeout = 300;
constexpr uint16_t kIteration = 1000;
constexpr uint16_t kSubscribeMinInterval = 0;
constexpr uint16_t kSubscribeMaxInterval = 60;
constexpr uint16_t kAggragatorEndpointId = 1;
constexpr uint16_t kMaxDiscriminatorLength = 4095;

} // namespace
Expand Down Expand Up @@ -193,23 +190,17 @@ void DeviceManager::UnpairLocalFabricBridge()

void DeviceManager::SubscribeRemoteFabricBridge()
{
// Listen to the state changes of the remote fabric bridge.
StringBuilder<kMaxCommandSize> commandBuilder;

// Prepare and push the descriptor subscribe command
commandBuilder.Add("descriptor subscribe parts-list ");
commandBuilder.AddFormat("%d %d %lu %d", kSubscribeMinInterval, kSubscribeMaxInterval, mRemoteBridgeNodeId,
kAggragatorEndpointId);
PushCommand(commandBuilder.c_str());
ChipLogProgress(NotSpecified, "Start subscription to the remote bridge.")

// Clear the builder for the next command
commandBuilder.Reset();
CHIP_ERROR error = mBridgeSubscriber.StartSubscription(PairingManager::Instance().CurrentCommissioner(),
mRemoteBridgeNodeId, kAggregatorEndpointId);

// Prepare and push the commissioner control subscribe command
commandBuilder.Add("commissionercontrol subscribe-event commissioning-request-result ");
commandBuilder.AddFormat("%d %d %lu %d --is-urgent true --keepSubscriptions true", kSubscribeMinInterval, kSubscribeMaxInterval,
mRemoteBridgeNodeId, kAggregatorEndpointId);
PushCommand(commandBuilder.c_str());
if (error != CHIP_NO_ERROR)
{
ChipLogError(NotSpecified, "Failed to subscribe to the remote bridge (NodeId: %lu). Error: %" CHIP_ERROR_FORMAT,
mRemoteBridgeNodeId, error.Format());
return;
}
}

void DeviceManager::ReadSupportedDeviceCategories()
Expand Down
3 changes: 3 additions & 0 deletions examples/fabric-admin/device_manager/DeviceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#pragma once

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

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

BridgeSubscription mBridgeSubscriber;
};

/**
Expand Down

0 comments on commit 1128818

Please sign in to comment.