Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable the use of the ServerClusterInterfaceRegistry in Codegen Provider #37851

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/ids_and_codes/ERROR_CODES.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,10 @@ This file was **AUTOMATICALLY** generated by
| 38 | 0x26 | `CHIP_ERROR_WRONG_TLV_TYPE` |
| 39 | 0x27 | `CHIP_ERROR_TLV_CONTAINER_OPEN` |
| 40 | 0x28 | `CHIP_ERROR_IN_USE` |
| 41 | 0x29 | `CHIP_ERROR_HAD_FAILURES` |
| 42 | 0x2A | `CHIP_ERROR_INVALID_MESSAGE_TYPE` |
| 43 | 0x2B | `CHIP_ERROR_UNEXPECTED_TLV_ELEMENT` |
| 44 | 0x2C | `CHIP_ERROR_ALREADY_INITIALIZED` |
| 45 | 0x2D | `CHIP_ERROR_NOT_IMPLEMENTED` |
| 46 | 0x2E | `CHIP_ERROR_INVALID_ADDRESS` |
| 47 | 0x2F | `CHIP_ERROR_INVALID_ARGUMENT` |
Expand Down
4 changes: 2 additions & 2 deletions src/app/data-model-provider/Provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class Provider : public ProviderMetadataTree

// During the transition phase, we expect a large subset of code to require access to
// event emitting, path marking and other operations
virtual InteractionModelContext CurrentContext() const { return mContext; }
[[nodiscard]] const InteractionModelContext & CurrentContext() const { return mContext; }

/// NOTE: this code is NOT required to handle `List` global attributes:
/// AcceptedCommandsList, GeneratedCommandsList OR AttributeList
Expand Down Expand Up @@ -117,7 +117,7 @@ class Provider : public ProviderMetadataTree
virtual std::optional<ActionReturnStatus> InvokeCommand(const InvokeRequest & request, chip::TLV::TLVReader & input_arguments,
CommandHandler * handler) = 0;

private:
protected:
InteractionModelContext mContext = { nullptr };
};

