Skip to content

Commit 66c416e

Browse files
pidarpedPeterC1965
authored andcommitted
Add TransportPayloadCapability flag for GetConnectedDevices and bubble (project-chip#34450)
up the flag to the wrapper IM Python APIs. Add python script binding methods for LargePayload tests --to check if session allows large payload. --to close the underlying TCP connection. --to check if the session is active.
1 parent a13bd22 commit 66c416e

File tree

3 files changed

+144
-22
lines changed

3 files changed

+144
-22
lines changed

src/controller/python/ChipDeviceController-ScriptBinding.cpp

+58-3
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,8 @@ PyChipError pychip_DeviceCommissioner_CloseBleConnection(chip::Controller::Devic
213213
const char * pychip_Stack_StatusReportToString(uint32_t profileId, uint16_t statusCode);
214214

215215
PyChipError pychip_GetConnectedDeviceByNodeId(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId,
216-
chip::Controller::Python::PyObject * context, DeviceAvailableFunc callback);
216+
chip::Controller::Python::PyObject * context, DeviceAvailableFunc callback,
217+
int transportPayloadCapability);
217218
PyChipError pychip_FreeOperationalDeviceProxy(chip::OperationalDeviceProxy * deviceProxy);
218219
PyChipError pychip_GetLocalSessionId(chip::OperationalDeviceProxy * deviceProxy, uint16_t * localSessionId);
219220
PyChipError pychip_GetNumSessionsToPeer(chip::OperationalDeviceProxy * deviceProxy, uint32_t * numSessions);
@@ -239,6 +240,13 @@ void pychip_Storage_ShutdownAdapter(chip::Controller::Python::StorageAdapter * s
239240
// ICD
240241
//
241242
void pychip_CheckInDelegate_SetOnCheckInCompleteCallback(PyChipCheckInDelegate::OnCheckInCompleteCallback * callback);
243+
244+
//
245+
// LargePayload and TCP
246+
PyChipError pychip_SessionAllowsLargePayload(chip::OperationalDeviceProxy * deviceProxy, bool * allowsLargePayload);
247+
PyChipError pychip_IsSessionOverTCPConnection(chip::OperationalDeviceProxy * deviceProxy, bool * isSessionOverTCP);
248+
PyChipError pychip_IsActiveSession(chip::OperationalDeviceProxy * deviceProxy, bool * isActiveSession);
249+
PyChipError pychip_CloseTCPConnectionWithPeer(chip::OperationalDeviceProxy * deviceProxy);
242250
}
243251

244252
void * pychip_Storage_InitializeStorageAdapter(chip::Controller::Python::PyObject * context,
@@ -807,11 +815,58 @@ struct GetDeviceCallbacks
807815
} // anonymous namespace
808816

809817
PyChipError pychip_GetConnectedDeviceByNodeId(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeId,
810-
chip::Controller::Python::PyObject * context, DeviceAvailableFunc callback)
818+
chip::Controller::Python::PyObject * context, DeviceAvailableFunc callback,
819+
int transportPayloadCapability)
811820
{
812821
VerifyOrReturnError(devCtrl != nullptr, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT));
813822
auto * callbacks = new GetDeviceCallbacks(context, callback);
814-
return ToPyChipError(devCtrl->GetConnectedDevice(nodeId, &callbacks->mOnSuccess, &callbacks->mOnFailure));
823+
return ToPyChipError(devCtrl->GetConnectedDevice(nodeId, &callbacks->mOnSuccess, &callbacks->mOnFailure,
824+
static_cast<chip::TransportPayloadCapability>(transportPayloadCapability)));
825+
}
826+
827+
PyChipError pychip_SessionAllowsLargePayload(chip::OperationalDeviceProxy * deviceProxy, bool * allowsLargePayload)
828+
{
829+
VerifyOrReturnError(deviceProxy->GetSecureSession().HasValue(), ToPyChipError(CHIP_ERROR_MISSING_SECURE_SESSION));
830+
VerifyOrReturnError(allowsLargePayload != nullptr, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT));
831+
832+
*allowsLargePayload = deviceProxy->GetSecureSession().Value()->AsSecureSession()->AllowsLargePayload();
833+
834+
return ToPyChipError(CHIP_NO_ERROR);
835+
}
836+
837+
PyChipError pychip_IsSessionOverTCPConnection(chip::OperationalDeviceProxy * deviceProxy, bool * isSessionOverTCP)
838+
{
839+
VerifyOrReturnError(deviceProxy->GetSecureSession().HasValue(), ToPyChipError(CHIP_ERROR_MISSING_SECURE_SESSION));
840+
VerifyOrReturnError(isSessionOverTCP != nullptr, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT));
841+
842+
*isSessionOverTCP = deviceProxy->GetSecureSession().Value()->AsSecureSession()->GetTCPConnection() != nullptr;
843+
844+
return ToPyChipError(CHIP_NO_ERROR);
845+
}
846+
847+
PyChipError pychip_IsActiveSession(chip::OperationalDeviceProxy * deviceProxy, bool * isActiveSession)
848+
{
849+
VerifyOrReturnError(isActiveSession != nullptr, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT));
850+
851+
*isActiveSession = false;
852+
if (deviceProxy->GetSecureSession().HasValue())
853+
{
854+
*isActiveSession = deviceProxy->GetSecureSession().Value()->AsSecureSession()->IsActiveSession();
855+
}
856+
857+
return ToPyChipError(CHIP_NO_ERROR);
858+
}
859+
860+
PyChipError pychip_CloseTCPConnectionWithPeer(chip::OperationalDeviceProxy * deviceProxy)
861+
{
862+
VerifyOrReturnError(deviceProxy->GetSecureSession().HasValue(), ToPyChipError(CHIP_ERROR_MISSING_SECURE_SESSION));
863+
VerifyOrReturnError(deviceProxy->GetSecureSession().Value()->AsSecureSession()->AllowsLargePayload(),
864+
ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT));
865+
866+
deviceProxy->GetExchangeManager()->GetSessionManager()->TCPDisconnect(
867+
deviceProxy->GetSecureSession().Value()->AsSecureSession()->GetTCPConnection(), /* shouldAbort = */ false);
868+
869+
return ToPyChipError(CHIP_NO_ERROR);
815870
}
816871

