Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use ProviderChangeListener directly to report attrbiute change #36756

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

yufengwangca
Copy link
Contributor

Currently, MarkDirty in ProviderChangeListener does exactly what Temporary_ReportAttributeChanged does.

Instead of calling Temporary_ReportAttributeChanged or interacting with Provider, consumers would now use:

Consumers call Provider::GetAttributeChangeReporter() to obtain a ProviderChangeListener reference. For testing, they can mock just the ProviderChangeListener.

This approach simplifies unit testing and mocking. By focusing on the ProviderChangeListener, which has a single method, we avoid the complexity of mocking the entire DataModel::Provider interface with its approximately 30 methods.

Unit Test:
A mock ProviderChangeListener can now be used for simplified testing.

class MockProviderChangeListener : public ProviderChangeListener
{
public:
    MOCK_METHOD(void, MarkDirty, (const AttributePathParams & path), (override));
};

Fix: #36661

Copy link

Review changes with  SemanticDiff

Copy link

github-actions bot commented Dec 6, 2024

PR #36756: Size comparison from 84fb78f to f65773e

Increases above 0.2%:

platform target config section 84fb78f f65773e0 change % change
linux thermostat-no-ble arm64 unknown 9536 9568 32 0.3
telink contact-sensor-app tlsr9528a_retention RAM 31440 31528 88 0.3
Full report (69 builds for bl602, bl702, bl702l, cc13x4_26x4, cc32xx, cyw30739, efr32, esp32, linux, nrfconnect, nxp, psoc6, qpg, stm32, telink, tizen)
platform target config section 84fb78f f65773e0 change % change
bl602 lighting-app bl602+mfd+littlefs+rpc FLASH 1353308 1353580 272 0.0
RAM 104112 104120 8 0.0
bl702 lighting-app bl702+eth FLASH 651816 651836 20 0.0
RAM 25353 25369 16 0.1
bl702+wifi FLASH 829144 829420 276 0.0
RAM 14093 14109 16 0.1
bl706+mfd+rpc+littlefs FLASH 1057594 1057870 276 0.0
RAM 23933 23949 16 0.1
bl702l lighting-app bl702l+mfd+littlefs FLASH 978968 978944 -24 -0.0
RAM 16596 16596 0 0.0
cc13x4_26x4 lighting-app LP_EM_CC1354P10_6 FLASH 839736 839840 104 0.0
RAM 123664 123672 8 0.0
lock-ftd LP_EM_CC1354P10_6 FLASH 825276 825372 96 0.0
RAM 125552 125560 8 0.0
pump-app LP_EM_CC1354P10_6 FLASH 772064 772160 96 0.0
RAM 114020 114028 8 0.0
pump-controller-app LP_EM_CC1354P10_6 FLASH 756260 756364 104 0.0
RAM 114228 114236 8 0.0
cc32xx air-purifier CC3235SF_LAUNCHXL FLASH 631026 631234 208 0.0
RAM 205824 205832 8 0.0
lock CC3235SF_LAUNCHXL FLASH 669622 669814 192 0.0
RAM 205968 205976 8 0.0
cyw30739 light CYW30739B2-P5-EVK-01 unknown 2040 2040 0 0.0
FLASH 681473 681577 104 0.0
RAM 78724 78732 8 0.0
CYW30739B2-P5-EVK-02 unknown 2040 2040 0 0.0
FLASH 701325 701429 104 0.0
RAM 81364 81372 8 0.0
CYW30739B2-P5-EVK-03 unknown 2040 2040 0 0.0
FLASH 701325 701429 104 0.0
RAM 81364 81372 8 0.0
CYW930739M2EVB-02 unknown 2040 2040 0 0.0
FLASH 658261 658357 96 0.0
RAM 73792 73800 8 0.0
light-switch CYW30739B2-P5-EVK-01 unknown 2040 2040 0 0.0
FLASH 618033 618129 96 0.0
RAM 71708 71716 8 0.0
CYW30739B2-P5-EVK-02 unknown 2040 2040 0 0.0
FLASH 637661 637765 104 0.0
RAM 74252 74260 8 0.0
CYW30739B2-P5-EVK-03 unknown 2040 2040 0 0.0
FLASH 637661 637765 104 0.0
RAM 74252 74260 8 0.0
lock CYW30739B2-P5-EVK-01 unknown 2040 2040 0 0.0
FLASH 637433 637537 104 0.0
RAM 74724 74732 8 0.0
CYW30739B2-P5-EVK-02 unknown 2040 2040 0 0.0
FLASH 657149 657253 104 0.0
RAM 77268 77276 8 0.0
CYW30739B2-P5-EVK-03 unknown 2040 2040 0 0.0
FLASH 657149 657253 104 0.0
RAM 77268 77276 8 0.0
thermostat CYW30739B2-P5-EVK-01 unknown 2040 2040 0 0.0
FLASH 613901 613997 96 0.0
RAM 68812 68820 8 0.0
CYW30739B2-P5-EVK-02 unknown 2040 2040 0 0.0
FLASH 633753 633849 96 0.0
RAM 71444 71452 8 0.0
CYW30739B2-P5-EVK-03 unknown 2040 2040 0 0.0
FLASH 633753 633849 96 0.0
RAM 71444 71452 8 0.0
efr32 lock-app BRD4187C FLASH 932308 932396 88 0.0
RAM 160192 160200 8 0.0
BRD4338a FLASH 746112 746312 200 0.0
RAM 233320 233328 8 0.0
window-app BRD4187C FLASH 1024752 1024960 208 0.0
RAM 128296 128304 8 0.0
esp32 all-clusters-app c3devkit DRAM 95360 95368 8 0.0
FLASH 1543466 1543594 128 0.0
IRAM 82542 82542 0 0.0
m5stack DRAM 116312 116320 8 0.0
FLASH 1550118 1550258 140 0.0
IRAM 117039 117039 0 0.0
linux air-purifier-app debug unknown 4720 4720 0 0.0
FLASH 2715045 2715809 764 0.0
RAM 129800 129896 96 0.1
all-clusters-app debug unknown 5560 5560 0 0.0
FLASH 6007046 6007810 764 0.0
RAM 523544 523624 80 0.0
all-clusters-minimal-app debug unknown 5456 5456 0 0.0
FLASH 5344818 5345562 744 0.0
RAM 242600 242680 80 0.0
bridge-app debug unknown 5440 5440 0 0.0
FLASH 4684354 4685118 764 0.0
RAM 218416 218480 64 0.0
chip-tool debug unknown 5992 5992 0 0.0
FLASH 12847594 12848320 726 0.0
RAM 582474 582530 56 0.0
chip-tool-ipv6only arm64 unknown 21344 21376 32 0.1
FLASH 10982320 10983296 976 0.0
RAM 633384 633896 512 0.1
fabric-admin debug unknown 5816 5816 0 0.0
FLASH 11254395 11255121 726 0.0
RAM 582850 582914 64 0.0
fabric-bridge-app debug unknown 4696 4696 0 0.0
FLASH 4509930 4510674 744 0.0
RAM 205600 205664 64 0.0
fabric-sync debug unknown 4936 4936 0 0.0
FLASH 5607797 5608533 736 0.0
RAM 472584 472648 64 0.0
lighting-app debug+rpc+ui unknown 6104 6104 0 0.0
FLASH 5621057 5621793 736 0.0
RAM 228792 228856 64 0.0
lock-app debug unknown 5376 5376 0 0.0
FLASH 4733594 4734338 744 0.0
RAM 204776 204856 80 0.0
ota-provider-app debug unknown 4752 4752 0 0.0
FLASH 4359332 4360076 744 0.0
RAM 198448 198528 80 0.0
ota-requestor-app debug unknown 4688 4688 0 0.0
FLASH 4498356 4499100 744 0.0
RAM 203032 203112 80 0.0
shell debug unknown 4248 4248 0 0.0
FLASH 3030061 3030813 752 0.0
RAM 160424 160512 88 0.1
thermostat-no-ble arm64 unknown 9536 9568 32 0.3
FLASH 4103472 4104464 992 0.0
RAM 243040 243552 512 0.2
tv-app debug unknown 5704 5704 0 0.0
FLASH 5958757 5959493 736 0.0
RAM 596016 596064 48 0.0
tv-casting-app debug unknown 5288 5288 0 0.0
FLASH 11054349 11055165 816 0.0
RAM 692152 692248 96 0.0
nrfconnect all-clusters-app nrf52840dk_nrf52840 FLASH 917576 917680 104 0.0
RAM 143292 143380 88 0.1
nrf7002dk_nrf5340_cpuapp FLASH 890080 890196 116 0.0
RAM 141487 141575 88 0.1
all-clusters-minimal-app nrf52840dk_nrf52840 FLASH 851720 851820 100 0.0
RAM 142200 142288 88 0.1
nxp contact k32w0+release FLASH 585408 585504 96 0.0
RAM 71080 71088 8 0.0
mcxw71+release FLASH 600016 600112 96 0.0
RAM 63176 63184 8 0.0
light k32w0+release FLASH 612364 612460 96 0.0
RAM 70472 70480 8 0.0
k32w1+release FLASH 686552 686664 112 0.0
RAM 48808 48816 8 0.0
lock mcxw71+release FLASH 762896 762992 96 0.0
RAM 70844 70852 8 0.0
psoc6 all-clusters cy8ckit_062s2_43012 FLASH 1646348 1646548 200 0.0
RAM 212104 212112 8 0.0
all-clusters-minimal cy8ckit_062s2_43012 FLASH 1554092 1554292 200 0.0
RAM 208904 208912 8 0.0
light cy8ckit_062s2_43012 FLASH 1469404 1469612 208 0.0
RAM 200880 200888 8 0.0
lock cy8ckit_062s2_43012 FLASH 1467148 1467340 192 0.0
RAM 225240 225248 8 0.0
qpg lighting-app qpg6105+debug FLASH 663976 664080 104 0.0
RAM 105424 105432 8 0.0
lock-app qpg6105+debug FLASH 621772 621876 104 0.0
RAM 99868 99876 8 0.0
stm32 light STM32WB5MM-DK FLASH 484696 484800 104 0.0
RAM 144880 144888 8 0.0
telink bridge-app tlsr9258a FLASH 682804 682920 116 0.0
RAM 91208 91296 88 0.1
contact-sensor-app tlsr9528a_retention FLASH 623230 623340 110 0.0
RAM 31440 31528 88 0.3
light-app-ota-compress-lzma-shell-factory-data tl3218x FLASH 764728 764838 110 0.0
RAM 50220 50308 88 0.2
light-switch-app-ota-compress-lzma-shell-factory-data tlsr9528a FLASH 710656 710766 110 0.0
RAM 73504 73592 88 0.1
lighting-app-ota-factory-data tlsr9118bdk40d FLASH 627762 627872 110 0.0
RAM 142140 142228 88 0.1
lighting-app-ota-rpc-factory-data-4mb tlsr9518adk80d FLASH 813690 813800 110 0.0
RAM 99684 99772 88 0.1
tizen all-clusters-app arm unknown 4988 4992 4 0.1
FLASH 1732480 1733120 640 0.0
RAM 90744 90768 24 0.0
chip-tool-ubsan arm unknown 10804 10812 8 0.1
FLASH 17969454 17973030 3576 0.0
RAM 7840748 7842684 1936 0.0

