LCOV - code coverage report
Current view: top level - app - CommandHandlerInterface.h (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 11 11 100.0 %
Date: 2024-02-15 08:20:41 Functions: 6 6 100.0 %

          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          12 :     void SetNext(CommandHandlerInterface * aNext) { mNext = aNext; }
     158          12 :     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          15 :     bool Matches(EndpointId aEndpointId, ClusterId aClusterId) const
     167             :     {
     168          15 :         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           6 :     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           6 :     bool Matches(const CommandHandlerInterface & aOther) const
     183             :     {
     184          12 :         return mClusterId == aOther.mClusterId &&
     185          12 :             (!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             : private:
     228             :     Optional<EndpointId> mEndpointId;
     229             :     ClusterId mClusterId;
     230             :     CommandHandlerInterface * mNext = nullptr;
     231             : };
     232             : 
     233             : } // namespace app
     234             : } // namespace chip

Generated by: LCOV version 1.14