Matter SDK Coverage Report
Current view: top level - app/reporting - SynchronizedReportSchedulerImpl.h (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 0.0 % 1 0
Test Date: 2025-01-17 19:00:11 Functions: 0.0 % 2 0

            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/reporting/ReportSchedulerImpl.h>
      22              : 
      23              : namespace chip {
      24              : namespace app {
      25              : namespace reporting {
      26              : 
      27              : using Timeout         = System::Clock::Timeout;
      28              : using Timestamp       = System::Clock::Timestamp;
      29              : using Milliseconds64  = System::Clock::Milliseconds64;
      30              : using ReadHandlerNode = ReportScheduler::ReadHandlerNode;
      31              : using TimerDelegate   = ReportScheduler::TimerDelegate;
      32              : 
      33              : /**
      34              :  * @class Synchronized ReportSchedulerImpl
      35              :  *
      36              :  * @brief This class extends ReportSchedulerImpl and overrides its scheduling logic.
      37              :  *
      38              :  * It overrides the OnTransitionToIdle methods from ReadHandler::Observer.
      39              :  *
      40              :  * It inherits from TimerContext so that it can be used as a TimerDelegate instead of relying on the nodes to schedule themselves.
      41              :  *
      42              :  * ## Scheduling Logic
      43              :  *
      44              :  * This class implements a scheduling logic that aims to make all ReadHandlers report at the same time when possible.
      45              :  * The goal is to minimize the number of times a device wakes up to report, and thus this aims to schedule all reports at the latest
      46              :  * possible time while ensuring that all reports get sent before their max interval.
      47              :  *
      48              :  * The logic also aims to minimize the impact on the responsiveness of the device.
      49              :  *
      50              :  * The scheduling logic is as follows:
      51              :  * - The CalculateNextReportTimeout is called by any ReadHandler methods that affect when/whether a report should be sent. These
      52              :  *   are:
      53              :  *      * OnSubscriptionEstablished,
      54              :  *      * OnBecameReportable,
      55              :  *      * OnSubscriptionReportSent
      56              :  *
      57              :  * - The Synchronized Scheduler keeps track of the next min and max interval timestamps and updates them in
      58              :  *   CalculateNextReportTimeout
      59              :  *
      60              :  * - The next max interval is calculated as the earliest max interval of all the registered ReadHandlersNodes.
      61              :  *
      62              :  * - The next min interval is calculated as the latest min interval of the registered ReadHandlersNodes that:
      63              :  *     * Have a min timestamp greater than the current time
      64              :  *     * Are Reportable (this prevents a ReadHandler that is not reportable from blocking the reporting of other ReadHandlers)
      65              :  *       TODO: Assess if we want to keep this behavior or simply let the min interval be the earliest min interval to prevent cases
      66              :  *             where a ReadHandler with a dirty path but a very high min interval blocks all reports
      67              :  * - If no ReadHandlerNode matches the min interval criteria, the next min interval is set to the current timestamp.
      68              :  *
      69              :  * - The next report timeout is calculated in CalculatedNextReportTimeout based on the next min and max interval timestamps, as well
      70              :  * as the status of each ReadHandlerNode in the pool.
      71              :  *
      72              :  * @note Unlike the non-synchronized implementation, the Synchronized Scheduler will reschedule itself in the event that a timer
      73              :  * fires before a reportable timestamp is reached.
      74              :  *
      75              :  * @note In this implementation, nodes still keep track of their own min and max interval timestamps.
      76              :  */
      77              : class SynchronizedReportSchedulerImpl : public ReportSchedulerImpl, public TimerContext
      78              : {
      79              : public:
      80              :     void OnReadHandlerDestroyed(ReadHandler * aReadHandler) override;
      81              : 
      82              :     SynchronizedReportSchedulerImpl(TimerDelegate * aTimerDelegate) : ReportSchedulerImpl(aTimerDelegate) {}
      83            0 :     ~SynchronizedReportSchedulerImpl() override { UnregisterAllHandlers(); }
      84              : 
      85              :     void OnTransitionToIdle() override;
      86              : 
      87              :     bool IsReportScheduled(ReadHandler * ReadHandler) override;
      88              : 
      89              :     /** @brief Callback called when the report timer expires to schedule an engine run regardless of the state of the ReadHandlers,
      90              :      *
      91              :      * It loops through all handlers and sets their CanBeSynced flag to true if the current timestamp is greater than
      92              :      * their respective minimal timestamps.
      93              :      *
      94              :      * While looping, it checks if any handler is reportable now. If not, we recalculate the next report timeout and reschedule the
      95              :      * report.
      96              :      *
      97              :      * If a Readhandler is reportable now, an engine run is scheduled.
      98              :      *
      99              :      * If the timer expires after all nodes are unregistered, no action is taken.
     100              :      */
     101              :     void TimerFired() override;
     102              : 
     103              : protected:
     104              :     /**
     105              :      * @brief Schedule a report for the Scheduler.
     106              :      *
     107              :      * If a report is already scheduled, cancel it and schedule a new one.
     108              :      *
     109              :      * @param[in] timeout The delay before the report will happen.
     110              :      * @param[in] node The node associated with the ReadHandler.
     111              :      * @param[in] now The current system timestamp.
     112              :      *
     113              :      * @return CHIP_ERROR CHIP_NO_ERROR on success, timer-related error code otherwise (This can only fail on starting the timer)
     114              :      */
     115              :     CHIP_ERROR ScheduleReport(System::Clock::Timeout timeout, ReadHandlerNode * node, const Timestamp & now) override;
     116              :     void CancelReport();
     117              : 
     118              : private:
     119              :     friend class chip::app::reporting::TestReportScheduler;
     120              : 
     121              :     /**
     122              :      * @brief Find the highest minimum timestamp possible that still respects the lowest max timestamp and sets it as the common
     123              :      * minimum. If the max timestamp has not been updated and is in the past, or if no min timestamp is lower than the current max
     124              :      * timestamp, this will set the "now" parameter as the common minimum timestamp, thus allowing the report to be sent
     125              :      * immediately.
     126              :      *
     127              :      * @param[in] now The current system timestamp, set by the event that triggered the call of this method.
     128              :      *
     129              :      * @return CHIP_ERROR on success or CHIP_ERROR_INVALID_LIST_LENGTH if the list is empty
     130              :      */
     131              :     CHIP_ERROR FindNextMinInterval(const Timestamp & now);
     132              : 
     133              :     /**
     134              :      * @brief Find the smallest maximum interval possible and set it as the common maximum
     135              :      *
     136              :      * @param[in] now The current system timestamp, set by the event that triggered the call of this method.
     137              :      *
     138              :      * @return CHIP_ERROR on success or CHIP_ERROR_INVALID_LIST_LENGTH if the list is empty
     139              :      */
     140              :     CHIP_ERROR FindNextMaxInterval(const Timestamp & now);
     141              : 
     142              :     /**
     143              :      *  @brief Calculate the next report timeout for all ReadHandlerNodes
     144              :      *
     145              :      * @param[out] timeout The timeout to calculate.
     146              :      * @param[in] aReadHandlerNode unused, kept to preserve the signature of the base class
     147              :      * @param[in] now The current system timestamp when the event leading to the call of this method happened.
     148              :      *
     149              :      *  The next report timeout is calculated by looping through all the ReadHandlerNodes and finding if any are reportable now
     150              :      *      or at min.
     151              :      *   * If a ReadHandlerNode is reportable now, the timeout is set to 0.
     152              :      *   * If a ReadHandlerNode is reportable at min, the timeout is set to the difference between the Scheduler's  min timestamp
     153              :      *      and the current time.
     154              :      *   * If no ReadHandlerNode is reportable, the timeout is set to the difference between the Scheduler's max timestamp and the
     155              :      *      current time.
     156              :       @note When looping through the ReadHandlerNodes, the IsEngineRunScheduled flag is used to prevent calling ScheduleRun on a
     157              :             ReadHandler that already has an engine run scheduled, which would cause an endless report loop in some cases. The only
     158              :             reason why we would want to call ScheduleRun on a node that already has an engine run scheduled is if the ongoing report
     159              :             is chunked, which means that the report is not fully sent yet and that the EngineRun should be scheduled again until
     160              :      there are no chunks left.
     161              : 
     162              :           The Endless Reporting Loop Scenario would be:
     163              :           1. At least two ReadHandlers are registered to the Scheduler
     164              :           2. ScheduleRun() is called with 2 reportable ReadHandlers (meaning they both return true to IsReportableNow())
     165              :           3. The Scheduler sends the first report and calls OnSubscriptionReportSent on the ReadHandler
     166              :           4. OnSubscriptionReportSent calls CalculateNextReportTimeout, which loops through all ReadHandlers and finds that a least
     167              :            one ReadHandler is reportable now, and thus sets the timeout to 0.
     168              :           5. OnSubscriptionReportSent then calls ScheduleReport with a timeout of 0, which calls TimerFired on the Scheduler
     169              :           6. If the MinInterval of the ReadHandler is 0, the Scheduler will set the CanBeSynced flag to true, and the
     170              :              IsReportableNow Will return true since (now >= MinTimestamp || CanBeSynced()) will be true.
     171              :           7. ScheduleRun() will be called on the ReadHandler with 2 reportable ReadHandlers, and the loop will start again.
     172              :      *
     173              :      */
     174              :     CHIP_ERROR CalculateNextReportTimeout(Timeout & timeout, ReadHandlerNode * aReadHandlerNode, const Timestamp & now) override;
     175              : 
     176              :     Timestamp mNextMaxTimestamp = Milliseconds64(0);
     177              :     Timestamp mNextMinTimestamp = Milliseconds64(0);
     178              : 
     179              :     // Timestamp of the next report to be scheduled, used by OnTransitionToIdle to determine whether we should emit a report before
     180              :     // the device goes to idle mode
     181              :     Timestamp mNextReportTimestamp = Milliseconds64(0);
     182              : };
     183              : 
     184              : } // namespace reporting
     185              : } // namespace app
     186              : } // namespace chip
        

Generated by: LCOV version 2.0-1