Expand Down
1 change: 1 addition & 0 deletions src/app/server-cluster/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ source_set("server-cluster") {
sources = [
"DefaultServerCluster.cpp",
"DefaultServerCluster.h",
"ServerClusterContext.h",
"ServerClusterInterface.h",
]

Expand Down
29 changes: 28 additions & 1 deletion src/app/server-cluster/DefaultServerCluster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include <access/Privilege.h>
#include <app-common/zap-generated/ids/Attributes.h>
#include <app/ConcreteClusterPath.h>
#include <app/data-model-provider/MetadataTypes.h>
#include <crypto/RandUtils.h>
#include <lib/support/BitFlags.h>
Expand Down Expand Up @@ -66,6 +67,11 @@ constexpr std::array<AttributeEntry, 5> kGlobalAttributeEntries{ {

} // namespace

Span<const DataModel::AttributeEntry> DefaultServerCluster::GlobalAttributes()
{
return { kGlobalAttributeEntries.data(), kGlobalAttributeEntries.size() };
}

DefaultServerCluster::DefaultServerCluster()
{
// SPEC - 7.10.3. Cluster Data Version
Expand All @@ -76,7 +82,28 @@ DefaultServerCluster::DefaultServerCluster()
CHIP_ERROR DefaultServerCluster::Attributes(const ConcreteClusterPath & path, DataModel::ListBuilder<AttributeEntry> & builder)
{

return builder.ReferenceExisting(kGlobalAttributeEntries);
return builder.ReferenceExisting(GlobalAttributes());
}

CHIP_ERROR DefaultServerCluster::Startup(ServerClusterContext * context)
{
VerifyOrReturnError(mContext == nullptr, CHIP_ERROR_ALREADY_INITIALIZED);
mContext = context;
return CHIP_NO_ERROR;
}

void DefaultServerCluster::Shutdown()
{
mContext = nullptr;
}

void DefaultServerCluster::NotifyAttributeChanged(AttributeId attributeId)
{
IncreaseDataVersion();

VerifyOrReturn(mContext != nullptr);
const ConcreteClusterPath path = GetPath();
mContext->interactionContext->dataModelChangeListener->MarkDirty({ path.mEndpointId, path.mClusterId, attributeId });
}

BitFlags<ClusterQualityFlags> DefaultServerCluster::GetClusterFlags() const
Expand Down
19 changes: 19 additions & 0 deletions src/app/server-cluster/DefaultServerCluster.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ class DefaultServerCluster : public ServerClusterInterface

//////////////////////////// ServerClusterInterface implementation ////////////////////////////////////////

/// Startup allows only a single initialization per cluster and will
/// fail with CHIP_ERROR_ALREADY_INITIALIZED if the object has already
/// been initialized.
///
/// Call Shutdown to de-initialize the object.
CHIP_ERROR Startup(ServerClusterContext * context) override;
void Shutdown() override;

[[nodiscard]] DataVersion GetDataVersion() const override { return mDataVersion; }
[[nodiscard]] BitFlags<DataModel::ClusterQualityFlags> GetClusterFlags() const override;

Expand Down Expand Up @@ -73,9 +81,20 @@ class DefaultServerCluster : public ServerClusterInterface
/// Default implementation is a NOOP (no list items generated)
CHIP_ERROR GeneratedCommands(const ConcreteClusterPath & path, DataModel::ListBuilder<CommandId> & builder) override;

/// Returns all global attributes that the spec defines in `7.13 Global Elements / Table 93: Global Attributes`
static Span<const DataModel::AttributeEntry> GlobalAttributes();

protected:
ServerClusterContext * mContext = nullptr;

void IncreaseDataVersion() { mDataVersion++; }

/// Marks that a specific attribute has changed value
///
/// This increases cluster data version and if a cluster context is available it will
/// notify that the attribute has changed.
void NotifyAttributeChanged(AttributeId attributeId);

private:
DataVersion mDataVersion; // will be random-initialized as per spec
};
Expand Down
52 changes: 52 additions & 0 deletions src/app/server-cluster/ServerClusterContext.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2025 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once

#include <app/data-model-provider/Context.h>
#include <app/data-model-provider/Provider.h>
#include <lib/core/CHIPPersistentStorageDelegate.h>

namespace chip {
namespace app {

/// Represents a runtime context for server cluster interfaces to interact
/// with the outside world such as:
/// - notify of state changes to trigger attribute reports
/// - emit events
/// - potentially interact/review global metadata
///
/// The context object is quite large in terms of exposed functionality and not all clusters use
/// all the information within, however a common context is used to minimize RAM overhead
/// for every cluster maintaining such a context.
struct ServerClusterContext
{
DataModel::Provider * const provider = nullptr; /// underlying provider that the cluster operates in
PersistentStorageDelegate * const storage = nullptr; /// read/write persistent storage
DataModel::InteractionModelContext * const interactionContext = nullptr; /// outside-world communication

ServerClusterContext(ServerClusterContext &&) = default;

bool operator!=(const ServerClusterContext & other) const
{
return (provider != other.provider) //
|| (interactionContext != other.interactionContext) //
|| (storage != other.storage);
}
};

} // namespace app
} // namespace chip
10 changes: 10 additions & 0 deletions src/app/server-cluster/ServerClusterInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <app/data-model-provider/MetadataList.h>
#include <app/data-model-provider/MetadataTypes.h>
#include <app/data-model-provider/OperationTypes.h>
#include <app/server-cluster/ServerClusterContext.h>
#include <lib/core/CHIPError.h>
#include <lib/core/DataModelTypes.h>
#include <lib/support/BitFlags.h>
Expand All @@ -42,6 +43,15 @@ class ServerClusterInterface
public:
virtual ~ServerClusterInterface() = default;

/// Starts up the server cluster interface.
///
/// The `context` lifetime must be guaranteed to last
/// until `Shutdown` is called.
virtual CHIP_ERROR Startup(ServerClusterContext * context) = 0;

/// A shutdown will always be paired with a corresponding Startup.
virtual void Shutdown() = 0;

///////////////////////////////////// Cluster Metadata Support //////////////////////////////////////////////////

/// The path to this cluster instance.
Expand Down
31 changes: 31 additions & 0 deletions src/app/server-cluster/testing/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Copyright (c) 2025 Project CHIP Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import("//build_overrides/build.gni")
import("//build_overrides/chip.gni")
import("${chip_root}/build/chip/chip_test_suite.gni")

source_set("testing") {
sources = [
"EmptyProvider.cpp",
"EmptyProvider.h",
"TestEventGenerator.h",
"TestProviderChangeListener.h",
"TestServerClusterContext.h",
]

public_deps = [
"${chip_root}/src/app/server-cluster",
"${chip_root}/src/lib/support:testing",
]
}
91 changes: 91 additions & 0 deletions src/app/server-cluster/testing/EmptyProvider.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Copyright (c) 2025 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 <app/server-cluster/testing/EmptyProvider.h>

namespace chip {
namespace Test {

using Protocols::InteractionModel::Status;
using namespace chip::app;
using namespace chip::app::DataModel;

CHIP_ERROR EmptyProvider::Shutdown()
{
return CHIP_NO_ERROR;
}

CHIP_ERROR EmptyProvider::Endpoints(ListBuilder<app::DataModel::EndpointEntry> & builder)
{
return CHIP_NO_ERROR;
}

CHIP_ERROR EmptyProvider::SemanticTags(EndpointId endpointId, ListBuilder<SemanticTag> & builder)
{
return CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint);
}
CHIP_ERROR EmptyProvider::DeviceTypes(EndpointId endpointId, ListBuilder<app::DataModel::DeviceTypeEntry> & builder)
{
return CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint);
}

CHIP_ERROR EmptyProvider::ClientClusters(EndpointId endpointId, ListBuilder<ClusterId> & builder)
{
return CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint);
}
CHIP_ERROR EmptyProvider::ServerClusters(EndpointId endpointId, ListBuilder<app::DataModel::ServerClusterEntry> & builder)
{
return CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint);
}

