LCOV - code coverage report
Current view: top level - app/reporting - ReportScheduler.h (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 45 47 95.7 %
Date: 2024-02-15 08:20:41 Functions: 18 23 78.3 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  *    Copyright (c) 2023 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/ReadHandler.h>
      22             : #include <app/icd/server/ICDStateObserver.h>
      23             : #include <lib/core/CHIPError.h>
      24             : #include <system/SystemClock.h>
      25             : 
      26             : namespace chip {
      27             : namespace app {
      28             : namespace reporting {
      29             : 
      30             : // Forward declaration of TestReportScheduler to allow it to be friend with ReportScheduler
      31             : class TestReportScheduler;
      32             : 
      33             : class TimerContext
      34             : {
      35             : public:
      36           1 :     virtual ~TimerContext() {}
      37             :     virtual void TimerFired() = 0;
      38             : };
      39             : 
      40             : /**
      41             :  * @class ReportScheduler
      42             :  *
      43             :  * @brief This class is responsible for scheduling Engine runs based on the reporting intervals of the ReadHandlers.
      44             :  *
      45             :  *
      46             :  * This class holds a pool of ReadHandlerNodes that are used to keep track of the minimum and maximum timestamps for a report to be
      47             :  * emitted based on the reporting intervals of the ReadHandlers associated with the node.
      48             :  *
      49             :  * The ReportScheduler also holds a TimerDelegate pointer that is used to start and cancel timers for the ReadHandlers depending
      50             :  * on the reporting logic of the Scheduler.
      51             :  *
      52             :  * It inherits the ReadHandler::Observer class to be notified of reportability changes in the ReadHandlers.
      53             :  * It inherits the ICDStateObserver class to allow the implementation to generate reports based on the changes in ICD devices state,
      54             :  * such as going from idle to active and vice-versa.
      55             :  *
      56             :  * @note The logic for how and when to schedule reports is implemented in the subclasses of ReportScheduler, such as
      57             :  * ReportSchedulerImpl and SyncronizedReportSchedulerImpl.
      58             :  */
      59             : class ReportScheduler : public ReadHandler::Observer, public ICDStateObserver
      60             : {
      61             : public:
      62             :     using Timestamp = System::Clock::Timestamp;
      63             : 
      64             :     /// @brief This class acts as an interface between the report scheduler and the system timer to reduce dependencies on the
      65             :     /// system layer.
      66             :     class TimerDelegate
      67             :     {
      68             :     public:
      69           0 :         virtual ~TimerDelegate() {}
      70             :         /// @brief Start a timer for a given context. The report scheduler must always cancel an existing timer for a context (using
      71             :         /// CancelTimer) before starting a new one for that context.
      72             :         /// @param context context to pass to the timer callback.
      73             :         /// @param aTimeout time in miliseconds before the timer expires
      74             :         virtual CHIP_ERROR StartTimer(TimerContext * context, System::Clock::Timeout aTimeout) = 0;
      75             :         /// @brief Cancel a timer for a given context
      76             :         /// @param context used to identify the timer to cancel
      77             :         virtual void CancelTimer(TimerContext * context)   = 0;
      78             :         virtual bool IsTimerActive(TimerContext * context) = 0;
      79             :         virtual Timestamp GetCurrentMonotonicTimestamp()   = 0;
      80             :     };
      81             : 
      82             :     /**
      83             :      * @class ReadHandlerNode
      84             :      *
      85             :      * @brief This class is in charge of determining when a ReadHandler is reportable depending on the monotonic timestamp of the
      86             :      * system and the intervals of the ReadHandler. It inherits the TimerContext class to allow it to be used as a context for a
      87             :      * TimerDelegate so the TimerDelegate can call the TimerFired method when the timer expires.
      88             :      *
      89             :      *  The Logic to determine if a ReadHandler is reportable at a precise timestamp is as follows:
      90             :      *  1: The ReadHandler is in the CanStartReporting state
      91             :      *  2: The minimal interval since last report has elapsed
      92             :      *  3: The maximal interval since last report has elapsed or the ReadHandler is dirty
      93             :      *  If the three conditions are met, the ReadHandler is reportable.
      94             :      *
      95             :      *  Additionnal flags have been provided for specific use cases:
      96             :      *
      97             :      *  CanbeSynced: Mechanism to allow the ReadHandler to emit a report if another readHandler is ReportableNow.
      98             :      *  This flag can substitute the maximal interval condition or the dirty condition. It is currently only used by the
      99             :      *  SynchronizedReportScheduler.
     100             :      *
     101             :      *  EngineRunScheduled: Mechanism to ensure that the reporting engine will see the ReadHandler as reportable if a timer fires.
     102             :      *  This flag can substitute the minimal interval condition or the maximal interval condition. The goal is to allow for
     103             :      *  reporting when timers fire earlier than the minimal timestamp du to mechanism such as NTP clock adjustments.
     104             :      */
     105             :     class ReadHandlerNode : public TimerContext
     106             :     {
     107             :     public:
     108             :         enum class ReadHandlerNodeFlags : uint8_t
     109             :         {
     110             :             // Flag to indicate if the engine run is already scheduled so the scheduler can ignore
     111             :             // it when calculating the next run time
     112             :             EngineRunScheduled = (1 << 0),
     113             :             // Flag to allow the read handler to be synced with other handlers that have an earlier max timestamp
     114             :             CanBeSynced = (1 << 1),
     115             :         };
     116             : 
     117         237 :         ReadHandlerNode(ReadHandler * aReadHandler, ReportScheduler * aScheduler, const Timestamp & now) : mScheduler(aScheduler)
     118             :         {
     119         237 :             VerifyOrDie(aReadHandler != nullptr);
     120         237 :             VerifyOrDie(aScheduler != nullptr);
     121             : 
     122         237 :             mReadHandler = aReadHandler;
     123         237 :             SetIntervalTimeStamps(aReadHandler, now);
     124         237 :         }
     125       18922 :         ReadHandler * GetReadHandler() const { return mReadHandler; }
     126             : 
     127             :         /// @brief Check if the Node is reportable now, meaning its readhandler was made reportable by attribute dirtying and
     128             :         /// handler state, and minimal time interval since last report has elapsed, or the maximal time interval since last
     129             :         /// report has elapsed.
     130             :         /// @note If a handler has been flaged as scheduled for engine run, it will be reported regardless of the timestamps. This
     131             :         /// is done to guarantee that the reporting engine will see the handler as reportable if a timer fires, even if it fires
     132             :         /// early.
     133             :         /// @param now current time to use for the check, user must ensure to provide a valid time for this to be reliable
     134         503 :         bool IsReportableNow(const Timestamp & now) const
     135             :         {
     136        1427 :             return (mReadHandler->CanStartReporting() &&
     137        1285 :                     ((now >= mMinTimestamp && (mReadHandler->IsDirty() || now >= mMaxTimestamp || CanBeSynced())) ||
     138         865 :                      IsEngineRunScheduled()));
     139             :         }
     140             : 
     141          80 :         bool IsChunkedReport() const { return mReadHandler->IsChunkedReport(); }
     142         362 :         bool IsEngineRunScheduled() const { return mFlags.Has(ReadHandlerNodeFlags::EngineRunScheduled); }
     143         303 :         void SetEngineRunScheduled(bool aEngineRunScheduled)
     144             :         {
     145         303 :             mFlags.Set(ReadHandlerNodeFlags::EngineRunScheduled, aEngineRunScheduled);
     146         303 :         }
     147         361 :         bool CanBeSynced() const { return mFlags.Has(ReadHandlerNodeFlags::CanBeSynced); }
     148         178 :         void SetCanBeSynced(bool aCanBeSynced) { mFlags.Set(ReadHandlerNodeFlags::CanBeSynced, aCanBeSynced); }
     149             : 
     150             :         /// @brief Set the interval timestamps for the node based on the read handler reporting intervals
     151             :         /// @param aReadHandler read handler to get the intervals from
     152             :         /// @param now current time to calculate the mMin and mMax timestamps, user must ensure to provide a valid time for this to
     153             :         /// be reliable
     154         255 :         void SetIntervalTimeStamps(ReadHandler * aReadHandler, const Timestamp & now)
     155             :         {
     156             :             uint16_t minInterval, maxInterval;
     157         255 :             aReadHandler->GetReportingIntervals(minInterval, maxInterval);
     158         255 :             mMinTimestamp = now + System::Clock::Seconds16(minInterval);
     159         255 :             mMaxTimestamp = now + System::Clock::Seconds16(maxInterval);
     160         255 :         }
     161             : 
     162         125 :         void TimerFired() override
     163             :         {
     164         125 :             SetEngineRunScheduled(true);
     165         125 :             mScheduler->ReportTimerCallback();
     166         125 :         }
     167             : 
     168         340 :         System::Clock::Timestamp GetMinTimestamp() const { return mMinTimestamp; }
     169         569 :         System::Clock::Timestamp GetMaxTimestamp() const { return mMaxTimestamp; }
     170             : 
     171             :     private:
     172             :         ReadHandler * mReadHandler;
     173             :         ReportScheduler * mScheduler;
     174             :         Timestamp mMinTimestamp;
     175             :         Timestamp mMaxTimestamp;
     176             : 
     177             :         BitFlags<ReadHandlerNodeFlags> mFlags;
     178             :     };
     179             : 
     180          54 :     ReportScheduler(TimerDelegate * aTimerDelegate) : mTimerDelegate(aTimerDelegate) {}
     181             :     /**
     182             :      *  Interface to act on changes in the ReadHandler reportability
     183             :      */
     184           0 :     virtual ~ReportScheduler() = default;
     185             : 
     186             :     virtual void ReportTimerCallback() = 0;
     187             : 
     188             :     /// @brief Check whether a ReadHandler is reportable right now, taking into account its minimum and maximum intervals.
     189             :     /// @param aReadHandler read handler to check
     190         218 :     bool IsReportableNow(ReadHandler * aReadHandler)
     191             :     {
     192             :         // Update the now timestamp to ensure external calls to IsReportableNow are always comparing to the current time
     193         218 :         Timestamp now          = mTimerDelegate->GetCurrentMonotonicTimestamp();
     194         218 :         ReadHandlerNode * node = FindReadHandlerNode(aReadHandler);
     195         218 :         return (nullptr != node) ? node->IsReportableNow(now) : false;
     196             :     }
     197             : 
     198             :     /// @brief Check if a ReadHandler is reportable without considering the timing
     199         346 :     bool IsReadHandlerReportable(ReadHandler * aReadHandler) const
     200             :     {
     201         346 :         return (nullptr != aReadHandler) ? aReadHandler->ShouldStartReporting() : false;
     202             :     }
     203             :     /// @brief Sets the ForceDirty flag of a ReadHandler
     204             :     void HandlerForceDirtyState(ReadHandler * aReadHandler) { aReadHandler->ForceDirtyState(); }
     205             : 
     206             :     /// @brief Get the number of ReadHandlers registered in the scheduler's node pool
     207             :     size_t GetNumReadHandlers() const { return mNodesPool.Allocated(); }
     208             : 
     209             : #ifdef CONFIG_BUILD_FOR_HOST_UNIT_TEST
     210             :     Timestamp GetMinTimestampForHandler(const ReadHandler * aReadHandler)
     211             :     {
     212             :         ReadHandlerNode * node = FindReadHandlerNode(aReadHandler);
     213             :         return node->GetMinTimestamp();
     214             :     }
     215             :     Timestamp GetMaxTimestampForHandler(const ReadHandler * aReadHandler)
     216             :     {
     217             :         ReadHandlerNode * node = FindReadHandlerNode(aReadHandler);
     218             :         return node->GetMaxTimestamp();
     219             :     }
     220             :     ReadHandlerNode * GetReadHandlerNode(const ReadHandler * aReadHandler) { return FindReadHandlerNode(aReadHandler); }
     221             : #endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST
     222             : 
     223             : protected:
     224             :     friend class chip::app::reporting::TestReportScheduler;
     225             : 
     226             :     /// @brief Find the ReadHandlerNode for a given ReadHandler pointer
     227             :     /// @param [in] aReadHandler ReadHandler pointer to look for in the ReadHandler nodes list
     228             :     /// @return Node Address if node was found, nullptr otherwise
     229        2935 :     ReadHandlerNode * FindReadHandlerNode(const ReadHandler * aReadHandler)
     230             :     {
     231        2935 :         ReadHandlerNode * foundNode = nullptr;
     232        2935 :         mNodesPool.ForEachActiveObject([&foundNode, aReadHandler](ReadHandlerNode * node) {
     233       18406 :             if (node->GetReadHandler() == aReadHandler)
     234             :             {
     235        1130 :                 foundNode = node;
     236        1130 :                 return Loop::Break;
     237             :             }
     238             : 
     239       17276 :             return Loop::Continue;
     240             :         });
     241        2935 :         return foundNode;
     242             :     }
     243             : 
     244             :     ObjectPool<ReadHandlerNode, CHIP_IM_MAX_NUM_READS + CHIP_IM_MAX_NUM_SUBSCRIPTIONS> mNodesPool;
     245             :     TimerDelegate * mTimerDelegate;
     246             : };
     247             : }; // namespace reporting
     248             : }; // namespace app
     249             : }; // namespace chip

Generated by: LCOV version 1.14