Skip to content

Commit 2c60cd0

Browse files
[ICD]Add a ShortIdleModeDuration to the ICDConfigurationData. (project-chip#42115)
* Add a ShortIdleModeDuration To the ICDConfigurationData. This is used for LIT capable device operating in SIT. * Add new ICD unit test to validate the ShortIdleTimeDuration configuration and behaviour. fix typos, and wrong function replaces in ICDManager test * Fix sl_short_idle_mode_duration_s default arg value. Use shortIdleInterval silabs and linux icd-lit-app. Add option to configure shortIdleInterval with Linux shell and in Linux AppMain * Apply clang-tidy [atoi -> stroul] to the now analysed Linux/Options.cpp. Restore active and Idle duration after new ICD tests * fixup * correct ICD report interval negotiation to keep the current behaviour. Cleanup the code and add comments to explain the logic * apply copilot suggestions
1 parent d377679 commit 2c60cd0

File tree

18 files changed

+369
-89
lines changed

18 files changed

+369
-89
lines changed

examples/lit-icd-app/linux/include/CHIPProjectAppConfig.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,6 @@
3838

3939
// ICD configurations
4040
#define CHIP_CONFIG_ICD_IDLE_MODE_DURATION_SEC 3600
41+
#define CHIP_CONFIG_ICD_SHORT_IDLE_MODE_DURATION_SEC 60
4142
#define CHIP_CONFIG_ICD_ACTIVE_MODE_DURATION_MS 10000
4243
#define CHIP_CONFIG_ICD_ACTIVE_MODE_THRESHOLD_MS 5000

examples/lit-icd-app/silabs/openthread.gni

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,6 @@ sl_transport_active_interval_ms = 1000 # 1000ms Active Polling Interval
4141

4242
# ICD Matter Configuration flags
4343
sl_idle_mode_duration_s = 3600 # 60min Idle Mode Duration
44+
sl_short_idle_mode_duration_s = 60 # 1min Short Idle Mode Duration
4445
sl_active_mode_duration_ms = 0 # 0 Active Mode Duration
4546
sl_active_mode_threshold_ms = 5000 # 5s Active Mode Threshold

examples/platform/linux/AppMain.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -729,10 +729,19 @@ int ChipLinuxAppInit(int argc, char * const argv[], OptionSet * customOptions,
729729

730730
#if CHIP_CONFIG_ENABLE_ICD_SERVER
731731
if (LinuxDeviceOptions::GetInstance().icdActiveModeDurationMs.HasValue() ||
732-
LinuxDeviceOptions::GetInstance().icdIdleModeDurationMs.HasValue())
732+
LinuxDeviceOptions::GetInstance().icdIdleModeDurationMs.HasValue() ||
733+
LinuxDeviceOptions::GetInstance().shortIdleModeDurationS.has_value())
733734
{
734-
err = Server::GetInstance().GetICDManager().SetModeDurations(LinuxDeviceOptions::GetInstance().icdActiveModeDurationMs,
735-
LinuxDeviceOptions::GetInstance().icdIdleModeDurationMs);
735+
// Convert icdIdleModeDurationMs to seconds for SetModeDurations api
736+
std::optional<System::Clock::Seconds32> tmpIdleModeDuration = std::nullopt;
737+
if (LinuxDeviceOptions::GetInstance().icdIdleModeDurationMs.HasValue())
738+
tmpIdleModeDuration = std::chrono::duration_cast<System::Clock::Seconds32>(
739+
LinuxDeviceOptions::GetInstance().icdIdleModeDurationMs.Value());
740+
741+
err = Server::GetInstance().GetICDManager().SetModeDurations(
742+
LinuxDeviceOptions::GetInstance().icdActiveModeDurationMs.std_optional(), tmpIdleModeDuration,
743+
LinuxDeviceOptions::GetInstance().shortIdleModeDurationS);
744+
736745
if (err != CHIP_NO_ERROR)
737746
{
738747
ChipLogError(NotSpecified, "Invalid arguments to set ICD mode durations");

examples/platform/linux/Options.cpp

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ enum
144144
#if CHIP_CONFIG_ENABLE_ICD_SERVER
145145
kDeviceOption_icdActiveModeDurationMs,
146146
kDeviceOption_icdIdleModeDuration,
147+
kDeviceOption_icdShortIdleModeDuration,
147148
#endif
148149
#if ENABLE_CAMERA_SERVER
149150
kDeviceOption_Camera_DeferredOffer,
@@ -247,6 +248,7 @@ OptionDef sDeviceOptionDefs[] = {
247248
#if CHIP_CONFIG_ENABLE_ICD_SERVER
248249
{ "icdActiveModeDurationMs", kArgumentRequired, kDeviceOption_icdActiveModeDurationMs },
249250
{ "icdIdleModeDuration", kArgumentRequired, kDeviceOption_icdIdleModeDuration },
251+
{ "icdShortIdleModeDuration", kArgumentRequired, kDeviceOption_icdShortIdleModeDuration },
250252
#endif
251253
#if ENABLE_CAMERA_SERVER
252254
{ "camera-deferred-offer", kNoArgument, kDeviceOption_Camera_DeferredOffer },
@@ -681,18 +683,18 @@ bool HandleOption(const char * aProgram, OptionSet * aOptions, int aIdentifier,
681683

682684
#if CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE || CHIP_DEVICE_ENABLE_PORT_PARAMS
683685
case kDeviceOption_SecuredDevicePort:
684-
LinuxDeviceOptions::GetInstance().securedDevicePort = static_cast<uint16_t>(atoi(aValue));
686+
LinuxDeviceOptions::GetInstance().securedDevicePort = static_cast<uint16_t>(strtoul(aValue, nullptr, 0));
685687
break;
686688

687689
case kDeviceOption_UnsecuredCommissionerPort:
688-
LinuxDeviceOptions::GetInstance().unsecuredCommissionerPort = static_cast<uint16_t>(atoi(aValue));
690+
LinuxDeviceOptions::GetInstance().unsecuredCommissionerPort = static_cast<uint16_t>(strtoul(aValue, nullptr, 0));
689691
break;
690692

691693
#endif
692694

693695
#if CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE
694696
case kDeviceOption_SecuredCommissionerPort:
695-
LinuxDeviceOptions::GetInstance().securedCommissionerPort = static_cast<uint16_t>(atoi(aValue));
697+
LinuxDeviceOptions::GetInstance().securedCommissionerPort = static_cast<uint16_t>(strtoul(aValue, nullptr, 0));
696698
break;
697699
case kCommissionerOption_FabricID:
698700
LinuxDeviceOptions::GetInstance().commissionerFabricId = static_cast<chip::FabricId>(strtoull(aValue, nullptr, 0));
@@ -717,21 +719,21 @@ bool HandleOption(const char * aProgram, OptionSet * aOptions, int aIdentifier,
717719

718720
case kDeviceOption_InterfaceId:
719721
LinuxDeviceOptions::GetInstance().interfaceId =
720-
Inet::InterfaceId(static_cast<chip::Inet::InterfaceId::PlatformType>(atoi(aValue)));
722+
Inet::InterfaceId(static_cast<chip::Inet::InterfaceId::PlatformType>(strtoul(aValue, nullptr, 0)));
721723
break;
722724

723725
#if CHIP_CONFIG_TRANSPORT_TRACE_ENABLED
724726
case kDeviceOption_TraceFile:
725727
LinuxDeviceOptions::GetInstance().traceStreamFilename.SetValue(std::string{ aValue });
726728
break;
727729
case kDeviceOption_TraceLog:
728-
if (atoi(aValue) != 0)
730+
if (strtoul(aValue, nullptr, 0) != 0)
729731
{
730732
LinuxDeviceOptions::GetInstance().traceStreamToLogEnabled = true;
731733
}
732734
break;
733735
case kDeviceOption_TraceDecode:
734-
if (atoi(aValue) != 0)
736+
if (strtoul(aValue, nullptr, 0) != 0)
735737
{
736738
LinuxDeviceOptions::GetInstance().traceStreamDecodeEnabled = true;
737739
}
@@ -807,15 +809,16 @@ bool HandleOption(const char * aProgram, OptionSet * aOptions, int aIdentifier,
807809
break;
808810
#if defined(PW_RPC_ENABLED)
809811
case kOptionRpcServerPort:
810-
LinuxDeviceOptions::GetInstance().rpcServerPort = static_cast<uint16_t>(atoi(aValue));
812+
LinuxDeviceOptions::GetInstance().rpcServerPort = static_cast<uint16_t>(strtoul(aValue, nullptr, 0));
811813
break;
812814
#endif
813815
#if CONFIG_BUILD_FOR_HOST_UNIT_TEST
814816
case kDeviceOption_SubscriptionCapacity:
815-
LinuxDeviceOptions::GetInstance().subscriptionCapacity = static_cast<int32_t>(atoi(aValue));
817+
LinuxDeviceOptions::GetInstance().subscriptionCapacity = static_cast<int32_t>(strtoul(aValue, nullptr, 0));
816818
break;
817819
case kDeviceOption_SubscriptionResumptionRetryIntervalSec:
818-
LinuxDeviceOptions::GetInstance().subscriptionResumptionRetryIntervalSec = static_cast<int32_t>(atoi(aValue));
820+
LinuxDeviceOptions::GetInstance().subscriptionResumptionRetryIntervalSec =
821+
static_cast<int32_t>(strtoul(aValue, nullptr, 0));
819822
break;
820823
case kDeviceOption_IdleRetransmitTimeout: {
821824
auto mrpConfig = GetLocalMRPConfig().ValueOr(GetDefaultMRPConfig());
@@ -881,12 +884,12 @@ bool HandleOption(const char * aProgram, OptionSet * aOptions, int aIdentifier,
881884
#endif
882885
#if CHIP_CONFIG_TERMS_AND_CONDITIONS_REQUIRED
883886
case kDeviceOption_TermsAndConditions_Version: {
884-
LinuxDeviceOptions::GetInstance().tcVersion.SetValue(static_cast<uint16_t>(atoi(aValue)));
887+
LinuxDeviceOptions::GetInstance().tcVersion.SetValue(static_cast<uint16_t>(strtoul(aValue, nullptr, 0)));
885888
break;
886889
}
887890

888891
case kDeviceOption_TermsAndConditions_Required: {
889-
LinuxDeviceOptions::GetInstance().tcRequired.SetValue(static_cast<uint16_t>(atoi(aValue)));
892+
LinuxDeviceOptions::GetInstance().tcRequired.SetValue(static_cast<uint16_t>(strtoul(aValue, nullptr, 0)));
890893
break;
891894
}
892895
#endif
@@ -913,11 +916,35 @@ bool HandleOption(const char * aProgram, OptionSet * aOptions, int aIdentifier,
913916
}
914917
else
915918
{
916-
// Covert from seconds to mini seconds
919+
// Covert from seconds to milli seconds
917920
LinuxDeviceOptions::GetInstance().icdIdleModeDurationMs.SetValue(chip::System::Clock::Milliseconds32(value * 1000));
918921
}
919922
break;
920923
}
924+
case kDeviceOption_icdShortIdleModeDuration: {
925+
uint32_t value = static_cast<uint32_t>(strtoul(aValue, nullptr, 0));
926+
if ((value < 1) || (value > 86400))
927+
{
928+
PrintArgError("%s: invalid value specified for icdShortIdleModeDuration: %s\n", aProgram, aValue);
929+
retval = false;
930+
}
931+
else
932+
{
933+
if (LinuxDeviceOptions::GetInstance().icdIdleModeDurationMs.HasValue() &&
934+
(value > std::chrono::duration_cast<System::Clock::Seconds32>(
935+
LinuxDeviceOptions::GetInstance().icdIdleModeDurationMs.Value())
936+
.count()))
937+
{
938+
PrintArgError("%s: icdShortIdleModeDuration value (%s) must be <= icdIdleModeDuration\n", aProgram, aValue);
939+
retval = false;
940+
}
941+
else
942+
{
943+
LinuxDeviceOptions::GetInstance().shortIdleModeDurationS = chip::System::Clock::Seconds32(value);
944+
}
945+
}
946+
break;
947+
}
921948
#endif
922949
#if ENABLE_CAMERA_SERVER
923950
case kDeviceOption_Camera_DeferredOffer: {

examples/platform/linux/Options.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#pragma once
2626

2727
#include <cstdint>
28+
#include <optional>
2829
#include <string>
2930
#include <vector>
3031

@@ -108,6 +109,7 @@ struct LinuxDeviceOptions
108109
#if CHIP_CONFIG_ENABLE_ICD_SERVER
109110
chip::Optional<chip::System::Clock::Milliseconds32> icdActiveModeDurationMs;
110111
chip::Optional<chip::System::Clock::Milliseconds32> icdIdleModeDurationMs;
112+
std::optional<chip::System::Clock::Seconds32> shortIdleModeDurationS;
111113
#endif
112114
chip::Optional<std::string> vendorName;
113115
chip::Optional<std::string> productName;

examples/platform/silabs/MatterConfig.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ extern "C" void sl_matter_em4_check(uint32_t expected_idle_time_ms)
403403
{
404404
if (chip::ICDConfigurationData::GetInstance().GetICDMode() == chip::ICDConfigurationData::ICDMode::LIT)
405405
{
406-
uint32_t idleDuration_seconds = chip::ICDConfigurationData::GetInstance().GetIdleModeDuration().count();
406+
uint32_t idleDuration_seconds = chip::ICDConfigurationData::GetInstance().GetModeBasedIdleModeDuration().count();
407407
uint32_t threshold_ms = idleDuration_seconds * SL_EM4_THRESHOLD_PERCENTAGE * 10;
408408
// Since the sleep timer will never match the actual idle time (hardware latency, etc), we set a threshold
409409
// Multiply by 10 to converts seconds into milliseconds (e.g. 90% of 1000sec in ms is 1000*90*10 = 900000ms)

src/app/ReadHandler.cpp

Lines changed: 25 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -773,52 +773,40 @@ CHIP_ERROR ReadHandler::ProcessSubscribeRequest(System::PacketBufferHandle && aP
773773

774774
#if CHIP_CONFIG_ENABLE_ICD_SERVER
775775

776-
// Default behavior for ICDs where the wanted MaxInterval for a subscription is the IdleModeDuration
777-
// defined in the ICD Management Cluster.
778-
// Behavior can be changed with the OnSubscriptionRequested function defined in the application callbacks
776+
// To optimize ICD sleep/idle cycles, it is preferred to synchronize the subscription report interval
777+
// with the IdleModeDuration defined in the ICD Management Cluster.
778+
// We aim to use IdleModeDuration, but must respect the Min Interval Floor and Max Interval Ceiling.
779+
// Note: This behavior can be overridden using the OnSubscriptionRequested callback defined
780+
// in the application.
779781

780-
// Default Behavior Steps :
781-
// If MinInterval > IdleModeDuration, try to set the MaxInterval to the first interval of IdleModeDurations above the
782-
// MinInterval.
783-
// If the next interval is greater than the MaxIntervalCeiling, use the MaxIntervalCeiling.
784-
// Otherwise, use IdleModeDuration as MaxInterval
782+
// Per spec and static checks, GetIdleModeDuration max value fits a uint16_t.
783+
// The final selected interval must be a uint16_t but we use a uint32 here during calculation to prevent overflows.
784+
uint32_t preferredMaxInterval = ICDConfigurationData::GetInstance().GetIdleModeDuration().count();
785+
// Determine the max interval ceiling between GetPublisherSelectedIntervalLimit
786+
// and the subscriber-requested MaxInterval.
787+
uint16_t maxIntervalCeiling = std::max(GetPublisherSelectedIntervalLimit(), mMaxInterval);
785788

786-
uint32_t decidedMaxInterval = ICDConfigurationData::GetInstance().GetIdleModeDuration().count();
787-
788-
// Check if the PublisherSelectedIntervalLimit is 0. If so, set decidedMaxInterval to MaxIntervalCeiling
789-
if (decidedMaxInterval == 0)
789+
// Spec doesn't allow IdleModeDuration of 0 but make sure here to prevent div0
790+
if (preferredMaxInterval == 0)
790791
{
791-
decidedMaxInterval = mMaxInterval;
792+
preferredMaxInterval = maxIntervalCeiling;
792793
}
793-
794-
// If requestedMinInterval is greater than the IdleTimeInterval, select next active up time as max interval
795-
if (mMinIntervalFloorSeconds > decidedMaxInterval)
794+
else
796795
{
797-
uint16_t ratio = mMinIntervalFloorSeconds / static_cast<uint16_t>(decidedMaxInterval);
798-
if (mMinIntervalFloorSeconds % decidedMaxInterval)
796+
// Must respect the minimal interval floor
797+
if (preferredMaxInterval < mMinIntervalFloorSeconds)
799798
{
800-
ratio++;
799+
// Round up to the nearest multiple of IdleModeDuration that is >= mMinIntervalFloorSeconds.
800+
const uint32_t remainder = mMinIntervalFloorSeconds % preferredMaxInterval;
801+
preferredMaxInterval =
802+
remainder == 0 ? mMinIntervalFloorSeconds : (mMinIntervalFloorSeconds + (preferredMaxInterval - remainder));
801803
}
802804

803-
decidedMaxInterval *= ratio;
804-
}
805-
806-
// Verify that decidedMaxInterval is an acceptable value (overflow)
807-
if (decidedMaxInterval > System::Clock::Seconds16::max().count())
808-
{
809-
decidedMaxInterval = System::Clock::Seconds16::max().count();
805+
// Use the smallest interval between preferredMaxInterval and maxIntervalCeiling.
806+
// This also ensure that no overflow occurs when casting to uint16_t below
807+
preferredMaxInterval = std::min<uint32_t>(preferredMaxInterval, maxIntervalCeiling);
810808
}
811-
812-
// Verify that the decidedMaxInterval respects MAX(GetPublisherSelectedIntervalLimit(), MaxIntervalCeiling)
813-
uint16_t maximumMaxInterval = std::max(GetPublisherSelectedIntervalLimit(), mMaxInterval);
814-
if (decidedMaxInterval > maximumMaxInterval)
815-
{
816-
decidedMaxInterval = maximumMaxInterval;
817-
}
818-
819-
// Set max interval of the subscription
820-
mMaxInterval = static_cast<uint16_t>(decidedMaxInterval);
821-
809+
mMaxInterval = static_cast<uint16_t>(preferredMaxInterval);
822810
#endif // CHIP_CONFIG_ENABLE_ICD_SERVER
823811

824812
//

src/app/icd/server/ICDConfigurationData.cpp

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,20 +63,62 @@ CHIP_ERROR ICDConfigurationData::SetModeDurations(Optional<System::Clock::Millis
6363
VerifyOrReturnError(activeModeDuration.HasValue() || idleModeDuration.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
6464

6565
// Convert idleModeDuration to seconds for the correct precision
66-
System::Clock::Seconds32 tmpIdleModeDuration = idleModeDuration.HasValue()
67-
? std::chrono::duration_cast<System::Clock::Seconds32>(idleModeDuration.Value())
68-
: mIdleModeDuration;
66+
std::optional<System::Clock::Seconds32> tmpIdleModeDuration = std::nullopt;
67+
if (idleModeDuration.HasValue())
68+
tmpIdleModeDuration = std::chrono::duration_cast<System::Clock::Seconds32>(idleModeDuration.Value());
6969

70-
System::Clock::Milliseconds32 tmpActiveModeDuration = activeModeDuration.ValueOr(mActiveModeDuration);
70+
// Here we set shortIdleModeDuration the same as idleModeDuration to maintain the api previous behaviour
71+
return SetModeDurations(activeModeDuration.std_optional(), tmpIdleModeDuration, tmpIdleModeDuration);
72+
}
73+
74+
CHIP_ERROR ICDConfigurationData::SetModeDurations(std::optional<System::Clock::Milliseconds32> activeModeDuration,
75+
std::optional<System::Clock::Seconds32> idleModeDuration,
76+
std::optional<System::Clock::Seconds32> shortIdleModeDuration)
77+
{
78+
VerifyOrReturnError(activeModeDuration.has_value() || idleModeDuration.has_value() || shortIdleModeDuration.has_value(),
79+
CHIP_ERROR_INVALID_ARGUMENT);
80+
81+
System::Clock::Milliseconds32 tmpActiveModeDuration = activeModeDuration.value_or(mActiveModeDuration);
82+
System::Clock::Seconds32 tmpIdleModeDuration = idleModeDuration.value_or(mIdleModeDuration);
7183

7284
VerifyOrReturnError(tmpActiveModeDuration <= tmpIdleModeDuration, CHIP_ERROR_INVALID_ARGUMENT);
7385
VerifyOrReturnError(tmpIdleModeDuration <= kMaxIdleModeDuration, CHIP_ERROR_INVALID_ARGUMENT);
7486
VerifyOrReturnError(tmpIdleModeDuration >= kMinIdleModeDuration, CHIP_ERROR_INVALID_ARGUMENT);
7587

76-
mIdleModeDuration = tmpIdleModeDuration;
77-
mActiveModeDuration = tmpActiveModeDuration;
88+
System::Clock::Seconds32 tmpShortIdleModeDuration;
89+
if (shortIdleModeDuration.has_value())
90+
{
91+
// shortIdleModeDuration was provided, it shall be lesser than or equal to idleModeDuration.
92+
tmpShortIdleModeDuration = shortIdleModeDuration.value();
93+
}
94+
else
95+
{
96+
// shortIdleModeDuration was not provided. To ensure correct mode transitions and device compliance,
97+
// shortIdleModeDuration must not exceed idleModeDuration, so we use the smaller of the current shortIdleModeDuration
98+
// and the resultant idleModeDuration.
99+
// This approach overwrites a previous valid shortIdleModeDuration rather than erroring the call but maintains the previous
100+
// api behavior.
101+
tmpShortIdleModeDuration = std::min(mShortIdleModeDuration, tmpIdleModeDuration);
102+
}
103+
104+
VerifyOrReturnError(tmpShortIdleModeDuration <= tmpIdleModeDuration, CHIP_ERROR_INVALID_ARGUMENT);
105+
106+
mIdleModeDuration = tmpIdleModeDuration;
107+
mActiveModeDuration = tmpActiveModeDuration;
108+
mShortIdleModeDuration = tmpShortIdleModeDuration;
78109

79110
return CHIP_NO_ERROR;
80111
}
81112

113+
bool ICDConfigurationData::ShouldUseShortIdle()
114+
{
115+
VerifyOrReturnValue(mShortIdleModeDuration < mIdleModeDuration, false);
116+
return (mFeatureMap.Has(app::Clusters::IcdManagement::Feature::kLongIdleTimeSupport) && mICDMode == ICDMode::SIT);
117+
}
118+
119+
System::Clock::Seconds32 ICDConfigurationData::GetModeBasedIdleModeDuration()
120+
{
121+
return ShouldUseShortIdle() ? mShortIdleModeDuration : mIdleModeDuration;
122+
}
123+
82124
} // namespace chip

0 commit comments

Comments
 (0)