mirror of
https://github.com/espressif/openthread.git
synced 2026-07-30 15:47:46 +00:00
[mle] update Avd trickle timer interval based on router neighbor count (#9307)
This commit updates the `TrickleTimer` to allow `IntervalMax` and `IntervalMin` to be changed while the timer is running. In particular, when `IntervalMax` is changed to a value that is shorter than the current interval being used by the timer, the timer will adapt the new shorter interval and may fire immediately. A unit test `test_trickle_timer` has been added to validate the behavior of `TrickleTimer` in detail. All different scenarios where `IntervalMax` or `IntervalMin` are changed are covered by the unit test. The new mechanism to change the trickle timer `IntervalMax` is used to update the MLE Advertisement trickle timer. The `IntervalMax` is determined based on the number of router neighbors of the device with link quality 2 or better. If the device has fewer router neighbors, it will use a shorter `IntervalMax`. As new links are established with routers, the `IntervalMax` is recalculated and updated on the Advertisement trickle timer. This commit also adds a new test `test-024-mle-adv-imax-change.py` to validate updates to the `IntervalMax` value based on the number of router neighbors. Co-authored-by: David Smith <[email protected]>
This commit is contained in:
co-authored by
David Smith
parent
9b0d3db228
commit
650e30f81c
@@ -53,7 +53,7 @@ extern "C" {
|
||||
* @note This number versions both OpenThread platform and user APIs.
|
||||
*
|
||||
*/
|
||||
#define OPENTHREAD_API_VERSION (348)
|
||||
#define OPENTHREAD_API_VERSION (349)
|
||||
|
||||
/**
|
||||
* @addtogroup api-instance
|
||||
|
||||
@@ -881,6 +881,16 @@ void otThreadGetRouterIdRange(otInstance *aInstance, uint8_t *aMinRouterId, uint
|
||||
*/
|
||||
otError otThreadSetRouterIdRange(otInstance *aInstance, uint8_t aMinRouterId, uint8_t aMaxRouterId);
|
||||
|
||||
/**
|
||||
* Gets the current Interval Max value used by Advertisement trickle timer.
|
||||
*
|
||||
* This API requires `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE`, and is intended for testing only.
|
||||
*
|
||||
* @returns The Interval Max of Advertisement trickle timer in milliseconds.
|
||||
*
|
||||
*/
|
||||
uint32_t otThreadGetAdvertisementTrickleIntervalMax(otInstance *aInstance);
|
||||
|
||||
/**
|
||||
* Indicates whether or not a Router ID is currently allocated.
|
||||
*
|
||||
|
||||
@@ -4135,6 +4135,23 @@ exit:
|
||||
#endif
|
||||
#endif // OPENTHREAD_FTD
|
||||
|
||||
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||
/**
|
||||
* @cli mleadvimax
|
||||
* @code
|
||||
* mleadvimax
|
||||
* 12000
|
||||
* Done
|
||||
* @endcode
|
||||
* @par api_copy
|
||||
* #otThreadGetAdvertisementTrickleIntervalMax
|
||||
*/
|
||||
template <> otError Interpreter::Process<Cmd("mleadvimax")>(Arg aArgs[])
|
||||
{
|
||||
return ProcessGet(aArgs, otThreadGetAdvertisementTrickleIntervalMax);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||
/**
|
||||
* @cli mliid
|
||||
@@ -8046,6 +8063,9 @@ otError Interpreter::ProcessCommand(Arg aArgs[])
|
||||
#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD
|
||||
CmdEntry("meshdiag"),
|
||||
#endif
|
||||
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||
CmdEntry("mleadvimax"),
|
||||
#endif
|
||||
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||
CmdEntry("mliid"),
|
||||
#endif
|
||||
|
||||
@@ -357,6 +357,7 @@ void otThreadSetDiscoveryRequestCallback(otInstance *aInsta
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||
|
||||
void otThreadSendAddressNotification(otInstance *aInstance,
|
||||
otIp6Address *aDestination,
|
||||
otIp6Address *aTarget,
|
||||
@@ -386,9 +387,7 @@ void otThreadSetThreadVersionCheckEnabled(otInstance *aInstance, bool aEnabled)
|
||||
{
|
||||
AsCoreType(aInstance).Get<Mle::MleRouter>().SetThreadVersionCheckEnabled(aEnabled);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||
void otThreadGetRouterIdRange(otInstance *aInstance, uint8_t *aMinRouterId, uint8_t *aMaxRouterId)
|
||||
{
|
||||
AssertPointerIsNotNull(aMinRouterId);
|
||||
@@ -401,7 +400,13 @@ otError otThreadSetRouterIdRange(otInstance *aInstance, uint8_t aMinRouterId, ui
|
||||
{
|
||||
return AsCoreType(aInstance).Get<RouterTable>().SetRouterIdRange(aMinRouterId, aMaxRouterId);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t otThreadGetAdvertisementTrickleIntervalMax(otInstance *aInstance)
|
||||
{
|
||||
return AsCoreType(aInstance).Get<Mle::MleRouter>().GetAdvertisementTrickleIntervalMax();
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||
|
||||
bool otThreadIsRouterIdAllocated(otInstance *aInstance, uint8_t aRouterId)
|
||||
{
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
#include "common/code_utils.hpp"
|
||||
#include "common/debug.hpp"
|
||||
#include "common/num_utils.hpp"
|
||||
#include "common/random.hpp"
|
||||
|
||||
namespace ot {
|
||||
@@ -53,6 +54,158 @@ TrickleTimer::TrickleTimer(Instance &aInstance, Handler aHandler)
|
||||
{
|
||||
}
|
||||
|
||||
TimeMilli TrickleTimer::GetStartTimeOfCurrentInterval(void) const
|
||||
{
|
||||
// Determines and returns the start time of the current
|
||||
// interval.
|
||||
|
||||
TimeMilli startTime = TimerMilli::GetFireTime();
|
||||
|
||||
if (mMode == kModePlainTimer)
|
||||
{
|
||||
startTime -= mInterval;
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
switch (mPhase)
|
||||
{
|
||||
case kBeforeRandomTime:
|
||||
startTime -= mTimeInInterval;
|
||||
break;
|
||||
|
||||
case kAfterRandomTime:
|
||||
startTime -= mInterval;
|
||||
break;
|
||||
}
|
||||
|
||||
exit:
|
||||
return startTime;
|
||||
}
|
||||
|
||||
void TrickleTimer::SetIntervalMin(uint32_t aIntervalMin)
|
||||
{
|
||||
VerifyOrExit(IsRunning());
|
||||
|
||||
mIntervalMin = aIntervalMin;
|
||||
|
||||
if (mIntervalMax < mIntervalMin)
|
||||
{
|
||||
SetIntervalMax(mIntervalMin);
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void TrickleTimer::SetIntervalMax(uint32_t aIntervalMax)
|
||||
{
|
||||
TimeMilli endOfInterval;
|
||||
|
||||
VerifyOrExit(IsRunning());
|
||||
|
||||
aIntervalMax = Max(mIntervalMin, aIntervalMax);
|
||||
VerifyOrExit(aIntervalMax != mIntervalMax);
|
||||
|
||||
mIntervalMax = aIntervalMax;
|
||||
|
||||
// If the new `aIntervalMax` is greater than the current
|
||||
// `mInterval`, no action is needed. The new `aIntervalMax` will be
|
||||
// used as the `mInterval` grows.
|
||||
|
||||
VerifyOrExit(mIntervalMax < mInterval);
|
||||
|
||||
// Determine the end of the interval as if the new and shorter
|
||||
// `mIntervalMax` would have been used. The calculated time may
|
||||
// be in the past. In this case, `FireAt(endOfInterval)` will
|
||||
// cause the timer to fire immediately.
|
||||
|
||||
endOfInterval = GetStartTimeOfCurrentInterval() + mIntervalMax;
|
||||
|
||||
if (mMode == kModePlainTimer)
|
||||
{
|
||||
TimerMilli::FireAt(endOfInterval);
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
// Trickle mode possible scenarios:
|
||||
//
|
||||
// We are in `kBeforeRandomTime` phase.
|
||||
//
|
||||
// a) If the new `aIntervalMax < mTimeInInterval`:
|
||||
// - Reschedule the timer to fire at new `endOfInterval`
|
||||
// (which may fire immediately).
|
||||
// - Set `mTimeInInterval = aIntervalMax`
|
||||
// - Set `mInterval` to use the shorter `aIntervalMax`.
|
||||
//
|
||||
// |<---- mInterval ----------------^------------------>|
|
||||
// |<---- mTimeInInterval ----------^---->| |
|
||||
// |<---- aIntervalMax -------->| ^ | |
|
||||
// | | now | |
|
||||
//
|
||||
// b) If the new `aIntervalMax >= mTimeInInterval`:
|
||||
// - Keep timer unchanged to fire at `mTimeInInterval`
|
||||
// - Keep `mTimeInInterval` unchanged.
|
||||
// - Set `mInterval` to use the shorter `aIntervalMax`.
|
||||
//
|
||||
// |<---- mInterval ----------------^------------------>|
|
||||
// |<---- mTimeInInterval ----------^---->| | |
|
||||
// |<---- aIntervalMax -------------^--------->| |
|
||||
// | now | |
|
||||
//
|
||||
// We are in `kAfterRandomTime` phase.
|
||||
//
|
||||
// c) If the new `aIntervalMax < mTimeInInterval`:
|
||||
// - Act as if current interval is already finished.
|
||||
// - Reschedule the timer to fire at new `endOfInterval`
|
||||
// (which should fire immediately).
|
||||
// - Set `mInterval` to use the shorter `aIntervalMax`.
|
||||
// - The `mTimeInInterval` value does not matter as we
|
||||
// are in `kAfterRandomTime` phase. Keep it unchanged.
|
||||
//
|
||||
// |<---- mInterval ---------------------------^------->|
|
||||
// |<---- mTimeInInterval --------------->| ^ |
|
||||
// |<---- aIntervalMax -------->| | ^ |
|
||||
// | | | now |
|
||||
//
|
||||
// d) If the new `aIntervalMax >= mTimeInInterval`:
|
||||
// - Reschedule the timer to fire at new `endOfInterval`
|
||||
// - Set `mInterval` to use the shorter `aIntervalMax`.
|
||||
// - The `mTimeInInterval` value does not matter as we
|
||||
// are in `kAfterRandomTime` phase. Keep it unchanged.
|
||||
//
|
||||
// |<---- mInterval ---------------------------^------->|
|
||||
// |<---- mTimeInInterval --------------->| ^ | |
|
||||
// |<---- aIntervalMax ------------------------^-->| |
|
||||
// | | now | |
|
||||
|
||||
// In all cases we need to set `mInterval` to the new
|
||||
// shorter `aIntervalMax`.
|
||||
|
||||
mInterval = aIntervalMax;
|
||||
|
||||
switch (mPhase)
|
||||
{
|
||||
case kBeforeRandomTime:
|
||||
if (aIntervalMax < mTimeInInterval)
|
||||
{
|
||||
mTimeInInterval = aIntervalMax;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
OT_FALL_THROUGH;
|
||||
|
||||
case kAfterRandomTime:
|
||||
TimerMilli::FireAt(endOfInterval);
|
||||
break;
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void TrickleTimer::Start(Mode aMode, uint32_t aIntervalMin, uint32_t aIntervalMax, uint16_t aRedundancyConstant)
|
||||
{
|
||||
OT_ASSERT((aIntervalMax >= aIntervalMin) && (aIntervalMin > 0));
|
||||
|
||||
@@ -57,6 +57,8 @@ namespace ot {
|
||||
*/
|
||||
class TrickleTimer : public TimerMilli
|
||||
{
|
||||
friend class TrickleTimerTester;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Defines the modes of operation for the `TrickleTimer`.
|
||||
@@ -110,7 +112,45 @@ public:
|
||||
Mode GetMode(void) const { return mMode; }
|
||||
|
||||
/**
|
||||
* Starts the trickle timer.
|
||||
* Gets the interval min value of the trickle timer.
|
||||
*
|
||||
* @returns The interval min value in milliseconds.
|
||||
*
|
||||
*/
|
||||
uint32_t GetIntervalMin(void) const { return mIntervalMin; }
|
||||
|
||||
/**
|
||||
* Sets the interval min value of the trickle timer while timer is running.
|
||||
*
|
||||
* If @p aIntervalMin is smaller than the current `GetIntervalMax()` the interval max value is also updated to
|
||||
* the new @p aIntervalMin (as if `SetIntervalMax(aIntervalMin)` was called).
|
||||
*
|
||||
* @param[in] aIntervalMin The minimum interval in milliseconds.
|
||||
*
|
||||
*/
|
||||
void SetIntervalMin(uint32_t aIntervalMin);
|
||||
|
||||
/**
|
||||
* Gets the interval max value of the trickle timer.
|
||||
*
|
||||
* @returns The interval max value in milliseconds.
|
||||
*
|
||||
*/
|
||||
uint32_t GetIntervalMax(void) const { return mIntervalMax; }
|
||||
|
||||
/**
|
||||
* Sets the interval max value of the trickle timer while timer is running.
|
||||
*
|
||||
* If the given @p aIntervalMax is smaller than the current `GetIntervalMin()`, the interval min value will be
|
||||
* used instead.
|
||||
*
|
||||
* @param[in] aIntervalMax The maximum interval in milliseconds.
|
||||
*
|
||||
*/
|
||||
void SetIntervalMax(uint32_t aIntervalMax);
|
||||
|
||||
/**
|
||||
* This method starts the trickle timer.
|
||||
*
|
||||
* @param[in] aMode The operation mode of timer (trickle or plain periodic mode).
|
||||
* @param[in] aIntervalMin The minimum interval for the timer in milliseconds.
|
||||
@@ -162,6 +202,7 @@ private:
|
||||
void HandleTimer(void);
|
||||
void HandleEndOfTimeInInterval(void);
|
||||
void HandleEndOfInterval(void);
|
||||
TimeMilli GetStartTimeOfCurrentInterval(void) const;
|
||||
|
||||
// Shadow base class `TimerMilli` methods to ensure they are hidden.
|
||||
void StartAt(void) {}
|
||||
|
||||
@@ -340,12 +340,13 @@ void Mpl::AddBufferedMessage(Message &aMessage, uint16_t aSeedId, uint8_t aSeque
|
||||
Message *messageCopy = nullptr;
|
||||
Metadata metadata;
|
||||
uint8_t hopLimit = 0;
|
||||
uint8_t interval;
|
||||
|
||||
#if OPENTHREAD_CONFIG_MPL_DYNAMIC_INTERVAL_ENABLE
|
||||
// adjust the first MPL forward interval dynamically according to the network scale
|
||||
uint8_t interval = (kDataMessageInterval / Mle::kMaxRouters) * Get<RouterTable>().GetNeighborCount();
|
||||
interval = (kDataMessageInterval / Mle::kMaxRouters) * Get<RouterTable>().GetNeighborCount(kLinkQuality1);
|
||||
#else
|
||||
uint8_t interval = kDataMessageInterval;
|
||||
interval = kDataMessageInterval;
|
||||
#endif
|
||||
|
||||
VerifyOrExit(GetTimerExpirations() > 0);
|
||||
|
||||
@@ -482,14 +482,38 @@ exit:
|
||||
|
||||
void MleRouter::StopAdvertiseTrickleTimer(void) { mAdvertiseTrickleTimer.Stop(); }
|
||||
|
||||
uint32_t MleRouter::DetermineAdvertiseIntervalMax(void) const
|
||||
{
|
||||
uint32_t interval;
|
||||
|
||||
#if OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE
|
||||
interval = kAdvIntervalMaxLogRoutes;
|
||||
#else
|
||||
// Determine the interval based on the number of router neighbors
|
||||
// with link quality 2 or higher.
|
||||
|
||||
interval = (Get<RouterTable>().GetNeighborCount(kLinkQuality2) + 1) * kAdvIntervalNeighborMultiplier;
|
||||
interval = Clamp(interval, kAdvIntervalMaxLowerBound, kAdvIntervalMaxUpperBound);
|
||||
#endif
|
||||
|
||||
return interval;
|
||||
}
|
||||
|
||||
void MleRouter::UpdateAdvertiseInterval(void)
|
||||
{
|
||||
if (IsRouterOrLeader() && mAdvertiseTrickleTimer.IsRunning())
|
||||
{
|
||||
mAdvertiseTrickleTimer.SetIntervalMax(DetermineAdvertiseIntervalMax());
|
||||
}
|
||||
}
|
||||
|
||||
void MleRouter::ResetAdvertiseInterval(void)
|
||||
{
|
||||
VerifyOrExit(IsRouterOrLeader());
|
||||
|
||||
if (!mAdvertiseTrickleTimer.IsRunning())
|
||||
{
|
||||
mAdvertiseTrickleTimer.Start(TrickleTimer::kModeTrickle, Time::SecToMsec(kAdvertiseIntervalMin),
|
||||
Time::SecToMsec(kAdvertiseIntervalMax));
|
||||
mAdvertiseTrickleTimer.Start(TrickleTimer::kModeTrickle, kAdvIntervalMin, DetermineAdvertiseIntervalMax());
|
||||
}
|
||||
|
||||
mAdvertiseTrickleTimer.IndicateInconsistent();
|
||||
@@ -1312,7 +1336,7 @@ Error MleRouter::HandleAdvertisement(RxInfo &aRxInfo, uint16_t aSourceAddress, c
|
||||
VerifyOrExit(router != nullptr);
|
||||
|
||||
if (!router->IsStateValid() && !router->IsStateLinkRequest() &&
|
||||
(mRouterTable.GetNeighborCount() < mChildRouterLinks))
|
||||
(mRouterTable.GetNeighborCount(kLinkQuality1) < mChildRouterLinks))
|
||||
{
|
||||
InitNeighbor(*router, aRxInfo);
|
||||
router->SetState(Neighbor::kStateLinkRequest);
|
||||
|
||||
@@ -502,6 +502,14 @@ public:
|
||||
*/
|
||||
void ResetAdvertiseInterval(void);
|
||||
|
||||
/**
|
||||
* Updates the MLE Advertisement Trickle timer max interval (if timer is running).
|
||||
*
|
||||
* This is called when there is change in router table.
|
||||
*
|
||||
*/
|
||||
void UpdateAdvertiseInterval(void);
|
||||
|
||||
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
|
||||
/**
|
||||
* Generates an MLE Time Synchronization message.
|
||||
@@ -532,6 +540,7 @@ public:
|
||||
uint8_t GetMaxChildIpAddresses(void) const;
|
||||
|
||||
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||
|
||||
/**
|
||||
* Sets/restores the maximum number of IP addresses that each MTD child may register with this
|
||||
* device as parent.
|
||||
@@ -560,9 +569,27 @@ public:
|
||||
*
|
||||
*/
|
||||
void SetThreadVersionCheckEnabled(bool aEnabled) { mThreadVersionCheckEnabled = aEnabled; }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Gets the current Interval Max value used by Advertisement trickle timer.
|
||||
*
|
||||
* @returns The Interval Max of Advertisement trickle timer in milliseconds.
|
||||
*
|
||||
*/
|
||||
uint32_t GetAdvertisementTrickleIntervalMax(void) const { return mAdvertiseTrickleTimer.GetIntervalMax(); }
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||
|
||||
private:
|
||||
// Advertisement trickle timer constants - all times are in milliseconds.
|
||||
static constexpr uint32_t kAdvIntervalMin = 1000; // I_MIN
|
||||
static constexpr uint32_t kAdvIntervalNeighborMultiplier = 4000; // Multiplier for I_MAX per router neighbor
|
||||
static constexpr uint32_t kAdvIntervalMaxLowerBound = 12000; // Lower bound for I_MAX
|
||||
static constexpr uint32_t kAdvIntervalMaxUpperBound = 32000; // Upper bound for I_MAX
|
||||
#if OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE
|
||||
constexpr uint32_t kAdvIntervalMaxLogRoutes = 5000;
|
||||
#endif
|
||||
|
||||
static constexpr uint16_t kDiscoveryMaxJitter = 250; // Max jitter delay Discovery Responses (in msec).
|
||||
static constexpr uint16_t kChallengeTimeout = 2; // Challenge timeout (in sec).
|
||||
static constexpr uint16_t kUnsolicitedDataResponseJitter = 500; // Max delay for unsol Data Response (in msec).
|
||||
@@ -606,7 +633,9 @@ private:
|
||||
Error ProcessRouteTlv(const RouteTlv &aRouteTlv, RxInfo &aRxInfo);
|
||||
Error ReadAndProcessRouteTlvOnFed(RxInfo &aRxInfo, uint8_t aParentId);
|
||||
|
||||
void StopAdvertiseTrickleTimer(void);
|
||||
void StopAdvertiseTrickleTimer(void);
|
||||
uint32_t DetermineAdvertiseIntervalMax(void) const;
|
||||
|
||||
Error SendAddressSolicit(ThreadStatusTlv::Status aStatus);
|
||||
void SendAddressSolicitResponse(const Coap::Message &aRequest,
|
||||
ThreadStatusTlv::Status aResponseStatus,
|
||||
|
||||
@@ -81,7 +81,7 @@ constexpr uint8_t kFailedChildTransmissions = OPENTHREAD_CONFIG_FAILED_CHILD_T
|
||||
// Extra one for core Backbone Router Service.
|
||||
constexpr uint8_t kMaxServiceAlocs = OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_MAX_ALOCS + 1;
|
||||
#else
|
||||
constexpr uint8_t kMaxServiceAlocs = OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_MAX_ALOCS;
|
||||
constexpr uint8_t kMaxServiceAlocs = OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_MAX_ALOCS;
|
||||
#endif
|
||||
|
||||
constexpr uint16_t kUdpPort = 19788; ///< MLE UDP Port
|
||||
@@ -124,7 +124,7 @@ constexpr uint32_t kMinTimeout = OT_MAX(kMinTimeoutKeepAlive, kMinTimeo
|
||||
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
|
||||
constexpr uint8_t kLinkAcceptMaxRouters = 3; ///< Max Route TLV entries in a Link Accept message
|
||||
#else
|
||||
constexpr uint8_t kLinkAcceptMaxRouters = 20; ///< Max Route TLV entries in a Link Accept message
|
||||
constexpr uint8_t kLinkAcceptMaxRouters = 20; ///< Max Route TLV entries in a Link Accept message
|
||||
#endif
|
||||
constexpr uint8_t kLinkAcceptSequenceRollback = 64; ///< Route Sequence value rollback in a Link Accept message.
|
||||
|
||||
@@ -141,13 +141,6 @@ constexpr uint8_t kMaxChallengeSize = 8; ///< Maximum Challenge size in bytes.
|
||||
* Routing Protocol Constants
|
||||
*
|
||||
*/
|
||||
constexpr uint32_t kAdvertiseIntervalMin = 1; ///< Min Advertise interval (in sec)
|
||||
#if OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE
|
||||
constexpr uint32_t kAdvertiseIntervalMax = 5; ///< Max Advertise interval (in sec)
|
||||
#else
|
||||
constexpr uint32_t kAdvertiseIntervalMax = 32; ///< Max Advertise interval (in sec)
|
||||
#endif
|
||||
|
||||
constexpr uint8_t kFailedRouterTransmissions = 4;
|
||||
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
|
||||
constexpr uint8_t kFailedCslDataPollTransmissions = 15;
|
||||
@@ -160,7 +153,7 @@ constexpr uint32_t kMaxNeighborAge = 100; ///< (in sec)
|
||||
#if OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE
|
||||
constexpr uint8_t kMaxRouteCost = 127;
|
||||
#else
|
||||
constexpr uint8_t kMaxRouteCost = 16;
|
||||
constexpr uint8_t kMaxRouteCost = 16;
|
||||
#endif
|
||||
|
||||
constexpr uint8_t kMaxRouterId = OT_NETWORK_MAX_ROUTER_ID; ///< Max Router ID
|
||||
|
||||
@@ -310,6 +310,13 @@ void NeighborTable::Signal(Event aEvent, const Neighbor &aNeighbor)
|
||||
#endif
|
||||
break;
|
||||
|
||||
#if OPENTHREAD_FTD
|
||||
case kRouterAdded:
|
||||
case kRouterRemoved:
|
||||
Get<RouterTable>().SignalTableChanged();
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -343,13 +343,13 @@ uint32_t RouterTable::GetLeaderAge(void) const
|
||||
return (!mRouters.IsEmpty()) ? Time::MsecToSec(TimerMilli::GetNow() - mRouterIdSequenceLastUpdated) : 0xffffffff;
|
||||
}
|
||||
|
||||
uint8_t RouterTable::GetNeighborCount(void) const
|
||||
uint8_t RouterTable::GetNeighborCount(LinkQuality aLinkQuality) const
|
||||
{
|
||||
uint8_t count = 0;
|
||||
|
||||
for (const Router &router : mRouters)
|
||||
{
|
||||
if (router.IsStateValid())
|
||||
if (router.IsStateValid() && (router.GetLinkQualityIn() >= aLinkQuality))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
@@ -881,6 +881,8 @@ void RouterTable::HandleTableChanged(void)
|
||||
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
|
||||
Get<Utils::HistoryTracker>().RecordRouterTableChange();
|
||||
#endif
|
||||
|
||||
Get<Mle::MleRouter>().UpdateAdvertiseInterval();
|
||||
}
|
||||
|
||||
#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
|
||||
|
||||
@@ -329,12 +329,14 @@ public:
|
||||
bool IsRouteTlvIdSequenceMoreRecent(const Mle::RouteTlv &aRouteTlv) const;
|
||||
|
||||
/**
|
||||
* Returns the number of neighbor links.
|
||||
* Gets the number of router neighbors with `GetLinkQualityIn()` better than or equal to a given threshold.
|
||||
*
|
||||
* @returns The number of neighbor links.
|
||||
* @param[in] aLinkQuality Link quality threshold.
|
||||
*
|
||||
* @returns Number of router neighbors with link quality of @o aLinkQuality or better.
|
||||
*
|
||||
*/
|
||||
uint8_t GetNeighborCount(void) const;
|
||||
uint8_t GetNeighborCount(LinkQuality aLinkQuality) const;
|
||||
|
||||
/**
|
||||
* Indicates whether or not a Router ID is allocated.
|
||||
|
||||
@@ -457,6 +457,12 @@ class Node(object):
|
||||
verify(False)
|
||||
return counter
|
||||
|
||||
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# Misc
|
||||
|
||||
def get_mle_adv_imax(self):
|
||||
return self._cli_single_output('mleadvimax')
|
||||
|
||||
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# UDP
|
||||
|
||||
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2023, The OpenThread Authors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. Neither the name of the copyright holder nor the
|
||||
# names of its contributors may be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from cli import verify
|
||||
from cli import verify_within
|
||||
import cli
|
||||
import time
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Test description:
|
||||
#
|
||||
# Validate changes to `IntervalMax` for MLE Advertisement Trickle Timer based on number of
|
||||
# router neighbors of the device.
|
||||
#
|
||||
|
||||
test_name = __file__[:-3] if __file__.endswith('.py') else __file__
|
||||
print('-' * 120)
|
||||
print('Starting \'{}\''.format(test_name))
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Creating `cli.Node` instances
|
||||
|
||||
speedup = 20
|
||||
cli.Node.set_time_speedup_factor(speedup)
|
||||
|
||||
leader = cli.Node()
|
||||
routers = []
|
||||
for num in range(0, 9):
|
||||
routers.append(cli.Node())
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Test Implementation
|
||||
|
||||
leader.form('mle-adv-imax')
|
||||
|
||||
verify(leader.get_state() == 'leader')
|
||||
|
||||
# The Imax is determined as `Clamp((n + 1) * 4, 12, 32)` with `n` as
|
||||
# number of router neighbors with link quality 2 or higher
|
||||
|
||||
verify(int(leader.get_mle_adv_imax()) == 12000)
|
||||
|
||||
expected_neighbor_count = 0
|
||||
|
||||
|
||||
def check_leader_has_expected_number_of_neighbors():
|
||||
verify(len(leader.get_neighbor_table()) == expected_neighbor_count)
|
||||
|
||||
|
||||
# Add two routers one by one and check that Imax
|
||||
# remains at 12 seconds.
|
||||
|
||||
for num in range(0, 2):
|
||||
r = routers[num]
|
||||
|
||||
r.join(leader)
|
||||
verify(r.get_state() == 'router')
|
||||
|
||||
expected_neighbor_count += 1
|
||||
verify_within(check_leader_has_expected_number_of_neighbors, 10)
|
||||
|
||||
verify(int(leader.get_mle_adv_imax()) == 12000)
|
||||
|
||||
# Adding the third router, we should see Imax increasing
|
||||
# to 16 seconds.
|
||||
|
||||
r = routers[2]
|
||||
r.join(leader)
|
||||
verify(r.get_state() == 'router')
|
||||
|
||||
expected_neighbor_count += 1
|
||||
verify_within(check_leader_has_expected_number_of_neighbors, 10)
|
||||
|
||||
verify(int(leader.get_mle_adv_imax()) == 16000)
|
||||
|
||||
# Adding a neighbor with poor link quality which should not
|
||||
# count.
|
||||
|
||||
r_poor_lqi = routers[3]
|
||||
leader.set_macfilter_lqi_to_node(r_poor_lqi, 1)
|
||||
|
||||
r_poor_lqi.join(leader)
|
||||
verify(r_poor_lqi.get_state() == 'router')
|
||||
|
||||
expected_neighbor_count += 1
|
||||
verify_within(check_leader_has_expected_number_of_neighbors, 10)
|
||||
verify(int(leader.get_mle_adv_imax()) == 16000)
|
||||
|
||||
expected_imax = 16000
|
||||
|
||||
# Add four new routers one by one and check that Imax is
|
||||
# increased by 4 second for each new router neighbor up to
|
||||
# 32 seconds.
|
||||
|
||||
for num in range(4, 8):
|
||||
r = routers[num]
|
||||
|
||||
r.join(leader)
|
||||
verify(r.get_state() == 'router')
|
||||
|
||||
expected_neighbor_count += 1
|
||||
verify_within(check_leader_has_expected_number_of_neighbors, 10)
|
||||
expected_imax += 4000
|
||||
verify(int(leader.get_mle_adv_imax()) == expected_imax)
|
||||
|
||||
# Check that Imax does not increase beyond 32 seconds.
|
||||
|
||||
r = routers[8]
|
||||
|
||||
r.join(leader)
|
||||
verify(r.get_state() == 'router')
|
||||
|
||||
expected_neighbor_count += 1
|
||||
verify_within(check_leader_has_expected_number_of_neighbors, 10)
|
||||
|
||||
verify(int(leader.get_mle_adv_imax()) == 32000)
|
||||
|
||||
# Check that all routers see each other as neighbor and they are all also
|
||||
# using 32 seconds as Imax.
|
||||
|
||||
|
||||
def check_all_routers_have_expected_number_of_neighbors():
|
||||
for r in routers:
|
||||
verify(len(r.get_neighbor_table()) == expected_neighbor_count)
|
||||
|
||||
|
||||
verify_within(check_all_routers_have_expected_number_of_neighbors, 10)
|
||||
|
||||
for r in routers:
|
||||
verify(int(r.get_mle_adv_imax()) == 32000)
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------
|
||||
# Test finished
|
||||
|
||||
cli.Node.finalize_all_nodes()
|
||||
|
||||
print('\'{}\' passed.'.format(test_name))
|
||||
@@ -188,6 +188,7 @@ if [ "$TORANJ_CLI" = 1 ]; then
|
||||
run cli/test-021-br-route-prf.py
|
||||
run cli/test-022-netdata-full.py
|
||||
run cli/test-023-mesh-diag.py
|
||||
run cli/test-024-mle-adv-imax-change.py
|
||||
run cli/test-400-srp-client-server.py
|
||||
run cli/test-601-channel-manager-channel-change.py
|
||||
# Skip the "channel-select" test on a TREL only radio link, since it
|
||||
|
||||
@@ -1082,6 +1082,27 @@ target_link_libraries(ot-test-timer
|
||||
|
||||
add_test(NAME ot-test-timer COMMAND ot-test-timer)
|
||||
|
||||
add_executable(ot-test-trickle-timer
|
||||
test_trickle_timer.cpp
|
||||
)
|
||||
|
||||
target_include_directories(ot-test-trickle-timer
|
||||
PRIVATE
|
||||
${COMMON_INCLUDES}
|
||||
)
|
||||
|
||||
target_compile_options(ot-test-trickle-timer
|
||||
PRIVATE
|
||||
${COMMON_COMPILE_OPTIONS}
|
||||
)
|
||||
|
||||
target_link_libraries(ot-test-trickle-timer
|
||||
PRIVATE
|
||||
${COMMON_LIBS}
|
||||
)
|
||||
|
||||
add_test(NAME ot-test-trickle-timer COMMAND ot-test-trickle-timer)
|
||||
|
||||
add_executable(ot-test-tlv
|
||||
test_tlv.cpp
|
||||
)
|
||||
|
||||
@@ -0,0 +1,516 @@
|
||||
/*
|
||||
* Copyright (c) 2023, The OpenThread Authors.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holder nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "test_platform.h"
|
||||
|
||||
#include "common/code_utils.hpp"
|
||||
#include "common/debug.hpp"
|
||||
#include "common/instance.hpp"
|
||||
#include "common/num_utils.hpp"
|
||||
#include "common/trickle_timer.hpp"
|
||||
|
||||
static ot::Instance *sInstance;
|
||||
|
||||
static uint32_t sNow = 0;
|
||||
static uint32_t sAlarmTime;
|
||||
static bool sAlarmOn = false;
|
||||
|
||||
extern "C" {
|
||||
|
||||
void otPlatAlarmMilliStop(otInstance *) { sAlarmOn = false; }
|
||||
|
||||
void otPlatAlarmMilliStartAt(otInstance *, uint32_t aT0, uint32_t aDt)
|
||||
{
|
||||
sAlarmOn = true;
|
||||
sAlarmTime = aT0 + aDt;
|
||||
}
|
||||
|
||||
uint32_t otPlatAlarmMilliGetNow(void) { return sNow; }
|
||||
|
||||
} // extern "C"
|
||||
|
||||
namespace ot {
|
||||
|
||||
void AdvanceTime(uint32_t aDuration)
|
||||
{
|
||||
uint32_t time = sNow + aDuration;
|
||||
|
||||
while (TimeMilli(sAlarmTime) <= TimeMilli(time))
|
||||
{
|
||||
sNow = sAlarmTime;
|
||||
otPlatAlarmMilliFired(sInstance);
|
||||
}
|
||||
|
||||
sNow = time;
|
||||
}
|
||||
|
||||
class TrickleTimerTester : public TrickleTimer
|
||||
{
|
||||
public:
|
||||
explicit TrickleTimerTester(Instance &aInstance)
|
||||
: TrickleTimer(aInstance, HandleTimerFired)
|
||||
, mDidFire(false)
|
||||
{
|
||||
}
|
||||
|
||||
Time GetFireTime(void) const { return TimerMilli::GetFireTime(); }
|
||||
uint32_t GetInterval(void) const { return TrickleTimer::mInterval; }
|
||||
uint32_t GetTimeInInterval(void) const { return TrickleTimer::mTimeInInterval; }
|
||||
|
||||
void VerifyTimerDidFire(void)
|
||||
{
|
||||
VerifyOrQuit(mDidFire);
|
||||
mDidFire = false;
|
||||
}
|
||||
|
||||
void VerifyTimerDidNotFire(void) const { VerifyOrQuit(!mDidFire); }
|
||||
|
||||
static void RemoveAll(Instance &aInstance) { TimerMilli::RemoveAll(aInstance); }
|
||||
|
||||
private:
|
||||
static void HandleTimerFired(TrickleTimer &aTimer) { static_cast<TrickleTimerTester &>(aTimer).HandleTimerFired(); }
|
||||
void HandleTimerFired(void) { mDidFire = true; }
|
||||
|
||||
bool mDidFire;
|
||||
};
|
||||
|
||||
void AlarmFired(otInstance *aInstance) { otPlatAlarmMilliFired(aInstance); }
|
||||
|
||||
void TestTrickleTimerPlainMode(void)
|
||||
{
|
||||
static constexpr uint32_t kMinInterval = 2000;
|
||||
static constexpr uint32_t kMaxInterval = 5000;
|
||||
|
||||
Instance *instance = testInitInstance();
|
||||
TrickleTimerTester timer(*instance);
|
||||
uint32_t interval;
|
||||
|
||||
sInstance = instance;
|
||||
TrickleTimerTester::RemoveAll(*instance);
|
||||
|
||||
printf("TestTrickleTimerPlainMode() ");
|
||||
|
||||
// Validate that timer picks a random interval between min and max
|
||||
// on start.
|
||||
|
||||
sNow = 1000;
|
||||
timer.Start(TrickleTimer::kModePlainTimer, kMinInterval, kMaxInterval, 0);
|
||||
|
||||
VerifyOrQuit(timer.IsRunning());
|
||||
VerifyOrQuit(timer.GetIntervalMax() == kMaxInterval);
|
||||
VerifyOrQuit(timer.GetIntervalMin() == kMinInterval);
|
||||
|
||||
interval = timer.GetInterval();
|
||||
VerifyOrQuit((interval >= kMinInterval) && (interval <= kMaxInterval));
|
||||
|
||||
for (uint8_t iter = 0; iter <= 10; iter++)
|
||||
{
|
||||
AdvanceTime(interval);
|
||||
|
||||
timer.VerifyTimerDidFire();
|
||||
|
||||
// The plain mode trickle timer restarts with a new random
|
||||
// interval between min and max.
|
||||
|
||||
VerifyOrQuit(timer.IsRunning());
|
||||
interval = timer.GetInterval();
|
||||
VerifyOrQuit((interval >= kMinInterval) && (interval <= kMaxInterval));
|
||||
}
|
||||
|
||||
printf(" --> PASSED\n");
|
||||
|
||||
testFreeInstance(instance);
|
||||
}
|
||||
|
||||
void TestTrickleTimerTrickleMode(uint32_t aRedundancyConstant, uint32_t aConsistentCalls)
|
||||
{
|
||||
static constexpr uint32_t kMinInterval = 1000;
|
||||
static constexpr uint32_t kMaxInterval = 9000;
|
||||
|
||||
Instance *instance = testInitInstance();
|
||||
TrickleTimerTester timer(*instance);
|
||||
uint32_t interval;
|
||||
uint32_t t;
|
||||
|
||||
sInstance = instance;
|
||||
TrickleTimerTester::RemoveAll(*instance);
|
||||
|
||||
printf("TestTrickleTimerTrickleMode(aRedundancyConstant:%u, aConsistentCalls:%u) ", aRedundancyConstant,
|
||||
aConsistentCalls);
|
||||
|
||||
sNow = 1000;
|
||||
timer.Start(TrickleTimer::kModeTrickle, kMinInterval, kMaxInterval, aRedundancyConstant);
|
||||
|
||||
// Validate that trickle timer starts with random interval between
|
||||
// min/max.
|
||||
|
||||
VerifyOrQuit(timer.IsRunning());
|
||||
VerifyOrQuit(timer.GetIntervalMax() == kMaxInterval);
|
||||
VerifyOrQuit(timer.GetIntervalMin() == kMinInterval);
|
||||
|
||||
interval = timer.GetInterval();
|
||||
VerifyOrQuit((kMinInterval <= interval) && (interval <= kMaxInterval));
|
||||
t = timer.GetTimeInInterval();
|
||||
VerifyOrQuit((interval / 2 <= t) && (t <= interval));
|
||||
|
||||
// After `IndicateInconsistent()` should go back to min
|
||||
// interval.
|
||||
|
||||
timer.IndicateInconsistent();
|
||||
|
||||
VerifyOrQuit(timer.IsRunning());
|
||||
interval = timer.GetInterval();
|
||||
VerifyOrQuit(interval == kMinInterval);
|
||||
t = timer.GetTimeInInterval();
|
||||
VerifyOrQuit((interval / 2 <= t) && (t <= interval));
|
||||
|
||||
for (uint8_t iter = 0; iter < 10; iter++)
|
||||
{
|
||||
for (uint32_t index = 0; index < aConsistentCalls; index++)
|
||||
{
|
||||
timer.IndicateConsistent();
|
||||
}
|
||||
|
||||
AdvanceTime(t);
|
||||
|
||||
if (aConsistentCalls < aRedundancyConstant)
|
||||
{
|
||||
timer.VerifyTimerDidFire();
|
||||
}
|
||||
else
|
||||
{
|
||||
timer.VerifyTimerDidNotFire();
|
||||
}
|
||||
|
||||
AdvanceTime(interval - t);
|
||||
|
||||
// Verify that interval is doubling each time up
|
||||
// to max interval.
|
||||
|
||||
VerifyOrQuit(timer.IsRunning());
|
||||
VerifyOrQuit(timer.GetInterval() == Min(interval * 2, kMaxInterval));
|
||||
|
||||
interval = timer.GetInterval();
|
||||
t = timer.GetTimeInInterval();
|
||||
VerifyOrQuit((interval / 2 <= t) && (t <= interval));
|
||||
}
|
||||
|
||||
AdvanceTime(t);
|
||||
|
||||
timer.IndicateInconsistent();
|
||||
|
||||
VerifyOrQuit(timer.IsRunning());
|
||||
interval = timer.GetInterval();
|
||||
VerifyOrQuit(interval == kMinInterval);
|
||||
|
||||
printf(" --> PASSED\n");
|
||||
|
||||
testFreeInstance(instance);
|
||||
}
|
||||
|
||||
void TestTrickleTimerMinMaxIntervalChange(void)
|
||||
{
|
||||
Instance *instance = testInitInstance();
|
||||
TrickleTimerTester timer(*instance);
|
||||
TimeMilli fireTime;
|
||||
uint32_t interval;
|
||||
uint32_t t;
|
||||
|
||||
sInstance = instance;
|
||||
TrickleTimerTester::RemoveAll(*instance);
|
||||
|
||||
printf("TestTrickleTimerMinMaxIntervalChange()");
|
||||
|
||||
sNow = 1000;
|
||||
timer.Start(TrickleTimer::kModeTrickle, 2000, 4000);
|
||||
|
||||
VerifyOrQuit(timer.IsRunning());
|
||||
VerifyOrQuit(timer.GetIntervalMin() == 2000);
|
||||
VerifyOrQuit(timer.GetIntervalMax() == 4000);
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Validate that `SetIntervalMin()` to a larger value than
|
||||
// previously set does not impact the current interval.
|
||||
|
||||
timer.IndicateInconsistent();
|
||||
interval = timer.GetInterval();
|
||||
t = timer.GetTimeInInterval();
|
||||
fireTime = timer.GetFireTime();
|
||||
|
||||
VerifyOrQuit(interval == 2000);
|
||||
VerifyOrQuit((interval / 2 <= t) && (t < interval));
|
||||
|
||||
// Change `IntervalMin` before time `t`.
|
||||
|
||||
timer.SetIntervalMin(3000);
|
||||
|
||||
VerifyOrQuit(timer.IsRunning());
|
||||
VerifyOrQuit(timer.GetIntervalMin() == 3000);
|
||||
VerifyOrQuit(timer.GetIntervalMax() == 4000);
|
||||
|
||||
VerifyOrQuit(interval == timer.GetInterval());
|
||||
VerifyOrQuit(t == timer.GetTimeInInterval());
|
||||
VerifyOrQuit(fireTime == timer.GetFireTime());
|
||||
|
||||
AdvanceTime(t);
|
||||
timer.VerifyTimerDidFire();
|
||||
fireTime = timer.GetFireTime();
|
||||
|
||||
// Change `IntervalMin` after time `t`.
|
||||
|
||||
timer.SetIntervalMin(3500);
|
||||
|
||||
VerifyOrQuit(timer.IsRunning());
|
||||
VerifyOrQuit(timer.GetIntervalMin() == 3500);
|
||||
VerifyOrQuit(timer.GetIntervalMax() == 4000);
|
||||
|
||||
VerifyOrQuit(interval == timer.GetInterval());
|
||||
VerifyOrQuit(t == timer.GetTimeInInterval());
|
||||
VerifyOrQuit(fireTime == timer.GetFireTime());
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Validate that `SetIntervalMin()` to a smaller value
|
||||
// also does not impact the current interval.
|
||||
|
||||
timer.IndicateInconsistent();
|
||||
|
||||
interval = timer.GetInterval();
|
||||
t = timer.GetTimeInInterval();
|
||||
fireTime = timer.GetFireTime();
|
||||
|
||||
VerifyOrQuit(interval == 3500);
|
||||
VerifyOrQuit((interval / 2 <= t) && (t < interval));
|
||||
|
||||
// Change `IntervalMin` before time `t`.
|
||||
|
||||
timer.SetIntervalMin(3000);
|
||||
|
||||
VerifyOrQuit(timer.IsRunning());
|
||||
VerifyOrQuit(timer.GetIntervalMin() == 3000);
|
||||
VerifyOrQuit(timer.GetIntervalMax() == 4000);
|
||||
|
||||
VerifyOrQuit(interval == timer.GetInterval());
|
||||
VerifyOrQuit(t == timer.GetTimeInInterval());
|
||||
VerifyOrQuit(fireTime == timer.GetFireTime());
|
||||
|
||||
AdvanceTime(t);
|
||||
timer.VerifyTimerDidFire();
|
||||
fireTime = timer.GetFireTime();
|
||||
|
||||
// Change `IntervalMin` after time `t`.
|
||||
|
||||
timer.SetIntervalMin(2000);
|
||||
|
||||
VerifyOrQuit(timer.IsRunning());
|
||||
VerifyOrQuit(timer.GetIntervalMin() == 2000);
|
||||
VerifyOrQuit(timer.GetIntervalMax() == 4000);
|
||||
|
||||
VerifyOrQuit(interval == timer.GetInterval());
|
||||
VerifyOrQuit(t == timer.GetTimeInInterval());
|
||||
VerifyOrQuit(fireTime == timer.GetFireTime());
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Validate that changing `IntervalMax` to a larger value
|
||||
// than the current interval being used by timer, does not
|
||||
// impact the current internal.
|
||||
|
||||
timer.IndicateInconsistent();
|
||||
|
||||
interval = timer.GetInterval();
|
||||
t = timer.GetTimeInInterval();
|
||||
fireTime = timer.GetFireTime();
|
||||
|
||||
VerifyOrQuit(interval == 2000);
|
||||
VerifyOrQuit((interval / 2 <= t) && (t < interval));
|
||||
|
||||
// Change `IntervalMax` before time `t`.
|
||||
|
||||
timer.SetIntervalMax(2500);
|
||||
|
||||
VerifyOrQuit(timer.GetIntervalMax() == 2500);
|
||||
VerifyOrQuit(timer.IsRunning());
|
||||
|
||||
VerifyOrQuit(interval == timer.GetInterval());
|
||||
VerifyOrQuit(t == timer.GetTimeInInterval());
|
||||
VerifyOrQuit(fireTime == timer.GetFireTime());
|
||||
|
||||
AdvanceTime(t);
|
||||
|
||||
timer.VerifyTimerDidFire();
|
||||
|
||||
fireTime = timer.GetFireTime();
|
||||
|
||||
// Change `IntervalMax` after time `t`.
|
||||
|
||||
timer.SetIntervalMax(3000);
|
||||
|
||||
VerifyOrQuit(interval == timer.GetInterval());
|
||||
VerifyOrQuit(t == timer.GetTimeInInterval());
|
||||
VerifyOrQuit(fireTime == timer.GetFireTime());
|
||||
|
||||
timer.Stop();
|
||||
VerifyOrQuit(!timer.IsRunning());
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Check behavior when the new `IntervalMax` is smaller
|
||||
// than the current interval being used by timer.
|
||||
|
||||
// New `Imax` is smaller than `t` and before now.
|
||||
//
|
||||
// |<---- interval --^-------------------------------->|
|
||||
// |<---- t ---------^------------------>| |
|
||||
// |<---- new Imax --^--->| | |
|
||||
// | now | | |
|
||||
|
||||
timer.Start(TrickleTimer::kModeTrickle, 2000, 2000);
|
||||
interval = timer.GetInterval();
|
||||
t = timer.GetTimeInInterval();
|
||||
fireTime = timer.GetFireTime();
|
||||
|
||||
VerifyOrQuit(interval == 2000);
|
||||
VerifyOrQuit((interval / 2 <= t) && (t < interval));
|
||||
timer.SetIntervalMin(500);
|
||||
|
||||
AdvanceTime(100);
|
||||
timer.VerifyTimerDidNotFire();
|
||||
|
||||
timer.SetIntervalMax(500);
|
||||
|
||||
VerifyOrQuit(timer.GetInterval() == 500);
|
||||
VerifyOrQuit(timer.GetTimeInInterval() == 500);
|
||||
VerifyOrQuit(timer.GetFireTime() != fireTime);
|
||||
timer.VerifyTimerDidNotFire();
|
||||
|
||||
AdvanceTime(400);
|
||||
timer.VerifyTimerDidFire();
|
||||
|
||||
// New `Imax` is smaller than `t` and after now.
|
||||
//
|
||||
// |<---- interval --------------^-------------------->|
|
||||
// |<---- t ---------------------^------>| |
|
||||
// |<---- new Imax ------>| ^ | |
|
||||
// | | now | |
|
||||
|
||||
timer.Start(TrickleTimer::kModeTrickle, 2000, 2000);
|
||||
interval = timer.GetInterval();
|
||||
t = timer.GetTimeInInterval();
|
||||
fireTime = timer.GetFireTime();
|
||||
|
||||
VerifyOrQuit(interval == 2000);
|
||||
VerifyOrQuit((interval / 2 <= t) && (t < interval));
|
||||
timer.SetIntervalMin(500);
|
||||
|
||||
AdvanceTime(800);
|
||||
timer.VerifyTimerDidNotFire();
|
||||
|
||||
timer.SetIntervalMax(500);
|
||||
|
||||
VerifyOrQuit(timer.GetInterval() == 500);
|
||||
VerifyOrQuit(timer.GetTimeInInterval() == 500);
|
||||
VerifyOrQuit(timer.GetFireTime() != fireTime);
|
||||
timer.VerifyTimerDidNotFire();
|
||||
|
||||
AdvanceTime(0);
|
||||
timer.VerifyTimerDidFire();
|
||||
|
||||
// New `Imax` is larger than `t` and before now.
|
||||
//
|
||||
// |<---- interval --------------------------------^-->|
|
||||
// |<---- t ---------------------------->| ^ |
|
||||
// |<---- new Imax --------------------------->| ^ |
|
||||
// | | | now |
|
||||
|
||||
timer.Start(TrickleTimer::kModeTrickle, 2000, 2000);
|
||||
|
||||
interval = timer.GetInterval();
|
||||
t = timer.GetTimeInInterval();
|
||||
|
||||
VerifyOrQuit(interval == 2000);
|
||||
VerifyOrQuit((interval / 2 <= t) && (t < interval));
|
||||
timer.SetIntervalMin(500);
|
||||
|
||||
AdvanceTime(1999);
|
||||
timer.VerifyTimerDidFire();
|
||||
|
||||
timer.SetIntervalMax(t + 1);
|
||||
|
||||
VerifyOrQuit(timer.GetInterval() == t + 1);
|
||||
fireTime = timer.GetFireTime();
|
||||
|
||||
// Check that new interval is started immediately.
|
||||
AdvanceTime(0);
|
||||
timer.VerifyTimerDidNotFire();
|
||||
VerifyOrQuit(fireTime != timer.GetFireTime());
|
||||
VerifyOrQuit(timer.GetInterval() == timer.GetIntervalMax());
|
||||
|
||||
// New `Imax` is larger than `t` and after now.
|
||||
//
|
||||
// |<---- interval -------------------------^--------->|
|
||||
// |<---- t ---------------------------->| ^ |
|
||||
// |<---- new Imax -------------------------^->| |
|
||||
// | | now | |
|
||||
|
||||
timer.Start(TrickleTimer::kModeTrickle, 2000, 2000);
|
||||
|
||||
interval = timer.GetInterval();
|
||||
t = timer.GetTimeInInterval();
|
||||
|
||||
VerifyOrQuit(interval == 2000);
|
||||
VerifyOrQuit((interval / 2 <= t) && (t < interval));
|
||||
timer.SetIntervalMin(500);
|
||||
|
||||
AdvanceTime(t);
|
||||
timer.VerifyTimerDidFire();
|
||||
|
||||
timer.SetIntervalMax(t + 1);
|
||||
|
||||
VerifyOrQuit(timer.GetInterval() == t + 1);
|
||||
fireTime = timer.GetFireTime();
|
||||
|
||||
AdvanceTime(1);
|
||||
timer.VerifyTimerDidNotFire();
|
||||
VerifyOrQuit(fireTime != timer.GetFireTime());
|
||||
VerifyOrQuit(timer.GetInterval() == timer.GetIntervalMax());
|
||||
|
||||
printf(" --> PASSED\n");
|
||||
|
||||
testFreeInstance(instance);
|
||||
}
|
||||
|
||||
} // namespace ot
|
||||
|
||||
int main(void)
|
||||
{
|
||||
ot::TestTrickleTimerPlainMode();
|
||||
ot::TestTrickleTimerTrickleMode(/* aRedundancyConstant */ 5, /* aConsistentCalls */ 3);
|
||||
ot::TestTrickleTimerTrickleMode(/* aRedundancyConstant */ 3, /* aConsistentCalls */ 3);
|
||||
ot::TestTrickleTimerMinMaxIntervalChange();
|
||||
|
||||
printf("All tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user