From 04e5fe317b7163997862e3084c8ac41e5c9da002 Mon Sep 17 00:00:00 2001 From: William Date: Tue, 15 Aug 2023 15:12:27 +0100 Subject: [PATCH] Update operational state server and related examples (#28577) * 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. * Refactored the OperationalState server code to match with other derived cluters. Updated the examples to work with this change. * Explicitly initialised the edpoint ID in the operational state examples to improve readability. * OpState: Made OpState getters return const. * Restyled by clang-format * Moved the responsibility of storing and managing the countdown time attribute to the delegate. * Applied minor review changes. * OpState: Made the improved the relationship between the Delegate and Instance classes. * OpState: Removed the ability to set the OperationalState to the Error state and the ability to set the OperationalError. These can only be set via the ErrorDetected event. * OpState: Fixed GetCurrentOperationalError * Restyled by clang-format * Added checks to the phase and operational state setters to ensure that the value given is valid. * Added a safe method to check if two GenericOperationalError objects are equal. * GetInstance returns a const pointer. Co-authored-by: Boris Zbarsky * Restyled by clang-format --------- Co-authored-by: Restyled.io Co-authored-by: Boris Zbarsky --- .../include/operational-state-delegate-impl.h | 137 ++++--- .../src/operational-state-delegate-impl.cpp | 173 +++++--- .../src/operational-state-delegates.cpp | 161 -------- examples/all-clusters-app/linux/BUILD.gn | 1 - .../all-clusters-app/linux/main-common.cpp | 14 +- examples/dishwasher-app/asr/BUILD.gn | 1 - examples/dishwasher-app/asr/src/AppTask.cpp | 3 - .../include/operational-state-delegate-impl.h | 82 +--- .../include/operational-state-delegates.h | 22 - .../src/operational-state-delegate-impl.cpp | 132 +++--- .../src/operational-state-delegates.cpp | 133 ------ examples/dishwasher-app/linux/BUILD.gn | 1 - examples/dishwasher-app/linux/main.cpp | 15 +- .../operational-state-cluster-objects.h | 29 ++ .../operational-state-server.cpp | 377 +++++++++++------- .../operational-state-server.h | 219 +++++----- 16 files changed, 665 insertions(+), 835 deletions(-) delete mode 100644 examples/all-clusters-app/all-clusters-common/src/operational-state-delegates.cpp delete mode 100644 examples/dishwasher-app/dishwasher-common/include/operational-state-delegates.h delete mode 100644 examples/dishwasher-app/dishwasher-common/src/operational-state-delegates.cpp 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 939213d296ad53..fd6df4b90359f1 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 @@ -26,79 +26,39 @@ namespace chip { namespace app { namespace Clusters { + namespace OperationalState { // This is an application level delegate to handle operational state commands according to the specific business logic. -class OperationalStateDelegate : public Delegate +class GenericOperationalStateDelegateImpl : public Delegate { - public: /** - * Get the current operational state. - * @return The current operational state value + * Get the countdown time. This attribute is not used in this application. + * @return The current countdown time. */ - uint8_t GetCurrentOperationalState() override; + app::DataModel::Nullable GetCountdownTime() override { return {}; }; /** - * 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. + * Note: This is used by the SDK to populate the operational state list attribute. If the contents of this list changes, + * the device SHALL call the Instance's ReportOperationalStateListChange method to report that this attribute has changed. * @param index The index of the state, with 0 representing the first state. * @param operationalState The GenericOperationalState is filled. */ CHIP_ERROR GetOperationalStateAtIndex(size_t index, GenericOperationalState & operationalState) override; /** - * 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. + * Note: This is used by the SDK to populate the phase list attribute. If the contents of this list changes, the + * device SHALL call the Instance's ReportPhaseListChange method to report that this attribute has changed. * @param index The index of the phase, with 0 representing the first phase. * @param operationalPhase The GenericOperationalPhase is filled. */ CHIP_ERROR GetOperationalPhaseAtIndex(size_t index, GenericOperationalPhase & operationalPhase) override; - /** - * Get current operational error. - * @param error The GenericOperationalError to fill with the current operational error value - */ - void GetCurrentOperationalError(GenericOperationalError & error) override; - - /** - * Get current phase - * @param phase The app::DataModel::Nullable to fill with the current phase value - */ - void GetCurrentPhase(app::DataModel::Nullable & phase) override; - - /** - * Get countdown time - * @param time The app::DataModel::Nullable to fill with the coutdown time value - */ - void GetCountdownTime(app::DataModel::Nullable & time) override; - - /** - * Set operational error. - * @param opErrState The new operational error. - */ - CHIP_ERROR SetOperationalError(const GenericOperationalError & opErrState) override; - - /** - * Set current operational state. - * @param opState The operational state that should now be the current one. - */ - CHIP_ERROR SetOperationalState(uint8_t opState) override; - - /** - * Set operational phase. - * @param phase The operational phase that should now be the current one. - */ - CHIP_ERROR SetPhase(const app::DataModel::Nullable & phase) override; - - /** - * Set coutdown time. - * @param time The coutdown time that should now be the current one. - */ - CHIP_ERROR SetCountdownTime(const app::DataModel::Nullable & time) override; - // command callback /** * Handle Command Callback in application: Pause @@ -124,27 +84,74 @@ class OperationalStateDelegate : public Delegate */ void HandleStopStateCallback(GenericOperationalError & err) override; - OperationalStateDelegate(uint8_t aOperationalState, GenericOperationalError aOperationalError, - Span aOperationalStateList, - Span aOperationalPhaseList, - app::DataModel::Nullable aPhase = DataModel::Nullable(), - app::DataModel::Nullable aCountdownTime = DataModel::Nullable()) : - mOperationalState(aOperationalState), - mOperationalError(aOperationalError), mOperationalStateList(aOperationalStateList), - mOperationalPhaseList(aOperationalPhaseList), mOperationalPhase(aPhase), mCountdownTime(aCountdownTime) - {} - ~OperationalStateDelegate() = default; +protected: + Span mOperationalStateList; + Span mOperationalPhaseList; +}; +// This is an application level delegate to handle operational state commands according to the specific business logic. +class OperationalStateDelegate : public GenericOperationalStateDelegateImpl +{ private: - uint8_t mOperationalState; - GenericOperationalError mOperationalError; - app::DataModel::List mOperationalStateList; - Span mOperationalPhaseList; - app::DataModel::Nullable mOperationalPhase; - app::DataModel::Nullable mCountdownTime; + const GenericOperationalState opStateList[4] = { + GenericOperationalState(to_underlying(OperationalStateEnum::kStopped)), + GenericOperationalState(to_underlying(OperationalStateEnum::kRunning)), + GenericOperationalState(to_underlying(OperationalStateEnum::kPaused)), + GenericOperationalState(to_underlying(OperationalStateEnum::kError)), + }; + + const GenericOperationalPhase opPhaseList[1] = { + // Phase List is null + GenericOperationalPhase(DataModel::Nullable()), + }; + +public: + OperationalStateDelegate() + { + GenericOperationalStateDelegateImpl::mOperationalStateList = Span(opStateList); + GenericOperationalStateDelegateImpl::mOperationalPhaseList = Span(opPhaseList); + } }; +void Shutdown(); + } // namespace OperationalState + +namespace RvcOperationalState { + +// This is an application level delegate to handle operational state commands according to the specific business logic. +class RvcOperationalStateDelegate : public OperationalState::GenericOperationalStateDelegateImpl +{ +private: + const OperationalState::GenericOperationalState rvcOpStateList[7] = { + OperationalState::GenericOperationalState(to_underlying(OperationalState::OperationalStateEnum::kStopped)), + OperationalState::GenericOperationalState(to_underlying(OperationalState::OperationalStateEnum::kRunning)), + OperationalState::GenericOperationalState(to_underlying(OperationalState::OperationalStateEnum::kPaused)), + OperationalState::GenericOperationalState(to_underlying(OperationalState::OperationalStateEnum::kError)), + OperationalState::GenericOperationalState( + to_underlying(Clusters::RvcOperationalState::OperationalStateEnum::kSeekingCharger)), + OperationalState::GenericOperationalState(to_underlying(Clusters::RvcOperationalState::OperationalStateEnum::kCharging)), + OperationalState::GenericOperationalState(to_underlying(Clusters::RvcOperationalState::OperationalStateEnum::kDocked)), + }; + + const OperationalState::GenericOperationalPhase rvcOpPhaseList[1] = { + // Phase List is null + OperationalState::GenericOperationalPhase(DataModel::Nullable()), + }; + +public: + RvcOperationalStateDelegate() + { + GenericOperationalStateDelegateImpl::mOperationalStateList = + Span(rvcOpStateList); + GenericOperationalStateDelegateImpl::mOperationalPhaseList = + Span(rvcOpPhaseList); + } +}; + +void Shutdown(); + +} // namespace RvcOperationalState } // namespace Clusters } // namespace app } // namespace chip diff --git a/examples/all-clusters-app/all-clusters-common/src/operational-state-delegate-impl.cpp b/examples/all-clusters-app/all-clusters-common/src/operational-state-delegate-impl.cpp index 7501e386db00b7..f8a465e831f6eb 100644 --- a/examples/all-clusters-app/all-clusters-common/src/operational-state-delegate-impl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/operational-state-delegate-impl.cpp @@ -17,106 +17,151 @@ */ #include -namespace chip { -namespace app { -namespace Clusters { -namespace OperationalState { +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::OperationalState; +using namespace chip::app::Clusters::RvcOperationalState; -using chip::Protocols::InteractionModel::Status; - -CHIP_ERROR OperationalStateDelegate::SetOperationalState(uint8_t opState) +CHIP_ERROR GenericOperationalStateDelegateImpl::GetOperationalStateAtIndex(size_t index, GenericOperationalState & operationalState) { - mOperationalState = opState; + if (index >= mOperationalStateList.size()) + { + return CHIP_ERROR_NOT_FOUND; + } + operationalState = mOperationalStateList[index]; return CHIP_NO_ERROR; } -CHIP_ERROR OperationalStateDelegate::SetPhase(const app::DataModel::Nullable & phase) +CHIP_ERROR GenericOperationalStateDelegateImpl::GetOperationalPhaseAtIndex(size_t index, GenericOperationalPhase & operationalPhase) { - mOperationalPhase = phase; + if (index >= mOperationalPhaseList.size()) + { + return CHIP_ERROR_NOT_FOUND; + } + operationalPhase = mOperationalPhaseList[index]; return CHIP_NO_ERROR; } -CHIP_ERROR OperationalStateDelegate::SetCountdownTime(const app::DataModel::Nullable & time) +void GenericOperationalStateDelegateImpl::HandlePauseStateCallback(GenericOperationalError & err) { - mCountdownTime = time; - return CHIP_NO_ERROR; + // placeholder implementation + auto error = GetInstance()->SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kPaused)); + if (error == CHIP_NO_ERROR) + { + err.Set(to_underlying(ErrorStateEnum::kNoError)); + } + else + { + err.Set(to_underlying(ErrorStateEnum::kUnableToCompleteOperation)); + } } -uint8_t OperationalStateDelegate::GetCurrentOperationalState() +void GenericOperationalStateDelegateImpl::HandleResumeStateCallback(GenericOperationalError & err) { - return mOperationalState; + // placeholder implementation + auto error = GetInstance()->SetOperationalState(to_underlying(OperationalStateEnum::kRunning)); + if (error == CHIP_NO_ERROR) + { + err.Set(to_underlying(ErrorStateEnum::kNoError)); + } + else + { + err.Set(to_underlying(ErrorStateEnum::kUnableToCompleteOperation)); + } } -CHIP_ERROR OperationalStateDelegate::SetOperationalError(const GenericOperationalError & opErrState) +void GenericOperationalStateDelegateImpl::HandleStartStateCallback(GenericOperationalError & err) { - mOperationalError = opErrState; - return CHIP_NO_ERROR; + // placeholder implementation + auto error = GetInstance()->SetOperationalState(to_underlying(OperationalStateEnum::kRunning)); + if (error == CHIP_NO_ERROR) + { + err.Set(to_underlying(ErrorStateEnum::kNoError)); + } + else + { + err.Set(to_underlying(ErrorStateEnum::kUnableToCompleteOperation)); + } } -void OperationalStateDelegate::GetCurrentOperationalError(GenericOperationalError & error) +void GenericOperationalStateDelegateImpl::HandleStopStateCallback(GenericOperationalError & err) { - error = mOperationalError; + // placeholder implementation + auto error = GetInstance()->SetOperationalState(to_underlying(OperationalStateEnum::kStopped)); + if (error == CHIP_NO_ERROR) + { + err.Set(to_underlying(ErrorStateEnum::kNoError)); + } + else + { + err.Set(to_underlying(ErrorStateEnum::kUnableToCompleteOperation)); + } } -void OperationalStateDelegate::GetCurrentPhase(app::DataModel::Nullable & phase) -{ - phase = mOperationalPhase; -} +// Init Operational State cluster -void OperationalStateDelegate::GetCountdownTime(app::DataModel::Nullable & time) -{ - time = mCountdownTime; -} +static OperationalState::Instance * gOperationalStateInstance = nullptr; +static OperationalStateDelegate * gOperationalStateDelegate = nullptr; -CHIP_ERROR OperationalStateDelegate::GetOperationalStateAtIndex(size_t index, GenericOperationalState & operationalState) +void OperationalState::Shutdown() { - if (index > mOperationalStateList.size() - 1) + if (gOperationalStateInstance != nullptr) { - return CHIP_ERROR_NOT_FOUND; + delete gOperationalStateInstance; + gOperationalStateInstance = nullptr; } - operationalState = mOperationalStateList[index]; - return CHIP_NO_ERROR; -} - -CHIP_ERROR OperationalStateDelegate::GetOperationalPhaseAtIndex(size_t index, GenericOperationalPhase & operationalPhase) -{ - if (index > mOperationalPhaseList.size() - 1) + if (gOperationalStateDelegate != nullptr) { - return CHIP_ERROR_NOT_FOUND; + delete gOperationalStateDelegate; + gOperationalStateDelegate = nullptr; } - operationalPhase = mOperationalPhaseList[index]; - return CHIP_NO_ERROR; } -void OperationalStateDelegate::HandlePauseStateCallback(GenericOperationalError & err) +void emberAfOperationalStateClusterInitCallback(chip::EndpointId endpointId) { - // placeholder implementation - mOperationalState = to_underlying(OperationalStateEnum::kPaused); - err.Set(to_underlying(ErrorStateEnum::kNoError)); -} + VerifyOrDie(endpointId == 1); // this cluster is only enabled for endpoint 1. + VerifyOrDie(gOperationalStateInstance == nullptr && gOperationalStateDelegate == nullptr); -void OperationalStateDelegate::HandleResumeStateCallback(GenericOperationalError & err) -{ - // placeholder implementation - mOperationalState = to_underlying(OperationalStateEnum::kRunning); - err.Set(to_underlying(ErrorStateEnum::kNoError)); + gOperationalStateDelegate = new OperationalStateDelegate; + EndpointId operationalStateEndpoint = 0x01; + gOperationalStateInstance = new Instance(gOperationalStateDelegate, operationalStateEndpoint, Clusters::OperationalState::Id); + + gOperationalStateInstance->SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kStopped)); + + gOperationalStateInstance->Init(); } -void OperationalStateDelegate::HandleStartStateCallback(GenericOperationalError & err) +// Init RVC Operational State cluster + +static OperationalState::Instance * gRvcOperationalStateInstance = nullptr; +static RvcOperationalStateDelegate * gRvcOperationalStateDelegate = nullptr; + +void RvcOperationalState::Shutdown() { - // placeholder implementation - mOperationalState = to_underlying(OperationalStateEnum::kRunning); - err.Set(to_underlying(ErrorStateEnum::kNoError)); + if (gRvcOperationalStateInstance != nullptr) + { + delete gRvcOperationalStateInstance; + gRvcOperationalStateInstance = nullptr; + } + if (gRvcOperationalStateDelegate != nullptr) + { + delete gRvcOperationalStateDelegate; + gRvcOperationalStateDelegate = nullptr; + } } -void OperationalStateDelegate::HandleStopStateCallback(GenericOperationalError & err) +void emberAfRvcOperationalStateClusterInitCallback(chip::EndpointId endpointId) { - // placeholder implementation - mOperationalState = to_underlying(OperationalStateEnum::kStopped); - err.Set(to_underlying(ErrorStateEnum::kNoError)); -} + VerifyOrDie(endpointId == 1); // this cluster is only enabled for endpoint 1. + VerifyOrDie(gRvcOperationalStateInstance == nullptr && gRvcOperationalStateDelegate == nullptr); -} // namespace OperationalState -} // namespace Clusters -} // namespace app -} // namespace chip + gRvcOperationalStateDelegate = new RvcOperationalStateDelegate; + EndpointId operationalStateEndpoint = 0x01; + gRvcOperationalStateInstance = + new Instance(gRvcOperationalStateDelegate, operationalStateEndpoint, Clusters::RvcOperationalState::Id); + + gRvcOperationalStateInstance->SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kStopped)); + + gRvcOperationalStateInstance->Init(); +} diff --git a/examples/all-clusters-app/all-clusters-common/src/operational-state-delegates.cpp b/examples/all-clusters-app/all-clusters-common/src/operational-state-delegates.cpp deleted file mode 100644 index 2b8bb08be608ca..00000000000000 --- a/examples/all-clusters-app/all-clusters-common/src/operational-state-delegates.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/* - * - * Copyright (c) 2023 Project CHIP Authors - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include -#include -#include -#include - -namespace chip { -namespace app { -namespace Clusters { -namespace OperationalState { - -/** - * class to present Enquiry Table of Delegates - */ -struct DelegatesEnquiryTable -{ - /** - * Endpoint Id - */ - EndpointId mEndpointId; - /** - * Cluster Id - */ - ClusterId mClusterId; - /** - * The delegate for the cluster instance given by mEndpointId and mClusterId. - */ - Delegate * pItem; -}; - -/* - * An example to present device's endpointId - */ -constexpr EndpointId kDemoEndpointId = 1; - -/** - * Operational State List - * Note: User Define - */ -static const GenericOperationalState opStateList[] = { - GenericOperationalState(to_underlying(OperationalStateEnum::kStopped)), - GenericOperationalState(to_underlying(OperationalStateEnum::kRunning)), - GenericOperationalState(to_underlying(OperationalStateEnum::kPaused)), - GenericOperationalState(to_underlying(OperationalStateEnum::kError)), -}; - -/** - * Phase List - * Note: User Define - */ -static const GenericOperationalPhase opPhaseList[] = { - /** - * Phase List is null - */ - GenericOperationalPhase(DataModel::Nullable()), -}; - -/** - * Operational State Delegate - * Note: User Define - */ -static OperationalStateDelegate opStateDelegate(to_underlying(OperationalStateEnum::kStopped), - GenericOperationalError(to_underlying(ErrorStateEnum::kNoError)), - Span(opStateList), - Span(opPhaseList)); - -/** - * RVC Operational State List - * Note: User Define - */ -static const GenericOperationalState rvcOpStateList[] = { - GenericOperationalState(to_underlying(OperationalStateEnum::kStopped)), - GenericOperationalState(to_underlying(OperationalStateEnum::kRunning)), - GenericOperationalState(to_underlying(OperationalStateEnum::kPaused)), - GenericOperationalState(to_underlying(OperationalStateEnum::kError)), - GenericOperationalState(to_underlying(Clusters::RvcOperationalState::OperationalStateEnum::kSeekingCharger)), - GenericOperationalState(to_underlying(Clusters::RvcOperationalState::OperationalStateEnum::kCharging)), - GenericOperationalState(to_underlying(Clusters::RvcOperationalState::OperationalStateEnum::kDocked)), -}; - -/** - * RVC Operational State Delegate - * Note: User Define - */ -static OperationalStateDelegate rvcOpStateDelegate(to_underlying(OperationalStateEnum::kStopped), - GenericOperationalError(to_underlying(ErrorStateEnum::kNoError)), - Span(rvcOpStateList), - Span(opPhaseList)); - -/** - * Enquiry Table of Operational State Cluster and alias Cluter Delegate corresponding to endpointId and clusterId - * Note: User Define - */ -constexpr DelegatesEnquiryTable kDelegatesEnquiryTable[] = { - // EndpointId, ClusterId, Delegate - { Clusters::OperationalState::kDemoEndpointId, Clusters::OperationalState::Id, &opStateDelegate }, - { Clusters::OperationalState::kDemoEndpointId, Clusters::RvcOperationalState::Id, &rvcOpStateDelegate }, -}; - -/** - * Get the pointer of target delegate for target endpoint and cluster - * @param[in] aEndpiontId The endpointId - * @param[in] aClusterID The clusterId - * @return the pointer of target delegate - */ -Delegate * getGenericDelegateTable(EndpointId aEndpointId, ClusterId aClusterId) -{ - for (size_t i = 0; i < ArraySize(kDelegatesEnquiryTable); ++i) - { - if (kDelegatesEnquiryTable[i].mEndpointId == aEndpointId && kDelegatesEnquiryTable[i].mClusterId == aClusterId) - { - return kDelegatesEnquiryTable[i].pItem; - } - } - return nullptr; -} - -// @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. -// @return Default global delegate instance. -Delegate * GetOperationalStateDelegate(EndpointId endpointId, ClusterId clusterId) -{ - return getGenericDelegateTable(endpointId, clusterId); -} - -} // namespace OperationalState -} // namespace Clusters -} // namespace app -} // namespace chip - -void MatterOperationalStateServerInit() -{ - using namespace chip::app; - static Clusters::OperationalState::OperationalStateServer operationalstateServer(Clusters::OperationalState::kDemoEndpointId, - Clusters::OperationalState::Id); - operationalstateServer.Init(); - - static Clusters::OperationalState::OperationalStateServer rvcOperationalstateServer(Clusters::OperationalState::kDemoEndpointId, - Clusters::RvcOperationalState::Id); - rvcOperationalstateServer.Init(); -} diff --git a/examples/all-clusters-app/linux/BUILD.gn b/examples/all-clusters-app/linux/BUILD.gn index 766b482e5a4600..457f911debc9b0 100644 --- a/examples/all-clusters-app/linux/BUILD.gn +++ b/examples/all-clusters-app/linux/BUILD.gn @@ -29,7 +29,6 @@ source_set("chip-all-clusters-common") { "${chip_root}/examples/all-clusters-app/all-clusters-common/src/laundry-washer-controls-delegate-impl.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/laundry-washer-mode.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/operational-state-delegate-impl.cpp", - "${chip_root}/examples/all-clusters-app/all-clusters-common/src/operational-state-delegates.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/resource-monitoring-instances.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/rvc-modes.cpp", "${chip_root}/examples/all-clusters-app/all-clusters-common/src/smco-stub.cpp", diff --git a/examples/all-clusters-app/linux/main-common.cpp b/examples/all-clusters-app/linux/main-common.cpp index 71e86397ce7120..3f0c5071b13de1 100644 --- a/examples/all-clusters-app/linux/main-common.cpp +++ b/examples/all-clusters-app/linux/main-common.cpp @@ -22,6 +22,7 @@ #include "include/tv-callbacks.h" #include "laundry-washer-controls-delegate-impl.h" #include "laundry-washer-mode.h" +#include "operational-state-delegate-impl.h" #include "rvc-modes.h" #include "tcc-mode.h" #include @@ -56,12 +57,9 @@ NamedPipeCommands sChipNamedPipeCommands; AllClustersCommandDelegate sAllClustersCommandDelegate; Clusters::WindowCovering::WindowCoveringManager sWindowCoveringManager; -app::Clusters::TemperatureControl::AppSupportedTemperatureLevelsDelegate sAppSupportedTemperatureLevelsDelegate; +Clusters::TemperatureControl::AppSupportedTemperatureLevelsDelegate sAppSupportedTemperatureLevelsDelegate; } // namespace -#ifdef EMBER_AF_PLUGIN_OPERATIONAL_STATE_SERVER -extern void MatterOperationalStateServerInit(); -#endif #ifdef EMBER_AF_PLUGIN_DISHWASHER_ALARM_SERVER extern void MatterDishwasherAlarmServerInit(); #endif @@ -182,14 +180,11 @@ void ApplicationInit() gExampleDeviceInstanceInfoProvider.Init(defaultProvider); SetDeviceInstanceInfoProvider(&gExampleDeviceInstanceInfoProvider); } -#ifdef EMBER_AF_PLUGIN_OPERATIONAL_STATE_SERVER - MatterOperationalStateServerInit(); -#endif #ifdef EMBER_AF_PLUGIN_DISHWASHER_ALARM_SERVER MatterDishwasherAlarmServerInit(); #endif - app::Clusters::TemperatureControl::SetInstance(&sAppSupportedTemperatureLevelsDelegate); + Clusters::TemperatureControl::SetInstance(&sAppSupportedTemperatureLevelsDelegate); } void ApplicationShutdown() @@ -201,6 +196,9 @@ void ApplicationShutdown() Clusters::RvcRunMode::Shutdown(); Clusters::RefrigeratorAndTemperatureControlledCabinetMode::Shutdown(); + Clusters::OperationalState::Shutdown(); + Clusters::RvcOperationalState::Shutdown(); + if (sChipNamedPipeCommands.Stop() != CHIP_NO_ERROR) { ChipLogError(NotSpecified, "Failed to stop CHIP NamedPipeCommands"); diff --git a/examples/dishwasher-app/asr/BUILD.gn b/examples/dishwasher-app/asr/BUILD.gn index 2b0cc1ef7507df..5a288f7a46ebdb 100755 --- a/examples/dishwasher-app/asr/BUILD.gn +++ b/examples/dishwasher-app/asr/BUILD.gn @@ -72,7 +72,6 @@ asr_executable("dishwasher_app") { sources = [ "${chip_root}/examples/dishwasher-app/dishwasher-common/src/operational-state-delegate-impl.cpp", - "${chip_root}/examples/dishwasher-app/dishwasher-common/src/operational-state-delegates.cpp", "${examples_plat_dir}/CHIPDeviceManager.cpp", "${examples_plat_dir}/LEDWidget.cpp", "${examples_plat_dir}/init_Matter.cpp", diff --git a/examples/dishwasher-app/asr/src/AppTask.cpp b/examples/dishwasher-app/asr/src/AppTask.cpp index d2d8d0cb34fa64..55acdfb95d6aea 100644 --- a/examples/dishwasher-app/asr/src/AppTask.cpp +++ b/examples/dishwasher-app/asr/src/AppTask.cpp @@ -20,7 +20,6 @@ #include "AppConfig.h" #include "CHIPDeviceManager.h" #include "DeviceCallbacks.h" -#include "operational-state-delegates.h" #include "qrcodegen.h" #include #include @@ -105,8 +104,6 @@ void AppTask::AppTaskMain(void * pvParameter) PrintOnboardingCodes(RendezvousInformationFlag(RendezvousInformationFlag::kOnNetwork)); #endif /* CONFIG_NETWORK_LAYER_BLE */ - /*Operational State Server Init*/ - MatterOperationalStateServerInit(); /* Delete task */ lega_rtos_delete_thread(NULL); } 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 939213d296ad53..5133fb75c93b00 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 @@ -34,10 +34,10 @@ class OperationalStateDelegate : public Delegate public: /** - * Get the current operational state. - * @return The current operational state value + * Get the countdown time. This attribute is not used in this application. + * @return The current countdown time. */ - uint8_t GetCurrentOperationalState() override; + app::DataModel::Nullable GetCountdownTime() override { return {}; }; /** * Get the list of supported operational states. @@ -57,48 +57,6 @@ class OperationalStateDelegate : public Delegate */ CHIP_ERROR GetOperationalPhaseAtIndex(size_t index, GenericOperationalPhase & operationalPhase) override; - /** - * Get current operational error. - * @param error The GenericOperationalError to fill with the current operational error value - */ - void GetCurrentOperationalError(GenericOperationalError & error) override; - - /** - * Get current phase - * @param phase The app::DataModel::Nullable to fill with the current phase value - */ - void GetCurrentPhase(app::DataModel::Nullable & phase) override; - - /** - * Get countdown time - * @param time The app::DataModel::Nullable to fill with the coutdown time value - */ - void GetCountdownTime(app::DataModel::Nullable & time) override; - - /** - * Set operational error. - * @param opErrState The new operational error. - */ - CHIP_ERROR SetOperationalError(const GenericOperationalError & opErrState) override; - - /** - * Set current operational state. - * @param opState The operational state that should now be the current one. - */ - CHIP_ERROR SetOperationalState(uint8_t opState) override; - - /** - * Set operational phase. - * @param phase The operational phase that should now be the current one. - */ - CHIP_ERROR SetPhase(const app::DataModel::Nullable & phase) override; - - /** - * Set coutdown time. - * @param time The coutdown time that should now be the current one. - */ - CHIP_ERROR SetCountdownTime(const app::DataModel::Nullable & time) override; - // command callback /** * Handle Command Callback in application: Pause @@ -124,26 +82,26 @@ class OperationalStateDelegate : public Delegate */ void HandleStopStateCallback(GenericOperationalError & err) override; - OperationalStateDelegate(uint8_t aOperationalState, GenericOperationalError aOperationalError, - Span aOperationalStateList, - Span aOperationalPhaseList, - app::DataModel::Nullable aPhase = DataModel::Nullable(), - app::DataModel::Nullable aCountdownTime = DataModel::Nullable()) : - mOperationalState(aOperationalState), - mOperationalError(aOperationalError), mOperationalStateList(aOperationalStateList), - mOperationalPhaseList(aOperationalPhaseList), mOperationalPhase(aPhase), mCountdownTime(aCountdownTime) - {} - ~OperationalStateDelegate() = default; - private: - uint8_t mOperationalState; - GenericOperationalError mOperationalError; - app::DataModel::List mOperationalStateList; - Span mOperationalPhaseList; - app::DataModel::Nullable mOperationalPhase; - app::DataModel::Nullable mCountdownTime; + const GenericOperationalState rvcOpStateList[4] = { + GenericOperationalState(to_underlying(OperationalStateEnum::kStopped)), + GenericOperationalState(to_underlying(OperationalStateEnum::kRunning)), + GenericOperationalState(to_underlying(OperationalStateEnum::kPaused)), + GenericOperationalState(to_underlying(OperationalStateEnum::kError)), + }; + + app::DataModel::List mOperationalStateList = Span(rvcOpStateList); + + const GenericOperationalPhase opPhaseList[1] = { + // Phase List is null + GenericOperationalPhase(DataModel::Nullable()), + }; + + Span mOperationalPhaseList = Span(opPhaseList); }; +void Shutdown(); + } // namespace OperationalState } // namespace Clusters } // namespace app diff --git a/examples/dishwasher-app/dishwasher-common/include/operational-state-delegates.h b/examples/dishwasher-app/dishwasher-common/include/operational-state-delegates.h deleted file mode 100644 index 5653da44fb15a5..00000000000000 --- a/examples/dishwasher-app/dishwasher-common/include/operational-state-delegates.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * - * Copyright (c) 2023 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 - -// Operational State Server Init in application -void MatterOperationalStateServerInit(); diff --git a/examples/dishwasher-app/dishwasher-common/src/operational-state-delegate-impl.cpp b/examples/dishwasher-app/dishwasher-common/src/operational-state-delegate-impl.cpp index 7501e386db00b7..0b57ecec6fb62a 100644 --- a/examples/dishwasher-app/dishwasher-common/src/operational-state-delegate-impl.cpp +++ b/examples/dishwasher-app/dishwasher-common/src/operational-state-delegate-impl.cpp @@ -17,56 +17,10 @@ */ #include -namespace chip { -namespace app { -namespace Clusters { -namespace OperationalState { - -using chip::Protocols::InteractionModel::Status; - -CHIP_ERROR OperationalStateDelegate::SetOperationalState(uint8_t opState) -{ - mOperationalState = opState; - return CHIP_NO_ERROR; -} - -CHIP_ERROR OperationalStateDelegate::SetPhase(const app::DataModel::Nullable & phase) -{ - mOperationalPhase = phase; - return CHIP_NO_ERROR; -} - -CHIP_ERROR OperationalStateDelegate::SetCountdownTime(const app::DataModel::Nullable & time) -{ - mCountdownTime = time; - return CHIP_NO_ERROR; -} - -uint8_t OperationalStateDelegate::GetCurrentOperationalState() -{ - return mOperationalState; -} - -CHIP_ERROR OperationalStateDelegate::SetOperationalError(const GenericOperationalError & opErrState) -{ - mOperationalError = opErrState; - return CHIP_NO_ERROR; -} - -void OperationalStateDelegate::GetCurrentOperationalError(GenericOperationalError & error) -{ - error = mOperationalError; -} - -void OperationalStateDelegate::GetCurrentPhase(app::DataModel::Nullable & phase) -{ - phase = mOperationalPhase; -} - -void OperationalStateDelegate::GetCountdownTime(app::DataModel::Nullable & time) -{ - time = mCountdownTime; -} +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::OperationalState; CHIP_ERROR OperationalStateDelegate::GetOperationalStateAtIndex(size_t index, GenericOperationalState & operationalState) { @@ -91,32 +45,86 @@ CHIP_ERROR OperationalStateDelegate::GetOperationalPhaseAtIndex(size_t index, Ge void OperationalStateDelegate::HandlePauseStateCallback(GenericOperationalError & err) { // placeholder implementation - mOperationalState = to_underlying(OperationalStateEnum::kPaused); - err.Set(to_underlying(ErrorStateEnum::kNoError)); + auto error = GetInstance()->SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kPaused)); + if (error == CHIP_NO_ERROR) + { + err.Set(to_underlying(ErrorStateEnum::kNoError)); + } + else + { + err.Set(to_underlying(ErrorStateEnum::kUnableToCompleteOperation)); + } } void OperationalStateDelegate::HandleResumeStateCallback(GenericOperationalError & err) { // placeholder implementation - mOperationalState = to_underlying(OperationalStateEnum::kRunning); - err.Set(to_underlying(ErrorStateEnum::kNoError)); + auto error = GetInstance()->SetOperationalState(to_underlying(OperationalStateEnum::kRunning)); + if (error == CHIP_NO_ERROR) + { + err.Set(to_underlying(ErrorStateEnum::kNoError)); + } + else + { + err.Set(to_underlying(ErrorStateEnum::kUnableToCompleteOperation)); + } } void OperationalStateDelegate::HandleStartStateCallback(GenericOperationalError & err) { // placeholder implementation - mOperationalState = to_underlying(OperationalStateEnum::kRunning); - err.Set(to_underlying(ErrorStateEnum::kNoError)); + auto error = GetInstance()->SetOperationalState(to_underlying(OperationalStateEnum::kRunning)); + if (error == CHIP_NO_ERROR) + { + err.Set(to_underlying(ErrorStateEnum::kNoError)); + } + else + { + err.Set(to_underlying(ErrorStateEnum::kUnableToCompleteOperation)); + } } void OperationalStateDelegate::HandleStopStateCallback(GenericOperationalError & err) { // placeholder implementation - mOperationalState = to_underlying(OperationalStateEnum::kStopped); - err.Set(to_underlying(ErrorStateEnum::kNoError)); + auto error = GetInstance()->SetOperationalState(to_underlying(OperationalStateEnum::kStopped)); + if (error == CHIP_NO_ERROR) + { + err.Set(to_underlying(ErrorStateEnum::kNoError)); + } + else + { + err.Set(to_underlying(ErrorStateEnum::kUnableToCompleteOperation)); + } +} + +static OperationalState::Instance * gOperationalStateInstance = nullptr; +static OperationalStateDelegate * gOperationalStateDelegate = nullptr; + +void OperationalState::Shutdown() +{ + if (gOperationalStateInstance != nullptr) + { + delete gOperationalStateInstance; + gOperationalStateInstance = nullptr; + } + if (gOperationalStateDelegate != nullptr) + { + delete gOperationalStateDelegate; + gOperationalStateDelegate = nullptr; + } } -} // namespace OperationalState -} // namespace Clusters -} // namespace app -} // namespace chip +void emberAfOperationalStateClusterInitCallback(chip::EndpointId endpointId) +{ + VerifyOrDie(endpointId == 1); // this cluster is only enabled for endpoint 1. + VerifyOrDie(gOperationalStateInstance == nullptr && gOperationalStateDelegate == nullptr); + + gOperationalStateDelegate = new OperationalStateDelegate; + EndpointId operationalStateEndpoint = 0x01; + gOperationalStateInstance = new Instance(gOperationalStateDelegate, operationalStateEndpoint, Clusters::OperationalState::Id); + + gOperationalStateInstance->SetOperationalState(to_underlying(OperationalState::OperationalStateEnum::kStopped)); + + gOperationalStateInstance->Init(); +} diff --git a/examples/dishwasher-app/dishwasher-common/src/operational-state-delegates.cpp b/examples/dishwasher-app/dishwasher-common/src/operational-state-delegates.cpp deleted file mode 100644 index 9d4a5cec4b111e..00000000000000 --- a/examples/dishwasher-app/dishwasher-common/src/operational-state-delegates.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * - * Copyright (c) 2023 Project CHIP Authors - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include -#include -#include -#include - -namespace chip { -namespace app { -namespace Clusters { -namespace OperationalState { - -/** - * class to present Enquiry Table of Delegates - */ -struct DelegatesEnquiryTable -{ - /** - * Endpoint Id - */ - EndpointId mEndpointId; - /** - * Cluster Id - */ - ClusterId mClusterId; - /** - * The delegate for the cluster instance given by mEndpointId and mClusterId. - */ - Delegate * pItem; -}; - -/* - * An example to present device's endpointId - */ -constexpr EndpointId kDemoEndpointId = 1; - -/** - * Operational State List - * Note: User Define - */ -static const GenericOperationalState opStateList[] = { - GenericOperationalState(to_underlying(OperationalStateEnum::kStopped)), - GenericOperationalState(to_underlying(OperationalStateEnum::kRunning)), - GenericOperationalState(to_underlying(OperationalStateEnum::kPaused)), - GenericOperationalState(to_underlying(OperationalStateEnum::kError)), -}; - -/** - * Phase List - * Note: User Define - */ -static const GenericOperationalPhase opPhaseList[] = { - /** - * Phase List is null - */ - GenericOperationalPhase(DataModel::Nullable()), -}; - -/** - * Operational State Delegate - * Note: User Define - */ -static OperationalStateDelegate opStateDelegate(to_underlying(OperationalStateEnum::kStopped), - GenericOperationalError(to_underlying(ErrorStateEnum::kNoError)), - Span(opStateList), - Span(opPhaseList)); - -/** - * Enquiry Table of Operational State Cluster and alias Cluter Delegate corresponding to endpointId and clusterId - * Note: User Define - */ -constexpr DelegatesEnquiryTable kDelegatesEnquiryTable[] = { - // EndpointId, ClusterId, Delegate - { Clusters::OperationalState::kDemoEndpointId, Clusters::OperationalState::Id, &opStateDelegate }, -}; - -/** - * Get the pointer of target delegate for target endpoint and cluster - * @param[in] aEndpiontId The endpointId - * @param[in] aClusterID The clusterId - * @return the pointer of target delegate - */ -Delegate * getGenericDelegateTable(EndpointId aEndpointId, ClusterId aClusterId) -{ - for (size_t i = 0; i < ArraySize(kDelegatesEnquiryTable); ++i) - { - if (kDelegatesEnquiryTable[i].mEndpointId == aEndpointId && kDelegatesEnquiryTable[i].mClusterId == aClusterId) - { - return kDelegatesEnquiryTable[i].pItem; - } - } - return nullptr; -} - -// @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. -// @return Default global delegate instance. -Delegate * GetOperationalStateDelegate(EndpointId endpointId, ClusterId clusterId) -{ - return getGenericDelegateTable(endpointId, clusterId); -} - -} // namespace OperationalState -} // namespace Clusters -} // namespace app -} // namespace chip - -void MatterOperationalStateServerInit() -{ - using namespace chip::app; - static Clusters::OperationalState::OperationalStateServer operationalstateServer(Clusters::OperationalState::kDemoEndpointId, - Clusters::OperationalState::Id); - operationalstateServer.Init(); -} diff --git a/examples/dishwasher-app/linux/BUILD.gn b/examples/dishwasher-app/linux/BUILD.gn index e6a53dc373168f..658fd8093d4616 100644 --- a/examples/dishwasher-app/linux/BUILD.gn +++ b/examples/dishwasher-app/linux/BUILD.gn @@ -25,7 +25,6 @@ config("includes") { executable("chip-dishwasher-app") { sources = [ "${chip_root}/examples/dishwasher-app/dishwasher-common/src/operational-state-delegate-impl.cpp", - "${chip_root}/examples/dishwasher-app/dishwasher-common/src/operational-state-delegates.cpp", "include/CHIPProjectAppConfig.h", "main.cpp", ] diff --git a/examples/dishwasher-app/linux/main.cpp b/examples/dishwasher-app/linux/main.cpp index 54e320ffc9e14c..d7bddbb102805b 100644 --- a/examples/dishwasher-app/linux/main.cpp +++ b/examples/dishwasher-app/linux/main.cpp @@ -15,25 +15,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "operational-state-delegates.h" +#include "operational-state-delegate-impl.h" #include -#include -#include -#include -#include - using namespace chip; using namespace chip::app; using namespace chip::app::Clusters; -void ApplicationInit() +void ApplicationInit() {} + +void ApplicationShutdown() { - MatterOperationalStateServerInit(); + OperationalState::Shutdown(); } -void ApplicationShutdown() {} - int main(int argc, char * argv[]) { if (ChipLinuxAppInit(argc, argv) != 0) diff --git a/src/app/clusters/operational-state-server/operational-state-cluster-objects.h b/src/app/clusters/operational-state-server/operational-state-cluster-objects.h index d7159791964915..c4248db70e8b9c 100644 --- a/src/app/clusters/operational-state-server/operational-state-cluster-objects.h +++ b/src/app/clusters/operational-state-server/operational-state-cluster-objects.h @@ -138,6 +138,35 @@ struct GenericOperationalError : public app::Clusters::detail::Structs::ErrorSta } } + bool IsEqual(const Structs::ErrorStateStruct::Type & rhs) + { + if (errorStateID != rhs.errorStateID) + { + return false; + } + if (errorStateLabel.HasValue() != rhs.errorStateLabel.HasValue() || + errorStateDetails.HasValue() != rhs.errorStateDetails.HasValue()) + { + return false; + } + if (errorStateLabel.HasValue()) + { + if (!errorStateLabel.Value().data_equal(rhs.errorStateLabel.Value())) + { + return false; + } + } + if (errorStateDetails.HasValue()) + { + if (!errorStateDetails.Value().data_equal(rhs.errorStateDetails.Value())) + { + return false; + } + } + + return true; + } + private: char mErrorStateLabelBuffer[kOperationalErrorLabelMaxSize]; char mErrorStateDetailsBuffer[kOperationalErrorDetailsMaxSize]; 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 f818c63c5cc14f..8e74786ba0ffaf 100644 --- a/src/app/clusters/operational-state-server/operational-state-server.cpp +++ b/src/app/clusters/operational-state-server/operational-state-server.cpp @@ -22,19 +22,10 @@ #include "operational-state-server.h" #include #include -#include -#include -#include -#include -#include -#include #include #include #include -#include #include -#include -#include using namespace chip; using namespace chip::app; @@ -44,7 +35,20 @@ using namespace chip::app::Clusters::OperationalState::Attributes; using Status = Protocols::InteractionModel::Status; -CHIP_ERROR OperationalStateServer::Init() +Instance::Instance(Delegate * aDelegate, EndpointId aEndpointId, ClusterId aClusterId) : + CommandHandlerInterface(MakeOptional(aEndpointId), aClusterId), AttributeAccessInterface(MakeOptional(aEndpointId), aClusterId), + mDelegate(aDelegate), mEndpointId(aEndpointId), mClusterId(aClusterId) +{ + mDelegate->SetInstance(this); +} + +Instance::~Instance() +{ + InteractionModelEngine::GetInstance()->UnregisterCommandHandler(this); + unregisterAttributeAccessOverride(this); +} + +CHIP_ERROR Instance::Init() { // Check if the cluster has been selected in zap if (!emberAfContainsServer(mEndpointId, mClusterId)) @@ -60,139 +64,156 @@ CHIP_ERROR OperationalStateServer::Init() return CHIP_NO_ERROR; } -void OperationalStateServer::Shutdown() +CHIP_ERROR Instance::SetCurrentPhase(const DataModel::Nullable & aPhase) { - InteractionModelEngine::GetInstance()->UnregisterCommandHandler(this); -} - -// This function is called by the interaction model engine when a command destined for this instance is received. -void OperationalStateServer::InvokeCommand(HandlerContext & handlerContext) -{ - ChipLogDetail(Zcl, "OperationalState: InvokeCommand"); - switch (handlerContext.mRequestPath.mCommandId) + if (!aPhase.IsNull()) { - case Commands::Pause::Id: - ChipLogDetail(Zcl, "OperationalState: Entering handling Pause state"); - - HandleCommand( - handlerContext, [this](HandlerContext & ctx, const auto & req) { HandlePauseState(ctx, req); }); - break; - - case Commands::Resume::Id: - ChipLogDetail(Zcl, "OperationalState: Entering handling Resume state"); - - HandleCommand( - handlerContext, [this](HandlerContext & ctx, const auto & req) { HandleResumeState(ctx, req); }); - break; - - case Commands::Start::Id: - ChipLogDetail(Zcl, "OperationalState: Entering handling Start state"); - - HandleCommand( - handlerContext, [this](HandlerContext & ctx, const auto & req) { HandleStartState(ctx, req); }); - break; - - case Commands::Stop::Id: - ChipLogDetail(Zcl, "OperationalState: Entering handling Stop state"); + if (!IsSupportedPhase(aPhase.Value())) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + } - HandleCommand(handlerContext, - [this](HandlerContext & ctx, const auto & req) { HandleStopState(ctx, req); }); - break; + DataModel::Nullable oldPhase = mCurrentPhase; + mCurrentPhase = aPhase; + if (mCurrentPhase != oldPhase) + { + ConcreteAttributePath path = ConcreteAttributePath(mEndpointId, mClusterId, Attributes::CurrentPhase::Id); + MatterReportingAttributeChangeCallback(path); } + return CHIP_NO_ERROR; } -void OperationalStateServer::HandlePauseState(HandlerContext & ctx, const Commands::Pause::DecodableType & req) +CHIP_ERROR Instance::SetOperationalState(uint8_t aOpState) { - ChipLogDetail(Zcl, "OperationalState: HandlePauseState"); - Commands::OperationalCommandResponse::Type response; - Delegate * delegate = OperationalState::GetOperationalStateDelegate(mEndpointId, mClusterId); - GenericOperationalError err(to_underlying(ErrorStateEnum::kNoError)); - - VerifyOrReturn(delegate != nullptr, ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::Failure)); - uint8_t opState = delegate->GetCurrentOperationalState(); + // Error is only allowed to be set by OnOperationalErrorDetected. + if (aOpState == to_underlying(OperationalStateEnum::kError) || !IsSupportedOperationalState(aOpState)) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } - if (opState != to_underlying(OperationalStateEnum::kPaused) && opState != to_underlying(OperationalStateEnum::kRunning)) + if (mOperationalError.errorStateID != to_underlying(ErrorStateEnum::kNoError)) { - err.Set(to_underlying(ErrorStateEnum::kCommandInvalidInState)); + mOperationalError.Set(to_underlying(ErrorStateEnum::kNoError)); + ConcreteAttributePath path = ConcreteAttributePath(mEndpointId, mClusterId, Attributes::OperationalError::Id); + MatterReportingAttributeChangeCallback(path); } - else if (opState != to_underlying(OperationalStateEnum::kPaused)) + + uint8_t oldState = mOperationalState; + mOperationalState = aOpState; + if (mOperationalState != oldState) { - delegate->HandlePauseStateCallback(err); + ConcreteAttributePath path = ConcreteAttributePath(mEndpointId, mClusterId, Attributes::OperationalState::Id); + MatterReportingAttributeChangeCallback(path); } - response.commandResponseState = err; + return CHIP_NO_ERROR; +} - ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); +DataModel::Nullable Instance::GetCurrentPhase() const +{ + return mCurrentPhase; } -void OperationalStateServer::HandleResumeState(HandlerContext & ctx, const Commands::Resume::DecodableType & req) +uint8_t Instance::GetCurrentOperationalState() const { - ChipLogDetail(Zcl, "OperationalState: HandleResumeState"); - Commands::OperationalCommandResponse::Type response; - Delegate * delegate = OperationalState::GetOperationalStateDelegate(mEndpointId, mClusterId); - GenericOperationalError err(to_underlying(ErrorStateEnum::kNoError)); + return mOperationalState; +} - VerifyOrReturn(delegate != nullptr, ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::Failure)); - uint8_t opState = delegate->GetCurrentOperationalState(); +void Instance::GetCurrentOperationalError(GenericOperationalError & error) const +{ + error.Set(mOperationalError.errorStateID, mOperationalError.errorStateLabel, mOperationalError.errorStateDetails); +} - if (opState != to_underlying(OperationalStateEnum::kPaused) && opState != to_underlying(OperationalStateEnum::kRunning)) +void Instance::OnOperationalErrorDetected(const Structs::ErrorStateStruct::Type & aError) +{ + ChipLogDetail(Zcl, "OperationalStateServer: OnOperationalErrorDetected"); + // Set the OperationalState attribute to Error + if (mOperationalState != to_underlying(OperationalStateEnum::kError)) { - err.Set(to_underlying(ErrorStateEnum::kCommandInvalidInState)); + mOperationalState = to_underlying(OperationalStateEnum::kError); + ConcreteAttributePath path = ConcreteAttributePath(mEndpointId, mClusterId, Attributes::OperationalState::Id); + MatterReportingAttributeChangeCallback(path); } - else if (opState == to_underlying(OperationalStateEnum::kPaused)) + + // Set the OperationalError attribute + if (mOperationalError.IsEqual(aError)) { - delegate->HandleResumeStateCallback(err); + mOperationalError.Set(aError.errorStateID, aError.errorStateLabel, aError.errorStateDetails); + ConcreteAttributePath path = ConcreteAttributePath(mEndpointId, mClusterId, Attributes::OperationalError::Id); + MatterReportingAttributeChangeCallback(path); } - response.commandResponseState = err; - ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); + // Generate an ErrorDetected event + GenericErrorEvent event(mClusterId, aError); + EventNumber eventNumber; + CHIP_ERROR error = LogEvent(event, mEndpointId, eventNumber); + + if (error != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "OperationalStateServer: Failed to record OperationalError event: %" CHIP_ERROR_FORMAT, error.Format()); + } } -void OperationalStateServer::HandleStartState(HandlerContext & ctx, const Commands::Start::DecodableType & req) +void Instance::OnOperationCompletionDetected(uint8_t aCompletionErrorCode, + const Optional> & aTotalOperationalTime, + const Optional> & aPausedTime) const { - ChipLogDetail(Zcl, "OperationalState: HandleStartState"); - Commands::OperationalCommandResponse::Type response; - Delegate * delegate = OperationalState::GetOperationalStateDelegate(mEndpointId, mClusterId); - GenericOperationalError err(to_underlying(ErrorStateEnum::kNoError)); + ChipLogDetail(Zcl, "OperationalStateServer: OnOperationCompletionDetected"); - VerifyOrReturn(delegate != nullptr, ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::Failure)); - uint8_t opState = delegate->GetCurrentOperationalState(); + GenericOperationCompletionEvent event(mClusterId, aCompletionErrorCode, aTotalOperationalTime, aPausedTime); + EventNumber eventNumber; + CHIP_ERROR error = LogEvent(event, mEndpointId, eventNumber); - if (opState != to_underlying(OperationalStateEnum::kRunning)) + if (error != CHIP_NO_ERROR) { - delegate->HandleStartStateCallback(err); + ChipLogError(Zcl, "OperationalStateServer: Failed to record OperationCompletion event: %" CHIP_ERROR_FORMAT, + error.Format()); } - response.commandResponseState = err; - - ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); } -void OperationalStateServer::HandleStopState(HandlerContext & ctx, const Commands::Stop::DecodableType & req) +void Instance::ReportOperationalStateListChange() { - ChipLogDetail(Zcl, "OperationalState: HandleStopState"); - Commands::OperationalCommandResponse::Type response; - Delegate * delegate = OperationalState::GetOperationalStateDelegate(mEndpointId, mClusterId); - GenericOperationalError err(to_underlying(ErrorStateEnum::kNoError)); + MatterReportingAttributeChangeCallback(ConcreteAttributePath(mEndpointId, mClusterId, Attributes::OperationalStateList::Id)); +} - VerifyOrReturn(delegate != nullptr, ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::Failure)); - uint8_t opState = delegate->GetCurrentOperationalState(); +void Instance::ReportPhaseListChange() +{ + MatterReportingAttributeChangeCallback(ConcreteAttributePath(mEndpointId, mClusterId, Attributes::PhaseList::Id)); +} - if (opState != to_underlying(OperationalStateEnum::kStopped)) +bool Instance::IsSupportedPhase(uint8_t aPhase) +{ + GenericOperationalPhase phase = GenericOperationalPhase(DataModel::Nullable()); + if (mDelegate->GetOperationalPhaseAtIndex(aPhase, phase) != CHIP_ERROR_NOT_FOUND) { - delegate->HandleStopStateCallback(err); + return true; } - response.commandResponseState = err; + return false; +} - ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); +bool Instance::IsSupportedOperationalState(uint8_t aState) +{ + GenericOperationalState opState; + for (uint8_t i = 0; mDelegate->GetOperationalStateAtIndex(i, opState) != CHIP_ERROR_NOT_FOUND; i++) + { + if (opState.operationalStateID == aState) + { + return true; + } + } + ChipLogDetail(Zcl, "Cannot find an operational state with value %u", aState); + return false; } +// private + template -void OperationalStateServer::HandleCommand(HandlerContext & handlerContext, FuncT func) +void Instance::HandleCommand(HandlerContext & handlerContext, FuncT func) { if (!handlerContext.mCommandHandled && (handlerContext.mRequestPath.mCommandId == RequestT::GetCommandId())) { RequestT requestPayload; - // // If the command matches what the caller is looking for, let's mark this as being handled // even if errors happen after this. This ensures that we don't execute any fall-back strategies // to handle this command since at this point, the caller is taking responsibility for handling @@ -211,15 +232,49 @@ void OperationalStateServer::HandleCommand(HandlerContext & handlerContext, Func } } -CHIP_ERROR OperationalStateServer::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) +// This function is called by the interaction model engine when a command destined for this instance is received. +void Instance::InvokeCommand(HandlerContext & handlerContext) +{ + ChipLogDetail(Zcl, "OperationalState: InvokeCommand"); + switch (handlerContext.mRequestPath.mCommandId) + { + case Commands::Pause::Id: + ChipLogDetail(Zcl, "OperationalState: Entering handling Pause state"); + + HandleCommand( + handlerContext, [this](HandlerContext & ctx, const auto & req) { HandlePauseState(ctx, req); }); + break; + + case Commands::Resume::Id: + ChipLogDetail(Zcl, "OperationalState: Entering handling Resume state"); + + HandleCommand( + handlerContext, [this](HandlerContext & ctx, const auto & req) { HandleResumeState(ctx, req); }); + break; + + case Commands::Start::Id: + ChipLogDetail(Zcl, "OperationalState: Entering handling Start state"); + + HandleCommand( + handlerContext, [this](HandlerContext & ctx, const auto & req) { HandleStartState(ctx, req); }); + break; + + case Commands::Stop::Id: + ChipLogDetail(Zcl, "OperationalState: Entering handling Stop state"); + + HandleCommand(handlerContext, + [this](HandlerContext & ctx, const auto & req) { HandleStopState(ctx, req); }); + break; + } +} + +CHIP_ERROR Instance::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) { + ChipLogError(Zcl, "OperationalState: Reading"); switch (aPath.mAttributeId) { case OperationalState::Attributes::OperationalStateList::Id: { - Delegate * delegate = OperationalState::GetOperationalStateDelegate(mEndpointId, mClusterId); - VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is nullptr")); - - return aEncoder.EncodeList([delegate](const auto & encoder) -> CHIP_ERROR { + return aEncoder.EncodeList([delegate = mDelegate](const auto & encoder) -> CHIP_ERROR { GenericOperationalState opState; size_t index = 0; CHIP_ERROR err = CHIP_NO_ERROR; @@ -238,36 +293,26 @@ CHIP_ERROR OperationalStateServer::Read(const ConcreteReadAttributePath & aPath, break; case OperationalState::Attributes::OperationalState::Id: { - - Delegate * delegate = OperationalState::GetOperationalStateDelegate(mEndpointId, mClusterId); - VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is nullptr")); - uint8_t opState = delegate->GetCurrentOperationalState(); - return aEncoder.Encode(opState); + ChipLogError(Zcl, "OperationalState: H1"); + ReturnErrorOnFailure(aEncoder.Encode(GetCurrentOperationalState())); } break; case OperationalState::Attributes::OperationalError::Id: { - Delegate * delegate = OperationalState::GetOperationalStateDelegate(mEndpointId, mClusterId); - GenericOperationalError opErr(to_underlying(ErrorStateEnum::kNoError)); - VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is nullptr")); - delegate->GetCurrentOperationalError(opErr); - return aEncoder.Encode(opErr); + ReturnErrorOnFailure(aEncoder.Encode(mOperationalError)); } break; case OperationalState::Attributes::PhaseList::Id: { - Delegate * delegate = OperationalState::GetOperationalStateDelegate(mEndpointId, mClusterId); - GenericOperationalPhase phase = GenericOperationalPhase(DataModel::Nullable()); size_t index = 0; - VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is nullptr")); - if (delegate->GetOperationalPhaseAtIndex(index, phase) == CHIP_ERROR_NOT_FOUND || phase.IsMissing()) + if (mDelegate->GetOperationalPhaseAtIndex(index, phase) == CHIP_ERROR_NOT_FOUND || phase.IsMissing()) { return aEncoder.EncodeNull(); } return aEncoder.EncodeList([&](const auto & encoder) -> CHIP_ERROR { - while (delegate->GetOperationalPhaseAtIndex(index, phase) != CHIP_ERROR_NOT_FOUND) + while (this->mDelegate->GetOperationalPhaseAtIndex(index, phase) != CHIP_ERROR_NOT_FOUND) { ReturnErrorOnFailure(encoder.Encode(phase.mPhaseName)); index++; @@ -278,56 +323,94 @@ CHIP_ERROR OperationalStateServer::Read(const ConcreteReadAttributePath & aPath, break; case OperationalState::Attributes::CurrentPhase::Id: { - DataModel::Nullable currentPhase; - Delegate * delegate = OperationalState::GetOperationalStateDelegate(mEndpointId, mClusterId); - - VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is nullptr")); - delegate->GetCurrentPhase(currentPhase); - return aEncoder.Encode(currentPhase); + ReturnErrorOnFailure(aEncoder.Encode(GetCurrentPhase())); } break; case OperationalState::Attributes::CountdownTime::Id: { - DataModel::Nullable countdownTime; - Delegate * delegate = OperationalState::GetOperationalStateDelegate(mEndpointId, mClusterId); - - VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is nullptr")); - delegate->GetCountdownTime(countdownTime); - return aEncoder.Encode(countdownTime); + ReturnErrorOnFailure(aEncoder.Encode(mDelegate->GetCountdownTime())); } break; } return CHIP_NO_ERROR; } -void OperationalStateServer::OnOperationalErrorDetected(const Structs::ErrorStateStruct::Type & aError) +void Instance::HandlePauseState(HandlerContext & ctx, const Commands::Pause::DecodableType & req) { - ChipLogDetail(Zcl, "OperationalStateServer: OnOperationalErrorDetected"); - MatterReportingAttributeChangeCallback(mEndpointId, mClusterId, Attributes::OperationalState::Id); + ChipLogDetail(Zcl, "OperationalState: HandlePauseState"); - GenericErrorEvent event(mClusterId, aError); - EventNumber eventNumber; - CHIP_ERROR error = app::LogEvent(event, mEndpointId, eventNumber); + GenericOperationalError err(to_underlying(ErrorStateEnum::kNoError)); + uint8_t opState = GetCurrentOperationalState(); - if (error != CHIP_NO_ERROR) + if (opState != to_underlying(OperationalStateEnum::kPaused) && opState != to_underlying(OperationalStateEnum::kRunning)) { - ChipLogError(Zcl, "OperationalStateServer: Failed to record OperationalError event: %" CHIP_ERROR_FORMAT, error.Format()); + err.Set(to_underlying(ErrorStateEnum::kCommandInvalidInState)); + } + else if (opState == to_underlying(OperationalStateEnum::kRunning)) + { + mDelegate->HandlePauseStateCallback(err); } + + Commands::OperationalCommandResponse::Type response; + response.commandResponseState = err; + + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); } -void OperationalStateServer::OnOperationCompletionDetected(uint8_t aCompletionErrorCode, - const Optional> & aTotalOperationalTime, - const Optional> & aPausedTime) +void Instance::HandleStopState(HandlerContext & ctx, const Commands::Stop::DecodableType & req) { - ChipLogDetail(Zcl, "OperationalStateServer: OnOperationCompletionDetected"); + ChipLogDetail(Zcl, "OperationalState: HandleStopState"); - GenericOperationCompletionEvent event(mClusterId, aCompletionErrorCode, aTotalOperationalTime, aPausedTime); - EventNumber eventNumber; - CHIP_ERROR error = app::LogEvent(event, mEndpointId, eventNumber); + GenericOperationalError err(to_underlying(ErrorStateEnum::kNoError)); + uint8_t opState = GetCurrentOperationalState(); - if (error != CHIP_NO_ERROR) + if (opState != to_underlying(OperationalStateEnum::kStopped)) { - ChipLogError(Zcl, "OperationalStateServer: Failed to record OperationCompletion event: %" CHIP_ERROR_FORMAT, - error.Format()); + mDelegate->HandleStopStateCallback(err); + } + + Commands::OperationalCommandResponse::Type response; + response.commandResponseState = err; + + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); +} + +void Instance::HandleStartState(HandlerContext & ctx, const Commands::Start::DecodableType & req) +{ + ChipLogDetail(Zcl, "OperationalState: HandleStartState"); + + GenericOperationalError err(to_underlying(ErrorStateEnum::kNoError)); + uint8_t opState = GetCurrentOperationalState(); + + if (opState != to_underlying(OperationalStateEnum::kRunning)) + { + mDelegate->HandleStartStateCallback(err); } + + Commands::OperationalCommandResponse::Type response; + response.commandResponseState = err; + + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); +} + +void Instance::HandleResumeState(HandlerContext & ctx, const Commands::Resume::DecodableType & req) +{ + ChipLogDetail(Zcl, "OperationalState: HandleResumeState"); + + GenericOperationalError err(to_underlying(ErrorStateEnum::kNoError)); + uint8_t opState = GetCurrentOperationalState(); + + if (opState != to_underlying(OperationalStateEnum::kPaused) && opState != to_underlying(OperationalStateEnum::kRunning)) + { + err.Set(to_underlying(ErrorStateEnum::kCommandInvalidInState)); + } + else if (opState == to_underlying(OperationalStateEnum::kPaused)) + { + mDelegate->HandleResumeStateCallback(err); + } + + Commands::OperationalCommandResponse::Type response; + response.commandResponseState = err; + + ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); } 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 3e96fa6ca03405..4d093318ac4833 100644 --- a/src/app/clusters/operational-state-server/operational-state-server.h +++ b/src/app/clusters/operational-state-server/operational-state-server.h @@ -33,54 +33,83 @@ class Uncopyable { protected: Uncopyable() {} - ~Uncopyable() {} + ~Uncopyable() = default; private: Uncopyable(const Uncopyable &) = delete; Uncopyable & operator=(const Uncopyable &) = delete; }; +class Delegate; + /** - * OperationalStateServer is a class that represents an instance of a derivation of the operational state cluster. + * Instance is a class that represents an instance of a derivation of the operational state cluster. * It implements CommandHandlerInterface so it can generically handle commands for any derivation cluster id. */ -class OperationalStateServer : public CommandHandlerInterface, public AttributeAccessInterface, public Uncopyable +class Instance : 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. + * 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. + * It is possible to set the CurrentPhase and OperationalState via the Set... methods before calling Init(). + * @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 operational state aliased cluster to be instantiated. + * @param aClusterId The ID of the operational state derived cluster to be instantiated. */ - OperationalStateServer(EndpointId aEndpointId, ClusterId aClusterId) : - CommandHandlerInterface(MakeOptional(aEndpointId), aClusterId), - AttributeAccessInterface(MakeOptional(aEndpointId), aClusterId) - { - - mEndpointId = aEndpointId; - mClusterId = aClusterId; - } + Instance(Delegate * aDelegate, EndpointId aEndpointId, ClusterId aClusterId); - ~OperationalStateServer() override {} + ~Instance() override; /** - * Init the operational state server. - * This function must be called after defining a OperationalStateServer class object. - * @param void - * @return CHIP_ERROR CHIP_NO_ERROR on success, or corresponding error code. + * Initialise the operational state server instance. + * This function must be called after defining an Instance class object. + * @return Returns an error if the given endpoint and cluster ID have not been enabled in zap or if the + * CommandHandler or AttributeHandler registration fails, else returns CHIP_NO_ERROR. */ CHIP_ERROR Init(); + // Attribute setters + /** + * Set operational phase. + * @param aPhase The operational phase that should now be the current one. + * @return CHIP_ERROR_INVALID_ARGUMENT if aPhase is an invalid value. CHIP_NO_ERROR if set was successful. + */ + CHIP_ERROR SetCurrentPhase(const app::DataModel::Nullable & aPhase); + + /** + * Set current operational state. + * NOTE: This method cannot be used to set the error state. The error state must be set via the + * OnOperationalErrorDetected method. + * @param aOpState The operational state that should now be the current one. + * @return CHIP_ERROR_INVALID_ARGUMENT if aOpState is an invalid value. CHIP_NO_ERROR if set was successful. + */ + CHIP_ERROR SetOperationalState(uint8_t aOpState); + + // Attribute getters + /** + * Get current phase. + * @return The current phase. + */ + app::DataModel::Nullable GetCurrentPhase() const; + /** - * Shut down the operational state server. - * This function must be called before destroying a OperationalStateServer class object. - * @param void + * Get the current operational state. + * @return The current operational state value. */ - void Shutdown(); + uint8_t GetCurrentOperationalState() const; + /** + * Get current operational error. + * @param error The GenericOperationalError to fill with the current operational error value + */ + void GetCurrentOperationalError(GenericOperationalError & error) const; + + // Event triggers /** * @brief Called when the Node detects a OperationalError has been raised. + * Note: This function also sets the OperationalState attribute to Error. * @param aError OperationalError which detects */ void OnOperationalErrorDetected(const Structs::ErrorStateStruct::Type & aError); @@ -93,9 +122,43 @@ class OperationalStateServer : public CommandHandlerInterface, public AttributeA */ void OnOperationCompletionDetected(uint8_t aCompletionErrorCode, const Optional> & aTotalOperationalTime = NullOptional, - const Optional> & aPausedTime = NullOptional); + const Optional> & aPausedTime = NullOptional) const; + + // List change reporting + /** + * Reports that the contents of the operational state list has changed. + * The device SHALL call this method whenever it changes the operational state list. + */ + void ReportOperationalStateListChange(); + + /** + * Reports that the contents of the phase list has changed. + * The device SHALL call this method whenever it changes the phase list. + */ + void ReportPhaseListChange(); + + /** + * This function returns true if the phase value given exists in the PhaseList attribute, otherwise it returns false. + */ + bool IsSupportedPhase(uint8_t aPhase); + + /** + * This function returns true if the operational state value given exists in the OperationalStateList attribute, + * otherwise it returns false. + */ + bool IsSupportedOperationalState(uint8_t aState); private: + Delegate * mDelegate; + + EndpointId mEndpointId; + ClusterId mClusterId; + + // Attribute Data Store + app::DataModel::Nullable mCurrentPhase; + uint8_t mOperationalState = 0; // assume 0 for now. + GenericOperationalError mOperationalError = to_underlying(ErrorStateEnum::kNoError); + // Inherited from CommandHandlerInterface template void HandleCommand(HandlerContext & handlerContext, FuncT func); @@ -103,9 +166,10 @@ class OperationalStateServer : public CommandHandlerInterface, public AttributeA // Inherited from CommandHandlerInterface void InvokeCommand(HandlerContext & ctx) override; - /// IM-level implementation of read - /// - /// Returns appropriately mapped CHIP_ERROR if applicable (may return CHIP_IM_GLOBAL_STATUS errors) + /** + * IM-level implementation of read + * @return appropriately mapped CHIP_ERROR if applicable (may return CHIP_IM_GLOBAL_STATUS errors) + */ CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; /** @@ -114,9 +178,9 @@ class OperationalStateServer : public CommandHandlerInterface, public AttributeA void HandlePauseState(HandlerContext & ctx, const Commands::Pause::DecodableType & req); /** - * Handle Command: Resume. + * Handle Command: Stop. */ - void HandleResumeState(HandlerContext & ctx, const Commands::Resume::DecodableType & req); + void HandleStopState(HandlerContext & ctx, const Commands::Stop::DecodableType & req); /** * Handle Command: Start. @@ -124,12 +188,9 @@ class OperationalStateServer : public CommandHandlerInterface, public AttributeA void HandleStartState(HandlerContext & ctx, const Commands::Start::DecodableType & req); /** - * Handle Command: Stop. + * Handle Command: Resume. */ - void HandleStopState(HandlerContext & ctx, const Commands::Stop::DecodableType & req); - - EndpointId mEndpointId; - ClusterId mClusterId; + void HandleResumeState(HandlerContext & ctx, const Commands::Resume::DecodableType & req); }; /** @@ -141,109 +202,77 @@ class OperationalStateServer : public CommandHandlerInterface, public AttributeA class Delegate { public: + Delegate() = default; + + virtual ~Delegate() = default; + /** - * Get the current operational state. - * @return The current operational state value + * Get the countdown time. + * NOTE: Changes to this attribute should not be reported. + * From the spec: Changes to this value SHALL NOT be reported in a subscription. + * @return The current countdown time. */ - virtual uint8_t GetCurrentOperationalState() = 0; + virtual app::DataModel::Nullable GetCountdownTime() = 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. + * Note: This is used by the SDK to populate the operational state list attribute. If the contents of this list changes, + * the device SHALL call the Instance's ReportOperationalStateListChange method to report that this attribute has changed. * @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. + * Note: This is used by the SDK to populate the phase list attribute. If the contents of this list changes, the + * device SHALL call the Instance's ReportPhaseListChange method to report that this attribute has changed. * @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. + * @param[out] err operational error after callback. */ virtual void HandlePauseStateCallback(GenericOperationalError & err) = 0; /** * Handle Command Callback in application: Resume - * @param[out] get operational error after callback. + * @param[out] err operational error after callback. */ virtual void HandleResumeStateCallback(GenericOperationalError & err) = 0; /** * Handle Command Callback in application: Start - * @param[out] get operational error after callback. + * @param[out] err operational error after callback. */ virtual void HandleStartStateCallback(GenericOperationalError & err) = 0; /** * Handle Command Callback in application: Stop - * @param[out] get operational error after callback. + * @param[out] err operational error after callback. */ virtual void HandleStopStateCallback(GenericOperationalError & err) = 0; - Delegate() = default; +private: + friend class Instance; - virtual ~Delegate() = default; -}; + Instance * mInstance = nullptr; -// @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); + /** + * This method is used by the SDK to set the instance pointer. This is done during the instantiation of a Instance object. + * @param aInstance A pointer to the Instance object related to this delegate object. + */ + void SetInstance(Instance * aInstance) { mInstance = aInstance; } + +protected: + Instance * GetInstance() const { return mInstance; } +}; } // namespace OperationalState } // namespace Clusters