Matter SDK Coverage Report
Current view: top level - app/cluster-building-blocks - QuieterReporting.h (source / functions) Coverage Total Hit
Test: SHA:4d2388ac7eed75b2fe5e05e20de377999c632502 Lines: 100.0 % 31 31
Test Date: 2025-07-27 07:17:09 Functions: 100.0 % 16 16

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2024 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 <functional>
      22              : #include <stdbool.h>
      23              : #include <type_traits>
      24              : 
      25              : #include <app/data-model/Nullable.h>
      26              : #include <lib/support/BitFlags.h>
      27              : #include <system/SystemClock.h>
      28              : 
      29              : namespace chip {
      30              : namespace app {
      31              : 
      32              : enum class QuieterReportingPolicyEnum
      33              : {
      34              :     kMarkDirtyOnChangeToFromZero = (1u << 0),
      35              :     kMarkDirtyOnDecrement        = (1u << 1),
      36              :     kMarkDirtyOnIncrement        = (1u << 2),
      37              : };
      38              : 
      39              : enum class AttributeDirtyState
      40              : {
      41              :     kNoReportNeeded = 0,
      42              :     kMustReport     = 1,
      43              : };
      44              : 
      45              : using QuieterReportingPolicyFlags = BitFlags<QuieterReportingPolicyEnum>;
      46              : 
      47              : namespace detail {
      48              : 
      49              : using Timestamp = System::Clock::Milliseconds64;
      50              : template <typename T>
      51              : using Nullable = DataModel::Nullable<T>;
      52              : 
      53              : /**
      54              :  * This class helps track reporting state of an attribute to properly keep track of whether
      55              :  * it needs to be marked as dirty or not for purposes of reporting using
      56              :  * "7.7.9 Quieter Reporting Quality" (Q quality)
      57              :  *
      58              :  * The class can be configured via `policy()` to have some/all of the common reasons
      59              :  * for reporting (e.g. increment only, decrement only, change to/from zero).
      60              :  *
      61              :  * Changes of null to non-null or non-null to null are always considered dirty.
      62              :  *
      63              :  * It is possible to force mark the attribute as dirty (see `ForceDirty()`) such as
      64              :  * for conditions like "When there is any increase or decrease in the estimated time
      65              :  * remaining that was due to progressing insight of the server's control logic".
      66              :  *
      67              :  * Class maintains a `current value` and a timestamped `dirty` state. The `SetValue()`
      68              :  * method must be used to update the `current value` and will return AttributeDirtyState::kMustReport
      69              :  * if the attribute should be marked dirty/
      70              :  *
      71              :  * - `SetValue()` has internal rules for null/non-null changes and policy-based rules
      72              :  * - `SetValue()` with a `SufficientChangePredicate` uses the internal rules in addition to
      73              :  *    the predicate to determine dirty state
      74              :  *
      75              :  * See [QuieterReportingPolicyEnum] for policy flags on when a value is considered dirty
      76              :  * beyond non/non-null changes.
      77              :  *
      78              :  * Common quieter reporting usecases that can be supported by this class are:
      79              :  * - If attribute has changed due to a change in the X or Y attributes
      80              :  *   - Use SufficientChangePredicate version
      81              :  * - When it changes from 0 to any other value and vice versa
      82              :  *   - Use `kMarkDirtyOnChangeToFromZero` internal policy.
      83              :  * - When it changes from null to any other value and vice versa
      84              :  *   - Built-in rule.
      85              :  * - When it increases
      86              :  *   - Use `kMarkDirtyOnIncrement` internal policy.
      87              :  * - When it decreases
      88              :  *   - Use `kMarkDirtyOnDecrement` internal policy.
      89              :  * - When there is any increase or decrease in the estimated time remaining that was
      90              :  *   due to progressing insight of the server's control logic
      91              :  *   - Use SufficientChangePredicate version with an always-true predicate.
      92              :  * - When it changes at a rate significantly different from one unit per second.
      93              :  *   - Use SufficientChangePredicate version.
      94              :  * Example usage in-situ:
      95              :  *
      96              :  * Class has:
      97              :  *     QuieterReportingAttribute<uint8_t> mAttrib;
      98              :  *
      99              :  * Code at time of setting new value has:
     100              :  *
     101              :  *     uint8_t newValue = driver.GetNewValue();
     102              :  *     auto now = SystemClock().GetMonotonicTimestamp();
     103              :  *     if (mAttrib.SetValue(newValue, now) == AttributeDirtyState::kMustReport)
     104              :  *     {
     105              :  *         MatterReportingAttributeChangeCallback(path_for_attribute);
     106              :  *     }
     107              :  *
     108              :  * @tparam T - the type of underlying numerical value that will be held by the class.
     109              :  */
     110              : template <typename T, std::enable_if_t<std::is_arithmetic<T>::value, bool> = true>
     111              : class QuieterReportingAttribute
     112              : {
     113              : public:
     114          131 :     explicit QuieterReportingAttribute(const Nullable<T> & initialValue) : mValue(initialValue), mLastDirtyValue(initialValue) {}
     115              :     // constructor that works with arrays of QuieterReportingAttribute
     116              :     explicit QuieterReportingAttribute() : mValue(DataModel::NullNullable), mLastDirtyValue(DataModel::NullNullable) {}
     117              : 
     118              :     struct SufficientChangePredicateCandidate
     119              :     {
     120              :         // Timestamp of last time attribute was marked dirty.
     121              :         Timestamp lastDirtyTimestamp;
     122              :         // New (`now`) timestamp passed in `SetValue()`.
     123              :         Timestamp nowTimestamp;
     124              :         // Value last marked as dirty.
     125              :         const Nullable<T> & lastDirtyValue;
     126              :         // New value passed in `SetValue()`, to compare against lastDirtyValue for sufficient change if needed.
     127              :         const Nullable<T> & newValue;
     128              :     };
     129              : 
     130              :     using SufficientChangePredicate = std::function<bool(const SufficientChangePredicateCandidate &)>;
     131              : 
     132              :     /**
     133              :      * @brief Factory to generate a functor for "attribute was last reported" at least `minimumDurationMillis` ago.
     134              :      *
     135              :      * @param minimumDurationMillis - number of millis needed since last marked as dirty before we mark dirty again.
     136              :      * @return a functor usable for the `changedPredicate` arg of `SetValue()`
     137              :      */
     138              :     static SufficientChangePredicate
     139           37 :     GetPredicateForSufficientTimeSinceLastDirty(System::Clock::Milliseconds64 minimumDurationMillis)
     140              :     {
     141           12 :         return [minimumDurationMillis](const SufficientChangePredicateCandidate & candidate) -> bool {
     142           24 :             return (candidate.lastDirtyValue != candidate.newValue) &&
     143           24 :                 ((candidate.nowTimestamp - candidate.lastDirtyTimestamp) >= minimumDurationMillis);
     144           37 :         };
     145              :     }
     146              : 
     147           60 :     Nullable<T> value() const { return mValue; }
     148           81 :     QuieterReportingPolicyFlags & policy() { return mPolicyFlags; }
     149              :     const QuieterReportingPolicyFlags & policy() const { return mPolicyFlags; }
     150              : 
     151              :     /**
     152              :      * Set the updated value of the attribute, computing whether it needs to be reported according to `changedPredicate` and
     153              :      * policies.
     154              :      *
     155              :      * - Any change of nullability between `newValue` and the old value will be considered dirty.
     156              :      * - The policies from `QuieterReportingPolicyEnum` and set via `SetPolicy()` are self-explanatory by name.
     157              :      * - The changedPredicate will be called with last dirty <timestamp, value> and new <timestamp value> and may override
     158              :      *   the dirty state altogether when it returns true. Use sparingly and default to a functor returning false.
     159              :      *   The changedPredicate is only called on change.
     160              :      *
     161              :      * Internal recording will be done about last dirty value and last dirty timestamp based on the policies having applied.
     162              :      *
     163              :      * @param newValue - new value to set for the attribute
     164              :      * @param now - system monotonic timestamp at the time of the call
     165              :      * @param changedPredicate - functor to possibly override dirty state
     166              :      * @return AttributeDirtyState::kMustReport if attribute must be marked dirty right away, or
     167              :      * AttributeDirtyState::kNoReportNeeded otherwise.
     168              :      */
     169          195 :     AttributeDirtyState SetValue(const DataModel::Nullable<T> & newValue, Timestamp now, SufficientChangePredicate changedPredicate)
     170              :     {
     171          195 :         bool isChangeOfNull         = newValue.IsNull() != mValue.IsNull();
     172          195 :         bool areBothValuesNonNull   = !newValue.IsNull() && !mValue.IsNull();
     173          195 :         bool areBothValuesDifferent = areBothValuesNonNull && (newValue.Value() != mValue.Value());
     174              : 
     175          195 :         bool changeToFromZero = areBothValuesNonNull && areBothValuesDifferent && (newValue.Value() == 0 || mValue.Value() == 0);
     176          195 :         bool isIncrement      = areBothValuesNonNull && (newValue.Value() > mValue.Value());
     177          195 :         bool isDecrement      = areBothValuesNonNull && (newValue.Value() < mValue.Value());
     178              : 
     179          195 :         bool isNewlyDirty = isChangeOfNull;
     180          195 :         isNewlyDirty =
     181          195 :             isNewlyDirty || (mPolicyFlags.Has(QuieterReportingPolicyEnum::kMarkDirtyOnChangeToFromZero) && changeToFromZero);
     182          195 :         isNewlyDirty = isNewlyDirty || (mPolicyFlags.Has(QuieterReportingPolicyEnum::kMarkDirtyOnDecrement) && isDecrement);
     183          195 :         isNewlyDirty = isNewlyDirty || (mPolicyFlags.Has(QuieterReportingPolicyEnum::kMarkDirtyOnIncrement) && isIncrement);
     184              : 
     185              :         // Only execute predicate on value change from last marked dirty.
     186          195 :         if (newValue != mLastDirtyValue)
     187              :         {
     188          167 :             SufficientChangePredicateCandidate candidate{
     189              :                 mLastDirtyTimestampMillis, // lastDirtyTimestamp
     190              :                 now,                       // nowTimestamp
     191          167 :                 mLastDirtyValue,           // lastDirtyValue
     192              :                 newValue                   // newValue
     193              :             };
     194          167 :             isNewlyDirty = isNewlyDirty || changedPredicate(candidate);
     195              :         }
     196              : 
     197          195 :         mValue = newValue;
     198              : 
     199          195 :         if (isNewlyDirty)
     200              :         {
     201          152 :             mLastDirtyValue           = newValue;
     202          152 :             mLastDirtyTimestampMillis = now;
     203              :         }
     204              : 
     205          195 :         return isNewlyDirty ? AttributeDirtyState::kMustReport : AttributeDirtyState::kNoReportNeeded;
     206              :     }
     207              : 
     208              :     /**
     209              :      * Same as the other `SetValue`, but assumes a changedPredicate that never overrides to dirty.
     210              :      *
     211              :      * This is the easy/common case.
     212              :      *
     213              :      * @param newValue - new value to set for the attribute
     214              :      * @param now - system monotonic timestamp at the time of the call
     215              :      * @return AttributeDirtyState::kMustReport if attribute must be marked dirty right away, or
     216              :      * AttributeDirtyState::kNoReportNeeded otherwise.
     217              :      */
     218           23 :     AttributeDirtyState SetValue(const DataModel::Nullable<T> & newValue, Timestamp now)
     219              :     {
     220           31 :         return SetValue(newValue, now, [](const SufficientChangePredicateCandidate &) -> bool { return false; });
     221              :     }
     222              : 
     223              : protected:
     224              :     // Current value of the attribute.
     225              :     DataModel::Nullable<T> mValue;
     226              :     // Last value that was marked as dirty (to use in comparisons for change, e.g. by SufficientChangePredicate).
     227              :     DataModel::Nullable<T> mLastDirtyValue;
     228              :     // Enabled internal change detection policies.
     229              :     QuieterReportingPolicyFlags mPolicyFlags{ 0 };
     230              :     // Timestamp associated with the last time the attribute was marked dirty (to use in comparisons for change).
     231              :     chip::System::Clock::Timestamp mLastDirtyTimestampMillis{};
     232              : };
     233              : 
     234              : } // namespace detail
     235              : 
     236              : using detail::QuieterReportingAttribute;
     237              : 
     238              : } // namespace app
     239              : } // namespace chip
        

Generated by: LCOV version 2.0-1