817872
PyChipError pychip_FreeOperationalDeviceProxy(chip::OperationalDeviceProxy * deviceProxy)

src/controller/python/chip/ChipDeviceCtrl.py

+82-17
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,16 @@
8484

8585
_ChipDeviceController_IterateDiscoveredCommissionableNodesFunct = CFUNCTYPE(None, c_char_p, c_size_t)
8686

87+
# Defines for the transport payload types to use to select the suitable
88+
# underlying transport of the session.
89+
# class TransportPayloadCapability(ctypes.c_int):
90+
91+
92+
class TransportPayloadCapability(ctypes.c_int):
93+
MRP_PAYLOAD = 0
94+
LARGE_PAYLOAD = 1
95+
MRP_OR_TCP_PAYLOAD = 2
96+
8797

8898
@dataclass
8999
class CommissioningParameters:
@@ -371,6 +381,53 @@ def attestationChallenge(self) -> bytes:
371381

372382
return bytes(buf)
373383

384+
@property
385+
def sessionAllowsLargePayload(self) -> bool:
386+
self._dmLib.pychip_SessionAllowsLargePayload.argtypes = [ctypes.c_void_p, POINTER(ctypes.c_bool)]
387+
self._dmLib.pychip_SessionAllowsLargePayload.restype = PyChipError
388+
389+
supportsLargePayload = ctypes.c_bool(False)
390+
391+
builtins.chipStack.Call(
392+
lambda: self._dmLib.pychip_SessionAllowsLargePayload(self._deviceProxy, pointer(supportsLargePayload))
393+
).raise_on_error()
394+
395+
return supportsLargePayload.value
396+
397+
@property
398+
def isSessionOverTCPConnection(self) -> bool:
399+
self._dmLib.pychip_IsSessionOverTCPConnection.argtypes = [ctypes.c_void_p, POINTER(ctypes.c_bool)]
400+
self._dmLib.pychip_IsSessionOverTCPConnection.restype = PyChipError
401+
402+
isSessionOverTCP = ctypes.c_bool(False)
403+
404+
builtins.chipStack.Call(
405+
lambda: self._dmLib.pychip_IsSessionOverTCPConnection(self._deviceProxy, pointer(isSessionOverTCP))
406+
).raise_on_error()
407+
408+
return isSessionOverTCP.value
409+
410+
@property
411+
def isActiveSession(self) -> bool:
412+
self._dmLib.pychip_IsActiveSession.argtypes = [ctypes.c_void_p, POINTER(ctypes.c_bool)]
413+
self._dmLib.pychip_IsActiveSession.restype = PyChipError
414+
415+
isActiveSession = ctypes.c_bool(False)
416+
417+
builtins.chipStack.Call(
418+
lambda: self._dmLib.pychip_IsActiveSession(self._deviceProxy, pointer(isActiveSession))
419+
).raise_on_error()
420+
421+
return isActiveSession.value
422+
423+
def closeTCPConnectionWithPeer(self):
424+
self._dmLib.pychip_CloseTCPConnectionWithPeer.argtypes = [ctypes.c_void_p]
425+
self._dmLib.pychip_CloseTCPConnectionWithPeer.restype = PyChipError
426+
427+
builtins.chipStack.Call(
428+
lambda: self._dmLib.pychip_CloseTCPConnectionWithPeer(self._deviceProxy)
429+
).raise_on_error()
430+
374431

