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] Refactor to use API methods instead of PushCommand (3/3) #36033

Merged
merged 5 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}
161 changes: 161 additions & 0 deletions examples/fabric-admin/device_manager/BridgeSubscription.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
* 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)
yufengwangca marked this conversation as resolved.
Show resolved Hide resolved
{
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());

// Reset the subscription state to allow retry
subscriptionStarted = false;
yufengwangca marked this conversation as resolved.
Show resolved Hide resolved
}

void BridgeSubscription::OnDone(ReadClient * apReadClient)
{
mClient.reset();
ChipLogProgress(NotSpecified, "The remote fabric sync bridge subscription is terminated");
yufengwangca marked this conversation as resolved.
Show resolved Hide resolved

// 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;

yufengwangca marked this conversation as resolved.
Show resolved Hide resolved
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
Loading