Matter SDK Coverage Report
Current view: top level - data-model-providers/codedriven - CodeDrivenDataModelProvider.cpp (source / functions) Coverage Total Hit
Test: SHA:3108862db59e5fa02f4a254cea1d5089c60155eb Lines: 95.4 % 153 146
Test Date: 2025-10-12 07:08:15 Functions: 100.0 % 22 22

            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, ReadOnlyBufferBuilder<SemanticTag> & out)
     162              : {
     163            1 :     EndpointInterface * endpoint = GetEndpointInterface(endpointId);
     164            1 :     VerifyOrReturnError(endpoint != nullptr, CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint));
     165            1 :     return endpoint->SemanticTags(out);
     166              : }
     167              : 
     168            1 : CHIP_ERROR CodeDrivenDataModelProvider::DeviceTypes(EndpointId endpointId, ReadOnlyBufferBuilder<DataModel::DeviceTypeEntry> & out)
     169              : {
     170            1 :     EndpointInterface * endpoint = GetEndpointInterface(endpointId);
     171            1 :     VerifyOrReturnError(endpoint != nullptr, CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint));
     172            1 :     return endpoint->DeviceTypes(out);
     173              : }
     174              : 
     175            1 : CHIP_ERROR CodeDrivenDataModelProvider::ClientClusters(EndpointId endpointId, ReadOnlyBufferBuilder<ClusterId> & out)
     176              : {
     177            1 :     EndpointInterface * endpoint = GetEndpointInterface(endpointId);
     178            1 :     VerifyOrReturnError(endpoint != nullptr, CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint));
     179            1 :     return endpoint->ClientClusters(out);
     180              : }
     181              : 
     182            5 : CHIP_ERROR CodeDrivenDataModelProvider::ServerClusters(EndpointId endpointId,
     183              :                                                        ReadOnlyBufferBuilder<DataModel::ServerClusterEntry> & out)
     184              : {
     185            5 :     EndpointInterface * endpoint = GetEndpointInterface(endpointId);
     186            5 :     VerifyOrReturnError(endpoint != nullptr, CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint));
     187              : 
     188            4 :     size_t count = 0;
     189           10 :     for (auto * cluster : mServerClusterRegistry.AllServerClusterInstances())
     190              :     {
     191           15 :         for (const auto & path : cluster->GetPaths())
     192              :         {
     193            9 :             if (path.mEndpointId == endpointId)
     194              :             {
     195            7 :                 count++;
     196              :             }
     197              :         }
     198              :     }
     199              : 
     200            4 :     ReturnErrorOnFailure(out.EnsureAppendCapacity(count));
     201              : 
     202           10 :     for (auto * cluster : mServerClusterRegistry.AllServerClusterInstances())
     203              :     {
     204           15 :         for (const auto & path : cluster->GetPaths())
     205              :         {
     206            9 :             if (path.mEndpointId == endpointId)
     207              :             {
     208            7 :                 ReturnErrorOnFailure(
     209              :                     out.Append({ path.mClusterId, cluster->GetDataVersion(path), cluster->GetClusterFlags(path) }));
     210              :             }
     211              :         }
     212              :     }
     213            4 :     return CHIP_NO_ERROR;
     214              : }
     215              : 
     216            1 : CHIP_ERROR CodeDrivenDataModelProvider::GeneratedCommands(const ConcreteClusterPath & path, ReadOnlyBufferBuilder<CommandId> & out)
     217              : {
     218            1 :     ServerClusterInterface * serverCluster = GetServerClusterInterface(path);
     219            1 :     VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
     220            1 :     return serverCluster->GeneratedCommands(path, out);
     221              : }
     222            1 : CHIP_ERROR CodeDrivenDataModelProvider::AcceptedCommands(const ConcreteClusterPath & path,
     223              :                                                          ReadOnlyBufferBuilder<DataModel::AcceptedCommandEntry> & out)
     224              : {
     225            1 :     ServerClusterInterface * serverCluster = GetServerClusterInterface(path);
     226            1 :     VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
     227            1 :     return serverCluster->AcceptedCommands(path, out);
     228              : }
     229              : 
     230            1 : CHIP_ERROR CodeDrivenDataModelProvider::Attributes(const ConcreteClusterPath & path,
     231              :                                                    ReadOnlyBufferBuilder<DataModel::AttributeEntry> & out)
     232              : {
     233            1 :     ServerClusterInterface * serverCluster = GetServerClusterInterface(path);
     234            1 :     VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
     235            1 :     return serverCluster->Attributes(path, out);
     236              : }
     237              : 
     238            1 : CHIP_ERROR CodeDrivenDataModelProvider::EventInfo(const ConcreteEventPath & path, DataModel::EventEntry & eventInfo)
     239              : {
     240            1 :     ServerClusterInterface * serverCluster = GetServerClusterInterface(path);
     241            1 :     VerifyOrReturnError(serverCluster != nullptr, CHIP_ERROR_KEY_NOT_FOUND);
     242            1 :     return serverCluster->EventInfo(path, eventInfo);
     243              : }
     244              : 
     245            1 : void CodeDrivenDataModelProvider::Temporary_ReportAttributeChanged(const AttributePathParams & path)
     246              : {
     247            1 :     if (!mInteractionModelContext)
     248              :     {
     249            0 :         ChipLogError(DataManagement, "Temporary_ReportAttributeChanged called before provider has been started.");
     250            0 :         return;
     251              :     }
     252            1 :     mInteractionModelContext->dataModelChangeListener.MarkDirty(path);
     253              : }
     254              : 
     255           43 : CHIP_ERROR CodeDrivenDataModelProvider::AddEndpoint(EndpointInterfaceRegistration & registration)
     256              : {
     257           43 :     VerifyOrReturnError(registration.endpointEntry.id != kInvalidEndpointId, CHIP_ERROR_INVALID_ARGUMENT);
     258              : 
     259              :     // If the endpoint ID is already in use, return an error.
     260           42 :     if (mEndpointInterfaceRegistry.Get(registration.endpointEntry.id) != nullptr)
     261              :     {
     262            1 :         return CHIP_ERROR_DUPLICATE_KEY_ID;
     263              :     }
     264              : 
     265           41 :     ReturnErrorOnFailure(mEndpointInterfaceRegistry.Register(registration));
     266              : 
     267           41 :     if (mServerClusterContext.has_value())
     268              :     {
     269              :         // If the provider has been started, we need to check if any clusters on this new endpoint
     270              :         // should be started up.
     271           61 :         for (auto * cluster : mServerClusterRegistry.AllServerClusterInstances())
     272              :         {
     273           23 :             bool clusterIsOnNewEndpoint = false;
     274           23 :             int registeredEndpointCount = 0;
     275              : 
     276           53 :             for (const auto & path : cluster->GetPaths())
     277              :             {
     278           30 :                 if (mEndpointInterfaceRegistry.Get(path.mEndpointId) != nullptr)
     279              :                 {
     280           27 :                     registeredEndpointCount++;
     281              :                 }
     282           30 :                 if (path.mEndpointId == registration.endpointEntry.id)
     283              :                 {
     284           24 :                     clusterIsOnNewEndpoint = true;
     285              :                 }
     286              :             }
     287              : 
     288              :             // If the cluster is on the endpoint we just added, and this is the *only*
     289              :             // registered endpoint for this cluster, it's time to start it.
     290           23 :             if (clusterIsOnNewEndpoint && registeredEndpointCount == 1)
     291              :             {
     292           19 :                 ReturnErrorOnFailure(cluster->Startup(*mServerClusterContext));
     293              :             }
     294              :         }
     295              :     }
     296              : 
     297           41 :     return CHIP_NO_ERROR;
     298              : }
     299              : 
     300           42 : CHIP_ERROR CodeDrivenDataModelProvider::RemoveEndpoint(EndpointId endpointId)
     301              : {
     302           42 :     if (mServerClusterContext.has_value())
     303              :     {
     304              :         // If the provider has been started, we need to check if any clusters on this endpoint
     305              :         // need to be shut down because it's their last registered endpoint.
     306           67 :         for (auto * cluster : mServerClusterRegistry.AllServerClusterInstances())
     307              :         {
     308           26 :             bool clusterIsOnEndpoint    = false;
     309           26 :             int registeredEndpointCount = 0;
     310              : 
     311           59 :             for (const auto & path : cluster->GetPaths())
     312              :             {
     313           33 :                 if (mEndpointInterfaceRegistry.Get(path.mEndpointId) != nullptr)
     314              :                 {
     315           29 :                     registeredEndpointCount++;
     316              :                 }
     317           33 :                 if (path.mEndpointId == endpointId)
     318              :                 {
     319           26 :                     clusterIsOnEndpoint = true;
     320              :                 }
     321              :             }
     322              : 
     323           26 :             if (clusterIsOnEndpoint && registeredEndpointCount == 1)
     324              :             {
     325              :                 // This is the last registered endpoint for this cluster. Shut it down.
     326           21 :                 cluster->Shutdown();
     327              :             }
     328              :         }
     329              :     }
     330              : 
     331           42 :     return mEndpointInterfaceRegistry.Unregister(endpointId);
     332              : }
     333              : 
     334           27 : CHIP_ERROR CodeDrivenDataModelProvider::AddCluster(ServerClusterRegistration & entry)
     335              : {
     336           27 :     VerifyOrReturnError(entry.serverClusterInterface != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     337              : 
     338           27 :     if (mServerClusterContext.has_value())
     339              :     {
     340              :         // If the provider has been started, prevent non-atomic changes to an endpoint.
     341              :         // Check if any of the cluster's paths are associated with an already registered endpoint.
     342           48 :         for (const auto & path : entry.serverClusterInterface->GetPaths())
     343              :         {
     344           26 :             if (mEndpointInterfaceRegistry.Get(path.mEndpointId) != nullptr)
     345              :             {
     346            1 :                 return CHIP_ERROR_INCORRECT_STATE;
     347              :             }
     348              :         }
     349              :     }
     350              : 
     351           26 :     return mServerClusterRegistry.Register(entry);
     352              : }
     353              : 
     354            4 : CHIP_ERROR CodeDrivenDataModelProvider::RemoveCluster(ServerClusterInterface * cluster)
     355              : {
     356            4 :     VerifyOrReturnError(cluster != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
     357              : 
     358            4 :     if (mServerClusterContext.has_value())
     359              :     {
     360            5 :         for (const auto & path : cluster->GetPaths())
     361              :         {
     362            3 :             if (mEndpointInterfaceRegistry.Get(path.mEndpointId) != nullptr)
     363              :             {
     364            1 :                 return CHIP_ERROR_INCORRECT_STATE;
     365              :             }
     366              :         }
     367              :     }
     368              : 
     369            3 :     return mServerClusterRegistry.Unregister(cluster);
     370              : }
     371              : 
     372            8 : EndpointInterface * CodeDrivenDataModelProvider::GetEndpointInterface(EndpointId endpointId)
     373              : {
     374            8 :     return mEndpointInterfaceRegistry.Get(endpointId);
     375              : }
     376              : 
     377           15 : ServerClusterInterface * CodeDrivenDataModelProvider::GetServerClusterInterface(const ConcreteClusterPath & clusterPath)
     378              : {
     379           15 :     return mServerClusterRegistry.Get(clusterPath);
     380              : }
     381              : 
     382              : } // namespace app
     383              : } // namespace chip
        

Generated by: LCOV version 2.0-1