Skip to content

Commit

Permalink
Make MTRClusters just use MTRDevice for command invokes. (#29541)
Browse files Browse the repository at this point in the history
Decouples them from MTRBaseClusters.
  • Loading branch information
bzbarsky-apple authored and pull[bot] committed Apr 1, 2024
1 parent 9dd042a commit 3250447
Show file tree
Hide file tree
Showing 16 changed files with 19,032 additions and 6,891 deletions.
47 changes: 41 additions & 6 deletions src/darwin/Framework/CHIP/MTRBaseDevice.mm
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ - (void)subscribeWithQueue:(dispatch_queue_t)queue
}

// Convert TLV data into data-value dictionary as described in MTRDeviceResponseHandler
id _Nullable MTRDecodeDataValueDictionaryFromCHIPTLV(chip::TLV::TLVReader * data)
NSDictionary<NSString *, id> * _Nullable MTRDecodeDataValueDictionaryFromCHIPTLV(chip::TLV::TLVReader * data)
{
chip::TLV::TLVType dataTLVType = data->GetType();
switch (dataTLVType) {
Expand Down Expand Up @@ -1208,14 +1208,45 @@ - (void)invokeCommandWithEndpointID:(NSNumber *)endpointID
timedInvokeTimeout:(NSNumber * _Nullable)timeoutMs
queue:(dispatch_queue_t)queue
completion:(MTRDeviceResponseHandler)completion
{
// We don't have a way to communicate a non-default invoke timeout
// here for now.
// TODO: https://github.com/project-chip/connectedhomeip/issues/24563
[self _invokeCommandWithEndpointID:endpointID
clusterID:clusterID
commandID:commandID
commandFields:commandFields
timedInvokeTimeout:timeoutMs
serverSideProcessingTimeout:nil
queue:queue
completion:completion];
}

- (void)_invokeCommandWithEndpointID:(NSNumber *)endpointID
clusterID:(NSNumber *)clusterID
commandID:(NSNumber *)commandID
commandFields:(id)commandFields
timedInvokeTimeout:(NSNumber * _Nullable)timeoutMs
serverSideProcessingTimeout:(NSNumber * _Nullable)serverSideProcessingTimeout
queue:(dispatch_queue_t)queue
completion:(MTRDeviceResponseHandler)completion
{
endpointID = (endpointID == nil) ? nil : [endpointID copy];
clusterID = (clusterID == nil) ? nil : [clusterID copy];
commandID = (commandID == nil) ? nil : [commandID copy];
// TODO: This is not going to deep-copy the NSArray instances in
// commandFields. We need to do something smarter here.
commandFields = (commandFields == nil) ? nil : [commandFields copy];
timeoutMs = (timeoutMs == nil) ? nil : [timeoutMs copy];

serverSideProcessingTimeout = [serverSideProcessingTimeout copy];
if (serverSideProcessingTimeout != nil) {
serverSideProcessingTimeout = MTRClampedNumber(serverSideProcessingTimeout, @(0), @(UINT16_MAX));
}

timeoutMs = [timeoutMs copy];
if (timeoutMs != nil) {
timeoutMs = MTRClampedNumber(timeoutMs, @(1), @(UINT16_MAX));
}

auto * bridge = new MTRDataValueDictionaryCallbackBridge(queue, completion,
^(ExchangeManager & exchangeManager, const SessionHandle & session, MTRDataValueDictionaryCallback successCb,
Expand Down Expand Up @@ -1261,10 +1292,14 @@ - (void)invokeCommandWithEndpointID:(NSNumber *)endpointID
ReturnErrorOnFailure(commandSender->AddRequestData(commandPath, MTRDataValueDictionaryDecodableType(commandFields),
(timeoutMs == nil) ? NullOptional : Optional<uint16_t>([timeoutMs unsignedShortValue])));

// We don't have a way to communicate a non-default invoke timeout
// here for now.
// TODO: https://github.com/project-chip/connectedhomeip/issues/24563
ReturnErrorOnFailure(commandSender->SendCommandRequest(session, NullOptional));
Optional<System::Clock::Timeout> invokeTimeout;
if (serverSideProcessingTimeout != nil) {
// Clamp to a number of seconds that will not overflow 32-bit
// int when converted to ms.
auto serverTimeoutInSeconds = System::Clock::Seconds16(serverSideProcessingTimeout.unsignedShortValue);
invokeTimeout.SetValue(session->ComputeRoundTripTimeout(serverTimeoutInSeconds));
}
ReturnErrorOnFailure(commandSender->SendCommandRequest(session, invokeTimeout));

decoder.release();
commandSender.release();
Expand Down
16 changes: 15 additions & 1 deletion src/darwin/Framework/CHIP/MTRBaseDevice_Internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,20 @@ static inline MTRTransportType MTRMakeTransportType(chip::Transport::Type type)
clusterID:(chip::ClusterId)clusterID
commandID:(chip::CommandId)commandID
error:(NSError * __autoreleasing *)error;

/**
* Like the public invokeCommandWithEndpointID but allows passing through a
* serverSideProcessingTimeout.
*/
- (void)_invokeCommandWithEndpointID:(NSNumber *)endpointID
clusterID:(NSNumber *)clusterID
commandID:(NSNumber *)commandID
commandFields:(id)commandFields
timedInvokeTimeout:(NSNumber * _Nullable)timeoutMs
serverSideProcessingTimeout:(NSNumber * _Nullable)serverSideProcessingTimeout
queue:(dispatch_queue_t)queue
completion:(MTRDeviceResponseHandler)completion;

@end

@interface MTRClusterPath ()
Expand Down Expand Up @@ -140,6 +154,6 @@ static inline MTRTransportType MTRMakeTransportType(chip::Transport::Type type)

// Exported utility function
// Convert TLV data into data-value dictionary as described in MTRDeviceResponseHandler
id _Nullable MTRDecodeDataValueDictionaryFromCHIPTLV(chip::TLV::TLVReader * data);
NSDictionary<NSString *, id> * _Nullable MTRDecodeDataValueDictionaryFromCHIPTLV(chip::TLV::TLVReader * data);

NS_ASSUME_NONNULL_END
9 changes: 0 additions & 9 deletions src/darwin/Framework/CHIP/MTRCluster.mm
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,6 @@ - (instancetype)initWithEndpointID:(NSNumber *)endpointID queue:(dispatch_queue_
return self;
}

- (chip::ByteSpan)asByteSpan:(NSData *)value
{
return AsByteSpan(value);
}

- (chip::CharSpan)asCharSpan:(NSString *)value
{
return AsCharSpan(value);
}
@end

@implementation MTRWriteParams
Expand Down
2 changes: 0 additions & 2 deletions src/darwin/Framework/CHIP/MTRCluster_Internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) chip::EndpointId endpoint;

- (instancetype)initWithEndpointID:(NSNumber *)endpointID queue:(dispatch_queue_t)queue;
- (chip::ByteSpan)asByteSpan:(NSData *)value;
- (chip::CharSpan)asCharSpan:(NSString *)value;
@end

@interface MTRReadParams ()
Expand Down
125 changes: 103 additions & 22 deletions src/darwin/Framework/CHIP/MTRDevice.mm
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#import "MTRError_Internal.h"
#import "MTREventTLVValueDecoder_Internal.h"
#import "MTRLogging_Internal.h"
#import "zap-generated/MTRCommandPayloads_Internal.h"

#include "lib/core/CHIPError.h"
#include "lib/core/DataModelTypes.h"
Expand Down Expand Up @@ -1056,17 +1057,44 @@ - (void)invokeCommandWithEndpointID:(NSNumber *)endpointID
timedInvokeTimeout:(NSNumber * _Nullable)timeout
queue:(dispatch_queue_t)queue
completion:(MTRDeviceResponseHandler)completion
{
// We don't have a way to communicate a non-default invoke timeout
// here for now.
// TODO: https://github.com/project-chip/connectedhomeip/issues/24563

[self _invokeCommandWithEndpointID:endpointID
clusterID:clusterID
commandID:commandID
commandFields:commandFields
expectedValues:expectedValues
expectedValueInterval:expectedValueInterval
timedInvokeTimeout:timeout
serverSideProcessingTimeout:nil
queue:queue
completion:completion];
}

- (void)_invokeCommandWithEndpointID:(NSNumber *)endpointID
clusterID:(NSNumber *)clusterID
commandID:(NSNumber *)commandID
commandFields:(id)commandFields
expectedValues:(NSArray<NSDictionary<NSString *, id> *> * _Nullable)expectedValues
expectedValueInterval:(NSNumber * _Nullable)expectedValueInterval
timedInvokeTimeout:(NSNumber * _Nullable)timeout
serverSideProcessingTimeout:(NSNumber * _Nullable)serverSideProcessingTimeout
queue:(dispatch_queue_t)queue
completion:(MTRDeviceResponseHandler)completion
{
NSString * logPrefix = [NSString stringWithFormat:@"%@ command %@ %@ %@", self, endpointID, clusterID, commandID];
if (timeout) {
timeout = MTRClampedNumber(timeout, @(1), @(UINT16_MAX));
}
if (!expectedValueInterval || ([expectedValueInterval compare:@(0)] == NSOrderedAscending)) {
expectedValues = nil;
} else {
expectedValueInterval = MTRClampedNumber(expectedValueInterval, @(1), @(UINT32_MAX));
}

serverSideProcessingTimeout = [serverSideProcessingTimeout copy];
timeout = [timeout copy];

uint64_t expectedValueID = 0;
NSMutableArray<MTRAttributePath *> * attributePaths = nil;
if (expectedValues) {
Expand All @@ -1087,30 +1115,83 @@ - (void)invokeCommandWithEndpointID:(NSNumber *)endpointID
MTR_LOG_DEFAULT("%@ dequeueWorkItem %@", logPrefix, self->_asyncWorkQueue);
MTRBaseDevice * baseDevice = [self newBaseDevice];
[baseDevice
invokeCommandWithEndpointID:endpointID
clusterID:clusterID
commandID:commandID
commandFields:commandFields
timedInvokeTimeout:timeout
queue:self.queue
completion:^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
// Log the data at the INFO level (not usually persisted permanently),
// but make sure we log the work completion at the DEFAULT level.
MTR_LOG_INFO("%@ received response: %@ error: %@", logPrefix, values, error);
dispatch_async(queue, ^{
completion(values, error);
});
if (error && expectedValues) {
[self removeExpectedValuesForAttributePaths:attributePaths expectedValueID:expectedValueID];
}
MTR_LOG_DEFAULT("%@ endWork", logPrefix);
workCompletion(MTRAsyncWorkComplete);
}];
_invokeCommandWithEndpointID:endpointID
clusterID:clusterID
commandID:commandID
commandFields:commandFields
timedInvokeTimeout:timeout
serverSideProcessingTimeout:serverSideProcessingTimeout
queue:self.queue
completion:^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
// Log the data at the INFO level (not usually persisted permanently),
// but make sure we log the work completion at the DEFAULT level.
MTR_LOG_INFO("%@ received response: %@ error: %@", logPrefix, values, error);
dispatch_async(queue, ^{
completion(values, error);
});
if (error && expectedValues) {
[self removeExpectedValuesForAttributePaths:attributePaths expectedValueID:expectedValueID];
}
MTR_LOG_DEFAULT("%@ endWork", logPrefix);
workCompletion(MTRAsyncWorkComplete);
}];
}];
MTR_LOG_DEFAULT("%@ enqueueWorkItem %@", logPrefix, _asyncWorkQueue);
[_asyncWorkQueue enqueueWorkItem:workItem];
}

