@@ -75,6 +75,9 @@ CommandSender::CommandSender(ExtendableCallback * apExtendableCallback, Messagin
75
75
mTimedRequest (aIsTimedRequest), mUseExtendableCallback (true )
76
76
{
77
77
assertChipStackLockedByCurrentThread ();
78
+ #if CHIP_CONFIG_COMMAND_SENDER_BUILTIN_SUPPORT_FOR_BATCHED_COMMANDS
79
+ mpPendingResponseTracker = &mNonTestPendingResponseTracker ;
80
+ #endif // CHIP_CONFIG_COMMAND_SENDER_BUILTIN_SUPPORT_FOR_BATCHED_COMMANDS
78
81
}
79
82
80
83
CommandSender::~CommandSender ()
@@ -222,9 +225,9 @@ CHIP_ERROR CommandSender::OnMessageReceived(Messaging::ExchangeContext * apExcha
222
225
223
226
if (aPayloadHeader.HasMessageType (MsgType::InvokeCommandResponse))
224
227
{
228
+ mInvokeResponseMessageCount ++;
225
229
err = ProcessInvokeResponse (std::move (aPayload), moreChunkedMessages);
226
230
SuccessOrExit (err);
227
- mInvokeResponseMessageCount ++;
228
231
if (moreChunkedMessages)
229
232
{
230
233
StatusResponse::Send (Status::Success, apExchangeContext, /* aExpectResponse = */ true );
@@ -258,6 +261,10 @@ CHIP_ERROR CommandSender::OnMessageReceived(Messaging::ExchangeContext * apExcha
258
261
259
262
if (mState != State::AwaitingResponse)
260
263
{
264
+ if (err == CHIP_NO_ERROR)
265
+ {
266
+ FlushNoCommandResponse ();
267
+ }
261
268
Close ();
262
269
}
263
270
// Else we got a response to a Timed Request and just sent the invoke.
@@ -331,12 +338,25 @@ void CommandSender::OnResponseTimeout(Messaging::ExchangeContext * apExchangeCon
331
338
Close ();
332
339
}
333
340
341
+ void CommandSender::FlushNoCommandResponse ()
342
+ {
343
+ if (mpPendingResponseTracker && mUseExtendableCallback && mCallbackHandle .extendableCallback )
344
+ {
345
+ Optional<uint16_t > commandRef = mpPendingResponseTracker->PopPendingResponse ();
346
+ while (commandRef.HasValue ())
347
+ {
348
+ NoResponseData noResponseData = { commandRef.Value () };
349
+ mCallbackHandle .extendableCallback ->OnNoResponse (this , noResponseData);
350
+ commandRef = mpPendingResponseTracker->PopPendingResponse ();
351
+ }
352
+ }
353
+ }
354
+
334
355
void CommandSender::Close ()
335
356
{
336
357
mSuppressResponse = false ;
337
358
mTimedRequest = false ;
338
359
MoveToState (State::AwaitingDestruction);
339
-
340
360
OnDoneCallback ();
341
361
}
342
362
@@ -350,10 +370,10 @@ CHIP_ERROR CommandSender::ProcessInvokeResponseIB(InvokeResponseIB::Parser & aIn
350
370
StatusIB statusIB;
351
371
352
372
{
353
- bool commandRefExpected = (mFinishedCommandCount > 1 );
354
- bool hasDataResponse = false ;
373
+ bool hasDataResponse = false ;
355
374
TLV::TLVReader commandDataReader;
356
375
Optional<uint16_t > commandRef;
376
+ bool commandRefExpected = mpPendingResponseTracker && (mpPendingResponseTracker->Count () > 1 );
357
377
358
378
CommandStatusIB::Parser commandStatus;
359
379
err = aInvokeResponse.GetStatus (&commandStatus);
@@ -409,6 +429,27 @@ CHIP_ERROR CommandSender::ProcessInvokeResponseIB(InvokeResponseIB::Parser & aIn
409
429
}
410
430
ReturnErrorOnFailure (err);
411
431
432
+ if (commandRef.HasValue () && mpPendingResponseTracker != nullptr )
433
+ {
434
+ err = mpPendingResponseTracker->Remove (commandRef.Value ());
435
+ if (err != CHIP_NO_ERROR)
436
+ {
437
+ // This can happen for two reasons:
438
+ // 1. The current InvokeResponse is a duplicate (based on its commandRef).
439
+ // 2. The current InvokeResponse is for a request we never sent (based on its commandRef).
440
+ // Used when logging errors related to server violating spec.
441
+ [[maybe_unused]] ScopedNodeId remoteScopedNode;
442
+ if (mExchangeCtx .Get ()->HasSessionHandle ())
443
+ {
444
+ remoteScopedNode = mExchangeCtx .Get ()->GetSessionHandle ()->GetPeer ();
445
+ }
446
+ ChipLogError (DataManagement,
447
+ " Received Unexpected Response from remote node " ChipLogFormatScopedNodeId " , commandRef=%u" ,
448
+ ChipLogValueScopedNodeId (remoteScopedNode), commandRef.Value ());
449
+ return err;
450
+ }
451
+ }
452
+
412
453
// When using ExtendableCallbacks, we are adhering to a different API contract where path
413
454
// specific errors are sent to the OnResponse callback. For more information on the history
414
455
// of this issue please see https://github.com/project-chip/connectedhomeip/issues/30991
@@ -430,17 +471,19 @@ CHIP_ERROR CommandSender::ProcessInvokeResponseIB(InvokeResponseIB::Parser & aIn
430
471
431
472
CHIP_ERROR CommandSender::SetCommandSenderConfig (CommandSender::ConfigParameters & aConfigParams)
432
473
{
433
- #if CHIP_CONFIG_SENDING_BATCH_COMMANDS_ENABLED
434
474
VerifyOrReturnError (mState == State::Idle, CHIP_ERROR_INCORRECT_STATE);
435
475
VerifyOrReturnError (aConfigParams.remoteMaxPathsPerInvoke > 0 , CHIP_ERROR_INVALID_ARGUMENT);
476
+ if (mpPendingResponseTracker != nullptr )
477
+ {
436
478
437
- mRemoteMaxPathsPerInvoke = aConfigParams.remoteMaxPathsPerInvoke ;
438
- mBatchCommandsEnabled = (aConfigParams.remoteMaxPathsPerInvoke > 1 );
439
- return CHIP_NO_ERROR;
440
- #else
441
- VerifyOrReturnError (aConfigParams.remoteMaxPathsPerInvoke == 1 , CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE);
479
+ mRemoteMaxPathsPerInvoke = aConfigParams.remoteMaxPathsPerInvoke ;
480
+ mBatchCommandsEnabled = (aConfigParams.remoteMaxPathsPerInvoke > 1 );
481
+ }
482
+ else
483
+ {
484
+ VerifyOrReturnError (aConfigParams.remoteMaxPathsPerInvoke == 1 , CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE);
485
+ }
442
486
return CHIP_NO_ERROR;
443
- #endif
444
487
}
445
488
446
489
CHIP_ERROR CommandSender::PrepareCommand (const CommandPathParams & aCommandPathParams,
@@ -453,12 +496,19 @@ CHIP_ERROR CommandSender::PrepareCommand(const CommandPathParams & aCommandPathP
453
496
//
454
497
bool canAddAnotherCommand = (mState == State::AddedCommand && mBatchCommandsEnabled && mUseExtendableCallback );
455
498
VerifyOrReturnError (mState == State::Idle || canAddAnotherCommand, CHIP_ERROR_INCORRECT_STATE);
456
- VerifyOrReturnError (mFinishedCommandCount < mRemoteMaxPathsPerInvoke , CHIP_ERROR_MAXIMUM_PATHS_PER_INVOKE_EXCEEDED);
499
+
500
+ if (mpPendingResponseTracker != nullptr )
501
+ {
502
+ size_t pendingCount = mpPendingResponseTracker->Count ();
503
+ VerifyOrReturnError (pendingCount < mRemoteMaxPathsPerInvoke , CHIP_ERROR_MAXIMUM_PATHS_PER_INVOKE_EXCEEDED);
504
+ }
457
505
458
506
if (mBatchCommandsEnabled )
459
507
{
508
+ VerifyOrReturnError (mpPendingResponseTracker != nullptr , CHIP_ERROR_INCORRECT_STATE);
460
509
VerifyOrReturnError (aPrepareCommandParams.commandRef .HasValue (), CHIP_ERROR_INVALID_ARGUMENT);
461
- VerifyOrReturnError (aPrepareCommandParams.commandRef .Value () == mFinishedCommandCount , CHIP_ERROR_INVALID_ARGUMENT);
510
+ uint16_t commandRef = aPrepareCommandParams.commandRef .Value ();
511
+ VerifyOrReturnError (!mpPendingResponseTracker->IsTracked (commandRef), CHIP_ERROR_INVALID_ARGUMENT);
462
512
}
463
513
464
514
InvokeRequests::Builder & invokeRequests = mInvokeRequestBuilder .GetInvokeRequests ();
@@ -482,8 +532,10 @@ CHIP_ERROR CommandSender::FinishCommand(FinishCommandParameters & aFinishCommand
482
532
{
483
533
if (mBatchCommandsEnabled )
484
534
{
535
+ VerifyOrReturnError (mpPendingResponseTracker != nullptr , CHIP_ERROR_INCORRECT_STATE);
485
536
VerifyOrReturnError (aFinishCommandParams.commandRef .HasValue (), CHIP_ERROR_INVALID_ARGUMENT);
486
- VerifyOrReturnError (aFinishCommandParams.commandRef .Value () == mFinishedCommandCount , CHIP_ERROR_INVALID_ARGUMENT);
537
+ uint16_t commandRef = aFinishCommandParams.commandRef .Value ();
538
+ VerifyOrReturnError (!mpPendingResponseTracker->IsTracked (commandRef), CHIP_ERROR_INVALID_ARGUMENT);
487
539
}
488
540
489
541
return FinishCommandInternal (aFinishCommandParams);
@@ -511,7 +563,10 @@ CHIP_ERROR CommandSender::FinishCommandInternal(FinishCommandParameters & aFinis
511
563
512
564
MoveToState (State::AddedCommand);
513
565
514
- mFinishedCommandCount ++;
566
+ if (mpPendingResponseTracker && aFinishCommandParams.commandRef .HasValue ())
567
+ {
568
+ mpPendingResponseTracker->Add (aFinishCommandParams.commandRef .Value ());
569
+ }
515
570
516
571
if (aFinishCommandParams.timedInvokeTimeoutMs .HasValue ())
517
572
{
0 commit comments