From 142900251de586783a05759eb3ea66ad559392c7 Mon Sep 17 00:00:00 2001 From: lpbeliveau-silabs Date: Fri, 3 Feb 2023 15:45:56 -0500 Subject: [PATCH] implemented and tested EFSs handlers for the scene table --- src/app/chip_data_model.gni | 3 +- .../clusters/scenes/ExtensionFieldsSets.cpp | 94 ---------- src/app/clusters/scenes/ExtensionFieldsSets.h | 77 +-------- .../scenes/ExtensionFieldsSetsImpl.cpp | 152 +++++++++++++++++ .../clusters/scenes/ExtensionFieldsSetsImpl.h | 160 ++++++++++++++++++ src/app/clusters/scenes/SceneTable.h | 9 +- src/app/clusters/scenes/SceneTableImpl.cpp | 109 ++++++++++++ src/app/clusters/scenes/SceneTableImpl.h | 108 +++++++++++- src/app/tests/BUILD.gn | 4 +- src/app/tests/TestSceneTable.cpp | 145 +++++++++++++++- src/lib/core/CHIPConfig.h | 14 ++ 11 files changed, 690 insertions(+), 185 deletions(-) delete mode 100644 src/app/clusters/scenes/ExtensionFieldsSets.cpp create mode 100644 src/app/clusters/scenes/ExtensionFieldsSetsImpl.cpp create mode 100644 src/app/clusters/scenes/ExtensionFieldsSetsImpl.h diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index cfbd9c5911492c..221312a60c3906 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -157,8 +157,9 @@ template("chip_data_model") { "${_app_root}/clusters/identify-server/identify-server.h", "${_app_root}/clusters/level-control/level-control.h", "${_app_root}/clusters/on-off-server/on-off-server.h", - "${_app_root}/clusters/scenes/ExtensionFieldsSets.cpp", "${_app_root}/clusters/scenes/ExtensionFieldsSets.h", + "${_app_root}/clusters/scenes/ExtensionFieldsSetsImpl.cpp", + "${_app_root}/clusters/scenes/ExtensionFieldsSetsImpl.h", "${_app_root}/clusters/scenes/SceneTable.h", "${_app_root}/clusters/scenes/SceneTableImpl.cpp", "${_app_root}/clusters/scenes/SceneTableImpl.h", diff --git a/src/app/clusters/scenes/ExtensionFieldsSets.cpp b/src/app/clusters/scenes/ExtensionFieldsSets.cpp deleted file mode 100644 index 2b25d9a3253042..00000000000000 --- a/src/app/clusters/scenes/ExtensionFieldsSets.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * - * Copyright (c) 2023 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 - * - * https://urldefense.com/v3/__http://www.apache.org/licenses/LICENSE-2.0__;!!N30Cs7Jr!UgbMbEQ59BIK-1Xslc7QXYm0lQBh92qA3ElecRe1CF_9YhXxbwPOZa6j4plru7B7kCJ7bKQgHxgQrket3-Dnk268sIdA7Qb8$ - * - * 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 "ExtensionFieldsSets.h" - -namespace chip { -namespace scenes { - -ExtensionFieldsSets::ExtensionFieldsSets() -{ - // check if any of the clusters with scene attributes are enabled - if (this->kExtensionFieldsSetsSize == 1) - { - // a size of 1 byte indicates an empty struct, or a struct that only contains : - // the on-off cluster - // the Mode select cluster - // the door lock cluster -#ifdef ZCL_USING_ON_OFF_CLUSTER_SERVER - this->enabledFieldSets.onOff = false; - -#elif defined(ZCL_USING_MODE_SELECT_CLUSTER_SERVER) - this->enabledFieldSets.currentMode = 0; -#elif defined(ZCL_USING_DOOR_LOCK_CLUSTER_SERVER) - this->enabledFieldSets.lockState = 0; -#else - this->empty = true; -#endif - } - else if (this->kExtensionFieldsSetsSize > 1) - { - memset(&this->enabledFieldSets, 0, kExtensionFieldsSetsSize); - } -} - -CHIP_ERROR ExtensionFieldsSets::Serialize(TLV::TLVWriter & writer) const -{ - if (!this->empty) - { - TLV::TLVType container; - ReturnErrorOnFailure(writer.StartContainer(TLV::ContextTag(1), TLV::kTLVType_Structure, container)); - - ReturnErrorOnFailure(writer.PutBytes(TagEnabledFielsSets(), (uint8_t *) &this->enabledFieldSets, kExtensionFieldsSetsSize)); - - return writer.EndContainer(container); - } - else - { - return CHIP_NO_ERROR; - } -} - -CHIP_ERROR ExtensionFieldsSets::Deserialize(TLV::TLVReader & reader) -{ - if (!this->empty) - { - TLV::TLVType container; - ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::ContextTag(1))); - ReturnErrorOnFailure(reader.EnterContainer(container)); - - ReturnErrorOnFailure(reader.Next(TagEnabledFielsSets())); - ReturnErrorOnFailure(reader.GetBytes((uint8_t *) &this->enabledFieldSets, kExtensionFieldsSetsSize)); - - return reader.ExitContainer(container); - } - else - { - return CHIP_NO_ERROR; - } -} -void ExtensionFieldsSets::Clear() -{ - if (!this->empty) - { - memset(&this->enabledFieldSets, 0, sizeof(this->enabledFieldSets)); - } -} - -} // namespace scenes - -} // namespace chip diff --git a/src/app/clusters/scenes/ExtensionFieldsSets.h b/src/app/clusters/scenes/ExtensionFieldsSets.h index ca1e080aa9dbf4..eedd3834e97dc3 100644 --- a/src/app/clusters/scenes/ExtensionFieldsSets.h +++ b/src/app/clusters/scenes/ExtensionFieldsSets.h @@ -17,85 +17,22 @@ #pragma once -#include #include -#include #include namespace chip { namespace scenes { -typedef struct -{ - -#ifdef ZCL_USING_ON_OFF_CLUSTER_SERVER - bool onOff; -#endif - -#ifdef ZCL_USING_LEVEL_CONTROL_CLUSTER_SERVER - uint8_t currentLevel; - uint16_t currentFrequency; -#endif - -#ifdef ZCL_USING_MODE_SELECT_CLUSTER_SERVER - uint8_t currentMode; -#endif - -#ifdef ZCL_USING_COLOR_CONTROL_CLUSTER_SERVER - uint8_t currentSaturation; - uint16_t currentX; - uint16_t currentY; - uint16_t colorTemperatureMireds; - uint16_t enhancedCurrentHue; - uint8_t enhancedColorMode; - uint8_t colorLoopActive; - uint8_t colorLoopDirection; - uint16_t colorLoopTime; -#endif - -#ifdef ZCL_USING_THERMOSTAT_CLUSTER_SERVER - uint16_t occupiedCoolingSetpoint; - uint16_t occupiedHeatingSetpoint; - uint8_t systemMode; -#endif - -#ifdef ZCL_USING_DOOR_LOCK_CLUSTER_SERVER - uint8_t lockState; -#endif - -#ifdef ZCL_USING_WINDOW_COVERING_CLUSTER_SERVER - uint8_t currentPositionLiftPercentage; - uint8_t currentPositionTiltPercentage; - uint8_t targetPositionLiftPercent100ths; - uint8_t targetPositionTiltPercent100ths; -#endif -} FieldSets; - class ExtensionFieldsSets { public: - static constexpr size_t kExtensionFieldsSetsSize = sizeof(FieldSets); - static constexpr TLV::Tag TagEnabledFielsSets() { return TLV::ContextTag(1); } - FieldSets enabledFieldSets; - bool empty = false; - - ExtensionFieldsSets(); - ~ExtensionFieldsSets(){}; - - CHIP_ERROR Serialize(TLV::TLVWriter & writer) const; - CHIP_ERROR Deserialize(TLV::TLVReader & reader); - - void Clear(); - - bool operator==(const ExtensionFieldsSets & other) - { - return (!memcmp(&this->enabledFieldSets, &other.enabledFieldSets, kExtensionFieldsSetsSize)); - } + ExtensionFieldsSets(){}; + virtual ~ExtensionFieldsSets() = default; - void operator=(const ExtensionFieldsSets & other) - { - memcpy(&this->enabledFieldSets, &other.enabledFieldSets, kExtensionFieldsSetsSize); - } + virtual CHIP_ERROR Serialize(TLV::TLVWriter & writer) const = 0; + virtual CHIP_ERROR Deserialize(TLV::TLVReader & reader) = 0; + virtual void Clear() = 0; + virtual bool is_empty() const = 0; }; } // namespace scenes -} // namespace chip +} // namespace chip \ No newline at end of file diff --git a/src/app/clusters/scenes/ExtensionFieldsSetsImpl.cpp b/src/app/clusters/scenes/ExtensionFieldsSetsImpl.cpp new file mode 100644 index 00000000000000..ed3dac591463b3 --- /dev/null +++ b/src/app/clusters/scenes/ExtensionFieldsSetsImpl.cpp @@ -0,0 +1,152 @@ +/* + * + * Copyright (c) 2023 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 + * + * https://urldefense.com/v3/__http://www.apache.org/licenses/LICENSE-2.0__;!!N30Cs7Jr!UgbMbEQ59BIK-1Xslc7QXYm0lQBh92qA3ElecRe1CF_9YhXxbwPOZa6j4plru7B7kCJ7bKQgHxgQrket3-Dnk268sIdA7Qb8$ + * + * 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 + +namespace chip { +namespace scenes { + +ExtensionFieldsSetsImpl::ExtensionFieldsSetsImpl() : ExtensionFieldsSets() {} + +CHIP_ERROR ExtensionFieldsSetsImpl::Serialize(TLV::TLVWriter & writer) const +{ + TLV::TLVType container; + ReturnErrorOnFailure(writer.StartContainer(TLV::ContextTag(1), TLV::kTLVType_Structure, container)); + ReturnErrorOnFailure(writer.Put(TagFieldNum(), static_cast(this->fieldNum))); + if (!this->is_empty()) + { + for (uint8_t i = 0; i < CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENES; i++) + { + if (!this->EFS[i].is_empty()) + { + LogErrorOnFailure(this->EFS[i].Serialize(writer)); + } + } + } + + return writer.EndContainer(container); +} + +CHIP_ERROR ExtensionFieldsSetsImpl::Deserialize(TLV::TLVReader & reader) +{ + TLV::TLVType container; + ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::ContextTag(1))); + ReturnErrorOnFailure(reader.EnterContainer(container)); + + ReturnErrorOnFailure(reader.Next(TagFieldNum())); + ReturnErrorOnFailure(reader.Get(this->fieldNum)); + + if (!this->is_empty()) + { + for (uint8_t i = 0; i < this->fieldNum; i++) + { + ReturnErrorOnFailure(this->EFS[i].Deserialize(reader)); + } + } + + return reader.ExitContainer(container); +} +void ExtensionFieldsSetsImpl::Clear() +{ + if (!this->is_empty()) + { + for (uint8_t i = 0; i < CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENES; i++) + { + this->EFS[i].Clear(); + } + } + this->fieldNum = 0; +} + +bool ExtensionFieldsSetsImpl::is_empty() const +{ + return (this->fieldNum <= 0); +} + +/// @brief Inserts a field set into the array of extension field sets for a scene entry if the same ID is present in the EFS array, +/// it will overwrite it +/// @param field field set to be inserted +/// @return CHIP_NO_ERROR if insertion worked, CHIP_ERROR_BUFFER_TOO_SMALL if the array is already full +CHIP_ERROR ExtensionFieldsSetsImpl::insertField(ExtensionFieldsSet & field) +{ + uint8_t idPosition = 0xff, fisrtEmptyPosition = 0xff; + for (uint8_t i = 0; i < CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENES; i++) + { + if (this->EFS[i].ID == field.ID) + { + idPosition = i; + break; + } + + if (this->EFS[i].is_empty() && fisrtEmptyPosition == 0xFF) + { + fisrtEmptyPosition = i; + } + } + + // if found, insert at found position, otherwise at first free possition, otherwise return error + if (idPosition < CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENES) + { + ReturnErrorOnFailure(this->EFS[idPosition] = field); + return CHIP_NO_ERROR; + } + else if (fisrtEmptyPosition < CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENES) + { + ReturnErrorOnFailure(this->EFS[fisrtEmptyPosition] = field); + this->fieldNum++; + return CHIP_NO_ERROR; + } + else + { + return CHIP_ERROR_BUFFER_TOO_SMALL; + } +} + +CHIP_ERROR ExtensionFieldsSetsImpl::getFieldAtPosition(ExtensionFieldsSet & field, uint8_t position) +{ + if (position < CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENES) + { + ReturnErrorOnFailure(field = this->EFS[position]); + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR ExtensionFieldsSetsImpl::removeFieldAtPosition(uint8_t position) +{ + if (!this->is_empty()) + { + if (position < CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENES) + { + if (!this->EFS[position].is_empty()) + { + this->EFS[position].Clear(); + this->fieldNum--; + } + } + else + { + return CHIP_ERROR_ACCESS_DENIED; + } + } + + return CHIP_NO_ERROR; +} + +} // namespace scenes + +} // namespace chip diff --git a/src/app/clusters/scenes/ExtensionFieldsSetsImpl.h b/src/app/clusters/scenes/ExtensionFieldsSetsImpl.h new file mode 100644 index 00000000000000..58438cfab192b3 --- /dev/null +++ b/src/app/clusters/scenes/ExtensionFieldsSetsImpl.h @@ -0,0 +1,160 @@ +/** + * + * Copyright (c) 2023 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 + * + * https://urldefense.com/v3/__http://www.apache.org/licenses/LICENSE-2.0__;!!N30Cs7Jr!UgbMbEQ59BIK-1Xslc7QXYm0lQBh92qA3ElecRe1CF_9YhXxbwPOZa6j4plru7B7kCJ7bKQgHxgQrket3-Dnk268sIdA7Qb8$ + * + * 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 + +namespace chip { +namespace scenes { + +using clusterId = chip::ClusterId; + +struct ExtensionFieldsSet +{ + static constexpr TLV::Tag TagClusterId() { return TLV::ContextTag(1); } + static constexpr TLV::Tag TagEFS() { return TLV::ContextTag(2); } + clusterId ID = kInvalidClusterId; + uint8_t bytesBuffer[CHIP_CONFIG_SCENES_MAX_EXTENSION_FIELDSET_SIZE_PER_CLUSTER] = { 0 }; + uint8_t usedBytes = 0; + + ExtensionFieldsSet() = default; + ExtensionFieldsSet(clusterId cID, uint8_t * data, uint8_t dataSize) : ID(cID), usedBytes(dataSize) + { + if (dataSize <= CHIP_CONFIG_SCENES_MAX_EXTENSION_FIELDSET_SIZE_PER_CLUSTER) + { + memcpy(bytesBuffer, data, usedBytes); + } + } + ~ExtensionFieldsSet() = default; + + CHIP_ERROR Serialize(TLV::TLVWriter & writer) const + { + TLV::TLVType container; + ReturnErrorOnFailure(writer.StartContainer(TLV::ContextTag(1), TLV::kTLVType_Structure, container)); + + ReturnErrorOnFailure(writer.Put(TagClusterId(), static_cast(this->ID))); + ReturnErrorOnFailure(writer.PutBytes(TagEFS(), bytesBuffer, usedBytes)); + + return writer.EndContainer(container); + } + CHIP_ERROR Deserialize(TLV::TLVReader & reader) + { + ByteSpan buffer; + TLV::TLVType container; + ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::ContextTag(1))); + ReturnErrorOnFailure(reader.EnterContainer(container)); + + ReturnErrorOnFailure(reader.Next(TagClusterId())); + ReturnErrorOnFailure(reader.Get(this->ID)); + + ReturnErrorOnFailure(reader.Next(TagEFS())); + ReturnErrorOnFailure(reader.Get(buffer)); + if (buffer.size() > CHIP_CONFIG_SCENES_MAX_EXTENSION_FIELDSET_SIZE_PER_CLUSTER) + { + this->usedBytes = CHIP_CONFIG_SCENES_MAX_EXTENSION_FIELDSET_SIZE_PER_CLUSTER; + } + else + { + this->usedBytes = static_cast(buffer.size()); + } + memcpy(this->bytesBuffer, buffer.data(), this->usedBytes); + // ReturnErrorOnFailure(reader.GetBytes(bytesBuffer, CHIP_CONFIG_SCENES_MAX_EXTENSION_FIELDSET_SIZE_PER_CLUSTER)); + + return reader.ExitContainer(container); + } + + void Clear() + { + this->ID = kInvalidClusterId; + memset(this->bytesBuffer, 0, CHIP_CONFIG_SCENES_MAX_EXTENSION_FIELDSET_SIZE_PER_CLUSTER); + this->usedBytes = 0; + } + + bool is_empty() const { return (this->usedBytes == 0); } + + bool operator==(const ExtensionFieldsSet & other) + { + return (this->ID == other.ID && !memcmp(this->bytesBuffer, other.bytesBuffer, this->usedBytes) && + this->usedBytes == other.usedBytes); + } + + CHIP_ERROR operator=(const ExtensionFieldsSet & other) + { + if (other.usedBytes <= CHIP_CONFIG_SCENES_MAX_EXTENSION_FIELDSET_SIZE_PER_CLUSTER) + { + memcpy(this->bytesBuffer, other.bytesBuffer, other.usedBytes); + } + else + { + return CHIP_ERROR_BUFFER_TOO_SMALL; + } + this->ID = other.ID; + this->usedBytes = other.usedBytes; + + return CHIP_NO_ERROR; + } +}; + +class ExtensionFieldsSetsImpl : public ExtensionFieldsSets +{ +public: + ExtensionFieldsSetsImpl(); + ~ExtensionFieldsSetsImpl() override{}; + + // overrides + CHIP_ERROR Serialize(TLV::TLVWriter & writer) const override; + CHIP_ERROR Deserialize(TLV::TLVReader & reader) override; + void Clear() override; + bool is_empty() const override; + + // implementation + CHIP_ERROR insertField(ExtensionFieldsSet & field); + CHIP_ERROR getFieldAtPosition(ExtensionFieldsSet & field, uint8_t position); + CHIP_ERROR removeFieldAtPosition(uint8_t position); + + bool operator==(const ExtensionFieldsSetsImpl & other) + { + for (uint8_t i = 0; i < CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENES; i++) + { + if (!(this->EFS[i] == other.EFS[i])) + { + return false; + } + } + return true; + } + + CHIP_ERROR operator=(const ExtensionFieldsSetsImpl & other) + { + for (uint8_t i = 0; i < CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENES; i++) + { + ReturnErrorOnFailure(this->EFS[i] = other.EFS[i]); + } + fieldNum = other.fieldNum; + + return CHIP_NO_ERROR; + } + +protected: + static constexpr TLV::Tag TagFieldNum() { return TLV::ContextTag(1); } + ExtensionFieldsSet EFS[CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENES]; + uint8_t fieldNum = 0; +}; +} // namespace scenes +} // namespace chip diff --git a/src/app/clusters/scenes/SceneTable.h b/src/app/clusters/scenes/SceneTable.h index ddc96f69036c95..3322983848dd0d 100644 --- a/src/app/clusters/scenes/SceneTable.h +++ b/src/app/clusters/scenes/SceneTable.h @@ -17,7 +17,7 @@ #pragma once #include -#include +#include #include #include #include @@ -111,7 +111,7 @@ class SceneTable char name[kSceneNameMax] = { 0 }; size_t nameLength = 0; SceneTransitionTime sceneTransitionTime = 0; - ExtensionFieldsSets extentsionFieldsSets; + ExtensionFieldsSetsImpl extentsionFieldsSets; TransitionTime100ms transitionTime100 = 0; CharSpan nameSpan; @@ -120,7 +120,7 @@ class SceneTable { this->SetName(sceneName); } - SceneData(ExtensionFieldsSets fields, const CharSpan & sceneName = CharSpan(), SceneTransitionTime time = 0, + SceneData(ExtensionFieldsSetsImpl fields, const CharSpan & sceneName = CharSpan(), SceneTransitionTime time = 0, TransitionTime100ms time100ms = 0) : sceneTransitionTime(time), transitionTime100(time100ms) @@ -134,6 +134,7 @@ class SceneTable this->SetName(other.nameSpan); extentsionFieldsSets = other.extentsionFieldsSets; } + ~SceneData(){}; CHIP_ERROR Serialize(TLV::TLVWriter & writer) const { @@ -167,8 +168,6 @@ class SceneTable { ReturnErrorOnFailure(reader.Get(this->nameSpan)); this->SetName(this->nameSpan); - - // Putting a null terminator ReturnErrorOnFailure(reader.Next(TagSceneTransitionTime())); } diff --git a/src/app/clusters/scenes/SceneTableImpl.cpp b/src/app/clusters/scenes/SceneTableImpl.cpp index c3c20d0c749dfe..a0502e21e9166d 100644 --- a/src/app/clusters/scenes/SceneTableImpl.cpp +++ b/src/app/clusters/scenes/SceneTableImpl.cpp @@ -403,6 +403,115 @@ CHIP_ERROR DefaultSceneTableImpl::RemoveSceneTableEntry(FabricIndex fabric_index return CHIP_NO_ERROR; } +/// @brief Registers handle to get extension fields set for a specific cluster. If the handler is already present in the handler +/// array, it will be overwritten +/// @param ID ID of the cluster used to fill the extension fields set +/// @param get_function pointer to function to call to get the extension fiels set from the cluster +/// @param set_function pointer to function to call send an extension field to the cluster +/// @return CHIP_ERROR_BUFFER_TO_SMALL if couldn't insert the handler, otherwise CHIP_NO_ERROR +CHIP_ERROR DefaultSceneTableImpl::registerHandler(ClusterId ID, clusterFieldsHandle get_function, clusterFieldsHandle set_function) +{ + uint8_t idPosition = 0xff, fisrtEmptyPosition = 0xff; + for (uint8_t i = 0; i < CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENES; i++) + { + if (this->handlers[i].getID() == ID) + { + idPosition = i; + break; + } + if (!this->handlers[i].isInitialized() && fisrtEmptyPosition == 0xff) + { + fisrtEmptyPosition = i; + } + } + + // if found, insert at found position, otherwise at first free possition, otherwise return error + if (idPosition < CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENES) + { + this->handlers[idPosition].initSceneHandler(ID, get_function, set_function); + return CHIP_NO_ERROR; + } + else if (fisrtEmptyPosition < CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENES) + { + this->handlers[fisrtEmptyPosition].initSceneHandler(ID, get_function, set_function); + this->handlerNum++; + return CHIP_NO_ERROR; + } + else + { + return CHIP_ERROR_BUFFER_TOO_SMALL; + } + + return CHIP_ERROR_BUFFER_TOO_SMALL; +} + +CHIP_ERROR DefaultSceneTableImpl::unregisterHandler(uint8_t position) +{ + if (!handlerListEmpty()) + { + if (position < CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENES) + { + if (this->handlers[position].isInitialized()) + { + this->handlers[position].clearSceneHandler(); + this->handlerNum--; + } + } + else + { + return CHIP_ERROR_ACCESS_DENIED; + } + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR DefaultSceneTableImpl::EFSValuesFromCluster(ExtensionFieldsSetsImpl & fieldSets) +{ + ExtensionFieldsSet EFS; + if (!this->handlerListEmpty()) + { + for (uint8_t i = 0; i < CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENES; i++) + { + if (this->handlers[i].isInitialized()) + { + ReturnErrorOnFailure(this->handlers[i].getClusterEFS(EFS)); + ReturnErrorOnFailure(fieldSets.insertField(EFS)); + } + } + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR DefaultSceneTableImpl::EFSValuesToCluster(ExtensionFieldsSetsImpl & fieldSets) +{ + ExtensionFieldsSet EFS; + if (!this->handlerListEmpty()) + { + for (uint8_t i = 0; i < CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENES; i++) + { + fieldSets.getFieldAtPosition(EFS, i); + + if (!EFS.is_empty()) + { + if (!this->handlerListEmpty()) + { + for (uint8_t j = 0; j < CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENES; j++) + { + if (EFS.ID == this->handlers[j].getID()) + { + ReturnErrorOnFailure(this->handlers[j].setClusterEFS(EFS)); + } + } + } + } + } + } + + return CHIP_NO_ERROR; +} + CHIP_ERROR DefaultSceneTableImpl::RemoveFabric(FabricIndex fabric_index) { FabricSceneData fabric(fabric_index); diff --git a/src/app/clusters/scenes/SceneTableImpl.h b/src/app/clusters/scenes/SceneTableImpl.h index f191b19d243d08..0513e39dde5ae6 100644 --- a/src/app/clusters/scenes/SceneTableImpl.h +++ b/src/app/clusters/scenes/SceneTableImpl.h @@ -25,6 +25,90 @@ namespace chip { namespace scenes { +using clusterId = chip::ClusterId; + +typedef CHIP_ERROR (*clusterFieldsHandle)(ExtensionFieldsSet & fields); + +/// @brief Class to allow extension field sets to be handled by the scene table without any knowledge of the cluster or its +/// implementation +class SceneHandler +{ +public: + SceneHandler(ClusterId Id = kInvalidClusterId, clusterFieldsHandle getClusterEFS = nullptr, + clusterFieldsHandle setClusterEFS = nullptr) + { + if (getClusterEFS != nullptr && setClusterEFS != nullptr && Id != kInvalidClusterId) + { + getEFS = getClusterEFS; + setEFS = setClusterEFS; + cID = Id; + initialized = true; + } + }; + ~SceneHandler(){}; + + void initSceneHandler(ClusterId Id, clusterFieldsHandle getClusterEFS, clusterFieldsHandle setClusterEFS) + { + if (getClusterEFS != nullptr && setClusterEFS != nullptr && Id != kInvalidClusterId) + { + getEFS = getClusterEFS; + setEFS = setClusterEFS; + cID = Id; + initialized = true; + } + } + + void clearSceneHandler() + { + getEFS = nullptr; + setEFS = nullptr; + cID = kInvalidClusterId; + initialized = false; + } + + CHIP_ERROR getClusterEFS(ExtensionFieldsSet & clusterFields) + { + if (this->isInitialized()) + { + ReturnErrorOnFailure(getEFS(clusterFields)); + } + + return CHIP_NO_ERROR; + } + CHIP_ERROR setClusterEFS(ExtensionFieldsSet & clusterFields) + { + if (this->isInitialized()) + { + ReturnErrorOnFailure(setEFS(clusterFields)); + } + + return CHIP_NO_ERROR; + } + + bool isInitialized() const { return this->initialized; } + + ClusterId getID() { return cID; } + + bool operator==(const SceneHandler & other) + { + return (this->getEFS == other.getEFS && this->setEFS == other.setEFS && this->cID == other.cID && + initialized == other.initialized); + } + void operator=(const SceneHandler & other) + { + this->getEFS = other.getEFS; + this->setEFS = other.setEFS; + this->cID = other.cID; + this->initialized = true; + } + +protected: + clusterFieldsHandle getEFS = nullptr; + clusterFieldsHandle setEFS = nullptr; + ClusterId cID = kInvalidClusterId; + bool initialized = false; +}; + /** * @brief Implementation of a storage in nonvolatile storage of the scene table. * @@ -43,22 +127,30 @@ class DefaultSceneTableImpl : public SceneTable CHIP_ERROR Init(PersistentStorageDelegate * storage) override; void Finish() override; - // - // Scene Data - // - - // By id + // Scene access by Id CHIP_ERROR SetSceneTableEntry(FabricIndex fabric_index, const SceneTableEntry & entry) override; CHIP_ERROR GetSceneTableEntry(FabricIndex fabric_index, DefaultSceneTableImpl::SceneStorageId scene_id, SceneTableEntry & entry) override; CHIP_ERROR RemoveSceneTableEntry(FabricIndex fabric_index, DefaultSceneTableImpl::SceneStorageId scene_id) override; - // Iterators - SceneEntryIterator * IterateSceneEntry(FabricIndex fabric_index) override; + // SceneHandlers + CHIP_ERROR registerHandler(ClusterId ID, clusterFieldsHandle get_function, clusterFieldsHandle set_function); + CHIP_ERROR unregisterHandler(uint8_t position); + + // Extension field sets operation + CHIP_ERROR EFSValuesFromCluster(ExtensionFieldsSetsImpl & fieldSets); + CHIP_ERROR EFSValuesToCluster(ExtensionFieldsSetsImpl & fieldSets); // Fabrics CHIP_ERROR RemoveFabric(FabricIndex fabric_index) override; + // Iterators + SceneEntryIterator * IterateSceneEntry(FabricIndex fabric_index) override; + + bool handlerListEmpty() { return (handlerNum == 0); } + bool handlerListFull() { return (handlerNum >= CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENES); } + uint8_t getHandlerNum() { return this->handlerNum; } + protected: class SceneEntryIteratorImpl : public SceneEntryIterator { @@ -79,6 +171,8 @@ class DefaultSceneTableImpl : public SceneTable chip::PersistentStorageDelegate * mStorage = nullptr; ObjectPool mSceneEntryIterators; + SceneHandler handlers[CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENES]; + uint8_t handlerNum = 0; }; // class DefaultSceneTableImpl /** diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn index fe55906fe57ef2..606b6d74937d54 100644 --- a/src/app/tests/BUILD.gn +++ b/src/app/tests/BUILD.gn @@ -81,8 +81,9 @@ source_set("ota-requestor-test-srcs") { source_set("scenes-table-test-srcs") { sources = [ - "${chip_root}/src/app/clusters/scenes/ExtensionFieldsSets.cpp", "${chip_root}/src/app/clusters/scenes/ExtensionFieldsSets.h", + "${chip_root}/src/app/clusters/scenes/ExtensionFieldsSetsImpl.cpp", + "${chip_root}/src/app/clusters/scenes/ExtensionFieldsSetsImpl.h", "${chip_root}/src/app/clusters/scenes/SceneTable.h", "${chip_root}/src/app/clusters/scenes/SceneTableImpl.cpp", "${chip_root}/src/app/clusters/scenes/SceneTableImpl.h", @@ -93,7 +94,6 @@ source_set("scenes-table-test-srcs") { "${chip_root}/src/app/common:cluster-objects", "${chip_root}/src/lib/core", ] - defines = [ "SCENES_TESTING_CONFIGURATION=1" ] } chip_test_suite("tests") { diff --git a/src/app/tests/TestSceneTable.cpp b/src/app/tests/TestSceneTable.cpp index 791c6d4f2e0b2c..055b1514cd69fc 100644 --- a/src/app/tests/TestSceneTable.cpp +++ b/src/app/tests/TestSceneTable.cpp @@ -21,12 +21,13 @@ #include #include -using FabricIndex = chip::FabricIndex; -using SceneTableEntry = chip::scenes::DefaultSceneTableImpl::SceneTableEntry; -using SceneTableImpl = chip::scenes::DefaultSceneTableImpl; -using SceneStorageId = chip::scenes::DefaultSceneTableImpl::SceneStorageId; -using SceneData = chip::scenes::DefaultSceneTableImpl::SceneData; -using CharSpan = chip::CharSpan; +using FabricIndex = chip::FabricIndex; +using SceneTableEntry = chip::scenes::DefaultSceneTableImpl::SceneTableEntry; +using SceneTableImpl = chip::scenes::DefaultSceneTableImpl; +using SceneStorageId = chip::scenes::DefaultSceneTableImpl::SceneStorageId; +using SceneData = chip::scenes::DefaultSceneTableImpl::SceneData; +using CharSpan = chip::CharSpan; +using ExtensionFieldsSet = chip::scenes::ExtensionFieldsSet; namespace { @@ -76,6 +77,82 @@ SceneTableEntry scene10(sceneId1, sceneData10); SceneTableEntry scene11(sceneId5, sceneData11); SceneTableEntry scene12(sceneId8, sceneData12); +// EFS +static const ExtensionFieldsSet onOffEFS1 = ExtensionFieldsSet(0x0006, (uint8_t *) "1", 1); +static const ExtensionFieldsSet onOffEFS2 = ExtensionFieldsSet(0x0006, (uint8_t *) "0", 1); +static const ExtensionFieldsSet levelControlEFS1 = ExtensionFieldsSet(0x0008, (uint8_t *) "511", 3); +static const ExtensionFieldsSet levelControlEFS2 = ExtensionFieldsSet(0x0008, (uint8_t *) "222", 3); +static const ExtensionFieldsSet colorControlEFS1 = ExtensionFieldsSet(0x0303, (uint8_t *) "123456789abcde", 14); +static const ExtensionFieldsSet colorControlEFS2 = ExtensionFieldsSet(0x0303, (uint8_t *) "abcdefghi12345", 14); + +// Simulation of clusters callbacks (sate #1) +CHIP_ERROR test_on_off_from_cluster_callback1(ExtensionFieldsSet & fields) +{ + ReturnErrorOnFailure(fields = onOffEFS1); + return CHIP_NO_ERROR; +} +CHIP_ERROR test_on_off_to_cluster_callback1(ExtensionFieldsSet & fields) +{ + VerifyOrReturnError(fields == onOffEFS1, CHIP_ERROR_WRITE_FAILED); + return CHIP_NO_ERROR; +} +CHIP_ERROR test_level_control_from_cluster_callback1(ExtensionFieldsSet & fields) +{ + ReturnErrorOnFailure(fields = levelControlEFS1); + return CHIP_NO_ERROR; +} +CHIP_ERROR test_level_control_to_cluster_callback1(ExtensionFieldsSet & fields) +{ + VerifyOrReturnError(fields == levelControlEFS1, CHIP_ERROR_WRITE_FAILED); + return CHIP_NO_ERROR; +} + +CHIP_ERROR test_color_control_from_cluster_callback1(ExtensionFieldsSet & fields) +{ + ReturnErrorOnFailure(fields = colorControlEFS1); + return CHIP_NO_ERROR; +} + +CHIP_ERROR test_color_control_to_cluster_callback1(ExtensionFieldsSet & fields) +{ + VerifyOrReturnError(fields == colorControlEFS1, CHIP_ERROR_WRITE_FAILED); + return CHIP_NO_ERROR; +} + +// Simulation of clusters callbacks (sate #2) +CHIP_ERROR test_on_off_from_cluster_callback2(ExtensionFieldsSet & fields) +{ + ReturnErrorOnFailure(fields = onOffEFS1); + return CHIP_NO_ERROR; +} +CHIP_ERROR test_on_off_to_cluster_callback2(ExtensionFieldsSet & fields) +{ + VerifyOrReturnError(fields == onOffEFS1, CHIP_ERROR_WRITE_FAILED); + return CHIP_NO_ERROR; +} +CHIP_ERROR test_level_control_from_cluster_callback2(ExtensionFieldsSet & fields) +{ + ReturnErrorOnFailure(fields = levelControlEFS1); + return CHIP_NO_ERROR; +} +CHIP_ERROR test_level_control_to_cluster_callback2(ExtensionFieldsSet & fields) +{ + VerifyOrReturnError(fields == levelControlEFS1, CHIP_ERROR_WRITE_FAILED); + return CHIP_NO_ERROR; +} + +CHIP_ERROR test_color_control_from_cluster_callback2(ExtensionFieldsSet & fields) +{ + ReturnErrorOnFailure(fields = colorControlEFS1); + return CHIP_NO_ERROR; +} + +CHIP_ERROR test_color_control_to_cluster_callback2(ExtensionFieldsSet & fields) +{ + VerifyOrReturnError(fields == colorControlEFS1, CHIP_ERROR_WRITE_FAILED); + return CHIP_NO_ERROR; +} + void ResetSceneTable(SceneTableImpl * sceneTable) { sceneTable->RemoveFabric(kFabric1); @@ -90,6 +167,26 @@ void TestStoreScenes(nlTestSuite * aSuite, void * aContext) // Reset test ResetSceneTable(sceneTable); + // Test SceneHandlers + NL_TEST_ASSERT( + aSuite, + CHIP_NO_ERROR == + sceneTable->registerHandler(onOffEFS1.ID, &test_on_off_from_cluster_callback1, &test_on_off_to_cluster_callback1)); + NL_TEST_ASSERT(aSuite, + CHIP_NO_ERROR == + sceneTable->registerHandler(levelControlEFS1.ID, &test_level_control_from_cluster_callback1, + &test_level_control_to_cluster_callback1)); + NL_TEST_ASSERT(aSuite, + CHIP_NO_ERROR == + sceneTable->registerHandler(colorControlEFS1.ID, &test_color_control_from_cluster_callback1, + &test_color_control_to_cluster_callback1)); + + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->EFSValuesFromCluster(scene1.storageData.extentsionFieldsSets)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->EFSValuesFromCluster(scene2.storageData.extentsionFieldsSets)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->EFSValuesFromCluster(scene3.storageData.extentsionFieldsSets)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->EFSValuesFromCluster(scene4.storageData.extentsionFieldsSets)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->EFSValuesFromCluster(scene5.storageData.extentsionFieldsSets)); + SceneTableEntry scene; // Set test @@ -110,14 +207,24 @@ void TestStoreScenes(nlTestSuite * aSuite, void * aContext) NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId1, scene)); NL_TEST_ASSERT(aSuite, scene == scene1); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->EFSValuesToCluster(scene.storageData.extentsionFieldsSets)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId2, scene)); NL_TEST_ASSERT(aSuite, scene == scene2); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->EFSValuesToCluster(scene.storageData.extentsionFieldsSets)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId3, scene)); NL_TEST_ASSERT(aSuite, scene == scene3); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->EFSValuesToCluster(scene.storageData.extentsionFieldsSets)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId4, scene)); NL_TEST_ASSERT(aSuite, scene == scene4); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->EFSValuesToCluster(scene.storageData.extentsionFieldsSets)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId5, scene)); NL_TEST_ASSERT(aSuite, scene == scene5); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->EFSValuesToCluster(scene.storageData.extentsionFieldsSets)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId6, scene)); NL_TEST_ASSERT(aSuite, scene == scene6); NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId7, scene)); @@ -131,6 +238,27 @@ void TestOverwriteScenes(nlTestSuite * aSuite, void * aContext) SceneTableImpl * sceneTable = chip::scenes::GetSceneTable(); NL_TEST_ASSERT(aSuite, sceneTable); + // Test SceneHandlers overwrite + NL_TEST_ASSERT( + aSuite, + CHIP_NO_ERROR == + sceneTable->registerHandler(onOffEFS2.ID, &test_on_off_from_cluster_callback2, &test_on_off_to_cluster_callback2)); + NL_TEST_ASSERT(aSuite, + CHIP_NO_ERROR == + sceneTable->registerHandler(levelControlEFS2.ID, &test_level_control_from_cluster_callback2, + &test_level_control_to_cluster_callback2)); + NL_TEST_ASSERT(aSuite, + CHIP_NO_ERROR == + sceneTable->registerHandler(colorControlEFS2.ID, &test_color_control_from_cluster_callback2, + &test_color_control_to_cluster_callback2)); + + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->EFSValuesFromCluster(scene10.storageData.extentsionFieldsSets)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->EFSValuesFromCluster(scene11.storageData.extentsionFieldsSets)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->EFSValuesFromCluster(scene12.storageData.extentsionFieldsSets)); + + // Verfies the overwrite hasn't changed the handlers number + NL_TEST_ASSERT(aSuite, sceneTable->getHandlerNum() == 3); + SceneTableEntry scene; // Overwriting the first entry NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->SetSceneTableEntry(kFabric1, scene10)); @@ -142,10 +270,15 @@ void TestOverwriteScenes(nlTestSuite * aSuite, void * aContext) // Scene 10 has the same sceneId as scene 1, Get->sceneId1 should thus return scene 10, etc. NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId1, scene)); NL_TEST_ASSERT(aSuite, scene == scene10); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->EFSValuesToCluster(scene.storageData.extentsionFieldsSets)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId5, scene)); NL_TEST_ASSERT(aSuite, scene == scene11); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->EFSValuesToCluster(scene.storageData.extentsionFieldsSets)); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->GetSceneTableEntry(kFabric1, sceneId8, scene)); NL_TEST_ASSERT(aSuite, scene == scene12); + NL_TEST_ASSERT(aSuite, CHIP_NO_ERROR == sceneTable->EFSValuesToCluster(scene.storageData.extentsionFieldsSets)); } void TestIterateScenes(nlTestSuite * aSuite, void * aContext) diff --git a/src/lib/core/CHIPConfig.h b/src/lib/core/CHIPConfig.h index e951b485e753ef..05976b1d73687c 100644 --- a/src/lib/core/CHIPConfig.h +++ b/src/lib/core/CHIPConfig.h @@ -1413,6 +1413,20 @@ extern const char CHIP_NON_PRODUCTION_MARKER[]; #define CHIP_CONFIG_SCENES_MAX_PER_FABRIC (CHIP_CONFIG_SCENES_MAX_NUMBER / 2) #endif +/** + * @brief The maximum number of cluster per scene + */ +#ifndef CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENES +#define CHIP_CONFIG_SCENES_MAX_CLUSTERS_PER_SCENES 7 +#endif + +/** + * @brief The maximum size of a single extension field set for a single cluster + */ +#ifndef CHIP_CONFIG_SCENES_MAX_EXTENSION_FIELDSET_SIZE_PER_CLUSTER +#define CHIP_CONFIG_SCENES_MAX_EXTENSION_FIELDSET_SIZE_PER_CLUSTER 15 +#endif + /** * @} */