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 116 : void ReportSchedulerImpl::ReportTimerCallback()
32 : {
33 116 : TEMPORARY_RETURN_IGNORED InteractionModelEngine::GetInstance()->GetReportingEngine().ScheduleRun();
34 116 : }
35 :
36 66 : ReportSchedulerImpl::ReportSchedulerImpl(TimerDelegate * aTimerDelegate) : ReportScheduler(aTimerDelegate)
37 : {
38 66 : VerifyOrDie(nullptr != mTimerDelegate);
39 66 : }
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 310 : void ReportSchedulerImpl::OnSubscriptionEstablished(ReadHandler * aReadHandler)
59 : {
60 310 : ReadHandlerNode * newNode = FindReadHandlerNode(aReadHandler);
61 : // Handler must not be registered yet; it's just being constructed.
62 310 : VerifyOrDie(nullptr == newNode);
63 :
64 310 : 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 310 : newNode = mNodesPool.CreateObject(aReadHandler, this, now);
69 :
70 310 : 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 310 : mNumTotalSubscriptionsEstablished++;
76 310 : }
77 :
78 765 : void ReportSchedulerImpl::OnBecameReportable(ReadHandler * aReadHandler)
79 : {
80 765 : ReadHandlerNode * node = FindReadHandlerNode(aReadHandler);
81 765 : VerifyOrReturn(nullptr != node);
82 :
83 505 : Timestamp now = mTimerDelegate->GetCurrentMonotonicTimestamp();
84 :
85 : Milliseconds32 newTimeout;
86 505 : TEMPORARY_RETURN_IGNORED CalculateNextReportTimeout(newTimeout, node, now);
87 505 : TEMPORARY_RETURN_IGNORED ScheduleReport(newTimeout, node, now);
88 : }
89 :
90 108 : void ReportSchedulerImpl::OnSubscriptionReportSent(ReadHandler * aReadHandler)
91 : {
92 108 : ReadHandlerNode * node = FindReadHandlerNode(aReadHandler);
93 108 : VerifyOrReturn(nullptr != node);
94 :
95 108 : 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 108 : node->SetCanBeSynced(false);
100 108 : node->SetIntervalTimeStamps(aReadHandler, now);
101 : Milliseconds32 newTimeout;
102 : // Reset the EngineRunScheduled flag so that the next report is scheduled correctly
103 108 : node->SetEngineRunScheduled(false);
104 108 : TEMPORARY_RETURN_IGNORED CalculateNextReportTimeout(newTimeout, node, now);
105 108 : TEMPORARY_RETURN_IGNORED ScheduleReport(newTimeout, node, now);
106 : }
107 :
108 1109 : void ReportSchedulerImpl::OnReadHandlerDestroyed(ReadHandler * aReadHandler)
109 : {
110 1109 : CancelReport(aReadHandler);
111 :
112 1109 : ReadHandlerNode * removeNode = FindReadHandlerNode(aReadHandler);
113 : // Nothing to remove if the handler is not found in the list
114 1109 : VerifyOrReturn(nullptr != removeNode);
115 :
116 284 : mNodesPool.ReleaseObject(removeNode);
117 : }
118 :
119 460 : CHIP_ERROR ReportSchedulerImpl::ScheduleReport(Timeout timeout, ReadHandlerNode * node, const Timestamp & now)
120 : {
121 : // Cancel Report if it is currently scheduled
122 460 : mTimerDelegate->CancelTimer(node);
123 460 : if (timeout == Milliseconds32(0))
124 : {
125 102 : node->TimerFired();
126 102 : return CHIP_NO_ERROR;
127 : }
128 716 : ReturnErrorOnFailure(mTimerDelegate->StartTimer(node, timeout));
129 :
130 358 : return CHIP_NO_ERROR;
131 : }
132 :
133 1110 : void ReportSchedulerImpl::CancelReport(ReadHandler * aReadHandler)
134 : {
135 1110 : ReadHandlerNode * node = FindReadHandlerNode(aReadHandler);
136 1110 : VerifyOrReturn(nullptr != node);
137 285 : mTimerDelegate->CancelTimer(node);
138 : }
139 :
140 101 : void ReportSchedulerImpl::UnregisterAllHandlers()
141 : {
142 101 : mNodesPool.ForEachActiveObject([this](ReadHandlerNode * node) {
143 18 : this->OnReadHandlerDestroyed(node->GetReadHandler());
144 18 : return Loop::Continue;
145 : });
146 101 : }
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 460 : CHIP_ERROR ReportSchedulerImpl::CalculateNextReportTimeout(Timeout & timeout, ReadHandlerNode * aNode, const Timestamp & now)
156 : {
157 460 : VerifyOrReturnError(nullptr != FindReadHandlerNode(aNode->GetReadHandler()), CHIP_ERROR_INVALID_ARGUMENT);
158 :
159 : // If the handler is reportable now, just schedule a report immediately
160 460 : if (aNode->IsReportableNow(now))
161 : {
162 : // If the handler is reportable now, just schedule a report immediately
163 93 : timeout = Milliseconds32(0);
164 : }
165 367 : 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 362 : timeout = aNode->GetMaxTimestamp() - now;
175 : }
176 460 : return CHIP_NO_ERROR;
177 : }
178 :
179 : } // namespace reporting
180 : } // namespace app
181 : } // namespace chip
|