LCOV - code coverage report
Current view: top level - app/reporting - SynchronizedReportSchedulerImpl.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 75 82 91.5 %
Date: 2024-02-15 08:20:41 Functions: 12 13 92.3 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  *    Copyright (c) 2023 Project CHIP Authors
       4             :  *
       5             :  *    Licensed under the Apache License, Version 2.0 (the "License");
       6             :  *    you may not use this file except in compliance with the License.
       7             :  *    You may obtain a copy of the License at
       8             :  *
       9             :  *        http://www.apache.org/licenses/LICENSE-2.0
      10             :  *
      11             :  *    Unless required by applicable law or agreed to in writing, software
      12             :  *    distributed under the License is distributed on an "AS IS" BASIS,
      13             :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14             :  *    See the License for the specific language governing permissions and
      15             :  *    limitations under the License.
      16             :  */
      17             : 
      18             : #include <app/InteractionModelEngine.h>
      19             : #include <app/reporting/SynchronizedReportSchedulerImpl.h>
      20             : #include <lib/support/logging/CHIPLogging.h>
      21             : 
      22             : namespace chip {
      23             : namespace app {
      24             : namespace reporting {
      25             : 
      26             : using namespace System::Clock;
      27             : using ReadHandlerNode = ReportScheduler::ReadHandlerNode;
      28             : 
      29          52 : void SynchronizedReportSchedulerImpl::OnReadHandlerDestroyed(ReadHandler * aReadHandler)
      30             : {
      31             :     // Verify list is populated
      32          52 :     VerifyOrReturn(mNodesPool.Allocated());
      33             : 
      34          26 :     ReadHandlerNode * removeNode = FindReadHandlerNode(aReadHandler);
      35             :     // Nothing to remove if the handler is not found in the list
      36          26 :     VerifyOrReturn(nullptr != removeNode);
      37             : 
      38          26 :     mNodesPool.ReleaseObject(removeNode);
      39             : 
      40          26 :     if (!mNodesPool.Allocated())
      41             :     {
      42             :         // Only cancel the timer if there are no more handlers registered
      43          21 :         CancelReport();
      44             :     }
      45             : }
      46             : 
      47           0 : void SynchronizedReportSchedulerImpl::OnTransitionToIdle()
      48             : {
      49           0 :     Timestamp now               = mTimerDelegate->GetCurrentMonotonicTimestamp();
      50           0 :     uint32_t targetIdleInterval = static_cast<uint32_t>(ICD_SLEEP_TIME_JITTER_MS);
      51           0 :     VerifyOrReturn(now >= mNextReportTimestamp);
      52           0 :     if (((mNextReportTimestamp - now) < Seconds16(targetIdleInterval)) && (now > mNextMinTimestamp))
      53             :     {
      54             :         // If the next report is due in less than the idle mode duration and we are past the min interval, we can just send it now
      55           0 :         CancelReport();
      56           0 :         TimerFired();
      57             :     }
      58             : }
      59             : 
      60         154 : CHIP_ERROR SynchronizedReportSchedulerImpl::ScheduleReport(Timeout timeout, ReadHandlerNode * node, const Timestamp & now)
      61             : {
      62             :     // Cancel Report if it is currently scheduled
      63         154 :     mTimerDelegate->CancelTimer(this);
      64         154 :     if (timeout == Milliseconds32(0))
      65             :     {
      66          29 :         TimerFired();
      67          29 :         return CHIP_NO_ERROR;
      68             :     }
      69         125 :     ReturnErrorOnFailure(mTimerDelegate->StartTimer(this, timeout));
      70         125 :     mNextReportTimestamp = now + timeout;
      71             : 
      72         125 :     return CHIP_NO_ERROR;
      73             : }
      74             : 
      75          21 : void SynchronizedReportSchedulerImpl::CancelReport()
      76             : {
      77             :     // We don't need to take action on the handler, since the timer is common here
      78          21 :     mTimerDelegate->CancelTimer(this);
      79          21 : }
      80             : 
      81             : /// @brief Checks if the timer is active for the ReportScheduler
      82           6 : bool SynchronizedReportSchedulerImpl::IsReportScheduled(ReadHandler * ReadHandler)
      83             : {
      84           6 :     return mTimerDelegate->IsTimerActive(this);
      85             : }
      86             : 
      87         154 : CHIP_ERROR SynchronizedReportSchedulerImpl::FindNextMaxInterval(const Timestamp & now)
      88             : {
      89         154 :     VerifyOrReturnError(mNodesPool.Allocated(), CHIP_ERROR_INVALID_LIST_LENGTH);
      90         154 :     System::Clock::Timestamp earliest = now + Seconds16::max();
      91             : 
      92         154 :     mNodesPool.ForEachActiveObject([&earliest, now](ReadHandlerNode * node) {
      93         252 :         if (node->GetMaxTimestamp() < earliest && node->GetMaxTimestamp() > now)
      94             :         {
      95         150 :             earliest = node->GetMaxTimestamp();
      96             :         }
      97             : 
      98         252 :         return Loop::Continue;
      99             :     });
     100             : 
     101         154 :     mNextMaxTimestamp = earliest;
     102             : 
     103         154 :     return CHIP_NO_ERROR;
     104             : }
     105             : 
     106         154 : CHIP_ERROR SynchronizedReportSchedulerImpl::FindNextMinInterval(const Timestamp & now)
     107             : {
     108         154 :     VerifyOrReturnError(mNodesPool.Allocated(), CHIP_ERROR_INVALID_LIST_LENGTH);
     109         154 :     System::Clock::Timestamp latest = now;
     110             : 
     111         154 :     mNodesPool.ForEachActiveObject([&latest, this](ReadHandlerNode * node) {
     112             :         // We only consider the min interval if the handler is reportable to prevent holding the reports
     113         261 :         if (node->GetMinTimestamp() > latest && this->IsReadHandlerReportable(node->GetReadHandler()) &&
     114         261 :             node->GetMinTimestamp() <= this->mNextMaxTimestamp)
     115             :         {
     116             :             // We do not want the new min to be set above the max for any handler
     117           7 :             latest = node->GetMinTimestamp();
     118             :         }
     119             : 
     120         252 :         return Loop::Continue;
     121             :     });
     122             : 
     123         154 :     mNextMinTimestamp = latest;
     124             : 
     125         154 :     return CHIP_NO_ERROR;
     126             : }
     127             : 
     128         154 : CHIP_ERROR SynchronizedReportSchedulerImpl::CalculateNextReportTimeout(Timeout & timeout, ReadHandlerNode * aNode,
     129             :                                                                        const Timestamp & now)
     130             : {
     131         154 :     ReturnErrorOnFailure(FindNextMaxInterval(now));
     132         154 :     ReturnErrorOnFailure(FindNextMinInterval(now));
     133         154 :     bool reportableNow   = false;
     134         154 :     bool reportableAtMin = false;
     135             : 
     136             :     // Find out if any handler is reportable now or at the next min interval
     137         154 :     mNodesPool.ForEachActiveObject([&reportableNow, &reportableAtMin, this, now](ReadHandlerNode * node) {
     138             :         // If a node is already scheduled, we don't need to check if it is reportable now, unless a chunked report is in progress
     139             :         // in which case we need to keep scheduling engine runs until the report is complete
     140         250 :         if (!node->IsEngineRunScheduled() || node->IsChunkedReport())
     141             :         {
     142         176 :             if (node->IsReportableNow(now))
     143             :             {
     144          29 :                 reportableNow = true;
     145          29 :                 return Loop::Break;
     146             :             }
     147             : 
     148         147 :             if (this->IsReadHandlerReportable(node->GetReadHandler()) && node->GetMinTimestamp() <= this->mNextMaxTimestamp)
     149             :             {
     150           7 :                 reportableAtMin = true;
     151             :             }
     152             :         }
     153             : 
     154         221 :         return Loop::Continue;
     155             :     });
     156             : 
     157         154 :     if (reportableNow)
     158             :     {
     159          29 :         timeout = Milliseconds32(0);
     160             :     }
     161         125 :     else if (reportableAtMin)
     162             :     {
     163           7 :         timeout = mNextMinTimestamp - now;
     164             :     }
     165             :     else
     166             :     {
     167             :         // Schedule report at next max otherwise
     168         118 :         timeout = mNextMaxTimestamp - now;
     169             :     }
     170             : 
     171         154 :     return CHIP_NO_ERROR;
     172             : }
     173             : 
     174          49 : void SynchronizedReportSchedulerImpl::TimerFired()
     175             : {
     176          49 :     Timestamp now   = mTimerDelegate->GetCurrentMonotonicTimestamp();
     177          49 :     bool firedEarly = true;
     178             : 
     179             :     // If there are no handlers registered, no need to do anything.
     180          49 :     VerifyOrReturn(mNodesPool.Allocated());
     181             : 
     182          49 :     mNodesPool.ForEachActiveObject([now, &firedEarly](ReadHandlerNode * node) {
     183          71 :         if (node->GetMinTimestamp() <= now)
     184             :         {
     185             :             // Mark the handler as CanBeSynced if the min interval has elapsed so it will emit a report on the next engine run
     186          65 :             node->SetCanBeSynced(true);
     187             :         }
     188             : 
     189          71 :         if (node->IsReportableNow(now))
     190             :         {
     191             :             // We set firedEarly false here because we assume we fired the timer early if no handler is reportable at the
     192             :             // moment, which becomes false if we find a handler that is reportable
     193          65 :             firedEarly = false;
     194          65 :             node->SetEngineRunScheduled(true);
     195          65 :             ChipLogProgress(DataManagement, "Handler: %p with min: 0x" ChipLogFormatX64 " and max: 0x" ChipLogFormatX64 "", (node),
     196             :                             ChipLogValueX64(node->GetMinTimestamp().count()), ChipLogValueX64(node->GetMaxTimestamp().count()));
     197             :         }
     198             : 
     199          71 :         return Loop::Continue;
     200             :     });
     201             : 
     202          49 :     if (firedEarly)
     203             :     {
     204             :         // If we fired the timer early, we need to recalculate the next report timeout and reschedule the report
     205           1 :         Timeout timeout = Milliseconds32(0);
     206           1 :         ReturnOnFailure(CalculateNextReportTimeout(timeout, nullptr, now));
     207           1 :         ScheduleReport(timeout, nullptr, now);
     208             :     }
     209             :     else
     210             :     {
     211             :         // If we did not fire the timer early, we can schedule an engine run
     212          48 :         InteractionModelEngine::GetInstance()->GetReportingEngine().ScheduleRun();
     213             :     }
     214             : }
     215             : 
     216             : } // namespace reporting
     217             : } // namespace app
     218             : } // namespace chip

Generated by: LCOV version 1.14