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 129 : void ReportSchedulerImpl::ReportTimerCallback()
32 : {
33 129 : InteractionModelEngine::GetInstance()->GetReportingEngine().ScheduleRun();
34 129 : }
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 : // This method is called after the report is sent, so the ReadHandler is no longer reportable, and thus CanBeSynced and
96 : // EngineRunScheduled of the node associated with the ReadHandler are set to false here.
97 113 : node->SetCanBeSynced(false);
98 113 : node->SetIntervalTimeStamps(aReadHandler, now);
99 : Milliseconds32 newTimeout;
100 : // Reset the EngineRunScheduled flag so that the next report is scheduled correctly
101 113 : node->SetEngineRunScheduled(false);
102 113 : CalculateNextReportTimeout(newTimeout, node, now);
103 113 : ScheduleReport(newTimeout, node, now);
104 : }
105 :
106 1033 : void ReportSchedulerImpl::OnReadHandlerDestroyed(ReadHandler * aReadHandler)
107 : {
108 1033 : CancelReport(aReadHandler);
109 :
110 1033 : ReadHandlerNode * removeNode = FindReadHandlerNode(aReadHandler);
111 : // Nothing to remove if the handler is not found in the list
112 1033 : VerifyOrReturn(nullptr != removeNode);
113 :
114 211 : mNodesPool.ReleaseObject(removeNode);
115 : }
116 :
117 392 : CHIP_ERROR ReportSchedulerImpl::ScheduleReport(Timeout timeout, ReadHandlerNode * node, const Timestamp & now)
118 : {
119 : // Cancel Report if it is currently scheduled
120 392 : mTimerDelegate->CancelTimer(node);
121 392 : if (timeout == Milliseconds32(0))
122 : {
123 102 : node->TimerFired();
124 102 : return CHIP_NO_ERROR;
125 : }
126 290 : ReturnErrorOnFailure(mTimerDelegate->StartTimer(node, timeout));
127 :
128 290 : return CHIP_NO_ERROR;
129 : }
130 :
131 1034 : void ReportSchedulerImpl::CancelReport(ReadHandler * aReadHandler)
132 : {
133 1034 : ReadHandlerNode * node = FindReadHandlerNode(aReadHandler);
134 1034 : VerifyOrReturn(nullptr != node);
135 212 : mTimerDelegate->CancelTimer(node);
136 : }
137 :
138 83 : void ReportSchedulerImpl::UnregisterAllHandlers()
139 : {
140 83 : mNodesPool.ForEachActiveObject([this](ReadHandlerNode * node) {
141 18 : this->OnReadHandlerDestroyed(node->GetReadHandler());
142 18 : return Loop::Continue;
143 : });
144 83 : }
145 :
146 14 : bool ReportSchedulerImpl::IsReportScheduled(ReadHandler * aReadHandler)
147 : {
148 14 : ReadHandlerNode * node = FindReadHandlerNode(aReadHandler);
149 14 : VerifyOrReturnValue(nullptr != node, false);
150 11 : return mTimerDelegate->IsTimerActive(node);
151 : }
152 :
153 392 : CHIP_ERROR ReportSchedulerImpl::CalculateNextReportTimeout(Timeout & timeout, ReadHandlerNode * aNode, const Timestamp & now)
154 : {
155 392 : VerifyOrReturnError(nullptr != FindReadHandlerNode(aNode->GetReadHandler()), CHIP_ERROR_INVALID_ARGUMENT);
156 :
157 : // If the handler is reportable now, just schedule a report immediately
158 392 : if (aNode->IsReportableNow(now))
159 : {
160 : // If the handler is reportable now, just schedule a report immediately
161 93 : timeout = Milliseconds32(0);
162 : }
163 299 : else if (IsReadHandlerReportable(aNode->GetReadHandler()) && (aNode->GetMinTimestamp() > now))
164 : {
165 : // If the handler is reportable now, but the min interval is not elapsed, schedule a report for the moment the min interval
166 : // has elapsed
167 5 : timeout = aNode->GetMinTimestamp() - now;
168 : }
169 : else
170 : {
171 : // If the handler is not reportable now, schedule a report for the max interval
172 294 : timeout = aNode->GetMaxTimestamp() - now;
173 : }
174 392 : return CHIP_NO_ERROR;
175 : }
176 :
177 : } // namespace reporting
178 : } // namespace app
179 : } // namespace chip
|