Matter SDK Coverage Report
Current view: top level - app/reporting - ReportScheduler.h (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 93.8 % 48 45
Test Date: 2025-01-17 19:00:11 Functions: 75.0 % 24 18

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

Generated by: LCOV version 2.0-1