From b3fc1d08238b8f062f58d4fa5d2ec86a97553614 Mon Sep 17 00:00:00 2001 From: Yufeng Wang Date: Thu, 30 May 2024 23:29:17 -0700 Subject: [PATCH] [Fabric-Bridge] Refactor device management from main (#33618) * [Fabric-Bridge] Refactor device management from main * Address review comments --- examples/fabric-bridge-app/linux/BUILD.gn | 2 + examples/fabric-bridge-app/linux/Device.cpp | 6 +- .../fabric-bridge-app/linux/DeviceManager.cpp | 130 +++++++++ .../linux/include/DeviceManager.h | 68 +++++ examples/fabric-bridge-app/linux/main.cpp | 265 ++++-------------- 5 files changed, 260 insertions(+), 211 deletions(-) create mode 100644 examples/fabric-bridge-app/linux/DeviceManager.cpp create mode 100644 examples/fabric-bridge-app/linux/include/DeviceManager.h diff --git a/examples/fabric-bridge-app/linux/BUILD.gn b/examples/fabric-bridge-app/linux/BUILD.gn index 21c93344416b62..ea7e6e0b31b331 100644 --- a/examples/fabric-bridge-app/linux/BUILD.gn +++ b/examples/fabric-bridge-app/linux/BUILD.gn @@ -22,7 +22,9 @@ executable("fabric-bridge-app") { sources = [ "${chip_root}/examples/fabric-bridge-app/fabric-bridge-common/include/CHIPProjectAppConfig.h", "Device.cpp", + "DeviceManager.cpp", "include/Device.h", + "include/DeviceManager.h", "main.cpp", ] diff --git a/examples/fabric-bridge-app/linux/Device.cpp b/examples/fabric-bridge-app/linux/Device.cpp index 40cd8c007e8baa..4cf72a281113b6 100644 --- a/examples/fabric-bridge-app/linux/Device.cpp +++ b/examples/fabric-bridge-app/linux/Device.cpp @@ -46,11 +46,11 @@ void Device::SetReachable(bool aReachable) if (aReachable) { - ChipLogProgress(DeviceLayer, "Device[%s]: ONLINE", mName); + ChipLogProgress(NotSpecified, "Device[%s]: ONLINE", mName); } else { - ChipLogProgress(DeviceLayer, "Device[%s]: OFFLINE", mName); + ChipLogProgress(NotSpecified, "Device[%s]: OFFLINE", mName); } if (changed) @@ -63,7 +63,7 @@ void Device::SetName(const char * szName) { bool changed = (strncmp(mName, szName, sizeof(mName)) != 0); - ChipLogProgress(DeviceLayer, "Device[%s]: New Name=\"%s\"", mName, szName); + ChipLogProgress(NotSpecified, "Device[%s]: New Name=\"%s\"", mName, szName); chip::Platform::CopyString(mName, szName); diff --git a/examples/fabric-bridge-app/linux/DeviceManager.cpp b/examples/fabric-bridge-app/linux/DeviceManager.cpp new file mode 100644 index 00000000000000..a5ffe68445949b --- /dev/null +++ b/examples/fabric-bridge-app/linux/DeviceManager.cpp @@ -0,0 +1,130 @@ +/* + * + * 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 "DeviceManager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::Credentials; +using namespace chip::Inet; +using namespace chip::Transport; +using namespace chip::DeviceLayer; +using namespace chip::app::Clusters; + +namespace { +constexpr uint8_t kMaxRetries = 10; +} // namespace + +DeviceManager::DeviceManager() +{ + memset(mDevices, 0, sizeof(mDevices)); + mFirstDynamicEndpointId = static_cast( + static_cast(emberAfEndpointFromIndex(static_cast(emberAfFixedEndpointCount() - 1))) + 1); + mCurrentEndpointId = mFirstDynamicEndpointId; +} + +int DeviceManager::AddDeviceEndpoint(Device * dev, EmberAfEndpointType * ep, + const chip::Span & deviceTypeList, + const chip::Span & dataVersionStorage, chip::EndpointId parentEndpointId) +{ + uint8_t index = 0; + while (index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) + { + if (nullptr == mDevices[index]) + { + mDevices[index] = dev; + CHIP_ERROR err; + int retryCount = 0; + while (retryCount < kMaxRetries) + { + DeviceLayer::StackLock lock; + dev->SetEndpointId(mCurrentEndpointId); + dev->SetParentEndpointId(parentEndpointId); + err = + 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); + return index; + } + if (err != CHIP_ERROR_ENDPOINT_EXISTS) + { + return -1; // Return error as endpoint addition failed due to an error other than endpoint already exists + } + // Increment the endpoint ID and handle wrap condition + if (++mCurrentEndpointId < mFirstDynamicEndpointId) + { + mCurrentEndpointId = mFirstDynamicEndpointId; + } + retryCount++; + } + ChipLogError(NotSpecified, "Failed to add dynamic endpoint after %d retries", kMaxRetries); + return -1; // Return error as all retries are exhausted + } + index++; + } + ChipLogProgress(NotSpecified, "Failed to add dynamic endpoint: No endpoints available!"); + return -1; +} + +int DeviceManager::RemoveDeviceEndpoint(Device * dev) +{ + uint8_t index = 0; + while (index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) + { + if (mDevices[index] == dev) + { + DeviceLayer::StackLock lock; + // Silence complaints about unused ep when progress logging + // disabled. + [[maybe_unused]] EndpointId ep = emberAfClearDynamicEndpoint(index); + mDevices[index] = nullptr; + ChipLogProgress(NotSpecified, "Removed device %s from dynamic endpoint %d (index=%d)", dev->GetName(), ep, index); + return index; + } + index++; + } + return -1; +} + +Device * DeviceManager::GetDevice(uint16_t index) const +{ + if (index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) + { + return mDevices[index]; + } + return nullptr; +} diff --git a/examples/fabric-bridge-app/linux/include/DeviceManager.h b/examples/fabric-bridge-app/linux/include/DeviceManager.h new file mode 100644 index 00000000000000..8e87c9059bcb78 --- /dev/null +++ b/examples/fabric-bridge-app/linux/include/DeviceManager.h @@ -0,0 +1,68 @@ +/* + * + * 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 + +#include "Device.h" + +class DeviceManager +{ +public: + DeviceManager(); + + /** + * @brief Adds a device to a dynamic endpoint. + * + * This function attempts to add a device to a dynamic endpoint. It tries to find an available + * endpoint slot and retries the addition process up to a specified maximum number of times if + * the endpoint already exists. If the addition is successful, it returns the index of the + * 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); + + /** + * @brief Removes a device from a dynamic endpoint. + * + * This function attempts to remove a device from a dynamic endpoint by iterating through the + * available endpoints and checking if the device matches. 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 dev A pointer to the device to be removed. + * @return int The index of the removed dynamic endpoint if successful, -1 otherwise. + */ + int RemoveDeviceEndpoint(Device * dev); + + Device * GetDevice(uint16_t index) const; + +private: + chip::EndpointId mCurrentEndpointId; + chip::EndpointId mFirstDynamicEndpointId; + Device * mDevices[CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT + 1]; +}; diff --git a/examples/fabric-bridge-app/linux/main.cpp b/examples/fabric-bridge-app/linux/main.cpp index d639cea1ca0697..600d4a2a376d55 100644 --- a/examples/fabric-bridge-app/linux/main.cpp +++ b/examples/fabric-bridge-app/linux/main.cpp @@ -17,54 +17,27 @@ */ #include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "CommissionableInit.h" #include "Device.h" +#include "DeviceManager.h" + +#include +#include -#include -#include #include #include #include using namespace chip; -using namespace chip::app; -using namespace chip::Credentials; -using namespace chip::Inet; -using namespace chip::Transport; -using namespace chip::DeviceLayer; -using namespace chip::app::Clusters; #define POLL_INTERVAL_MS (100) +#define ZCL_DESCRIPTOR_CLUSTER_REVISION (1u) +#define ZCL_BRIDGED_DEVICE_BASIC_INFORMATION_CLUSTER_REVISION (2u) +#define ZCL_BRIDGED_DEVICE_BASIC_INFORMATION_FEATURE_MAP (0u) namespace { -EndpointId gCurrentEndpointId; -EndpointId gFirstDynamicEndpointId; -Device * gDevices[CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT + 1]; - bool KeyboardHit() { int bytesWaiting; @@ -81,7 +54,7 @@ void BridgePollingThread() int ch = getchar(); if (ch == 'e') { - ChipLogProgress(DeviceLayer, "Exiting....."); + ChipLogProgress(NotSpecified, "Exiting....."); exit(0); } continue; @@ -92,213 +65,89 @@ void BridgePollingThread() } } -} // namespace - -// REVISION DEFINITIONS: -// ================================================================================= - -#define ZCL_DESCRIPTOR_CLUSTER_REVISION (1u) -#define ZCL_BRIDGED_DEVICE_BASIC_INFORMATION_CLUSTER_REVISION (2u) -#define ZCL_BRIDGED_DEVICE_BASIC_INFORMATION_FEATURE_MAP (0u) - -// --------------------------------------------------------------------------- - -int AddDeviceEndpoint(Device * dev, EmberAfEndpointType * ep, const Span & deviceTypeList, - const Span & dataVersionStorage, chip::EndpointId parentEndpointId = chip::kInvalidEndpointId) -{ - uint8_t index = 0; - const int maxRetries = 10; // Set the maximum number of retries - while (index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) - { - if (nullptr == gDevices[index]) - { - gDevices[index] = dev; - CHIP_ERROR err; - int retryCount = 0; - while (retryCount < maxRetries) - { - DeviceLayer::StackLock lock; - dev->SetEndpointId(gCurrentEndpointId); - dev->SetParentEndpointId(parentEndpointId); - err = - emberAfSetDynamicEndpoint(index, gCurrentEndpointId, ep, dataVersionStorage, deviceTypeList, parentEndpointId); - if (err == CHIP_NO_ERROR) - { - ChipLogProgress(DeviceLayer, "Added device %s to dynamic endpoint %d (index=%d)", dev->GetName(), - gCurrentEndpointId, index); - return index; - } - if (err != CHIP_ERROR_ENDPOINT_EXISTS) - { - return -1; // Return error as endpoint addition failed due to an error other than endpoint already exists - } - // Increment the endpoint ID and handle wrap condition - if (++gCurrentEndpointId < gFirstDynamicEndpointId) - { - gCurrentEndpointId = gFirstDynamicEndpointId; - } - retryCount++; - } - ChipLogError(DeviceLayer, "Failed to add dynamic endpoint after %d retries", maxRetries); - return -1; // Return error as all retries are exhausted - } - index++; - } - ChipLogProgress(DeviceLayer, "Failed to add dynamic endpoint: No endpoints available!"); - return -1; -} +DeviceManager gDeviceManager; -int RemoveDeviceEndpoint(Device * dev) -{ - uint8_t index = 0; - while (index < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) - { - if (gDevices[index] == dev) - { - DeviceLayer::StackLock lock; - // Silence complaints about unused ep when progress logging - // disabled. - [[maybe_unused]] EndpointId ep = emberAfClearDynamicEndpoint(index); - gDevices[index] = nullptr; - ChipLogProgress(DeviceLayer, "Removed device %s from dynamic endpoint %d (index=%d)", dev->GetName(), ep, index); - return index; - } - index++; - } - return -1; -} +} // namespace -namespace { -void CallReportingCallback(intptr_t closure) +void ApplicationInit() { - auto path = reinterpret_cast(closure); - MatterReportingAttributeChangeCallback(*path); - Platform::Delete(path); + // Start a thread for bridge polling + std::thread pollingThread(BridgePollingThread); + pollingThread.detach(); } -void ScheduleReportingCallback(Device * dev, ClusterId cluster, AttributeId attribute) -{ - auto * path = Platform::New(dev->GetEndpointId(), cluster, attribute); - PlatformMgr().ScheduleWork(CallReportingCallback, reinterpret_cast(path)); -} -} // anonymous namespace +void ApplicationShutdown() {} -void HandleDeviceStatusChanged(Device * dev, Device::Changed_t itemChangedMask) +int main(int argc, char * argv[]) { - if (itemChangedMask & Device::kChanged_Reachable) - { - ScheduleReportingCallback(dev, BridgedDeviceBasicInformation::Id, BridgedDeviceBasicInformation::Attributes::Reachable::Id); - } - - if (itemChangedMask & Device::kChanged_Name) + if (ChipLinuxAppInit(argc, argv) != 0) { - ScheduleReportingCallback(dev, BridgedDeviceBasicInformation::Id, BridgedDeviceBasicInformation::Attributes::NodeLabel::Id); + return -1; } -} - -Protocols::InteractionModel::Status HandleReadBridgedDeviceBasicAttribute(Device * dev, chip::AttributeId attributeId, - uint8_t * buffer, uint16_t maxReadLength) -{ - using namespace BridgedDeviceBasicInformation::Attributes; - - ChipLogProgress(DeviceLayer, "HandleReadBridgedDeviceBasicAttribute: attrId=%d, maxReadLength=%d", attributeId, maxReadLength); - if ((attributeId == Reachable::Id) && (maxReadLength == 1)) - { - *buffer = dev->IsReachable() ? 1 : 0; - } - else if ((attributeId == NodeLabel::Id) && (maxReadLength == 32)) - { - MutableByteSpan zclNameSpan(buffer, maxReadLength); - MakeZclCharString(zclNameSpan, dev->GetName()); - } - else if ((attributeId == ClusterRevision::Id) && (maxReadLength == 2)) - { - uint16_t rev = ZCL_BRIDGED_DEVICE_BASIC_INFORMATION_CLUSTER_REVISION; - memcpy(buffer, &rev, sizeof(rev)); - } - else if ((attributeId == FeatureMap::Id) && (maxReadLength == 4)) - { - uint32_t featureMap = ZCL_BRIDGED_DEVICE_BASIC_INFORMATION_FEATURE_MAP; - memcpy(buffer, &featureMap, sizeof(featureMap)); - } - else - { - return Protocols::InteractionModel::Status::Failure; - } + ChipLinuxAppMainLoop(); - return Protocols::InteractionModel::Status::Success; + return 0; } +// External attribute read callback function Protocols::InteractionModel::Status emberAfExternalAttributeReadCallback(EndpointId endpoint, ClusterId clusterId, const EmberAfAttributeMetadata * attributeMetadata, uint8_t * buffer, uint16_t maxReadLength) { - uint16_t endpointIndex = emberAfGetDynamicIndexFromEndpoint(endpoint); + uint16_t endpointIndex = emberAfGetDynamicIndexFromEndpoint(endpoint); + AttributeId attributeId = attributeMetadata->attributeId; - Protocols::InteractionModel::Status ret = Protocols::InteractionModel::Status::Failure; - - if ((endpointIndex < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) && (gDevices[endpointIndex] != nullptr)) + Device * dev = gDeviceManager.GetDevice(endpointIndex); + if (dev != nullptr && clusterId == app::Clusters::BridgedDeviceBasicInformation::Id) { - Device * dev = gDevices[endpointIndex]; + using namespace app::Clusters::BridgedDeviceBasicInformation::Attributes; + ChipLogProgress(NotSpecified, "HandleReadBridgedDeviceBasicAttribute: attrId=%d, maxReadLength=%d", attributeId, + maxReadLength); - if (clusterId == BridgedDeviceBasicInformation::Id) + if ((attributeId == Reachable::Id) && (maxReadLength == 1)) + { + *buffer = dev->IsReachable() ? 1 : 0; + } + else if ((attributeId == NodeLabel::Id) && (maxReadLength == 32)) + { + MutableByteSpan zclNameSpan(buffer, maxReadLength); + MakeZclCharString(zclNameSpan, dev->GetName()); + } + else if ((attributeId == ClusterRevision::Id) && (maxReadLength == 2)) + { + uint16_t rev = ZCL_BRIDGED_DEVICE_BASIC_INFORMATION_CLUSTER_REVISION; + memcpy(buffer, &rev, sizeof(rev)); + } + else if ((attributeId == FeatureMap::Id) && (maxReadLength == 4)) + { + uint32_t featureMap = ZCL_BRIDGED_DEVICE_BASIC_INFORMATION_FEATURE_MAP; + memcpy(buffer, &featureMap, sizeof(featureMap)); + } + else { - ret = HandleReadBridgedDeviceBasicAttribute(dev, attributeMetadata->attributeId, buffer, maxReadLength); + return Protocols::InteractionModel::Status::Failure; } + return Protocols::InteractionModel::Status::Success; } - return ret; + return Protocols::InteractionModel::Status::Failure; } +// External attribute write callback function Protocols::InteractionModel::Status emberAfExternalAttributeWriteCallback(EndpointId endpoint, ClusterId clusterId, const EmberAfAttributeMetadata * attributeMetadata, uint8_t * buffer) { - uint16_t endpointIndex = emberAfGetDynamicIndexFromEndpoint(endpoint); - + uint16_t endpointIndex = emberAfGetDynamicIndexFromEndpoint(endpoint); Protocols::InteractionModel::Status ret = Protocols::InteractionModel::Status::Failure; - if (endpointIndex < CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT) + Device * dev = gDeviceManager.GetDevice(endpointIndex); + if (dev != nullptr && dev->IsReachable()) { - Device * dev = gDevices[endpointIndex]; - - if (dev->IsReachable()) - { - ChipLogProgress(DeviceLayer, "emberAfExternalAttributeWriteCallback: ep=%d, clusterId=%d", endpoint, clusterId); - ret = Protocols::InteractionModel::Status::Success; - } + ChipLogProgress(NotSpecified, "emberAfExternalAttributeWriteCallback: ep=%d, clusterId=%d", endpoint, clusterId); + ret = Protocols::InteractionModel::Status::Success; } return ret; } - -void ApplicationInit() -{ - // Clear out the device database - memset(gDevices, 0, sizeof(gDevices)); - - // Set starting endpoint id where dynamic endpoints will be assigned, which - // will be the next consecutive endpoint id after the last fixed endpoint. - gFirstDynamicEndpointId = static_cast( - static_cast(emberAfEndpointFromIndex(static_cast(emberAfFixedEndpointCount() - 1))) + 1); - gCurrentEndpointId = gFirstDynamicEndpointId; - - // Start a thread for bridge polling - std::thread pollingThread(BridgePollingThread); - pollingThread.detach(); -} - -void ApplicationShutdown() {} - -int main(int argc, char * argv[]) -{ - if (ChipLinuxAppInit(argc, argv) != 0) - { - return -1; - } - - ChipLinuxAppMainLoop(); - - return 0; -}