375432
DiscoveryFilterType = discovery.FilterType
376433
DiscoveryType = discovery.DiscoveryType
@@ -906,7 +963,7 @@ async def FindOrEstablishPASESession(self, setupCode: str, nodeid: int, timeoutM
906963
if res.is_success:
907964
return DeviceProxyWrapper(returnDevice, DeviceProxyWrapper.DeviceProxyType.COMMISSIONEE, self._dmLib)
908965

909-
def GetConnectedDeviceSync(self, nodeid, allowPASE=True, timeoutMs: int = None):
966+
def GetConnectedDeviceSync(self, nodeid, allowPASE=True, timeoutMs: int = None, payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD):
910967
''' Gets an OperationalDeviceProxy or CommissioneeDeviceProxy for the specified Node.
911968
912969
nodeId: Target's Node ID
@@ -943,7 +1000,7 @@ def deviceAvailable(self, device, err):
9431000
closure = DeviceAvailableClosure()
9441001
ctypes.pythonapi.Py_IncRef(ctypes.py_object(closure))
9451002
self._ChipStack.Call(lambda: self._dmLib.pychip_GetConnectedDeviceByNodeId(
946-
self.devCtrl, nodeid, ctypes.py_object(closure), _DeviceAvailableCallback),
1003+
self.devCtrl, nodeid, ctypes.py_object(closure), _DeviceAvailableCallback, payloadCapability),
9471004
timeoutMs).raise_on_error()
9481005

9491006
# The callback might have been received synchronously (during self._ChipStack.Call()).
@@ -975,7 +1032,8 @@ async def WaitForActive(self, nodeid, *, timeoutSeconds=30.0, stayActiveDuration
9751032
await WaitForCheckIn(ScopedNodeId(nodeid, self._fabricIndex), timeoutSeconds=timeoutSeconds)
9761033
return await self.SendCommand(nodeid, 0, Clusters.IcdManagement.Commands.StayActiveRequest(stayActiveDuration=stayActiveDurationMs))
9771034

978-
async def GetConnectedDevice(self, nodeid, allowPASE: bool = True, timeoutMs: int = None):
1035+
async def GetConnectedDevice(self, nodeid, allowPASE: bool = True, timeoutMs: int = None,
1036+
payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD):
9791037
''' Gets an OperationalDeviceProxy or CommissioneeDeviceProxy for the specified Node.
9801038
9811039
nodeId: Target's Node ID
@@ -1020,7 +1078,7 @@ def deviceAvailable(self, device, err):
10201078
closure = DeviceAvailableClosure(eventLoop, future)
10211079
ctypes.pythonapi.Py_IncRef(ctypes.py_object(closure))
10221080
await self._ChipStack.CallAsync(lambda: self._dmLib.pychip_GetConnectedDeviceByNodeId(
1023-
self.devCtrl, nodeid, ctypes.py_object(closure), _DeviceAvailableCallback),
1081+
self.devCtrl, nodeid, ctypes.py_object(closure), _DeviceAvailableCallback, payloadCapability),
10241082
timeoutMs)
10251083

