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