Skip to content

Commit 4105172

Browse files
Set up a cluster state cache in MTRDevice.
We only really need this for data version and event number handling during re-subscribe, to minimize network traffic and load on devices.
1 parent f54f0f6 commit 4105172

File tree

2 files changed

+88
-8
lines changed

2 files changed

+88
-8
lines changed

src/darwin/Framework/CHIP/MTRDevice.mm

+16-8
Original file line numberDiff line numberDiff line change
@@ -401,10 +401,7 @@ - (void)_setupSubscription
401401
attributePath.release();
402402
eventPath.release();
403403

404-
std::unique_ptr<SubscriptionCallback> callback;
405-
std::unique_ptr<ReadClient> readClient;
406-
std::unique_ptr<ClusterStateCache> clusterStateCache;
407-
callback = std::make_unique<SubscriptionCallback>(
404+
auto callback = std::make_unique<SubscriptionCallback>(
408405
^(NSArray * value) {
409406
MTR_LOG_INFO("%@ got attribute report %@", self, value);
410407
dispatch_async(self.queue, ^{
@@ -447,8 +444,18 @@ - (void)_setupSubscription
447444
[self _handleSubscriptionReset];
448445
});
449446
});
450-
readClient = std::make_unique<ReadClient>(InteractionModelEngine::GetInstance(), exchangeManager,
451-
callback->GetBufferedCallback(), ReadClient::InteractionType::Subscribe);
447+
448+
// Set up a cluster state cache. We really just want this for the
449+
// logic it has for tracking data versions and event numbers so we
450+
// minimize the amount of data we request on resubscribes; we
451+
// don't care about the data it stores. Ideally we could use the
452+
// dataversion-management logic without needing to store the data
453+
// separately from the data store we already have, or we would
454+
// stop storing our data separately.
455+
auto clusterStateCache = std::make_unique<ClusterStateCache>(*callback.get());
456+
auto readClient
457+
= std::make_unique<ReadClient>(InteractionModelEngine::GetInstance(), exchangeManager,
458+
clusterStateCache->GetBufferedCallback(), ReadClient::InteractionType::Subscribe);
452459

453460
// SendAutoResubscribeRequest cleans up the params, even on failure.
454461
CHIP_ERROR err = readClient->SendAutoResubscribeRequest(std::move(readParams));
@@ -463,9 +470,10 @@ - (void)_setupSubscription
463470
return;
464471
}
465472

466-
// Callback and ReadClient will be deleted when OnDone is called or an error is
467-
// encountered.
473+
// Callback and ClusterStateCache and ReadClient will be deleted
474+
// when OnDone is called or an error is encountered.
468475
callback->AdoptReadClient(std::move(readClient));
476+
callback->AdoptClusterStateCache(std::move(clusterStateCache));
469477
callback.release();
470478
}];
471479
}

src/darwin/Framework/CHIPTests/MTRDeviceTests.m

+72
Original file line numberDiff line numberDiff line change
@@ -116,24 +116,37 @@ - (void)controller:(MTRDeviceController *)controller commissioningComplete:(NSEr
116116

117117
@end
118118

119+
typedef void (^MTRDeviceTestDelegateDataHandler)(NSArray<NSDictionary<NSString *, id> *> *);
120+
119121
@interface MTRDeviceTestDelegate : NSObject <MTRDeviceDelegate>
120122
@property (nonatomic) dispatch_block_t onSubscriptionEstablished;
123+
@property (nonatomic, nullable) MTRDeviceTestDelegateDataHandler onAttributeDataReceived;
124+
@property (nonatomic, nullable) MTRDeviceTestDelegateDataHandler onEventDataReceived;
125+
@property (nonatomic, nullable) dispatch_block_t onSubscriptionDropped;
121126
@end
122127

123128
@implementation MTRDeviceTestDelegate
124129
- (void)device:(MTRDevice *)device stateChanged:(MTRDeviceState)state
125130
{
126131
if (state == MTRDeviceStateReachable) {
127132
self.onSubscriptionEstablished();
133+
} else if (state == MTRDeviceStateUnknown && self.onSubscriptionDropped != nil) {
134+
self.onSubscriptionDropped();
128135
}
129136
}
130137

131138
- (void)device:(MTRDevice *)device receivedAttributeReport:(NSArray<NSDictionary<NSString *, id> *> *)attributeReport
132139
{
140+
if (self.onAttributeDataReceived != nil) {
141+
self.onAttributeDataReceived(attributeReport);
142+
}
133143
}
134144

135145
- (void)device:(MTRDevice *)device receivedEventReport:(NSArray<NSDictionary<NSString *, id> *> *)eventReport
136146
{
147+
if (self.onEventDataReceived != nil) {
148+
self.onEventDataReceived(eventReport);
149+
}
137150
}
138151

139152
@end
@@ -1369,9 +1382,68 @@ - (void)test017_TestMTRDeviceBasics
13691382
[subscriptionExpectation fulfill];
13701383
};
13711384

