From 47077574fdf42eff27889ced4812ff5bddaa0aee Mon Sep 17 00:00:00 2001 From: William Date: Tue, 8 Aug 2023 19:33:11 +0100 Subject: [PATCH] Mode base reporting change in supported modes (#28575) * Reordered Mode Base functions. * Moved generic OpState cluster objects into one header file. * Reordered OperationalStateServer functions. * Moved OpState delegate in the server header file. * Updated the TestOperationalStateDelegate inctludes following the operational state delegate change in location. * Renamed TestOperationalStateDelegate.cpp -> src/app/tests/TestOperationalStateClusterObjects.cpp * Restyled by clang-format * Minor corrections following revision. * Added a method for the SDK consumer to report that the contents of the supported modes has changed and updated documentation accordingly. * Fixed comments from review. * Restyled by clang-format --------- Co-authored-by: Restyled.io --- .../include/operational-state-delegate-impl.h | 2 +- .../include/operational-state-delegate-impl.h | 2 +- src/app/chip_data_model.gni | 2 +- .../mode-base-server/mode-base-server.cpp | 460 +++++++++--------- .../mode-base-server/mode-base-server.h | 146 +++--- ....h => operational-state-cluster-objects.h} | 142 ++---- .../operational-state-server.cpp | 50 -- .../operational-state-server.h | 150 +++++- src/app/tests/BUILD.gn | 4 +- ...=> TestOperationalStateClusterObjects.cpp} | 8 +- 10 files changed, 497 insertions(+), 469 deletions(-) rename src/app/clusters/operational-state-server/{operational-state-delegate.h => operational-state-cluster-objects.h} (60%) rename src/app/tests/{TestOperationalStateDelegate.cpp => TestOperationalStateClusterObjects.cpp} (99%) diff --git a/examples/all-clusters-app/all-clusters-common/include/operational-state-delegate-impl.h b/examples/all-clusters-app/all-clusters-common/include/operational-state-delegate-impl.h index 935fdb626bfd14..939213d296ad53 100644 --- a/examples/all-clusters-app/all-clusters-common/include/operational-state-delegate-impl.h +++ b/examples/all-clusters-app/all-clusters-common/include/operational-state-delegate-impl.h @@ -19,7 +19,7 @@ #pragma once #include -#include +#include #include #include diff --git a/examples/dishwasher-app/dishwasher-common/include/operational-state-delegate-impl.h b/examples/dishwasher-app/dishwasher-common/include/operational-state-delegate-impl.h index 935fdb626bfd14..939213d296ad53 100644 --- a/examples/dishwasher-app/dishwasher-common/include/operational-state-delegate-impl.h +++ b/examples/dishwasher-app/dishwasher-common/include/operational-state-delegate-impl.h @@ -19,7 +19,7 @@ #pragma once #include -#include +#include #include #include diff --git a/src/app/chip_data_model.gni b/src/app/chip_data_model.gni index 20b1f9d0e18e1e..d484d7ce654e9b 100644 --- a/src/app/chip_data_model.gni +++ b/src/app/chip_data_model.gni @@ -267,7 +267,7 @@ template("chip_data_model") { sources += [ "${_app_root}/clusters/${cluster}/${cluster}.cpp", "${_app_root}/clusters/${cluster}/${cluster}.h", - "${_app_root}/clusters/${cluster}/operational-state-delegate.h", + "${_app_root}/clusters/${cluster}/operational-state-cluster-objects.h", ] } else if (cluster == "dishwasher-alarm-server") { sources += [ diff --git a/src/app/clusters/mode-base-server/mode-base-server.cpp b/src/app/clusters/mode-base-server/mode-base-server.cpp index 4495e728fc7ecb..b5b8a403b9ba3e 100644 --- a/src/app/clusters/mode-base-server/mode-base-server.cpp +++ b/src/app/clusters/mode-base-server/mode-base-server.cpp @@ -39,104 +39,22 @@ namespace app { namespace Clusters { namespace ModeBase { -void Instance::RegisterInstance() -{ - if (!gModeBaseAliasesInstances.Contains(this)) - { - gModeBaseAliasesInstances.PushBack(this); - } -} - -void Instance::UnregisterInstance() -{ - gModeBaseAliasesInstances.Remove(this); -} - -bool Instance::HasFeature(Feature feature) const +Instance::Instance(Delegate * aDelegate, EndpointId aEndpointId, ClusterId aClusterId, uint32_t aFeature) : + CommandHandlerInterface(Optional(aEndpointId), aClusterId), + AttributeAccessInterface(Optional(aEndpointId), aClusterId), mDelegate(aDelegate), mEndpointId(aEndpointId), + mClusterId(aClusterId), + mCurrentMode(0), // This is a temporary value and may not be valid. We will change this to the value of the first + // mode in the list at the start of the Init function to ensure that it represents a valid mode. + mFeature(aFeature) { - return (mFeature & to_underlying(feature)) != 0; + mDelegate->SetInstance(this); } -void Instance::LoadPersistentAttributes() +Instance::~Instance() { - // Load Current Mode - uint8_t tempCurrentMode; - CHIP_ERROR err = GetSafeAttributePersistenceProvider()->ReadScalarValue( - ConcreteAttributePath(mEndpointId, mClusterId, Attributes::CurrentMode::Id), tempCurrentMode); - if (err == CHIP_NO_ERROR) - { - Status status = UpdateCurrentMode(tempCurrentMode); - if (status == Status::Success) - { - ChipLogDetail(Zcl, "ModeBase: Loaded CurrentMode as %u", GetCurrentMode()); - } - else - { - ChipLogError(Zcl, "ModeBase: Could not update CurrentMode to %u: %u", tempCurrentMode, to_underlying(status)); - } - } - else - { - // If we cannot find the previous CurrentMode, we will assume it to be the first mode in the - // list, as was initialised in the constructor. - ChipLogDetail(Zcl, "ModeBase: Unable to load the CurrentMode from the KVS. Assuming %u", GetCurrentMode()); - } - - // Load Start-Up Mode - DataModel::Nullable tempStartUpMode; - err = GetSafeAttributePersistenceProvider()->ReadScalarValue( - ConcreteAttributePath(mEndpointId, mClusterId, Attributes::StartUpMode::Id), tempStartUpMode); - if (err == CHIP_NO_ERROR) - { - Status status = UpdateStartUpMode(tempStartUpMode); - if (status == Status::Success) - { - if (GetStartUpMode().IsNull()) - { - ChipLogDetail(Zcl, "ModeBase: Loaded StartUpMode as null"); - } - else - { - ChipLogDetail(Zcl, "ModeBase: Loaded StartUpMode as %u", GetStartUpMode().Value()); - } - } - else - { - ChipLogError(Zcl, "ModeBase: Could not update StartUpMode: %u", to_underlying(status)); - } - } - else - { - ChipLogDetail(Zcl, "ModeBase: Unable to load the StartUpMode from the KVS. Assuming null"); - } - - // Load On Mode - DataModel::Nullable tempOnMode; - err = GetSafeAttributePersistenceProvider()->ReadScalarValue( - ConcreteAttributePath(mEndpointId, mClusterId, Attributes::OnMode::Id), tempOnMode); - if (err == CHIP_NO_ERROR) - { - Status status = UpdateOnMode(tempOnMode); - if (status == Status::Success) - { - if (GetOnMode().IsNull()) - { - ChipLogDetail(Zcl, "ModeBase: Loaded OnMode as null"); - } - else - { - ChipLogDetail(Zcl, "ModeBase: Loaded OnMode as %u", GetOnMode().Value()); - } - } - else - { - ChipLogError(Zcl, "ModeBase: Could not update OnMode: %u", to_underlying(status)); - } - } - else - { - ChipLogDetail(Zcl, "ModeBase: Unable to load the OnMode from the KVS. Assuming null"); - } + UnregisterInstance(); + chip::app::InteractionModelEngine::GetInstance()->UnregisterCommandHandler(this); + unregisterAttributeAccessOverride(this); } CHIP_ERROR Instance::Init() @@ -194,6 +112,7 @@ CHIP_ERROR Instance::Init() } } } + #ifdef EMBER_AF_PLUGIN_ON_OFF_SERVER // OnMode with Power Up // If the On/Off feature is supported and the On/Off cluster attribute StartUpOnOff is present, with a @@ -229,9 +148,110 @@ CHIP_ERROR Instance::Init() } } #endif // EMBER_AF_PLUGIN_ON_OFF_SERVER + return CHIP_NO_ERROR; } +Status Instance::UpdateCurrentMode(uint8_t aNewMode) +{ + if (!IsSupportedMode(aNewMode)) + { + return Protocols::InteractionModel::Status::ConstraintError; + } + uint8_t oldMode = mCurrentMode; + mCurrentMode = aNewMode; + if (mCurrentMode != oldMode) + { + // Write new value to persistent storage. + ConcreteAttributePath path = ConcreteAttributePath(mEndpointId, mClusterId, Attributes::CurrentMode::Id); + GetSafeAttributePersistenceProvider()->WriteScalarValue(path, mCurrentMode); + MatterReportingAttributeChangeCallback(path); + } + return Protocols::InteractionModel::Status::Success; +} + +Status Instance::UpdateStartUpMode(DataModel::Nullable aNewStartUpMode) +{ + if (!aNewStartUpMode.IsNull()) + { + if (!IsSupportedMode(aNewStartUpMode.Value())) + { + return Protocols::InteractionModel::Status::ConstraintError; + } + } + DataModel::Nullable oldStartUpMode = mStartUpMode; + mStartUpMode = aNewStartUpMode; + if (mStartUpMode != oldStartUpMode) + { + // Write new value to persistent storage. + ConcreteAttributePath path = ConcreteAttributePath(mEndpointId, mClusterId, Attributes::StartUpMode::Id); + GetSafeAttributePersistenceProvider()->WriteScalarValue(path, mStartUpMode); + MatterReportingAttributeChangeCallback(path); + } + return Protocols::InteractionModel::Status::Success; +} + +Status Instance::UpdateOnMode(DataModel::Nullable aNewOnMode) +{ + if (!aNewOnMode.IsNull()) + { + if (!IsSupportedMode(aNewOnMode.Value())) + { + return Protocols::InteractionModel::Status::ConstraintError; + } + } + DataModel::Nullable oldOnMode = mOnMode; + mOnMode = aNewOnMode; + if (mOnMode != oldOnMode) + { + // Write new value to persistent storage. + ConcreteAttributePath path = ConcreteAttributePath(mEndpointId, mClusterId, Attributes::OnMode::Id); + GetSafeAttributePersistenceProvider()->WriteScalarValue(path, mOnMode); + MatterReportingAttributeChangeCallback(path); + } + return Protocols::InteractionModel::Status::Success; +} + +uint8_t Instance::GetCurrentMode() const +{ + return mCurrentMode; +} + +DataModel::Nullable Instance::GetStartUpMode() const +{ + return mStartUpMode; +} + +DataModel::Nullable Instance::GetOnMode() const +{ + return mOnMode; +} + +void Instance::ReportSupportedModesChange() +{ + MatterReportingAttributeChangeCallback(ConcreteAttributePath(mEndpointId, mClusterId, Attributes::SupportedModes::Id)); +} + +bool Instance::HasFeature(Feature feature) const +{ + return (mFeature & to_underlying(feature)) != 0; +} + +bool Instance::IsSupportedMode(uint8_t modeValue) +{ + uint8_t value; + for (uint8_t i = 0; mDelegate->GetModeValueByIndex(i, value) != CHIP_ERROR_PROVIDER_LIST_EXHAUSTED; i++) + { + if (value == modeValue) + { + return true; + } + } + ChipLogDetail(Zcl, "Cannot find a mode with value %u", modeValue); + return false; +} + +// private methods template void Instance::HandleCommand(HandlerContext & handlerContext, FuncT func) { @@ -257,31 +277,6 @@ void Instance::HandleCommand(HandlerContext & handlerContext, FuncT func) } } -void Instance::HandleChangeToMode(HandlerContext & ctx, const Commands::ChangeToMode::DecodableType & commandData) -{ - uint8_t newMode = commandData.newMode; - - Commands::ChangeToModeResponse::Type response; - - if (!IsSupportedMode(newMode)) - { - ChipLogError(Zcl, "ModeBase: Failed to find the option with mode %u", newMode); - response.status = to_underlying(StatusCode::kUnsupportedMode); - ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); - return; - } - - mDelegate->HandleChangeToMode(newMode, response); - - if (response.status == to_underlying(StatusCode::kSuccess)) - { - UpdateCurrentMode(newMode); - ChipLogProgress(Zcl, "ModeBase: HandleChangeToMode changed to mode %u", newMode); - } - - ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); -} - // This function is called by the interaction model engine when a command destined for this instance is received. void Instance::InvokeCommand(HandlerContext & handlerContext) { @@ -310,38 +305,6 @@ CHIP_ERROR Instance::EnumerateGeneratedCommands(const ConcreteClusterPath & clus return CHIP_NO_ERROR; } -CHIP_ERROR Instance::EncodeSupportedModes(const AttributeValueEncoder::ListEncodeHelper & encoder) -{ - for (uint8_t i = 0; true; i++) - { - ModeOptionStructType mode; - - // Get the mode label - char buffer[kMaxModeLabelSize]; - MutableCharSpan label(buffer); - auto err = mDelegate->GetModeLabelByIndex(i, label); - if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) - { - return CHIP_NO_ERROR; - } - ReturnErrorOnFailure(err); - - mode.label = label; - - // Get the mode value - ReturnErrorOnFailure(mDelegate->GetModeValueByIndex(i, mode.mode)); - - // Get the mode tags - ModeTagStructType tagsBuffer[kMaxNumOfModeTags]; - DataModel::List tags(tagsBuffer); - ReturnErrorOnFailure(mDelegate->GetModeTagsByIndex(i, tags)); - mode.modeTags = tags; - - ReturnErrorOnFailure(encoder.Encode(mode)); - } - return CHIP_NO_ERROR; -} - // Implements the read functionality for complex attributes. CHIP_ERROR Instance::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) { @@ -387,111 +350,156 @@ CHIP_ERROR Instance::Write(const ConcreteDataAttributePath & attributePath, Attr return CHIP_ERROR_INCORRECT_STATE; } -Status Instance::UpdateCurrentMode(uint8_t aNewMode) +void Instance::RegisterInstance() { - if (!IsSupportedMode(aNewMode)) + if (!gModeBaseAliasesInstances.Contains(this)) { - return Protocols::InteractionModel::Status::ConstraintError; + gModeBaseAliasesInstances.PushBack(this); } - uint8_t oldMode = mCurrentMode; - mCurrentMode = aNewMode; - if (mCurrentMode != oldMode) +} + +void Instance::UnregisterInstance() +{ + gModeBaseAliasesInstances.Remove(this); +} + +void Instance::HandleChangeToMode(HandlerContext & ctx, const Commands::ChangeToMode::DecodableType & commandData) +{ + uint8_t newMode = commandData.newMode; + + Commands::ChangeToModeResponse::Type response; + + if (!IsSupportedMode(newMode)) { - // Write new value to persistent storage. - ConcreteAttributePath path = ConcreteAttributePath(mEndpointId, mClusterId, Attributes::CurrentMode::Id); - GetSafeAttributePersistenceProvider()->WriteScalarValue(path, mCurrentMode); - MatterReportingAttributeChangeCallback(path); + ChipLogError(Zcl, "ModeBase: Failed to find the option with mode %u", newMode); + response.status = to_underlying(StatusCode::kUnsupportedMode); + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + return; } - return Protocols::InteractionModel::Status::Success; + + mDelegate->HandleChangeToMode(newMode, response); + + if (response.status == to_underlying(StatusCode::kSuccess)) + { + UpdateCurrentMode(newMode); + ChipLogProgress(Zcl, "ModeBase: HandleChangeToMode changed to mode %u", newMode); + } + + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); } -Status Instance::UpdateStartUpMode(DataModel::Nullable aNewStartUpMode) +void Instance::LoadPersistentAttributes() { - if (!aNewStartUpMode.IsNull()) + // Load Current Mode + uint8_t tempCurrentMode; + CHIP_ERROR err = GetSafeAttributePersistenceProvider()->ReadScalarValue( + ConcreteAttributePath(mEndpointId, mClusterId, Attributes::CurrentMode::Id), tempCurrentMode); + if (err == CHIP_NO_ERROR) { - if (!IsSupportedMode(aNewStartUpMode.Value())) + Status status = UpdateCurrentMode(tempCurrentMode); + if (status == Status::Success) { - return Protocols::InteractionModel::Status::ConstraintError; + ChipLogDetail(Zcl, "ModeBase: Loaded CurrentMode as %u", GetCurrentMode()); + } + else + { + ChipLogError(Zcl, "ModeBase: Could not update CurrentMode to %u: %u", tempCurrentMode, to_underlying(status)); } } - DataModel::Nullable oldStartUpMode = mStartUpMode; - mStartUpMode = aNewStartUpMode; - if (mStartUpMode != oldStartUpMode) + else { - // Write new value to persistent storage. - ConcreteAttributePath path = ConcreteAttributePath(mEndpointId, mClusterId, Attributes::StartUpMode::Id); - GetSafeAttributePersistenceProvider()->WriteScalarValue(path, mStartUpMode); - MatterReportingAttributeChangeCallback(path); + // If we cannot find the previous CurrentMode, we will assume it to be the first mode in the + // list, as was initialised in the constructor. + ChipLogDetail(Zcl, "ModeBase: Unable to load the CurrentMode from the KVS. Assuming %u", GetCurrentMode()); } - return Protocols::InteractionModel::Status::Success; -} -Status Instance::UpdateOnMode(DataModel::Nullable aNewOnMode) -{ - if (!aNewOnMode.IsNull()) + // Load Start-Up Mode + DataModel::Nullable tempStartUpMode; + err = GetSafeAttributePersistenceProvider()->ReadScalarValue( + ConcreteAttributePath(mEndpointId, mClusterId, Attributes::StartUpMode::Id), tempStartUpMode); + if (err == CHIP_NO_ERROR) { - if (!IsSupportedMode(aNewOnMode.Value())) + Status status = UpdateStartUpMode(tempStartUpMode); + if (status == Status::Success) { - return Protocols::InteractionModel::Status::ConstraintError; + if (GetStartUpMode().IsNull()) + { + ChipLogDetail(Zcl, "ModeBase: Loaded StartUpMode as null"); + } + else + { + ChipLogDetail(Zcl, "ModeBase: Loaded StartUpMode as %u", GetStartUpMode().Value()); + } + } + else + { + ChipLogError(Zcl, "ModeBase: Could not update StartUpMode: %u", to_underlying(status)); } } - DataModel::Nullable oldOnMode = mOnMode; - mOnMode = aNewOnMode; - if (mOnMode != oldOnMode) + else { - // Write new value to persistent storage. - ConcreteAttributePath path = ConcreteAttributePath(mEndpointId, mClusterId, Attributes::OnMode::Id); - GetSafeAttributePersistenceProvider()->WriteScalarValue(path, mOnMode); - MatterReportingAttributeChangeCallback(path); + ChipLogDetail(Zcl, "ModeBase: Unable to load the StartUpMode from the KVS. Assuming null"); } - return Protocols::InteractionModel::Status::Success; -} -DataModel::Nullable Instance::GetStartUpMode() const -{ - return mStartUpMode; -} - -DataModel::Nullable Instance::GetOnMode() const -{ - return mOnMode; -} - -uint8_t Instance::GetCurrentMode() const -{ - return mCurrentMode; -} - -bool Instance::IsSupportedMode(uint8_t modeValue) -{ - uint8_t value; - for (uint8_t i = 0; mDelegate->GetModeValueByIndex(i, value) != CHIP_ERROR_PROVIDER_LIST_EXHAUSTED; i++) + // Load On Mode + DataModel::Nullable tempOnMode; + err = GetSafeAttributePersistenceProvider()->ReadScalarValue( + ConcreteAttributePath(mEndpointId, mClusterId, Attributes::OnMode::Id), tempOnMode); + if (err == CHIP_NO_ERROR) { - if (value == modeValue) + Status status = UpdateOnMode(tempOnMode); + if (status == Status::Success) { - return true; + if (GetOnMode().IsNull()) + { + ChipLogDetail(Zcl, "ModeBase: Loaded OnMode as null"); + } + else + { + ChipLogDetail(Zcl, "ModeBase: Loaded OnMode as %u", GetOnMode().Value()); + } + } + else + { + ChipLogError(Zcl, "ModeBase: Could not update OnMode: %u", to_underlying(status)); } } - ChipLogDetail(Zcl, "Cannot find a mode with value %u", modeValue); - return false; + else + { + ChipLogDetail(Zcl, "ModeBase: Unable to load the OnMode from the KVS. Assuming null"); + } } -Instance::Instance(Delegate * aDelegate, EndpointId aEndpointId, ClusterId aClusterId, uint32_t aFeature) : - CommandHandlerInterface(Optional(aEndpointId), aClusterId), - AttributeAccessInterface(Optional(aEndpointId), aClusterId), mDelegate(aDelegate), mEndpointId(aEndpointId), - mClusterId(aClusterId), - mCurrentMode(0), // This is a temporary value and may not be valid. We will change this to the value of the first - // mode in the list at the start of the Init function to ensure that it represents a valid mode. - mFeature(aFeature) +CHIP_ERROR Instance::EncodeSupportedModes(const AttributeValueEncoder::ListEncodeHelper & encoder) { - mDelegate->SetInstance(this); -} + for (uint8_t i = 0; true; i++) + { + ModeOptionStructType mode; -Instance::~Instance() -{ - UnregisterInstance(); - chip::app::InteractionModelEngine::GetInstance()->UnregisterCommandHandler(this); - unregisterAttributeAccessOverride(this); + // Get the mode label + char buffer[kMaxModeLabelSize]; + MutableCharSpan label(buffer); + auto err = mDelegate->GetModeLabelByIndex(i, label); + if (err == CHIP_ERROR_PROVIDER_LIST_EXHAUSTED) + { + return CHIP_NO_ERROR; + } + ReturnErrorOnFailure(err); + + mode.label = label; + + // Get the mode value + ReturnErrorOnFailure(mDelegate->GetModeValueByIndex(i, mode.mode)); + + // Get the mode tags + ModeTagStructType tagsBuffer[kMaxNumOfModeTags]; + DataModel::List tags(tagsBuffer); + ReturnErrorOnFailure(mDelegate->GetModeTagsByIndex(i, tags)); + mode.modeTags = tags; + + ReturnErrorOnFailure(encoder.Encode(mode)); + } + return CHIP_NO_ERROR; } IntrusiveList & GetModeBaseInstanceList() diff --git a/src/app/clusters/mode-base-server/mode-base-server.h b/src/app/clusters/mode-base-server/mode-base-server.h index b75eeb85e67481..10a2a97c07080f 100644 --- a/src/app/clusters/mode-base-server/mode-base-server.h +++ b/src/app/clusters/mode-base-server/mode-base-server.h @@ -34,55 +34,20 @@ class Delegate; class Instance : public CommandHandlerInterface, public AttributeAccessInterface, public IntrusiveListNodeBase<> { -private: - Delegate * mDelegate; - - EndpointId mEndpointId{}; - ClusterId mClusterId{}; - - // Attribute data store - uint8_t mCurrentMode; - DataModel::Nullable mStartUpMode; - DataModel::Nullable mOnMode; - uint32_t mFeature; - - // CommandHandlerInterface - void InvokeCommand(HandlerContext & ctx) override; - CHIP_ERROR EnumerateAcceptedCommands(const ConcreteClusterPath & cluster, CommandIdCallback callback, void * context) override; - CHIP_ERROR EnumerateGeneratedCommands(const ConcreteClusterPath & cluster, CommandIdCallback callback, void * context) override; - - // AttributeAccessInterface - CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; - CHIP_ERROR Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder) override; - - /** - * Register this ModeBase instance. - */ - void RegisterInstance(); - - /** - * Unregister this ModeBase instance. - */ - void UnregisterInstance(); - - /** - * Internal change-to-mode command handler function. - */ - void HandleChangeToMode(HandlerContext & ctx, const Commands::ChangeToMode::DecodableType & req); - +public: /** - * Helper function that loads all the persistent attributes from the KVS. These attributes are CurrentMode, - * StartUpMode and OnMode. + * Creates a mode base cluster instance. The Init() function needs to be called for this instance to be registered and + * called by the interaction model at the appropriate times. + * @param aDelegate A pointer to the delegate to be used by this server. + * Note: the caller must ensure that the delegate lives throughout the instance's lifetime. + * @param aEndpointId The endpoint on which this cluster exists. This must match the zap configuration. + * @param aClusterId The ID of the ModeBase derived cluster to be instantiated. + * @param aFeature The bitmask value that identifies which features are supported by this instance. */ - void LoadPersistentAttributes(); + Instance(Delegate * aDelegate, EndpointId aEndpointId, ClusterId aClusterId, uint32_t aFeature); - /** - * Helper function that encodes the supported modes. - * @param encoder The encoder to encode the supported modes into. - */ - CHIP_ERROR EncodeSupportedModes(const AttributeValueEncoder::ListEncodeHelper & encoder); + ~Instance() override; -public: /** * Initialise the ModeBase server instance. * @return Returns an error if the given endpoint and cluster ID have not been enabled in zap, if the @@ -91,6 +56,13 @@ class Instance : public CommandHandlerInterface, public AttributeAccessInterface CHIP_ERROR Init(); // Attribute setters + /** + * Sets the Current-Mode attribute. Note, this also handles writing the new value into non-volatile storage. + * @param aNewMode The value to which the Current-Mode mode is to be set. + * @return Returns a ConstraintError if the aNewMode value is not valid. Returns Success otherwise. + */ + Protocols::InteractionModel::Status UpdateCurrentMode(uint8_t aNewMode); + /** * Sets the Start-Up attribute. Note, this also handles writing the new value into non-volatile storage. * @param aNewStartUpMode The value to which the Start-Up mode is to be set. @@ -105,14 +77,12 @@ class Instance : public CommandHandlerInterface, public AttributeAccessInterface */ Protocols::InteractionModel::Status UpdateOnMode(DataModel::Nullable aNewOnMode); + // Attribute getters. /** - * Sets the Current-Mode attribute. Note, this also handles writing the new value into non-volatile storage. - * @param aNewMode The value to which the Current-Mode mode is to be set. - * @return Returns a ConstraintError if the aNewMode value is not valid. Returns Success otherwise. + * @return The Current mode. */ - Protocols::InteractionModel::Status UpdateCurrentMode(uint8_t aNewMode); + uint8_t GetCurrentMode() const; - // Attribute getters. /** * @return The Start-Up mode. */ @@ -123,11 +93,6 @@ class Instance : public CommandHandlerInterface, public AttributeAccessInterface */ DataModel::Nullable GetOnMode() const; - /** - * @return The Current mode. - */ - uint8_t GetCurrentMode() const; - /** * @return The endpoint ID. */ @@ -137,6 +102,13 @@ class Instance : public CommandHandlerInterface, public AttributeAccessInterface static constexpr uint8_t kMaxModeLabelSize = 64; static constexpr uint8_t kMaxNumOfModeTags = 8; + // List change reporting + /** + * Reports that the contents of the supported modes attribute have changed. + * The device SHALL call this method whenever it changes the list of supported modes. + */ + void ReportSupportedModesChange(); + /** * Returns true if the feature is supported. * @param feature the feature to check. @@ -149,19 +121,56 @@ class Instance : public CommandHandlerInterface, public AttributeAccessInterface */ bool IsSupportedMode(uint8_t mode); - /** - * Creates a mode base cluster instance. The Init() function needs to be called for this instance to be registered and - * called by the interaction model at the appropriate times. - * @param aEndpointId The endpoint on which this cluster exists. This must match the zap configuration. - * @param aClusterId The ID of the ModeBase aliased cluster to be instantiated. - * @param aFeature The bitmask value that identifies which features are supported by this instance. - */ - Instance(Delegate * aDelegate, EndpointId aEndpointId, ClusterId aClusterId, uint32_t aFeature); +private: + Delegate * mDelegate; - ~Instance() override; + EndpointId mEndpointId{}; + ClusterId mClusterId{}; + + // Attribute data store + uint8_t mCurrentMode; + DataModel::Nullable mStartUpMode; + DataModel::Nullable mOnMode; + uint32_t mFeature; template void HandleCommand(HandlerContext & handlerContext, FuncT func); + + // CommandHandlerInterface + void InvokeCommand(HandlerContext & ctx) override; + CHIP_ERROR EnumerateAcceptedCommands(const ConcreteClusterPath & cluster, CommandIdCallback callback, void * context) override; + CHIP_ERROR EnumerateGeneratedCommands(const ConcreteClusterPath & cluster, CommandIdCallback callback, void * context) override; + + // AttributeAccessInterface + CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; + CHIP_ERROR Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder) override; + + /** + * Register this ModeBase instance. + */ + void RegisterInstance(); + + /** + * Unregister this ModeBase instance. + */ + void UnregisterInstance(); + + /** + * Internal change-to-mode command handler function. + */ + void HandleChangeToMode(HandlerContext & ctx, const Commands::ChangeToMode::DecodableType & req); + + /** + * Helper function that loads all the persistent attributes from the KVS. These attributes are CurrentMode, + * StartUpMode and OnMode. + */ + void LoadPersistentAttributes(); + + /** + * Helper function that encodes the supported modes. + * @param encoder The encoder to encode the supported modes into. + */ + CHIP_ERROR EncodeSupportedModes(const AttributeValueEncoder::ListEncodeHelper & encoder); }; class Delegate @@ -194,6 +203,9 @@ class Delegate * CopyCharSpanToMutableCharSpan to copy into the MutableCharSpan. * @return Returns a CHIP_NO_ERROR if there was no error and the label was returned successfully. * CHIP_ERROR_PROVIDER_LIST_EXHAUSTED if the modeIndex in beyond the list of available labels. + * + * Note: This is used by the SDK to populate the supported modes attribute. If the contents of this list change, + * the device SHALL call the Instance's ReportSupportedModesChange method to report that this attribute has changed. */ virtual CHIP_ERROR GetModeLabelByIndex(uint8_t modeIndex, MutableCharSpan & label) = 0; @@ -203,6 +215,9 @@ class Delegate * @param value a reference to the uint8_t variable that is to contain the mode value. * @return Returns a CHIP_NO_ERROR if there was no error and the value was returned successfully. * CHIP_ERROR_PROVIDER_LIST_EXHAUSTED if the modeIndex in beyond the list of available values. + * + * Note: This is used by the SDK to populate the supported modes attribute. If the contents of this list change, + * the device SHALL call the Instance's ReportSupportedModesChange method to report that this attribute has changed. */ virtual CHIP_ERROR GetModeValueByIndex(uint8_t modeIndex, uint8_t & value) = 0; @@ -218,6 +233,9 @@ class Delegate * to copy into the buffer. * @return Returns a CHIP_NO_ERROR if there was no error and the mode tags were returned successfully. * CHIP_ERROR_PROVIDER_LIST_EXHAUSTED if the modeIndex in beyond the list of available mode tags. + * + * Note: This is used by the SDK to populate the supported modes attribute. If the contents of this list change, + * the device SHALL call the Instance's ReportSupportedModesChange method to report that this attribute has changed. */ virtual CHIP_ERROR GetModeTagsByIndex(uint8_t modeIndex, DataModel::List & modeTags) = 0; diff --git a/src/app/clusters/operational-state-server/operational-state-delegate.h b/src/app/clusters/operational-state-server/operational-state-cluster-objects.h similarity index 60% rename from src/app/clusters/operational-state-server/operational-state-delegate.h rename to src/app/clusters/operational-state-server/operational-state-cluster-objects.h index 34bbe2d1ba10c0..d7159791964915 100644 --- a/src/app/clusters/operational-state-server/operational-state-delegate.h +++ b/src/app/clusters/operational-state-server/operational-state-cluster-objects.h @@ -188,118 +188,54 @@ struct GenericOperationalPhase }; /** - * A delegate to handle application logic of the Operational State aliased Cluster. - * The delegate API assumes there will be separate delegate objects for each cluster instance. - * (i.e. each separate operational state cluster derivation, on each separate endpoint), - * since the delegate methods are not handed the cluster id or endpoint. + * A class which represents the operational error event of an Operational State cluster derivation instance. */ -class Delegate +class GenericErrorEvent : private app::Clusters::OperationalState::Events::OperationalError::Type { -public: - /** - * Get the current operational state. - * @return The current operational state value - */ - virtual uint8_t GetCurrentOperationalState() = 0; - - /** - * Get the list of supported operational states. - * Fills in the provided GenericOperationalState with the state at index `index` if there is one, - * or returns CHIP_ERROR_NOT_FOUND if the index is out of range for the list of states. - * @param index The index of the state, with 0 representing the first state. - * @param operationalState The GenericOperationalState is filled. - */ - virtual CHIP_ERROR GetOperationalStateAtIndex(size_t index, GenericOperationalState & operationalState) = 0; - - /** - * Get the list of supported operational phases. - * Fills in the provided GenericOperationalPhase with the phase at index `index` if there is one, - * or returns CHIP_ERROR_NOT_FOUND if the index is out of range for the list of phases. - * @param index The index of the phase, with 0 representing the first phase. - * @param operationalPhase The GenericOperationalPhase is filled. - */ - virtual CHIP_ERROR GetOperationalPhaseAtIndex(size_t index, GenericOperationalPhase & operationalPhase) = 0; - - /** - * Get current operational error. - * @param error The GenericOperationalError to fill with the current operational error value - */ - virtual void GetCurrentOperationalError(GenericOperationalError & error) = 0; - - /** - * Get current phase - * @param phase The app::DataModel::Nullable to fill with the current phase value - */ - virtual void GetCurrentPhase(app::DataModel::Nullable & phase) = 0; - - /** - * Get countdown time - * @param time The app::DataModel::Nullable to fill with the coutdown time value - */ - virtual void GetCountdownTime(app::DataModel::Nullable & time) = 0; + using super = app::Clusters::OperationalState::Events::OperationalError::Type; - /** - * Set current operational state. - * @param opState The operational state that should now be the current one. - */ - virtual CHIP_ERROR SetOperationalState(uint8_t opState) = 0; - - /** - * Set operational error. - * @param opErrState The new operational error. - */ - virtual CHIP_ERROR SetOperationalError(const GenericOperationalError & opErrState) = 0; - - /** - * Set operational phase. - * @param phase The operational phase that should now be the current one. - */ - virtual CHIP_ERROR SetPhase(const app::DataModel::Nullable & phase) = 0; - - /** - * Set coutdown time. - * @param time The coutdown time that should now be the current one. - */ - virtual CHIP_ERROR SetCountdownTime(const app::DataModel::Nullable & time) = 0; - - // command callback - /** - * Handle Command Callback in application: Pause - * @param[out] get operational error after callback. - */ - virtual void HandlePauseStateCallback(GenericOperationalError & err) = 0; - - /** - * Handle Command Callback in application: Resume - * @param[out] get operational error after callback. - */ - virtual void HandleResumeStateCallback(GenericOperationalError & err) = 0; +public: + GenericErrorEvent(ClusterId aClusterId, const Structs::ErrorStateStruct::Type & aError) : mClusterId(aClusterId) + { + errorState = aError; + } + using super::GetEventId; + using super::GetPriorityLevel; + ClusterId GetClusterId() const { return mClusterId; } + using super::Encode; + using super::kIsFabricScoped; - /** - * Handle Command Callback in application: Start - * @param[out] get operational error after callback. - */ - virtual void HandleStartStateCallback(GenericOperationalError & err) = 0; +private: + ClusterId mClusterId; +}; - /** - * Handle Command Callback in application: Stop - * @param[out] get operational error after callback. - */ - virtual void HandleStopStateCallback(GenericOperationalError & err) = 0; +/** + * A class which represents the operational completion event of an Operational State cluster derivation instance. + */ +class GenericOperationCompletionEvent : private app::Clusters::OperationalState::Events::OperationCompletion::Type +{ + using super = app::Clusters::OperationalState::Events::OperationCompletion::Type; - Delegate() = default; +public: + GenericOperationCompletionEvent(ClusterId aClusterId, uint8_t aCompletionErrorCode, + const Optional> & aTotalOperationalTime = NullOptional, + const Optional> & aPausedTime = NullOptional) : + mClusterId(aClusterId) + { + completionErrorCode = aCompletionErrorCode; + totalOperationalTime = aTotalOperationalTime; + pausedTime = aPausedTime; + } + using super::GetEventId; + using super::GetPriorityLevel; + ClusterId GetClusterId() const { return mClusterId; } + using super::Encode; + using super::kIsFabricScoped; - virtual ~Delegate() = default; +private: + ClusterId mClusterId; }; -// @brief Instance getter for the delegate for the given operational state alias cluster on the given endpoint. -// The delegate API assumes there will be separate delegate objects for each cluster instance. -// (i.e. each separate operational state cluster derivation, on each separate endpoint) -// @note This API should always be called prior to using the delegate and the return pointer should never be cached. -// This should be implemented by the application. -// @return Default global delegate instance. -Delegate * GetOperationalStateDelegate(EndpointId endpointId, ClusterId clusterId); - } // namespace OperationalState } // namespace Clusters } // namespace app diff --git a/src/app/clusters/operational-state-server/operational-state-server.cpp b/src/app/clusters/operational-state-server/operational-state-server.cpp index 5fa7473f0e1641..f818c63c5cc14f 100644 --- a/src/app/clusters/operational-state-server/operational-state-server.cpp +++ b/src/app/clusters/operational-state-server/operational-state-server.cpp @@ -20,7 +20,6 @@ * @brief Implementation for the Operational State Server Cluster ***************************************************************************/ #include "operational-state-server.h" -#include "operational-state-delegate.h" #include #include #include @@ -45,55 +44,6 @@ using namespace chip::app::Clusters::OperationalState::Attributes; using Status = Protocols::InteractionModel::Status; -/** - * A class which represents the operational error event of an Operational State cluster derivation instance. - */ -class GenericErrorEvent : private app::Clusters::OperationalState::Events::OperationalError::Type -{ - using super = app::Clusters::OperationalState::Events::OperationalError::Type; - -public: - GenericErrorEvent(ClusterId aClusterId, const Structs::ErrorStateStruct::Type & aError) : mClusterId(aClusterId) - { - errorState = aError; - } - using super::GetEventId; - using super::GetPriorityLevel; - ClusterId GetClusterId() const { return mClusterId; } - using super::Encode; - using super::kIsFabricScoped; - -private: - ClusterId mClusterId; -}; - -/** - * A class which represents the operational completion event of an Operational State cluster derivation instance. - */ -class GenericOperationCompletionEvent : private app::Clusters::OperationalState::Events::OperationCompletion::Type -{ - using super = app::Clusters::OperationalState::Events::OperationCompletion::Type; - -public: - GenericOperationCompletionEvent(ClusterId aClusterId, uint8_t aCompletionErrorCode, - const Optional> & aTotalOperationalTime = NullOptional, - const Optional> & aPausedTime = NullOptional) : - mClusterId(aClusterId) - { - completionErrorCode = aCompletionErrorCode; - totalOperationalTime = aTotalOperationalTime; - pausedTime = aPausedTime; - } - using super::GetEventId; - using super::GetPriorityLevel; - ClusterId GetClusterId() const { return mClusterId; } - using super::Encode; - using super::kIsFabricScoped; - -private: - ClusterId mClusterId; -}; - CHIP_ERROR OperationalStateServer::Init() { // Check if the cluster has been selected in zap diff --git a/src/app/clusters/operational-state-server/operational-state-server.h b/src/app/clusters/operational-state-server/operational-state-server.h index 5bb50d3b5df6ef..3e96fa6ca03405 100644 --- a/src/app/clusters/operational-state-server/operational-state-server.h +++ b/src/app/clusters/operational-state-server/operational-state-server.h @@ -17,6 +17,8 @@ */ #pragma once + +#include "operational-state-cluster-objects.h" #include #include #include @@ -45,6 +47,23 @@ class Uncopyable class OperationalStateServer : public CommandHandlerInterface, public AttributeAccessInterface, public Uncopyable { public: + /** + * Creates an operational state cluster instance. The Init() function needs to be called for this instance to be registered and + * called by the interaction model at the appropriate times. + * @param aEndpointId The endpoint on which this cluster exists. This must match the zap configuration. + * @param aClusterId The ID of the operational state aliased cluster to be instantiated. + */ + OperationalStateServer(EndpointId aEndpointId, ClusterId aClusterId) : + CommandHandlerInterface(MakeOptional(aEndpointId), aClusterId), + AttributeAccessInterface(MakeOptional(aEndpointId), aClusterId) + { + + mEndpointId = aEndpointId; + mClusterId = aClusterId; + } + + ~OperationalStateServer() override {} + /** * Init the operational state server. * This function must be called after defining a OperationalStateServer class object. @@ -76,23 +95,6 @@ class OperationalStateServer : public CommandHandlerInterface, public AttributeA const Optional> & aTotalOperationalTime = NullOptional, const Optional> & aPausedTime = NullOptional); - /** - * Creates an operational state cluster instance. The Init() function needs to be called for this instance to be registered and - * called by the interaction model at the appropriate times. - * @param aEndpointId The endpoint on which this cluster exists. This must match the zap configuration. - * @param aClusterId The ID of the operational state aliased cluster to be instantiated. - */ - OperationalStateServer(EndpointId aEndpointId, ClusterId aClusterId) : - CommandHandlerInterface(MakeOptional(aEndpointId), aClusterId), - AttributeAccessInterface(MakeOptional(aEndpointId), aClusterId) - { - - mEndpointId = aEndpointId; - mClusterId = aClusterId; - } - - ~OperationalStateServer() override {} - private: // Inherited from CommandHandlerInterface template @@ -129,6 +131,120 @@ class OperationalStateServer : public CommandHandlerInterface, public AttributeA EndpointId mEndpointId; ClusterId mClusterId; }; + +/** + * A delegate to handle application logic of the Operational State aliased Cluster. + * The delegate API assumes there will be separate delegate objects for each cluster instance. + * (i.e. each separate operational state cluster derivation, on each separate endpoint), + * since the delegate methods are not handed the cluster id or endpoint. + */ +class Delegate +{ +public: + /** + * Get the current operational state. + * @return The current operational state value + */ + virtual uint8_t GetCurrentOperationalState() = 0; + + /** + * Get the list of supported operational states. + * Fills in the provided GenericOperationalState with the state at index `index` if there is one, + * or returns CHIP_ERROR_NOT_FOUND if the index is out of range for the list of states. + * @param index The index of the state, with 0 representing the first state. + * @param operationalState The GenericOperationalState is filled. + */ + virtual CHIP_ERROR GetOperationalStateAtIndex(size_t index, GenericOperationalState & operationalState) = 0; + + /** + * Get the list of supported operational phases. + * Fills in the provided GenericOperationalPhase with the phase at index `index` if there is one, + * or returns CHIP_ERROR_NOT_FOUND if the index is out of range for the list of phases. + * @param index The index of the phase, with 0 representing the first phase. + * @param operationalPhase The GenericOperationalPhase is filled. + */ + virtual CHIP_ERROR GetOperationalPhaseAtIndex(size_t index, GenericOperationalPhase & operationalPhase) = 0; + + /** + * Get current operational error. + * @param error The GenericOperationalError to fill with the current operational error value + */ + virtual void GetCurrentOperationalError(GenericOperationalError & error) = 0; + + /** + * Get current phase + * @param phase The app::DataModel::Nullable to fill with the current phase value + */ + virtual void GetCurrentPhase(app::DataModel::Nullable & phase) = 0; + + /** + * Get countdown time + * @param time The app::DataModel::Nullable to fill with the coutdown time value + */ + virtual void GetCountdownTime(app::DataModel::Nullable & time) = 0; + + /** + * Set current operational state. + * @param opState The operational state that should now be the current one. + */ + virtual CHIP_ERROR SetOperationalState(uint8_t opState) = 0; + + /** + * Set operational error. + * @param opErrState The new operational error. + */ + virtual CHIP_ERROR SetOperationalError(const GenericOperationalError & opErrState) = 0; + + /** + * Set operational phase. + * @param phase The operational phase that should now be the current one. + */ + virtual CHIP_ERROR SetPhase(const app::DataModel::Nullable & phase) = 0; + + /** + * Set coutdown time. + * @param time The coutdown time that should now be the current one. + */ + virtual CHIP_ERROR SetCountdownTime(const app::DataModel::Nullable & time) = 0; + + // command callback + /** + * Handle Command Callback in application: Pause + * @param[out] get operational error after callback. + */ + virtual void HandlePauseStateCallback(GenericOperationalError & err) = 0; + + /** + * Handle Command Callback in application: Resume + * @param[out] get operational error after callback. + */ + virtual void HandleResumeStateCallback(GenericOperationalError & err) = 0; + + /** + * Handle Command Callback in application: Start + * @param[out] get operational error after callback. + */ + virtual void HandleStartStateCallback(GenericOperationalError & err) = 0; + + /** + * Handle Command Callback in application: Stop + * @param[out] get operational error after callback. + */ + virtual void HandleStopStateCallback(GenericOperationalError & err) = 0; + + Delegate() = default; + + virtual ~Delegate() = default; +}; + +// @brief Instance getter for the delegate for the given operational state alias cluster on the given endpoint. +// The delegate API assumes there will be separate delegate objects for each cluster instance. +// (i.e. each separate operational state cluster derivation, on each separate endpoint) +// @note This API should always be called prior to using the delegate and the return pointer should never be cached. +// This should be implemented by the application. +// @return Default global delegate instance. +Delegate * GetOperationalStateDelegate(EndpointId endpointId, ClusterId clusterId); + } // namespace OperationalState } // namespace Clusters } // namespace app diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn index 9078702fb983e9..65ea68ff53eee4 100644 --- a/src/app/tests/BUILD.gn +++ b/src/app/tests/BUILD.gn @@ -109,7 +109,7 @@ source_set("scenes-table-test-srcs") { } source_set("operational-state-test-srcs") { - sources = [ "${chip_root}/src/app/clusters/operational-state-server/operational-state-delegate.h" ] + sources = [ "${chip_root}/src/app/clusters/operational-state-server/operational-state-cluster-objects.h" ] public_deps = [ "${chip_root}/src/app/common:cluster-objects", @@ -143,7 +143,7 @@ chip_test_suite("tests") { "TestInteractionModelEngine.cpp", "TestMessageDef.cpp", "TestNumericAttributeTraits.cpp", - "TestOperationalStateDelegate.cpp", + "TestOperationalStateClusterObjects.cpp", "TestPendingNotificationMap.cpp", "TestPowerSourceCluster.cpp", "TestReadInteraction.cpp", diff --git a/src/app/tests/TestOperationalStateDelegate.cpp b/src/app/tests/TestOperationalStateClusterObjects.cpp similarity index 99% rename from src/app/tests/TestOperationalStateDelegate.cpp rename to src/app/tests/TestOperationalStateClusterObjects.cpp index e82f80bbdaff84..493d02f8ae1935 100644 --- a/src/app/tests/TestOperationalStateDelegate.cpp +++ b/src/app/tests/TestOperationalStateClusterObjects.cpp @@ -15,7 +15,7 @@ * limitations under the License. */ -#include +#include #include #include @@ -579,13 +579,13 @@ int TestTearDown(void * inContext) } // namespace -int TestOperationalStateDelegate() +int TestOperationalStateClusterObjects() { - nlTestSuite theSuite = { "Test Operational State delegate tests", &sTests[0], TestSetup, TestTearDown }; + nlTestSuite theSuite = { "Test Operational State Cluster Objects tests", &sTests[0], TestSetup, TestTearDown }; // Run test suit againt one context. nlTestRunner(&theSuite, nullptr); return nlTestRunnerStats(&theSuite); } -CHIP_REGISTER_TEST_SUITE(TestOperationalStateDelegate) +CHIP_REGISTER_TEST_SUITE(TestOperationalStateClusterObjects)