- (void)_invokeKnownCommandWithEndpointID:(NSNumber *)endpointID
clusterID:(NSNumber *)clusterID
commandID:(NSNumber *)commandID
commandPayload:(id)commandPayload
expectedValues:(NSArray<NSDictionary<NSString *, id> *> * _Nullable)expectedValues
expectedValueInterval:(NSNumber * _Nullable)expectedValueInterval
timedInvokeTimeout:(NSNumber * _Nullable)timeout
serverSideProcessingTimeout:(NSNumber * _Nullable)serverSideProcessingTimeout
responseClass:(Class _Nullable)responseClass
queue:(dispatch_queue_t)queue
completion:(void (^)(id _Nullable response, NSError * _Nullable error))completion
{
if (![commandPayload respondsToSelector:@selector(_encodeAsDataValue:)]) {
dispatch_async(queue, ^{
completion(nil, [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_ARGUMENT]);
});
return;
}

NSError * encodingError;
auto * commandFields = [commandPayload _encodeAsDataValue:&encodingError];
if (commandFields == nil) {
dispatch_async(queue, ^{
completion(nil, encodingError);
});
return;
}

auto responseHandler = ^(NSArray<NSDictionary<NSString *, id> *> * _Nullable values, NSError * _Nullable error) {
id _Nullable response = nil;
if (error == nil) {
if (values.count != 1) {
error = [NSError errorWithDomain:MTRErrorDomain code:MTRErrorCodeSchemaMismatch userInfo:nil];
} else if (responseClass != nil) {
response = [[responseClass alloc] initWithResponseValue:values[0] error:&error];
}
}
completion(response, error);
};

[self _invokeCommandWithEndpointID:endpointID
clusterID:clusterID
commandID:commandID
commandFields:commandFields
expectedValues:expectedValues
expectedValueInterval:expectedValueInterval
timedInvokeTimeout:timeout
serverSideProcessingTimeout:serverSideProcessingTimeout
queue:queue
completion:responseHandler];
}

