forked from project-chip/connectedhomeip
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathEngine.h
293 lines (249 loc) · 10.6 KB
/
Engine.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
/*
*
* Copyright (c) 2021 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* This file defines Reporting Engine for a CHIP Interaction Model
*
*/
#pragma once
#include <access/AccessControl.h>
#include <app/MessageDef/ReportDataMessage.h>
#include <app/ReadHandler.h>
#include <app/util/basic-types.h>
#include <lib/core/CHIPCore.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/logging/CHIPLogging.h>
#include <messaging/ExchangeContext.h>
#include <messaging/ExchangeMgr.h>
#include <protocols/Protocols.h>
#include <system/SystemPacketBuffer.h>
#include <system/TLVPacketBufferBackingStore.h>
namespace chip {
namespace app {
class InteractionModelEngine;
class TestReadInteraction;
namespace reporting {
/*
* @class Engine
*
* @brief The reporting engine is responsible for generating reports to reader. It is able to find the intersection
* between the path interest set of each reader with what has changed in the publisher data store and generate tailored
* reports for each reader.
*
* At its core, it tries to gather and pack as much relevant attributes changes and/or events as possible into a report
* message before sending that to the reader. It continues to do so until it has no more work to do.
*/
class Engine
{
public:
/**
* Constructor Engine with a valid InteractionModelEngine pointer.
*/
Engine(InteractionModelEngine * apImEngine);
/**
* Initializes the reporting engine. Should only be called once.
*
* @retval #CHIP_NO_ERROR On success.
* @retval other Was unable to retrieve data and write it into the writer.
*/
CHIP_ERROR Init();
void Shutdown();
#if CONFIG_BUILD_FOR_HOST_UNIT_TEST
void SetWriterReserved(uint32_t aReservedSize) { mReservedSize = aReservedSize; }
void SetMaxAttributesPerChunk(uint32_t aMaxAttributesPerChunk) { mMaxAttributesPerChunk = aMaxAttributesPerChunk; }
#endif
/**
* Should be invoked when the device receives a Status report, or when the Report data request times out.
* This allows the engine to do some clean-up.
*
*/
void OnReportConfirm();
/**
* Main work-horse function that executes the run-loop asynchronously on the CHIP thread
*/
CHIP_ERROR ScheduleRun();
/**
* Application marks mutated change path and would be sent out in later report.
*/
CHIP_ERROR SetDirty(AttributePathParams & aAttributePathParams);
/**
* @brief
* Schedule the event delivery
*
*/
CHIP_ERROR ScheduleEventDelivery(ConcreteEventPath & aPath, uint32_t aBytesWritten);
/*
* Resets the tracker that tracks the currently serviced read handler.
* apReadHandler can be non-null to indicate that the reset is due to a
* specific ReadHandler being deallocated.
*/
void ResetReadHandlerTracker(ReadHandler * apReadHandlerBeingDeleted)
{
if (apReadHandlerBeingDeleted == mRunningReadHandler)
{
// Just decrement, so our increment after we finish running it will
// do the right thing.
--mCurReadHandlerIdx;
}
else
{
// No idea what to do here to make the indexing sane. Just start at
// the beginning. We need to do better here; see
// https://github.com/project-chip/connectedhomeip/issues/13809
mCurReadHandlerIdx = 0;
}
}
uint32_t GetNumReportsInFlight() const { return mNumReportsInFlight; }
uint64_t GetDirtySetGeneration() const { return mDirtyGeneration; }
/**
* Schedule event delivery to happen immediately and run reporting to get
* those reports into messages and on the wire. This can be done either for
* a specific fabric, identified by the provided FabricIndex, or across all
* fabrics if no FabricIndex is provided.
*/
void ScheduleUrgentEventDeliverySync(Optional<FabricIndex> fabricIndex = NullOptional);
#if CONFIG_BUILD_FOR_HOST_UNIT_TEST
size_t GetGlobalDirtySetSize() { return mGlobalDirtySet.Allocated(); }
#endif
private:
/**
* Main work-horse function that executes the run-loop.
*/
void Run();
friend class TestReportingEngine;
friend class ::chip::app::TestReadInteraction;
bool IsRunScheduled() const { return mRunScheduled; }
struct AttributePathParamsWithGeneration : public AttributePathParams
{
AttributePathParamsWithGeneration() {}
AttributePathParamsWithGeneration(const AttributePathParams aPath) : AttributePathParams(aPath) {}
uint64_t mGeneration = 0;
};
/**
* Build Single Report Data including attribute changes and event data stream, and send out
*
*/
CHIP_ERROR BuildAndSendSingleReportData(ReadHandler * apReadHandler);
CHIP_ERROR BuildSingleReportDataAttributeReportIBs(ReportDataMessage::Builder & reportDataBuilder, ReadHandler * apReadHandler,
bool * apHasMoreChunks, bool * apHasEncodedData);
CHIP_ERROR BuildSingleReportDataEventReports(ReportDataMessage::Builder & reportDataBuilder, ReadHandler * apReadHandler,
bool aBufferIsUsed, bool * apHasMoreChunks, bool * apHasEncodedData);
CHIP_ERROR RetrieveClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered,
AttributeReportIBs::Builder & aAttributeReportIBs,
const ConcreteReadAttributePath & aClusterInfo, AttributeEncodeState * apEncoderState);
CHIP_ERROR CheckAccessDeniedEventPaths(TLV::TLVWriter & aWriter, bool & aHasEncodedData, ReadHandler * apReadHandler);
// If version match, it means don't send, if version mismatch, it means send.
// If client sends the same path with multiple data versions, client will get the data back per the spec, because at least one
// of those will fail to match. This function should return false if either nothing in the list matches the given
// endpoint+cluster in the path or there is an entry in the list that matches the endpoint+cluster in the path but does not
// match the current data version of that cluster.
bool IsClusterDataVersionMatch(const SingleLinkedListNode<DataVersionFilter> * aDataVersionFilterList,
const ConcreteReadAttributePath & aPath);
/**
* Send Report via ReadHandler
*
*/
CHIP_ERROR SendReport(ReadHandler * apReadHandler, System::PacketBufferHandle && aPayload, bool aHasMoreChunks);
/**
* Generate and send the report data request when there exists subscription or read request
*
*/
static void Run(System::Layer * aSystemLayer, void * apAppState);
CHIP_ERROR ScheduleBufferPressureEventDelivery(uint32_t aBytesWritten);
void GetMinEventLogPosition(uint32_t & aMinLogPosition);
/**
* If the provided path is a superset of our of our existing paths, update that existing path to match the
* provided path.
*
* Return whether one of our paths is now a superset of the provided path.
*/
bool MergeOverlappedAttributePath(const AttributePathParams & aAttributePath);
/**
* If we are running out of ObjectPool for the global dirty set, we will try to merge the existing items by clusters.
*
* Returns whether we have released any paths.
*/
bool MergeDirtyPathsUnderSameCluster();
/**
* If we are running out of ObjectPool for the global dirty set and we cannot find a slot after merging the existing items by
* clusters, we will try to merge the existing items by endpoints.
*
* Returns whether we have released any paths.
*/
bool MergeDirtyPathsUnderSameEndpoint();
/**
* During the iterating of the paths, releasing the object in the inner loop will cause undefined behavior of the ObjectPool, so
* we replace the items to be cleared by a tomb first, then clear all the tombs after the iteration.
*
* Returns whether we have released any paths.
*/
bool ClearTombPaths();
CHIP_ERROR InsertPathIntoDirtySet(const AttributePathParams & aAttributePath);
inline void BumpDirtySetGeneration() { mDirtyGeneration++; }
/**
* Boolean to indicate if ScheduleRun is pending. This flag is used to prevent calling ScheduleRun multiple times
* within the same execution context to avoid applying too much pressure on platforms that use small, fixed size event queues.
*
*/
bool mRunScheduled = false;
/**
* The number of report date request in flight
*
*/
uint32_t mNumReportsInFlight = 0;
/**
* Current read handler index
*
*/
uint32_t mCurReadHandlerIdx = 0;
/**
* The read handler we're calling BuildAndSendSingleReportData on right now.
*/
ReadHandler * mRunningReadHandler = nullptr;
/**
* mGlobalDirtySet is used to track the set of attribute/event paths marked dirty for reporting purposes.
*
*/
#if CONFIG_BUILD_FOR_HOST_UNIT_TEST
// For unit tests, always use inline allocation for code coverage.
ObjectPool<AttributePathParamsWithGeneration, CHIP_IM_SERVER_MAX_NUM_DIRTY_SET, ObjectPoolMem::kInline> mGlobalDirtySet;
#else
ObjectPool<AttributePathParamsWithGeneration, CHIP_IM_SERVER_MAX_NUM_DIRTY_SET> mGlobalDirtySet;
#endif
/**
* A generation counter for the dirty attrbute set.
* ReadHandlers can save the generation value when generating reports.
*
* Then we can tell whether they might have missed reporting an attribute by
* comparing its generation counter to the saved one.
*
* mDirtySetGeneration will increase by one when SetDirty is called.
*
* Count it from 1, so 0 can be used in ReadHandler to indicate "the read handler has never
* completed a report".
*/
uint64_t mDirtyGeneration = 1;
#if CONFIG_BUILD_FOR_HOST_UNIT_TEST
uint32_t mReservedSize = 0;
uint32_t mMaxAttributesPerChunk = UINT32_MAX;
#endif
InteractionModelEngine * mpImEngine = nullptr;
};
}; // namespace reporting
}; // namespace app
}; // namespace chip