Line data Source code
1 : /*
2 : * Copyright (c) 2025 Project CHIP Authors
3 : * All rights reserved.
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 : #pragma once
18 :
19 : #include <access/Privilege.h>
20 : #include <app/ConcreteClusterPath.h>
21 : #include <app/data-model/Nullable.h>
22 : #include <app/server-cluster/ServerClusterInterface.h>
23 : #include <lib/core/CHIPError.h>
24 :
25 : #include <optional>
26 : #include <type_traits>
27 : #include <utility>
28 :
29 : namespace chip {
30 : namespace app {
31 :
32 : /// Provides an implementation of most methods for a `ServerClusterInterface`
33 : /// to make it easier to implement spec-compliant classes.
34 : ///
35 : /// In particular it does:
36 : /// - handles a SINGLE cluster path that is set at construction time
37 : /// - maintains a data version and provides `IncreaseDataVersion`. Ensures this
38 : /// version is spec-compliant initialized (with a random value)
39 : /// - Provides default implementations for most virtual methods EXCEPT:
40 : /// - ReadAttribute (since that one needs to handle featuremap and revision)
41 : ///
42 : class DefaultServerCluster : public ServerClusterInterface
43 : {
44 : public:
45 11 : DefaultServerCluster(const ConcreteClusterPath & path) : mPath(path) {}
46 :
47 1427 : constexpr DefaultServerCluster(ConcreteClusterPath && path) :
48 2854 : mPath(std::move(path)),
49 1427 : mDataVersion(0) // data version will be initialized in startup, however constexpr requires initialization
50 1427 : {}
51 :
52 1453 : ~DefaultServerCluster() override = default;
53 :
54 : //////////////////////////// ServerClusterInterface implementation ////////////////////////////////////////
55 :
56 : /// Startup allows only a single initialization per cluster and will
57 : /// fail with CHIP_ERROR_ALREADY_INITIALIZED if the object has already
58 : /// been initialized.
59 : ///
60 : /// Call Shutdown to de-initialize the object.
61 : CHIP_ERROR Startup(ServerClusterContext & context) override;
62 : void Shutdown(ClusterShutdownType) override;
63 :
64 1720964 : [[nodiscard]] Span<const ConcreteClusterPath> GetPaths() const override { return { &mPath, 1 }; }
65 :
66 41 : [[nodiscard]] DataVersion GetDataVersion(const ConcreteClusterPath &) const override { return mDataVersion; }
67 : [[nodiscard]] BitFlags<DataModel::ClusterQualityFlags> GetClusterFlags(const ConcreteClusterPath &) const override;
68 :
69 : /// Default implementation errors out with an unsupported write on every attribute.
70 : DataModel::ActionReturnStatus WriteAttribute(const DataModel::WriteAttributeRequest & request,
71 : AttributeValueDecoder & decoder) override;
72 :
73 : /// Must only be implemented if support for any non-global attributes
74 : /// is required.
75 : ///
76 : /// Default implementation just returns the global attributes required by the API contract.
77 : CHIP_ERROR Attributes(const ConcreteClusterPath & path, ReadOnlyBufferBuilder<DataModel::AttributeEntry> & builder) override;
78 :
79 : /// Must only be implemented if event readability is relevant
80 3 : CHIP_ERROR EventInfo(const ConcreteEventPath & path, DataModel::EventEntry & eventInfo) override
81 : {
82 3 : eventInfo.readPrivilege = Access::Privilege::kView;
83 3 : return CHIP_NO_ERROR;
84 : }
85 :
86 : ///////////////////////////////////// Command Support /////////////////////////////////////////////////////////
87 :
88 : /// Must only be implemented if commands are supported by the cluster
89 : ///
90 : /// Default implementation errors out with an UnsupportedCommand error.
91 : std::optional<DataModel::ActionReturnStatus> InvokeCommand(const DataModel::InvokeRequest & request,
92 : chip::TLV::TLVReader & input_arguments,
93 : CommandHandler * handler) override;
94 :
95 : /// Must only be implemented if commands are supported by the cluster
96 : ///
97 : /// Default implementation is a NOOP (no list items generated)
98 : CHIP_ERROR AcceptedCommands(const ConcreteClusterPath & path,
99 : ReadOnlyBufferBuilder<DataModel::AcceptedCommandEntry> & builder) override;
100 :
101 : /// Must only be implemented if commands that return values are supported by the cluster.
102 : ///
103 : /// Default implementation is a NOOP (no list items generated)
104 : CHIP_ERROR GeneratedCommands(const ConcreteClusterPath & path, ReadOnlyBufferBuilder<CommandId> & builder) override;
105 :
106 : /// Returns all global attributes that the spec defines in `7.13 Global Elements / Table 93: Global Attributes`
107 : static Span<const DataModel::AttributeEntry> GlobalAttributes();
108 :
109 : protected:
110 : const ConcreteClusterPath mPath;
111 : ServerClusterContext * mContext = nullptr;
112 :
113 : bool IsStarted() const { return mContext != nullptr; }
114 :
115 2126 : void IncreaseDataVersion() { mDataVersion++; }
116 :
117 : /// Marks that a specific attribute has changed value
118 : ///
119 : /// This increases cluster data version and if a cluster context is available it will
120 : /// notify that the attribute has changed.
121 : void NotifyAttributeChanged(AttributeId attributeId);
122 :
123 : /// Apply the very common pattern of:
124 : /// - if a variable value needs changing, update and NotifyAttributeChanged
125 : ///
126 : /// Returns true if the value has been updated to a new value.
127 : template <typename T, typename U, typename = std::enable_if_t<std::is_convertible_v<U, T>>>
128 326 : bool SetAttributeValue(T & dest, const U & value, AttributeId attributeId)
129 : {
130 326 : VerifyOrReturnValue(dest != value, false);
131 221 : dest = value;
132 221 : NotifyAttributeChanged(attributeId);
133 221 : return true;
134 : }
135 :
136 : template <typename T>
137 2 : bool SetAttributeValue(DataModel::Nullable<T> & dest, decltype(DataModel::NullNullable), AttributeId attributeId)
138 : {
139 2 : VerifyOrReturnValue(!dest.IsNull(), false);
140 1 : dest.SetNull();
141 1 : NotifyAttributeChanged(attributeId);
142 1 : return true;
143 : }
144 :
145 : template <typename T, typename U, typename = std::enable_if_t<std::is_convertible_v<U, T>>>
146 4 : bool SetAttributeValue(DataModel::Nullable<T> & dest, const U & value, AttributeId attributeId)
147 : {
148 4 : VerifyOrReturnValue(dest != static_cast<T>(value), false);
149 3 : dest.SetNonNull(value);
150 3 : NotifyAttributeChanged(attributeId);
151 3 : return true;
152 : }
153 :
154 : /// Marks that a specific attribute has changed value, if `status` is success.
155 : ///
156 : /// Will return `status`
157 : DataModel::ActionReturnStatus NotifyAttributeChangedIfSuccess(AttributeId attributeId, DataModel::ActionReturnStatus status);
158 :
159 : private:
160 : DataVersion mDataVersion; // will be random-initialized as per spec
161 : };
162 :
163 : } // namespace app
164 : } // namespace chip
|