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