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 <app/CommandHandlerInterface.h>
20 : #include <app/ConcreteCommandPath.h>
21 : #include <app/data-model-provider/ActionReturnStatus.h>
22 : #include <app/data-model-provider/MetadataTypes.h>
23 : #include <app/data-model-provider/Provider.h>
24 : #include <app/persistence/AttributePersistenceProvider.h>
25 : #include <app/server-cluster/ServerClusterInterface.h>
26 : #include <app/server-cluster/ServerClusterInterfaceRegistry.h>
27 : #include <data-model-providers/codedriven/endpoint/EndpointInterface.h>
28 : #include <data-model-providers/codedriven/endpoint/EndpointInterfaceRegistry.h>
29 : #include <lib/support/ReadOnlyBuffer.h>
30 :
31 : namespace chip {
32 : namespace app {
33 : /**
34 : * @brief An implementation of DataModel::Provider that constructs the data model
35 : * programmatically by aggregating EndpointInterface instances.
36 : *
37 : * This provider allows applications to define their Matter device data model (endpoints,
38 : * clusters, attributes, commands) dynamically at runtime. It manages a list of EndpointInterface
39 : * objects, each representing an endpoint on the device.
40 : *
41 : * The expected usage pattern by the application is as follows:
42 : * 1. Instantiate ServerClusterInterface(s) and ServerClusterRegistration(s).
43 : * 2. Instantiate EndpointInterface(s) and EndpointInterfaceRegistration(s).
44 : * 2. Instantiate the CodeDrivenDataModelProvider.
45 : * 3. Register ServerClusterInterfaceRegistration(s) to the CodeDrivenDataModelProvider using AddCluster().
46 : * 4. Register EndpointInterfaceRegistration(s) to the CodeDrivenDataModelProvider using AddEndpoint().
47 : * Note: Step 4 MUST come after Step 3 (Endpoint needs to know about its clusters).
48 : * 5. Call Startup() on the CodeDrivenDataModelProvider.
49 : *
50 : * Note: if the CodeDrivenDataModelProvider has already been started (runtime change to add/remove Endpoints/Clusters),
51 : * the Startup() method on each ServerClusterInterface will be called when the EndpointInterface is added (Step 4).
52 : * If the provider hasn't been started, the Startup() method will be called when the provider is started (Step 5).
53 : *
54 : * TODO: Notify composition changes when the provider is started up and endpoints are added/removed at runtime.
55 : * For now, applications are responsible for handling composition changes and calling markDirty() when needed.
56 : *
57 : * Lifecycle:
58 : * - The CodeDrivenDataModelProvider stores raw pointers to EndpointInterface and ServerClusterInterface.
59 : * It does NOT take ownership. Callers must ensure these instances outlive the provider.
60 : */
61 : class CodeDrivenDataModelProvider : public DataModel::Provider
62 : {
63 : public:
64 50 : CodeDrivenDataModelProvider(PersistentStorageDelegate & storage, AttributePersistenceProvider & attributeStorage) :
65 50 : mPersistentStorageDelegate(storage), mAttributePersistenceProvider(attributeStorage)
66 50 : {}
67 :
68 : /* DataModel::Provider implementation */
69 : CHIP_ERROR Startup(DataModel::InteractionModelContext context) override;
70 : CHIP_ERROR Shutdown() override;
71 :
72 : DataModel::ActionReturnStatus ReadAttribute(const DataModel::ReadAttributeRequest & request,
73 : AttributeValueEncoder & encoder) override;
74 : DataModel::ActionReturnStatus WriteAttribute(const DataModel::WriteAttributeRequest & request,
75 : AttributeValueDecoder & decoder) override;
76 :
77 : void ListAttributeWriteNotification(const ConcreteAttributePath & path, DataModel::ListWriteOperation opType) override;
78 : std::optional<DataModel::ActionReturnStatus> InvokeCommand(const DataModel::InvokeRequest & request,
79 : TLV::TLVReader & input_arguments, CommandHandler * handler) override;
80 :
81 : /* ProviderMetadataTree implementation */
82 : CHIP_ERROR Endpoints(ReadOnlyBufferBuilder<DataModel::EndpointEntry> & out) override;
83 : CHIP_ERROR SemanticTags(EndpointId endpointId, ReadOnlyBufferBuilder<SemanticTag> & out) override;
84 : CHIP_ERROR DeviceTypes(EndpointId endpointId, ReadOnlyBufferBuilder<DataModel::DeviceTypeEntry> & out) override;
85 : CHIP_ERROR ClientClusters(EndpointId endpointId, ReadOnlyBufferBuilder<ClusterId> & out) override;
86 : CHIP_ERROR ServerClusters(EndpointId endpointId, ReadOnlyBufferBuilder<DataModel::ServerClusterEntry> & out) override;
87 : CHIP_ERROR GeneratedCommands(const ConcreteClusterPath & path, ReadOnlyBufferBuilder<CommandId> & out) override;
88 : CHIP_ERROR AcceptedCommands(const ConcreteClusterPath & path,
89 : ReadOnlyBufferBuilder<DataModel::AcceptedCommandEntry> & out) override;
90 : CHIP_ERROR Attributes(const ConcreteClusterPath & path, ReadOnlyBufferBuilder<DataModel::AttributeEntry> & builder) override;
91 : CHIP_ERROR EventInfo(const ConcreteEventPath & path, DataModel::EventEntry & eventInfo) override;
92 : void Temporary_ReportAttributeChanged(const AttributePathParams & path) override;
93 :
94 : /**
95 : * @brief Adds an endpoint to the data model provider.
96 : *
97 : * This method registers an endpoint, making it part of the device's data model.
98 : * If the provider has already been started, this may trigger a Startup() call on
99 : * each ServerClusterInterface associated with the endpoint.
100 : * The Startup() call on the associated clusters will ONLY happen if this is the first
101 : * endpoint associated with the cluster (i.e. ServerClusterInterface.GetPaths() returns
102 : * at least one path with endpoint ID == registration.endpointEntry.id, and none
103 : * of the other endpoints in GetPaths() are registered yet). This ensures
104 : * the cluster is only started once, even if it is associated with multiple endpoints.
105 : *
106 : * Prerequisites:
107 : * - It MUST be called after all clusters for the endpoint have been registered with
108 : * AddCluster().
109 : * - The provided `registration` (EndpointInterfaceRegistration) must not already be
110 : * part of another list (i.e., `registration.next` must be nullptr).
111 : * - The EndpointInterface within the `registration` must be valid (i.e.,
112 : * `registration.endpointInterface` must not be nullptr).
113 : * - The `registration.endpointEntry.id` must be valid (not `kInvalidEndpointId` and
114 : * not used by another endpoint).
115 : * - The LIFETIME of `registration` must outlive the provider (or the registration must
116 : * be removed using `RemoveEndpoint` before it goes away).
117 : *
118 : * @param registration The registration object for the endpoint, containing a valid
119 : * EndpointInterface and Endpoint ID.
120 : * @return CHIP_NO_ERROR on success.
121 : * CHIP_ERROR_INVALID_ARGUMENT if `registration.next` is not nullptr or
122 : * `registration.endpointInterface` is nullptr or
123 : * `registration.endpointEntry.id` is kInvalidEndpointId.
124 : * CHIP_ERROR_DUPLICATE_KEY_ID if `registration.endpointEntry.id` is already in use.
125 : */
126 : CHIP_ERROR AddEndpoint(EndpointInterfaceRegistration & registration);
127 :
128 : /**
129 : * @brief Removes an endpoint from the data model provider.
130 : *
131 : * This method unregisters an endpoint, removing it from the device's data model.
132 : * If the provider has already been started, this might trigger a Shutdown() call on
133 : * each ServerClusterInterface associated with the endpoint.
134 : * The Shutdown() call on the associated clusters will ONLY happen if this is the last
135 : * endpoint associated with the cluster (i.e. ServerClusterInterface.GetPaths() returns
136 : * no paths with valid Endpoint IDs).
137 : *
138 : * Note: Removing an Endpoint does not remove any clusters associated with the endpoint.
139 : * Those can be removed using RemoveCluster() AFTER the endpoint has been removed.
140 :
141 : * Prerequisites:
142 : * - It MUST be called BEFORE removing associted clusters with RemoveCluster() to guarantee
143 : * the endpoint removal is atomic.
144 : * - The endpoint ID must be valid.
145 : *
146 : * @param endpointId The ID of the endpoint to remove.
147 : * @return CHIP_NO_ERROR on success.
148 : * CHIP_ERROR_NOT_FOUND if no endpoint with the given ID is registered.
149 : * CHIP_ERROR_INVALID_ARGUMENT if endpointId is kInvalidEndpointId.
150 : */
151 : CHIP_ERROR RemoveEndpoint(EndpointId endpointId);
152 :
153 : /**
154 : * @brief Add a ServerClusterInterface to the Data Model Provider.
155 : *
156 : * Requirements:
157 : * - entry MUST NOT be part of any other registration
158 : * - paths MUST NOT be part of any other ServerClusterInterface (i.e. only a single
159 : * registration for a given `endpointId/clusterId` path).
160 : * - The LIFETIME of entry must outlive the provider (or the entry must be unregistered
161 : * via RemoveCluster before it goes away).
162 : * - If the provider has already been started, this method must be called prior to
163 : * calling AddEndpoint() (i.e. the `endpointId` of all paths in `entry.GetPaths()`
164 : * must NOT be registered in the provider yet), otherwise this would cause non-atomic
165 : * changes to an endpoint, which is not allowed.
166 : *
167 : * @param entry The ServerClusterRegistration containing the cluster to register.
168 : * @return CHIP_NO_ERROR on success.
169 : * CHIP_ERROR_INVALID_ARGUMENT if `entry.next` is not `nullptr` or
170 : * `entry.serverClusterInterface` is `nullptr` or
171 : * `entry.serverClusterInterface.GetPaths()` is empty/invalid.
172 : * CHIP_ERROR_DUPLICATE_KEY_ID if the cluster is already registered.
173 : * CHIP_ERROR_INCORRECT_STATE if the provider has been started and an endpoint for
174 : * one of the cluster paths has already been registered.
175 : */
176 : CHIP_ERROR AddCluster(ServerClusterRegistration & entry);
177 :
178 : /**
179 : * @brief Remove a ServerClusterInterface from the Data Model Provider.
180 : *
181 : * To avoid violating the requirement of non-atomic changes to endpoints, this SHALL only be
182 : * called after all endpoints associated with the cluster have been removed using RemoveEndpoint().
183 : *
184 : * Requirements:
185 : * - entry MUST be valid
186 : * - The `endpointId` of all paths in `entry.GetPaths()` are no longer registered in the provider.
187 : *
188 : * @param entry The ServerClusterInterface to remove.
189 : * @return CHIP_NO_ERROR on success.
190 : * CHIP_ERROR_INVALID_ARGUMENT if `entry` is nullptr.
191 : * CHIP_ERROR_NOT_FOUND if the entry is not registered.
192 : * CHIP_ERROR_INCORRECT_STATE if an endpoint for one of the cluster paths is still registered.
193 : */
194 : CHIP_ERROR RemoveCluster(ServerClusterInterface * entry);
195 :
196 : private:
197 : EndpointInterfaceRegistry mEndpointInterfaceRegistry;
198 : ServerClusterInterfaceRegistry mServerClusterRegistry;
199 : std::optional<ServerClusterContext> mServerClusterContext;
200 : std::optional<DataModel::InteractionModelContext> mInteractionModelContext;
201 : PersistentStorageDelegate & mPersistentStorageDelegate;
202 : AttributePersistenceProvider & mAttributePersistenceProvider;
203 :
204 : /// Return the interface registered for the given endpoint ID or nullptr if one does not exist
205 : EndpointInterface * GetEndpointInterface(EndpointId endpointId);
206 :
207 : /// Return the interface registered for the given cluster path or nullptr if one does not exist
208 : ServerClusterInterface * GetServerClusterInterface(const ConcreteClusterPath & path);
209 : };
210 :
211 : } // namespace app
212 : } // namespace chip
|