diff --git a/examples/all-clusters-app/esp32/main/CMakeLists.txt b/examples/all-clusters-app/esp32/main/CMakeLists.txt index 1f831fa8103a78..bd0f28a8d4e55a 100644 --- a/examples/all-clusters-app/esp32/main/CMakeLists.txt +++ b/examples/all-clusters-app/esp32/main/CMakeLists.txt @@ -53,6 +53,7 @@ set(SRC_DIRS_LIST "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/content-launch-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/operational-credentials-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/media-input-server" + "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/mode-select-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/low-power-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/keypad-input-server" "${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/media-playback-server" diff --git a/examples/all-clusters-app/mbed/CMakeLists.txt b/examples/all-clusters-app/mbed/CMakeLists.txt index 4922a534d6bc39..9f27e485a132e5 100644 --- a/examples/all-clusters-app/mbed/CMakeLists.txt +++ b/examples/all-clusters-app/mbed/CMakeLists.txt @@ -108,6 +108,8 @@ target_sources(${APP_TARGET} PRIVATE ${APP_CLUSTERS}/low-power-server/low-power-server.cpp ${APP_CLUSTERS}/media-input-server/media-input-server.cpp ${APP_CLUSTERS}/media-playback-server/media-playback-server.cpp + ${APP_CLUSTERS}/mode-select-server/mode-select-server.cpp + ${APP_CLUSTERS}/mode-select-server/static-supported-modes-manager.cpp ${APP_CLUSTERS}/network-commissioning/network-commissioning-ember.cpp ${APP_CLUSTERS}/network-commissioning/network-commissioning.cpp ${APP_CLUSTERS}/on-off-server/on-off-server.cpp diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index 3191084bd0008c..bf89177867fea7 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -142,6 +142,11 @@ template("chip_data_model") { "${_app_root}/clusters/${cluster}/${cluster}-ember.cpp", "${_app_root}/clusters/${cluster}/${cluster}.cpp", ] + } else if (cluster == "mode-select-server") { + sources += [ + "${_app_root}/clusters/${cluster}/${cluster}.cpp", + "${_app_root}/clusters/${cluster}/static-supported-modes-manager.cpp", + ] } else { sources += [ "${_app_root}/clusters/${cluster}/${cluster}.cpp" ] } diff --git a/src/app/clusters/mode-select-server/mode-select-server.cpp b/src/app/clusters/mode-select-server/mode-select-server.cpp new file mode 100644 index 00000000000000..2e0a12ae5fc745 --- /dev/null +++ b/src/app/clusters/mode-select-server/mode-select-server.cpp @@ -0,0 +1,114 @@ +/** + * + * Copyright (c) 2021 Project CHIP Authors + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; + +namespace { + +class ModeSelectAttrAccess : public AttributeAccessInterface +{ +public: + ModeSelectAttrAccess() : AttributeAccessInterface(Optional::Missing(), ModeSelect::Id) {} + + CHIP_ERROR Read(const ConcreteAttributePath & aPath, AttributeValueEncoder & aEncoder) override; +}; + +ModeSelectAttrAccess gModeSelectAttrAccess; + +CHIP_ERROR ModeSelectAttrAccess::Read(const ConcreteAttributePath & aPath, AttributeValueEncoder & aEncoder) +{ + VerifyOrDie(aPath.mClusterId == ModeSelect::Id); + + const ModeSelect::StaticSupportedModesManager & gSupportedModeManager = + ModeSelect::StaticSupportedModesManager::getStaticSupportedModesManagerInstance(); + + if (ModeSelect::Attributes::SupportedModes::Id == aPath.mAttributeId) + { + const ModeSelect::StaticSupportedModesManager::IteratorFactory* iteratorFactory = + gSupportedModeManager.getIteratorFactory(aPath.mEndpointId); + if (iteratorFactory == nullptr) + { + aEncoder.Encode(DataModel::List()); + return CHIP_NO_ERROR; + } + CHIP_ERROR err; + err = aEncoder.EncodeList([iteratorFactory](const TagBoundEncoder & encoder) -> CHIP_ERROR { + const auto & end = *(iteratorFactory->end()); + for (auto it = *(iteratorFactory->begin()); it != end; ++it) + { + emberAfPrintln(EMBER_AF_PRINT_DEBUG, "ModeSelect: dereferencing it"); + emberAfPrintln(EMBER_AF_PRINT_DEBUG, "ModeSelect: it= %p", (void*) it.operator->()); + auto& modeOption = *it; + ReturnErrorOnFailure(encoder.Encode(modeOption)); + } + return CHIP_NO_ERROR; + }); + ReturnErrorOnFailure(err); + } + return CHIP_NO_ERROR; +} + +} // anonymous namespace + +bool emberAfModeSelectClusterChangeToModeCallback( + CommandHandler * commandHandler, const ConcreteCommandPath & commandPath, + const ModeSelect::Commands::ChangeToMode::DecodableType & commandData) +{ + emberAfPrintln(EMBER_AF_PRINT_DEBUG, "ModeSelect: Entering emberAfModeSelectClusterChangeToModeCallback"); + EndpointId endpointId = commandPath.mEndpointId; + uint8_t newMode = commandData.newMode; + // Check that the newMode matches one of the supported options + const ModeSelect::Structs::ModeOptionStruct::Type * modeOptionPtr; + const ModeSelect::StaticSupportedModesManager & gSupportedModeManager = + ModeSelect::StaticSupportedModesManager::getStaticSupportedModesManagerInstance(); + EmberAfStatus checkSupportedModeStatus = gSupportedModeManager.getModeOptionByMode(endpointId, newMode, &modeOptionPtr); + if (EMBER_ZCL_STATUS_SUCCESS != checkSupportedModeStatus) + { + emberAfPrintln(EMBER_AF_PRINT_DEBUG, "ModeSelect: Failed to find the option with mode %" PRIu8, newMode); + emberAfSendImmediateDefaultResponse(checkSupportedModeStatus); + return false; + } + ModeSelect::Attributes::CurrentMode::Set(endpointId, newMode); + // TODO: Implement application logic + + emberAfPrintln(EMBER_AF_PRINT_DEBUG, "ModeSelect: ChangeToMode successful"); + emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_SUCCESS); + return true; +} + +void MatterModeSelectPluginServerInitCallback(void) +{ + registerAttributeAccessOverride(&gModeSelectAttrAccess); +} diff --git a/src/app/clusters/mode-select-server/static-supported-modes-manager.cpp b/src/app/clusters/mode-select-server/static-supported-modes-manager.cpp new file mode 100644 index 00000000000000..5a43b7df2ab7ff --- /dev/null +++ b/src/app/clusters/mode-select-server/static-supported-modes-manager.cpp @@ -0,0 +1,67 @@ +// +// Created by Ding, Li-an on 10/21/21. +// +#include +#include +#include + +using namespace std; +using namespace chip; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::ModeSelect; + +using ModeOptionStructType = Structs::ModeOptionStruct::Type; +using storage_value_type = const ModeOptionStructType *; +namespace +{ +Structs::ModeOptionStruct::Type buildModeOptionStruct(const char * label, uint8_t mode, uint32_t semanticTag) +{ + Structs::ModeOptionStruct::Type option; + option.label = CharSpan(label, strlen(label)); + option.mode = mode; + option.semanticTag = semanticTag; + return option; +} +} + +const Structs::ModeOptionStruct::Type StaticSupportedModesManager::blackOption = buildModeOptionStruct("Black", 0, 0); +const Structs::ModeOptionStruct::Type StaticSupportedModesManager::cappuccinoOption = buildModeOptionStruct("Cappuccino", 4, 0); +const Structs::ModeOptionStruct::Type StaticSupportedModesManager::espressoOption = buildModeOptionStruct("Espresso", 7, 0); +storage_value_type StaticSupportedModesManager::coffeeOptions[] = { &blackOption, &cappuccinoOption, &espressoOption }; +const Span StaticSupportedModesManager::coffeeOptionsSpan = + Span(StaticSupportedModesManager::coffeeOptions); +const map> StaticSupportedModesManager::optionsByEndpoints = { + { 1, StaticSupportedModesManager::coffeeOptionsSpan } +}; + +const StaticSupportedModesManager StaticSupportedModesManager::instance = StaticSupportedModesManager(); + +const StaticSupportedModesManager::IteratorFactory * +StaticSupportedModesManager::getIteratorFactory(EndpointId endpointId) const +{ + const auto& it = _iteratorFactoriesByEndpoints.find(endpointId); + return (it == _iteratorFactoriesByEndpoints.end()) ? nullptr : &(it->second); +} + +EmberAfStatus StaticSupportedModesManager::getModeOptionByMode(unsigned short endpointId, unsigned char mode, + const ModeOptionStructType ** dataPtr) const +{ + auto* iteratorFactory = this->getIteratorFactory(endpointId); + if(iteratorFactory == nullptr) + { + return EMBER_ZCL_STATUS_UNSUPPORTED_CLUSTER; + } + const StaticSupportedModesManager::Iterator & begin = *(this->getIteratorFactory(endpointId)->begin()); + const StaticSupportedModesManager::Iterator & end = *(this->getIteratorFactory(endpointId)->end()); + for (auto it = begin; it != end; ++it) + { + auto & modeOption = *it; + if (modeOption.mode == mode) + { + *dataPtr = &modeOption; + return EMBER_ZCL_STATUS_SUCCESS; + } + } + emberAfPrintln(EMBER_AF_PRINT_DEBUG, "Cannot find the mode %c", mode); + return EMBER_ZCL_STATUS_INVALID_ARGUMENT; +} diff --git a/src/app/clusters/mode-select-server/static-supported-modes-manager.h b/src/app/clusters/mode-select-server/static-supported-modes-manager.h new file mode 100644 index 00000000000000..d37ce765372837 --- /dev/null +++ b/src/app/clusters/mode-select-server/static-supported-modes-manager.h @@ -0,0 +1,133 @@ +/* + * + * Copyright (c) 2021 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 +#include +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace ModeSelect { + +/** + * This implementation statically defines the options. + */ + +class StaticSupportedModesManager : public chip::app::Clusters::ModeSelect::SupportedModesManager +{ + using ModeOptionStructType = Structs::ModeOptionStruct::Type; + using storage_value_type = const ModeOptionStructType*; + + static const ModeOptionStructType blackOption; + static const ModeOptionStructType cappuccinoOption; + static const ModeOptionStructType espressoOption; + + static const ModeOptionStructType* coffeeOptions[]; + static const Span coffeeOptionsSpan; + static const std::map> optionsByEndpoints; + + + +public: + static const StaticSupportedModesManager instance; + + struct Iterator: public chip::app::Clusters::ModeSelect::SupportedModesManager::ModeOptionStructIterator + { + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using pointer = storage_value_type*; + using base_iterator_type= chip::app::Clusters::ModeSelect::SupportedModesManager::ModeOptionStructIterator; + + Iterator(const pointer aPtr):mPtr(aPtr) {} + ~Iterator() = default; + + const ModeOptionStructType& operator*() const override { return **mPtr; } + const ModeOptionStructType* operator->() override { return *mPtr; } + const ModeOptionStructType* operator->() const override { return *mPtr; } + + // Prefix increment + base_iterator_type& operator++() override { ++mPtr; return *this; } + + bool operator== (const base_iterator_type& other) const override + { + // Warning: we are not doing type check + // TODO: use of typeid requires -frtti + // if (typeid(other) != typeid(*this)) + // { + // return false; + // } + return this->operator->() == other.operator->(); + } + bool operator!= (const base_iterator_type& other) const override { return !((*this) == other); } + + private: + pointer mPtr; + + }; + + struct IteratorFactory: public chip::app::Clusters::ModeSelect::SupportedModesManager::ModeOptionStructIteratorFactory + { + using pointer = Iterator*; + using const_pointer = const pointer; + + IteratorFactory(const Span& aSupportedOptions): _begin(Iterator(aSupportedOptions.data())), _end(Iterator(aSupportedOptions.data() + aSupportedOptions.size())) { } + ~IteratorFactory() = default; + + const Iterator* begin() const override { return &_begin; } + const Iterator* end() const override { return &_end; } + + private: + const Iterator _begin; + const Iterator _end; + + }; + + const IteratorFactory* getIteratorFactory(EndpointId endpointId) const override; + + EmberAfStatus getModeOptionByMode(EndpointId endpointId, uint8_t mode, const ModeOptionStructType ** dataPtr) const override; + + ~StaticSupportedModesManager(){}; + + StaticSupportedModesManager() : StaticSupportedModesManager(&optionsByEndpoints) {} + + static inline const StaticSupportedModesManager & getStaticSupportedModesManagerInstance() { return instance; } + +private: + StaticSupportedModesManager(const std::map> * const supportedModes) : + _iteratorFactoriesByEndpoints(std::map()) + { + for (auto & entry : *supportedModes) + { + _iteratorFactoriesByEndpoints.insert(std::pair(entry.first, IteratorFactory(entry.second))); + } + } + // TODO: Implement move constructor? + + std::map _iteratorFactoriesByEndpoints; +}; + + +} // namespace ModeSelect +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/mode-select-server/supported-modes-manager.h b/src/app/clusters/mode-select-server/supported-modes-manager.h new file mode 100644 index 00000000000000..61f828fab74bf9 --- /dev/null +++ b/src/app/clusters/mode-select-server/supported-modes-manager.h @@ -0,0 +1,110 @@ +/* + * + * Copyright (c) 2021 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 + +#include +#include +#include +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace ModeSelect { + +/** + * Interface to help manage the supported modes of the Mode Select Cluster. + */ +class SupportedModesManager +{ + + using ModeOptionStructType = Structs::ModeOptionStruct::Type; + +public: + struct ModeOptionStructIterator + { + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = const ModeOptionStructType; + using pointer = value_type*; + using reference = value_type&; + + virtual reference operator*() const = 0; + virtual pointer operator->() = 0; + virtual pointer operator->() const = 0; + + // Prefix increment + virtual ModeOptionStructIterator& operator++() = 0; + + virtual bool operator== (const ModeOptionStructIterator& other) const = 0; + virtual bool operator!= (const ModeOptionStructIterator& other) const = 0; + + + virtual ~ModeOptionStructIterator() {} + }; + + /** + * A factory that can return the ModeOptionStructIterators for a specific endpoint. + */ + struct ModeOptionStructIteratorFactory + { + using const_pointer = const ModeOptionStructIterator*; + + /** + * Returns the ModeOptionStructIterator to the first option. + */ + virtual const_pointer begin() const = 0; + + /** + * Returns the ModeOptionStructIterator to an element after the last option. + */ + virtual const_pointer end() const = 0; + + virtual ~ModeOptionStructIteratorFactory() {} + }; + + /** + * Given the endpointId, returns all its supported modes options. + * @param endpointId + * @return The iterator factory for the endpoint, or nullptr if the endpoint doesn't support ModeSelectCluster. + */ + virtual const ModeOptionStructIteratorFactory * getIteratorFactory(EndpointId endpointId) const = 0; + + /** + * Given the endpointId and a mode value, find the ModeOptionStruct that matches the mode. + * @param endpointId The endpoint for which to search the ModeOptionStruct. + * @param mode The mode for which to search the ModeOptionStruct. + * @param dataPtr The pointer to receive the ModeOptionStruct, if one is found. + * @return EMBER_ZCL_STATUS_SUCCESS if sucessfully found the option. Otherwise, returns appropriate status code (found in ) + */ + virtual EmberAfStatus getModeOptionByMode(EndpointId endpointId, uint8_t mode, const ModeOptionStructType ** dataPtr) const = 0; + + virtual ~SupportedModesManager(){} + + +}; + +} // namespace ModeSelect +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/operational-credentials-server/operational-credentials-server.cpp b/src/app/clusters/operational-credentials-server/operational-credentials-server.cpp index 0996e429652a71..c010952f825035 100644 --- a/src/app/clusters/operational-credentials-server/operational-credentials-server.cpp +++ b/src/app/clusters/operational-credentials-server/operational-credentials-server.cpp @@ -318,7 +318,7 @@ class OpCredsFabricTableDelegate : public FabricTableDelegate // Gets called when a fabric is deleted from KVS store void OnFabricDeletedFromStorage(FabricIndex fabricId) override { - emberAfPrintln(EMBER_AF_PRINT_DEBUG, "OpCreds: Fabric 0x%" PRIX16 " was deleted from fabric storage.", fabricId); + emberAfPrintln(EMBER_AF_PRINT_DEBUG, "OpCreds: Fabric 0x%" PRIu8 " was deleted from fabric storage.", fabricId); writeFabricsIntoFabricsListAttribute(); } @@ -326,7 +326,7 @@ class OpCredsFabricTableDelegate : public FabricTableDelegate void OnFabricRetrievedFromStorage(FabricInfo * fabric) override { emberAfPrintln(EMBER_AF_PRINT_DEBUG, - "OpCreds: Fabric 0x%" PRIX16 " was retrieved from storage. FabricId 0x" ChipLogFormatX64 + "OpCreds: Fabric 0x%" PRIu8 " was retrieved from storage. FabricId 0x" ChipLogFormatX64 ", NodeId 0x" ChipLogFormatX64 ", VendorId 0x%04" PRIX16, fabric->GetFabricIndex(), ChipLogValueX64(fabric->GetFabricId()), ChipLogValueX64(fabric->GetPeerId().GetNodeId()), fabric->GetVendorId());