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 it's scheduling logic. 37 : * 38 : * It only overrides Observers method where the scheduling logic make it necessary, the others are kept as is. 39 : * 40 : * It inherits from TimerContext so that it can be used as a TimerDelegate instead on 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 different 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 responsivity of the device. 49 : * 50 : * The scheduling logic is as follows: 51 : * - The CalculateNextReportTimeout is called by the same ReadHandler Observer callbacks than the non-synchronized implementation: 52 : * * OnSubscriptionEstablished, 53 : * * OnBecameReportable, 54 : * * OnSubscriptionReportSent 55 : * 56 : * - The Synchronized Scheduler keeps track of the next min and max interval timestamps. It updates in CalculateNextReportTimeout 57 : * 58 : * - The next max interval is calculated as the earliest max interval of all the registered ReadHandlersNodes. 59 : * 60 : * - The next min interval is calculated as the latest min interval of the registered ReadHandlersNodes that: 61 : * * Have a min timestamp greater than the current time 62 : * * Are Reportable (this prevents a ReadHandler that is not reportable to hold the report of all the others) 63 : * TODO: Assess if we want to keep this behavior or simply let the min interval be the earliest min interval to prevent cases 64 : * where a ReadHandler with a dirty path but a very high min interval blocks all reports 65 : * - If no ReadHandlerNode matches min interval the criteria, the next min interval is set to current timestamp. 66 : * 67 : * - The next report timeout is calculated in CalculatedNextReportTimeout based on the next min and max interval timestamps, as well 68 : * as the status of each ReadHandlerNode in the pool. 69 : * 70 : * @note Unlike the non-synchronized implementation, the Synchronized Scheduler will reschedule itself in the event where a timer 71 : * fires before a reportable timestamp is reached. 72 : * 73 : * @note In this implementation, nodes still keep track of their own min and max interval timestamps. 74 : */ 75 : class SynchronizedReportSchedulerImpl : public ReportSchedulerImpl, public TimerContext 76 : { 77 : public: 78 : void OnReadHandlerDestroyed(ReadHandler * aReadHandler) override; 79 : 80 : SynchronizedReportSchedulerImpl(TimerDelegate * aTimerDelegate) : ReportSchedulerImpl(aTimerDelegate) {} 81 0 : ~SynchronizedReportSchedulerImpl() override { UnregisterAllHandlers(); } 82 : 83 : void OnTransitionToIdle() override; 84 : 85 : bool IsReportScheduled(ReadHandler * ReadHandler) override; 86 : 87 : /** @brief Callback called when the report timer expires to schedule an engine run regardless of the state of the ReadHandlers, 88 : * 89 : * It loops through all handlers and sets their CanBeSynced flag to true if the current timstamp is greater than 90 : * their respective minimal timestamps. 91 : * 92 : * While looping, it checks if any handler is reportable now. If not, we recalculate the next report timeout and reschedule the 93 : * report. 94 : * 95 : * If a Readhangler is reportable now, an engine run is scheduled. 96 : * 97 : * If the timer expires after all nodes were unregistered, no action is taken. 98 : */ 99 : void TimerFired() override; 100 : 101 : protected: 102 : /** 103 : * @brief Schedule a report for the Scheduler. 104 : * 105 : * If a report is already scheduled, cancel it and schedule a new one. 106 : * 107 : * @param[in] timeout The timeout to schedule the report. 108 : * @param[in] node The node associated to the ReadHandler. 109 : * @param[in] now The current system timestamp. 110 : * 111 : * @return CHIP_ERROR CHIP_NO_ERROR on success, timer related error code otherwise (This can only fail on starting the timer) 112 : */ 113 : CHIP_ERROR ScheduleReport(System::Clock::Timeout timeout, ReadHandlerNode * node, const Timestamp & now) override; 114 : void CancelReport(); 115 : 116 : private: 117 : friend class chip::app::reporting::TestReportScheduler; 118 : 119 : /** 120 : * @brief Find the highest minimum timestamp possible that still respects the lowest max timestamp and sets it as the common 121 : * 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 122 : * timestamp, this will set now as the common minimum timestamp, thus allowing the report to be sent immediately. 123 : * 124 : * @param[in] now The current system timestamp, set by the event that triggered the call of this method. 125 : * 126 : * @return CHIP_ERROR on success or CHIP_ERROR_INVALID_LIST_LENGTH if the list is empty 127 : */ 128 : CHIP_ERROR FindNextMinInterval(const Timestamp & now); 129 : 130 : /** 131 : * @brief Find the smallest maximum interval possible and set it as the common maximum 132 : * 133 : * @param[in] now The current system timestamp, set by the event that triggered the call of this method. 134 : * 135 : * @return CHIP_ERROR on success or CHIP_ERROR_INVALID_LIST_LENGTH if the list is empty 136 : */ 137 : CHIP_ERROR FindNextMaxInterval(const Timestamp & now); 138 : 139 : /** 140 : * @brief Calculate the next report timeout for all ReadHandlerNodes 141 : * 142 : * @param[out] timeout The timeout to calculate. 143 : * @param[in] aReadHandlerNode unused, kept to preserve the signature of the base class 144 : * @param[in] now The current system timestamp when the event leading to the call of this method happened. 145 : * 146 : * The next report timeout is calculated by looping through all the ReadHandlerNodes and finding if any are reportable now 147 : * or at min. 148 : * * If a ReadHandlerNode is reportable now, the timeout is set to 0. 149 : * * If a ReadHandlerNode is reportable at min, the timeout is set to the difference between the Scheduler's min timestamp 150 : * and the current time. 151 : * * If no ReadHandlerNode is reportable, the timeout is set to the difference between the Scheduler's max timestamp and the 152 : * current time. 153 : * 154 : * @note Since this method is called after the OnSubscriptionReportSent callback, to avoid an endless reporting loop, Nodes with 155 : * the IsEngineRunScheduled flag set are ignored when finding if the Scheduler should report at min, max or now. 156 : * 157 : * @note If a ReadHandler's report is Chunked, the IsEngineRunScheduled is ignored since we do want to keep rescheduling the 158 : * report to the now timestamp until it is fully sent. IsChunkedReport is used to prevent starting a chunked report and 159 : * then waiting on the max interval after the first chunk is sent. 160 : */ 161 : CHIP_ERROR CalculateNextReportTimeout(Timeout & timeout, ReadHandlerNode * aReadHandlerNode, const Timestamp & now) override; 162 : 163 : Timestamp mNextMaxTimestamp = Milliseconds64(0); 164 : Timestamp mNextMinTimestamp = Milliseconds64(0); 165 : 166 : // Timestamp of the next report to be scheduled, used by OnTransitionToIdle to determine whether we should emit a report before 167 : // the device goes to idle mode 168 : Timestamp mNextReportTimestamp = Milliseconds64(0); 169 : }; 170 : 171 : } // namespace reporting 172 : } // namespace app 173 : } // namespace chip