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

FabricSync example changed to use ScopedNodeId in some locations #35805

Closed
wants to merge 7 commits into from
Closed
Changes from 4 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
4 changes: 2 additions & 2 deletions examples/common/pigweed/protos/fabric_admin_service.proto
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ package chip.rpc;

// Define the message for a synchronized end device with necessary fields
message DeviceCommissioningWindowInfo {
uint64 node_id = 1;
uint64 device_handle_id = 1;
uint32 commissioning_timeout = 2;
uint32 discriminator = 3;
uint32 iterations = 4;
@@ -25,7 +25,7 @@ message DeviceCommissioningInfo {
}

message KeepActiveParameters {
uint64 node_id = 1;
uint64 device_handle_id = 1;
uint32 stay_active_duration_ms = 2;
uint32 timeout_ms = 3;
}
8 changes: 5 additions & 3 deletions examples/common/pigweed/protos/fabric_bridge_service.proto
Original file line number Diff line number Diff line change
@@ -6,7 +6,9 @@ package chip.rpc;

// Define the message for a synchronized end device with necessary fields
message SynchronizedDevice {
uint64 node_id = 1;
// Using device_handle_id instead of node_id because over multiple fabrics
// there can be overlapping node_ids that are not unique.
uint64 device_handle_id = 1;

optional string unique_id = 2;
optional string vendor_name = 3;
@@ -22,12 +24,12 @@ message SynchronizedDevice {
}

message KeepActiveChanged {
uint64 node_id = 1;
uint64 device_handle_id = 1;
uint32 promised_active_duration_ms = 2;
}

message AdministratorCommissioningChanged {
uint64 node_id = 1;
uint64 device_handle_id = 1;
uint32 window_status = 2;
optional uint32 opener_fabric_index = 3;
optional uint32 opener_vendor_id = 4;
2 changes: 2 additions & 0 deletions examples/fabric-admin/BUILD.gn
Original file line number Diff line number Diff line change
@@ -80,6 +80,8 @@ static_library("fabric-admin-utils") {
"commands/pairing/OpenCommissioningWindowCommand.h",
"commands/pairing/PairingCommand.cpp",
"commands/pairing/ToTLVCert.cpp",
"device_manager/BridgeAdminDeviceMapper.cpp",
Copy link
Contributor Author

@tehampson tehampson Sep 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am open to alternative naming suggestion

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ScopedNodeIdHandleMapper? Reflects the role of mapping between ScopedNodeId and handles. But I am not clear why can't we just use ScopedNodeId directly.

"device_manager/BridgeAdminDeviceMapper.h",
"device_manager/DeviceManager.cpp",
"device_manager/DeviceManager.h",
"device_manager/DeviceSubscription.cpp",
58 changes: 58 additions & 0 deletions examples/fabric-admin/device_manager/BridgeAdminDeviceMapper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
*
* 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 "BridgeAdminDeviceMapper.h"

#include <limits>

#include <lib/support/CodeUtils.h>

std::optional<uint64_t> BridgeAdminDeviceMapper::AddScopedNodeId(const chip::ScopedNodeId & scopedNodeId)
{
// We are assuming that we will never run out of HandleIds
VerifyOrDie(mNextHandleId != std::numeric_limits<uint64_t>::max());
VerifyOrReturnValue(mScopedNodeIdToHandleId.find(scopedNodeId) == mScopedNodeIdToHandleId.end(), std::nullopt);

uint64_t handleId = mNextHandleId;
mHandleIdToScopedNodeId[handleId] = scopedNodeId;
mScopedNodeIdToHandleId[scopedNodeId] = handleId;
mNextHandleId++;
return handleId;
}

void BridgeAdminDeviceMapper::RemoveScopedNodeIdByHandleId(uint64_t handleId)
{
auto it = mHandleIdToScopedNodeId.find(handleId);
VerifyOrReturn(it != mHandleIdToScopedNodeId.end());
mScopedNodeIdToHandleId.erase(it->second);
mHandleIdToScopedNodeId.erase(handleId);
}

std::optional<uint64_t> BridgeAdminDeviceMapper::GetHandleId(const chip::ScopedNodeId & scopedNodeId)
{
auto scopedNodeIterator = mScopedNodeIdToHandleId.find(scopedNodeId);
VerifyOrReturnValue(scopedNodeIterator != mScopedNodeIdToHandleId.end(), std::nullopt);
return scopedNodeIterator->second;
}

std::optional<chip::ScopedNodeId> BridgeAdminDeviceMapper::GetScopedNodeId(uint64_t handleId)
{
auto it = mHandleIdToScopedNodeId.find(handleId);
VerifyOrReturnValue(it != mHandleIdToScopedNodeId.end(), std::nullopt);
return it->second;
}
58 changes: 58 additions & 0 deletions examples/fabric-admin/device_manager/BridgeAdminDeviceMapper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
*
* 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 <memory>
#include <optional>
#include <set>
#include <unordered_map>

#include <lib/core/DataModelTypes.h>
#include <lib/core/ScopedNodeId.h>

struct ScopedNodeIdHasher
{
std::size_t operator()(const chip::ScopedNodeId & scopedNodeId) const
{
std::size_t h1 = std::hash<uint64_t>{}(scopedNodeId.GetFabricIndex());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NodeId within ScopedNodeId is 64-bit, so is it possible for different ScopedNodeId instances to map to the same hash value, even if the chances are minimized?

Copy link
Contributor Author

@tehampson tehampson Oct 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Collisions are fine. Overall a hash table is best case O(1), when there isn't a collision and worst case of O(n) when there are collisions all the time

https://en.wikipedia.org/wiki/Hash_table#Collision_resolution

unordered_map's default hashing funtion already maps to size_t right so already there is always a risk of a collision with anything you have ever used in the past

std::size_t h2 = std::hash<uint64_t>{}(scopedNodeId.GetNodeId());
// Bitshifting h2 reduces collisions where fabricIndex == nodeId resulting
// in hash return of 0.
return h1 ^ (h2 << 1);
}
};

// Bi-directional translation between handle for aggregator and information about the
// the device required for fabric admin to communicate with local device.
class BridgeAdminDeviceMapper
{
public:
std::optional<uint64_t> AddScopedNodeId(const chip::ScopedNodeId & scopedNodeId);
void RemoveScopedNodeIdByHandleId(uint64_t handleId);

std::optional<uint64_t> GetHandleId(const chip::ScopedNodeId & scopedNodeId);
std::optional<chip::ScopedNodeId> GetScopedNodeId(uint64_t handleId);

private:
uint64_t mNextHandleId = 0;
// If we ever need more data other than ScopedNodeId we can change
// mHandleIdToScopedNodeId value from ScopedNodeId to AggregatorDeviceInfo.
std::unordered_map<uint64_t, chip::ScopedNodeId> mHandleIdToScopedNodeId;
std::unordered_map<chip::ScopedNodeId, uint64_t, ScopedNodeIdHasher> mScopedNodeIdToHandleId;
};
5 changes: 5 additions & 0 deletions examples/fabric-admin/device_manager/DeviceManager.h
Original file line number Diff line number Diff line change
@@ -18,6 +18,8 @@

#pragma once

#include "BridgeAdminDeviceMapper.h"

#include <app-common/zap-generated/cluster-objects.h>
#include <commands/pairing/PairingCommand.h>
#include <platform/CHIPDeviceLayer.h>
@@ -174,6 +176,8 @@ class DeviceManager : public PairingDelegate
Device * FindDeviceByEndpoint(chip::EndpointId endpointId);
Device * FindDeviceByNode(chip::NodeId nodeId);

BridgeAdminDeviceMapper & BridgeToAdminDeviceMapper() { return mLocalBridgeToAdminDeviceMapper; }

private:
friend DeviceManager & DeviceMgr();

@@ -206,6 +210,7 @@ class DeviceManager : public PairingDelegate
chip::NodeId mLocalBridgeNodeId = chip::kUndefinedNodeId;

std::set<Device> mSyncedDevices;
BridgeAdminDeviceMapper mLocalBridgeToAdminDeviceMapper;
bool mAutoSyncEnabled = false;
bool mInitialized = false;
uint64_t mRequestId = 0;
6 changes: 3 additions & 3 deletions examples/fabric-admin/device_manager/DeviceSubscription.cpp
Original file line number Diff line number Diff line change
@@ -207,16 +207,16 @@ void DeviceSubscription::OnDeviceConnectionFailure(const ScopedNodeId & peerId,
}

CHIP_ERROR DeviceSubscription::StartSubscription(OnDoneCallback onDoneCallback, Controller::DeviceController & controller,
NodeId nodeId)
NodeId nodeId, uint64_t handleId)
{
assertChipStackLockedByCurrentThread();
VerifyOrDie(mState == State::Idle);

mNodeId = nodeId;

#if defined(PW_RPC_ENABLED)
mCurrentAdministratorCommissioningAttributes = chip_rpc_AdministratorCommissioningChanged_init_default;
mCurrentAdministratorCommissioningAttributes.node_id = nodeId;
mCurrentAdministratorCommissioningAttributes = chip_rpc_AdministratorCommissioningChanged_init_default;
mCurrentAdministratorCommissioningAttributes.device_handle_id = handleId;
mCurrentAdministratorCommissioningAttributes.window_status =
static_cast<uint32_t>(Clusters::AdministratorCommissioning::CommissioningWindowStatusEnum::kWindowNotOpen);
#endif
2 changes: 1 addition & 1 deletion examples/fabric-admin/device_manager/DeviceSubscription.h
Original file line number Diff line number Diff line change
@@ -44,7 +44,7 @@ class DeviceSubscription : public chip::app::ReadClient::Callback
DeviceSubscription();

CHIP_ERROR StartSubscription(OnDoneCallback onDoneCallback, chip::Controller::DeviceController & controller,
chip::NodeId nodeId);
chip::NodeId nodeId, uint64_t handleId);

/// This will trigger stopping the subscription. Once subscription is stopped the OnDoneCallback
/// provided in StartSubscription will be called to indicate that subscription have been terminated.
Original file line number Diff line number Diff line change
@@ -38,7 +38,7 @@ DeviceSubscriptionManager & DeviceSubscriptionManager::Instance()
return instance;
}

CHIP_ERROR DeviceSubscriptionManager::StartSubscription(Controller::DeviceController & controller, NodeId nodeId)
CHIP_ERROR DeviceSubscriptionManager::StartSubscription(Controller::DeviceController & controller, NodeId nodeId, uint64_t handleId)
{
assertChipStackLockedByCurrentThread();
auto it = mDeviceSubscriptionMap.find(nodeId);
@@ -47,7 +47,7 @@ CHIP_ERROR DeviceSubscriptionManager::StartSubscription(Controller::DeviceContro
auto deviceSubscription = std::make_unique<DeviceSubscription>();
VerifyOrReturnError(deviceSubscription, CHIP_ERROR_NO_MEMORY);
ReturnErrorOnFailure(deviceSubscription->StartSubscription(
[this](NodeId aNodeId) { this->DeviceSubscriptionTerminated(aNodeId); }, controller, nodeId));
[this](NodeId aNodeId) { this->DeviceSubscriptionTerminated(aNodeId); }, controller, nodeId, handleId));

mDeviceSubscriptionMap[nodeId] = std::move(deviceSubscription);
return CHIP_NO_ERROR;
Original file line number Diff line number Diff line change
@@ -32,7 +32,7 @@ class DeviceSubscriptionManager

/// Usually called after we have added a synchronized device to fabric-bridge to monitor
/// for any changes that need to be propagated to fabric-bridge.
CHIP_ERROR StartSubscription(chip::Controller::DeviceController & controller, chip::NodeId nodeId);
CHIP_ERROR StartSubscription(chip::Controller::DeviceController & controller, chip::NodeId nodeId, uint64_t handleId);

CHIP_ERROR RemoveSubscription(chip::NodeId nodeId);

10 changes: 7 additions & 3 deletions examples/fabric-admin/device_manager/DeviceSynchronization.cpp
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@

#include "DeviceSynchronization.h"

#include "BridgeAdminDeviceMapper.h"
#include "DeviceSubscriptionManager.h"

#if defined(PW_RPC_ENABLED)
@@ -206,7 +207,6 @@ void DeviceSynchronizer::StartDeviceSynchronization(Controller::DeviceController

#if defined(PW_RPC_ENABLED)
mCurrentDeviceData = chip_rpc_SynchronizedDevice_init_default;
mCurrentDeviceData.node_id = nodeId;
mCurrentDeviceData.has_is_icd = true;
mCurrentDeviceData.is_icd = deviceIsIcd;
#endif
@@ -270,13 +270,17 @@ void DeviceSynchronizer::SynchronizationCompleteAddDevice()
VerifyOrDie(mState == State::ReceivedResponse || mState == State::GettingUid);

#if defined(PW_RPC_ENABLED)
VerifyOrDie(mController);
ScopedNodeId scopedNodeId(mNodeId, mController->GetFabricIndex());
auto handleId = DeviceMgr().BridgeToAdminDeviceMapper().AddScopedNodeId(scopedNodeId);
VerifyOrDie(handleId.has_value());
mCurrentDeviceData.device_handle_id = handleId.value();
AddSynchronizedDevice(mCurrentDeviceData);
// TODO(#35077) Figure out how we should reflect CADMIN values of ICD.
if (!mCurrentDeviceData.is_icd)
{
VerifyOrDie(mController);
// TODO(#35333) Figure out how we should recover in this circumstance.
CHIP_ERROR err = DeviceSubscriptionManager::Instance().StartSubscription(*mController, mNodeId);
CHIP_ERROR err = DeviceSubscriptionManager::Instance().StartSubscription(*mController, mNodeId, handleId.value());
if (err != CHIP_NO_ERROR)
{
ChipLogError(NotSpecified, "Failed start subscription to NodeId:" ChipLogFormatX64, ChipLogValueX64(mNodeId));
8 changes: 4 additions & 4 deletions examples/fabric-admin/rpc/RpcClient.cpp
Original file line number Diff line number Diff line change
@@ -144,12 +144,12 @@ CHIP_ERROR AddSynchronizedDevice(const chip_rpc_SynchronizedDevice & data)
return WaitForResponse(call);
}

CHIP_ERROR RemoveSynchronizedDevice(NodeId nodeId)
CHIP_ERROR RemoveSynchronizedDevice(uint64_t handleId)
{
ChipLogProgress(NotSpecified, "RemoveSynchronizedDevice");

chip_rpc_SynchronizedDevice device = chip_rpc_SynchronizedDevice_init_default;
device.node_id = nodeId;
device.device_handle_id = handleId;

// The RPC call is kept alive until it completes. When a response is received, it will be logged by the handler
// function and the call will complete.
@@ -164,12 +164,12 @@ CHIP_ERROR RemoveSynchronizedDevice(NodeId nodeId)
return WaitForResponse(call);
}

CHIP_ERROR ActiveChanged(NodeId nodeId, uint32_t promisedActiveDurationMs)
CHIP_ERROR ActiveChanged(uint64_t handleId, uint32_t promisedActiveDurationMs)
{
ChipLogProgress(NotSpecified, "ActiveChanged");

chip_rpc_KeepActiveChanged parameters;
parameters.node_id = nodeId;
parameters.device_handle_id = handleId;
parameters.promised_active_duration_ms = promisedActiveDurationMs;

// The RPC call is kept alive until it completes. When a response is received, it will be logged by the handler
8 changes: 4 additions & 4 deletions examples/fabric-admin/rpc/RpcClient.h
Original file line number Diff line number Diff line change
@@ -57,25 +57,25 @@ CHIP_ERROR AddSynchronizedDevice(const chip_rpc_SynchronizedDevice & data);
* It logs the progress and checks if a `RemoveSynchronizedDevice` operation is already in progress.
* If an operation is in progress, it returns `CHIP_ERROR_BUSY`.
*
* @param nodeId The Node ID of the device to be removed.
* @param handleId The device handle ID of the device to be removed.
* @return CHIP_ERROR An error code indicating the success or failure of the operation.
* - CHIP_NO_ERROR: The RPC command was successfully processed.
* - CHIP_ERROR_BUSY: Another operation is currently in progress.
* - CHIP_ERROR_INTERNAL: An internal error occurred while activating the RPC call.
*/
CHIP_ERROR RemoveSynchronizedDevice(chip::NodeId nodeId);
CHIP_ERROR RemoveSynchronizedDevice(uint64_t handleId);

/**
* @brief Received StayActiveResponse on behalf of client that previously called KeepActive
*
* @param nodeId The Node ID of the device we recieved a StayActiveResponse.
* @param handleId The device handle ID of the device we recieved a StayActiveResponse.
* @param promisedActiveDurationMs the computed duration (in milliseconds) that the ICD intends to stay active for.
* @return CHIP_ERROR An error code indicating the success or failure of the operation.
* - CHIP_NO_ERROR: The RPC command was successfully processed.
* - CHIP_ERROR_BUSY: Another operation is currently in progress.
* - CHIP_ERROR_INTERNAL: An internal error occurred while activating the RPC call.
*/
CHIP_ERROR ActiveChanged(chip::NodeId nodeId, uint32_t promisedActiveDurationMs);
CHIP_ERROR ActiveChanged(uint64_t handleId, uint32_t promisedActiveDurationMs);

/**
* @brief CADMIN attribute has changed of one of the bridged devices that was previously added.
Loading