diff --git a/src/controller/python/chip/clusters/Attribute.py b/src/controller/python/chip/clusters/Attribute.py index 25691bb826cf55..96a66f5706f174 100644 --- a/src/controller/python/chip/clusters/Attribute.py +++ b/src/controller/python/chip/clusters/Attribute.py @@ -24,7 +24,7 @@ import logging import sys from asyncio.futures import Future -from ctypes import CFUNCTYPE, c_size_t, c_uint8, c_uint16, c_uint32, c_uint64, c_void_p, py_object +from ctypes import CFUNCTYPE, POINTER, c_size_t, c_uint8, c_uint16, c_uint32, c_uint64, c_void_p, cast, py_object from dataclasses import dataclass, field from enum import Enum, unique from typing import Any, Callable, Dict, List, Optional, Tuple, Union @@ -34,6 +34,7 @@ import chip.interaction_model import chip.tlv import construct +from chip.interaction_model import PyWriteAttributeData from chip.native import ErrorSDKPart, PyChipError from rich.pretty import pprint @@ -955,22 +956,22 @@ def WriteAttributes(future: Future, eventLoop, device, interactionTimeoutMs: Union[None, int] = None, busyWaitMs: Union[None, int] = None) -> PyChipError: handle = chip.native.GetLibraryHandle() - writeargs = [] - for attr in attributes: + numberOfAttributes = len(attributes) + pyWriteAttributesArrayType = PyWriteAttributeData * numberOfAttributes + pyWriteAttributes = pyWriteAttributesArrayType() + for idx, attr in enumerate(attributes): if attr.Attribute.must_use_timed_write and timedRequestTimeoutMs is None or timedRequestTimeoutMs == 0: raise chip.interaction_model.InteractionModelError(chip.interaction_model.Status.NeedsTimedInteraction) - path = chip.interaction_model.AttributePathIBstruct.parse( - b'\x00' * chip.interaction_model.AttributePathIBstruct.sizeof()) - path.EndpointId = attr.EndpointId - path.ClusterId = attr.Attribute.cluster_id - path.AttributeId = attr.Attribute.attribute_id - path.DataVersion = attr.DataVersion - path.HasDataVersion = attr.HasDataVersion - path = chip.interaction_model.AttributePathIBstruct.build(path) + tlv = attr.Attribute.ToTLV(None, attr.Data) - writeargs.append(ctypes.c_char_p(path)) - writeargs.append(ctypes.c_char_p(bytes(tlv))) - writeargs.append(ctypes.c_int(len(tlv))) + + pyWriteAttributes[idx].attributePath.endpointId = c_uint16(attr.EndpointId) + pyWriteAttributes[idx].attributePath.clusterId = c_uint32(attr.Attribute.cluster_id) + pyWriteAttributes[idx].attributePath.attributeId = c_uint32(attr.Attribute.attribute_id) + pyWriteAttributes[idx].attributePath.dataVersion = c_uint32(attr.DataVersion) + pyWriteAttributes[idx].attributePath.hasDataVersion = c_uint8(attr.HasDataVersion) + pyWriteAttributes[idx].tlvData = cast(ctypes.c_char_p(bytes(tlv)), c_void_p) + pyWriteAttributes[idx].tlvLength = c_size_t(len(tlv)) transaction = AsyncWriteTransaction(future, eventLoop) ctypes.pythonapi.Py_IncRef(ctypes.py_object(transaction)) @@ -980,7 +981,7 @@ def WriteAttributes(future: Future, eventLoop, device, ctypes.c_size_t(0 if timedRequestTimeoutMs is None else timedRequestTimeoutMs), ctypes.c_size_t(0 if interactionTimeoutMs is None else interactionTimeoutMs), ctypes.c_size_t(0 if busyWaitMs is None else busyWaitMs), - ctypes.c_size_t(len(attributes)), *writeargs) + pyWriteAttributes, ctypes.c_size_t(numberOfAttributes)) ) if not res.is_success: ctypes.pythonapi.Py_DecRef(ctypes.py_object(transaction)) @@ -990,26 +991,26 @@ def WriteAttributes(future: Future, eventLoop, device, def WriteGroupAttributes(groupId: int, devCtrl: c_void_p, attributes: List[AttributeWriteRequest], busyWaitMs: Union[None, int] = None) -> PyChipError: handle = chip.native.GetLibraryHandle() - writeargs = [] - for attr in attributes: - path = chip.interaction_model.AttributePathIBstruct.parse( - b'\x00' * chip.interaction_model.AttributePathIBstruct.sizeof()) - path.EndpointId = attr.EndpointId - path.ClusterId = attr.Attribute.cluster_id - path.AttributeId = attr.Attribute.attribute_id - path.DataVersion = attr.DataVersion - path.HasDataVersion = attr.HasDataVersion - path = chip.interaction_model.AttributePathIBstruct.build(path) + numberOfAttributes = len(attributes) + pyWriteAttributesArrayType = PyWriteAttributeData * numberOfAttributes + pyWriteAttributes = pyWriteAttributesArrayType() + for idx, attr in enumerate(attributes): + tlv = attr.Attribute.ToTLV(None, attr.Data) - writeargs.append(ctypes.c_char_p(path)) - writeargs.append(ctypes.c_char_p(bytes(tlv))) - writeargs.append(ctypes.c_int(len(tlv))) + + pyWriteAttributes[idx].attributePath.endpointId = c_uint16(attr.EndpointId) + pyWriteAttributes[idx].attributePath.clusterId = c_uint32(attr.Attribute.cluster_id) + pyWriteAttributes[idx].attributePath.attributeId = c_uint32(attr.Attribute.attribute_id) + pyWriteAttributes[idx].attributePath.dataVersion = c_uint32(attr.DataVersion) + pyWriteAttributes[idx].attributePath.hasDataVersion = c_uint8(attr.HasDataVersion) + pyWriteAttributes[idx].tlvData = cast(ctypes.c_char_p(bytes(tlv)), c_void_p) + pyWriteAttributes[idx].tlvLength = c_size_t(len(tlv)) return builtins.chipStack.Call( lambda: handle.pychip_WriteClient_WriteGroupAttributes( ctypes.c_size_t(groupId), devCtrl, ctypes.c_size_t(0 if busyWaitMs is None else busyWaitMs), - ctypes.c_size_t(len(attributes)), *writeargs) + pyWriteAttributes, ctypes.c_size_t(numberOfAttributes)) ) @@ -1037,10 +1038,12 @@ def Read(future: Future, eventLoop, device, devCtrl, transaction = AsyncReadTransaction( future, eventLoop, devCtrl, returnClusterObject) - readargs = [] - + attributePathsForCffi = None if attributes is not None: - for attr in attributes: + numberOfAttributePaths = len(attributes) + attributePathsForCffiArrayType = c_void_p * numberOfAttributePaths + attributePathsForCffi = attributePathsForCffiArrayType() + for idx, attr in enumerate(attributes): path = chip.interaction_model.AttributePathIBstruct.parse( b'\xff' * chip.interaction_model.AttributePathIBstruct.sizeof()) if attr.EndpointId is not None: @@ -1050,10 +1053,14 @@ def Read(future: Future, eventLoop, device, devCtrl, if attr.AttributeId is not None: path.AttributeId = attr.AttributeId path = chip.interaction_model.AttributePathIBstruct.build(path) - readargs.append(ctypes.c_char_p(path)) + attributePathsForCffi[idx] = cast(ctypes.c_char_p(path), c_void_p) + dataVersionFiltersForCffi = None if dataVersionFilters is not None: - for f in dataVersionFilters: + numberOfDataVersionFilters = len(dataVersionFilters) + dataVersionFiltersForCffiArrayType = c_void_p * numberOfDataVersionFilters + dataVersionFiltersForCffi = dataVersionFiltersForCffiArrayType() + for idx, f in enumerate(dataVersionFilters): filter = chip.interaction_model.DataVersionFilterIBstruct.parse( b'\xff' * chip.interaction_model.DataVersionFilterIBstruct.sizeof()) if f.EndpointId is not None: @@ -1073,10 +1080,14 @@ def Read(future: Future, eventLoop, device, devCtrl, "DataVersionFilter must provide DataVersion.") filter = chip.interaction_model.DataVersionFilterIBstruct.build( filter) - readargs.append(ctypes.c_char_p(filter)) + dataVersionFiltersForCffi[idx] = cast(ctypes.c_char_p(filter), c_void_p) + eventPathsForCffi = None if events is not None: - for event in events: + numberOfEvents = len(events) + eventPathsForCffiArrayType = c_void_p * numberOfEvents + eventPathsForCffi = eventPathsForCffiArrayType() + for idx, event in enumerate(events): path = chip.interaction_model.EventPathIBstruct.parse( b'\xff' * chip.interaction_model.EventPathIBstruct.sizeof()) if event.EndpointId is not None: @@ -1090,7 +1101,7 @@ def Read(future: Future, eventLoop, device, devCtrl, else: path.Urgent = 0 path = chip.interaction_model.EventPathIBstruct.build(path) - readargs.append(ctypes.c_char_p(path)) + eventPathsForCffi[idx] = cast(ctypes.c_char_p(path), c_void_p) readClientObj = ctypes.POINTER(c_void_p)() readCallbackObj = ctypes.POINTER(c_void_p)() @@ -1116,12 +1127,14 @@ def Read(future: Future, eventLoop, device, devCtrl, ctypes.byref(readCallbackObj), device, ctypes.c_char_p(params), + attributePathsForCffi, ctypes.c_size_t(0 if attributes is None else len(attributes)), + dataVersionFiltersForCffi, ctypes.c_size_t( 0 if dataVersionFilters is None else len(dataVersionFilters)), + eventPathsForCffi, ctypes.c_size_t(0 if events is None else len(events)), - eventNumberFilterPtr, - *readargs)) + eventNumberFilterPtr)) transaction.SetClientObjPointers(readClientObj, readCallbackObj) @@ -1168,8 +1181,10 @@ def Init(): # attribute information we want written using a vector. This possibility was not implemented at the # time where simply specified the argtypes, because of time constraints. This solution was quicker # to fix the crash on ARM64 Apple platforms without a refactor. - handle.pychip_WriteClient_WriteAttributes.argtypes = [py_object, c_void_p, c_size_t, c_size_t, c_size_t, c_size_t] - handle.pychip_WriteClient_WriteGroupAttributes.argtypes = [c_size_t, c_void_p, c_size_t, c_size_t] + handle.pychip_WriteClient_WriteAttributes.argtypes = [py_object, c_void_p, + c_size_t, c_size_t, c_size_t, POINTER(PyWriteAttributeData), c_size_t] + handle.pychip_WriteClient_WriteGroupAttributes.argtypes = [ + c_size_t, c_void_p, c_size_t, POINTER(PyWriteAttributeData), c_size_t] setter.Set('pychip_WriteClient_InitCallbacks', None, [ _OnWriteResponseCallbackFunct, _OnWriteErrorCallbackFunct, _OnWriteDoneCallbackFunct]) diff --git a/src/controller/python/chip/clusters/Command.py b/src/controller/python/chip/clusters/Command.py index d3dcc5bba913ff..6e25a76d50230f 100644 --- a/src/controller/python/chip/clusters/Command.py +++ b/src/controller/python/chip/clusters/Command.py @@ -21,12 +21,13 @@ import logging import sys from asyncio.futures import Future -from ctypes import CFUNCTYPE, c_bool, c_char_p, c_size_t, c_uint8, c_uint16, c_uint32, c_void_p, py_object +from ctypes import CFUNCTYPE, POINTER, c_bool, c_char_p, c_size_t, c_uint8, c_uint16, c_uint32, c_void_p, cast, py_object from dataclasses import dataclass from typing import List, Optional, Type, Union import chip.exceptions import chip.interaction_model +from chip.interaction_model import PyInvokeRequestData from chip.native import PyChipError from .ClusterObjects import ClusterCommand @@ -313,8 +314,10 @@ def SendBatchCommands(future: Future, eventLoop, device, commands: List[InvokeRe handle = chip.native.GetLibraryHandle() responseTypes = [] - commandargs = [] - for command in commands: + numberOfCommands = len(commands) + pyBatchCommandsDataArrayType = PyInvokeRequestData * numberOfCommands + pyBatchCommandsData = pyBatchCommandsDataArrayType() + for idx, command in enumerate(commands): clusterCommand = command.Command responseType = command.ResponseType if (responseType is not None) and (not issubclass(responseType, ClusterCommand)): @@ -322,15 +325,13 @@ def SendBatchCommands(future: Future, eventLoop, device, commands: List[InvokeRe if clusterCommand.must_use_timed_invoke and timedRequestTimeoutMs is None or timedRequestTimeoutMs == 0: raise chip.interaction_model.InteractionModelError(chip.interaction_model.Status.NeedsTimedInteraction) - commandPath = chip.interaction_model.CommandPathIBStruct.build({ - "EndpointId": command.EndpointId, - "ClusterId": clusterCommand.cluster_id, - "CommandId": clusterCommand.command_id}) payloadTLV = clusterCommand.ToTLV() - commandargs.append(c_char_p(commandPath)) - commandargs.append(c_char_p(bytes(payloadTLV))) - commandargs.append(c_size_t(len(payloadTLV))) + pyBatchCommandsData[idx].commandPath.endpointId = c_uint16(command.EndpointId) + pyBatchCommandsData[idx].commandPath.clusterId = c_uint32(clusterCommand.cluster_id) + pyBatchCommandsData[idx].commandPath.commandId = c_uint32(clusterCommand.command_id) + pyBatchCommandsData[idx].tlvData = cast(c_char_p(bytes(payloadTLV)), c_void_p) + pyBatchCommandsData[idx].tlvLength = c_size_t(len(payloadTLV)) responseTypes.append(responseType) @@ -344,7 +345,7 @@ def SendBatchCommands(future: Future, eventLoop, device, commands: List[InvokeRe c_uint16(0 if interactionTimeoutMs is None else interactionTimeoutMs), c_uint16(0 if busyWaitMs is None else busyWaitMs), c_bool(False if suppressResponse is None else suppressResponse), - c_size_t(len(commands)), *commandargs) + pyBatchCommandsData, c_size_t(numberOfCommands)) ) @@ -375,7 +376,7 @@ def Init(): setter.Set('pychip_CommandSender_SendCommand', PyChipError, [py_object, c_void_p, c_uint16, c_uint32, c_uint32, c_char_p, c_size_t, c_uint16, c_bool]) setter.Set('pychip_CommandSender_SendBatchCommands', - PyChipError, [py_object, c_void_p, c_uint16, c_uint16, c_uint16, c_bool, c_size_t]) + PyChipError, [py_object, c_void_p, c_uint16, c_uint16, c_uint16, c_bool, POINTER(PyInvokeRequestData), c_size_t]) setter.Set('pychip_CommandSender_TestOnlySendCommandTimedRequestNoTimedInvoke', PyChipError, [py_object, c_void_p, c_uint32, c_uint32, c_char_p, c_size_t, c_uint16, c_bool]) setter.Set('pychip_CommandSender_SendGroupCommand', diff --git a/src/controller/python/chip/clusters/attribute.cpp b/src/controller/python/chip/clusters/attribute.cpp index d85d132b36bf8a..8abaaab18e1ed9 100644 --- a/src/controller/python/chip/clusters/attribute.cpp +++ b/src/controller/python/chip/clusters/attribute.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -256,13 +257,14 @@ struct __attribute__((packed)) PyReadAttributeParams bool autoResubscribe; }; -// Encodes n attribute write requests, follows 3 * n arguments, in the (AttributeWritePath*=void *, uint8_t*, size_t) order. PyChipError pychip_WriteClient_WriteAttributes(void * appContext, DeviceProxy * device, size_t timedWriteTimeoutMsSizeT, - size_t interactionTimeoutMsSizeT, size_t busyWaitMsSizeT, size_t n, ...); + size_t interactionTimeoutMsSizeT, size_t busyWaitMsSizeT, + chip::python::PyWriteAttributeData * writeAttributesData, + size_t attributeDataLength); PyChipError pychip_WriteClient_WriteGroupAttributes(size_t groupIdSizeT, chip::Controller::DeviceCommissioner * devCtrl, - size_t busyWaitMsSizeT, size_t n, ...); -PyChipError pychip_ReadClient_ReadAttributes(void * appContext, ReadClient ** pReadClient, ReadClientCallback ** pCallback, - DeviceProxy * device, uint8_t * readParamsBuf, size_t n, size_t total, ...); + size_t busyWaitMsSizeT, + chip::python::PyWriteAttributeData * writeAttributesData, + size_t attributeDataLength); } using OnWriteResponseCallback = void (*)(PyObject * appContext, chip::EndpointId endpointId, chip::ClusterId clusterId, @@ -337,7 +339,8 @@ void pychip_ReadClient_InitCallbacks(OnReadAttributeDataCallback onReadAttribute } PyChipError pychip_WriteClient_WriteAttributes(void * appContext, DeviceProxy * device, size_t timedWriteTimeoutMsSizeT, - size_t interactionTimeoutMsSizeT, size_t busyWaitMsSizeT, size_t n, ...) + size_t interactionTimeoutMsSizeT, size_t busyWaitMsSizeT, + python::PyWriteAttributeData * writeAttributesData, size_t attributeDataLength) { CHIP_ERROR err = CHIP_NO_ERROR; @@ -352,35 +355,27 @@ PyChipError pychip_WriteClient_WriteAttributes(void * appContext, DeviceProxy * app::InteractionModelEngine::GetInstance()->GetExchangeManager(), callback->GetChunkedCallback(), timedWriteTimeoutMs != 0 ? Optional(timedWriteTimeoutMs) : Optional::Missing()); - va_list args; - va_start(args, n); - VerifyOrExit(device != nullptr && device->GetSecureSession().HasValue(), err = CHIP_ERROR_MISSING_SECURE_SESSION); + for (size_t i = 0; i < attributeDataLength; i++) { - for (size_t i = 0; i < n; i++) + python::PyAttributePath path = writeAttributesData[i].attributePath; + void * tlv = writeAttributesData[i].tlvData; + size_t length = writeAttributesData[i].tlvLength; + + uint8_t * tlvBuffer = reinterpret_cast(tlv); + + TLV::TLVReader reader; + reader.Init(tlvBuffer, static_cast(length)); + reader.Next(); + Optional dataVersion; + if (path.hasDataVersion == 1) { - void * path = va_arg(args, void *); - void * tlv = va_arg(args, void *); - int length = va_arg(args, int); - - python::AttributePath pathObj; - memcpy(&pathObj, path, sizeof(python::AttributePath)); - uint8_t * tlvBuffer = reinterpret_cast(tlv); - - TLV::TLVReader reader; - reader.Init(tlvBuffer, static_cast(length)); - reader.Next(); - Optional dataVersion; - if (pathObj.hasDataVersion == 1) - { - dataVersion.SetValue(pathObj.dataVersion); - } - SuccessOrExit( - err = client->PutPreencodedAttribute( - chip::app::ConcreteDataAttributePath(pathObj.endpointId, pathObj.clusterId, pathObj.attributeId, dataVersion), - reader)); + dataVersion.SetValue(path.dataVersion); } + SuccessOrExit( + err = client->PutPreencodedAttribute( + chip::app::ConcreteDataAttributePath(path.endpointId, path.clusterId, path.attributeId, dataVersion), reader)); } SuccessOrExit(err = client->SendWriteRequest(device->GetSecureSession().Value(), @@ -396,12 +391,12 @@ PyChipError pychip_WriteClient_WriteAttributes(void * appContext, DeviceProxy * } exit: - va_end(args); return ToPyChipError(err); } PyChipError pychip_WriteClient_WriteGroupAttributes(size_t groupIdSizeT, chip::Controller::DeviceCommissioner * devCtrl, - size_t busyWaitMsSizeT, size_t n, ...) + size_t busyWaitMsSizeT, python::PyWriteAttributeData * writeAttributesData, + size_t attributeDataLength) { CHIP_ERROR err = CHIP_NO_ERROR; @@ -417,34 +412,26 @@ PyChipError pychip_WriteClient_WriteGroupAttributes(size_t groupIdSizeT, chip::C std::unique_ptr client = std::make_unique( app::InteractionModelEngine::GetInstance()->GetExchangeManager(), nullptr /* callback */, Optional::Missing()); - va_list args; - va_start(args, n); - + for (size_t i = 0; i < attributeDataLength; i++) { - for (size_t i = 0; i < n; i++) + python::PyAttributePath path = writeAttributesData[i].attributePath; + void * tlv = writeAttributesData[i].tlvData; + size_t length = writeAttributesData[i].tlvLength; + + uint8_t * tlvBuffer = reinterpret_cast(tlv); + + TLV::TLVReader reader; + reader.Init(tlvBuffer, static_cast(length)); + reader.Next(); + Optional dataVersion; + if (path.hasDataVersion == 1) { - void * path = va_arg(args, void *); - void * tlv = va_arg(args, void *); - int length = va_arg(args, int); - - python::AttributePath pathObj; - memcpy(&pathObj, path, sizeof(python::AttributePath)); - uint8_t * tlvBuffer = reinterpret_cast(tlv); - - TLV::TLVReader reader; - reader.Init(tlvBuffer, static_cast(length)); - reader.Next(); - Optional dataVersion; - if (pathObj.hasDataVersion == 1) - { - dataVersion.SetValue(pathObj.dataVersion); - } - // Using kInvalidEndpointId as that used when sending group write requests. - SuccessOrExit( - err = client->PutPreencodedAttribute( - chip::app::ConcreteDataAttributePath(kInvalidEndpointId, pathObj.clusterId, pathObj.attributeId, dataVersion), - reader)); + dataVersion.SetValue(path.dataVersion); } + // Using kInvalidEndpointId as that used when sending group write requests. + SuccessOrExit( + err = client->PutPreencodedAttribute( + chip::app::ConcreteDataAttributePath(kInvalidEndpointId, path.clusterId, path.attributeId, dataVersion), reader)); } { @@ -460,7 +447,6 @@ PyChipError pychip_WriteClient_WriteGroupAttributes(size_t groupIdSizeT, chip::C } exit: - va_end(args); return ToPyChipError(err); } @@ -487,8 +473,9 @@ PyChipError pychip_ReadClient_GetReportingIntervals(ReadClient * pReadClient, ui } PyChipError pychip_ReadClient_Read(void * appContext, ReadClient ** pReadClient, ReadClientCallback ** pCallback, - DeviceProxy * device, uint8_t * readParamsBuf, size_t numAttributePaths, - size_t numDataversionFilters, size_t numEventPaths, uint64_t * eventNumberFilter, ...) + DeviceProxy * device, uint8_t * readParamsBuf, void ** attributePathsFromPython, + size_t numAttributePaths, void ** dataversionFiltersFromPython, size_t numDataversionFilters, + void ** eventPathsFromPython, size_t numEventPaths, uint64_t * eventNumberFilter) { CHIP_ERROR err = CHIP_NO_ERROR; PyReadAttributeParams pyParams = {}; @@ -497,9 +484,6 @@ PyChipError pychip_ReadClient_Read(void * appContext, ReadClient ** pReadClient, std::unique_ptr callback = std::make_unique(appContext); - va_list args; - va_start(args, eventNumberFilter); - std::unique_ptr attributePaths(new AttributePathParams[numAttributePaths]); std::unique_ptr dataVersionFilters(new chip::app::DataVersionFilter[numDataversionFilters]); std::unique_ptr eventPaths(new EventPathParams[numEventPaths]); @@ -507,7 +491,7 @@ PyChipError pychip_ReadClient_Read(void * appContext, ReadClient ** pReadClient, for (size_t i = 0; i < numAttributePaths; i++) { - void * path = va_arg(args, void *); + void * path = attributePathsFromPython[i]; python::AttributePath pathObj; memcpy(&pathObj, path, sizeof(python::AttributePath)); @@ -517,7 +501,7 @@ PyChipError pychip_ReadClient_Read(void * appContext, ReadClient ** pReadClient, for (size_t i = 0; i < numDataversionFilters; i++) { - void * filter = va_arg(args, void *); + void * filter = dataversionFiltersFromPython[i]; python::DataVersionFilter filterObj; memcpy(&filterObj, filter, sizeof(python::DataVersionFilter)); @@ -527,7 +511,7 @@ PyChipError pychip_ReadClient_Read(void * appContext, ReadClient ** pReadClient, for (size_t i = 0; i < numEventPaths; i++) { - void * path = va_arg(args, void *); + void * path = eventPathsFromPython[i]; python::EventPath pathObj; memcpy(&pathObj, path, sizeof(python::EventPath)); @@ -599,7 +583,6 @@ PyChipError pychip_ReadClient_Read(void * appContext, ReadClient ** pReadClient, callback.release(); exit: - va_end(args); return ToPyChipError(err); } } diff --git a/src/controller/python/chip/clusters/command.cpp b/src/controller/python/chip/clusters/command.cpp index c920a82c53fb54..7195cefe16eba1 100644 --- a/src/controller/python/chip/clusters/command.cpp +++ b/src/controller/python/chip/clusters/command.cpp @@ -41,7 +41,7 @@ PyChipError pychip_CommandSender_SendCommand(void * appContext, DeviceProxy * de PyChipError pychip_CommandSender_SendBatchCommands(void * appContext, DeviceProxy * device, uint16_t timedRequestTimeoutMs, uint16_t interactionTimeoutMs, uint16_t busyWaitMs, bool suppressResponse, - size_t n, ...); + chip::python::PyInvokeRequestData * batchCommandData, size_t length); PyChipError pychip_CommandSender_TestOnlySendCommandTimedRequestNoTimedInvoke( void * appContext, DeviceProxy * device, chip::EndpointId endpointId, chip::ClusterId clusterId, chip::CommandId commandId, @@ -68,13 +68,6 @@ OnCommandSenderResponseCallback gOnCommandSenderResponseCallback = nullptr; OnCommandSenderErrorCallback gOnCommandSenderErrorCallback = nullptr; OnCommandSenderDoneCallback gOnCommandSenderDoneCallback = nullptr; -struct __attribute__((packed)) CommandPath -{ - chip::EndpointId endpointId; - chip::ClusterId clusterId; - chip::CommandId commandId; -}; - class CommandSenderCallback : public CommandSender::ExtendableCallback { public: @@ -256,7 +249,7 @@ PyChipError pychip_CommandSender_SendCommand(void * appContext, DeviceProxy * de PyChipError pychip_CommandSender_SendBatchCommands(void * appContext, DeviceProxy * device, uint16_t timedRequestTimeoutMs, uint16_t interactionTimeoutMs, uint16_t busyWaitMs, bool suppressResponse, - size_t n, ...) + python::PyInvokeRequestData * batchCommandData, size_t length) { CHIP_ERROR err = CHIP_NO_ERROR; @@ -279,47 +272,41 @@ PyChipError pychip_CommandSender_SendBatchCommands(void * appContext, DeviceProx std::make_unique(callback.get(), device->GetExchangeManager(), /* is timed request */ timedRequestTimeoutMs != 0, suppressResponse); - // TODO(#30986): Move away from passing these command through variadic arguments. - va_list args; - va_start(args, n); - SuccessOrExit(err = sender->SetCommandSenderConfig(config)); + for (size_t i = 0; i < length; i++) { - for (size_t i = 0; i < n; i++) - { - void * commandPath = va_arg(args, void *); - void * tlv = va_arg(args, void *); - int length = va_arg(args, int); + chip::EndpointId endpointId = batchCommandData[i].commandPath.endpointId; + chip::ClusterId clusterId = batchCommandData[i].commandPath.clusterId; + chip::CommandId commandId = batchCommandData[i].commandPath.commandId; + void * tlv = batchCommandData[i].tlvData; + size_t tlvLength = batchCommandData[i].tlvLength; - python::CommandPath invokeRequestInfoObj; - memcpy(&invokeRequestInfoObj, commandPath, sizeof(python::CommandPath)); - const uint8_t * tlvBuffer = reinterpret_cast(tlv); + const uint8_t * tlvBuffer = reinterpret_cast(tlv); - app::CommandPathParams cmdParams = { invokeRequestInfoObj.endpointId, /* group id */ 0, invokeRequestInfoObj.clusterId, - invokeRequestInfoObj.commandId, (app::CommandPathFlags::kEndpointIdValid) }; + app::CommandPathParams cmdParams = { endpointId, /* group id */ 0, clusterId, commandId, + (app::CommandPathFlags::kEndpointIdValid) }; - CommandSender::AdditionalCommandParameters additionalParams; + CommandSender::AdditionalCommandParameters additionalParams; - SuccessOrExit(err = sender->PrepareCommand(cmdParams, additionalParams)); - { - auto writer = sender->GetCommandDataIBTLVWriter(); - VerifyOrExit(writer != nullptr, err = CHIP_ERROR_INCORRECT_STATE); - TLV::TLVReader reader; - reader.Init(tlvBuffer, static_cast(length)); - reader.Next(); - SuccessOrExit(err = writer->CopyContainer(TLV::ContextTag(CommandDataIB::Tag::kFields), reader)); - } + SuccessOrExit(err = sender->PrepareCommand(cmdParams, additionalParams)); + { + auto writer = sender->GetCommandDataIBTLVWriter(); + VerifyOrExit(writer != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + TLV::TLVReader reader; + reader.Init(tlvBuffer, static_cast(tlvLength)); + reader.Next(); + SuccessOrExit(err = writer->CopyContainer(TLV::ContextTag(CommandDataIB::Tag::kFields), reader)); + } - SuccessOrExit(err = sender->FinishCommand(timedRequestTimeoutMs != 0 ? Optional(timedRequestTimeoutMs) - : Optional::Missing(), - additionalParams)); + SuccessOrExit(err = sender->FinishCommand(timedRequestTimeoutMs != 0 ? Optional(timedRequestTimeoutMs) + : Optional::Missing(), + additionalParams)); - // CommandSender provides us with the CommandReference for this associated command. In order to match responses - // we have to add CommandRef to index lookup. - VerifyOrExit(additionalParams.commandRef.HasValue(), err = CHIP_ERROR_INVALID_ARGUMENT); - SuccessOrExit(err = callback->AddCommandRefToIndexLookup(additionalParams.commandRef.Value(), i)); - } + // CommandSender provides us with the CommandReference for this associated command. In order to match responses + // we have to add CommandRef to index lookup. + VerifyOrExit(additionalParams.commandRef.HasValue(), err = CHIP_ERROR_INVALID_ARGUMENT); + SuccessOrExit(err = callback->AddCommandRefToIndexLookup(additionalParams.commandRef.Value(), i)); } SuccessOrExit(err = sender->SendCommandRequest(device->GetSecureSession().Value(), @@ -338,7 +325,6 @@ PyChipError pychip_CommandSender_SendBatchCommands(void * appContext, DeviceProx } exit: - va_end(args); return ToPyChipError(err); } diff --git a/src/controller/python/chip/interaction_model/Delegate.h b/src/controller/python/chip/interaction_model/Delegate.h index c92d0d042459c7..f11f4931fc4c1d 100644 --- a/src/controller/python/chip/interaction_model/Delegate.h +++ b/src/controller/python/chip/interaction_model/Delegate.h @@ -23,8 +23,45 @@ namespace chip { namespace python { + static constexpr ClusterStatus kUndefinedClusterStatus = 0xFF; -} + +// This needs to match the python definition that uses the same name. +struct PyCommandPath +{ + chip::EndpointId endpointId; + chip::ClusterId clusterId; + chip::CommandId commandId; +}; + +// This needs to match the python definition that uses the same name. +struct PyInvokeRequestData +{ + PyCommandPath commandPath; + void * tlvData; + size_t tlvLength; +}; + +// This needs to match the python definition that uses the same name. +struct PyAttributePath +{ + chip::EndpointId endpointId; + chip::ClusterId clusterId; + chip::AttributeId attributeId; + chip::DataVersion dataVersion; + uint8_t hasDataVersion; +}; + +// This needs to match the python definition that uses the same name. +struct PyWriteAttributeData +{ + PyAttributePath attributePath; + void * tlvData; + size_t tlvLength; +}; + +} // namespace python + namespace Controller { // The command status will be used for python script. diff --git a/src/controller/python/chip/interaction_model/__init__.py b/src/controller/python/chip/interaction_model/__init__.py index 97c185bdcd96e5..78568563a73475 100644 --- a/src/controller/python/chip/interaction_model/__init__.py +++ b/src/controller/python/chip/interaction_model/__init__.py @@ -26,12 +26,12 @@ from chip.exceptions import ChipStackException -from .delegate import (AttributePath, AttributePathIBstruct, CommandPathIBStruct, DataVersionFilterIBstruct, EventPath, - EventPathIBstruct, SessionParameters, SessionParametersStruct) +from .delegate import (AttributePath, AttributePathIBstruct, DataVersionFilterIBstruct, EventPath, EventPathIBstruct, + PyInvokeRequestData, PyWriteAttributeData, SessionParameters, SessionParametersStruct) -__all__ = ["AttributePath", "AttributePathIBstruct", "CommandPathIBStruct", - "DataVersionFilterIBstruct", "EventPath", "EventPathIBstruct", - "InteractionModelError", "SessionParameters", "SessionParametersStruct", "Status"] +__all__ = ["AttributePath", "AttributePathIBstruct", "DataVersionFilterIBstruct", + "EventPath", "EventPathIBstruct", "InteractionModelError", "PyInvokeRequestData", + "PyWriteAttributeData", "SessionParameters", "SessionParametersStruct", "Status"] # defined src/controller/python/chip/interaction_model/Delegate.h diff --git a/src/controller/python/chip/interaction_model/delegate.py b/src/controller/python/chip/interaction_model/delegate.py index 6ce577b94580de..a9a9c04c4365a9 100644 --- a/src/controller/python/chip/interaction_model/delegate.py +++ b/src/controller/python/chip/interaction_model/delegate.py @@ -46,12 +46,6 @@ "AttributeId" / Int32ul, ) -CommandPathIBStruct = Struct( - "EndpointId" / Int16ul, - "ClusterId" / Int32ul, - "CommandId" / Int32ul, -) - # AttributePath should not contain padding AttributePathIBstruct = Struct( "EndpointId" / Int16ul, @@ -134,6 +128,77 @@ class SessionParameters: maxPathsPerInvoke: int +class PyCommandPath(ctypes.Structure): + ''' InvokeRequest Path struct that has c++ counterpart for CFFI. + + We are using the following struct for passing the information of InvokeRequest between Python and C++: + + ```c + struct PyCommandPath + { + chip::EndpointId endpointId; + chip::ClusterId clusterId; + chip::CommandId commandId; + }; + ``` + ''' + _fields_ = [('endpointId', ctypes.c_uint16), ('clusterId', ctypes.c_uint32), ('commandId', ctypes.c_uint32)] + + +class PyInvokeRequestData(ctypes.Structure): + ''' InvokeRequest struct that has c++ counterpart for CFFI. + + We are using the following struct for passing the information of InvokeRequest between Python and C++: + + ```c + struct PyInvokeRequestData + { + PyCommandPath commandPath; + void * tlvData; + size_t tlvLength; + }; + ``` + ''' + _fields_ = [('commandPath', PyCommandPath), ('tlvData', ctypes.c_void_p), ('tlvLength', ctypes.c_size_t)] + + +class PyAttributePath(ctypes.Structure): + ''' Attributed Path struct that has c++ counterpart for CFFI. + + We are using the following struct for passing the information of WriteAttributes between Python and C++: + + ```c + struct PyAttributePath + { + chip::EndpointId endpointId; + chip::ClusterId clusterId; + chip::AttributeId attributeId; + chip::DataVersion dataVersion; + uint8_t hasDataVersion; + }; + ``` + ''' + _fields_ = [('endpointId', ctypes.c_uint16), ('clusterId', ctypes.c_uint32), ('attributeId', + ctypes.c_uint32), ('dataVersion', ctypes.c_uint32), ('hasDataVersion', ctypes.c_uint8)] + + +class PyWriteAttributeData(ctypes.Structure): + ''' WriteAttribute struct that has c++ counterpart for CFFI. + + We are using the following struct for passing the information of WriteAttributes between Python and C++: + + ```c + struct PyWriteAttributeData + { + PyAttributePath attributePath; + void * tlvData; + size_t tlvLength; + }; + ``` + ''' + _fields_ = [('attributePath', PyAttributePath), ('tlvData', ctypes.c_void_p), ('tlvLength', ctypes.c_size_t)] + + # typedef void (*PythonInteractionModelDelegate_OnCommandResponseStatusCodeReceivedFunct)(uint64_t commandSenderPtr, # void * commandStatusBuf); # typedef void (*PythonInteractionModelDelegate_OnCommandResponseProtocolErrorFunct)(uint64_t commandSenderPtr,