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

Include event header information in results from multi-path read/subscribe. #26173

Merged
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
19 changes: 19 additions & 0 deletions src/darwin/Framework/CHIP/MTRBaseDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,20 @@ NS_ASSUME_NONNULL_BEGIN
* MTRDataKey: Data-value NSDictionary object.
* Included when there is data and when there is no error.
* The data-value is described below.
* MTREventNumberKey : NSNumber-wrapped uint64_t value. Monotonically increasing, and consecutive event reports
* should have consecutive numbers unless device reboots, or if events are lost.
* Only present when both MTREventPathKey and MTRDataKey are present.
* MTREventPriorityKey : NSNumber-wrapped MTREventPriority value.
* Only present when both MTREventPathKey and MTRDataKey are present.
* MTREventTimeTypeKey : NSNumber-wrapped MTREventTimeType value.
* Only present when both MTREventPathKey and MTRDataKey are present.
* MTREventSystemUpTimeKey : NSNumber-wrapped NSTimeInterval value.
* Only present when MTREventTimeTypeKey is MTREventTimeTypeSystemUpTime.
* MTREventTimestampDateKey : NSDate object.
* Only present when MTREventTimeTypeKey is MTREventTimeTypeTimestampDate.
*
* Only one of MTREventTimestampDateKey and MTREventSystemUpTimeKey will be present, depending on the value for
* MTREventTimeTypeKey.
*
* A data-value is an NSDictionary object with the following key values:
*
Expand Down Expand Up @@ -117,6 +131,11 @@ extern NSString * const MTRDoubleValueType;
extern NSString * const MTRNullValueType;
extern NSString * const MTRStructureValueType;
extern NSString * const MTRArrayValueType;
extern NSString * const MTREventNumberKey API_AVAILABLE(ios(16.5), macos(13.4), watchos(9.5), tvos(16.5));
extern NSString * const MTREventPriorityKey API_AVAILABLE(ios(16.5), macos(13.4), watchos(9.5), tvos(16.5));
extern NSString * const MTREventTimeTypeKey API_AVAILABLE(ios(16.5), macos(13.4), watchos(9.5), tvos(16.5));
extern NSString * const MTREventSystemUpTimeKey API_AVAILABLE(ios(16.5), macos(13.4), watchos(9.5), tvos(16.5));
extern NSString * const MTREventTimestampDateKey API_AVAILABLE(ios(16.5), macos(13.4), watchos(9.5), tvos(16.5));

@class MTRClusterStateCacheContainer;
@class MTRAttributeCacheContainer;
Expand Down
120 changes: 98 additions & 22 deletions src/darwin/Framework/CHIP/MTRBaseDevice.mm
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@
NSString * const MTRNullValueType = @"Null";
NSString * const MTRStructureValueType = @"Structure";
NSString * const MTRArrayValueType = @"Array";
NSString * const MTREventNumberKey = @"eventNumber";
NSString * const MTREventPriorityKey = @"eventPriority";
NSString * const MTREventTimeTypeKey = @"eventTimeType";
NSString * const MTREventSystemUpTimeKey = @"eventSystemUpTime";
NSString * const MTREventTimestampDateKey = @"eventTimestampDate";

class MTRDataValueDictionaryCallbackBridge;