- (void)openCommissioningWindowWithSetupPasscode:(NSNumber *)setupPasscode
discriminator:(NSNumber *)discriminator
duration:(NSNumber *)duration
Expand Down
20 changes: 20 additions & 0 deletions src/darwin/Framework/CHIP/MTRDevice_Internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,26 @@ typedef void (^MTRDevicePerformAsyncBlock)(MTRBaseDevice * baseDevice);
// false-positives, for example due to compressed fabric id collisions.
- (void)nodeMayBeAdvertisingOperational;

/**
* Like the public invokeCommandWithEndpointID but:
*
* 1) Allows passing through a serverSideProcessingTimeout.
* 2) Expects one of the command payload structs as commandPayload
* 3) On success, returns an instance of responseClass via the completion (or
* nil if there is no responseClass, which indicates a status-only command).
*/
- (void)_invokeKnownCommandWithEndpointID:(NSNumber *)endpointID
clusterID:(NSNumber *)clusterID
commandID:(NSNumber *)commandID
commandPayload:(id)commandPayload
expectedValues:(NSArray<NSDictionary<NSString *, id> *> * _Nullable)expectedValues
expectedValueInterval:(NSNumber * _Nullable)expectedValueInterval
timedInvokeTimeout:(NSNumber * _Nullable)timeout
serverSideProcessingTimeout:(NSNumber * _Nullable)serverSideProcessingTimeout
responseClass:(Class _Nullable)responseClass
queue:(dispatch_queue_t)queue
completion:(void (^)(id _Nullable response, NSError * _Nullable error))completion;

@property (nonatomic, readonly) MTRDeviceController * deviceController;
@property (nonatomic, readonly, copy) NSNumber * nodeID;
// Queue used for various internal bookkeeping work.
Expand Down
2 changes: 2 additions & 0 deletions src/darwin/Framework/CHIP/templates/MTRBaseClusters-src.zapt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#import "MTRCommandPayloadsObjc.h"
#import "MTRDevice_Internal.h"
#import "MTRStructsObjc.h"
#import "NSStringSpanConversion.h"
#import "NSDataSpanConversion.h"

#include <controller/CHIPCluster.h>
#include <lib/support/CHIPListUtils.h>
Expand Down
Loading

0 comments on commit 3250447

Please sign in to comment.