Coverage Report

Created: 2026-02-12 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/connectedhomeip/src/app/reporting/ReportSchedulerImpl.cpp
Line
Count
Source
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
void ReportSchedulerImpl::ReportTimerCallback()
32
0
{
33
0
    TEMPORARY_RETURN_IGNORED InteractionModelEngine::GetInstance()->GetReportingEngine().ScheduleRun();
34
0
}
35
36
1
ReportSchedulerImpl::ReportSchedulerImpl(TimerDelegate * aTimerDelegate) : ReportScheduler(aTimerDelegate)
37
1
{
38
1
    VerifyOrDie(nullptr != mTimerDelegate);
39
1
}
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
void ReportSchedulerImpl::OnEnterActiveMode()
44
0
{
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
void ReportSchedulerImpl::OnSubscriptionEstablished(ReadHandler * aReadHandler)
59
0
{
60
0
    ReadHandlerNode * newNode = FindReadHandlerNode(aReadHandler);
61
    // Handler must not be registered yet; it's just being constructed.
62
0
    VerifyOrDie(nullptr == newNode);
63
64
0
    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
0
    newNode = mNodesPool.CreateObject(aReadHandler, this, now);
69
70
0
    ChipLogProgress(DataManagement,
71
0
                    "Registered a ReadHandler that will schedule a report between system Timestamp: 0x" ChipLogFormatX64
72
0
                    " and system Timestamp 0x" ChipLogFormatX64 ".",
73
0
                    ChipLogValueX64(newNode->GetMinTimestamp().count()), ChipLogValueX64(newNode->GetMaxTimestamp().count()));
74
75
0
    mNumTotalSubscriptionsEstablished++;
76
0
}
77
78
void ReportSchedulerImpl::OnBecameReportable(ReadHandler * aReadHandler)
79
0
{
80
0
    ReadHandlerNode * node = FindReadHandlerNode(aReadHandler);
81
0
    VerifyOrReturn(nullptr != node);
82
83
0
    Timestamp now = mTimerDelegate->GetCurrentMonotonicTimestamp();
84
85
0
    Milliseconds32 newTimeout;
86
0
    TEMPORARY_RETURN_IGNORED CalculateNextReportTimeout(newTimeout, node, now);
87
0
    TEMPORARY_RETURN_IGNORED ScheduleReport(newTimeout, node, now);
88
0
}
89
90
void ReportSchedulerImpl::OnSubscriptionReportSent(ReadHandler * aReadHandler)
91
0
{
92
0
    ReadHandlerNode * node = FindReadHandlerNode(aReadHandler);
93
0
    VerifyOrReturn(nullptr != node);
94
95
0
    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
0
    node->SetCanBeSynced(false);
100
0
    node->SetIntervalTimeStamps(aReadHandler, now);
101
0
    Milliseconds32 newTimeout;
102
    // Reset the EngineRunScheduled flag so that the next report is scheduled correctly
103
0
    node->SetEngineRunScheduled(false);
104
0
    TEMPORARY_RETURN_IGNORED CalculateNextReportTimeout(newTimeout, node, now);
105
0
    TEMPORARY_RETURN_IGNORED ScheduleReport(newTimeout, node, now);
106
0
}
107
108
void ReportSchedulerImpl::OnReadHandlerDestroyed(ReadHandler * aReadHandler)
109
0
{
110
0
    CancelReport(aReadHandler);
111
112
0
    ReadHandlerNode * removeNode = FindReadHandlerNode(aReadHandler);
113
    // Nothing to remove if the handler is not found in the list
114
0
    VerifyOrReturn(nullptr != removeNode);
115
116
0
    mNodesPool.ReleaseObject(removeNode);
117
0
}
118
119
CHIP_ERROR ReportSchedulerImpl::ScheduleReport(Timeout timeout, ReadHandlerNode * node, const Timestamp & now)
120
0
{
121
    // Cancel Report if it is currently scheduled
122
0
    mTimerDelegate->CancelTimer(node);
123
0
    if (timeout == Milliseconds32(0))
124
0
    {
125
0
        node->TimerFired();
126
0
        return CHIP_NO_ERROR;
127
0
    }
128
0
    ReturnErrorOnFailure(mTimerDelegate->StartTimer(node, timeout));
129
130
0
    return CHIP_NO_ERROR;
131
0
}
132
133
void ReportSchedulerImpl::CancelReport(ReadHandler * aReadHandler)
134
0
{
135
0
    ReadHandlerNode * node = FindReadHandlerNode(aReadHandler);
136
0
    VerifyOrReturn(nullptr != node);
137
0
    mTimerDelegate->CancelTimer(node);
138
0
}
139
140
void ReportSchedulerImpl::UnregisterAllHandlers()
141
1
{
142
1
    mNodesPool.ForEachActiveObject([this](ReadHandlerNode * node) {
143
0
        this->OnReadHandlerDestroyed(node->GetReadHandler());
144
0
        return Loop::Continue;
145
0
    });
146
1
}
147
148
bool ReportSchedulerImpl::IsReportScheduled(ReadHandler * aReadHandler)
149
0
{
150
0
    ReadHandlerNode * node = FindReadHandlerNode(aReadHandler);
151
0
    VerifyOrReturnValue(nullptr != node, false);
152
0
    return mTimerDelegate->IsTimerActive(node);
153
0
}
154
155
CHIP_ERROR ReportSchedulerImpl::CalculateNextReportTimeout(Timeout & timeout, ReadHandlerNode * aNode, const Timestamp & now)
156
0
{
157
0
    VerifyOrReturnError(nullptr != FindReadHandlerNode(aNode->GetReadHandler()), CHIP_ERROR_INVALID_ARGUMENT);
158
159
    // If the handler is reportable now, just schedule a report immediately
160
0
    if (aNode->IsReportableNow(now))
161
0
    {
162
        // If the handler is reportable now, just schedule a report immediately
163
0
        timeout = Milliseconds32(0);
164
0
    }
165
0
    else if (IsReadHandlerReportable(aNode->GetReadHandler()) && (aNode->GetMinTimestamp() > now))
166
0
    {
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
0
        timeout = aNode->GetMinTimestamp() - now;
170
0
    }
171
0
    else
172
0
    {
173
        // If the handler is not reportable now, schedule a report for the max interval
174
0
        timeout = aNode->GetMaxTimestamp() - now;
175
0
    }
176
0
    return CHIP_NO_ERROR;
177
0
}
178
179
} // namespace reporting
180
} // namespace app
181
} // namespace chip