@@ -80,6 +80,17 @@ class EnumeratorCommandFinder
}
};

class DefaultProviderChangeListener : public DataModel::ProviderChangeListener
Copy link
Contributor

@andy31415 andy31415 Dec 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets place this in its own header. I also believe that detail::app::detail::DefaultProviderChangeListener does not make it obvious on what it is. This is not any default but rather maybe EmberDirtyMarker or EmberProviderChangeListener or CodegenProviderChangeListener

Thinking of it maybe eventually as we stabilize the API we can figure out a better name than ProviderChangeListener as that name makes me think of "a listener that gets notified when a provider changes" which is not at all what it is. Do you have any suggestions?

Something like AttributeDirtyMarker or DataChangeNotifier or something like that. Obviously not for this PR, but this PR made it obvious to me that the naming is not great.

///
/// It is expected that implementations provide a valid reference, ensuring that
/// attribute change reporting is functional throughout the lifecycle of the Provider.
virtual ProviderChangeListener & GetAttributeChangeReporter() = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we hit naming again. A GetAttributeChangeReporter should return a class AttributeChangeReporter or more closely related name. Maybe we can figure out something better.

I would add to the comments that direct hookups to this should eventually be rare and to prefer to get passed in such change listeners in constructors for easier testing/mocking.

