Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021 Project CHIP Authors
4 : * All rights reserved.
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : #pragma once
20 :
21 : #include "lib/core/CHIPError.h"
22 : #include "system/SystemPacketBuffer.h"
23 : #include "system/TLVPacketBufferBackingStore.h"
24 : #include <app/AppConfig.h>
25 : #include <app/AttributePathParams.h>
26 : #include <app/BufferedReadCallback.h>
27 : #include <app/ConcreteAttributePath.h>
28 : #include <app/ReadClient.h>
29 : #include <app/data-model/DecodableList.h>
30 : #include <app/data-model/Decode.h>
31 : #include <lib/support/Variant.h>
32 : #include <list>
33 : #include <map>
34 : #include <queue>
35 : #include <set>
36 : #include <vector>
37 :
38 : #if CHIP_CONFIG_ENABLE_READ_CLIENT
39 : namespace chip {
40 : namespace app {
41 : /*
42 : * This implements a cluster state cache designed to aggregate both attribute and event data received by a client
43 : * from either read or subscribe interactions and keep it resident and available for clients to
44 : * query at any time while the cache is active.
45 : *
46 : * The cache can be used with either read/subscribe, with the consumer connecting it up appropriately
47 : * to the right ReadClient instance.
48 : *
49 : * The cache provides an up-to-date and consistent view of the state of a target node, with the scope of the
50 : * state being determined by the associated ReadClient's path set.
51 : *
52 : * The cache provides a number of getters and helper functions to iterate over the topology
53 : * of the received data which is organized by endpoint, cluster and attribute ID (for attributes). These permit greater
54 : * flexibility when dealing with interactions that use wildcards heavily.
55 : *
56 : * For events, functions that permit iteration over the cached events sorted by event number are provided.
57 : *
58 : * The data is stored internally in the cache as TLV. This permits re-use of the existing cluster objects
59 : * to de-serialize the state on-demand.
60 : *
61 : * The cache serves as a callback adapter as well in that it 'forwards' the ReadClient::Callback calls transparently
62 : * through to a registered callback. In addition, it provides its own enhancements to the base ReadClient::Callback
63 : * to make it easier to know what has changed in the cache.
64 : *
65 : * **NOTE**
66 : * 1. This already includes the BufferedReadCallback, so there is no need to add that to the ReadClient callback chain.
67 : * 2. The same cache cannot be used by multiple subscribe/read interactions at the same time.
68 : *
69 : */
70 : template <bool CanEnableDataCaching>
71 : class ClusterStateCacheT : protected ReadClient::Callback
72 : {
73 : public:
74 : class Callback : public ReadClient::Callback
75 : {
76 : public:
77 11 : Callback() = default;
78 :
79 : // Callbacks are not expected to be copyable or movable.
80 : Callback(const Callback &) = delete;
81 : Callback(Callback &&) = delete;
82 : Callback & operator=(const Callback &) = delete;
83 : Callback & operator=(Callback &&) = delete;
84 :
85 : /*
86 : * Called anytime an attribute value has changed in the cache
87 : */
88 91 : virtual void OnAttributeChanged(ClusterStateCacheT * cache, const ConcreteAttributePath & path){};
89 :
90 : /*
91 : * Called anytime any attribute in a cluster has changed in the cache
92 : */
93 23 : virtual void OnClusterChanged(ClusterStateCacheT * cache, EndpointId endpointId, ClusterId clusterId){};
94 :
95 : /*
96 : * Called anytime an endpoint was added to the cache
97 : */
98 5 : virtual void OnEndpointAdded(ClusterStateCacheT * cache, EndpointId endpointId){};
99 : };
100 :
101 : /**
102 : *
103 : * @param [in] callback the derived callback which inherit from ReadClient::Callback
104 : * @param [in] highestReceivedEventNumber optional highest received event number, if cache receive the events with the number
105 : * less than or equal to this value, skip those events
106 : */
107 11 : ClusterStateCacheT(Callback & callback, Optional<EventNumber> highestReceivedEventNumber = Optional<EventNumber>::Missing()) :
108 11 : mCallback(callback), mBufferedReader(*this)
109 : {
110 11 : mHighestReceivedEventNumber = highestReceivedEventNumber;
111 11 : }
112 :
113 : template <bool DataCachingEnabled = CanEnableDataCaching, std::enable_if_t<DataCachingEnabled, bool> = true>
114 2 : ClusterStateCacheT(Callback & callback, Optional<EventNumber> highestReceivedEventNumber = Optional<EventNumber>::Missing(),
115 : bool cacheData = true) :
116 2 : mCallback(callback),
117 2 : mBufferedReader(*this), mCacheData(cacheData)
118 : {
119 2 : mHighestReceivedEventNumber = highestReceivedEventNumber;
120 2 : }
121 :
122 : ClusterStateCacheT(const ClusterStateCacheT &) = delete;
123 : ClusterStateCacheT(ClusterStateCacheT &&) = delete;
124 : ClusterStateCacheT & operator=(const ClusterStateCacheT &) = delete;
125 : ClusterStateCacheT & operator=(ClusterStateCacheT &&) = delete;
126 :
127 2 : void SetHighestReceivedEventNumber(EventNumber highestReceivedEventNumber)
128 : {
129 2 : mHighestReceivedEventNumber.SetValue(highestReceivedEventNumber);
130 2 : }
131 :
132 : /*
133 : * When registering as a callback to the ReadClient, the ClusterStateCache cannot not be passed as a callback
134 : * directly. Instead, utilize this method below to correctly set up the callback chain such that
135 : * the buffered reader is the first callback in the chain before calling into cache subsequently.
136 : */
137 560 : ReadClient::Callback & GetBufferedCallback() { return mBufferedReader; }
138 :
139 : /*
140 : * Retrieve the value of an attribute from the cache (if present) given a concrete path by decoding
141 : * it using DataModel::Decode into the in-out argument 'value'.
142 : *
143 : * For some types of attributes, the value for the attribute is directly backed by the underlying TLV buffer
144 : * and has pointers into that buffer. (e.g octet strings, char strings and lists). This buffer only remains
145 : * valid until the cached value for that path is updated, so it must not be held
146 : * across any async call boundaries.
147 : *
148 : * The template parameter AttributeObjectTypeT is generally expected to be a
149 : * ClusterName::Attributes::AttributeName::DecodableType, but any
150 : * object that can be decoded using the DataModel::Decode machinery will work.
151 : *
152 : * Notable return values:
153 : * - If the provided attribute object's Cluster and Attribute IDs don't match that of the provided path,
154 : * a CHIP_ERROR_SCHEMA_MISMATCH shall be returned.
155 : *
156 : * - If neither data or status for the specified path don't exist in the cache, CHIP_ERROR_KEY_NOT_FOUND
157 : * shall be returned.
158 : *
159 : * - If a StatusIB is present in the cache instead of data, a CHIP_ERROR_IM_STATUS_CODE_RECEIVED error
160 : * shall be returned from this call instead. The actual StatusIB can be retrieved using the GetStatus() API below.
161 : *
162 : */
163 : template <typename AttributeObjectTypeT>
164 11 : CHIP_ERROR Get(const ConcreteAttributePath & path, typename AttributeObjectTypeT::DecodableType & value) const
165 : {
166 11 : TLV::TLVReader reader;
167 :
168 11 : if (path.mClusterId != AttributeObjectTypeT::GetClusterId() || path.mAttributeId != AttributeObjectTypeT::GetAttributeId())
169 : {
170 0 : return CHIP_ERROR_SCHEMA_MISMATCH;
171 : }
172 :
173 11 : ReturnErrorOnFailure(Get(path, reader));
174 10 : return DataModel::Decode(reader, value);
175 : }
176 :
177 : /**
178 : * Get the value of a particular attribute for the given endpoint. See the
179 : * documentation for Get() with a ConcreteAttributePath above.
180 : */
181 : template <typename AttributeObjectTypeT>
182 0 : CHIP_ERROR Get(EndpointId endpoint, typename AttributeObjectTypeT::DecodableType & value) const
183 : {
184 0 : ConcreteAttributePath path(endpoint, AttributeObjectTypeT::GetClusterId(), AttributeObjectTypeT::GetAttributeId());
185 0 : return Get<AttributeObjectTypeT>(path, value);
186 : }
187 :
188 : /*
189 : * Retrieve the StatusIB for a given attribute if one exists currently in the cache.
190 : *
191 : * Notable return values:
192 : * - If neither data or status for the specified path don't exist in the cache, CHIP_ERROR_KEY_NOT_FOUND
193 : * shall be returned.
194 : *
195 : * - If data exists in the cache instead of status, CHIP_ERROR_INVALID_ARGUMENT shall be returned.
196 : *
197 : */
198 : CHIP_ERROR GetStatus(const ConcreteAttributePath & path, StatusIB & status) const;
199 :
200 : /*
201 : * Encapsulates a StatusIB and a ConcreteAttributePath pair.
202 : */
203 : struct AttributeStatus
204 : {
205 1 : AttributeStatus(const ConcreteAttributePath & path, StatusIB & status) : mPath(path), mStatus(status) {}
206 : ConcreteAttributePath mPath;
207 : StatusIB mStatus;
208 : };
209 :
210 : /*
211 : * Retrieve the value of an entire cluster instance from the cache (if present) given a path
212 : * and decode it using DataModel::Decode into the in-out argument 'value'. If any StatusIBs
213 : * are present in the cache instead of data, they will be provided in the statusList argument.
214 : *
215 : * For some types of attributes, the value for the attribute is directly backed by the underlying TLV buffer
216 : * and has pointers into that buffer. (e.g octet strings, char strings and lists). This buffer only remains
217 : * valid until the cached value for that path is updated, so it must not be held
218 : * across any async call boundaries.
219 : *
220 : * The template parameter ClusterObjectT is generally expected to be a
221 : * ClusterName::Attributes::DecodableType, but any
222 : * object that can be decoded using the DataModel::Decode machinery will work.
223 : *
224 : * Notable return values:
225 : * - If neither data or status for the specified path exist in the cache, CHIP_ERROR_KEY_NOT_FOUND
226 : * shall be returned.
227 : *
228 : */
229 : template <typename ClusterObjectTypeT>
230 11 : CHIP_ERROR Get(EndpointId endpointId, ClusterId clusterId, ClusterObjectTypeT & value,
231 : std::list<AttributeStatus> & statusList) const
232 : {
233 11 : statusList.clear();
234 :
235 11 : return ForEachAttribute(endpointId, clusterId, [&value, this, &statusList](const ConcreteAttributePath & path) {
236 13 : TLV::TLVReader reader;
237 : CHIP_ERROR err;
238 :
239 13 : err = Get(path, reader);
240 13 : if (err == CHIP_ERROR_IM_STATUS_CODE_RECEIVED)
241 : {
242 1 : StatusIB status;
243 1 : ReturnErrorOnFailure(GetStatus(path, status));
244 1 : statusList.push_back(AttributeStatus(path, status));
245 1 : err = CHIP_NO_ERROR;
246 : }
247 12 : else if (err == CHIP_NO_ERROR)
248 : {
249 12 : ReturnErrorOnFailure(DataModel::Decode(reader, path, value));
250 : }
251 : else
252 : {
253 0 : return err;
254 : }
255 :
256 13 : return CHIP_NO_ERROR;
257 11 : });
258 : }
259 :
260 : /*
261 : * Retrieve the value of an attribute by updating a in-out TLVReader to be positioned
262 : * right at the attribute value.
263 : *
264 : * The underlying TLV buffer only remains valid until the cached value for that path is updated, so it must
265 : * not be held across any async call boundaries.
266 : *
267 : * Notable return values:
268 : * - If neither data nor status for the specified path exist in the cache, CHIP_ERROR_KEY_NOT_FOUND
269 : * shall be returned.
270 : *
271 : * - If a StatusIB is present in the cache instead of data, a CHIP_ERROR_IM_STATUS_CODE_RECEIVED error
272 : * shall be returned from this call instead. The actual StatusIB can be retrieved using the GetStatus() API above.
273 : *
274 : */
275 : CHIP_ERROR Get(const ConcreteAttributePath & path, TLV::TLVReader & reader) const;
276 :
277 : /*
278 : * Retrieve the data version for the given cluster. If there is no data for the specified path in the cache,
279 : * CHIP_ERROR_KEY_NOT_FOUND shall be returned. Otherwise aVersion will be set to the
280 : * current data version for the cluster (which may have no value if we don't have a known data version
281 : * for it, for example because none of our paths were wildcards that covered the whole cluster).
282 : */
283 : CHIP_ERROR GetVersion(const ConcreteClusterPath & path, Optional<DataVersion> & aVersion) const;
284 :
285 : /*
286 : * Get highest received event number.
287 : */
288 13 : virtual CHIP_ERROR GetHighestReceivedEventNumber(Optional<EventNumber> & aEventNumber) final
289 : {
290 13 : aEventNumber = mHighestReceivedEventNumber;
291 13 : return CHIP_NO_ERROR;
292 : }
293 :
294 : /*
295 : * Retrieve the value of an event from the cache given an EventNumber by decoding
296 : * it using DataModel::Decode into the in-out argument 'value'.
297 : *
298 : * This should be used in conjunction with the ForEachEvent() iterator function to
299 : * retrieve the EventHeader (and corresponding metadata information for the event) along with its EventNumber.
300 : *
301 : * For some types of events, the values for the fields in the event are directly backed by the underlying TLV buffer
302 : * and have pointers into that buffer. (e.g octet strings, char strings and lists). Unlike its attribute counterpart,
303 : * these pointers are stable and will not change until a call to `ClearEventCache` happens.
304 : *
305 : * The template parameter EventObjectTypeT is generally expected to be a
306 : * ClusterName::Events::EventName::DecodableType, but any
307 : * object that can be decoded using the DataModel::Decode machinery will work.
308 : *
309 : * Notable return values:
310 : * - If the provided event object's Cluster and Event IDs don't match those of the event in the cache,
311 : * a CHIP_ERROR_SCHEMA_MISMATCH shall be returned.
312 : *
313 : * - If event doesn't exist in the cache, CHIP_ERROR_KEY_NOT_FOUND
314 : * shall be returned.
315 : */
316 :
317 : template <typename EventObjectTypeT>
318 44 : CHIP_ERROR Get(EventNumber eventNumber, EventObjectTypeT & value) const
319 : {
320 44 : TLV::TLVReader reader;
321 : CHIP_ERROR err;
322 :
323 44 : auto * eventData = GetEventData(eventNumber, err);
324 44 : ReturnErrorOnFailure(err);
325 :
326 44 : if (eventData->first.mPath.mClusterId != value.GetClusterId() || eventData->first.mPath.mEventId != value.GetEventId())
327 : {
328 0 : return CHIP_ERROR_SCHEMA_MISMATCH;
329 : }
330 :
331 44 : ReturnErrorOnFailure(Get(eventNumber, reader));
332 44 : return DataModel::Decode(reader, value);
333 : }
334 :
335 : /*
336 : * Retrieve the data of an event by updating a in-out TLVReader to be positioned
337 : * right at the structure that encapsulates the event payload.
338 : *
339 : * Notable return values:
340 : * - If no event with a matching eventNumber exists in the cache, CHIP_ERROR_KEY_NOT_FOUND
341 : * shall be returned.
342 : *
343 : */
344 : CHIP_ERROR Get(EventNumber eventNumber, TLV::TLVReader & reader) const;
345 :
346 : /*
347 : * Retrieve the StatusIB for a specific event from the event status cache (if one exists).
348 : * Otherwise, a CHIP_ERROR_KEY_NOT_FOUND error will be returned.
349 : *
350 : * This is to be used with the `ForEachEventStatus` iterator function.
351 : *
352 : * NOTE: Receipt of a StatusIB does not affect any pre-existing or future event data entries in the cache (and vice-versa).
353 : *
354 : */
355 : CHIP_ERROR GetStatus(const ConcreteEventPath & path, StatusIB & status) const;
356 :
357 : /*
358 : * Execute an iterator function that is called for every attribute
359 : * in a given endpoint and cluster. The function when invoked is provided a concrete attribute path
360 : * to every attribute that matches in the cache.
361 : *
362 : * The iterator is expected to have this signature:
363 : * CHIP_ERROR IteratorFunc(const ConcreteAttributePath &path);
364 : *
365 : * Notable return values:
366 : * - If a cluster instance corresponding to endpointId and clusterId doesn't exist in the cache,
367 : * CHIP_ERROR_KEY_NOT_FOUND shall be returned.
368 : *
369 : * - If func returns an error, that will result in termination of any further iteration over attributes
370 : * and that error shall be returned back up to the original call to this function.
371 : *
372 : */
373 : template <typename IteratorFunc>
374 20 : CHIP_ERROR ForEachAttribute(EndpointId endpointId, ClusterId clusterId, IteratorFunc func) const
375 : {
376 : CHIP_ERROR err;
377 :
378 20 : auto clusterState = GetClusterState(endpointId, clusterId, err);
379 20 : ReturnErrorOnFailure(err);
380 :
381 43 : for (auto & attributeIter : clusterState->mAttributes)
382 : {
383 23 : const ConcreteAttributePath path(endpointId, clusterId, attributeIter.first);
384 23 : ReturnErrorOnFailure(func(path));
385 : }
386 :
387 20 : return CHIP_NO_ERROR;
388 : }
389 :
390 : /*
391 : * Execute an iterator function that is called for every attribute
392 : * for a given cluster across all endpoints in the cache. The function is passed a
393 : * concrete attribute path to every attribute that matches in the cache.
394 : *
395 : * The iterator is expected to have this signature:
396 : * CHIP_ERROR IteratorFunc(const ConcreteAttributePath &path);
397 : *
398 : * Notable return values:
399 : * - If func returns an error, that will result in termination of any further iteration over attributes
400 : * and that error shall be returned back up to the original call to this function.
401 : *
402 : */
403 : template <typename IteratorFunc>
404 9 : CHIP_ERROR ForEachAttribute(ClusterId clusterId, IteratorFunc func) const
405 : {
406 19 : for (auto & endpointIter : mCache)
407 : {
408 20 : for (auto & clusterIter : endpointIter.second)
409 : {
410 10 : if (clusterIter.first == clusterId)
411 : {
412 21 : for (auto & attributeIter : clusterIter.second.mAttributes)
413 : {
414 11 : const ConcreteAttributePath path(endpointIter.first, clusterId, attributeIter.first);
415 11 : ReturnErrorOnFailure(func(path));
416 : }
417 : }
418 : }
419 : }
420 9 : return CHIP_NO_ERROR;
421 : }
422 :
423 : /*
424 : * Execute an iterator function that is called for every attribute
425 : * in the cache. The function is passed a concrete attribute path
426 : * to every attribute in the cache.
427 : *
428 : * The iterator is expected to have this signature:
429 : * CHIP_ERROR IteratorFunc(const ConcreteAttributePath &path);
430 : *
431 : * Notable return values:
432 : * - If func returns an error, that will result in termination of any further iteration over attributes
433 : * and that error shall be returned back up to the original call to this function.
434 : */
435 : template <typename IteratorFunc>
436 9 : CHIP_ERROR ForEachAttribute(IteratorFunc func) const
437 : {
438 19 : for (const auto & [endpointId, endpointState] : mCache)
439 : {
440 20 : for (const auto & [clusterId, clusterState] : endpointState)
441 : {
442 21 : for (const auto & attributeIter : clusterState.mAttributes)
443 : {
444 11 : const ConcreteAttributePath path(endpointId, clusterId, attributeIter.first);
445 11 : ReturnErrorOnFailure(func(path));
446 : }
447 : }
448 : }
449 9 : return CHIP_NO_ERROR;
450 : }
451 :
452 : /*
453 : * Execute an iterator function that is called for every cluster
454 : * in a given endpoint and passed a ClusterId for every cluster that
455 : * matches.
456 : *
457 : * The iterator is expected to have this signature:
458 : * CHIP_ERROR IteratorFunc(ClusterId clusterId);
459 : *
460 : * Notable return values:
461 : * - If func returns an error, that will result in termination of any further iteration over attributes
462 : * and that error shall be returned back up to the original call to this function.
463 : *
464 : */
465 : template <typename IteratorFunc>
466 : CHIP_ERROR ForEachCluster(EndpointId endpointId, IteratorFunc func) const
467 : {
468 : auto endpointIter = mCache.find(endpointId);
469 : if (endpointIter->first == endpointId)
470 : {
471 : for (auto & clusterIter : endpointIter->second)
472 : {
473 : ReturnErrorOnFailure(func(clusterIter.first));
474 : }
475 : }
476 : return CHIP_NO_ERROR;
477 : }
478 :
479 : /*
480 : * Execute an iterator function that is called for every event in the event data cache that satisfies the following
481 : * conditions:
482 : * - It matches the provided path filter
483 : * - Its event number is greater than or equal to the provided minimum event number filter.
484 : *
485 : * Each filter argument can be omitted from the match criteria above by passing in an empty EventPathParams() and/or
486 : * a minimum event filter of 0.
487 : *
488 : * This iterator is called in increasing order from the event with the lowest event number to the highest.
489 : *
490 : * The function is passed a const reference to the EventHeader associated with that event.
491 : *
492 : * The iterator is expected to have this signature:
493 : * CHIP_ERROR IteratorFunc(const EventHeader & eventHeader);
494 : *
495 : * Notable return values:
496 : * - If func returns an error, that will result in termination of any further iteration over events
497 : * and that error shall be returned back up to the original call to this function.
498 : *
499 : */
500 : template <typename IteratorFunc>
501 11 : CHIP_ERROR ForEachEventData(IteratorFunc func, EventPathParams pathFilter = EventPathParams(),
502 : EventNumber minEventNumberFilter = 0) const
503 : {
504 57 : for (const auto & item : mEventDataCache)
505 : {
506 46 : if (pathFilter.IsEventPathSupersetOf(item.first.mPath) && item.first.mEventNumber >= minEventNumberFilter)
507 : {
508 44 : ReturnErrorOnFailure(func(item.first));
509 : }
510 : }
511 :
512 11 : return CHIP_NO_ERROR;
513 : }
514 :
515 : /*
516 : * Execute an iterator function that is called for every StatusIB in the event status cache.
517 : *
518 : * The iterator is expected to have this signature:
519 : * CHIP_ERROR IteratorFunc(const ConcreteEventPath & eventPath, const StatusIB & statusIB);
520 : *
521 : * Notable return values:
522 : * - If func returns an error, that will result in termination of any further iteration over events
523 : * and that error shall be returned back up to the original call to this function.
524 : *
525 : * NOTE: Receipt of a StatusIB does not affect any pre-existing event data entries in the cache (and vice-versa).
526 : *
527 : */
528 : template <typename IteratorFunc>
529 : CHIP_ERROR ForEachEventStatus(IteratorFunc func) const
530 : {
531 : for (const auto & item : mEventStatusCache)
532 : {
533 : ReturnErrorOnFailure(func(item.first, item.second));
534 : }
535 : }
536 :
537 : /*
538 : * Clear out all the attribute data and DataVersions stored for a given endpoint.
539 : */
540 : void ClearAttributes(EndpointId endpoint);
541 :
542 : /*
543 : * Clear out all the attribute data and the DataVersion stored for a given cluster.
544 : */
545 : void ClearAttributes(const ConcreteClusterPath & cluster);
546 :
547 : /*
548 : * Clear out the data (or size, if not storing data) stored for an
549 : * attribute.
550 : */
551 : void ClearAttribute(const ConcreteAttributePath & attribute);
552 :
553 : /*
554 : * Clear out the event data and status caches.
555 : *
556 : * By default, this will not clear out any internally tracked event counters, specifically:
557 : * - the highest event number seen so far. This is used in reads/subscribe requests to express to the receiving
558 : * server to not send events that the client has already seen so far.
559 : *
560 : * That can be over-ridden by passing in 'true' to `resetTrackedEventCounters`.
561 : *
562 : */
563 4 : void ClearEventCache(bool resetTrackedEventCounters = false)
564 : {
565 4 : mEventDataCache.clear();
566 4 : if (resetTrackedEventCounters)
567 : {
568 2 : mHighestReceivedEventNumber.ClearValue();
569 : }
570 :
571 4 : mEventStatusCache.clear();
572 4 : }
573 :
574 : /*
575 : * Get the last concrete report data path, if path is not concrete cluster path, return CHIP_ERROR_NOT_FOUND
576 : *
577 : */
578 : CHIP_ERROR GetLastReportDataPath(ConcreteClusterPath & aPath);
579 :
580 : private:
581 : // An attribute state can be one of three things:
582 : // * If we got a path-specific error for the attribute, the corresponding
583 : // status.
584 : // * If we got data for the attribute and we are storing data ourselves, the
585 : // data.
586 : // * If we got data for the attribute and we are not storing data
587 : // oureselves, the size of the data, so we can still prioritize sending
588 : // DataVersions correctly.
589 : //
590 : // The data for a single attribute is not going to be gigabytes in size, so
591 : // using uint32_t for the size is fine; on 64-bit systems this can save
592 : // quite a bit of space.
593 : using AttributeData = Platform::ScopedMemoryBufferWithSize<uint8_t>;
594 : using AttributeState = std::conditional_t<CanEnableDataCaching, Variant<StatusIB, AttributeData, uint32_t>, uint32_t>;
595 : // mPendingDataVersion represents a tentative data version for a cluster that we have gotten some reports for.
596 : //
597 : // mCurrentDataVersion represents a known data version for a cluster. In order for this to have a
598 : // value the cluster must be included in a path in mRequestPathSet that has a wildcard attribute
599 : // and we must not be in the middle of receiving reports for that cluster.
600 : struct ClusterState
601 : {
602 : std::map<AttributeId, AttributeState> mAttributes;
603 : Optional<DataVersion> mPendingDataVersion;
604 : Optional<DataVersion> mCommittedDataVersion;
605 : };
606 : using EndpointState = std::map<ClusterId, ClusterState>;
607 : using NodeState = std::map<EndpointId, EndpointState>;
608 :
609 : struct Comparator
610 : {
611 1054 : bool operator()(const AttributePathParams & x, const AttributePathParams & y) const
612 : {
613 1054 : return x.mEndpointId < y.mEndpointId || x.mClusterId < y.mClusterId;
614 : }
615 : };
616 :
617 : using EventData = std::pair<EventHeader, System::PacketBufferHandle>;
618 :
619 : //
620 : // This is a custom comparator for use with the std::set<EventData> below. Uniqueness
621 : // is determined solely by the event number associated with each event.
622 : //
623 : struct EventDataCompare
624 : {
625 422 : bool operator()(const EventData & lhs, const EventData & rhs) const
626 : {
627 422 : return (lhs.first.mEventNumber < rhs.first.mEventNumber);
628 : }
629 : };
630 :
631 : /*
632 : * These functions provide a way to index into the cached state with different sub-sets of a path, returning
633 : * appropriate slices of the data as requested.
634 : *
635 : * In all variants, the respective slice is returned if a valid path is provided. 'err' is updated to reflect
636 : * the status of the operation.
637 : *
638 : * Notable status values:
639 : * - If a cluster instance corresponding to endpointId and clusterId doesn't exist in the cache,
640 : * CHIP_ERROR_KEY_NOT_FOUND shall be returned.
641 : *
642 : */
643 : const EndpointState * GetEndpointState(EndpointId endpointId, CHIP_ERROR & err) const;
644 : const ClusterState * GetClusterState(EndpointId endpointId, ClusterId clusterId, CHIP_ERROR & err) const;
645 : const AttributeState * GetAttributeState(EndpointId endpointId, ClusterId clusterId, AttributeId attributeId,
646 : CHIP_ERROR & err) const;
647 :
648 : const EventData * GetEventData(EventNumber number, CHIP_ERROR & err) const;
649 :
650 : /*
651 : * Updates the state of an attribute in the cache given a reader. If the reader is null, the state is updated
652 : * with the provided status.
653 : */
654 : CHIP_ERROR UpdateCache(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData, const StatusIB & aStatus);
655 :
656 : /*
657 : * If apData is not null, updates the cached event set with the specified event header + payload.
658 : * If apData is null and apStatus is not null, the StatusIB is stored in the event status cache.
659 : *
660 : * Storage of either of these do not affect pre-existing data for the other events in the cache.
661 : *
662 : */
663 : CHIP_ERROR UpdateEventCache(const EventHeader & aEventHeader, TLV::TLVReader * apData, const StatusIB * apStatus);
664 :
665 : //
666 : // ReadClient::Callback
667 : //
668 : void OnReportBegin() override;
669 : void OnReportEnd() override;
670 : void OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData, const StatusIB & aStatus) override;
671 0 : void OnError(CHIP_ERROR aError) override { return mCallback.OnError(aError); }
672 :
673 : void OnEventData(const EventHeader & aEventHeader, TLV::TLVReader * apData, const StatusIB * apStatus) override;
674 :
675 20 : void OnDone(ReadClient * apReadClient) override
676 : {
677 20 : mRequestPathSet.clear();
678 20 : return mCallback.OnDone(apReadClient);
679 : }
680 :
681 0 : void OnSubscriptionEstablished(SubscriptionId aSubscriptionId) override
682 : {
683 0 : mCallback.OnSubscriptionEstablished(aSubscriptionId);
684 0 : }
685 :
686 0 : CHIP_ERROR OnResubscriptionNeeded(ReadClient * apReadClient, CHIP_ERROR aTerminationCause) override
687 : {
688 0 : return mCallback.OnResubscriptionNeeded(apReadClient, aTerminationCause);
689 : }
690 :
691 0 : void OnDeallocatePaths(chip::app::ReadPrepareParams && aReadPrepareParams) override
692 : {
693 0 : mCallback.OnDeallocatePaths(std::move(aReadPrepareParams));
694 0 : }
695 :
696 : virtual CHIP_ERROR OnUpdateDataVersionFilterList(DataVersionFilterIBs::Builder & aDataVersionFilterIBsBuilder,
697 : const Span<AttributePathParams> & aAttributePaths,
698 : bool & aEncodedDataVersionList) override;
699 :
700 0 : void OnUnsolicitedMessageFromPublisher(ReadClient * apReadClient) override
701 : {
702 0 : return mCallback.OnUnsolicitedMessageFromPublisher(apReadClient);
703 : }
704 :
705 0 : void OnCASESessionEstablished(const SessionHandle & aSession, ReadPrepareParams & aSubscriptionParams) override
706 : {
707 0 : return mCallback.OnCASESessionEstablished(aSession, aSubscriptionParams);
708 : }
709 :
710 : // Commit the pending cluster data version, if there is one.
711 : void CommitPendingDataVersion();
712 :
713 : // Get our list of data version filters, sorted from larges to smallest by the total size of the TLV
714 : // payload for the filter's cluster. Applying filters in this order should maximize space savings
715 : // on the wire if not all filters can be applied.
716 : void GetSortedFilters(std::vector<std::pair<DataVersionFilter, size_t>> & aVector) const;
717 :
718 : CHIP_ERROR GetElementTLVSize(TLV::TLVReader * apData, uint32_t & aSize);
719 :
720 : Callback & mCallback;
721 : NodeState mCache;
722 : std::set<ConcreteAttributePath> mChangedAttributeSet;
723 : std::set<AttributePathParams, Comparator> mRequestPathSet; // wildcard attribute request path only
724 : std::vector<EndpointId> mAddedEndpoints;
725 :
726 : std::set<EventData, EventDataCompare> mEventDataCache;
727 : Optional<EventNumber> mHighestReceivedEventNumber;
728 : std::map<ConcreteEventPath, StatusIB> mEventStatusCache;
729 : BufferedReadCallback mBufferedReader;
730 : ConcreteClusterPath mLastReportDataPath = ConcreteClusterPath(kInvalidEndpointId, kInvalidClusterId);
731 : const bool mCacheData = CanEnableDataCaching;
732 : };
733 :
734 : using ClusterStateCache = ClusterStateCacheT<true>;
735 : using ClusterStateCacheNoData = ClusterStateCacheT<false>;
736 :
737 : }; // namespace app
738 : }; // namespace chip
739 : #endif // CHIP_CONFIG_ENABLE_READ_CLIENT
|