Expand Down Expand Up @@ -752,7 +757,7 @@ CHIP_ERROR Encode(chip::TLV::TLVWriter & writer, chip::TLV::Tag tag) const
public:
using OnSuccessAttributeCallbackType
= std::function<void(const ConcreteAttributePath & aPath, const DecodableValueType & aData)>;
using OnSuccessEventCallbackType = std::function<void(const ConcreteEventPath & aPath, const DecodableValueType & aData)>;
using OnSuccessEventCallbackType = std::function<void(const EventHeader & aEventHeader, const DecodableValueType & aData)>;
using OnErrorCallbackType = std::function<void(
const app::ConcreteAttributePath * attributePath, const app::ConcreteEventPath * eventPath, CHIP_ERROR aError)>;
using OnDoneCallbackType = std::function<void(BufferedReadClientCallback * callback)>;
Expand Down Expand Up @@ -844,7 +849,7 @@ void OnEventData(const EventHeader & aEventHeader, TLV::TLVReader * apData, cons

SuccessOrExit(err = app::DataModel::Decode(*apData, value));

mOnEventSuccess(aEventHeader.mPath, value);
mOnEventSuccess(aEventHeader, value);

exit:
if (err != CHIP_NO_ERROR) {
Expand Down Expand Up @@ -944,31 +949,28 @@ - (void)readAttributePaths:(NSArray<MTRAttributeRequestPath *> * _Nullable)attri

auto resultArray = [[NSMutableArray alloc] init];
auto onAttributeSuccessCb
= [resultArray](const ConcreteAttributePath & attributePath, const MTRDataValueDictionaryDecodableType & aData) {
= [resultArray](const ConcreteAttributePath & aAttributePath, const MTRDataValueDictionaryDecodableType & aData) {
[resultArray addObject:@ {
MTRAttributePathKey : [[MTRAttributePath alloc] initWithPath:attributePath],
MTRAttributePathKey : [[MTRAttributePath alloc] initWithPath:aAttributePath],
MTRDataKey : aData.GetDecodedObject()
}];
};

auto onEventSuccessCb
= [resultArray](const ConcreteEventPath & eventPath, const MTRDataValueDictionaryDecodableType & aData) {
[resultArray addObject:@ {
MTREventPathKey : [[MTREventPath alloc] initWithPath:eventPath],
MTRDataKey : aData.GetDecodedObject()
}];
= [resultArray](const EventHeader & aEventHeader, const MTRDataValueDictionaryDecodableType & aData) {
[resultArray addObject:[MTRBaseDevice eventReportForHeader:aEventHeader andData:aData.GetDecodedObject()]];
};

auto onFailureCb = [resultArray, interactionStatus](const app::ConcreteAttributePath * attributePath,
const app::ConcreteEventPath * eventPath, CHIP_ERROR aError) {
if (attributePath != nullptr) {
auto onFailureCb = [resultArray, interactionStatus](const app::ConcreteAttributePath * aAttributePath,
const app::ConcreteEventPath * aEventPath, CHIP_ERROR aError) {
if (aAttributePath != nullptr) {
[resultArray addObject:@ {
MTRAttributePathKey : [[MTRAttributePath alloc] initWithPath:*attributePath],
MTRAttributePathKey : [[MTRAttributePath alloc] initWithPath:*aAttributePath],
MTRErrorKey : [MTRError errorForCHIPErrorCode:aError]
}];
} else if (eventPath != nullptr) {
} else if (aEventPath != nullptr) {
[resultArray addObject:@ {
MTREventPathKey : [[MTREventPath alloc] initWithPath:*eventPath],
MTREventPathKey : [[MTREventPath alloc] initWithPath:*aEventPath],
MTRErrorKey : [MTRError errorForCHIPErrorCode:aError]
}];
} else {
Expand Down Expand Up @@ -1343,14 +1345,11 @@ - (void)subscribeToAttributePaths:(NSArray<MTRAttributeRequestPath *> * _Nullabl
});
};

auto onEventReportCb = [queue, reportHandler](const ConcreteEventPath & eventPath,
const MTRDataValueDictionaryDecodableType & data) {
id valueObject = data.GetDecodedObject();
ConcreteEventPath pathCopy(eventPath);
auto onEventReportCb = [queue, reportHandler](
const EventHeader & eventHeader, const MTRDataValueDictionaryDecodableType & data) {
NSDictionary * report = [MTRBaseDevice eventReportForHeader:eventHeader andData:data.GetDecodedObject()];
dispatch_async(queue, ^{
reportHandler(
@[ @ { MTREventPathKey : [[MTREventPath alloc] initWithPath:pathCopy], MTRDataKey : valueObject } ],
nil);
reportHandler(@[ report ], nil);
});
};

Expand Down Expand Up @@ -1540,6 +1539,43 @@ static CHIP_ERROR OpenCommissioningWindow(Controller::DeviceController * control
delete self;
}

#pragma mark - Utility for time conversion
NSTimeInterval MTRTimeIntervalForEventTimestampValue(uint64_t timeValue)
{
// Note: The event timestamp value as written in the spec is in microseconds, but the released 1.0 SDK implemented it in
// milliseconds. The following issue was filed to address the inconsistency:
// https://github.com/CHIP-Specifications/connectedhomeip-spec/issues/6236
// For consistency with the released behavior, calculations here will be done in milliseconds.

// First convert the event timestamp value (in milliseconds) to NSTimeInterval - to minimize potential loss of precision
// of uint64 => NSTimeInterval (double), convert whole seconds and remainder separately and then combine
uint64_t eventTimestampValueSeconds = timeValue / chip::kMillisecondsPerSecond;
uint64_t eventTimestampValueRemainderMilliseconds = timeValue % chip::kMillisecondsPerSecond;
NSTimeInterval eventTimestampValueRemainder
= NSTimeInterval(eventTimestampValueRemainderMilliseconds) / chip::kMillisecondsPerSecond;
NSTimeInterval eventTimestampValue = eventTimestampValueSeconds + eventTimestampValueRemainder;

return eventTimestampValue;
}

#pragma mark - Utility for event priority conversion
BOOL MTRPriorityLevelIsValid(chip::app::PriorityLevel priorityLevel)
{
return (priorityLevel >= chip::app::PriorityLevel::Debug) && (priorityLevel <= chip::app::PriorityLevel::Critical);
}

MTREventPriority MTREventPriorityForValidPriorityLevel(chip::app::PriorityLevel priorityLevel)
{
switch (priorityLevel) {
case chip::app::PriorityLevel::Debug:
return MTREventPriorityDebug;
case chip::app::PriorityLevel::Info:
return MTREventPriorityInfo;
default:
return MTREventPriorityCritical;
}
}

} // anonymous namespace

- (void)_openCommissioningWindowWithSetupPasscode:(nullable NSNumber *)setupPasscode
Expand Down Expand Up @@ -1738,6 +1774,46 @@ - (void)subscribeToEventsWithEndpointID:(NSNumber * _Nullable)endpointID
subscriptionEstablished:subscriptionEstablished
resubscriptionScheduled:nil];
}

+ (NSDictionary *)eventReportForHeader:(const chip::app::EventHeader &)header andData:(id _Nullable)data
{
MTREventPath * eventPath = [[MTREventPath alloc] initWithPath:header.mPath];
if (data == nil) {
MTR_LOG_ERROR("%@ could not decode event data", eventPath);
return @{ MTREventPathKey : eventPath, MTRErrorKey : [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_ARGUMENT] };
}

// Construct the right type, and key/value depending on the type
NSNumber * eventTimeType;
NSString * timestampKey;
id timestampValue;
if (header.mTimestamp.mType == Timestamp::Type::kSystem) {
eventTimeType = @(MTREventTimeTypeSystemUpTime);
timestampKey = MTREventSystemUpTimeKey;
timestampValue = @(MTRTimeIntervalForEventTimestampValue(header.mTimestamp.mValue));
} else if (header.mTimestamp.mType == Timestamp::Type::kEpoch) {
eventTimeType = @(MTREventTimeTypeTimestampDate);
timestampKey = MTREventTimestampDateKey;
timestampValue = [NSDate dateWithTimeIntervalSince1970:MTRTimeIntervalForEventTimestampValue(header.mTimestamp.mValue)];
} else {
MTR_LOG_ERROR("%@ Unsupported event timestamp type %u - ignoring", eventPath, (unsigned int) header.mTimestamp.mType);
return @{ MTREventPathKey : eventPath, MTRErrorKey : [MTRError errorForCHIPErrorCode:CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE] };
}

if (!MTRPriorityLevelIsValid(header.mPriorityLevel)) {
MTR_LOG_ERROR("%@ Unsupported event priority %u - ignoring", eventPath, (unsigned int) header.mPriorityLevel);
return @{ MTREventPathKey : eventPath, MTRErrorKey : [MTRError errorForCHIPErrorCode:CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE] };
}

return @{
MTREventPathKey : eventPath,
MTRDataKey : data,
MTREventNumberKey : @(header.mEventNumber),
MTREventPriorityKey : @(MTREventPriorityForValidPriorityLevel(header.mPriorityLevel)),
MTREventTimeTypeKey : eventTimeType,
timestampKey : timestampValue
};
}
@end

@implementation MTRBaseDevice (Deprecated)
Expand Down
8 changes: 8 additions & 0 deletions src/darwin/Framework/CHIP/MTRBaseDevice_Internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <app/ConcreteCommandPath.h>
#include <app/ConcreteEventPath.h>
#include <app/DeviceProxy.h>
#include <app/EventHeader.h>
#include <app/EventLoggingTypes.h>
#include <app/EventPathParams.h>

Expand Down Expand Up @@ -75,6 +76,13 @@ static inline MTRTransportType MTRMakeTransportType(chip::Transport::Type type)
*/
- (instancetype)initWithNodeID:(NSNumber *)nodeID controller:(MTRDeviceController *)controller;

/**
* Create a report, suitable in including in the sort of data structure that
* gets passed to MTRDeviceResponseHandler, from a given event header and
* already-decoded event data. The data is allowed to be nil in error cases
* (e.g. when TLV decoding failed).
*/
+ (NSDictionary *)eventReportForHeader:(const chip::app::EventHeader &)header andData:(id _Nullable)data;
@end

@interface MTRClusterPath ()
Expand Down
6 changes: 0 additions & 6 deletions src/darwin/Framework/CHIP/MTRDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,12 +202,6 @@ typedef NS_ENUM(NSUInteger, MTRDeviceState) {

@end

extern NSString * const MTREventNumberKey API_AVAILABLE(ios(16.5), macos(13.4), watchos(9.5), tvos(16.5));
extern NSString * const MTREventPriorityKey API_AVAILABLE(ios(16.5), macos(13.4), watchos(9.5), tvos(16.5));
extern NSString * const MTREventTimeTypeKey API_AVAILABLE(ios(16.5), macos(13.4), watchos(9.5), tvos(16.5));
extern NSString * const MTREventSystemUpTimeKey API_AVAILABLE(ios(16.5), macos(13.4), watchos(9.5), tvos(16.5));
extern NSString * const MTREventTimestampDateKey API_AVAILABLE(ios(16.5), macos(13.4), watchos(9.5), tvos(16.5));

@protocol MTRDeviceDelegate <NSObject>
@required
/**
Expand Down
84 changes: 7 additions & 77 deletions src/darwin/Framework/CHIP/MTRDevice.mm
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,6 @@
#include <app/InteractionModelEngine.h>
#include <platform/PlatformManager.h>

NSString * const MTREventNumberKey = @"eventNumber";
NSString * const MTREventPriorityKey = @"eventPriority";
NSString * const MTREventTimeTypeKey = @"eventTimeType";
NSString * const MTREventSystemUpTimeKey = @"eventSystemUpTime";
NSString * const MTREventTimestampDateKey = @"eventTimestampDate";

typedef void (^MTRDeviceAttributeReportHandler)(NSArray * _Nonnull);

// Consider moving utility classes to their own file
Expand Down Expand Up @@ -89,41 +83,6 @@ - (id)strongObject
return aNumber;
}

NSTimeInterval MTRTimeIntervalForEventTimestampValue(uint64_t timeValue)
{
// Note: The event timestamp value as written in the spec is in microseconds, but the released 1.0 SDK implemented it in
// milliseconds. The following issue was filed to address the inconsistency:
// https://github.com/CHIP-Specifications/connectedhomeip-spec/issues/6236
// For consistency with the released behavior, calculations here will be done in milliseconds.

// First convert the event timestamp value (in milliseconds) to NSTimeInterval - to minimize potential loss of precision
// of uint64 => NSTimeInterval (double), convert whole seconds and remainder separately and then combine
uint64_t eventTimestampValueSeconds = timeValue / chip::kMillisecondsPerSecond;
uint64_t eventTimestampValueRemainderMilliseconds = timeValue % chip::kMillisecondsPerSecond;
NSTimeInterval eventTimestampValueRemainder
= NSTimeInterval(eventTimestampValueRemainderMilliseconds) / chip::kMillisecondsPerSecond;
NSTimeInterval eventTimestampValue = eventTimestampValueSeconds + eventTimestampValueRemainder;

return eventTimestampValue;
}

BOOL MTRPriorityLevelIsValid(chip::app::PriorityLevel priorityLevel)
{
return (priorityLevel >= chip::app::PriorityLevel::Debug) && (priorityLevel <= chip::app::PriorityLevel::Critical);
}

MTREventPriority MTREventPriorityForValidPriorityLevel(chip::app::PriorityLevel priorityLevel)
{
switch (priorityLevel) {
case chip::app::PriorityLevel::Debug:
return MTREventPriorityDebug;
case chip::app::PriorityLevel::Info:
return MTREventPriorityInfo;
default:
return MTREventPriorityCritical;
}
}

#pragma mark - SubscriptionCallback class declaration
using namespace chip;
using namespace chip::app;
Expand Down Expand Up @@ -1214,43 +1173,14 @@ - (void)invokeCommandWithEndpointID:(NSNumber *)endpointID
MTRErrorKey : [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_ARGUMENT]
}];
} else {
id value = MTRDecodeDataValueDictionaryFromCHIPTLV(apData);
if (value) {
// Construct the right type, and key/value depending on the type
NSNumber * eventTimeType;
NSString * timestampKey;
id timestampValue;
if (aEventHeader.mTimestamp.mType == Timestamp::Type::kSystem) {
eventTimeType = @(MTREventTimeTypeSystemUpTime);
timestampKey = MTREventSystemUpTimeKey;
timestampValue = @(MTRTimeIntervalForEventTimestampValue(aEventHeader.mTimestamp.mValue));
} else if (aEventHeader.mTimestamp.mType == Timestamp::Type::kEpoch) {
eventTimeType = @(MTREventTimeTypeTimestampDate);
timestampKey = MTREventTimestampDateKey;
timestampValue =
[NSDate dateWithTimeIntervalSince1970:MTRTimeIntervalForEventTimestampValue(aEventHeader.mTimestamp.mValue)];
} else {
MTR_LOG_INFO(
"%@ Unsupported event timestamp type %u - ignoring", eventPath, (unsigned int) aEventHeader.mTimestamp.mType);
ReportError(CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE);
return;
}

if (!MTRPriorityLevelIsValid(aEventHeader.mPriorityLevel)) {
MTR_LOG_INFO("%@ Unsupported event priority %u - ignoring", eventPath, (unsigned int) aEventHeader.mPriorityLevel);
ReportError(CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE);
return;
}

[mEventReports addObject:@{
MTREventPathKey : eventPath,
MTRDataKey : value,
MTREventNumberKey : @(aEventHeader.mEventNumber),
MTREventPriorityKey : @(MTREventPriorityForValidPriorityLevel(aEventHeader.mPriorityLevel)),
MTREventTimeTypeKey : eventTimeType,
timestampKey : timestampValue
}];
id value;
if (apData == nullptr) {
value = nil;
} else {
value = MTRDecodeDataValueDictionaryFromCHIPTLV(apData);
}

[mEventReports addObject:[MTRBaseDevice eventReportForHeader:aEventHeader andData:value]];
}
}

Expand Down
7 changes: 0 additions & 7 deletions src/darwin/Framework/CHIP/MTRDevice_Internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,4 @@ typedef void (^MTRDevicePerformAsyncBlock)(MTRBaseDevice * baseDevice);
// Returns min or max, if it is below or above, respectively.
NSNumber * MTRClampedNumber(NSNumber * aNumber, NSNumber * min, NSNumber * max);

#pragma mark - Utility for time conversion
NSTimeInterval MTRTimeIntervalForEventTimestampValue(uint64_t timeValue);

#pragma mark - Utility for event priority conversion
BOOL MTRPriorityLevelIsValid(chip::app::PriorityLevel priorityLevel);
MTREventPriority MTREventPriorityForValidPriorityLevel(chip::app::PriorityLevel);

NS_ASSUME_NONNULL_END
Loading