diff --git a/examples/fabric-bridge-app/linux/Device.cpp b/examples/fabric-bridge-app/linux/Device.cpp index 215db48f683ef5..e72fd856513b5a 100644 --- a/examples/fabric-bridge-app/linux/Device.cpp +++ b/examples/fabric-bridge-app/linux/Device.cpp @@ -25,11 +25,11 @@ using namespace chip::app::Clusters::Actions; -Device::Device(chip::NodeId nodeId, const char * name) +Device::Device(chip::NodeId nodeId) { - chip::Platform::CopyString(mName, name); mReachable = false; - mEndpointId = 0; + mNodeId = nodeId; + mEndpointId = chip::kInvalidEndpointId; } bool Device::IsReachable() diff --git a/examples/fabric-bridge-app/linux/DeviceManager.cpp b/examples/fabric-bridge-app/linux/DeviceManager.cpp index 3b158b876bf033..bbfff03ee5cdad 100644 --- a/examples/fabric-bridge-app/linux/DeviceManager.cpp +++ b/examples/fabric-bridge-app/linux/DeviceManager.cpp @@ -46,6 +46,77 @@ using namespace chip::app::Clusters; namespace { constexpr uint8_t kMaxRetries = 10; +constexpr int kNodeLabelSize = 32; + +// Current ZCL implementation of Struct uses a max-size array of 254 bytes +constexpr int kDescriptorAttributeArraySize = 254; + +// ENDPOINT DEFINITIONS: +// ================================================================================= +// +// Endpoint definitions will be reused across multiple endpoints for every instance of the +// endpoint type. +// There will be no intrinsic storage for the endpoint attributes declared here. +// Instead, all attributes will be treated as EXTERNAL, and therefore all reads +// or writes to the attributes must be handled within the emberAfExternalAttributeWriteCallback +// and emberAfExternalAttributeReadCallback functions declared herein. This fits +// the typical model of a bridge, since a bridge typically maintains its own +// state database representing the devices connected to it. + +// (taken from matter-devices.xml) +#define DEVICE_TYPE_BRIDGED_NODE 0x0013 + +// Device Version for dynamic endpoints: +#define DEVICE_VERSION_DEFAULT 1 + +// --------------------------------------------------------------------------- +// +// SYNCED DEVICE ENDPOINT: contains the following clusters: +// - Descriptor +// - Bridged Device Basic Information +// - Administrator Commissioning + +// Declare Descriptor cluster attributes +DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(descriptorAttrs) +DECLARE_DYNAMIC_ATTRIBUTE(Descriptor::Attributes::DeviceTypeList::Id, ARRAY, kDescriptorAttributeArraySize, 0), /* device list */ + DECLARE_DYNAMIC_ATTRIBUTE(Descriptor::Attributes::ServerList::Id, ARRAY, kDescriptorAttributeArraySize, 0), /* server list */ + DECLARE_DYNAMIC_ATTRIBUTE(Descriptor::Attributes::ClientList::Id, ARRAY, kDescriptorAttributeArraySize, 0), /* client list */ + DECLARE_DYNAMIC_ATTRIBUTE(Descriptor::Attributes::PartsList::Id, ARRAY, kDescriptorAttributeArraySize, 0), /* parts list */ + DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); + +// Declare Bridged Device Basic Information cluster attributes +DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(bridgedDeviceBasicAttrs) +DECLARE_DYNAMIC_ATTRIBUTE(BridgedDeviceBasicInformation::Attributes::NodeLabel::Id, CHAR_STRING, kNodeLabelSize, 0), /* NodeLabel */ + DECLARE_DYNAMIC_ATTRIBUTE(BridgedDeviceBasicInformation::Attributes::Reachable::Id, BOOLEAN, 1, 0), /* Reachable */ + DECLARE_DYNAMIC_ATTRIBUTE(BridgedDeviceBasicInformation::Attributes::FeatureMap::Id, BITMAP32, 4, 0), /* feature map */ + DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); + +// Declare Administrator Commissioning cluster attributes +DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(AdministratorCommissioningAttrs) +DECLARE_DYNAMIC_ATTRIBUTE(AdministratorCommissioning::Attributes::WindowStatus::Id, ENUM8, 1, 0), /* NodeLabel */ + DECLARE_DYNAMIC_ATTRIBUTE(AdministratorCommissioning::Attributes::AdminFabricIndex::Id, FABRIC_IDX, 1, 0), /* Reachable */ + DECLARE_DYNAMIC_ATTRIBUTE(AdministratorCommissioning::Attributes::AdminVendorId::Id, VENDOR_ID, 2, 0), /* Reachable */ + DECLARE_DYNAMIC_ATTRIBUTE_LIST_END(); + +constexpr CommandId administratorCommissioningCommands[] = { + app::Clusters::AdministratorCommissioning::Commands::OpenCommissioningWindow::Id, + app::Clusters::AdministratorCommissioning::Commands::OpenBasicCommissioningWindow::Id, + app::Clusters::AdministratorCommissioning::Commands::RevokeCommissioning::Id, + kInvalidCommandId, +}; + +// Declare Cluster List for Bridged Node endpoint +DECLARE_DYNAMIC_CLUSTER_LIST_BEGIN(bridgedNodeClusters) +DECLARE_DYNAMIC_CLUSTER(Descriptor::Id, descriptorAttrs, ZAP_CLUSTER_MASK(SERVER), nullptr, nullptr), + DECLARE_DYNAMIC_CLUSTER(BridgedDeviceBasicInformation::Id, bridgedDeviceBasicAttrs, ZAP_CLUSTER_MASK(SERVER), nullptr, nullptr), + DECLARE_DYNAMIC_CLUSTER(AdministratorCommissioning::Id, AdministratorCommissioningAttrs, ZAP_CLUSTER_MASK(SERVER), + administratorCommissioningCommands, nullptr) DECLARE_DYNAMIC_CLUSTER_LIST_END; + +// Declare Bridged Node endpoint +DECLARE_DYNAMIC_ENDPOINT(sBridgedNodeEndpoint, bridgedNodeClusters); +DataVersion sBridgedNodeDataVersions[ArraySize(bridgedNodeClusters)]; + +const EmberAfDeviceType sBridgedDeviceTypes[] = { { DEVICE_TYPE_BRIDGED_NODE, DEVICE_VERSION_DEFAULT } }; } // namespace @@ -60,11 +131,13 @@ void DeviceManager::Init() mCurrentEndpointId = mFirstDynamicEndpointId; } -int DeviceManager::AddDeviceEndpoint(Device * dev, EmberAfEndpointType * ep, - const chip::Span & deviceTypeList, - const chip::Span & dataVersionStorage, chip::EndpointId parentEndpointId) +int DeviceManager::AddDeviceEndpoint(Device * dev, chip::EndpointId parentEndpointId) { - uint8_t index = 0; + uint8_t index = 0; + EmberAfEndpointType * ep = &sBridgedNodeEndpoint; + const chip::Span & deviceTypeList = Span(sBridgedDeviceTypes); + const chip::Span & dataVersionStorage = Span(sBridgedNodeDataVersions); + while (index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) { if (nullptr == mDevices[index]) @@ -81,8 +154,9 @@ int DeviceManager::AddDeviceEndpoint(Device * dev, EmberAfEndpointType * ep, emberAfSetDynamicEndpoint(index, mCurrentEndpointId, ep, dataVersionStorage, deviceTypeList, parentEndpointId); if (err == CHIP_NO_ERROR) { - ChipLogProgress(NotSpecified, "Added device %s to dynamic endpoint %d (index=%d)", dev->GetName(), - mCurrentEndpointId, index); + ChipLogProgress(NotSpecified, + "Added device with nodeId=0x" ChipLogFormatX64 " to dynamic endpoint %d (index=%d)", + ChipLogValueX64(dev->GetNodeId()), mCurrentEndpointId, index); return index; } if (err != CHIP_ERROR_ENDPOINT_EXISTS) @@ -125,11 +199,43 @@ int DeviceManager::RemoveDeviceEndpoint(Device * dev) return -1; } -Device * DeviceManager::GetDevice(uint16_t index) const +Device * DeviceManager::GetDevice(chip::EndpointId endpointId) const +{ + for (uint8_t index = 0; index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; ++index) + { + if (mDevices[index] && mDevices[index]->GetEndpointId() == endpointId) + { + return mDevices[index]; + } + } + return nullptr; +} + +Device * DeviceManager::GetDeviceByNodeId(chip::NodeId nodeId) const { - if (index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) + for (uint8_t index = 0; index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; ++index) { - return mDevices[index]; + if (mDevices[index] && mDevices[index]->GetNodeId() == nodeId) + { + return mDevices[index]; + } } return nullptr; } + +int DeviceManager::RemoveDeviceByNodeId(chip::NodeId nodeId) +{ + for (uint8_t index = 0; index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; ++index) + { + if (mDevices[index] && mDevices[index]->GetNodeId() == nodeId) + { + DeviceLayer::StackLock lock; + EndpointId ep = emberAfClearDynamicEndpoint(index); + mDevices[index] = nullptr; + ChipLogProgress(NotSpecified, "Removed device with NodeId=0x" ChipLogFormatX64 " from dynamic endpoint %d (index=%d)", + ChipLogValueX64(nodeId), ep, index); + return index; + } + } + return -1; +} diff --git a/examples/fabric-bridge-app/linux/RpcClient.cpp b/examples/fabric-bridge-app/linux/RpcClient.cpp index c09a447cff28c9..815250f12bc328 100644 --- a/examples/fabric-bridge-app/linux/RpcClient.cpp +++ b/examples/fabric-bridge-app/linux/RpcClient.cpp @@ -65,7 +65,7 @@ CHIP_ERROR InitRpcClient(uint16_t rpcServerPort) CHIP_ERROR OpenCommissioningWindow(NodeId nodeId) { - ChipLogProgress(NotSpecified, "OpenCommissioningWindow\n"); + ChipLogProgress(NotSpecified, "OpenCommissioningWindow with Node Id 0x:" ChipLogFormatX64, ChipLogValueX64(nodeId)); if (openCommissioningWindowCall.active()) { diff --git a/examples/fabric-bridge-app/linux/RpcServer.cpp b/examples/fabric-bridge-app/linux/RpcServer.cpp index c971811b193016..088fc9a50a38b8 100644 --- a/examples/fabric-bridge-app/linux/RpcServer.cpp +++ b/examples/fabric-bridge-app/linux/RpcServer.cpp @@ -20,26 +20,49 @@ #include "pw_rpc_system_server/rpc_server.h" #include "pw_rpc_system_server/socket.h" -#include +#include + +#include #include #if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE #include "pigweed/rpc_services/FabricBridge.h" #endif +#include "Device.h" +#include "DeviceManager.h" + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; + namespace { #if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE class FabricBridge final : public chip::rpc::FabricBridge { public: - pw::Status AddSynchronizedDevice(const chip_rpc_SynchronizedDevice & request, pw_protobuf_Empty & response) override + pw::Status AddSynchronizedDevice(const chip_rpc_SynchronizedDevice & request, pw_protobuf_Empty & response) override; +}; + +pw::Status FabricBridge::AddSynchronizedDevice(const chip_rpc_SynchronizedDevice & request, pw_protobuf_Empty & response) +{ + NodeId nodeId = request.node_id; + ChipLogProgress(NotSpecified, "Received AddSynchronizedDevice: " ChipLogFormatX64, ChipLogValueX64(nodeId)); + + Device * device = new Device(nodeId); + device->SetReachable(true); + + int result = DeviceMgr().AddDeviceEndpoint(device, 1); + if (result == -1) { - chip::NodeId nodeId = request.node_id; - ChipLogProgress(NotSpecified, "Received AddSynchronizedDevice: " ChipLogFormatX64, ChipLogValueX64(nodeId)); - return pw::OkStatus(); + delete device; + ChipLogError(NotSpecified, "Failed to add device with nodeId=0x" ChipLogFormatX64, ChipLogValueX64(nodeId)); + return pw::Status::Unknown(); } -}; + + return pw::OkStatus(); +} FabricBridge fabric_bridge_service; #endif // defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE diff --git a/examples/fabric-bridge-app/linux/include/Device.h b/examples/fabric-bridge-app/linux/include/Device.h index 64940f33c23190..c709af8cf0695b 100644 --- a/examples/fabric-bridge-app/linux/include/Device.h +++ b/examples/fabric-bridge-app/linux/include/Device.h @@ -32,7 +32,7 @@ class Device public: static const int kDeviceNameSize = 32; - Device(chip::NodeId nodeId, const char * name); + Device(chip::NodeId nodeId); virtual ~Device() {} bool IsReachable(); @@ -41,6 +41,7 @@ class Device void SetLocation(std::string location) { mLocation = location; }; inline void SetEndpointId(chip::EndpointId id) { mEndpointId = id; }; inline chip::EndpointId GetEndpointId() { return mEndpointId; }; + inline chip::NodeId GetNodeId() { return mNodeId; }; inline void SetParentEndpointId(chip::EndpointId id) { mParentEndpointId = id; }; inline chip::EndpointId GetParentEndpointId() { return mParentEndpointId; }; inline char * GetName() { return mName; }; diff --git a/examples/fabric-bridge-app/linux/include/DeviceManager.h b/examples/fabric-bridge-app/linux/include/DeviceManager.h index 2667fc071d4ca8..b1dd791033511f 100644 --- a/examples/fabric-bridge-app/linux/include/DeviceManager.h +++ b/examples/fabric-bridge-app/linux/include/DeviceManager.h @@ -27,6 +27,12 @@ class DeviceManager public: DeviceManager() = default; + /** + * @brief Initializes the DeviceManager. + * + * This function sets up the initial state of the DeviceManager, clearing + * any existing devices and setting the starting dynamic endpoint ID. + */ void Init(); /** @@ -38,15 +44,10 @@ class DeviceManager * dynamic endpoint; otherwise, it returns -1. * * @param dev A pointer to the device to be added. - * @param ep A pointer to the endpoint type. - * @param deviceTypeList A span containing the list of device types. - * @param dataVersionStorage A span containing the data version storage. * @param parentEndpointId The parent endpoint ID. Defaults to an invalid endpoint ID. * @return int The index of the dynamic endpoint if successful, -1 otherwise. */ - int AddDeviceEndpoint(Device * dev, EmberAfEndpointType * ep, const chip::Span & deviceTypeList, - const chip::Span & dataVersionStorage, - chip::EndpointId parentEndpointId = chip::kInvalidEndpointId); + int AddDeviceEndpoint(Device * dev, chip::EndpointId parentEndpointId = chip::kInvalidEndpointId); /** * @brief Removes a device from a dynamic endpoint. @@ -61,7 +62,40 @@ class DeviceManager */ int RemoveDeviceEndpoint(Device * dev); - Device * GetDevice(uint16_t index) const; + /** + * @brief Gets a device from its endpoint ID. + * + * This function iterates through the available devices and returns the device that matches the + * specified endpoint ID. If no device matches the endpoint ID, it returns nullptr. + * + * @param endpointId The endpoint ID of the device to be retrieved. + * @return Device* A pointer to the device if found, nullptr otherwise. + */ + Device * GetDevice(chip::EndpointId endpointId) const; + + /** + * @brief Gets a device from its NodeId. + * + * This function iterates through the available devices and returns the device that matches the + * specified NodeId. If no device matches the NodeId, it returns nullptr. + * + * @param nodeId The NodeId of the device to be retrieved. + * @return Device* A pointer to the device if found, nullptr otherwise. + */ + Device * GetDeviceByNodeId(chip::NodeId nodeId) const; + + /** + * @brief Removes a device from a dynamic endpoint by its NodeId. + * + * This function attempts to remove a device from a dynamic endpoint by iterating through the + * available endpoints and checking if the device matches the specified NodeId. If the device is + * found, it clears the dynamic endpoint, logs the removal, and returns the index of the removed + * endpoint. If the device is not found, it returns -1. + * + * @param nodeId The NodeId of the device to be removed. + * @return int The index of the removed dynamic endpoint if successful, -1 otherwise. + */ + int RemoveDeviceByNodeId(chip::NodeId nodeId); private: friend DeviceManager & DeviceMgr();