Matter SDK Coverage Report
Current view: top level - app - CommandHandlerInterface.h (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 100.0 % 11 11
Test Date: 2025-01-17 19:00:11 Functions: 100.0 % 6 6

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2021 Project CHIP Authors
       4              :  *    All rights reserved.
       5              :  *
       6              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       7              :  *    you may not use this file except in compliance with the License.
       8              :  *    You may obtain a copy of the License at
       9              :  *
      10              :  *        http://www.apache.org/licenses/LICENSE-2.0
      11              :  *
      12              :  *    Unless required by applicable law or agreed to in writing, software
      13              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      14              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      15              :  *    See the License for the specific language governing permissions and
      16              :  *    limitations under the License.
      17              :  */
      18              : 
      19              : #pragma once
      20              : 
      21              : #include <app/CommandHandler.h>
      22              : #include <app/ConcreteClusterPath.h>
      23              : #include <app/ConcreteCommandPath.h>
      24              : #include <app/data-model/Decode.h>
      25              : #include <app/data-model/List.h> // So we can encode lists
      26              : #include <lib/core/DataModelTypes.h>
      27              : #include <lib/support/Iterators.h>
      28              : 
      29              : namespace chip {
      30              : namespace app {
      31              : 
      32              : /*
      33              :  * This interface permits applications to register a server-side command handler
      34              :  * at run-time for a given cluster. The handler can either be configured to handle all endpoints
      35              :  * for the given cluster or only handle a specific endpoint.
      36              :  *
      37              :  * If a command is not handled through this interface, it will default to invoking the generated DispatchSingleClusterCommand
      38              :  * instead.
      39              :  *
      40              :  */
      41              : class CommandHandlerInterface
      42              : {
      43              : public:
      44              :     struct HandlerContext
      45              :     {
      46              :     public:
      47            3 :         HandlerContext(CommandHandler & commandHandler, const ConcreteCommandPath & requestPath, TLV::TLVReader & aReader) :
      48            3 :             mCommandHandler(commandHandler), mRequestPath(requestPath), mPayload(aReader)
      49            3 :         {}
      50              : 
      51              :         void SetCommandHandled() { mCommandHandled = true; }
      52              :         void SetCommandNotHandled() { mCommandHandled = false; }
      53              : 
      54              :         /*
      55              :          * Returns a TLVReader positioned at the TLV struct that contains the payload of the command.
      56              :          *
      57              :          * If the reader is requested from the context, then we can assume there is an intention
      58              :          * to access the payload of this command and consequently, to handle this command.
      59              :          *
      60              :          * If this is not true, the application should call SetCommandNotHandled().
      61              :          *
      62              :          */
      63              :         TLV::TLVReader & GetReader()
      64              :         {
      65              :             SetCommandHandled();
      66              :             return mPayload;
      67              :         }
      68              : 
      69              :         CommandHandler & mCommandHandler;
      70              :         const ConcreteCommandPath & mRequestPath;
      71              :         TLV::TLVReader & mPayload;
      72              :         bool mCommandHandled = false;
      73              :     };
      74              : 
      75              :     /**
      76              :      * aEndpointId can be Missing to indicate that this object is meant to be
      77              :      * used with all endpoints.
      78              :      */
      79              :     CommandHandlerInterface(Optional<EndpointId> aEndpointId, ClusterId aClusterId) :
      80              :         mEndpointId(aEndpointId), mClusterId(aClusterId)
      81              :     {}
      82              : 
      83              :     virtual ~CommandHandlerInterface() {}
      84              : 
      85              :     /**
      86              :      * Callback that must be implemented to handle an invoke request.
      87              :      *
      88              :      * The callee is required to handle *all* errors that may occur during the handling of this command,
      89              :      * including errors like those encountered during decode and encode of the payloads as
      90              :      * well as application-specific errors. As part of handling the error, the callee is required
      91              :      * to handle generation of an appropriate status response.
      92              :      *
      93              :      * The only exception to this rule is if the HandleCommand helper method is used below - it will
      94              :      * help handle some of these cases (see below).
      95              :      *
      96              :      * @param [in] handlerContext Context that encapsulates the current invoke request.
      97              :      *                            Handlers are responsible for correctly calling SetCommandHandled()
      98              :      *                            on the context if they did handle the command.
      99              :      *
     100              :      *                            This is not necessary if the HandleCommand() method below is invoked.
     101              :      */
     102              :     virtual void InvokeCommand(HandlerContext & handlerContext) = 0;
     103              : 
     104              :     typedef Loop (*CommandIdCallback)(CommandId id, void * context);
     105              : 
     106              :     /**
     107              :      * Function that may be implemented to enumerate accepted (client-to-server)
     108              :      * commands for the given cluster.
     109              :      *
     110              :      * If this function returns CHIP_ERROR_NOT_IMPLEMENTED, the list of accepted
     111              :      * commands will come from the endpoint metadata for the cluster.
     112              :      *
     113              :      * If this function returns any other error, that will be treated as an
     114              :      * error condition by the caller, and handling will depend on the caller.
     115              :      *
     116              :      * Otherwise the list of accepted commands will be the list of values passed
     117              :      * to the provided callback.
     118              :      *
     119              :      * The implementation _must_ pass the provided context to the callback.
     120              :      *
     121              :      * If the callback returns Loop::Break, there must be no more calls to it.
     122              :      * This is used by callbacks that just look for a particular value in the
     123              :      * list.
     124              :      */
     125              :     virtual CHIP_ERROR EnumerateAcceptedCommands(const ConcreteClusterPath & cluster, CommandIdCallback callback, void * context)
     126              :     {
     127              :         return CHIP_ERROR_NOT_IMPLEMENTED;
     128              :     }
     129              : 
     130              :     /**
     131              :      * Function that may be implemented to enumerate generated (response)
     132              :      * commands for the given cluster.
     133              :      *
     134              :      * If this function returns CHIP_ERROR_NOT_IMPLEMENTED, the list of
     135              :      * generated commands will come from the endpoint metadata for the cluster.
     136              :      *
     137              :      * If this function returns any other error, that will be treated as an
     138              :      * error condition by the caller, and handling will depend on the caller.
     139              :      *
     140              :      * Otherwise the list of generated commands will be the list of values
     141              :      * passed to the provided callback.
     142              :      *
     143              :      * The implementation _must_ pass the provided context to the callback.
     144              :      *
     145              :      * If the callback returns Loop::Break, there must be no more calls to it.
     146              :      * This is used by callbacks that just look for a particular value in the
     147              :      * list.
     148              :      */
     149              :     virtual CHIP_ERROR EnumerateGeneratedCommands(const ConcreteClusterPath & cluster, CommandIdCallback callback, void * context)
     150              :     {
     151              :         return CHIP_ERROR_NOT_IMPLEMENTED;
     152              :     }
     153              : 
     154              :     /**
     155              :      * Mechanism for keeping track of a chain of CommandHandlerInterface.
     156              :      */
     157           28 :     void SetNext(CommandHandlerInterface * aNext) { mNext = aNext; }
     158           60 :     CommandHandlerInterface * GetNext() const { return mNext; }
     159              : 
     160              :     /**
     161              :      * Check whether a this CommandHandlerInterface is relevant for a
     162              :      * particular endpoint+cluster.  An CommandHandlerInterface will be used
     163              :      * for an invoke from a particular cluster only when this function returns
     164              :      * true.
     165              :      */
     166           75 :     bool Matches(EndpointId aEndpointId, ClusterId aClusterId) const
     167              :     {
     168           75 :         return (!mEndpointId.HasValue() || mEndpointId.Value() == aEndpointId) && mClusterId == aClusterId;
     169              :     }
     170              : 
     171              :     /**
     172              :      * Check whether an CommandHandlerInterface is relevant for a particular
     173              :      * specific endpoint.  This is used to clean up overrides registered for an
     174              :      * endpoint that becomes disabled.
     175              :      */
     176           10 :     bool MatchesEndpoint(EndpointId aEndpointId) const { return mEndpointId.HasValue() && mEndpointId.Value() == aEndpointId; }
     177              : 
     178              :     /**
     179              :      * Check whether another CommandHandlerInterface wants to handle the same set of
     180              :      * commands as we do.
     181              :      */
     182           25 :     bool Matches(const CommandHandlerInterface & aOther) const
     183              :     {
     184           35 :         return mClusterId == aOther.mClusterId &&
     185           35 :             (!mEndpointId.HasValue() || !aOther.mEndpointId.HasValue() || mEndpointId.Value() == aOther.mEndpointId.Value());
     186              :     }
     187              : 
     188              : protected:
     189              :     /*
     190              :      * Helper function to automatically de-serialize the data payload into a cluster object
     191              :      * of type RequestT if the Cluster ID and Command ID in the context match. Upon successful
     192              :      * de-serialization, the provided function is invoked and passed in a reference to the cluster object.
     193              :      *
     194              :      * Any errors encountered in this function prior to calling func result in the automatic generation of a status response.
     195              :      * If `func` is called, the responsibility for doing so shifts to the callee to handle any further errors that are encountered.
     196              :      *
     197              :      * The provided function is expected to have the following signature:
     198              :      *  void Func(HandlerContext &handlerContext, const RequestT &requestPayload);
     199              :      */
     200              :     template <typename RequestT, typename FuncT>
     201              :     void HandleCommand(HandlerContext & handlerContext, FuncT func)
     202              :     {
     203              :         if (!handlerContext.mCommandHandled && (handlerContext.mRequestPath.mClusterId == RequestT::GetClusterId()) &&
     204              :             (handlerContext.mRequestPath.mCommandId == RequestT::GetCommandId()))
     205              :         {
     206              :             RequestT requestPayload;
     207              : 
     208              :             //
     209              :             // If the command matches what the caller is looking for, let's mark this as being handled
     210              :             // even if errors happen after this. This ensures that we don't execute any fall-back strategies
     211              :             // to handle this command since at this point, the caller is taking responsibility for handling
     212              :             // the command in its entirety, warts and all.
     213              :             //
     214              :             handlerContext.SetCommandHandled();
     215              : 
     216              :             if (DataModel::Decode(handlerContext.mPayload, requestPayload) != CHIP_NO_ERROR)
     217              :             {
     218              :                 handlerContext.mCommandHandler.AddStatus(handlerContext.mRequestPath,
     219              :                                                          Protocols::InteractionModel::Status::InvalidCommand);
     220              :                 return;
     221              :             }
     222              : 
     223              :             func(handlerContext, requestPayload);
     224              :         }
     225              :     }
     226              : 
     227              :     Optional<EndpointId> GetEndpointId() { return mEndpointId; }
     228              : 
     229              : private:
     230              :     Optional<EndpointId> mEndpointId;
     231              :     ClusterId mClusterId;
     232              :     CommandHandlerInterface * mNext = nullptr;
     233              : };
     234              : 
     235              : } // namespace app
     236              : } // namespace chip
        

Generated by: LCOV version 2.0-1