@@ -165,8 +165,16 @@ typedef NS_ENUM(NSUInteger, MTRDeviceReadRequestFieldIndex) {
165
165
MTRDeviceReadRequestFieldParamsIndex = 1
166
166
};
167
167
168
+ typedef NS_ENUM (NSUInteger , MTRDeviceWriteRequestFieldIndex) {
169
+ MTRDeviceWriteRequestFieldPathIndex = 0 ,
170
+ MTRDeviceWriteRequestFieldValueIndex = 1 ,
171
+ MTRDeviceWriteRequestFieldTimeoutIndex = 2 ,
172
+ MTRDeviceWriteRequestFieldExpectedValueIDIndex = 3 ,
173
+ };
174
+
168
175
typedef NS_ENUM (NSUInteger , MTRDeviceWorkItemBatchingID) {
169
176
MTRDeviceWorkItemBatchingReadID = 1 ,
177
+ MTRDeviceWorkItemBatchingWriteID = 2 ,
170
178
};
171
179
172
180
typedef NS_ENUM (NSUInteger , MTRDeviceWorkItemDuplicateTypeID) {
@@ -1658,6 +1666,50 @@ - (void)writeAttributeWithEndpointID:(NSNumber *)endpointID
1658
1666
1659
1667
MTRAsyncWorkItem * workItem = [[MTRAsyncWorkItem alloc ] initWithQueue: self .queue];
1660
1668
uint64_t workItemID = workItem.uniqueID ; // capture only the ID, not the work item
1669
+ NSNumber * nodeID = _nodeID;
1670
+
1671
+ // Write request data is an array of items (for now always length 1). Each
1672
+ // item is an array containing:
1673
+ //
1674
+ // [ attribute path, value, timedWriteTimeout, expectedValueID ]
1675
+ //
1676
+ // where expectedValueID is stored as NSNumber and NSNull represents nil timeouts
1677
+ auto * writeData = @[ attributePath, [value copy ], timeout ?: [NSNull null ], @(expectedValueID) ];
1678
+
1679
+ NSMutableArray <NSArray *> * writeRequests = [NSMutableArray arrayWithObject: writeData];
1680
+
1681
+ [workItem setBatchingID: MTRDeviceWorkItemBatchingWriteID data: writeRequests handler: ^(id opaqueDataCurrent, id opaqueDataNext) {
1682
+ mtr_hide (self); // don't capture self accidentally
1683
+ NSMutableArray <NSArray *> * writeRequestsCurrent = opaqueDataCurrent;
1684
+ NSMutableArray <NSArray *> * writeRequestsNext = opaqueDataNext;
1685
+
1686
+ if (writeRequestsCurrent.count != 1 ) {
1687
+ // Very unexpected!
1688
+ MTR_LOG_ERROR (" Batching write attribute work item [%llu]: Unexpected write request count %tu" , workItemID, writeRequestsCurrent.count );
1689
+ return MTRNotBatched;
1690
+ }
1691
+
1692
+ MTRBatchingOutcome outcome = MTRNotBatched;
1693
+ while (writeRequestsNext.count ) {
1694
+ // If paths don't match, we cannot replace the earlier write
1695
+ // with the later one.
1696
+ if (![writeRequestsNext[0 ][MTRDeviceWriteRequestFieldPathIndex]
1697
+ isEqual: writeRequestsCurrent[0 ][MTRDeviceWriteRequestFieldPathIndex]]) {
1698
+ MTR_LOG_INFO (" Batching write attribute work item [%llu]: cannot replace with next work item due to path mismatch" , workItemID);
1699
+ return outcome;
1700
+ }
1701
+
1702
+ // Replace our one request with the first one from the next item.
1703
+ auto writeItem = writeRequestsNext.firstObject ;
1704
+ [writeRequestsNext removeObjectAtIndex: 0 ];
1705
+ [writeRequestsCurrent replaceObjectAtIndex: 0 withObject: writeItem];
1706
+ MTR_LOG_INFO (" Batching write attribute work item [%llu]: replaced with new write value %@ [0x%016llX]" ,
1707
+ workItemID, writeItem, nodeID.unsignedLongLongValue );
1708
+ outcome = MTRBatchedPartially;
1709
+ }
1710
+ NSCAssert (writeRequestsNext.count == 0 , @" should have batched everything or returned early" );
1711
+ return MTRBatchedFully;
1712
+ }];
1661
1713
// The write operation will install a duplicate check handler, to return NO for "isDuplicate". Since a write operation may
1662
1714
// change values, only read requests after this should be considered for duplicate requests.
1663
1715
[workItem setDuplicateTypeID: MTRDeviceWorkItemDuplicateReadTypeID handler: ^(id opaqueItemData, BOOL * isDuplicate, BOOL * stop) {
@@ -1666,18 +1718,31 @@ - (void)writeAttributeWithEndpointID:(NSNumber *)endpointID
1666
1718
}];
1667
1719
[workItem setReadyHandler: ^(MTRDevice * self , NSInteger retryCount, MTRAsyncWorkCompletionBlock completion) {
1668
1720
MTRBaseDevice * baseDevice = [self newBaseDevice ];
1721
+ // Make sure to use writeRequests here, because that's what our batching
1722
+ // handler will modify as needed.
1723
+ NSCAssert (writeRequests.count == 1 , @" Incorrect number of write requests: %tu " , writeRequests.count);
1724
+
1725
+ auto * request = writeRequests[0 ];
1726
+ MTRAttributePath * path = request[MTRDeviceWriteRequestFieldPathIndex];
1727
+
1728
+ id timedWriteTimeout = request[MTRDeviceWriteRequestFieldTimeoutIndex];
1729
+ if (timedWriteTimeout == [NSNull null ]) {
1730
+ timedWriteTimeout = nil ;
1731
+ }
1732
+
1669
1733
[baseDevice
1670
- writeAttributeWithEndpointID: endpointID
1671
- clusterID: clusterID
1672
- attributeID: attributeID
1673
- value: value
1674
- timedWriteTimeout: timeout
1734
+ writeAttributeWithEndpointID: path.endpoint
1735
+ clusterID: path.cluster
1736
+ attributeID: path.attribute
1737
+ value: request[MTRDeviceWriteRequestFieldValueIndex]
1738
+ timedWriteTimeout: timedWriteTimeout
1675
1739
queue: self .queue
1676
1740
completion: ^(NSArray <NSDictionary <NSString *, id > *> * _Nullable values, NSError * _Nullable error) {
1677
1741
if (error) {
1678
1742
MTR_LOG_ERROR (" Write attribute work item [%llu] failed: %@" , workItemID, error);
1679
1743
if (useValueAsExpectedValue) {
1680
- [self removeExpectedValueForAttributePath: attributePath expectedValueID: expectedValueID];
1744
+ NSNumber * expectedValueID = request[MTRDeviceWriteRequestFieldExpectedValueIDIndex];
1745
+ [self removeExpectedValueForAttributePath: attributePath expectedValueID: expectedValueID.unsignedLongLongValue];
1681
1746
}
1682
1747
}
1683
1748
completion (MTRAsyncWorkComplete);
0 commit comments