10261084
# The callback might have been received synchronously (during self._ChipStack.CallAsync()).
@@ -1124,7 +1182,8 @@ async def TestOnlySendCommandTimedRequestFlagWithNoTimedInvoke(self, nodeid: int
11241182
async def SendCommand(self, nodeid: int, endpoint: int, payload: ClusterObjects.ClusterCommand, responseType=None,
11251183
timedRequestTimeoutMs: typing.Union[None, int] = None,
11261184
interactionTimeoutMs: typing.Union[None, int] = None, busyWaitMs: typing.Union[None, int] = None,
1127-
suppressResponse: typing.Union[None, bool] = None):
1185+
suppressResponse: typing.Union[None, bool] = None,
1186+
payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD):
11281187
'''
11291188
Send a cluster-object encapsulated command to a node and get returned a future that can be awaited upon to receive
11301189
the response. If a valid responseType is passed in, that will be used to de-serialize the object. If not,
@@ -1144,7 +1203,7 @@ async def SendCommand(self, nodeid: int, endpoint: int, payload: ClusterObjects.
11441203
eventLoop = asyncio.get_running_loop()
11451204
future = eventLoop.create_future()
11461205

1147-
device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs)
1206+
device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs, payloadCapability=payloadCapability)
11481207
res = await ClusterCommand.SendCommand(
11491208
future, eventLoop, responseType, device.deviceProxy, ClusterCommand.CommandPath(
11501209
EndpointId=endpoint,
@@ -1158,7 +1217,8 @@ async def SendCommand(self, nodeid: int, endpoint: int, payload: ClusterObjects.
11581217
async def SendBatchCommands(self, nodeid: int, commands: typing.List[ClusterCommand.InvokeRequestInfo],
11591218
timedRequestTimeoutMs: typing.Optional[int] = None,
11601219
interactionTimeoutMs: typing.Optional[int] = None, busyWaitMs: typing.Optional[int] = None,
1161-
suppressResponse: typing.Optional[bool] = None):
1220+
suppressResponse: typing.Optional[bool] = None,
1221+
payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD):
11621222
'''
11631223
Send a batch of cluster-object encapsulated commands to a node and get returned a future that can be awaited upon to receive
11641224
the responses. If a valid responseType is passed in, that will be used to de-serialize the object. If not,
@@ -1186,7 +1246,7 @@ async def SendBatchCommands(self, nodeid: int, commands: typing.List[ClusterComm
11861246
eventLoop = asyncio.get_running_loop()
11871247
future = eventLoop.create_future()
11881248

1189-
device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs)
1249+
device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs, payloadCapability=payloadCapability)
11901250

