From 34eb1ad78dd3dee02c3d172faf4bc8bc158f27e6 Mon Sep 17 00:00:00 2001 From: Marcin Kajor Date: Wed, 2 Aug 2023 08:26:39 +0200 Subject: [PATCH] applications: Matter Bridge: architectural refactoring - allow to inject Matter and non-Matter devices to be bridged into the BridgeManager object - added devices factory to support bridged devices creation - implemented a dedicated map facilitating device object life time management - added necessary BridgeManager modifications Signed-off-by: Marcin Kajor --- applications/matter_bridge/CMakeLists.txt | 3 +- applications/matter_bridge/Kconfig | 3 + .../matter_bridge/src}/bridge_shell.cpp | 20 +- .../src/bridged_device_factory.h | 98 ++++++++++ .../common/src/bridge/bridge_manager.cpp | 175 ++++++------------ .../matter/common/src/bridge/bridge_manager.h | 41 ++-- .../matter/common/src/bridge/bridged_device.h | 2 +- .../common/src/bridge/util/bridge_util.h | 126 +++++++++++++ 8 files changed, 333 insertions(+), 135 deletions(-) rename {samples/matter/common/src/bridge => applications/matter_bridge/src}/bridge_shell.cpp (73%) create mode 100644 applications/matter_bridge/src/bridged_device_factory.h create mode 100644 samples/matter/common/src/bridge/util/bridge_util.h diff --git a/applications/matter_bridge/CMakeLists.txt b/applications/matter_bridge/CMakeLists.txt index 9b19b3de25fd..3c4c17cc557f 100644 --- a/applications/matter_bridge/CMakeLists.txt +++ b/applications/matter_bridge/CMakeLists.txt @@ -34,6 +34,7 @@ target_include_directories(app PRIVATE src/bridged_device_types ${COMMON_ROOT}/src ${COMMON_ROOT}/src/bridge/ + ${COMMON_ROOT}/src/bridge/util ${NLIO_ROOT}/include ${ZEPHYR_CONNECTEDHOMEIP_MODULE_DIR}/zzz_generated/app-common ) @@ -41,8 +42,8 @@ target_include_directories(app PRIVATE target_sources(app PRIVATE src/app_task.cpp src/main.cpp + src/bridge_shell.cpp ${COMMON_ROOT}/src/bridge/bridge_manager.cpp - ${COMMON_ROOT}/src/bridge/bridge_shell.cpp ${COMMON_ROOT}/src/bridge/bridged_device.cpp src/zap-generated/IMClusterCommandHandler.cpp src/zap-generated/callback-stub.cpp diff --git a/applications/matter_bridge/Kconfig b/applications/matter_bridge/Kconfig index 85234d4633e6..11e5fba28feb 100644 --- a/applications/matter_bridge/Kconfig +++ b/applications/matter_bridge/Kconfig @@ -41,6 +41,9 @@ config BRIDGE_HUMIDITY_SENSOR_MAX_MEASURED_VALUE endif +config EXPERIMENTAL + default y + source "${ZEPHYR_CONNECTEDHOMEIP_MODULE_DIR}/config/nrfconnect/chip-module/Kconfig.features" source "${ZEPHYR_CONNECTEDHOMEIP_MODULE_DIR}/config/nrfconnect/chip-module/Kconfig.defaults" source "${ZEPHYR_BASE}/../nrf/samples/matter/common/src/bridge/Kconfig" diff --git a/samples/matter/common/src/bridge/bridge_shell.cpp b/applications/matter_bridge/src/bridge_shell.cpp similarity index 73% rename from samples/matter/common/src/bridge/bridge_shell.cpp rename to applications/matter_bridge/src/bridge_shell.cpp index a9ce9c8aaa04..89a3821601bf 100644 --- a/samples/matter/common/src/bridge/bridge_shell.cpp +++ b/applications/matter_bridge/src/bridge_shell.cpp @@ -4,11 +4,12 @@ * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ +#include "bridge_manager.h" +#include "bridged_device_factory.h" + #include #include -#include "bridge_manager.h" - LOG_MODULE_DECLARE(app, CONFIG_CHIP_APP_LOG_LEVEL); static int add_bridged_device(const struct shell *shell, size_t argc, char **argv) @@ -20,8 +21,19 @@ static int add_bridged_device(const struct shell *shell, size_t argc, char **arg nodeLabel = argv[2]; } - CHIP_ERROR err = - GetBridgeManager().AddBridgedDevice(static_cast(deviceType), nodeLabel); + auto *newBridgedDevice = BridgeFactory::GetBridgedDeviceFactory().Create( + static_cast(deviceType), nodeLabel); + + VerifyOrReturnError(newBridgedDevice != nullptr, -EPERM, + shell_fprintf(shell, SHELL_INFO, "Cannot allocate Matter device of given type\n")); + + auto *newDataProvider = BridgeFactory::GetDataProviderFactory().Create( + static_cast(deviceType), BridgeManager::HandleUpdate); + + VerifyOrReturnError(newDataProvider != nullptr, -EPERM, delete newBridgedDevice, + shell_fprintf(shell, SHELL_INFO, "Cannot allocate data provider of given type\n")); + + CHIP_ERROR err = GetBridgeManager().AddBridgedDevices(newBridgedDevice, newDataProvider); if (err == CHIP_NO_ERROR) { shell_fprintf(shell, SHELL_INFO, "Done\n"); } else if (err == CHIP_ERROR_INVALID_STRING_LENGTH) { diff --git a/applications/matter_bridge/src/bridged_device_factory.h b/applications/matter_bridge/src/bridged_device_factory.h new file mode 100644 index 000000000000..6d80460e30f4 --- /dev/null +++ b/applications/matter_bridge/src/bridged_device_factory.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#pragma once + +#include "bridge_util.h" +#include "bridged_device.h" +#include "bridged_device_data_provider.h" +#include + +#ifdef CONFIG_BRIDGE_HUMIDITY_SENSOR_BRIDGED_DEVICE +#include "humidity_sensor.h" +#include "humidity_sensor_data_provider.h" +#endif + +#ifdef CONFIG_BRIDGE_ONOFF_LIGHT_BRIDGED_DEVICE +#include "onoff_light.h" +#include "onoff_light_data_provider.h" +#endif + +#ifdef CONFIG_BRIDGE_TEMPERATURE_SENSOR_BRIDGED_DEVICE +#include "temperature_sensor.h" +#include "temperature_sensor_data_provider.h" +#endif + +namespace BridgeFactory +{ + +using UpdateAttributeCallback = BridgedDeviceDataProvider::UpdateAttributeCallback; +using DeviceType = BridgedDevice::DeviceType; +using BridgedDeviceFactory = DeviceFactory; +using DataProviderFactory = DeviceFactory; + +auto checkLabel = [](const char *nodeLabel) { + /* If node label is provided it must fit the maximum defined length */ + if (!nodeLabel || (nodeLabel && (strlen(nodeLabel) < BridgedDevice::kNodeLabelSize))) { + return true; + } + return false; +}; + +inline BridgedDeviceFactory &GetBridgedDeviceFactory() +{ + static BridgedDeviceFactory sBridgedDeviceFactory{ +#ifdef CONFIG_BRIDGE_HUMIDITY_SENSOR_BRIDGED_DEVICE + { DeviceType::HumiditySensor, + [](const char *nodeLabel) -> BridgedDevice * { + if (!checkLabel(nodeLabel)) { + return nullptr; + } + return chip::Platform::New(nodeLabel); + } }, +#endif +#ifdef CONFIG_BRIDGE_ONOFF_LIGHT_BRIDGED_DEVICE + { DeviceType::OnOffLight, + [](const char *nodeLabel) -> BridgedDevice * { + if (!checkLabel(nodeLabel)) { + return nullptr; + } + return chip::Platform::New(nodeLabel); + } }, +#endif +#ifdef CONFIG_BRIDGE_TEMPERATURE_SENSOR_BRIDGED_DEVICE + { BridgedDevice::DeviceType::TemperatureSensor, + [](const char *nodeLabel) -> BridgedDevice * { + if (!checkLabel(nodeLabel)) { + return nullptr; + } + return chip::Platform::New(nodeLabel); + } }, +#endif + }; + return sBridgedDeviceFactory; +} + +inline DataProviderFactory &GetDataProviderFactory() +{ + static DataProviderFactory sDeviceDataProvider{ +#ifdef CONFIG_BRIDGE_HUMIDITY_SENSOR_BRIDGED_DEVICE + { DeviceType::HumiditySensor, + [](UpdateAttributeCallback clb) { return chip::Platform::New(clb); } }, +#endif +#ifdef CONFIG_BRIDGE_ONOFF_LIGHT_BRIDGED_DEVICE + { DeviceType::OnOffLight, + [](UpdateAttributeCallback clb) { return chip::Platform::New(clb); } }, +#endif +#ifdef CONFIG_BRIDGE_TEMPERATURE_SENSOR_BRIDGED_DEVICE + { DeviceType::TemperatureSensor, + [](UpdateAttributeCallback clb) { return chip::Platform::New(clb); } }, +#endif + }; + return sDeviceDataProvider; +} + +} // namespace BridgeFactory diff --git a/samples/matter/common/src/bridge/bridge_manager.cpp b/samples/matter/common/src/bridge/bridge_manager.cpp index c921544f9ab9..596839185ccc 100644 --- a/samples/matter/common/src/bridge/bridge_manager.cpp +++ b/samples/matter/common/src/bridge/bridge_manager.cpp @@ -6,21 +6,6 @@ #include "bridge_manager.h" -#if CONFIG_BRIDGE_ONOFF_LIGHT_BRIDGED_DEVICE -#include "onoff_light.h" -#include "onoff_light_data_provider.h" -#endif - -#if CONFIG_BRIDGE_TEMPERATURE_SENSOR_BRIDGED_DEVICE -#include "temperature_sensor.h" -#include "temperature_sensor_data_provider.h" -#endif - -#if CONFIG_BRIDGE_HUMIDITY_SENSOR_BRIDGED_DEVICE -#include "humidity_sensor.h" -#include "humidity_sensor_data_provider.h" -#endif - #include #include #include @@ -48,57 +33,10 @@ void BridgeManager::Init() false); } -CHIP_ERROR BridgeManager::AddBridgedDevice(BridgedDevice::DeviceType bridgedDeviceType, const char *nodeLabel) +CHIP_ERROR BridgeManager::AddBridgedDevices(BridgedDevice *aDevice, BridgedDeviceDataProvider *aDataProvider) { - Platform::UniquePtr device(nullptr); - Platform::UniquePtr dataProvider(nullptr); - - if (nodeLabel && (strlen(nodeLabel) >= BridgedDevice::kNodeLabelSize)) { - return CHIP_ERROR_INVALID_STRING_LENGTH; - } - - switch (bridgedDeviceType) { -#if CONFIG_BRIDGE_ONOFF_LIGHT_BRIDGED_DEVICE - case BridgedDevice::DeviceType::OnOffLight: { - LOG_INF("Adding OnOff Light bridged device"); - dataProvider.reset(Platform::New(BridgeManager::HandleUpdate)); - device.reset(Platform::New(nodeLabel)); - break; - } -#endif /* CONFIG_BRIDGE_ONOFF_LIGHT_BRIDGED_DEVICE */ -#if CONFIG_BRIDGE_TEMPERATURE_SENSOR_BRIDGED_DEVICE - case BridgedDevice::DeviceType::TemperatureSensor: { - LOG_INF("Adding TemperatureSensor bridged device"); - dataProvider.reset(Platform::New(BridgeManager::HandleUpdate)); - device.reset(Platform::New(nodeLabel)); - break; - } -#endif /* CONFIG_BRIDGE_TEMPERATURE_SENSOR_BRIDGED_DEVICE */ -#if CONFIG_BRIDGE_HUMIDITY_SENSOR_BRIDGED_DEVICE - case BridgedDevice::DeviceType::HumiditySensor: { - LOG_INF("Adding HumiditySensor bridged device"); - dataProvider.reset(Platform::New(BridgeManager::HandleUpdate)); - device.reset(Platform::New(nodeLabel)); - break; - } -#endif /* CONFIG_BRIDGE_HUMIDITY_SENSOR_BRIDGED_DEVICE */ - default: - return CHIP_ERROR_INVALID_ARGUMENT; - } - - if (device == nullptr || device->mDataVersion == nullptr) { - return CHIP_ERROR_NO_MEMORY; - } - - dataProvider->Init(); - CHIP_ERROR err = AddDevice(device.get()); - - /* In case adding succedeed map the objects relationship and release the ownership. */ - if (err == CHIP_NO_ERROR) { - mDevicesMap[device.get()] = dataProvider.get(); - device.release(); - dataProvider.release(); - } + aDataProvider->Init(); + CHIP_ERROR err = AddDevices(aDevice, aDataProvider); return err; } @@ -108,46 +46,68 @@ CHIP_ERROR BridgeManager::RemoveBridgedDevice(uint16_t endpoint) uint8_t index = 0; while (index < kMaxBridgedDevices) { - if (nullptr != mBridgedDevices[index]) { - if (mBridgedDevices[index]->GetEndpointId() == endpoint) { + if (mDevicesMap.Contains(index)) { + const DevicePair *devicesPtr = mDevicesMap[index]; + if (devicesPtr->mDevice->GetEndpointId() == endpoint) { LOG_INF("Removed dynamic endpoint %d (index=%d)", endpoint, index); /* Free dynamically allocated memory */ emberAfClearDynamicEndpoint(index); - Platform::Delete(mDevicesMap[mBridgedDevices[index]]); - Platform::Delete(mBridgedDevices[index]); - mBridgedDevices[index] = nullptr; - return CHIP_NO_ERROR; + if (mDevicesMap.Erase(index)) { + mNumberOfProviders--; + return CHIP_NO_ERROR; + } else { + LOG_ERR("Cannot remove bridged devices under index=%d", index); + return CHIP_ERROR_NOT_FOUND; + } } } index++; } - return CHIP_ERROR_NOT_FOUND; } -CHIP_ERROR BridgeManager::AddDevice(BridgedDevice *device) +CHIP_ERROR BridgeManager::AddDevices(BridgedDevice *aDevice, BridgedDeviceDataProvider *aDataProvider) { uint8_t index = 0; + Platform::UniquePtr device(aDevice); + Platform::UniquePtr provider(aDataProvider); + VerifyOrReturnError(device && provider, CHIP_ERROR_INTERNAL); + + /* Maximum number of Matter bridged devices is controlled inside mDevicesMap, + but the data providers may be created independently, so let's ensure we do not + violate the maximum number of supported instances. */ + VerifyOrReturnError(!mDevicesMap.IsFull(), CHIP_ERROR_INTERNAL); + VerifyOrReturnError(mNumberOfProviders + 1 <= kMaxDataProviders, CHIP_ERROR_INTERNAL); + mNumberOfProviders++; + while (index < kMaxBridgedDevices) { - /* Find the first empty index in the brdiged devices list */ - if (nullptr == mBridgedDevices[index]) { + /* Find the first empty index in the bridged devices list */ + if (!mDevicesMap.Contains(index)) { + mDevicesMap.Insert(index, DevicePair(std::move(device), std::move(provider))); EmberAfStatus ret; while (true) { + if (!mDevicesMap[index]) { + LOG_ERR("Cannot retrieve bridged device from index %d", index); + return CHIP_ERROR_INTERNAL; + } + auto &storedDevice = mDevicesMap[index]->mDevice; ret = emberAfSetDynamicEndpoint( - index, mCurrentDynamicEndpointId, device->mEp, - Span(device->mDataVersion, device->mDataVersionSize), - Span(device->mDeviceTypeList, - device->mDeviceTypeListSize)); + index, mCurrentDynamicEndpointId, storedDevice->mEp, + Span(storedDevice->mDataVersion, storedDevice->mDataVersionSize), + Span(storedDevice->mDeviceTypeList, + storedDevice->mDeviceTypeListSize)); if (ret == EMBER_ZCL_STATUS_SUCCESS) { LOG_INF("Added device to dynamic endpoint %d (index=%d)", mCurrentDynamicEndpointId, index); - mBridgedDevices[index] = device; - device->Init(mCurrentDynamicEndpointId); + storedDevice->Init(mCurrentDynamicEndpointId); return CHIP_NO_ERROR; } else if (ret != EMBER_ZCL_STATUS_DUPLICATE_EXISTS) { LOG_ERR("Failed to add dynamic endpoint: Internal error!"); + RemoveBridgedDevice(mCurrentDynamicEndpointId); // TODO: check if this is ok, we + // need to cleanup the unused + // devices return CHIP_ERROR_INTERNAL; } @@ -165,48 +125,30 @@ CHIP_ERROR BridgeManager::AddDevice(BridgedDevice *device) return CHIP_ERROR_NO_MEMORY; } -BridgedDevice *BridgeManager::GetBridgedDevice(uint16_t endpoint, const EmberAfAttributeMetadata *attributeMetadata, - uint8_t *buffer) -{ - if (endpoint >= kMaxBridgedDevices) { - return nullptr; - } - - BridgedDevice *device = sBridgeManager.mBridgedDevices[endpoint]; - if ((device == nullptr) || !buffer || !attributeMetadata) { - return nullptr; - } - return device; -} - -CHIP_ERROR BridgeManager::HandleRead(uint16_t endpoint, ClusterId clusterId, +CHIP_ERROR BridgeManager::HandleRead(uint16_t index, ClusterId clusterId, const EmberAfAttributeMetadata *attributeMetadata, uint8_t *buffer, uint16_t maxReadLength) { - BridgedDevice *device = GetBridgedDevice(endpoint, attributeMetadata, buffer); - - if (!device) { - return CHIP_ERROR_INVALID_ARGUMENT; - } + VerifyOrReturnError(attributeMetadata && buffer, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnValue(sBridgeManager.mDevicesMap.Contains(index), CHIP_ERROR_INTERNAL); + auto &device = sBridgeManager.mDevicesMap[index]->mDevice; return device->HandleRead(clusterId, attributeMetadata->attributeId, buffer, maxReadLength); } -CHIP_ERROR BridgeManager::HandleWrite(uint16_t endpoint, ClusterId clusterId, +CHIP_ERROR BridgeManager::HandleWrite(uint16_t index, ClusterId clusterId, const EmberAfAttributeMetadata *attributeMetadata, uint8_t *buffer) { - BridgedDevice *device = GetBridgedDevice(endpoint, attributeMetadata, buffer); - - if (!device) { - return CHIP_ERROR_INVALID_ARGUMENT; - } + VerifyOrReturnError(attributeMetadata && buffer, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnValue(sBridgeManager.mDevicesMap.Contains(index), CHIP_ERROR_INTERNAL); + auto &device = sBridgeManager.mDevicesMap[index]->mDevice; CHIP_ERROR err = device->HandleWrite(clusterId, attributeMetadata->attributeId, buffer); /* After updating Matter BridgedDevice state, forward request to the non-Matter device. */ if (err == CHIP_NO_ERROR) { - return sBridgeManager.mDevicesMap[device]->UpdateState(clusterId, attributeMetadata->attributeId, - buffer); + return sBridgeManager.mDevicesMap[index]->mProvider->UpdateState( + clusterId, attributeMetadata->attributeId, buffer); } return err; } @@ -214,18 +156,17 @@ CHIP_ERROR BridgeManager::HandleWrite(uint16_t endpoint, ClusterId clusterId, void BridgeManager::HandleUpdate(BridgedDeviceDataProvider &dataProvider, chip::ClusterId clusterId, chip::AttributeId attributeId, void *data, size_t dataSize) { - if (!data) { - return; - } + VerifyOrReturn(data); - /* The state update was triggered by non-Matter device, find related Matter Bridged Device to update it as well. + /* The state update was triggered by non-Matter device, find related Matter Bridged Device to update it as + well. */ - for (auto &item : sBridgeManager.mDevicesMap) { - if (item.second == &dataProvider) { + for (auto &item : sBridgeManager.mDevicesMap.mMap) { + if (item.value.mProvider.get() == &dataProvider) { /* If the Bridged Device state was updated successfully, schedule sending Matter data report. */ if (CHIP_NO_ERROR == - item.first->HandleAttributeChange(clusterId, attributeId, data, dataSize)) { - MatterReportingAttributeChangeCallback(item.first->GetEndpointId(), clusterId, + item.value.mDevice->HandleAttributeChange(clusterId, attributeId, data, dataSize)) { + MatterReportingAttributeChangeCallback(item.value.mDevice->GetEndpointId(), clusterId, attributeId); } } diff --git a/samples/matter/common/src/bridge/bridge_manager.h b/samples/matter/common/src/bridge/bridge_manager.h index 272e5f94ebd8..6668f5b78a3e 100644 --- a/samples/matter/common/src/bridge/bridge_manager.h +++ b/samples/matter/common/src/bridge/bridge_manager.h @@ -6,35 +6,52 @@ #pragma once +#include "bridge_util.h" #include "bridged_device.h" #include "bridged_device_data_provider.h" -#include class BridgeManager { public: void Init(); - CHIP_ERROR AddBridgedDevice(BridgedDevice::DeviceType bridgedDeviceType, const char *nodeLabel); + CHIP_ERROR AddBridgedDevices(BridgedDevice *aDevice, BridgedDeviceDataProvider *aDataProvider); CHIP_ERROR RemoveBridgedDevice(uint16_t endpoint); - static BridgedDevice *GetBridgedDevice(uint16_t endpoint, const EmberAfAttributeMetadata *attributeMetadata, - uint8_t *buffer); - static CHIP_ERROR HandleRead(uint16_t endpoint, chip::ClusterId clusterId, + static CHIP_ERROR HandleRead(uint16_t index, chip::ClusterId clusterId, const EmberAfAttributeMetadata *attributeMetadata, uint8_t *buffer, uint16_t maxReadLength); - static CHIP_ERROR HandleWrite(uint16_t endpoint, chip::ClusterId clusterId, + static CHIP_ERROR HandleWrite(uint16_t index, chip::ClusterId clusterId, const EmberAfAttributeMetadata *attributeMetadata, uint8_t *buffer); static void HandleUpdate(BridgedDeviceDataProvider &dataProvider, chip::ClusterId clusterId, chip::AttributeId attributeId, void *data, size_t dataSize); private: - CHIP_ERROR AddDevice(BridgedDevice *device); - static constexpr uint8_t kMaxBridgedDevices = CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; static constexpr uint8_t kMaxDataProviders = CONFIG_BRIDGE_MAX_BRIDGED_DEVICES_NUMBER; - /* TODO: Implement lightweight map to decrease flash usage. */ - std::map mDevicesMap; - BridgedDevice *mBridgedDevices[kMaxBridgedDevices]; - BridgedDeviceDataProvider *mDataProviders[kMaxDataProviders]; + using DevicePtr = chip::Platform::UniquePtr; + using ProviderPtr = chip::Platform::UniquePtr; + struct DevicePair { + DevicePair() : mDevice(nullptr), mProvider(nullptr) {} + DevicePair(DevicePtr device, ProviderPtr provider) + : mDevice(std::move(device)), mProvider(std::move(provider)) + { + } + DevicePair &operator=(DevicePair &&other) + { + mDevice = std::move(other.mDevice); + mProvider = std::move(other.mProvider); + return *this; + } + operator bool() const { return mDevice && mProvider; } + DevicePair &operator=(const DevicePair &other) = delete; + DevicePtr mDevice; + ProviderPtr mProvider; + }; + using DeviceMap = FiniteMap; + + CHIP_ERROR AddDevices(BridgedDevice *aDevice, BridgedDeviceDataProvider *aDataProvider); + + DeviceMap mDevicesMap; + uint16_t mNumberOfProviders{ 0 }; chip::EndpointId mFirstDynamicEndpointId; chip::EndpointId mCurrentDynamicEndpointId; diff --git a/samples/matter/common/src/bridge/bridged_device.h b/samples/matter/common/src/bridge/bridged_device.h index a270b1ffc34c..de58c0d83d96 100644 --- a/samples/matter/common/src/bridge/bridged_device.h +++ b/samples/matter/common/src/bridge/bridged_device.h @@ -38,7 +38,7 @@ class BridgedDevice { public: - enum class DeviceType : uint16_t { + enum DeviceType : uint16_t { BridgedNode = 0x0013, OnOffLight = 0x0100, TemperatureSensor = 0x0302, diff --git a/samples/matter/common/src/bridge/util/bridge_util.h b/samples/matter/common/src/bridge/util/bridge_util.h new file mode 100644 index 000000000000..fa00182c8cb4 --- /dev/null +++ b/samples/matter/common/src/bridge/util/bridge_util.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#pragma once + +#include "bridged_device.h" + +#include +#include +#include + +/* + DeviceFactory template container allows to instantiate a map which + binds supported BridgedDevice::DeviceType types with corresponding + creation method (e.g. constructor invocation). DeviceFactory can + only be constructed by passing a user-defined initialized list with + pairs. + Then, Create() method can be used to obtain an instance of demanded + device type with all passed arguments forwarded to the underlying + ConcreteDeviceCreator. +*/ +template class DeviceFactory { +public: + using ConcreteDeviceCreator = std::function; + using CreationMap = std::map; + + DeviceFactory(std::initializer_list> init) + { + for (auto &pair : init) { + mCreationMap.insert(pair); + } + } + + DeviceFactory() = delete; + DeviceFactory(const DeviceFactory &) = delete; + DeviceFactory(DeviceFactory &&) = delete; + DeviceFactory &operator=(const DeviceFactory &) = delete; + DeviceFactory &operator=(DeviceFactory &&) = delete; + ~DeviceFactory() = default; + + T *Create(BridgedDevice::DeviceType deviceType, Args... params) + { + if (mCreationMap.find(deviceType) == mCreationMap.end()) { + return nullptr; + } + return mCreationMap[deviceType](std::forward(params)...); + } + +private: + CreationMap mCreationMap; +}; + +/* + FiniteMap template container allows to wrap mappings between uint16_t type key and T type value. + The size or the container is predefined at compilation time, therefore + the maximum number of stored elements must be well known. FiniteMap owns + inserted values, meaning that once the user inserts a value it will not longer + be valid outside of the container, i.e. FiniteMap cannot return stored object by copy. + FiniteMap's API offers basic operations like: + * inserting a new value under provided key (Insert) + * erasing existing item under given key (Erase) + * retrieving value stored under provided key ([] operator) + * checking if the map contains a non-null value under given key (Contains) + * iterating though stored item via publicly available mMap member + Prerequisites: + * T must have =operator(&&) and bool()operator implemented +*/ +template struct FiniteMap { + struct Pair { + uint16_t key; + T value; + }; + + void Insert(uint16_t key, T &&value) + { + if (Contains(key)) { + /* The key with sane value already exists in the map, return prematurely. */ + return; + } else if (mElementsCount < N) { + mMap[key].key = key; + mMap[key].value = std::move(value); + mElementsCount++; + } + } + + bool Erase(uint16_t key) + { + const auto &it = std::find_if(std::begin(mMap), std::end(mMap), + [key](const Pair &pair) { return pair.key == key; }); + if (it != std::end(mMap) && it->value) { + /* Invalidate the value but leave the key unchanged */ + it->value = T{}; + mElementsCount--; + return true; + } + return false; + } + + /* TODO: refactor to return a reference (Constains() check will be always needed) */ + const T *operator[](uint16_t key) const + { + for (auto &it : mMap) { + if (key == it.key) + return &(it.value); + } + return nullptr; + } + + bool Contains(uint16_t key) + { + const T *stored = (*this)[key]; + if (stored && *stored) { + /* The key with sane value found in the map */ + return true; + } + return false; + } + + bool IsFull() { return mElementsCount >= N; } + + Pair mMap[N]; + uint16_t mElementsCount{ 0 }; +};