[mle] change the attach MLE Announcement transmissions (#2692)

This commit changes the attach state machine and how the MLE Announce
messages are sent as part of the attach process. A new attach state
`kAttachStateAnnounce` is added where the device sends MLE Announce on
all channels (in Active Dataset's channel mask).

This commit also adds a new `toranj` test-case which tests the
situation where a device misses a channel change and then reattaches
by sending MLE Announce messages.
This commit is contained in:
Abtin Keshavarzian
2018-05-14 12:33:47 -07:00
committed by Jonathan Hui
parent 7e2caa5f16
commit 4d6f5a7c4f
6 changed files with 415 additions and 86 deletions
+211 -52
View File
@@ -68,6 +68,8 @@ Mle::Mle(Instance &aInstance)
, mDeviceMode(ModeTlv::kModeRxOnWhenIdle | ModeTlv::kModeSecureDataRequest)
, mAttachState(kAttachStateIdle)
, mReattachState(kReattachStop)
, mAttachCounter(0)
, mAnnounceDelay(kAnnounceTimeout)
, mAttachTimer(aInstance, &Mle::HandleAttachTimer, this)
, mDelayedResponseTimer(aInstance, &Mle::HandleDelayedResponseTimer, this)
, mChildUpdateRequestTimer(aInstance, &Mle::HandleChildUpdateRequestTimer, this)
@@ -236,6 +238,7 @@ otError Mle::Start(bool aEnableReattach, bool aAnnounceAttach)
VerifyOrExit(netif.IsUp(), error = OT_ERROR_INVALID_STATE);
SetStateDetached();
mAttachCounter = 0;
netif.GetKeyManager().Start();
@@ -257,7 +260,7 @@ otError Mle::Start(bool aEnableReattach, bool aAnnounceAttach)
}
else
{
mAttachState = kAttachStateSynchronize;
SetAttachState(kAttachStateSynchronize);
mChildUpdateAttempts = 0;
SendChildUpdateRequest();
}
@@ -301,6 +304,16 @@ exit:
return;
}
void Mle::SetAttachState(AttachState aState)
{
VerifyOrExit(aState != mAttachState);
otLogInfoMle(GetInstance(), "AttachState %s -> %s", AttachStateToString(mAttachState), AttachStateToString(aState));
mAttachState = aState;
exit:
return;
}
otError Mle::Restore(void)
{
ThreadNetif & netif = GetNetif();
@@ -533,8 +546,6 @@ otError Mle::BecomeChild(AttachMode aMode)
VerifyOrExit(mRole != OT_DEVICE_ROLE_DISABLED, error = OT_ERROR_INVALID_STATE);
VerifyOrExit(mAttachState == kAttachStateIdle, error = OT_ERROR_BUSY);
otLogInfoMle(GetInstance(), "Attempt to attach");
if (mReattachState == kReattachStart)
{
if (netif.GetActiveDataset().Restore() == OT_ERROR_NONE)
@@ -548,9 +559,13 @@ otError Mle::BecomeChild(AttachMode aMode)
}
ResetParentCandidate();
mAttachState = kAttachStateStart;
SetAttachState(kAttachStateStart);
mParentRequestMode = aMode;
mAttachCounter++;
otLogInfoMle(GetInstance(), "Attempt to attach - attempt %d, %s %s", mAttachCounter, AttachModeToString(aMode),
ReattachStateToString(mReattachState));
if (aMode != kAttachBetter)
{
if (mDeviceMode & ModeTlv::kModeFFD)
@@ -559,9 +574,7 @@ otError Mle::BecomeChild(AttachMode aMode)
}
}
netif.GetMeshForwarder().SetRxOnWhenIdle(true);
mAttachTimer.Start(1 + Random::GetUint32InRange(0, kParentRequestRouterTimeout));
mAttachTimer.Start(1 + ((mAttachCounter == 1) ? Random::GetUint32InRange(0, kParentRequestRouterTimeout) : 0));
exit:
return error;
@@ -582,7 +595,7 @@ otError Mle::SetStateDetached(void)
}
SetRole(OT_DEVICE_ROLE_DETACHED);
mAttachState = kAttachStateIdle;
SetAttachState(kAttachStateIdle);
mAttachTimer.Stop();
mChildUpdateRequestTimer.Stop();
netif.GetMeshForwarder().SetRxOnWhenIdle(true);
@@ -605,7 +618,8 @@ otError Mle::SetStateChild(uint16_t aRloc16)
SetRloc16(aRloc16);
SetRole(OT_DEVICE_ROLE_CHILD);
mAttachState = kAttachStateIdle;
SetAttachState(kAttachStateIdle);
mAttachCounter = 0;
mReattachState = kReattachStop;
mChildUpdateAttempts = 0;
netif.GetMac().SetBeaconEnabled(false);
@@ -1432,7 +1446,28 @@ void Mle::HandleAttachTimer(Timer &aTimer)
void Mle::HandleAttachTimer(void)
{
uint32_t delay = 0;
uint32_t delay = 0;
bool shouldAnnounce = true;
if (mAttachState == kAttachStateParentRequestRouter || mAttachState == kAttachStateParentRequestReed ||
mAttachState == kAttachStateAnnounce)
{
// If already attached, accept the parent candidate if
// we are trying to attach to a better partition or if a
// Parent Response was also received from the current parent
// to which the device is attached. This ensures that the
// new parent candidate is compared with the current parent
// and that it is indeed preferred over the current one.
if (mParentCandidate.GetState() == Neighbor::kStateParentResponse &&
(mRole != OT_DEVICE_ROLE_CHILD || mReceivedResponseFromParent || mParentRequestMode == kAttachBetter) &&
SendChildIdRequest() == OT_ERROR_NONE)
{
SetAttachState(kAttachStateChildIdRequest);
delay = kParentRequestReedTimeout;
ExitNow();
}
}
switch (mAttachState)
{
@@ -1445,9 +1480,10 @@ void Mle::HandleAttachTimer(void)
break;
case kAttachStateStart:
mAttachState = kAttachStateParentRequestRouter;
SetAttachState(kAttachStateParentRequestRouter);
mParentCandidate.SetState(Neighbor::kStateInvalid);
mReceivedResponseFromParent = false;
GetNetif().GetMeshForwarder().SetRxOnWhenIdle(true);
if (mParentRequestMode == kAttachSame1 || mParentRequestMode == kAttachSame2)
{
@@ -1463,42 +1499,88 @@ void Mle::HandleAttachTimer(void)
break;
case kAttachStateParentRequestRouter:
mAttachState = kAttachStateParentRequestReed;
SetAttachState(kAttachStateParentRequestReed);
SendParentRequest(kParentRequestTypeRoutersAndReeds);
delay = kParentRequestReedTimeout;
break;
if (mParentCandidate.GetState() != Neighbor::kStateParentResponse)
case kAttachStateParentRequestReed:
shouldAnnounce = PrepareAnnounceState();
if (shouldAnnounce)
{
SetAttachState(kAttachStateAnnounce);
SendParentRequest(kParentRequestTypeRoutersAndReeds);
delay = kParentRequestReedTimeout;
mAnnounceChannel = OT_RADIO_CHANNEL_MIN;
delay = mAnnounceDelay;
break;
}
// fall through
case kAttachStateParentRequestReed:
if (mParentCandidate.GetState() == Neighbor::kStateParentResponse &&
(mRole != OT_DEVICE_ROLE_CHILD || mReceivedResponseFromParent || mParentRequestMode == kAttachBetter) &&
SendChildIdRequest() == OT_ERROR_NONE)
case kAttachStateAnnounce:
if (shouldAnnounce)
{
mAttachState = kAttachStateChildIdRequest;
delay = kParentRequestReedTimeout;
break;
if (SendOrphanAnnounce() == OT_ERROR_NONE)
{
delay = mAnnounceDelay;
break;
}
}
// fall through
case kAttachStateChildIdRequest:
mAttachState = kAttachStateIdle;
SetAttachState(kAttachStateIdle);
ResetParentCandidate();
delay = Reattach();
break;
}
exit:
if (delay != 0)
{
mAttachTimer.Start(delay);
}
}
bool Mle::PrepareAnnounceState(void)
{
bool shouldAnnounce = false;
uint16_t numChannels = 0;
const MeshCoP::ChannelMask0Tlv *channelMask;
MeshCoP::Dataset dataset(MeshCoP::Tlv::kActiveTimestamp);
VerifyOrExit((mRole != OT_DEVICE_ROLE_CHILD) && ((mDeviceMode & ModeTlv::kModeFFD) == 0) &&
(mReattachState == kReattachStop));
SuccessOrExit(GetNetif().GetActiveDataset().Get(dataset));
channelMask = static_cast<const MeshCoP::ChannelMask0Tlv *>(dataset.Get(MeshCoP::Tlv::kChannelMask));
VerifyOrExit(channelMask != NULL);
for (uint8_t channel = OT_RADIO_CHANNEL_MIN; channel <= OT_RADIO_CHANNEL_MAX; channel++)
{
if (channelMask->IsChannelSet(channel))
{
numChannels++;
}
}
mAnnounceDelay = kAnnounceTimeout / (numChannels + 1);
if (mAnnounceDelay < kMinAnnounceDelay)
{
mAnnounceDelay = kMinAnnounceDelay;
}
shouldAnnounce = true;
exit:
return shouldAnnounce;
}
uint32_t Mle::Reattach(void)
{
ThreadNetif &netif = GetNetif();
@@ -1510,8 +1592,8 @@ uint32_t Mle::Reattach(void)
{
netif.GetPendingDataset().ApplyConfiguration();
mReattachState = kReattachPending;
mAttachState = kAttachStateStart;
delay = kParentRequestRouterTimeout;
SetAttachState(kAttachStateStart);
delay = 1 + Random::GetUint32InRange(0, kParentRequestJitter);
}
else
{
@@ -1540,7 +1622,6 @@ uint32_t Mle::Reattach(void)
}
else if ((mDeviceMode & ModeTlv::kModeFFD) == 0)
{
SendOrphanAnnounce();
BecomeDetached();
}
else if (netif.GetMle().BecomeLeader() != OT_ERROR_NONE)
@@ -2044,45 +2125,30 @@ exit:
return error;
}
void Mle::SendOrphanAnnounce(void)
otError Mle::SendOrphanAnnounce(void)
{
otError error = OT_ERROR_NONE;
const MeshCoP::ChannelMask0Tlv *channelMask;
uint8_t channel;
MeshCoP::Dataset dataset(MeshCoP::Tlv::kActiveTimestamp);
GetNetif().GetActiveDataset().Get(dataset);
SuccessOrExit(error = GetNetif().GetActiveDataset().Get(dataset));
channelMask = static_cast<const MeshCoP::ChannelMask0Tlv *>(dataset.Get(MeshCoP::Tlv::kChannelMask));
VerifyOrExit(channelMask != NULL, error = OT_ERROR_NOT_FOUND);
VerifyOrExit(channelMask != NULL);
VerifyOrExit(mAnnounceChannel <= OT_RADIO_CHANNEL_MAX, error = OT_ERROR_NOT_FOUND);
// find next channel in the Active Operational Dataset Channel Mask
channel = mAnnounceChannel;
while (!channelMask->IsChannelSet(channel))
while (!channelMask->IsChannelSet(mAnnounceChannel))
{
channel++;
if (channel > OT_RADIO_CHANNEL_MAX)
{
channel = OT_RADIO_CHANNEL_MIN;
}
VerifyOrExit(channel != mAnnounceChannel);
mAnnounceChannel++;
VerifyOrExit(mAnnounceChannel <= OT_RADIO_CHANNEL_MAX, error = OT_ERROR_NOT_FOUND);
}
// Send Announce message
SendAnnounce(channel, true);
// Move to next channel
mAnnounceChannel = channel + 1;
if (mAnnounceChannel > OT_RADIO_CHANNEL_MAX)
{
mAnnounceChannel = OT_RADIO_CHANNEL_MIN;
}
SendAnnounce(mAnnounceChannel, true);
mAnnounceChannel++;
exit:
return;
return error;
}
otError Mle::SendMessage(Message &aMessage, const Ip6::Address &aDestination)
@@ -3709,5 +3775,98 @@ const char *Mle::RoleToString(otDeviceRole aRole)
return roleString;
}
#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MLE == 1)
const char *Mle::AttachModeToString(AttachMode aMode)
{
const char *str = "unknown";
switch (aMode)
{
case kAttachAny:
str = "any-partition";
break;
case kAttachSame1:
str = "same-partition-try-1";
break;
case kAttachSame2:
str = "same-partition-try-2";
break;
case kAttachBetter:
str = "better-partition";
break;
}
return str;
}
const char *Mle::AttachStateToString(AttachState aState)
{
const char *str = "Unknown";
switch (aState)
{
case kAttachStateIdle:
str = "Idle";
break;
case kAttachStateSynchronize:
str = "Sync";
break;
case kAttachStateStart:
str = "Start";
break;
case kAttachStateParentRequestRouter:
str = "ParentReqRouters";
break;
case kAttachStateParentRequestReed:
str = "ParentReqReeds";
break;
case kAttachStateAnnounce:
str = "Announce";
break;
case kAttachStateChildIdRequest:
str = "ChildIdReq";
break;
};
return str;
}
const char *Mle::ReattachStateToString(ReattachState aState)
{
const char *str = "unknown";
switch (aState)
{
case kReattachStop:
str = "";
break;
case kReattachStart:
str = "reattaching";
break;
case kReattachActive:
str = "reattaching with Active Dataset";
break;
case kReattachPending:
str = "reattaching with Pending Dataset";
break;
}
return str;
}
#endif // (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MLE == 1)
} // namespace Mle
} // namespace ot
+76 -32
View File
@@ -958,6 +958,33 @@ public:
static const char *RoleToString(otDeviceRole aRole);
protected:
/**
* States during attach (when searching for a parent).
*
*/
enum AttachState
{
kAttachStateIdle, ///< Not currently searching for a parent.
kAttachStateSynchronize, ///< Looking to synchronize with a parent (after reset).
kAttachStateStart, ///< Starting to look for a parent.
kAttachStateParentRequestRouter, ///< Searching for a Router to attach to.
kAttachStateParentRequestReed, ///< Searching for Routers or REEDs to attach to.
kAttachStateAnnounce, ///< Send Announce messages
kAttachStateChildIdRequest, ///< Sending a Child ID Request message.
};
/**
* States when reattaching network using stored dataset
*
*/
enum ReattachState
{
kReattachStop = 0, ///< Reattach process is disabled or finished
kReattachStart = 1, ///< Start reattach process
kReattachActive = 2, ///< Reattach using stored Active Dataset
kReattachPending = 3, ///< Reattach using stored Pending Dataset
};
enum
{
kMleMaxResponseDelay = 1000u, ///< Maximum delay before responding to a multicast request.
@@ -979,6 +1006,14 @@ protected:
*/
void SetRole(otDeviceRole aRole);
/**
* This method sets the attach state
*
* @param[in] aState An attach state
*
*/
void SetAttachState(AttachState aState);
/**
* This method appends an MLE header to a message.
*
@@ -1398,46 +1433,54 @@ protected:
*/
void InformPreviousChannel(void);
LeaderDataTlv mLeaderData; ///< Last received Leader Data TLV.
bool mRetrieveNewNetworkData; ///< Indicating new Network Data is needed if set.
otDeviceRole mRole; ///< Current Thread role.
Router mParent; ///< Parent information.
uint8_t mDeviceMode; ///< Device mode setting.
#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MLE == 1)
/**
* States during attach (when searching for a parent).
* This method converts an `AttachMode` enumeration value into a human-readable string.
*
* @param[in] aMode An attach mode
*
* @returns A human-readable string corresponding to the attach mode.
*
*/
enum AttachState
{
kAttachStateIdle, ///< Not currently searching for a parent.
kAttachStateSynchronize, ///< Looking to synchronize with a parent (after reset).
kAttachStateStart, ///< Starting to look for a parent.
kAttachStateParentRequestRouter, ///< Searching for a Router to attach to.
kAttachStateParentRequestReed, ///< Searching for Routers or REEDs to attach to.
kAttachStateChildIdRequest, ///< Sending a Child ID Request message.
};
AttachState mAttachState; ///< The parent request state.
static const char *AttachModeToString(AttachMode aMode);
/**
* States when reattaching network using stored dataset
* This method converts an `AttachState` enumeration value into a human-readable string.
*
* @param[in] aState An attach state
*
* @returns A human-readable string corresponding to the attach state.
*
*/
enum ReattachState
{
kReattachStop = 0, ///< Reattach process is disabled or finished
kReattachStart = 1, ///< Start reattach process
kReattachActive = 2, ///< Reattach using stored Active Dataset
kReattachPending = 3, ///< Reattach using stored Pending Dataset
};
ReattachState mReattachState;
static const char *AttachStateToString(AttachState aState);
TimerMilli mAttachTimer; ///< The timer for driving the attach process.
TimerMilli mDelayedResponseTimer; ///< The timer to delay MLE responses.
TimerMilli mChildUpdateRequestTimer; ///< The timer for sending MLE Child Update Request messages.
/**
* This method converts a `ReattachState` enumeration value into a human-readable string.
*
* @param[in] aState A reattach state
*
* @returns A human-readable string corresponding to the reattach state.
*
*/
static const char *ReattachStateToString(ReattachState aState);
#endif
uint8_t mParentLeaderCost;
LeaderDataTlv mLeaderData; ///< Last received Leader Data TLV.
bool mRetrieveNewNetworkData; ///< Indicating new Network Data is needed if set.
otDeviceRole mRole; ///< Current Thread role.
Router mParent; ///< Parent information.
uint8_t mDeviceMode; ///< Device mode setting.
AttachState mAttachState; ///< The parent request state.
ReattachState mReattachState; ///< Reattach state
uint16_t mAttachCounter; ///< Attach attempt counter.
uint16_t mAnnounceDelay; ///< Delay in between sending Announce messages during attach.
TimerMilli mAttachTimer; ///< The timer for driving the attach process.
TimerMilli mDelayedResponseTimer; ///< The timer to delay MLE responses.
TimerMilli mChildUpdateRequestTimer; ///< The timer for sending MLE Child Update Request messages.
uint32_t mLastPartitionId; ///< The partition ID of the previous Thread partition
uint8_t mLastPartitionRouterIdSequence; ///< The router ID sequence from the previous Thread partition
uint8_t mLastPartitionIdTimeout; ///< The time remaining to avoid the previous Thread partition
uint8_t mParentLeaderCost;
private:
enum
@@ -1493,7 +1536,8 @@ private:
otError SendParentRequest(ParentRequestType aType);
otError SendChildIdRequest(void);
void SendOrphanAnnounce(void);
otError SendOrphanAnnounce(void);
bool PrepareAnnounceState(void);
otError SendAnnounce(uint8_t aChannel, bool aOrphanAnnounce, const Ip6::Address &aDestination);
uint32_t Reattach(void);
+3
View File
@@ -61,6 +61,9 @@ enum
kUdpPort = 19788, ///< MLE UDP Port
kParentRequestRouterTimeout = 750, ///< Router Parent Request timeout
kParentRequestReedTimeout = 1250, ///< Router and REEDs Parent Request timeout
kParentRequestJitter = 50, ///< Maximum jitter time added to Parent Request timeout
kAnnounceTimeout = 1400, ///< Total timeout used for sending Announcement messages
kMinAnnounceDelay = 80, ///< Minimum delay between Announcement messages
kParentResponseMaxDelayRouters = 500, ///< Maximum delay for response for Parent Request sent to routers only
kParentResponseMaxDelayAll = 1000, ///< Maximum delay for response for Parent Request sent to all devices
kUnicastRetransmissionDelay = 1000, ///< Base delay before retransmitting an MLE unicast.
+4 -2
View File
@@ -296,7 +296,8 @@ otError MleRouter::SetStateRouter(uint16_t aRloc16)
SetRloc16(aRloc16);
SetRole(OT_DEVICE_ROLE_ROUTER);
mAttachState = kAttachStateIdle;
SetAttachState(kAttachStateIdle);
mAttachCounter = 0;
mAttachTimer.Stop();
mChildUpdateRequestTimer.Stop();
mAdvertiseTimer.Stop();
@@ -330,7 +331,8 @@ otError MleRouter::SetStateLeader(uint16_t aRloc16)
SetRloc16(aRloc16);
SetRole(OT_DEVICE_ROLE_LEADER);
mAttachState = kAttachStateIdle;
SetAttachState(kAttachStateIdle);
mAttachCounter = 0;
mAttachTimer.Stop();
mChildUpdateRequestTimer.Stop();
mAdvertiseTimer.Stop();
+1
View File
@@ -127,5 +127,6 @@ run test-100-mcu-power-state.py
run test-600-channel-manager-properties.py
run test-601-channel-manager-channel-change.py
run test-602-channel-manager-channel-select.py
run test-603-channel-manager-announce-recovery.py
exit 0
@@ -0,0 +1,120 @@
#!/usr/bin/env python
#
# Copyright (c) 2018, 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 wpan import verify
import wpan
import time
#-----------------------------------------------------------------------------------------------------------------------
# Test description: Orphaned node attach through MLE Announcement
test_name = __file__[:-3] if __file__.endswith('.py') else __file__
print '-' * 120
print 'Starting \'{}\''.format(test_name)
def verify_channel(nodes, new_channel, wait_time=20):
"""
This function checks the channel on a given list of `nodes` and verifies that all nodes
switch to a given `new_channel` (as int) within certain `wait_time` (int and in seconds)
"""
start_time = time.time()
while not all([ (new_channel == int(node.get(wpan.WPAN_CHANNEL), 0)) for node in nodes ]):
if time.time() - start_time > wait_time:
print 'Took too long to switch to channel {} ({}>{} sec)'.format(new_channel, time.time() - start_time,
wait_time)
exit(1)
time.sleep(0.1)
#-----------------------------------------------------------------------------------------------------------------------
# Creating `wpan.Nodes` instances
router = wpan.Node()
c1 = wpan.Node()
c2 = wpan.Node()
all_nodes = [router, c1, c2]
#-----------------------------------------------------------------------------------------------------------------------
# Init all nodes
wpan.Node.init_all_nodes()
#-----------------------------------------------------------------------------------------------------------------------
# Build network topology
for node in all_nodes:
node.set(wpan.WPAN_OT_LOG_LEVEL, '0')
#-----------------------------------------------------------------------------------------------------------------------
# Test implementation
router.form('announce-tst', channel=11)
c1.join_node(router, node_type=wpan.JOIN_TYPE_SLEEPY_END_DEVICE)
c2.join_node(router, node_type=wpan.JOIN_TYPE_SLEEPY_END_DEVICE)
c1.set(wpan.WPAN_POLL_INTERVAL, '500')
c2.set(wpan.WPAN_POLL_INTERVAL, '500')
c1.set(wpan.WPAN_THREAD_DEVICE_MODE,'5')
c2.set(wpan.WPAN_THREAD_DEVICE_MODE,'5')
# Reset c2 and keep it in detached state
c2.set('Daemon:AutoAssociateAfterReset', 'false')
c2.reset();
# Switch the rest of network to channel 26
router.set(wpan.WPAN_CHANNEL_MANAGER_NEW_CHANNEL, '26')
verify_channel([router, c1], 26)
# Now re-enable c2 and verify that it does attach to router and is on channel 26
# c2 would go through the ML Announce recovery.
c2.set('Daemon:AutoAssociateAfterReset', 'true')
c2.reset();
verify(int(c2.get(wpan.WPAN_CHANNEL), 0) == 11)
# wait for 20s for c2 to be attached/associated
start_time = time.time()
wait_time = 20
while not c2.is_associated():
if time.time() - start_time > wait_time:
print 'Took too long to recover through ML Announce ({}>{} sec)'.format(time.time() - start_time, wait_time)
exit(1)
time.sleep(0.1)
# Check that c2 did attach and is on channel 26.
verify(int(c2.get(wpan.WPAN_CHANNEL), 0) == 26)
#-----------------------------------------------------------------------------------------------------------------------
# Test finished
print '\'{}\' passed.'.format(test_name)