11911251
res = await ClusterCommand.SendBatchCommands(
11921252
future, eventLoop, device.deviceProxy, commands,
@@ -1215,7 +1275,8 @@ def SendGroupCommand(self, groupid: int, payload: ClusterObjects.ClusterCommand,
12151275
async def WriteAttribute(self, nodeid: int,
12161276
attributes: typing.List[typing.Tuple[int, ClusterObjects.ClusterAttributeDescriptor]],
12171277
timedRequestTimeoutMs: typing.Union[None, int] = None,
1218-
interactionTimeoutMs: typing.Union[None, int] = None, busyWaitMs: typing.Union[None, int] = None):
1278+
interactionTimeoutMs: typing.Union[None, int] = None, busyWaitMs: typing.Union[None, int] = None,
1279+
payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD):
12191280
'''
12201281
Write a list of attributes on a target node.
12211282
@@ -1237,7 +1298,7 @@ async def WriteAttribute(self, nodeid: int,
12371298
eventLoop = asyncio.get_running_loop()
12381299
future = eventLoop.create_future()
12391300

1240-
device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs)
1301+
device = await self.GetConnectedDevice(nodeid, timeoutMs=interactionTimeoutMs, payloadCapability=payloadCapability)
12411302

12421303
attrs = []
12431304
for v in attributes:
@@ -1396,7 +1457,8 @@ async def Read(self, nodeid: int, attributes: typing.List[typing.Union[
13961457
]] = None,
13971458
eventNumberFilter: typing.Optional[int] = None,
13981459
returnClusterObject: bool = False, reportInterval: typing.Tuple[int, int] = None,
1399-
fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True):
1460+
fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True,
1461+
payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD):
14001462
'''
14011463
Read a list of attributes and/or events from a target node
14021464
@@ -1456,7 +1518,7 @@ async def Read(self, nodeid: int, attributes: typing.List[typing.Union[
14561518
eventLoop = asyncio.get_running_loop()
14571519
future = eventLoop.create_future()
14581520

1459-
device = await self.GetConnectedDevice(nodeid)
1521+
device = await self.GetConnectedDevice(nodeid, payloadCapability=payloadCapability)
14601522
attributePaths = [self._parseAttributePathTuple(
14611523
v) for v in attributes] if attributes else None
14621524
clusterDataVersionFilters = [self._parseDataVersionFilterTuple(
@@ -1487,7 +1549,8 @@ async def ReadAttribute(self, nodeid: int, attributes: typing.List[typing.Union[
14871549
]], dataVersionFilters: typing.List[typing.Tuple[int, typing.Type[ClusterObjects.Cluster], int]] = None,
14881550
returnClusterObject: bool = False,
14891551
reportInterval: typing.Tuple[int, int] = None,
1490-
fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True):
1552+
fabricFiltered: bool = True, keepSubscriptions: bool = False, autoResubscribe: bool = True,
1553+
payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD):
14911554
'''
14921555
Read a list of attributes from a target node, this is a wrapper of DeviceController.Read()
14931556
@@ -1547,7 +1610,8 @@ async def ReadAttribute(self, nodeid: int, attributes: typing.List[typing.Union[
15471610
reportInterval=reportInterval,
15481611
fabricFiltered=fabricFiltered,
15491612
keepSubscriptions=keepSubscriptions,
1550-
autoResubscribe=autoResubscribe)
1613+
autoResubscribe=autoResubscribe,
1614+
payloadCapability=payloadCapability)
15511615
if isinstance(res, ClusterAttribute.SubscriptionTransaction):
15521616
return res
15531617
else:
@@ -1569,7 +1633,8 @@ async def ReadEvent(self, nodeid: int, events: typing.List[typing.Union[
15691633
fabricFiltered: bool = True,
15701634
reportInterval: typing.Tuple[int, int] = None,
15711635
keepSubscriptions: bool = False,
1572-
autoResubscribe: bool = True):
1636+
autoResubscribe: bool = True,
1637+
payloadCapability: int = TransportPayloadCapability.MRP_PAYLOAD):
15731638
'''
15741639
Read a list of events from a target node, this is a wrapper of DeviceController.Read()
15751640
@@ -1616,7 +1681,7 @@ async def ReadEvent(self, nodeid: int, events: typing.List[typing.Union[
16161681
'''
16171682
res = await self.Read(nodeid=nodeid, events=events, eventNumberFilter=eventNumberFilter,
16181683
fabricFiltered=fabricFiltered, reportInterval=reportInterval, keepSubscriptions=keepSubscriptions,
1619-
autoResubscribe=autoResubscribe)
1684+
autoResubscribe=autoResubscribe, payloadCapability=payloadCapability)
16201685
if isinstance(res, ClusterAttribute.SubscriptionTransaction):
16211686
return res
16221687
else:
@@ -1764,7 +1829,7 @@ def _InitLib(self):
17641829
self._dmLib.pychip_ScriptDevicePairingDelegate_SetExpectingPairingComplete.restype = PyChipError
17651830

17661831
self._dmLib.pychip_GetConnectedDeviceByNodeId.argtypes = [
1767-
c_void_p, c_uint64, py_object, _DeviceAvailableCallbackFunct]
1832+
c_void_p, c_uint64, py_object, _DeviceAvailableCallbackFunct, c_int]
17681833
self._dmLib.pychip_GetConnectedDeviceByNodeId.restype = PyChipError
17691834

17701835
self._dmLib.pychip_FreeOperationalDeviceProxy.argtypes = [

src/python_testing/matter_testing_support.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -928,15 +928,17 @@ async def read_single_attribute_expect_error(
928928
async def send_single_cmd(
929929
self, cmd: Clusters.ClusterObjects.ClusterCommand,
930930
dev_ctrl: ChipDeviceCtrl = None, node_id: int = None, endpoint: int = None,
931-
timedRequestTimeoutMs: typing.Union[None, int] = None) -> object:
931+
timedRequestTimeoutMs: typing.Union[None, int] = None,
932+
payloadCapability: int = ChipDeviceCtrl.TransportPayloadCapability.MRP_PAYLOAD) -> object:
932933
if dev_ctrl is None:
933934
dev_ctrl = self.default_controller
934935
if node_id is None:
935936
node_id = self.dut_node_id
936937
if endpoint is None:
937938
endpoint = self.matter_test_config.endpoint
938939

939-
result = await dev_ctrl.SendCommand(nodeid=node_id, endpoint=endpoint, payload=cmd, timedRequestTimeoutMs=timedRequestTimeoutMs)
940+
result = await dev_ctrl.SendCommand(nodeid=node_id, endpoint=endpoint, payload=cmd, timedRequestTimeoutMs=timedRequestTimeoutMs,
941+
payloadCapability=payloadCapability)
940942
return result
941943

942944
async def send_test_event_triggers(self, eventTrigger: int, enableKey: bytes = None):

0 commit comments

Comments
 (0)