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/AppConfig.h> 19 : #include <app/InteractionModelEngine.h> 20 : #include <app/reporting/ReportSchedulerImpl.h> 21 : 22 : namespace chip { 23 : namespace app { 24 : namespace reporting { 25 : 26 : using namespace System::Clock; 27 : using ReadHandlerNode = ReportScheduler::ReadHandlerNode; 28 : 29 : /// @brief Callback called when the report timer expires to schedule an engine run regardless of the state of the ReadHandlers, as 30 : /// the engine already verifies that read handlers are reportable before sending a report 31 125 : void ReportSchedulerImpl::ReportTimerCallback() 32 : { 33 125 : InteractionModelEngine::GetInstance()->GetReportingEngine().ScheduleRun(); 34 125 : } 35 : 36 54 : ReportSchedulerImpl::ReportSchedulerImpl(TimerDelegate * aTimerDelegate) : ReportScheduler(aTimerDelegate) 37 : { 38 54 : VerifyOrDie(nullptr != mTimerDelegate); 39 54 : } 40 : 41 : /// @brief Method that triggers a report emission on each ReadHandler that is not blocked on its min interval. 42 : /// Each read handler that is not blocked is immediately marked dirty so that it will report as soon as possible. 43 0 : void ReportSchedulerImpl::OnEnterActiveMode() 44 : { 45 : #if ICD_REPORT_ON_ENTER_ACTIVE_MODE 46 : Timestamp now = mTimerDelegate->GetCurrentMonotonicTimestamp(); 47 : mNodesPool.ForEachActiveObject([this, now](ReadHandlerNode * node) { 48 : if (now >= node->GetMinTimestamp()) 49 : { 50 : this->HandlerForceDirtyState(node->GetReadHandler()); 51 : } 52 : 53 : return Loop::Continue; 54 : }); 55 : #endif 56 0 : } 57 : 58 237 : void ReportSchedulerImpl::OnSubscriptionEstablished(ReadHandler * aReadHandler) 59 : { 60 237 : ReadHandlerNode * newNode = FindReadHandlerNode(aReadHandler); 61 : // Handler must not be registered yet; it's just being constructed. 62 237 : VerifyOrDie(nullptr == newNode); 63 : 64 237 : Timestamp now = mTimerDelegate->GetCurrentMonotonicTimestamp(); 65 : 66 : // The NodePool is the same size as the ReadHandler pool from the IM Engine, so we don't need a check for size here since if a 67 : // ReadHandler was created, space should be available. 68 237 : newNode = mNodesPool.CreateObject(aReadHandler, this, now); 69 : 70 237 : ChipLogProgress(DataManagement, 71 : "Registered a ReadHandler that will schedule a report between system Timestamp: 0x" ChipLogFormatX64 72 : " and system Timestamp 0x" ChipLogFormatX64 ".", 73 : ChipLogValueX64(newNode->GetMinTimestamp().count()), ChipLogValueX64(newNode->GetMaxTimestamp().count())); 74 237 : } 75 : 76 692 : void ReportSchedulerImpl::OnBecameReportable(ReadHandler * aReadHandler) 77 : { 78 692 : ReadHandlerNode * node = FindReadHandlerNode(aReadHandler); 79 692 : VerifyOrReturn(nullptr != node); 80 : 81 432 : Timestamp now = mTimerDelegate->GetCurrentMonotonicTimestamp(); 82 : 83 : Milliseconds32 newTimeout; 84 432 : CalculateNextReportTimeout(newTimeout, node, now); 85 432 : ScheduleReport(newTimeout, node, now); 86 : } 87 : 88 113 : void ReportSchedulerImpl::OnSubscriptionReportSent(ReadHandler * aReadHandler) 89 : { 90 113 : ReadHandlerNode * node = FindReadHandlerNode(aReadHandler); 91 113 : VerifyOrReturn(nullptr != node); 92 : 93 113 : Timestamp now = mTimerDelegate->GetCurrentMonotonicTimestamp(); 94 : 95 113 : node->SetCanBeSynced(false); 96 113 : node->SetIntervalTimeStamps(aReadHandler, now); 97 : Milliseconds32 newTimeout; 98 : // Reset the EngineRunScheduled flag so that the next report is scheduled correctly 99 113 : node->SetEngineRunScheduled(false); 100 113 : CalculateNextReportTimeout(newTimeout, node, now); 101 113 : ScheduleReport(newTimeout, node, now); 102 : } 103 : 104 949 : void ReportSchedulerImpl::OnReadHandlerDestroyed(ReadHandler * aReadHandler) 105 : { 106 949 : CancelReport(aReadHandler); 107 : 108 949 : ReadHandlerNode * removeNode = FindReadHandlerNode(aReadHandler); 109 : // Nothing to remove if the handler is not found in the list 110 949 : VerifyOrReturn(nullptr != removeNode); 111 : 112 211 : mNodesPool.ReleaseObject(removeNode); 113 : } 114 : 115 392 : CHIP_ERROR ReportSchedulerImpl::ScheduleReport(Timeout timeout, ReadHandlerNode * node, const Timestamp & now) 116 : { 117 : // Cancel Report if it is currently scheduled 118 392 : mTimerDelegate->CancelTimer(node); 119 392 : if (timeout == Milliseconds32(0)) 120 : { 121 102 : node->TimerFired(); 122 102 : return CHIP_NO_ERROR; 123 : } 124 290 : ReturnErrorOnFailure(mTimerDelegate->StartTimer(node, timeout)); 125 : 126 290 : return CHIP_NO_ERROR; 127 : } 128 : 129 950 : void ReportSchedulerImpl::CancelReport(ReadHandler * aReadHandler) 130 : { 131 950 : ReadHandlerNode * node = FindReadHandlerNode(aReadHandler); 132 950 : VerifyOrReturn(nullptr != node); 133 212 : mTimerDelegate->CancelTimer(node); 134 : } 135 : 136 83 : void ReportSchedulerImpl::UnregisterAllHandlers() 137 : { 138 83 : mNodesPool.ForEachActiveObject([this](ReadHandlerNode * node) { 139 18 : this->OnReadHandlerDestroyed(node->GetReadHandler()); 140 18 : return Loop::Continue; 141 : }); 142 83 : } 143 : 144 14 : bool ReportSchedulerImpl::IsReportScheduled(ReadHandler * aReadHandler) 145 : { 146 14 : ReadHandlerNode * node = FindReadHandlerNode(aReadHandler); 147 14 : VerifyOrReturnValue(nullptr != node, false); 148 11 : return mTimerDelegate->IsTimerActive(node); 149 : } 150 : 151 392 : CHIP_ERROR ReportSchedulerImpl::CalculateNextReportTimeout(Timeout & timeout, ReadHandlerNode * aNode, const Timestamp & now) 152 : { 153 392 : VerifyOrReturnError(nullptr != FindReadHandlerNode(aNode->GetReadHandler()), CHIP_ERROR_INVALID_ARGUMENT); 154 : 155 : // If the handler is reportable now, just schedule a report immediately 156 392 : if (aNode->IsReportableNow(now)) 157 : { 158 : // If the handler is reportable now, just schedule a report immediately 159 93 : timeout = Milliseconds32(0); 160 : } 161 299 : else if (IsReadHandlerReportable(aNode->GetReadHandler()) && (aNode->GetMinTimestamp() > now)) 162 : { 163 : // If the handler is reportable now, but the min interval is not elapsed, schedule a report for the moment the min interval 164 : // has elapsed 165 5 : timeout = aNode->GetMinTimestamp() - now; 166 : } 167 : else 168 : { 169 : // If the handler is not reportable now, schedule a report for the max interval 170 294 : timeout = aNode->GetMaxTimestamp() - now; 171 : } 172 392 : return CHIP_NO_ERROR; 173 : } 174 : 175 : } // namespace reporting 176 : } // namespace app 177 : } // namespace chip