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