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

Declare a "server cluster interface" that is intended to combine "attribute access interface" and "command handler interface" #37541

Merged
merged 1 commit into from
Mar 3, 2025
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
1 change: 1 addition & 0 deletions docs/ids_and_codes/ERROR_CODES.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ This file was **AUTOMATICALLY** generated by
| 37 | 0x25 | `CHIP_ERROR_UNKNOWN_IMPLICIT_TLV_TAG` |
| 38 | 0x26 | `CHIP_ERROR_WRONG_TLV_TYPE` |
| 39 | 0x27 | `CHIP_ERROR_TLV_CONTAINER_OPEN` |
| 40 | 0x28 | `CHIP_ERROR_IN_USE` |
| 42 | 0x2A | `CHIP_ERROR_INVALID_MESSAGE_TYPE` |
| 43 | 0x2B | `CHIP_ERROR_UNEXPECTED_TLV_ELEMENT` |
| 45 | 0x2D | `CHIP_ERROR_NOT_IMPLEMENTED` |
Expand Down
5 changes: 4 additions & 1 deletion scripts/build_coverage.sh
Original file line number Diff line number Diff line change
Expand Up @@ -222,16 +222,19 @@ lcov --initial --capture --directory "$OUTPUT_ROOT/obj/src" \
--exclude="$PWD"/zzz_generated/* \
--exclude="$PWD"/third_party/* \
--exclude=/usr/include/* \
--ignore-errors inconsistent \
--output-file "$COVERAGE_ROOT/lcov_base.info"

lcov --capture --directory "$OUTPUT_ROOT/obj/src" \
--ignore-errors inconsistent \
--exclude="$PWD"/zzz_generated/* \
--exclude="$PWD"/third_party/* \
--exclude=/usr/include/* \
--ignore-errors inconsistent \
--output-file "$COVERAGE_ROOT/lcov_test.info"

lcov --add-tracefile "$COVERAGE_ROOT/lcov_base.info" \
lcov --ignore-errors inconsistent \
--add-tracefile "$COVERAGE_ROOT/lcov_base.info" \
--add-tracefile "$COVERAGE_ROOT/lcov_test.info" \
--ignore-errors inconsistent \
--output-file "$COVERAGE_ROOT/lcov_final.info"
Expand Down
12 changes: 10 additions & 2 deletions src/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,14 @@ if (chip_build_tests) {
deps = []
tests = [
"${chip_root}/src/access/tests",
"${chip_root}/src/app/data-model/tests",
"${chip_root}/src/app/cluster-building-blocks/tests",
"${chip_root}/src/app/data-model-provider/tests",
"${chip_root}/src/app/data-model/tests",
"${chip_root}/src/app/icd/server/tests",
"${chip_root}/src/crypto/tests",
"${chip_root}/src/inet/tests",
"${chip_root}/src/lib/address_resolve/tests",
"${chip_root}/src/app/server-cluster/tests",
"${chip_root}/src/lib/asn1/tests",
"${chip_root}/src/lib/core/tests",
"${chip_root}/src/lib/format/tests",
Expand Down Expand Up @@ -94,12 +95,19 @@ if (chip_build_tests) {
# symbols for ember mocks which are used by other tests.

tests += [
"${chip_root}/src/data-model-providers/codegen/tests",
"${chip_root}/src/setup_payload/tests",
"${chip_root}/src/transport/raw/tests",
]
}

if (current_os != "zephyr" && current_os != "mbed" &&
chip_device_platform != "efr32") {
# Disabled from "one single binary" since the "mocks" contain duplicate sybmols
# with ember
# Disabled on EFR32 since "include <random>" fails with `::fabs` not defined
tests += [ "${chip_root}/src/data-model-providers/codegen/tests" ]
}

if (chip_device_platform != "efr32") {
tests += [
"${chip_root}/src/app/tests",
Expand Down
2 changes: 1 addition & 1 deletion src/app/InteractionModelEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1734,7 +1734,7 @@ void InteractionModelEngine::DispatchCommand(CommandHandlerImpl & apCommandObj,
request.invokeFlags.Set(DataModel::InvokeFlags::kTimed, apCommandObj.IsTimedInvoke());
request.subjectDescriptor = &subjectDescriptor;

std::optional<DataModel::ActionReturnStatus> status = GetDataModelProvider()->Invoke(request, apPayload, &apCommandObj);
std::optional<DataModel::ActionReturnStatus> status = GetDataModelProvider()->InvokeCommand(request, apPayload, &apCommandObj);

// Provider indicates that handler status or data was already set (or will be set asynchronously) by
// returning std::nullopt. If any other value is returned, it is requesting that a status is set. This
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 @@ -114,8 +114,8 @@ class Provider : public ProviderMetadataTree
/// - if a value is returned (not nullopt) then the handler response MUST NOT be filled. The caller
/// will then issue `handler->AddStatus(request.path, <return_value>->GetStatusCode())`. This is a
/// convenience to make writing Invoke calls easier.
virtual std::optional<ActionReturnStatus> Invoke(const InvokeRequest & request, chip::TLV::TLVReader & input_arguments,
CommandHandler * handler) = 0;
virtual std::optional<ActionReturnStatus> InvokeCommand(const InvokeRequest & request, chip::TLV::TLVReader & input_arguments,
CommandHandler * handler) = 0;

private:
InteractionModelContext mContext = { nullptr };
Expand Down
6 changes: 2 additions & 4 deletions src/app/data-model-provider/tests/TestActionReturnStatus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "lib/core/CHIPError.h"
#include "protocols/interaction_model/StatusCode.h"
#include "pw_unit_test/framework_backend.h"
#include <app/data-model-provider/ActionReturnStatus.h>
#include <app/data-model-provider/StringBuilderAdapters.h>
#include <lib/core/CHIPError.h>
#include <lib/support/CodeUtils.h>
#include <protocols/interaction_model/StatusCode.h>

#include <pw_unit_test/framework.h>

Expand Down
35 changes: 35 additions & 0 deletions src/app/server-cluster/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# 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/chip.gni")

source_set("server-cluster") {
sources = [
"DefaultServerCluster.cpp",
"DefaultServerCluster.h",
"ServerClusterInterface.h",
]

public_deps = [
"${chip_root}/src/app:attribute-access",
"${chip_root}/src/app:command-handler-interface",
"${chip_root}/src/app:paths",
"${chip_root}/src/app/common:ids",
"${chip_root}/src/app/data-model-provider",
"${chip_root}/src/app/data-model-provider:metadata",
"${chip_root}/src/crypto",
"${chip_root}/src/lib/core:error",
"${chip_root}/src/lib/core:types",
"${chip_root}/src/lib/support",
]
}
110 changes: 110 additions & 0 deletions src/app/server-cluster/DefaultServerCluster.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* 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/DefaultServerCluster.h>

#include <access/Privilege.h>
#include <app-common/zap-generated/ids/Attributes.h>
#include <app/data-model-provider/MetadataTypes.h>
#include <crypto/RandUtils.h>
#include <lib/support/BitFlags.h>
#include <optional>
#include <protocols/interaction_model/StatusCode.h>

namespace chip {
namespace app {
namespace {

using namespace chip::app::Clusters;
using namespace chip::app::DataModel;

constexpr std::array<AttributeEntry, 5> kGlobalAttributeEntries{ {
{
Globals::Attributes::ClusterRevision::Id,
BitFlags<AttributeQualityFlags>(),
Access::Privilege::kView,
std::nullopt,
},
{
Globals::Attributes::FeatureMap::Id,
BitFlags<AttributeQualityFlags>(),
Access::Privilege::kView,
std::nullopt,
},
{
Globals::Attributes::AttributeList::Id,
BitFlags<AttributeQualityFlags>(AttributeQualityFlags::kListAttribute),
Access::Privilege::kView,
std::nullopt,
},
{
Globals::Attributes::AcceptedCommandList::Id,
BitFlags<AttributeQualityFlags>(AttributeQualityFlags::kListAttribute),
Access::Privilege::kView,
std::nullopt,
},
{
Globals::Attributes::GeneratedCommandList::Id,
BitFlags<AttributeQualityFlags>(AttributeQualityFlags::kListAttribute),
Access::Privilege::kView,
std::nullopt,
},
} };

} // namespace

DefaultServerCluster::DefaultServerCluster()
{
// SPEC - 7.10.3. Cluster Data Version
// A cluster data version SHALL be initialized randomly when it is first published.
mDataVersion = Crypto::GetRandU32();
}

CHIP_ERROR DefaultServerCluster::Attributes(const ConcreteClusterPath & path, DataModel::ListBuilder<AttributeEntry> & builder)
{

return builder.ReferenceExisting(kGlobalAttributeEntries);
}

BitFlags<ClusterQualityFlags> DefaultServerCluster::GetClusterFlags() const
{
return {};
}

ActionReturnStatus DefaultServerCluster::WriteAttribute(const WriteAttributeRequest & request, AttributeValueDecoder & decoder)
{
return Protocols::InteractionModel::Status::UnsupportedWrite;
}

std::optional<ActionReturnStatus>
DefaultServerCluster::InvokeCommand(const InvokeRequest & request, chip::TLV::TLVReader & input_arguments, CommandHandler * handler)
{
return Protocols::InteractionModel::Status::UnsupportedCommand;
}

CHIP_ERROR DefaultServerCluster::AcceptedCommands(const ConcreteClusterPath & path,
DataModel::ListBuilder<AcceptedCommandEntry> & builder)
{
return CHIP_NO_ERROR;
}

CHIP_ERROR DefaultServerCluster::GeneratedCommands(const ConcreteClusterPath & path, DataModel::ListBuilder<CommandId> & builder)
{
return CHIP_NO_ERROR;
}

} // namespace app
} // namespace chip
84 changes: 84 additions & 0 deletions src/app/server-cluster/DefaultServerCluster.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* 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/server-cluster/ServerClusterInterface.h>

namespace chip {
namespace app {

/// Provides an implementation of most methods for a `ServerClusterInterface`
/// to make it easier to implement spec-compliant classes.
///
/// In particular it does:
/// - maintains a data version and provides `IncreaseDataVersion`. Ensures this
/// version is spec-compliant initialized (with a random value)
/// - Provides default implementations for most virtual methods EXCEPT:
/// - ReadAttribute (since that one needs to handle featuremap and revision)
/// - GetClusterId (since every implementation is for different clusters)
///
///
class DefaultServerCluster : public ServerClusterInterface
{
public:
DefaultServerCluster();
~DefaultServerCluster() override = default;

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

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

/// Default implementation errors out with an unsupported write on every attribute.
DataModel::ActionReturnStatus WriteAttribute(const DataModel::WriteAttributeRequest & request,
AttributeValueDecoder & decoder) override;

/// Must only be implemented if support for any non-global attributes
/// is required.
///
/// Default implementation just returns the global attributes required by the API contract.
CHIP_ERROR Attributes(const ConcreteClusterPath & path, DataModel::ListBuilder<DataModel::AttributeEntry> & builder) override;

///////////////////////////////////// Command Support /////////////////////////////////////////////////////////

/// Must only be implemented if commands are supported by the cluster
///
/// Default implementation errors out with an UnsupportedCommand error.
std::optional<DataModel::ActionReturnStatus> InvokeCommand(const DataModel::InvokeRequest & request,
chip::TLV::TLVReader & input_arguments,
CommandHandler * handler) override;

/// Must only be implemented if commands are supported by the cluster
///
/// Default implementation is a NOOP (no list items generated)
CHIP_ERROR AcceptedCommands(const ConcreteClusterPath & path,
DataModel::ListBuilder<DataModel::AcceptedCommandEntry> & builder) override;

/// Must only be implemented if commands that return values are supported by the cluster.
///
/// Default implementation is a NOOP (no list items generated)
CHIP_ERROR GeneratedCommands(const ConcreteClusterPath & path, DataModel::ListBuilder<CommandId> & builder) override;

protected:
void IncreaseDataVersion() { mDataVersion++; }

private:
DataVersion mDataVersion; // will be random-initialized as per spec
};

} // namespace app
} // namespace chip
Loading
Loading