@@ -149,6 +157,12 @@ std::optional<ActionReturnStatus> TestImCustomDataModel::Invoke(const InvokeRequ
return std::make_optional<ActionReturnStatus>(CHIP_ERROR_NOT_IMPLEMENTED);
}

ProviderChangeListener & TestImCustomDataModel::GetAttributeChangeReporter()
{
static TestProviderChangeListener changeListener;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why static? Since this is a test, could this thing be a property of the object itself? We could make it accumulate a std::vector of attributepathparams passed in, so we can validate their values.

@@ -88,6 +88,26 @@ std::optional<CHIP_ERROR> TryWriteViaAccessInterface(const ConcreteDataAttribute

} // namespace

namespace detail {

void DefaultProviderChangeListener::MarkDirty(const AttributePathParams & path)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add unit tests for this.

///
/// It is expected that implementations provide a valid reference, ensuring that
/// attribute change reporting is functional throughout the lifecycle of the Provider.
virtual ProviderChangeListener & GetAttributeChangeReporter() = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't understand what the actual long-term plan here is. Specifically:

  1. Who owns the data that might actually change? Presumably they are responsible for changing it as needed.
  2. Who owns the DataVersion for a cluster instance? Presumably the entity from item 1 is either this entity, or if not is responsible for notifying it, right?
  3. Who is responsible for notifying the reporting engine that things are dirty?

ProviderChangeListener seems to be designed to be a per-InteractionModelContext singleton right now, but this is not how it's being used here...

Also, how is ProviderChangeListener different from chip::app::AttributesChangedListener and if it's not why do we have both?

It feels like we are adding more and more complexity here without a clear plan for what the final (simple!) goal should be.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can define some design doc ... maybe we should start in releases. In my mind:

  • DataModel::Provider owns everything and it notifies outside about "something dirty". This includes DataModel::Provider owns the clusterVersion

  • Existing AttributeAccessInterfaces own the attributes. So they can change them and they may choose to mark the cluster dirty or not (quiet reporting attributes would not be marked)

I am looking for a binding between AttributeAccessInterface (data owner) and DataModel::Provider clusterVersion (cluster/metadata owner), hence my belief that AAI should get some sort of Listener that it can notify when something changed. I would need:

  • AAI and CommandHandlerInterfaces need abiltiy to say "things changed", which should go to Provider (to update cluster version) and then go to reporting engine (to trigger reports)
  • Interface should be available to the App as well to trigger things on external changes (e.g. I manually pres a button, or sensor detects temperature change or something). However I would prefer if application interacts with clusters for this (so app tells cluster "i changed data" or "change this value for me" and cluster in turns notifies the provider who notifes the InteractionModelEngine.

Thoughts? Should we create some dock/hackmd or chat separaely to actually define what is sane?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't tell whether the above bullets are mutually exclusive or "we should do both these things" (in the first set of two bullets). Doing a sync discussion about this sounds like it might be good, just to make sure we are not talking past each other....

But my first question is this: why should the DataVersion be owned by the provider, not the cluster implementation? Is that so we can use the same cluster implementation with different providers?

And if the idea is that the provider owns the DataVersion, why do we have a separate interface defined for telling it "data version should update" instead of just having that be a method on the provider?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably warrants some design doc/discussion. Will see about creating one and discussing separately. In my mind:

  • Cluster owning the version makes the most sense, however I did not see an obvious path given the current ember coupling. There is no "ask cluster implementation about data version" but rather a global chip::DataVersion *emberAfDataVersionStorage() and a global emberAfAttributeChanged callback that increments that storage. Because it was global, the closest I found was the provider (which is often global, at least to start) would own this (i.e. codegen provider owns all emberAf* calls)

  • Separate interface seems to make sense to be able to inject to some clusters implementation the ability to notify the world their internal state changes. This can be DataModel::Provider however that seems to be a much larger interface. Thinking of mocking or generally dependency management, I prefer to pass in smaller dependencies as most clusters only need version change support.

@mergify mergify bot added the conflict label Dec 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants