Skip to content

Commit 4a20ea5

Browse files
tehampsonpull[bot]
authored andcommitted
Add WriteGroupAttributes to chip-repl (#25260)
* Add WriteGroupAttributes to chip-repl * Address PR comments
1 parent 7d47580 commit 4a20ea5

File tree

5 files changed

+137
-10
lines changed

5 files changed

+137
-10
lines changed

scripts/tests/chiptest/__init__.py

+3-6
Original file line numberDiff line numberDiff line change
@@ -128,13 +128,10 @@ def _GetSlowTests() -> Set[str]:
128128
def _GetInDevelopmentTests() -> Set[str]:
129129
"""Tests that fail in YAML for some reason.
130130
131-
Goal is for this set to become empty.
131+
Currently this is empty and returns an empty set, but this is kept around in case
132+
there are tests that are a work in progress.
132133
"""
133-
return {
134-
# Needs group support in repl / The test is incorrect - see https://github.com/CHIP-Specifications/chip-test-plans/issues/2431 for details.
135-
"Test_TC_SC_5_2.yaml",
136-
"TestGroupMessaging.yaml", # Needs group support in repl
137-
}
134+
return set()
138135

139136

140137
def _AllYamlTests():

src/controller/python/chip/ChipDeviceCtrl.py

+28
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,34 @@ async def WriteAttribute(self, nodeid: int, attributes: typing.List[typing.Tuple
939939
future, eventLoop, device.deviceProxy, attrs, timedRequestTimeoutMs=timedRequestTimeoutMs, interactionTimeoutMs=interactionTimeoutMs, busyWaitMs=busyWaitMs).raise_on_error()
940940
return await future
941941

942+
def WriteGroupAttribute(self, groupid: int, attributes: typing.List[typing.Tuple[ClusterObjects.ClusterAttributeDescriptor, int]], busyWaitMs: typing.Union[None, int] = None):
943+
'''
944+
Write a list of attributes on a target group.
945+
946+
groupid: Group ID to send write attribute to.
947+
attributes: A list of tuples of type (cluster-object, data-version). The data-version can be omitted.
948+
949+
E.g
950+
(Clusters.UnitTesting.Attributes.XYZAttribute('hello'), 1) -- Group Write 'hello' with data version 1
951+
'''
952+
self.CheckIsActive()
953+
954+
attrs = []
955+
invalid_endpoint = 0xFFFF
956+
for v in attributes:
957+
if len(v) == 2:
958+
attrs.append(ClusterAttribute.AttributeWriteRequest(
959+
invalid_endpoint, v[0], v[1], 1, v[0].value))
960+
else:
961+
attrs.append(ClusterAttribute.AttributeWriteRequest(
962+
invalid_endpoint, v[0], 0, 0, v[0].value))
963+
964+
ClusterAttribute.WriteGroupAttributes(
965+
groupid, self.devCtrl, attrs, busyWaitMs=busyWaitMs).raise_on_error()
966+
967+
# An empty list is the expected return for sending group write attribute.
968+
return []
969+
942970
def _parseAttributePathTuple(self, pathTuple: typing.Union[
943971
None, # Empty tuple, all wildcard
944972
typing.Tuple[int], # Endpoint

src/controller/python/chip/clusters/Attribute.py

+27
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,32 @@ def WriteAttributes(future: Future, eventLoop, device, attributes: List[Attribut
949949
return res
950950

951951

952+
def WriteGroupAttributes(groupId: int, devCtrl: c_void_p, attributes: List[AttributeWriteRequest], busyWaitMs: Union[None, int] = None) -> PyChipError:
953+
handle = chip.native.GetLibraryHandle()
954+
955+
writeargs = []
956+
for attr in attributes:
957+
path = chip.interaction_model.AttributePathIBstruct.parse(
958+
b'\x00' * chip.interaction_model.AttributePathIBstruct.sizeof())
959+
path.EndpointId = attr.EndpointId
960+
path.ClusterId = attr.Attribute.cluster_id
961+
path.AttributeId = attr.Attribute.attribute_id
962+
path.DataVersion = attr.DataVersion
963+
path.HasDataVersion = attr.HasDataVersion
964+
path = chip.interaction_model.AttributePathIBstruct.build(path)
965+
tlv = attr.Attribute.ToTLV(None, attr.Data)
966+
writeargs.append(ctypes.c_char_p(path))
967+
writeargs.append(ctypes.c_char_p(bytes(tlv)))
968+
writeargs.append(ctypes.c_int(len(tlv)))
969+
970+
return builtins.chipStack.Call(
971+
lambda: handle.pychip_WriteClient_WriteGroupAttributes(
972+
ctypes.c_uint16(groupId), devCtrl,
973+
ctypes.c_uint16(0 if busyWaitMs is None else busyWaitMs),
974+
ctypes.c_size_t(len(attributes)), *writeargs)
975+
)
976+
977+
952978
# This struct matches the PyReadAttributeParams in attribute.cpp, for passing various params together.
953979
_ReadParams = construct.Struct(
954980
"MinInterval" / construct.Int32ul,
@@ -1081,6 +1107,7 @@ def Init():
10811107
setter = chip.native.NativeLibraryHandleMethodArguments(handle)
10821108

10831109
handle.pychip_WriteClient_WriteAttributes.restype = PyChipError
1110+
handle.pychip_WriteClient_WriteGroupAttributes.restype = PyChipError
10841111
setter.Set('pychip_WriteClient_InitCallbacks', None, [
10851112
_OnWriteResponseCallbackFunct, _OnWriteErrorCallbackFunct, _OnWriteDoneCallbackFunct])
10861113
handle.pychip_ReadClient_Read.restype = PyChipError

src/controller/python/chip/clusters/attribute.cpp

+61
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <app/InteractionModelEngine.h>
2727
#include <app/ReadClient.h>
2828
#include <app/WriteClient.h>
29+
#include <controller/CHIPDeviceController.h>
2930
#include <controller/python/chip/native/PyChipError.h>
3031
#include <lib/support/CodeUtils.h>
3132

@@ -247,6 +248,8 @@ struct __attribute__((packed)) PyReadAttributeParams
247248
// Encodes n attribute write requests, follows 3 * n arguments, in the (AttributeWritePath*=void *, uint8_t*, size_t) order.
248249
PyChipError pychip_WriteClient_WriteAttributes(void * appContext, DeviceProxy * device, uint16_t timedWriteTimeoutMs,
249250
uint16_t interactionTimeoutMs, uint16_t busyWaitMs, size_t n, ...);
251+
PyChipError pychip_WriteClient_WriteGroupAttributes(chip::GroupId groupId, chip::Controller::DeviceCommissioner * devCtrl,
252+
uint16_t busyWaitMs, size_t n, ...);
250253
PyChipError pychip_ReadClient_ReadAttributes(void * appContext, ReadClient ** pReadClient, ReadClientCallback ** pCallback,
251254
DeviceProxy * device, uint8_t * readParamsBuf, size_t n, size_t total, ...);
252255
}
@@ -380,6 +383,64 @@ PyChipError pychip_WriteClient_WriteAttributes(void * appContext, DeviceProxy *
380383
return ToPyChipError(err);
381384
}
382385

386+
PyChipError pychip_WriteClient_WriteGroupAttributes(chip::GroupId groupId, chip::Controller::DeviceCommissioner * devCtrl,
387+
uint16_t busyWaitMs, size_t n, ...)
388+
{
389+
CHIP_ERROR err = CHIP_NO_ERROR;
390+
391+
chip::Messaging::ExchangeManager * exchangeManager = chip::app::InteractionModelEngine::GetInstance()->GetExchangeManager();
392+
VerifyOrReturnError(exchangeManager != nullptr, ToPyChipError(CHIP_ERROR_INCORRECT_STATE));
393+
394+
std::unique_ptr<WriteClient> client = std::make_unique<WriteClient>(
395+
app::InteractionModelEngine::GetInstance()->GetExchangeManager(), nullptr /* callback */, Optional<uint16_t>::Missing());
396+
397+
va_list args;
398+
va_start(args, n);
399+
400+
{
401+
for (size_t i = 0; i < n; i++)
402+
{
403+
void * path = va_arg(args, void *);
404+
void * tlv = va_arg(args, void *);
405+
int length = va_arg(args, int);
406+
407+
python::AttributePath pathObj;
408+
memcpy(&pathObj, path, sizeof(python::AttributePath));
409+
uint8_t * tlvBuffer = reinterpret_cast<uint8_t *>(tlv);
410+
411+
TLV::TLVReader reader;
412+
reader.Init(tlvBuffer, static_cast<uint32_t>(length));
413+
reader.Next();
414+
Optional<DataVersion> dataVersion;
415+
if (pathObj.hasDataVersion == 1)
416+
{
417+
dataVersion.SetValue(pathObj.dataVersion);
418+
}
419+
// Using kInvalidEndpointId as that used when sending group write requests.
420+
SuccessOrExit(
421+
err = client->PutPreencodedAttribute(
422+
chip::app::ConcreteDataAttributePath(kInvalidEndpointId, pathObj.clusterId, pathObj.attributeId, dataVersion),
423+
reader));
424+
}
425+
}
426+
427+
{
428+
auto fabricIndex = devCtrl->GetFabricIndex();
429+
430+
chip::Transport::OutgoingGroupSession session(groupId, fabricIndex);
431+
SuccessOrExit(err = client->SendWriteRequest(chip::SessionHandle(session), System::Clock::kZero));
432+
}
433+
434+
if (busyWaitMs)
435+
{
436+
usleep(busyWaitMs * 1000);
437+
}
438+
439+
exit:
440+
va_end(args);
441+
return ToPyChipError(err);
442+
}
443+
383444
void pychip_ReadClient_Abort(ReadClient * apReadClient, ReadClientCallback * apCallback)
384445
{
385446
VerifyOrDie(apReadClient != nullptr);

src/controller/python/chip/yaml/runner.py

+18-4
Original file line numberDiff line numberDiff line change
@@ -547,8 +547,13 @@ def __init__(self, test_step, cluster: str, context: _ExecutionContext):
547547
self._endpoint = test_step.endpoint
548548
self._interation_timeout_ms = test_step.timed_interaction_timeout_ms
549549
self._node_id = test_step.node_id
550+
self._group_id = test_step.group_id
550551
self._request_object = None
551552

553+
if self._node_id is None and self._group_id is None:
554+
raise UnexpectedParsingError(
555+
'Both node_id and group_id are None, at least one needs to be provided')
556+
552557
attribute = context.data_model_lookup.get_attribute(
553558
self._cluster, self._attribute_name)
554559
if attribute is None:
@@ -575,12 +580,21 @@ def __init__(self, test_step, cluster: str, context: _ExecutionContext):
575580

576581
def run_action(self, dev_ctrl: ChipDeviceController) -> _ActionResult:
577582
try:
578-
resp = asyncio.run(
579-
dev_ctrl.WriteAttribute(self._node_id, [(self._endpoint, self._request_object)],
580-
timedRequestTimeoutMs=self._interation_timeout_ms,
581-
busyWaitMs=self._busy_wait_ms))
583+
if self._group_id:
584+
resp = dev_ctrl.WriteGroupAttribute(self._group_id, [(self._request_object,)],
585+
busyWaitMs=self._busy_wait_ms)
586+
else:
587+
resp = asyncio.run(
588+
dev_ctrl.WriteAttribute(self._node_id, [(self._endpoint, self._request_object)],
589+
timedRequestTimeoutMs=self._interation_timeout_ms,
590+
busyWaitMs=self._busy_wait_ms))
582591
except chip.interaction_model.InteractionModelError as error:
583592
return _ActionResult(status=_ActionStatus.ERROR, response=error)
593+
594+
# Group writes are expected to have no response upon success.
595+
if self._group_id and len(resp) == 0:
596+
return _ActionResult(status=_ActionStatus.SUCCESS, response=None)
597+
584598
if len(resp) == 1 and isinstance(resp[0], AttributeStatus):
585599
if resp[0].Status == chip.interaction_model.Status.Success:
586600
return _ActionResult(status=_ActionStatus.SUCCESS, response=None)

0 commit comments

Comments
 (0)