| /* |
| * |
| * Copyright (c) 2016-2018 Nest Labs, Inc. |
| * Copyright (c) 2019 Google, LLC. |
| * 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 notification engine for Weave |
| * Data Management (WDM) profile. |
| * |
| */ |
| |
| #ifndef _WEAVE_DATA_MANAGEMENT_NOTIFICATION_ENGINE_CURRENT_H |
| #define _WEAVE_DATA_MANAGEMENT_NOTIFICATION_ENGINE_CURRENT_H |
| |
| #include <Weave/Profiles/data-management/Current/WdmManagedNamespace.h> |
| #include <Weave/Core/WeaveCore.h> |
| #include <Weave/Profiles/data-management/SubscriptionHandler.h> |
| #include <Weave/Profiles/data-management/TraitData.h> |
| #include <Weave/Profiles/data-management/TraitCatalog.h> |
| |
| namespace nl { |
| namespace Weave { |
| namespace Profiles { |
| namespace WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Current) { |
| |
| /** |
| * @class IDataElementAccessControlDelegate |
| * |
| * @brief Interface that is to be implemented by a processor of data |
| * elements in a NotifyRequest |
| */ |
| class IDataElementAccessControlDelegate |
| { |
| public: |
| virtual WEAVE_ERROR DataElementAccessCheck(const TraitPath & aTraitPath, const TraitCatalogBase<TraitDataSink> & aCatalog) = 0; |
| }; |
| |
| /* |
| * @class NotificationEngine |
| * |
| * @brief The notification engine is responsible for generating notifies to subscriber. It is able to find the intersection between |
| * the path interest set of each subscriber with what has changed in the publisher data store and generate tailored notifies |
| * for each subscriber. |
| * |
| * To achieve this, the engine tracks data-changes (i.e data dirtiness) at a couple of different levels: |
| * |
| * - Per subscriber, per trait instance dirtiness: Every subscriber tracks trait-changes at a per-instance granularity. |
| * Anytime a data source makes known that a property handle within has changed, the NE will iterate over every subscriber |
| * that has subscribed to that trait instance and mark the fact that that instance is now dirty. |
| * |
| * - Granular per trait instance, per property handle dirtiness: If selected through compile-time options by the user, the |
| * engine will mark dirtiness down to the property handle. This allows it to generate compact notifies that convey as |
| * succinctly as possible the data that has changed. This will be described in more detail in the solvers section. |
| * |
| * At its core, it iterates over every subscription, then every dirty instance within that subscription and tries to gather |
| * and pack as much relevant data as possible into a notify message before sending that to the subscriber. It continues to |
| * do so until it has no more work to do. This could be due to a couple of reasons: |
| * |
| * - Notifies are in flight to the subscriber(s) |
| * - We have exceeded the maximum number of notifies that can be flight across all subscribers. |
| * - We have no more space in the packet to stuff in more data. |
| * - We have no more dirty data to process for a particular set of subscriptions. |
| * |
| * Once it surmises there is no more work to be done, it returns. If all work for a subscription has been completed, it will |
| * invoke a method in the SubscriptionHandler to finish processing that subscription (which might involve sending out |
| * subscription responses). |
| * |
| * During subscription establishment, the NE works slightly differently than at other times - it will retrieve *all* the |
| * data for a particular trait. There-after, it will only retrieve new, changed data. |
| * |
| * Some notable features: |
| * |
| * - Subscription fairness: The engine round-robins over all subscriptions and will always resume its work loop at the last |
| * subscription it was trying to process to ensure all subscriptions are handled with equal priority. |
| * |
| * - Trait instance fairness: Within a subscription, the engine also rounds robins over all trait instances and will resume |
| * its work loop at the last trait instance that was being processed *for that subscription*. This ensures trait instances |
| * that have a high rate of change don't starve out others. |
| * |
| * - Inter-trait chunking across multiple notifies: The engine supports splitting trait data over multiple notifies. It will |
| * however only do this split at the trait instance granularity. It cannot chunk up data within a trait. |
| * |
| * - Graceful degradation due to resource shortages: If it runs out space in the dirty stores, the engine will degrade |
| * gracefully by generating sub-optimal notify messages that have more data in them while still being protocol correct. |
| * |
| */ |
| class NotificationEngine |
| { |
| public: |
| /** |
| * Initializes the engine. Should only be called once. |
| * |
| * @retval #WEAVE_NO_ERROR On success. |
| * @retval other Was unable to retrieve data and write it into the writer. |
| */ |
| WEAVE_ERROR Init(void); |
| |
| /** |
| * Main work-horse function that executes the run-loop. |
| */ |
| void Run(void); |
| |
| /** |
| * Main work-horse function that executes the run-loop asynchronously on the Weave thread |
| */ |
| void ScheduleRun(void); |
| |
| /** |
| * Marks a handle associated with a data source as being dirty. |
| * |
| * @retval #WEAVE_NO_ERROR On success. |
| * @retval other Was unable to retrieve data and write it into the writer. |
| */ |
| WEAVE_ERROR SetDirty(TraitDataSource * aDataSource, PropertyPathHandle aPropertyHandle); |
| |
| WEAVE_ERROR DeleteKey(TraitDataSource * aDataSource, PropertyPathHandle aPropertyHandle); |
| |
| #if WDM_ENABLE_SUBSCRIPTIONLESS_NOTIFICATION |
| WEAVE_ERROR SendSubscriptionlessNotification(Binding * const apBinding, TraitPath *aPathList, uint16_t aPathListSize); |
| #endif // WDM_ENABLE_SUBSCRIPTIONLESS_NOTIFICATION |
| |
| enum NotifyRequestBuilderState |
| { |
| kNotifyRequestBuilder_Idle = 0, ///< The request has not been opened or has been closed and finalized |
| kNotifyRequestBuilder_Ready, ///< The request has been initialized and is ready for any optional toplevel elements |
| kNotifyRequestBuilder_BuildDataList, ///< The request is building the DataList portion of the structure |
| kNotifyRequestBuilder_BuildEventList ///< The request is building the EventList portion of the structure |
| }; |
| |
| /** |
| * @class NotifyRequestBuilder |
| * |
| * @brief This provides a helper class to compose notifies and abstract away the construction and structure of the message from |
| * its consumers. This is a more compact version of a similar class provided in MessageDef.cpp that aims to be sensitive |
| * to the flash and ram needs of the device. |
| */ |
| class NotifyRequestBuilder |
| { |
| public: |
| /** |
| * Initializes the builder. Should only be called once. |
| * |
| * @retval #WEAVE_NO_ERROR On success. |
| * @retval other Was unable to initialize the builder. |
| */ |
| WEAVE_ERROR Init(PacketBuffer * aBuf, TLV::TLVWriter * aWriter, SubscriptionHandler * aSubHandler, |
| uint32_t aMaxPayloadSize); |
| |
| /** |
| * Start the construction of the notify. |
| * |
| * @retval #WEAVE_NO_ERROR On success. |
| * @retval #WEAVE_ERROR_INCORRECT_STATE If the request is not at the toplevel of the buffer. |
| * @retval other Unable to construct the end of the notify. |
| */ |
| |
| WEAVE_ERROR StartNotifyRequest(); |
| |
| /** |
| * End the construction of the notify. |
| * |
| * @retval #WEAVE_NO_ERROR On success. |
| * @retval #WEAVE_ERROR_INCORRECT_STATE If the request is not at Notify container. |
| * @retval other Unable to construct the end of the notify. |
| */ |
| WEAVE_ERROR EndNotifyRequest(); |
| |
| /** |
| * Starts the construction of the data list array. |
| * |
| * @retval #WEAVE_NO_ERROR On success. |
| * @retval #WEAVE_ERROR_INCORRECT_STATE If the request is not at the Notify container. |
| * @retval other Unable to construct the beginning of the data list. |
| */ |
| WEAVE_ERROR StartDataList(void); |
| |
| /** |
| * End the construction of the data list array. |
| * |
| * @retval #WEAVE_NO_ERROR On success. |
| * @retval #WEAVE_ERROR_INCORRECT_STATE If the request is not at the DataList container. |
| * @retval other Unable to construct the end of the data list. |
| */ |
| WEAVE_ERROR EndDataList(); |
| |
| /** |
| * Starts the construction of the event list. |
| * |
| * @retval #WEAVE_NO_ERROR On success. |
| * @retval #WEAVE_ERROR_INCORRECT_STATE If the request is not at the Notify container. |
| * @retval other Unable to construct the beginning of the data list. |
| */ |
| WEAVE_ERROR StartEventList(); |
| |
| /** |
| * End the construction of the event list. |
| * |
| * @retval #WEAVE_NO_ERROR On success. |
| * @retval #WEAVE_ERROR_INCORRECT_STATE If the request is not at the EventList container. |
| * @retval other Unable to construct the end of the data list. |
| */ |
| WEAVE_ERROR EndEventList(); |
| |
| /** |
| * Given a trait path, write out the data element associated with that path. The caller can also optionally pass in a handle |
| * set allows for leveraging the merge operation with a narrower set of immediate child nodes of the parent property path |
| * handle. |
| * |
| * @retval #WEAVE_NO_ERROR On success. |
| * @retval other Unable to retrieve and write the data element. |
| */ |
| WEAVE_ERROR WriteDataElement(TraitDataHandle aTraitDataHandle, PropertyPathHandle aPropertyPathHandle, |
| SchemaVersion aSchemaVersion, PropertyPathHandle * aMergeDataHandleSet, |
| uint32_t aNumMergeDataHandles, PropertyPathHandle * aDeleteHandleSet, |
| uint32_t aNumDeleteHandles); |
| |
| /** |
| * Checkpoint the request state into a TLVWriter |
| * |
| * @param[out] aPoint A writer to checkpoint the state of the TLV writer into. |
| * |
| * @retval #WEAVE_NO_ERROR On success. |
| */ |
| WEAVE_ERROR Checkpoint(TLV::TLVWriter & aPoint); |
| |
| /** |
| * Rollback the request state into the checkpointed TLVWriter |
| * |
| * @param[in] aPoint A writer to that captured the state at some point in the past |
| * |
| * @retval #WEAVE_NO_ERROR On success. |
| */ |
| WEAVE_ERROR Rollback(TLV::TLVWriter & aPoint); |
| |
| TLV::TLVWriter * GetWriter(void) { return mWriter; } |
| |
| /** |
| * The main state transition function. The function takes the desired state (i.e., the phase of the notify request builder |
| * that we would like to reach), and transitions the request into that state. If the desired state is the same as the |
| * current state, the function does nothing. Otherwise, an PacketBuffer is allocated (if needed); the function first |
| * transitions the request into the toplevel notify request (either opening the notify request TLV structure, or closing the |
| * current TLV data container as needed), and then transitions the Notify request either by opening the appropriate TLV data |
| * container or by closing the overarching Notify request. |
| * |
| * @param aDesiredState The desired state the request should transition into |
| * |
| * @retval #WEAVE_NO_ERROR On success. |
| * @retval #WEAVE_ERROR_NO_MEMORY Could not transition into the state because of insufficient memory. |
| * @retval #WEAVE_ERROR_INCORRECT_STATE Corruption of the internal state machine. |
| * @retval other When the state machine could not record the state in its buffer, likely indicates a design flaw rather than |
| * a runtime issue. |
| */ |
| WEAVE_ERROR MoveToState(NotifyRequestBuilderState aDesiredState); |
| |
| private: |
| TLV::TLVWriter * mWriter; |
| NotifyRequestBuilderState mState; |
| PacketBuffer * mBuf; |
| SubscriptionHandler * mSub; |
| uint32_t mMaxPayloadSize; |
| }; |
| |
| /* |
| * GRAPH SOLVERS |
| * |
| * To generate notifies, NE institutes a notion of a solver to help crunch the tree-based representation of the schema along |
| * with the dirty handle information. This allows for consolidation of the logic to processing the schema for notify generation |
| * to a localized body of functions. |
| * |
| * To accommodate the varying capabilities of Weave-enabled devices, a number of different solvers have been provided that range |
| * in their capabilities and their constraints. |
| * |
| * These solvers can be chosen at compile time to reduce flash usage. |
| * |
| * ---- To facilitate this, all the public methods in the solvers have to have the same signature! If you decide to add a new |
| * solver, please ensure this criteria is met! --- |
| * |
| */ |
| |
| /* |
| * @class BasicGraphSolver |
| * |
| * @brief This is a coarse, basic solver that will retrieve the entire contents of a trait instance from root. The solver |
| * trades of computational complexity and reduced storage requirements with inefficiency in the data transmitted over |
| * the wire. This is rarely useful for most applications given the sheer in-efficiency of data transmitted over the |
| * wire, especially for traits with lots of key/value pairs. It is however useful for bring-up or for debugging issues |
| * with the other solvers. |
| * |
| * Constraints: It only supports subscriptions to root and nothing deeper. |
| */ |
| class BasicGraphSolver |
| { |
| public: |
| static bool IsPropertyPathSupported(PropertyPathHandle aHandle); |
| WEAVE_ERROR RetrieveTraitInstanceData(NotifyRequestBuilder * aBuilder, TraitDataHandle aTraitDataHandle, |
| SchemaVersion aSchemaVersion, bool aRetrieveAll); |
| static WEAVE_ERROR SetDirty(TraitDataHandle aTraitDataHandle, PropertyPathHandle aPropertyHandle); |
| WEAVE_ERROR ClearDirty(void); |
| }; |
| |
| /* |
| * @class IntermediateGraphSolver |
| * |
| * @brief This solver is able to generate compact notifies that try to only contain the modified bits of data. This leverages a |
| * finitely sized, global dirty store that houses granular dirty information per property handle per trait |
| * instance. When a notify is to be generated, the solver attempts to find the LCA (lowest-common-ancestor) of all the |
| * dirty nodes in the tree and generates a data-element against that path. In addition, it exploits the merge semantics |
| * of WDM to only include child trees of that LCA that contain dirty elements. This is pretty efficient given the |
| * reasonably flat, shallow structure of our IDLs. |
| * |
| * If it is unable to store anymore dirty items in the granular store, it will degrade to marking the entire trait |
| * instance as dirty. In addition, if it runs out of space in the merge handle set, it will degrade to including all |
| * child trees of the LCA'ed node. |
| * |
| */ |
| class IntermediateGraphSolver |
| { |
| public: |
| static bool IsPropertyPathSupported(PropertyPathHandle aHandle); |
| WEAVE_ERROR RetrieveTraitInstanceData(NotifyRequestBuilder * aBuilder, TraitDataHandle aTraitDataHandle, |
| SchemaVersion aSchemaVersion, bool aRetrieveAll); |
| WEAVE_ERROR SetDirty(TraitDataHandle aTraitDataHandle, PropertyPathHandle aPropertyHandle); |
| |
| #if TDM_ENABLE_PUBLISHER_DICTIONARY_SUPPORT |
| WEAVE_ERROR DeleteKey(TraitDataHandle aTraitDataHandle, PropertyPathHandle aPropertyHandle); |
| #endif |
| |
| WEAVE_ERROR ClearDirty(void); |
| |
| struct Store |
| { |
| public: |
| Store(); |
| bool AddItem(TraitPath aItem); |
| void RemoveItem(TraitDataHandle aDataHandle); |
| void RemoveItemAt(uint32_t aIndex); |
| bool IsPresent(TraitPath aItem); |
| bool IsFull() { return mNumItems >= WDM_PUBLISHER_MAX_ITEMS_IN_TRAIT_DIRTY_STORE; } |
| uint32_t GetNumItems() { return mNumItems; } |
| uint32_t GetStoreSize() { return WDM_PUBLISHER_MAX_ITEMS_IN_TRAIT_DIRTY_STORE; } |
| void Clear(); |
| |
| TraitPath mStore[WDM_PUBLISHER_MAX_ITEMS_IN_TRAIT_DIRTY_STORE]; |
| bool mValidFlags[WDM_PUBLISHER_MAX_ITEMS_IN_TRAIT_DIRTY_STORE]; |
| uint32_t mNumItems; |
| }; |
| |
| private: |
| static void ClearTraitInstanceDirty(void * aDataSource, TraitDataHandle aDataHandle, void * aContext); |
| PropertyPathHandle GetNextCandidateHandle(uint32_t & aChangeStoreCursor, TraitDataHandle aTargetDataHandle, |
| bool & aCandidateHandleIsDelete); |
| |
| Store mDirtyStore; |
| |
| #if TDM_ENABLE_PUBLISHER_DICTIONARY_SUPPORT |
| Store mDeleteStore; |
| #endif |
| }; |
| |
| private: |
| friend class SubscriptionHandler; |
| friend class UpdateClient; |
| friend class TestTdm; |
| friend class TestWdm; |
| |
| /** |
| * Should be invoked when the device receives a NotifyConfirm, or when the Notify request times out. |
| * This allows the engine to do some clean-up. |
| * |
| * @retval #WEAVE_NO_ERROR On success. |
| * @retval other Was unable to retrieve data and write it into the writer. |
| */ |
| void OnNotifyConfirm(SubscriptionHandler * aSubHandler, bool aNotifyDelivered); |
| |
| WEAVE_ERROR BuildSingleNotifyRequestDataList(SubscriptionHandler * aSubHandler, NotifyRequestBuilder & aNotifyRequest, |
| bool & isSubscriptionClean, bool & aNeWriteInProgress); |
| WEAVE_ERROR BuildSingleNotifyRequestEventList(SubscriptionHandler * aSubHandler, NotifyRequestBuilder & aNotifyRequest, |
| bool & isSubscriptionClean, bool & aNeWriteInProgress); |
| WEAVE_ERROR BuildSingleNotifyRequest(SubscriptionHandler * aSubHandler, bool & aSubscriptionHandled, |
| bool & isSubscriptionClean); |
| |
| WEAVE_ERROR RetrieveTraitInstanceData(SubscriptionHandler * aSubHandler, SubscriptionHandler::TraitInstanceInfo * aTraitInfo, |
| NotifyRequestBuilder * aBuilder, bool * aPacketFull); |
| WEAVE_ERROR SendNotify(PacketBuffer * aBuf, SubscriptionHandler * aSubHandler); |
| |
| WEAVE_ERROR SendNotifyRequest(); |
| |
| static void Run(System::Layer * aSystemLayer, void * aAppState, System::Error); |
| |
| #if WDM_ENABLE_SUBSCRIPTIONLESS_NOTIFICATION |
| WEAVE_ERROR BuildSubscriptionlessNotification(PacketBuffer *msgBuf, uint32_t maxPayloadSize, TraitPath *aPathList, |
| uint16_t aPathListSize); |
| #endif // WDM_ENABLE_SUBSCRIPTIONLESS_NOTIFICATION |
| uint32_t mCurSubscriptionHandlerIdx; |
| uint32_t mCurTraitInstanceIdx; |
| uint32_t mNumNotifiesInFlight; |
| nl::Weave::TLV::TLVType mOuterContainerType; |
| WEAVE_CONFIG_WDM_PUBLISHER_GRAPH_SOLVER mGraphSolver; |
| }; |
| |
| }; // namespace WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Current) |
| }; // namespace Profiles |
| }; // namespace Weave |
| }; // namespace nl |
| |
| #endif // _WEAVE_DATA_MANAGEMENT_NOTIFICATION_ENGINE_CURRENT_H |