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 : TEMPORARY_RETURN_IGNORED InteractionModelEngine::GetInstance()->GetReportingEngine().ScheduleRun();
34 125 : }
35 :
36 74 : ReportSchedulerImpl::ReportSchedulerImpl(TimerDelegate * aTimerDelegate) : ReportScheduler(aTimerDelegate)
37 : {
38 74 : VerifyOrDie(nullptr != mTimerDelegate);
39 74 : }
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 312 : void ReportSchedulerImpl::OnSubscriptionEstablished(ReadHandler * aReadHandler)
59 : {
60 312 : ReadHandlerNode * newNode = FindReadHandlerNode(aReadHandler);
61 : // Handler must not be registered yet; it's just being constructed.
62 312 : VerifyOrDie(nullptr == newNode);
63 :
64 312 : 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 312 : newNode = mNodesPool.CreateObject(aReadHandler, this, now);
69 :
70 312 : 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 :
75 312 : mNumTotalSubscriptionsEstablished++;
76 312 : }
77 :
78 780 : void ReportSchedulerImpl::OnBecameReportable(ReadHandler * aReadHandler)
79 : {
80 780 : ReadHandlerNode * node = FindReadHandlerNode(aReadHandler);
81 780 : VerifyOrReturn(nullptr != node);
82 :
83 520 : Timestamp now = mTimerDelegate->GetCurrentMonotonicTimestamp();
84 :
85 : Milliseconds32 newTimeout;
86 520 : TEMPORARY_RETURN_IGNORED CalculateNextReportTimeout(newTimeout, node, now);
87 520 : TEMPORARY_RETURN_IGNORED ScheduleReport(newTimeout, node, now);
88 : }
89 :
90 114 : void ReportSchedulerImpl::OnSubscriptionReportSent(ReadHandler * aReadHandler)
91 : {
92 114 : ReadHandlerNode * node = FindReadHandlerNode(aReadHandler);
93 114 : VerifyOrReturn(nullptr != node);
94 :
95 114 : Timestamp now = mTimerDelegate->GetCurrentMonotonicTimestamp();
96 :
97 : // This method is called after the report is sent, so the ReadHandler is no longer reportable, and thus CanBeSynced and
98 : // EngineRunScheduled of the node associated with the ReadHandler are set to false here.
99 114 : node->SetCanBeSynced(false);
100 114 : node->SetIntervalTimeStamps(aReadHandler, now);
101 : Milliseconds32 newTimeout;
102 : // Reset the EngineRunScheduled flag so that the next report is scheduled correctly
103 114 : node->SetEngineRunScheduled(false);
104 114 : TEMPORARY_RETURN_IGNORED CalculateNextReportTimeout(newTimeout, node, now);
105 114 : TEMPORARY_RETURN_IGNORED ScheduleReport(newTimeout, node, now);
106 : }
107 :
108 1113 : void ReportSchedulerImpl::OnReadHandlerDestroyed(ReadHandler * aReadHandler)
109 : {
110 1113 : CancelReport(aReadHandler);
111 :
112 1113 : ReadHandlerNode * removeNode = FindReadHandlerNode(aReadHandler);
113 : // Nothing to remove if the handler is not found in the list
114 1113 : VerifyOrReturn(nullptr != removeNode);
115 :
116 286 : mNodesPool.ReleaseObject(removeNode);
117 : }
118 :
119 481 : CHIP_ERROR ReportSchedulerImpl::ScheduleReport(Timeout timeout, ReadHandlerNode * node, const Timestamp & now)
120 : {
121 : // Cancel Report if it is currently scheduled
122 481 : mTimerDelegate->CancelTimer(node);
123 481 : if (timeout == Milliseconds32(0))
124 : {
125 109 : node->TimerFired();
126 109 : return CHIP_NO_ERROR;
127 : }
128 744 : ReturnErrorOnFailure(mTimerDelegate->StartTimer(node, timeout));
129 :
130 372 : return CHIP_NO_ERROR;
131 : }
132 :
133 1114 : void ReportSchedulerImpl::CancelReport(ReadHandler * aReadHandler)
134 : {
135 1114 : ReadHandlerNode * node = FindReadHandlerNode(aReadHandler);
136 1114 : VerifyOrReturn(nullptr != node);
137 287 : mTimerDelegate->CancelTimer(node);
138 : }
139 :
140 113 : void ReportSchedulerImpl::UnregisterAllHandlers()
141 : {
142 113 : mNodesPool.ForEachActiveObject([this](ReadHandlerNode * node) {
143 18 : this->OnReadHandlerDestroyed(node->GetReadHandler());
144 18 : return Loop::Continue;
145 : });
146 113 : }
147 :
148 14 : bool ReportSchedulerImpl::IsReportScheduled(ReadHandler * aReadHandler)
149 : {
150 14 : ReadHandlerNode * node = FindReadHandlerNode(aReadHandler);
151 14 : VerifyOrReturnValue(nullptr != node, false);
152 11 : return mTimerDelegate->IsTimerActive(node);
153 : }
154 :
155 481 : CHIP_ERROR ReportSchedulerImpl::CalculateNextReportTimeout(Timeout & timeout, ReadHandlerNode * aNode, const Timestamp & now)
156 : {
157 481 : VerifyOrReturnError(nullptr != FindReadHandlerNode(aNode->GetReadHandler()), CHIP_ERROR_INVALID_ARGUMENT);
158 :
159 : // If the handler is reportable now, just schedule a report immediately
160 481 : if (aNode->IsReportableNow(now))
161 : {
162 : // If the handler is reportable now, just schedule a report immediately
163 100 : timeout = Milliseconds32(0);
164 : }
165 381 : else if (IsReadHandlerReportable(aNode->GetReadHandler()) && (aNode->GetMinTimestamp() > now))
166 : {
167 : // If the handler is reportable now, but the min interval is not elapsed, schedule a report for the moment the min interval
168 : // has elapsed
169 5 : timeout = aNode->GetMinTimestamp() - now;
170 : }
171 : else
172 : {
173 : // If the handler is not reportable now, schedule a report for the max interval
174 376 : timeout = aNode->GetMaxTimestamp() - now;
175 : }
176 481 : return CHIP_NO_ERROR;
177 : }
178 :
179 : } // namespace reporting
180 : } // namespace app
181 : } // namespace chip
|