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-provider/AttributeChangeListener.h>
22 : #include <app/data-model/Nullable.h>
23 : #include <app/server-cluster/ServerClusterInterface.h>
24 : #include <lib/core/CHIPError.h>
25 :
26 : #include <optional>
27 : #include <type_traits>
28 : #include <utility>
29 :
30 : namespace chip {
31 : namespace app {
32 :
33 : /// Provides an implementation of most methods for a `ServerClusterInterface`
34 : /// to make it easier to implement spec-compliant classes.
35 : ///
36 : /// In particular it does:
37 : /// - handles a SINGLE cluster path that is set at construction time
38 : /// - maintains a data version and provides `IncreaseDataVersion`. Ensures this
39 : /// version is spec-compliant initialized (with a random value)
40 : /// - Provides default implementations for most virtual methods EXCEPT:
41 : /// - ReadAttribute (since that one needs to handle featuremap and revision)
42 : ///
43 : class DefaultServerCluster : public ServerClusterInterface
44 : {
45 : public:
46 11 : DefaultServerCluster(const ConcreteClusterPath & path) : mPath(path) {}
47 :
48 1556 : constexpr DefaultServerCluster(ConcreteClusterPath && path) :
49 3112 : mPath(std::move(path)),
50 1556 : mDataVersion(0) // data version will be initialized in startup, however constexpr requires initialization
51 1556 : {}
52 :
53 1582 : ~DefaultServerCluster() override = default;
54 :
55 : //////////////////////////// ServerClusterInterface implementation ////////////////////////////////////////
56 :
57 : /// Startup allows only a single initialization per cluster and will
58 : /// fail with CHIP_ERROR_ALREADY_INITIALIZED if the object has already
59 : /// been initialized.
60 : ///
61 : /// Call Shutdown to de-initialize the object.
62 : CHIP_ERROR Startup(ServerClusterContext & context) override;
63 : void Shutdown(ClusterShutdownType) override;
64 :
65 1721682 : [[nodiscard]] Span<const ConcreteClusterPath> GetPaths() const override { return { &mPath, 1 }; }
66 :
67 43 : [[nodiscard]] DataVersion GetDataVersion(const ConcreteClusterPath &) const override { return mDataVersion; }
68 : [[nodiscard]] BitFlags<DataModel::ClusterQualityFlags> GetClusterFlags(const ConcreteClusterPath &) const override;
69 :
70 : /// Default implementation errors out with an unsupported write on every attribute.
71 : DataModel::ActionReturnStatus WriteAttribute(const DataModel::WriteAttributeRequest & request,
72 : AttributeValueDecoder & decoder) override;
73 :
74 : /// Must only be implemented if support for any non-global attributes
75 : /// is required.
76 : ///
77 : /// Default implementation just returns the global attributes required by the API contract.
78 : CHIP_ERROR Attributes(const ConcreteClusterPath & path, ReadOnlyBufferBuilder<DataModel::AttributeEntry> & builder) override;
79 :
80 : /// Must only be implemented if event readability is relevant
81 3 : CHIP_ERROR EventInfo(const ConcreteEventPath & path, DataModel::EventEntry & eventInfo) override
82 : {
83 3 : eventInfo.readPrivilege = Access::Privilege::kView;
84 3 : return CHIP_NO_ERROR;
85 : }
86 :
87 : ///////////////////////////////////// Command Support /////////////////////////////////////////////////////////
88 :
89 : /// Must only be implemented if commands are supported by the cluster
90 : ///
91 : /// Default implementation errors out with an UnsupportedCommand error.
92 : std::optional<DataModel::ActionReturnStatus> InvokeCommand(const DataModel::InvokeRequest & request,
93 : chip::TLV::TLVReader & input_arguments,
94 : CommandHandler * handler) override;
95 :
96 : /// Must only be implemented if commands are supported by the cluster
97 : ///
98 : /// Default implementation is a NOOP (no list items generated)
99 : CHIP_ERROR AcceptedCommands(const ConcreteClusterPath & path,
100 : ReadOnlyBufferBuilder<DataModel::AcceptedCommandEntry> & builder) override;
101 :
102 : /// Must only be implemented if commands that return values are supported by the cluster.
103 : ///
104 : /// Default implementation is a NOOP (no list items generated)
105 : CHIP_ERROR GeneratedCommands(const ConcreteClusterPath & path, ReadOnlyBufferBuilder<CommandId> & builder) override;
106 :
107 : /// Returns all global attributes that the spec defines in `7.13 Global Elements / Table 93: Global Attributes`
108 : static Span<const DataModel::AttributeEntry> GlobalAttributes();
109 :
110 : protected:
111 : const ConcreteClusterPath mPath;
112 : ServerClusterContext * mContext = nullptr;
113 :
114 : bool IsStarted() const { return mContext != nullptr; }
115 :
116 2616 : void IncreaseDataVersion() { mDataVersion++; }
117 :
118 : /// Marks that a specific attribute has changed value
119 : ///
120 : /// This increases cluster data version and if a cluster context is available it will
121 : /// notify that the attribute has changed.
122 : void NotifyAttributeChanged(AttributeId attributeId,
123 : DataModel::AttributeChangeType type = DataModel::AttributeChangeType::kReportable);
124 :
125 : /// Apply the very common pattern of:
126 : /// - if a variable value needs changing, update and NotifyAttributeChanged
127 : ///
128 : /// Returns true if the value has been updated to a new value.
129 : template <typename T, typename U, typename = std::enable_if_t<std::is_convertible_v<U, T>>>
130 496 : bool SetAttributeValue(T & dest, const U & value, AttributeId attributeId,
131 : DataModel::AttributeChangeType type = DataModel::AttributeChangeType::kReportable)
132 : {
133 496 : VerifyOrReturnValue(dest != value, false);
134 387 : dest = value;
135 387 : NotifyAttributeChanged(attributeId, type);
136 387 : return true;
137 : }
138 :
139 : template <typename T>
140 2 : bool SetAttributeValue(DataModel::Nullable<T> & dest, decltype(DataModel::NullNullable), AttributeId attributeId,
141 : DataModel::AttributeChangeType type = DataModel::AttributeChangeType::kReportable)
142 : {
143 2 : VerifyOrReturnValue(!dest.IsNull(), false);
144 1 : dest.SetNull();
145 1 : NotifyAttributeChanged(attributeId, type);
146 1 : return true;
147 : }
148 :
149 : template <typename T, typename U, typename = std::enable_if_t<std::is_convertible_v<U, T>>>
150 4 : bool SetAttributeValue(DataModel::Nullable<T> & dest, const U & value, AttributeId attributeId,
151 : DataModel::AttributeChangeType type = DataModel::AttributeChangeType::kReportable)
152 : {
153 4 : VerifyOrReturnValue(dest != static_cast<T>(value), false);
154 3 : dest.SetNonNull(value);
155 3 : NotifyAttributeChanged(attributeId, type);
156 3 : return true;
157 : }
158 :
159 : /// Marks that a specific attribute has changed value, if `status` is success.
160 : ///
161 : /// Will return `status`
162 : DataModel::ActionReturnStatus NotifyAttributeChangedIfSuccess(AttributeId attributeId, DataModel::ActionReturnStatus status);
163 :
164 : private:
165 : DataVersion mDataVersion; // will be random-initialized as per spec
166 : };
167 :
168 : } // namespace app
169 : } // namespace chip
|