CHIP_ERROR EmptyProvider::Attributes(const app::ConcreteClusterPath & path, ListBuilder<app::DataModel::AttributeEntry> & builder)
{
return CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint);
}

CHIP_ERROR EmptyProvider::GeneratedCommands(const app::ConcreteClusterPath & path, ListBuilder<CommandId> & builder)
{
return CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint);
}

CHIP_ERROR EmptyProvider::AcceptedCommands(const app::ConcreteClusterPath & path,
ListBuilder<app::DataModel::AcceptedCommandEntry> & builder)
{
return CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint);
}

void EmptyProvider::Temporary_ReportAttributeChanged(const app::AttributePathParams & path) {}

ActionReturnStatus EmptyProvider::ReadAttribute(const app::DataModel::ReadAttributeRequest & request,
app::AttributeValueEncoder & encoder)
{
return Status::UnsupportedEndpoint;
}

ActionReturnStatus EmptyProvider::WriteAttribute(const app::DataModel::WriteAttributeRequest & request,
app::AttributeValueDecoder & decoder)
{
return Status::UnsupportedEndpoint;
}
std::optional<ActionReturnStatus> EmptyProvider::InvokeCommand(const app::DataModel::InvokeRequest & request,
chip::TLV::TLVReader & input_arguments,
app::CommandHandler * handler)
{
return Status::UnsupportedEndpoint;
}

} // namespace Test
} // namespace chip
64 changes: 64 additions & 0 deletions src/app/server-cluster/testing/EmptyProvider.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright (c) 2025 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once

#include <app/data-model-provider/Provider.h>
#include <protocols/interaction_model/StatusCode.h>

namespace chip {
namespace Test {

/// A provider that is emtpy - it contains no endpoints and most
/// calls fail with `Unsupported Endpoint`
///
/// This is a bare minimum implentation to have somethign that claims to be a `Provider`
/// however it has no real implementation that is useful. Over time this should be replaced
/// with some code-generated/controllable provider to allow for better testing.
class EmptyProvider : public app::DataModel::Provider
{
public:
using ActionReturnStatus = app::DataModel::ActionReturnStatus;
template <typename T>
using ListBuilder = app::DataModel::ListBuilder<T>;

CHIP_ERROR Shutdown() override;
CHIP_ERROR Endpoints(ListBuilder<app::DataModel::EndpointEntry> & builder) override;

CHIP_ERROR SemanticTags(EndpointId endpointId, ListBuilder<SemanticTag> & builder) override;
CHIP_ERROR DeviceTypes(EndpointId endpointId, ListBuilder<app::DataModel::DeviceTypeEntry> & builder) override;
CHIP_ERROR ClientClusters(EndpointId endpointId, ListBuilder<ClusterId> & builder) override;
CHIP_ERROR ServerClusters(EndpointId endpointId, ListBuilder<app::DataModel::ServerClusterEntry> & builder) override;
CHIP_ERROR Attributes(const app::ConcreteClusterPath & path, ListBuilder<app::DataModel::AttributeEntry> & builder) override;
CHIP_ERROR GeneratedCommands(const app::ConcreteClusterPath & path, ListBuilder<CommandId> & builder) override;
CHIP_ERROR AcceptedCommands(const app::ConcreteClusterPath & path,
ListBuilder<app::DataModel::AcceptedCommandEntry> & builder) override;
void ListAttributeWriteNotification(const app::ConcreteAttributePath & aPath,
app::DataModel::ListWriteOperation opType) override
{}

void Temporary_ReportAttributeChanged(const app::AttributePathParams & path) override;

ActionReturnStatus ReadAttribute(const app::DataModel::ReadAttributeRequest & request,
app::AttributeValueEncoder & encoder) override;
ActionReturnStatus WriteAttribute(const app::DataModel::WriteAttributeRequest & request,
app::AttributeValueDecoder & decoder) override;
std::optional<ActionReturnStatus> InvokeCommand(const app::DataModel::InvokeRequest & request,
chip::TLV::TLVReader & input_arguments, app::CommandHandler * handler) override;
};

} // namespace Test
} // namespace chip
Loading
Loading