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

Make MTRClusters just use MTRDevice for command invokes. #29541

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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
Loading