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 : #include "CodeDrivenDataModelProvider.h"
18 : #include <app/persistence/AttributePersistenceProvider.h>
19 : #include <app/server-cluster/ServerClusterContext.h>
20 : #include <app/server-cluster/ServerClusterInterface.h>
21 : #include <data-model-providers/codedriven/endpoint/EndpointInterface.h>
22 : #include <lib/core/CHIPError.h>
23 : #include <lib/support/CodeUtils.h>
24 : #include <lib/support/logging/CHIPLogging.h>
25 : #include <protocols/interaction_model/StatusCode.h>
26 :
27 : using chip::Protocols::InteractionModel::Status;
28 :
29 : namespace chip {
30 : namespace app {
31 :
32 49 : CHIP_ERROR CodeDrivenDataModelProvider::Startup(DataModel::InteractionModelContext context)
33 : {
34 49 : ReturnErrorOnFailure(DataModel::Provider::Startup(context));
35 :
36 49 : mInteractionModelContext.emplace(context);
37 :
38 49 : mServerClusterContext.emplace(ServerClusterContext{
39 : .provider = *this,
40 49 : .storage = mPersistentStorageDelegate,
41 49 : .attributeStorage = mAttributePersistenceProvider,
42 49 : .interactionContext = *mInteractionModelContext,
43 : });
44 :
45 : // Start up registered server clusters if one of their associated endpoints is registered.
46 49 : bool had_failure = false;
47 52 : for (auto * cluster : mServerClusterRegistry.AllServerClusterInstances())
48 : {
49 3 : bool endpointRegistered = false;
50 4 : for (const auto & path : cluster->GetPaths())
51 : {
52 3 : if (mEndpointInterfaceRegistry.Get(path.mEndpointId) != nullptr)
53 : {
54 2 : endpointRegistered = true;
55 2 : break;
56 : }
57 : }
58 :
59 3 : if (endpointRegistered)
60 : {
61 2 : if (cluster->Startup(*mServerClusterContext) != CHIP_NO_ERROR)
62 : {
63 0 : had_failure = true;
64 : }
65 : }
66 : }
67 :
68 49 : if (had_failure)
69 : {
70 0 : return CHIP_ERROR_HAD_FAILURES;
71 : }
72 :
73 49 : return CHIP_NO_ERROR;
74 : }
75 :
76 49 : CHIP_ERROR CodeDrivenDataModelProvider::Shutdown()
77 : {
78 49 : bool had_failure = false;
79 :
80 : // Remove all endpoints. This will trigger Shutdown() on associated clusters.
81 82 : while (mEndpointInterfaceRegistry.begin() != mEndpointInterfaceRegistry.end())
82 : {
83 33 : if (RemoveEndpoint(mEndpointInterfaceRegistry.begin()->GetEndpointEntry().id) != CHIP_NO_ERROR)
84 : {
85 0 : had_failure = true;
86 : }
87 : }
88 :
89 : // Now we're safe to clean up the cluster registry.
90 71 : while (mServerClusterRegistry.AllServerClusterInstances().begin() != mServerClusterRegistry.AllServerClusterInstances().end())
91 : {
92 22 : ServerClusterInterface * clusterToRemove = *mServerClusterRegistry.AllServerClusterInstances().begin();
93 22 : if (mServerClusterRegistry.Unregister(clusterToRemove) != CHIP_NO_ERROR)
94 : {
95 0 : had_failure = true;
96 : }
97 : }
98 :
99 49 : mServerClusterContext.reset();
100 49 : mInteractionModelContext.reset();
101 :
102 49 : if (had_failure)
103 : {
104 0 : return CHIP_ERROR_HAD_FAILURES;
105 : }
106 49 : return CHIP_NO_ERROR;
107 : }
108 :
109 4 : DataModel::ActionReturnStatus CodeDrivenDataModelProvider::ReadAttribute(const DataModel::ReadAttributeRequest & request,
110 : AttributeValueEncoder & encoder)
111 : {
112 4 : ServerClusterInterface * serverCluster = GetServerClusterInterface(request.path);
113 4 : VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
114 2 : return serverCluster->ReadAttribute(request, encoder);
115 : }
116 :
117 3 : DataModel::ActionReturnStatus CodeDrivenDataModelProvider::WriteAttribute(const DataModel::WriteAttributeRequest & request,
118 : AttributeValueDecoder & decoder)
119 : {
120 3 : ServerClusterInterface * serverCluster = GetServerClusterInterface(request.path);
121 3 : VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
122 1 : return serverCluster->WriteAttribute(request, decoder);
123 : }
124 :
125 1 : void CodeDrivenDataModelProvider::ListAttributeWriteNotification(const ConcreteAttributePath & path,
126 : DataModel::ListWriteOperation opType)
127 : {
128 1 : ServerClusterInterface * serverCluster = GetServerClusterInterface(path);
129 1 : VerifyOrReturn(serverCluster != nullptr);
130 1 : serverCluster->ListAttributeWriteNotification(path, opType);
131 : }
132 :
133 3 : std::optional<DataModel::ActionReturnStatus> CodeDrivenDataModelProvider::InvokeCommand(const DataModel::InvokeRequest & request,
134 : TLV::TLVReader & input_arguments,
135 : CommandHandler * handler)
136 : {
137 3 : ServerClusterInterface * serverCluster = GetServerClusterInterface(request.path);
138 3 : VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
139 1 : return serverCluster->InvokeCommand(request, input_arguments, handler);
140 : }
141 :
142 3 : CHIP_ERROR CodeDrivenDataModelProvider::Endpoints(ReadOnlyBufferBuilder<DataModel::EndpointEntry> & out)
143 : {
144 : // TODO: Add a size() method to EndpointInterfaceRegistry to avoid iterating twice.
145 3 : size_t count = 0;
146 8 : for (const auto & registration : mEndpointInterfaceRegistry)
147 : {
148 : (void) registration; // Silence unused variable warning
149 5 : count++;
150 : }
151 :
152 3 : ReturnErrorOnFailure(out.EnsureAppendCapacity(count));
153 8 : for (const auto & registration : mEndpointInterfaceRegistry)
154 : {
155 5 : ReturnErrorOnFailure(out.Append(registration.GetEndpointEntry()));
156 : }
157 3 : return CHIP_NO_ERROR;
158 : }
159 :
160 : CHIP_ERROR
161 1 : CodeDrivenDataModelProvider::SemanticTags(EndpointId endpointId,
162 : ReadOnlyBufferBuilder<Clusters::Descriptor::Structs::SemanticTagStruct::Type> & out)
163 : {
164 1 : EndpointInterface * endpoint = GetEndpointInterface(endpointId);
165 1 : VerifyOrReturnError(endpoint != nullptr, CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint));
166 1 : return endpoint->SemanticTags(out);
167 : }
168 :
169 1 : CHIP_ERROR CodeDrivenDataModelProvider::DeviceTypes(EndpointId endpointId, ReadOnlyBufferBuilder<DataModel::DeviceTypeEntry> & out)
170 : {
171 1 : EndpointInterface * endpoint = GetEndpointInterface(endpointId);
172 1 : VerifyOrReturnError(endpoint != nullptr, CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint));
173 1 : return endpoint->DeviceTypes(out);
174 : }
175 :
176 1 : CHIP_ERROR CodeDrivenDataModelProvider::ClientClusters(EndpointId endpointId, ReadOnlyBufferBuilder<ClusterId> & out)
177 : {
178 1 : EndpointInterface * endpoint = GetEndpointInterface(endpointId);
179 1 : VerifyOrReturnError(endpoint != nullptr, CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint));
180 1 : return endpoint->ClientClusters(out);
181 : }
182 :
183 5 : CHIP_ERROR CodeDrivenDataModelProvider::ServerClusters(EndpointId endpointId,
184 : ReadOnlyBufferBuilder<DataModel::ServerClusterEntry> & out)
185 : {
186 5 : EndpointInterface * endpoint = GetEndpointInterface(endpointId);
187 5 : VerifyOrReturnError(endpoint != nullptr, CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint));
188 :
189 4 : size_t count = 0;
190 10 : for (auto * cluster : mServerClusterRegistry.AllServerClusterInstances())
191 : {
192 15 : for (const auto & path : cluster->GetPaths())
193 : {
194 9 : if (path.mEndpointId == endpointId)
195 : {
196 7 : count++;
197 : }
198 : }
199 : }
200 :
201 4 : ReturnErrorOnFailure(out.EnsureAppendCapacity(count));
202 :
203 10 : for (auto * cluster : mServerClusterRegistry.AllServerClusterInstances())
204 : {
205 15 : for (const auto & path : cluster->GetPaths())
206 : {
207 9 : if (path.mEndpointId == endpointId)
208 : {
209 7 : ReturnErrorOnFailure(
210 : out.Append({ path.mClusterId, cluster->GetDataVersion(path), cluster->GetClusterFlags(path) }));
211 : }
212 : }
213 : }
214 4 : return CHIP_NO_ERROR;
215 : }
216 :
217 1 : CHIP_ERROR CodeDrivenDataModelProvider::GeneratedCommands(const ConcreteClusterPath & path, ReadOnlyBufferBuilder<CommandId> & out)
218 : {
219 1 : ServerClusterInterface * serverCluster = GetServerClusterInterface(path);
220 1 : VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
221 1 : return serverCluster->GeneratedCommands(path, out);
222 : }
223 1 : CHIP_ERROR CodeDrivenDataModelProvider::AcceptedCommands(const ConcreteClusterPath & path,
224 : ReadOnlyBufferBuilder<DataModel::AcceptedCommandEntry> & out)
225 : {
226 1 : ServerClusterInterface * serverCluster = GetServerClusterInterface(path);
227 1 : VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
228 1 : return serverCluster->AcceptedCommands(path, out);
229 : }
230 :
231 1 : CHIP_ERROR CodeDrivenDataModelProvider::Attributes(const ConcreteClusterPath & path,
232 : ReadOnlyBufferBuilder<DataModel::AttributeEntry> & out)
233 : {
234 1 : ServerClusterInterface * serverCluster = GetServerClusterInterface(path);
235 1 : VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
236 1 : return serverCluster->Attributes(path, out);
237 : }
238 :
239 1 : CHIP_ERROR CodeDrivenDataModelProvider::EventInfo(const ConcreteEventPath & path, DataModel::EventEntry & eventInfo)
240 : {
241 1 : ServerClusterInterface * serverCluster = GetServerClusterInterface(path);
242 1 : VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
243 1 : return serverCluster->EventInfo(path, eventInfo);
244 : }
245 :
246 1 : void CodeDrivenDataModelProvider::Temporary_ReportAttributeChanged(const AttributePathParams & path)
247 : {
248 1 : if (!mInteractionModelContext)
249 : {
250 0 : ChipLogError(DataManagement, "Temporary_ReportAttributeChanged called before provider has been started.");
251 0 : return;
252 : }
253 1 : mInteractionModelContext->dataModelChangeListener.MarkDirty(path);
254 : }
255 :
256 43 : CHIP_ERROR CodeDrivenDataModelProvider::AddEndpoint(EndpointInterfaceRegistration & registration)
257 : {
258 43 : VerifyOrReturnError(registration.endpointEntry.id != kInvalidEndpointId, CHIP_ERROR_INVALID_ARGUMENT);
259 :
260 : // If the endpoint ID is already in use, return an error.
261 42 : if (mEndpointInterfaceRegistry.Get(registration.endpointEntry.id) != nullptr)
262 : {
263 1 : return CHIP_ERROR_DUPLICATE_KEY_ID;
264 : }
265 :
266 41 : ReturnErrorOnFailure(mEndpointInterfaceRegistry.Register(registration));
267 :
268 41 : if (mServerClusterContext.has_value())
269 : {
270 : // If the provider has been started, we need to check if any clusters on this new endpoint
271 : // should be started up.
272 61 : for (auto * cluster : mServerClusterRegistry.AllServerClusterInstances())
273 : {
274 23 : bool clusterIsOnNewEndpoint = false;
275 23 : int registeredEndpointCount = 0;
276 :
277 53 : for (const auto & path : cluster->GetPaths())
278 : {
279 30 : if (mEndpointInterfaceRegistry.Get(path.mEndpointId) != nullptr)
280 : {
281 27 : registeredEndpointCount++;
282 : }
283 30 : if (path.mEndpointId == registration.endpointEntry.id)
284 : {
285 24 : clusterIsOnNewEndpoint = true;
286 : }
287 : }
288 :
289 : // If the cluster is on the endpoint we just added, and this is the *only*
290 : // registered endpoint for this cluster, it's time to start it.
291 23 : if (clusterIsOnNewEndpoint && registeredEndpointCount == 1)
292 : {
293 19 : ReturnErrorOnFailure(cluster->Startup(*mServerClusterContext));
294 : }
295 : }
296 : }
297 :
298 41 : return CHIP_NO_ERROR;
299 : }
300 :
301 42 : CHIP_ERROR CodeDrivenDataModelProvider::RemoveEndpoint(EndpointId endpointId)
302 : {
303 42 : if (mServerClusterContext.has_value())
304 : {
305 : // If the provider has been started, we need to check if any clusters on this endpoint
306 : // need to be shut down because it's their last registered endpoint.
307 67 : for (auto * cluster : mServerClusterRegistry.AllServerClusterInstances())
308 : {
309 26 : bool clusterIsOnEndpoint = false;
310 26 : int registeredEndpointCount = 0;
311 :
312 59 : for (const auto & path : cluster->GetPaths())
313 : {
314 33 : if (mEndpointInterfaceRegistry.Get(path.mEndpointId) != nullptr)
315 : {
316 29 : registeredEndpointCount++;
317 : }
318 33 : if (path.mEndpointId == endpointId)
319 : {
320 26 : clusterIsOnEndpoint = true;
321 : }
322 : }
323 :
324 26 : if (clusterIsOnEndpoint && registeredEndpointCount == 1)
325 : {
326 : // This is the last registered endpoint for this cluster. Shut it down.
327 21 : cluster->Shutdown();
328 : }
329 : }
330 : }
331 :
332 42 : return mEndpointInterfaceRegistry.Unregister(endpointId);
333 : }
334 :
335 27 : CHIP_ERROR CodeDrivenDataModelProvider::AddCluster(ServerClusterRegistration & entry)
336 : {
337 27 : VerifyOrReturnError(entry.serverClusterInterface != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
338 :
339 27 : if (mServerClusterContext.has_value())
340 : {
341 : // If the provider has been started, prevent non-atomic changes to an endpoint.
342 : // Check if any of the cluster's paths are associated with an already registered endpoint.
343 48 : for (const auto & path : entry.serverClusterInterface->GetPaths())
344 : {
345 26 : if (mEndpointInterfaceRegistry.Get(path.mEndpointId) != nullptr)
346 : {
347 1 : return CHIP_ERROR_INCORRECT_STATE;
348 : }
349 : }
350 : }
351 :
352 26 : return mServerClusterRegistry.Register(entry);
353 : }
354 :
355 4 : CHIP_ERROR CodeDrivenDataModelProvider::RemoveCluster(ServerClusterInterface * cluster)
356 : {
357 4 : VerifyOrReturnError(cluster != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
358 :
359 4 : if (mServerClusterContext.has_value())
360 : {
361 5 : for (const auto & path : cluster->GetPaths())
362 : {
363 3 : if (mEndpointInterfaceRegistry.Get(path.mEndpointId) != nullptr)
364 : {
365 1 : return CHIP_ERROR_INCORRECT_STATE;
366 : }
367 : }
368 : }
369 :
370 3 : return mServerClusterRegistry.Unregister(cluster);
371 : }
372 :
373 8 : EndpointInterface * CodeDrivenDataModelProvider::GetEndpointInterface(EndpointId endpointId)
374 : {
375 8 : return mEndpointInterfaceRegistry.Get(endpointId);
376 : }
377 :
378 15 : ServerClusterInterface * CodeDrivenDataModelProvider::GetServerClusterInterface(const ConcreteClusterPath & clusterPath)
379 : {
380 15 : return mServerClusterRegistry.Get(clusterPath);
381 : }
382 :
383 : } // namespace app
384 : } // namespace chip
|