1385+
__block unsigned attributeReportsReceived = 0;
1386+
delegate.onAttributeDataReceived = ^(NSArray<NSDictionary<NSString *, id> *> * data) {
1387+
attributeReportsReceived += data.count;
1388+
};
1389+
1390+
__block unsigned eventReportsReceived = 0;
1391+
delegate.onEventDataReceived = ^(NSArray<NSDictionary<NSString *, id> *> * data) {
1392+
eventReportsReceived += data.count;
1393+
};
1394+
13721395
[device setDelegate:delegate queue:queue];
13731396

13741397
[self waitForExpectations:@[ subscriptionExpectation ] timeout:60];
1398+
1399+
XCTAssertNotEqual(attributeReportsReceived, 0);
1400+
1401+
attributeReportsReceived = 0;
1402+
eventReportsReceived = 0;
1403+
1404+
XCTestExpectation * resubscriptionExpectation = [self expectationWithDescription:@"Resubscription has happened"];
1405+
delegate.onSubscriptionEstablished = ^() {
1406+
[resubscriptionExpectation fulfill];
1407+
};
1408+
1409+
XCTestExpectation * subscriptionDroppedExpectation = [self expectationWithDescription:@"Subscription has dropped"];
1410+
delegate.onSubscriptionDropped = ^() {
1411+
[subscriptionDroppedExpectation fulfill];
1412+
};
1413+
1414+
// Now trigger another subscription which will cause ours to drop; we should re-subscribe after that.
1415+
MTRBaseDevice * baseDevice = GetConnectedDevice();
1416+
__auto_type params = [[MTRSubscribeParams alloc] initWithMinInterval:@(1) maxInterval:@(10)];
1417+
params.resubscribeIfLost = NO;
1418+
params.replaceExistingSubscriptions = YES;
1419+
// Create second subscription which will cancel the first subscription. We
1420+
// can use a non-existent path here to cut down on the work that gets done.
1421+
[baseDevice subscribeAttributeWithEndpointId:@10000
1422+
clusterId:@6
1423+
attributeId:@0
1424+
minInterval:@(1)
1425+
maxInterval:@(2)
1426+
params:params
1427+
clientQueue:queue
1428+
reportHandler:^(id _Nullable values, NSError * _Nullable error) {
1429+
}
1430+
subscriptionEstablished:^() {
1431+
}];
1432+
1433+
[self waitForExpectations:@[ subscriptionDroppedExpectation, resubscriptionExpectation ] timeout:60 enforceOrder:YES];
1434+
1435+
// Now make sure we ignore later tests. Ideally we would just unsubscribe
1436+
// or remove the delegate, but there's no good way to do that.
1437+
delegate.onSubscriptionEstablished = ^() {
1438+
};
1439+
delegate.onSubscriptionDropped = nil;
1440+
delegate.onAttributeDataReceived = nil;
1441+
delegate.onEventDataReceived = nil;
1442+
1443+
// Make sure we got no updated reports (because we had a cluster state cache
1444+
// with data versions) during the resubscribe.
1445+
XCTAssertEqual(attributeReportsReceived, 0);
1446+
XCTAssertEqual(eventReportsReceived, 0);
13751447
}
13761448

13771449
- (void)test018_SubscriptionErrorWhenNotResubscribing

0 commit comments

Comments
 (0)