diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py index 5f57c5e8266d9e..20e21177fe2b1d 100644 --- a/src/controller/python/chip/ChipDeviceCtrl.py +++ b/src/controller/python/chip/ChipDeviceCtrl.py @@ -852,6 +852,34 @@ def GetRemoteSessionParameters(self, nodeid) -> typing.Optional[SessionParameter return res + async def TestOnlySendBatchCommands(self, nodeid: int, commands: typing.List[ClusterCommand.InvokeRequestInfo], + timedRequestTimeoutMs: typing.Optional[int] = None, + interactionTimeoutMs: typing.Optional[int] = None, busyWaitMs: typing.Optional[int] = None, + suppressResponse: typing.Optional[bool] = None, remoteMaxPathsPerInvoke: typing.Optional[int] = None, + suppressTimedRequestMessage: bool = False, commandRefsOverride: typing.Optional[typing.List[int]] = None): + ''' + + Please see SendBatchCommands for description. + TestOnly overridable arguments: + remoteMaxPathsPerInvoke: Overrides the number of batch commands we think can be sent to remote node. + suppressTimedRequestMessage: When set to true, we suppress sending Timed Request Message. + commandRefsOverride: List of commandRefs to use for each command with the same index in `commands`. + ''' + self.CheckIsActive() + + eventLoop = asyncio.get_running_loop() + future = eventLoop.create_future() + + device = self.GetConnectedDeviceSync(nodeid, timeoutMs=interactionTimeoutMs) + + ClusterCommand.TestOnlySendBatchCommands( + future, eventLoop, device.deviceProxy, commands, + timedRequestTimeoutMs=timedRequestTimeoutMs, + interactionTimeoutMs=interactionTimeoutMs, busyWaitMs=busyWaitMs, suppressResponse=suppressResponse, + remoteMaxPathsPerInvoke=remoteMaxPathsPerInvoke, suppressTimedRequestMessage=suppressTimedRequestMessage, + commandRefsOverride=commandRefsOverride).raise_on_error() + return await future + async def TestOnlySendCommandTimedRequestFlagWithNoTimedInvoke(self, nodeid: int, endpoint: int, payload: ClusterObjects.ClusterCommand, responseType=None): ''' @@ -927,7 +955,7 @@ async def SendBatchCommands(self, nodeid: int, commands: typing.List[ClusterComm - A value of `None` indicates success. - If only a single command fails, for example with `UNSUPPORTED_COMMAND`, the corresponding index associated with the command will, contain `interaction_model.Status.UnsupportedCommand`. - - If a command is not responded to by server, command will contain `interaction_model.Status.Failure` + - If a command is not responded to by server, command will contain `interaction_model.Status.NoCommandResponse` Raises: - InteractionModelError if error with sending of InvokeRequestMessage fails as a whole. ''' diff --git a/src/controller/python/chip/clusters/Command.py b/src/controller/python/chip/clusters/Command.py index 6e25a76d50230f..4c556fd97f6e45 100644 --- a/src/controller/python/chip/clusters/Command.py +++ b/src/controller/python/chip/clusters/Command.py @@ -27,7 +27,7 @@ import chip.exceptions import chip.interaction_model -from chip.interaction_model import PyInvokeRequestData +from chip.interaction_model import PyInvokeRequestData, TestOnlyPyBatchCommandsOverrides from chip.native import PyChipError from .ClusterObjects import ClusterCommand @@ -204,7 +204,9 @@ def handleError(self, status: Status, chipError: PyChipError): ) def _handleDone(self): - self._future.set_result(self._responses) + # Future might already be set with exception from `handleError` + if not self._future.done(): + self._future.set_result(self._responses) ctypes.pythonapi.Py_DecRef(ctypes.py_object(self)) def handleDone(self): @@ -296,24 +298,7 @@ def SendCommand(future: Future, eventLoop, responseType: Type, device, commandPa )) -def SendBatchCommands(future: Future, eventLoop, device, commands: List[InvokeRequestInfo], - timedRequestTimeoutMs: Optional[int] = None, interactionTimeoutMs: Optional[int] = None, busyWaitMs: Optional[int] = None, - suppressResponse: Optional[bool] = None) -> PyChipError: - ''' Send a cluster-object encapsulated command to a device and does the following: - - On receipt of a successful data response, returns the cluster-object equivalent through the provided future. - - None (on a successful response containing no data) - - Raises an exception if any errors are encountered. - - If no response type is provided above, the type will be automatically deduced. - - If a valid timedRequestTimeoutMs is provided, a timed interaction will be initiated instead. - If a valid interactionTimeoutMs is provided, the interaction will terminate with a CHIP_ERROR_TIMEOUT if a response - has not been received within that timeout. If it isn't provided, a sensible value will be automatically computed that - accounts for the underlying characteristics of both the transport and the responsiveness of the receiver. - ''' - handle = chip.native.GetLibraryHandle() - - responseTypes = [] +def _BuildPyInvokeRequestData(commands: List[InvokeRequestInfo], timedRequestTimeoutMs: Optional[int], responseTypes, suppressTimedRequestMessage: bool = False) -> List[PyInvokeRequestData]: numberOfCommands = len(commands) pyBatchCommandsDataArrayType = PyInvokeRequestData * numberOfCommands pyBatchCommandsData = pyBatchCommandsDataArrayType() @@ -323,7 +308,8 @@ def SendBatchCommands(future: Future, eventLoop, device, commands: List[InvokeRe if (responseType is not None) and (not issubclass(responseType, ClusterCommand)): raise ValueError("responseType must be a ClusterCommand or None") if clusterCommand.must_use_timed_invoke and timedRequestTimeoutMs is None or timedRequestTimeoutMs == 0: - raise chip.interaction_model.InteractionModelError(chip.interaction_model.Status.NeedsTimedInteraction) + if not suppressTimedRequestMessage: + raise chip.interaction_model.InteractionModelError(chip.interaction_model.Status.NeedsTimedInteraction) payloadTLV = clusterCommand.ToTLV() @@ -335,6 +321,41 @@ def SendBatchCommands(future: Future, eventLoop, device, commands: List[InvokeRe responseTypes.append(responseType) + return pyBatchCommandsData + + +def SendBatchCommands(future: Future, eventLoop, device, commands: List[InvokeRequestInfo], + timedRequestTimeoutMs: Optional[int] = None, interactionTimeoutMs: Optional[int] = None, busyWaitMs: Optional[int] = None, + suppressResponse: Optional[bool] = None) -> PyChipError: + ''' Initiates an InvokeInteraction with the batch commands provided. + + Arguments: + - timedRequestTimeoutMs: If a valid value is provided, a timed interaction will be initiated. + - interactionTimeoutMs: If a valid value is provided, the interaction will terminate with a + CHIP_ERROR_TIMEOUT if a response is not received within the specified timeout. If not provided, + a suitable value will be automatically computed based on transport characteristics and + receiver responsiveness. + + Returns: + - PyChipError: Indicates the outcome of initiating the InvokeRequest. Upon success the caller + is expected to await on `future` to get result of the InvokeInteraction. + + Results passed via the provided future: + - Successful InvokeInteraction with path-specific responses (including path-specific errors): + - A list of responses is returned in the same order as the `commands` argument. + - Possible response elements: + - `None`: Successful command execution without additional cluster data. + - Encapsulated cluster-object: Successful command with response data. + - interaction_model.Status.*: Command failure with IM Status. + - interaction_model.Status.NoCommandResponse: No response from the server for + a specific command. + - Non-path-specific error: An `InteractionModelError` exception is raised through the future. + ''' + handle = chip.native.GetLibraryHandle() + + responseTypes = [] + pyBatchCommandsData = _BuildPyInvokeRequestData(commands, timedRequestTimeoutMs, responseTypes) + transaction = AsyncBatchCommandsTransaction(future, eventLoop, responseTypes) ctypes.pythonapi.Py_IncRef(ctypes.py_object(transaction)) @@ -345,7 +366,50 @@ 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), - pyBatchCommandsData, c_size_t(numberOfCommands)) + pyBatchCommandsData, c_size_t(len(pyBatchCommandsData))) + ) + + +def TestOnlySendBatchCommands(future: Future, eventLoop, device, commands: List[InvokeRequestInfo], + timedRequestTimeoutMs: Optional[int] = None, interactionTimeoutMs: Optional[int] = None, busyWaitMs: Optional[int] = None, + suppressResponse: Optional[bool] = None, remoteMaxPathsPerInvoke: Optional[int] = None, + suppressTimedRequestMessage: bool = False, commandRefsOverride: Optional[List[int]] = None) -> PyChipError: + ''' ONLY TO BE USED FOR TEST: Send batch commands using various overrides. + ''' + if suppressTimedRequestMessage and timedRequestTimeoutMs is not None: + raise ValueError("timedRequestTimeoutMs has non-None value while suppressTimedRequestMessage") + + overrideCommandRefs = None + if commandRefsOverride is not None: + if len(commandRefsOverride) != len(commands): + raise ValueError("Mismatch in the number of elements provided in commandRefsOverride") + overrideCommandRefsType = c_uint16 * len(commandRefsOverride) + overrideCommandRefs = overrideCommandRefsType() + + handle = chip.native.GetLibraryHandle() + + responseTypes = [] + pyBatchCommandsData = _BuildPyInvokeRequestData(commands, timedRequestTimeoutMs, + responseTypes, suppressTimedRequestMessage=suppressTimedRequestMessage) + + transaction = AsyncBatchCommandsTransaction(future, eventLoop, responseTypes) + ctypes.pythonapi.Py_IncRef(ctypes.py_object(transaction)) + + testOnlyOverrides = TestOnlyPyBatchCommandsOverrides() + testOnlyOverrides.suppressTimedRequestMessage = suppressTimedRequestMessage + testOnlyOverrides.overrideRemoteMaxPathsPerInvoke = 0 if remoteMaxPathsPerInvoke is None else c_uint16(remoteMaxPathsPerInvoke) + testOnlyOverrides.overrideCommandRefsList = overrideCommandRefs + testOnlyOverrides.overrideCommandRefsListLength = 0 if overrideCommandRefs is None else c_size_t(len(overrideCommandRefs)) + + return builtins.chipStack.Call( + lambda: handle.pychip_CommandSender_TestOnlySendBatchCommands( + py_object(transaction), device, + c_uint16(0 if timedRequestTimeoutMs is None else timedRequestTimeoutMs), + 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), + testOnlyOverrides, + pyBatchCommandsData, c_size_t(len(pyBatchCommandsData))) ) @@ -377,6 +441,8 @@ def Init(): 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, POINTER(PyInvokeRequestData), c_size_t]) + setter.Set('pychip_CommandSender_TestOnlySendBatchCommands', + PyChipError, [py_object, c_void_p, c_uint16, c_uint16, c_uint16, c_bool, TestOnlyPyBatchCommandsOverrides, 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/command.cpp b/src/controller/python/chip/clusters/command.cpp index 7195cefe16eba1..9d718aa2aca222 100644 --- a/src/controller/python/chip/clusters/command.cpp +++ b/src/controller/python/chip/clusters/command.cpp @@ -181,6 +181,132 @@ class CommandSenderCallback : public CommandSender::ExtendableCallback bool mIsBatchedCommands; }; +PyChipError SendBatchCommandsInternal(void * appContext, DeviceProxy * device, uint16_t timedRequestTimeoutMs, + uint16_t interactionTimeoutMs, uint16_t busyWaitMs, bool suppressResponse, + python::TestOnlyPyBatchCommandsOverrides * testOnlyOverrides, + python::PyInvokeRequestData * batchCommandData, size_t length) +{ + CommandSender::ConfigParameters config; + CHIP_ERROR err = CHIP_NO_ERROR; + + bool testOnlySuppressTimedRequestMessage = false; + uint16_t * testOnlyCommandRefsOverride = nullptr; + + VerifyOrReturnError(device->GetSecureSession().HasValue(), ToPyChipError(CHIP_ERROR_MISSING_SECURE_SESSION)); + + // Test only override validation checks and setup + if (testOnlyOverrides != nullptr) + { + if (testOnlyOverrides->suppressTimedRequestMessage) + { + VerifyOrReturnError(timedRequestTimeoutMs == 0, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); + testOnlySuppressTimedRequestMessage = true; + } + if (testOnlyOverrides->overrideCommandRefsList != nullptr) + { + VerifyOrReturnError(length == testOnlyOverrides->overrideCommandRefsListLength, + ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); + testOnlyCommandRefsOverride = testOnlyOverrides->overrideCommandRefsList; + } + } + + if (testOnlyOverrides != nullptr && testOnlyOverrides->overrideRemoteMaxPathsPerInvoke) + { + config.SetRemoteMaxPathsPerInvoke(testOnlyOverrides->overrideRemoteMaxPathsPerInvoke); + } + else + { + auto remoteSessionParameters = device->GetSecureSession().Value()->GetRemoteSessionParameters(); + config.SetRemoteMaxPathsPerInvoke(remoteSessionParameters.GetMaxPathsPerInvoke()); + } + + std::unique_ptr callback = + std::make_unique(appContext, /* isBatchedCommands =*/true); + + bool isTimedRequest = timedRequestTimeoutMs != 0 || testOnlySuppressTimedRequestMessage; + std::unique_ptr sender = + std::make_unique(callback.get(), device->GetExchangeManager(), isTimedRequest, suppressResponse); + + SuccessOrExit(err = sender->SetCommandSenderConfig(config)); + + for (size_t i = 0; i < length; i++) + { + 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; + + const uint8_t * tlvBuffer = reinterpret_cast(tlv); + + app::CommandPathParams cmdParams = { endpointId, /* group id */ 0, clusterId, commandId, + (app::CommandPathFlags::kEndpointIdValid) }; + + CommandSender::AdditionalCommandParameters additionalParams; + + SuccessOrExit(err = sender->PrepareCommand(cmdParams, additionalParams)); + if (testOnlyCommandRefsOverride != nullptr) + { + additionalParams.commandRef.SetValue(testOnlyCommandRefsOverride[i]); + } + { + 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)); + + // 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); + if (testOnlyCommandRefsOverride != nullptr) + { + // Making sure the value we used to override CommandRef was actually used. + VerifyOrDie(additionalParams.commandRef.Value() == testOnlyCommandRefsOverride[i]); + // Ignoring the result of adding to index as the test might be trying to set duplicate CommandRefs. + callback->AddCommandRefToIndexLookup(additionalParams.commandRef.Value(), i); + } + else + { + SuccessOrExit(err = callback->AddCommandRefToIndexLookup(additionalParams.commandRef.Value(), i)); + } + } + + { + Optional interactionTimeout = interactionTimeoutMs != 0 + ? MakeOptional(System::Clock::Milliseconds32(interactionTimeoutMs)) + : Optional::Missing(); + if (testOnlySuppressTimedRequestMessage) + { + SuccessOrExit(err = sender->TestOnlyCommandSenderTimedRequestFlagWithNoTimedInvoke(device->GetSecureSession().Value(), + interactionTimeout)); + } + else + { + SuccessOrExit(err = sender->SendCommandRequest(device->GetSecureSession().Value(), interactionTimeout)); + } + } + + sender.release(); + callback.release(); + + // TODO(#30985): Reconsider the purpose of busyWait and if it can be broken out into it's + // own method/primitive. + if (busyWaitMs) + { + usleep(busyWaitMs * 1000); + } + +exit: + return ToPyChipError(err); +} + } // namespace python } // namespace chip @@ -251,81 +377,23 @@ PyChipError pychip_CommandSender_SendBatchCommands(void * appContext, DeviceProx uint16_t interactionTimeoutMs, uint16_t busyWaitMs, bool suppressResponse, python::PyInvokeRequestData * batchCommandData, size_t length) { - CHIP_ERROR err = CHIP_NO_ERROR; - - VerifyOrReturnError(device->GetSecureSession().HasValue(), ToPyChipError(CHIP_ERROR_MISSING_SECURE_SESSION)); - auto remoteSessionParameters = device->GetSecureSession().Value()->GetRemoteSessionParameters(); - CommandSender::ConfigParameters config; - - // TODO(#30986): Need to create a separate pychip_CommandSender_TestOnlySendBatchCommands so that we perform - // operations that is very clear at callsite that violating certain aspects like setting this MaxPathsPerInvoke - // to a number other than what is reported by the remote node is allowed. Right now the only user of this - // function is cert test script. To implement pychip_CommandSender_TestOnlySendBatchCommands in a clean way - // we need to move away from the variadic arguments. - // config.SetRemoteMaxPathsPerInvoke(remoteSessionParameters.GetMaxPathsPerInvoke()); - (void) remoteSessionParameters; // Still want to get remoteSessionParameters, just wont use it right now. - config.SetRemoteMaxPathsPerInvoke(std::numeric_limits::max()); - - std::unique_ptr callback = - std::make_unique(appContext, /* isBatchedCommands =*/true); - std::unique_ptr sender = - std::make_unique(callback.get(), device->GetExchangeManager(), - /* is timed request */ timedRequestTimeoutMs != 0, suppressResponse); - - SuccessOrExit(err = sender->SetCommandSenderConfig(config)); - - for (size_t i = 0; i < length; i++) - { - 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; - - const uint8_t * tlvBuffer = reinterpret_cast(tlv); - - app::CommandPathParams cmdParams = { endpointId, /* group id */ 0, clusterId, commandId, - (app::CommandPathFlags::kEndpointIdValid) }; - - 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(tlvLength)); - reader.Next(); - SuccessOrExit(err = writer->CopyContainer(TLV::ContextTag(CommandDataIB::Tag::kFields), reader)); - } - - 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)); - } - - SuccessOrExit(err = sender->SendCommandRequest(device->GetSecureSession().Value(), - interactionTimeoutMs != 0 - ? MakeOptional(System::Clock::Milliseconds32(interactionTimeoutMs)) - : Optional::Missing())); - - sender.release(); - callback.release(); - - // TODO(#30985): Reconsider the purpose of busyWait and if it can be broken out into it's - // own method/primitive. - if (busyWaitMs) - { - usleep(busyWaitMs * 1000); - } + python::TestOnlyPyBatchCommandsOverrides * testOnlyOverrides = nullptr; + return SendBatchCommandsInternal(appContext, device, timedRequestTimeoutMs, interactionTimeoutMs, busyWaitMs, suppressResponse, + testOnlyOverrides, batchCommandData, length); +} -exit: - return ToPyChipError(err); +PyChipError pychip_CommandSender_TestOnlySendBatchCommands(void * appContext, DeviceProxy * device, uint16_t timedRequestTimeoutMs, + uint16_t interactionTimeoutMs, uint16_t busyWaitMs, + bool suppressResponse, + python::TestOnlyPyBatchCommandsOverrides testOnlyOverrides, + python::PyInvokeRequestData * batchCommandData, size_t length) +{ +#if CONFIG_BUILD_FOR_HOST_UNIT_TEST + return SendBatchCommandsInternal(appContext, device, timedRequestTimeoutMs, interactionTimeoutMs, busyWaitMs, suppressResponse, + &testOnlyOverrides, batchCommandData, length); +#else + return ToPyChipError(CHIP_ERROR_NOT_IMPLEMENTED); +#endif } PyChipError pychip_CommandSender_TestOnlySendCommandTimedRequestNoTimedInvoke( diff --git a/src/controller/python/chip/interaction_model/Delegate.h b/src/controller/python/chip/interaction_model/Delegate.h index f11f4931fc4c1d..72832a7510f25e 100644 --- a/src/controller/python/chip/interaction_model/Delegate.h +++ b/src/controller/python/chip/interaction_model/Delegate.h @@ -60,6 +60,17 @@ struct PyWriteAttributeData size_t tlvLength; }; +struct TestOnlyPyBatchCommandsOverrides +{ + // When max paths per invoke override value is set to 0, we will not use + // it as an override. Otherwise, this value will be provided to the + // CommandSender as the remote node's maximum paths. + uint16_t overrideRemoteMaxPathsPerInvoke; + bool suppressTimedRequestMessage; + uint16_t * overrideCommandRefsList; + size_t overrideCommandRefsListLength; +}; + } // namespace python namespace Controller { diff --git a/src/controller/python/chip/interaction_model/__init__.py b/src/controller/python/chip/interaction_model/__init__.py index 78568563a73475..f7239d778058eb 100644 --- a/src/controller/python/chip/interaction_model/__init__.py +++ b/src/controller/python/chip/interaction_model/__init__.py @@ -27,11 +27,12 @@ from chip.exceptions import ChipStackException from .delegate import (AttributePath, AttributePathIBstruct, DataVersionFilterIBstruct, EventPath, EventPathIBstruct, - PyInvokeRequestData, PyWriteAttributeData, SessionParameters, SessionParametersStruct) + PyInvokeRequestData, PyWriteAttributeData, SessionParameters, SessionParametersStruct, + TestOnlyPyBatchCommandsOverrides) __all__ = ["AttributePath", "AttributePathIBstruct", "DataVersionFilterIBstruct", "EventPath", "EventPathIBstruct", "InteractionModelError", "PyInvokeRequestData", - "PyWriteAttributeData", "SessionParameters", "SessionParametersStruct", "Status"] + "PyWriteAttributeData", "SessionParameters", "SessionParametersStruct", "Status", "TestOnlyPyBatchCommandsOverrides"] # 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 a9a9c04c4365a9..d0ed641fa2a75c 100644 --- a/src/controller/python/chip/interaction_model/delegate.py +++ b/src/controller/python/chip/interaction_model/delegate.py @@ -17,7 +17,7 @@ import ctypes import threading import typing -from ctypes import CFUNCTYPE, c_uint8, c_uint32, c_uint64, c_void_p +from ctypes import CFUNCTYPE, POINTER, c_uint8, c_uint32, c_uint64, c_void_p from dataclasses import dataclass import chip.exceptions @@ -199,6 +199,25 @@ class PyWriteAttributeData(ctypes.Structure): _fields_ = [('attributePath', PyAttributePath), ('tlvData', ctypes.c_void_p), ('tlvLength', ctypes.c_size_t)] +class TestOnlyPyBatchCommandsOverrides(ctypes.Structure): + ''' TestOnly struct for overriding aspects of batch command to send invalid commands. + + We are using the following struct for passing the information of TestOnlyPyBatchCommandsOverrides between Python and C++: + + ```c + struct TestOnlyPyBatchCommandsOverrides + { + uint16_t overrideRemoteMaxPathsPerInvoke; + bool suppressTimedRequestMessage; + uint16_t * overrideCommandRefsList; + size_t overrideCommandRefsListLength; + }; + ``` + ''' + _fields_ = [('overrideRemoteMaxPathsPerInvoke', ctypes.c_uint16), ('suppressTimedRequestMessage', ctypes.c_bool), + ('overrideCommandRefsList', POINTER(ctypes.c_uint16)), ('overrideCommandRefsListLength', ctypes.c_size_t)] + + # typedef void (*PythonInteractionModelDelegate_OnCommandResponseStatusCodeReceivedFunct)(uint64_t commandSenderPtr, # void * commandStatusBuf); # typedef void (*PythonInteractionModelDelegate_OnCommandResponseProtocolErrorFunct)(uint64_t commandSenderPtr,