Matter SDK Coverage Report
Current view: top level - app/reporting - SynchronizedReportSchedulerImpl.cpp (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 91.5 % 82 75
Test Date: 2025-01-17 19:00:11 Functions: 92.3 % 13 12

            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           53 : void SynchronizedReportSchedulerImpl::OnReadHandlerDestroyed(ReadHandler * aReadHandler)
      30              : {
      31              :     // Verify list is populated
      32           53 :     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. This is done to have only reportable handlers
     113              :         // contribute to setting the next min interval and avoid delaying a report for a handler that would not generate
     114              :         // a one on its min interval anyway.
     115          261 :         if (node->GetMinTimestamp() > latest && this->IsReadHandlerReportable(node->GetReadHandler()) &&
     116          261 :             node->GetMinTimestamp() <= this->mNextMaxTimestamp)
     117              :         {
     118              :             // We do not want the new min to be set above the max for any handler
     119            7 :             latest = node->GetMinTimestamp();
     120              :         }
     121              : 
     122          252 :         return Loop::Continue;
     123              :     });
     124              : 
     125          154 :     mNextMinTimestamp = latest;
     126              : 
     127          154 :     return CHIP_NO_ERROR;
     128              : }
     129              : 
     130          154 : CHIP_ERROR SynchronizedReportSchedulerImpl::CalculateNextReportTimeout(Timeout & timeout, ReadHandlerNode * aNode,
     131              :                                                                        const Timestamp & now)
     132              : {
     133          154 :     ReturnErrorOnFailure(FindNextMaxInterval(now));
     134          154 :     ReturnErrorOnFailure(FindNextMinInterval(now));
     135          154 :     bool reportableNow   = false;
     136          154 :     bool reportableAtMin = false;
     137              : 
     138              :     // Find out if any handler is reportable now or at the next min interval
     139          154 :     mNodesPool.ForEachActiveObject([&reportableNow, &reportableAtMin, this, now](ReadHandlerNode * node) {
     140              :         // If a node is already scheduled, we don't need to check if it is reportable now unless a chunked report is in progress.
     141              :         // In this case, the node will be Reportable, as it is impossible to have node->IsChunkedReport() == true without being
     142              :         // reportable, therefore we need to keep scheduling engine runs until the report is complete
     143          250 :         if (!node->IsEngineRunScheduled() || node->IsChunkedReport())
     144              :         {
     145          176 :             if (node->IsReportableNow(now))
     146              :             {
     147           29 :                 reportableNow = true;
     148           29 :                 return Loop::Break;
     149              :             }
     150              : 
     151          147 :             if (this->IsReadHandlerReportable(node->GetReadHandler()) && node->GetMinTimestamp() <= this->mNextMaxTimestamp)
     152              :             {
     153            7 :                 reportableAtMin = true;
     154              :             }
     155              :         }
     156              : 
     157          221 :         return Loop::Continue;
     158              :     });
     159              : 
     160          154 :     if (reportableNow)
     161              :     {
     162           29 :         timeout = Milliseconds32(0);
     163              :     }
     164          125 :     else if (reportableAtMin)
     165              :     {
     166            7 :         timeout = mNextMinTimestamp - now;
     167              :     }
     168              :     else
     169              :     {
     170              :         // Schedule report at next max otherwise
     171          118 :         timeout = mNextMaxTimestamp - now;
     172              :     }
     173              : 
     174          154 :     return CHIP_NO_ERROR;
     175              : }
     176              : 
     177           49 : void SynchronizedReportSchedulerImpl::TimerFired()
     178              : {
     179           49 :     Timestamp now   = mTimerDelegate->GetCurrentMonotonicTimestamp();
     180           49 :     bool firedEarly = true;
     181              : 
     182              :     // If there are no handlers registered, no need to do anything.
     183           49 :     VerifyOrReturn(mNodesPool.Allocated());
     184              : 
     185           49 :     mNodesPool.ForEachActiveObject([now, &firedEarly](ReadHandlerNode * node) {
     186           71 :         if (node->GetMinTimestamp() <= now && node->CanStartReporting())
     187              :         {
     188              :             // Since this handler can now report whenever it wants to, mark it as allowed to report if any other handler is
     189              :             // reporting using the CanBeSynced flag.
     190           65 :             node->SetCanBeSynced(true);
     191              :         }
     192              : 
     193           71 :         if (node->IsReportableNow(now))
     194              :         {
     195              :             // We set firedEarly false here because we assume we fired the timer early if no handler is reportable at the
     196              :             // moment, which becomes false if we find a handler that is reportable
     197           65 :             firedEarly = false;
     198           65 :             node->SetEngineRunScheduled(true);
     199           65 :             ChipLogProgress(DataManagement, "Handler: %p with min: 0x" ChipLogFormatX64 " and max: 0x" ChipLogFormatX64 "", (node),
     200              :                             ChipLogValueX64(node->GetMinTimestamp().count()), ChipLogValueX64(node->GetMaxTimestamp().count()));
     201              :         }
     202              : 
     203           71 :         return Loop::Continue;
     204              :     });
     205              : 
     206           49 :     if (firedEarly)
     207              :     {
     208              :         // If we fired the timer early, we need to recalculate the next report timeout and reschedule the report so it can run when
     209              :         // at least one read handler is reportable. Here we can't set the SetEngineRunScheduled flag to true, because this flag
     210              :         // allows handlers to generate reports before their min (assuming their min has elapsed from the timer's perspective but not
     211              :         // from the monotonic timer), and we don't know which handler was the one that should be reportable.
     212            1 :         Timeout timeout = Milliseconds32(0);
     213            1 :         ReturnOnFailure(CalculateNextReportTimeout(timeout, nullptr, now));
     214            1 :         ScheduleReport(timeout, nullptr, now);
     215              :     }
     216              :     else
     217              :     {
     218              :         // If we have a reportable handler, we can schedule an engine run
     219           48 :         InteractionModelEngine::GetInstance()->GetReportingEngine().ScheduleRun();
     220              :     }
     221              : }
     222              : 
     223              : } // namespace reporting
     224              : } // namespace app
     225              : } // namespace chip
        

Generated by: LCOV version 2.0-1