Matter SDK Coverage Report
Current view: top level - data-model-providers/codedriven - CodeDrivenDataModelProvider.cpp (source / functions) Coverage Total Hit
Test: SHA:48cc3057d373f5189d8404fd2bac3595e32b29b9 Lines: 95.4 % 153 146
Test Date: 2025-09-06 07:10:37 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,
     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
        

Generated by: LCOV version 2.0-1