mirror of
https://github.com/espressif/openthread.git
synced 2026-10-02 22:07:36 +00:00
Energy scan. (#606)
This commit is contained in:
@@ -66,9 +66,63 @@ ThreadError otCommissionerStart(otInstance *aInstance, const char *aPSKd);
|
|||||||
*/
|
*/
|
||||||
ThreadError otCommissionerStop(otInstance *aInstance);
|
ThreadError otCommissionerStop(otInstance *aInstance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function pointer is called when the Commissioner receives an Energy Report.
|
||||||
|
*
|
||||||
|
* @param[in] aChannelMask The channel mask value.
|
||||||
|
* @param[in] aEnergyList A pointer to the energy measurement list.
|
||||||
|
* @param[in] aEnergyListLength Number of entries in @p aEnergyListLength.
|
||||||
|
* @param[in] aContext A pointer to application-specific context.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef void (*otCommissionerEnergyReportCallback)(uint32_t aChannelMask, const uint8_t *aEnergyList,
|
||||||
|
uint8_t aEnergyListLength, void *aContext);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function sends an Energy Scan Query message.
|
||||||
|
*
|
||||||
|
* @param[in] aInstance A pointer to an OpenThread instance.
|
||||||
|
* @param[in] aChannelMask The channel mask value.
|
||||||
|
* @param[in] aCount The number of energy measurements per channel.
|
||||||
|
* @param[in] aPeriod The time between energy measurements (milliseconds).
|
||||||
|
* @param[in] aScanDuration The scan duration for each energy measurement (milliseconds).
|
||||||
|
* @param[in] aAddress A pointer to the IPv6 destination.
|
||||||
|
* @param[in] aCallback A pointer to a function called on receiving an Energy Report message.
|
||||||
|
* @param[in] aContext A pointer to application-specific context.
|
||||||
|
*
|
||||||
|
* @retval kThreadError_None Successfully enqueued the Energy Scan Query message.
|
||||||
|
* @retval kThreadError_NoBufs Insufficient buffers to generate an Energy Scan Query message.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
ThreadError otCommissionerEnergyScan(otInstance *aInstance, uint32_t aChannelMask, uint8_t aCount, uint16_t aPeriod,
|
||||||
|
uint16_t aScanDuration, const otIp6Address *aAddress,
|
||||||
|
otCommissionerEnergyReportCallback aCallback, void *aContext);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function pointer is called when the Commissioner receives a PAN ID Conflict message.
|
||||||
|
*
|
||||||
|
* @param[in] aPanId The PAN ID value.
|
||||||
|
* @param[in] aChannelMask The channel mask value.
|
||||||
|
* @param[in] aContext A pointer to application-specific context.
|
||||||
|
*
|
||||||
|
*/
|
||||||
typedef void (*otCommissionerPanIdConflictCallback)(uint16_t aPanId, uint32_t aChannelMask, void *aContext);
|
typedef void (*otCommissionerPanIdConflictCallback)(uint16_t aPanId, uint32_t aChannelMask, void *aContext);
|
||||||
|
|
||||||
ThreadError otCommissionerPanIdQuery(otInstance *, uint16_t aPanId, uint32_t aChannelMask,
|
/**
|
||||||
|
* This function sends a PAN ID Query message.
|
||||||
|
*
|
||||||
|
* @param[in] aInstance A pointer to an OpenThread instance.
|
||||||
|
* @param[in] aPanId The PAN ID to query.
|
||||||
|
* @param[in] aChannelMask The channel mask value.
|
||||||
|
* @param[in] aAddress A pointer to the IPv6 destination.
|
||||||
|
* @param[in] aCallback A pointer to a function called on receiving an Energy Report message.
|
||||||
|
* @param[in] aContext A pointer to application-specific context.
|
||||||
|
*
|
||||||
|
* @retval kThreadError_None Successfully enqueued the PAN ID Query message.
|
||||||
|
* @retval kThreadError_NoBufs Insufficient buffers to generate a PAN ID Query message.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
ThreadError otCommissionerPanIdQuery(otInstance *aInstance, uint16_t aPanId, uint32_t aChannelMask,
|
||||||
const otIp6Address *aAddress,
|
const otIp6Address *aAddress,
|
||||||
otCommissionerPanIdConflictCallback aCallback, void *aContext);
|
otCommissionerPanIdConflictCallback aCallback, void *aContext);
|
||||||
|
|
||||||
|
|||||||
@@ -401,6 +401,10 @@ typedef enum otMeshcopTlvType
|
|||||||
OT_MESHCOP_TLV_PENDINGTIMESTAMP = 51, ///< meshcop Pending Timestamp TLV
|
OT_MESHCOP_TLV_PENDINGTIMESTAMP = 51, ///< meshcop Pending Timestamp TLV
|
||||||
OT_MESHCOP_TLV_DELAYTIMER = 52, ///< meshcop Delay Timer TLV
|
OT_MESHCOP_TLV_DELAYTIMER = 52, ///< meshcop Delay Timer TLV
|
||||||
OT_MESHCOP_TLV_CHANNELMASK = 53, ///< meshcop Channel Mask TLV
|
OT_MESHCOP_TLV_CHANNELMASK = 53, ///< meshcop Channel Mask TLV
|
||||||
|
OT_MESHCOP_TLV_COUNT = 54, ///< meshcop Count TLV
|
||||||
|
OT_MESHCOP_TLV_PERIOD = 55, ///< meshcop Period TLV
|
||||||
|
OT_MESHCOP_TLV_SCAN_DURATION = 56, ///< meshcop Scan Duration TLV
|
||||||
|
OT_MESHCOP_TLV_ENERGY_LIST = 57, ///< meshcop Energy List TLV
|
||||||
OT_MESHCOP_TLV_DISCOVERYREQUEST = 128, ///< meshcop Discovery Request TLV
|
OT_MESHCOP_TLV_DISCOVERYREQUEST = 128, ///< meshcop Discovery Request TLV
|
||||||
OT_MESHCOP_TLV_DISCOVERYRESPONSE = 129, ///< meshcop Discovery Response TLV
|
OT_MESHCOP_TLV_DISCOVERYRESPONSE = 129, ///< meshcop Discovery Response TLV
|
||||||
} otMeshcopTlvType;
|
} otMeshcopTlvType;
|
||||||
|
|||||||
+57
-2
@@ -1919,6 +1919,40 @@ void Interpreter::ProcessCommissioner(int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
otCommissionerStop(mInstance);
|
otCommissionerStop(mInstance);
|
||||||
}
|
}
|
||||||
|
else if (strcmp(argv[0], "energy") == 0)
|
||||||
|
{
|
||||||
|
long mask;
|
||||||
|
long count;
|
||||||
|
long period;
|
||||||
|
long scanDuration;
|
||||||
|
otIp6Address address;
|
||||||
|
|
||||||
|
VerifyOrExit(argc > 5, error = kThreadError_Parse);
|
||||||
|
|
||||||
|
// mask
|
||||||
|
SuccessOrExit(error = ParseLong(argv[1], mask));
|
||||||
|
|
||||||
|
// count
|
||||||
|
SuccessOrExit(error = ParseLong(argv[2], count));
|
||||||
|
|
||||||
|
// period
|
||||||
|
SuccessOrExit(error = ParseLong(argv[3], period));
|
||||||
|
|
||||||
|
// scan duration
|
||||||
|
SuccessOrExit(error = ParseLong(argv[4], scanDuration));
|
||||||
|
|
||||||
|
// destination
|
||||||
|
SuccessOrExit(error = otIp6AddressFromString(argv[5], &address));
|
||||||
|
|
||||||
|
SuccessOrExit(error = otCommissionerEnergyScan(mInstance,
|
||||||
|
static_cast<uint32_t>(mask),
|
||||||
|
static_cast<uint8_t>(count),
|
||||||
|
static_cast<uint16_t>(period),
|
||||||
|
static_cast<uint16_t>(scanDuration),
|
||||||
|
&address,
|
||||||
|
Interpreter::s_HandleEnergyReport,
|
||||||
|
this));
|
||||||
|
}
|
||||||
else if (strcmp(argv[0], "panid") == 0)
|
else if (strcmp(argv[0], "panid") == 0)
|
||||||
{
|
{
|
||||||
long panid;
|
long panid;
|
||||||
@@ -1936,15 +1970,36 @@ void Interpreter::ProcessCommissioner(int argc, char *argv[])
|
|||||||
// destination
|
// destination
|
||||||
SuccessOrExit(error = otIp6AddressFromString(argv[3], &address));
|
SuccessOrExit(error = otIp6AddressFromString(argv[3], &address));
|
||||||
|
|
||||||
SuccessOrExit(error = otCommissionerPanIdQuery(mInstance, static_cast<uint16_t>(panid),
|
SuccessOrExit(error = otCommissionerPanIdQuery(mInstance,
|
||||||
|
static_cast<uint16_t>(panid),
|
||||||
static_cast<uint32_t>(mask),
|
static_cast<uint32_t>(mask),
|
||||||
&address, Interpreter::s_HandlePanIdConflict, this));
|
&address,
|
||||||
|
Interpreter::s_HandlePanIdConflict,
|
||||||
|
this));
|
||||||
}
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
AppendResult(error);
|
AppendResult(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Interpreter::s_HandleEnergyReport(uint32_t aChannelMask, const uint8_t *aEnergyList, uint8_t aEnergyListLength,
|
||||||
|
void *aContext)
|
||||||
|
{
|
||||||
|
static_cast<Interpreter *>(aContext)->HandleEnergyReport(aChannelMask, aEnergyList, aEnergyListLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interpreter::HandleEnergyReport(uint32_t aChannelMask, const uint8_t *aEnergyList, uint8_t aEnergyListLength)
|
||||||
|
{
|
||||||
|
sServer->OutputFormat("Energy: %08x ", aChannelMask);
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < aEnergyListLength; i++)
|
||||||
|
{
|
||||||
|
sServer->OutputFormat("%d ", aEnergyList[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
sServer->OutputFormat("\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
void Interpreter::s_HandlePanIdConflict(uint16_t aPanId, uint32_t aChannelMask, void *aContext)
|
void Interpreter::s_HandlePanIdConflict(uint16_t aPanId, uint32_t aChannelMask, void *aContext)
|
||||||
{
|
{
|
||||||
static_cast<Interpreter *>(aContext)->HandlePanIdConflict(aPanId, aChannelMask);
|
static_cast<Interpreter *>(aContext)->HandlePanIdConflict(aPanId, aChannelMask);
|
||||||
|
|||||||
@@ -208,6 +208,8 @@ private:
|
|||||||
static void s_HandleActiveScanResult(otActiveScanResult *aResult, void *aContext);
|
static void s_HandleActiveScanResult(otActiveScanResult *aResult, void *aContext);
|
||||||
static void s_HandleNetifStateChanged(uint32_t aFlags, void *aContext);
|
static void s_HandleNetifStateChanged(uint32_t aFlags, void *aContext);
|
||||||
static void s_HandleLinkPcapReceive(const RadioPacket *aFrame, void *aContext);
|
static void s_HandleLinkPcapReceive(const RadioPacket *aFrame, void *aContext);
|
||||||
|
static void s_HandleEnergyReport(uint32_t aChannelMask, const uint8_t *aEnergyList, uint8_t aEnergyListLength,
|
||||||
|
void *aContext);
|
||||||
static void s_HandlePanIdConflict(uint16_t aPanId, uint32_t aChannelMask, void *aContext);
|
static void s_HandlePanIdConflict(uint16_t aPanId, uint32_t aChannelMask, void *aContext);
|
||||||
|
|
||||||
void HandleEchoResponse(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
|
void HandleEchoResponse(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
|
||||||
@@ -215,6 +217,7 @@ private:
|
|||||||
void HandleActiveScanResult(otActiveScanResult *aResult);
|
void HandleActiveScanResult(otActiveScanResult *aResult);
|
||||||
void HandleNetifStateChanged(uint32_t aFlags);
|
void HandleNetifStateChanged(uint32_t aFlags);
|
||||||
void HandleLinkPcapReceive(const RadioPacket *aFrame);
|
void HandleLinkPcapReceive(const RadioPacket *aFrame);
|
||||||
|
void HandleEnergyReport(uint32_t aChannelMask, const uint8_t *aEnergyList, uint8_t aEnergyListLength);
|
||||||
void HandlePanIdConflict(uint16_t aPanId, uint32_t aChannelMask);
|
void HandlePanIdConflict(uint16_t aPanId, uint32_t aChannelMask);
|
||||||
|
|
||||||
static const struct Command sCommands[];
|
static const struct Command sCommands[];
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ libopenthread_a_SOURCES = \
|
|||||||
net/netif.cpp \
|
net/netif.cpp \
|
||||||
net/udp6.cpp \
|
net/udp6.cpp \
|
||||||
thread/address_resolver.cpp \
|
thread/address_resolver.cpp \
|
||||||
|
thread/energy_scan_server.cpp \
|
||||||
thread/key_manager.cpp \
|
thread/key_manager.cpp \
|
||||||
thread/link_quality.cpp \
|
thread/link_quality.cpp \
|
||||||
thread/lowpan.cpp \
|
thread/lowpan.cpp \
|
||||||
@@ -84,6 +85,7 @@ libopenthread_a_SOURCES = \
|
|||||||
if OPENTHREAD_ENABLE_COMMISSIONER
|
if OPENTHREAD_ENABLE_COMMISSIONER
|
||||||
libopenthread_a_SOURCES += \
|
libopenthread_a_SOURCES += \
|
||||||
meshcop/commissioner.cpp \
|
meshcop/commissioner.cpp \
|
||||||
|
meshcop/energy_scan_client.cpp \
|
||||||
meshcop/panid_query_client.cpp \
|
meshcop/panid_query_client.cpp \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
endif # OPENTHREAD_ENABLE_COMMISSIONER
|
endif # OPENTHREAD_ENABLE_COMMISSIONER
|
||||||
@@ -124,6 +126,7 @@ noinst_HEADERS = \
|
|||||||
mac/mac_blacklist.hpp \
|
mac/mac_blacklist.hpp \
|
||||||
meshcop/commissioner.hpp \
|
meshcop/commissioner.hpp \
|
||||||
meshcop/dtls.hpp \
|
meshcop/dtls.hpp \
|
||||||
|
meshcop/energy_scan_client.hpp \
|
||||||
meshcop/joiner.hpp \
|
meshcop/joiner.hpp \
|
||||||
meshcop/joiner_router.hpp \
|
meshcop/joiner_router.hpp \
|
||||||
meshcop/leader.hpp \
|
meshcop/leader.hpp \
|
||||||
@@ -140,6 +143,7 @@ noinst_HEADERS = \
|
|||||||
net/udp6.hpp \
|
net/udp6.hpp \
|
||||||
net/tcp.hpp \
|
net/tcp.hpp \
|
||||||
thread/address_resolver.hpp \
|
thread/address_resolver.hpp \
|
||||||
|
thread/energy_scan_server.hpp \
|
||||||
thread/key_manager.hpp \
|
thread/key_manager.hpp \
|
||||||
thread/link_quality.hpp \
|
thread/link_quality.hpp \
|
||||||
thread/lowpan.hpp \
|
thread/lowpan.hpp \
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ namespace Thread {
|
|||||||
namespace MeshCoP {
|
namespace MeshCoP {
|
||||||
|
|
||||||
Commissioner::Commissioner(ThreadNetif &aThreadNetif):
|
Commissioner::Commissioner(ThreadNetif &aThreadNetif):
|
||||||
|
mEnergyScan(aThreadNetif),
|
||||||
mPanIdQuery(aThreadNetif),
|
mPanIdQuery(aThreadNetif),
|
||||||
mTimer(aThreadNetif.GetIp6().mTimerScheduler, HandleTimer, this),
|
mTimer(aThreadNetif.GetIp6().mTimerScheduler, HandleTimer, this),
|
||||||
mTransmitTask(aThreadNetif.GetIp6().mTaskletScheduler, &HandleUdpTransmit, this),
|
mTransmitTask(aThreadNetif.GetIp6().mTaskletScheduler, &HandleUdpTransmit, this),
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
|
|
||||||
#include <coap/coap_server.hpp>
|
#include <coap/coap_server.hpp>
|
||||||
#include <common/timer.hpp>
|
#include <common/timer.hpp>
|
||||||
|
#include <meshcop/energy_scan_client.hpp>
|
||||||
#include <meshcop/panid_query_client.hpp>
|
#include <meshcop/panid_query_client.hpp>
|
||||||
#include <net/udp6.hpp>
|
#include <net/udp6.hpp>
|
||||||
#include <thread/mle.hpp>
|
#include <thread/mle.hpp>
|
||||||
@@ -83,6 +84,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
uint16_t GetSessionId(void) const;
|
uint16_t GetSessionId(void) const;
|
||||||
|
|
||||||
|
EnergyScanClient mEnergyScan;
|
||||||
PanIdQueryClient mPanIdQuery;
|
PanIdQueryClient mPanIdQuery;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -0,0 +1,239 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016, 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* This file implements the Energy Scan Client.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <coap/coap_header.hpp>
|
||||||
|
#include <common/code_utils.hpp>
|
||||||
|
#include <common/debug.hpp>
|
||||||
|
#include <common/encoding.hpp>
|
||||||
|
#include <common/logging.hpp>
|
||||||
|
#include <meshcop/energy_scan_client.hpp>
|
||||||
|
#include <thread/meshcop_tlvs.hpp>
|
||||||
|
#include <thread/thread_netif.hpp>
|
||||||
|
#include <thread/thread_uris.hpp>
|
||||||
|
|
||||||
|
using Thread::Encoding::BigEndian::HostSwap16;
|
||||||
|
using Thread::Encoding::BigEndian::HostSwap32;
|
||||||
|
|
||||||
|
namespace Thread {
|
||||||
|
|
||||||
|
EnergyScanClient::EnergyScanClient(ThreadNetif &aThreadNetif) :
|
||||||
|
mEnergyScan(OPENTHREAD_URI_ENERGY_REPORT, &HandleReport, this),
|
||||||
|
mSocket(aThreadNetif.GetIp6().mUdp),
|
||||||
|
mTimer(aThreadNetif.GetIp6().mTimerScheduler, &HandleTimer, this),
|
||||||
|
mCoapServer(aThreadNetif.GetCoapServer()),
|
||||||
|
mNetif(aThreadNetif)
|
||||||
|
{
|
||||||
|
mCoapServer.AddResource(mEnergyScan);
|
||||||
|
mSocket.Open(HandleUdpReceive, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadError EnergyScanClient::SendQuery(uint32_t aChannelMask, uint8_t aCount, uint16_t aPeriod,
|
||||||
|
uint16_t aScanDuration, const Ip6::Address &aAddress,
|
||||||
|
otCommissionerEnergyReportCallback aCallback, void *aContext)
|
||||||
|
{
|
||||||
|
ThreadError error = kThreadError_None;
|
||||||
|
Coap::Header header;
|
||||||
|
MeshCoP::CommissionerSessionIdTlv sessionId;
|
||||||
|
MeshCoP::ChannelMaskTlv channelMask;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
MeshCoP::ChannelMaskEntry channelMaskEntry;
|
||||||
|
uint8_t channelMaskBuf[sizeof(MeshCoP::ChannelMaskEntry) + sizeof(aChannelMask)];
|
||||||
|
};
|
||||||
|
MeshCoP::CountTlv count;
|
||||||
|
MeshCoP::PeriodTlv period;
|
||||||
|
MeshCoP::ScanDurationTlv scanDuration;
|
||||||
|
Ip6::MessageInfo messageInfo;
|
||||||
|
Message *message;
|
||||||
|
|
||||||
|
header.Init();
|
||||||
|
header.SetVersion(1);
|
||||||
|
header.SetType(aAddress.IsMulticast() ? Coap::Header::kTypeNonConfirmable : Coap::Header::kTypeConfirmable);
|
||||||
|
header.SetCode(Coap::Header::kCodePost);
|
||||||
|
header.SetMessageId(0);
|
||||||
|
header.SetToken(NULL, 0);
|
||||||
|
header.AppendUriPathOptions(OPENTHREAD_URI_ENERGY_SCAN);
|
||||||
|
header.AppendContentFormatOption(Coap::Header::kApplicationOctetStream);
|
||||||
|
header.Finalize();
|
||||||
|
|
||||||
|
VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, error = kThreadError_NoBufs);
|
||||||
|
SuccessOrExit(error = message->Append(header.GetBytes(), header.GetLength()));
|
||||||
|
|
||||||
|
sessionId.Init();
|
||||||
|
sessionId.SetCommissionerSessionId(mNetif.GetCommissioner().GetSessionId());
|
||||||
|
SuccessOrExit(error = message->Append(&sessionId, sizeof(sessionId)));
|
||||||
|
|
||||||
|
channelMask.Init();
|
||||||
|
channelMask.SetLength(sizeof(channelMaskBuf));
|
||||||
|
SuccessOrExit(error = message->Append(&channelMask, sizeof(channelMask)));
|
||||||
|
|
||||||
|
channelMaskEntry.SetChannelPage(0);
|
||||||
|
channelMaskEntry.SetMaskLength(sizeof(aChannelMask));
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sizeof(aChannelMask); i++)
|
||||||
|
{
|
||||||
|
channelMaskBuf[sizeof(MeshCoP::ChannelMaskEntry) + i] = (aChannelMask >> (8 * i)) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
SuccessOrExit(error = message->Append(channelMaskBuf, sizeof(channelMaskBuf)));
|
||||||
|
|
||||||
|
count.Init();
|
||||||
|
count.SetCount(aCount);
|
||||||
|
SuccessOrExit(error = message->Append(&count, sizeof(count)));
|
||||||
|
|
||||||
|
period.Init();
|
||||||
|
period.SetPeriod(aPeriod);
|
||||||
|
SuccessOrExit(error = message->Append(&period, sizeof(period)));
|
||||||
|
|
||||||
|
scanDuration.Init();
|
||||||
|
scanDuration.SetScanDuration(aScanDuration);
|
||||||
|
SuccessOrExit(error = message->Append(&scanDuration, sizeof(scanDuration)));
|
||||||
|
|
||||||
|
memset(&messageInfo, 0, sizeof(messageInfo));
|
||||||
|
messageInfo.GetPeerAddr() = aAddress;
|
||||||
|
messageInfo.mPeerPort = kCoapUdpPort;
|
||||||
|
messageInfo.mInterfaceId = mNetif.GetInterfaceId();
|
||||||
|
SuccessOrExit(error = mSocket.SendTo(*message, messageInfo));
|
||||||
|
|
||||||
|
otLogInfoMeshCoP("sent energy scan query\r\n");
|
||||||
|
|
||||||
|
mCallback = aCallback;
|
||||||
|
mContext = aContext;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
|
||||||
|
if (error != kThreadError_None && message != NULL)
|
||||||
|
{
|
||||||
|
message->Free();
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnergyScanClient::HandleReport(void *aContext, Coap::Header &aHeader, Message &aMessage,
|
||||||
|
const Ip6::MessageInfo &aMessageInfo)
|
||||||
|
{
|
||||||
|
static_cast<EnergyScanClient *>(aContext)->HandleReport(aHeader, aMessage, aMessageInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnergyScanClient::HandleReport(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
|
||||||
|
{
|
||||||
|
OT_TOOL_PACKED_BEGIN
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
MeshCoP::ChannelMaskTlv tlv;
|
||||||
|
MeshCoP::ChannelMaskEntry header;
|
||||||
|
uint32_t mask;
|
||||||
|
} OT_TOOL_PACKED_END channelMask;
|
||||||
|
|
||||||
|
OT_TOOL_PACKED_BEGIN
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
MeshCoP::EnergyListTlv tlv;
|
||||||
|
uint8_t list[OPENTHREAD_CONFIG_MAX_ENERGY_RESULTS];
|
||||||
|
} OT_TOOL_PACKED_END energyList;
|
||||||
|
|
||||||
|
VerifyOrExit(aHeader.GetType() == Coap::Header::kTypeConfirmable &&
|
||||||
|
aHeader.GetCode() == Coap::Header::kCodePost, ;);
|
||||||
|
|
||||||
|
otLogInfoMeshCoP("received energy scan report\r\n");
|
||||||
|
|
||||||
|
SuccessOrExit(MeshCoP::Tlv::GetTlv(aMessage, MeshCoP::Tlv::kChannelMask, sizeof(channelMask), channelMask.tlv));
|
||||||
|
VerifyOrExit(channelMask.tlv.IsValid() &&
|
||||||
|
channelMask.header.GetChannelPage() == 0 &&
|
||||||
|
channelMask.header.GetMaskLength() >= sizeof(uint32_t), ;);
|
||||||
|
|
||||||
|
SuccessOrExit(MeshCoP::Tlv::GetTlv(aMessage, MeshCoP::Tlv::kEnergyList, sizeof(energyList), energyList.tlv));
|
||||||
|
VerifyOrExit(energyList.tlv.IsValid(), ;);
|
||||||
|
|
||||||
|
if (mCallback != NULL)
|
||||||
|
{
|
||||||
|
mCallback(HostSwap32(channelMask.mask), energyList.list, energyList.tlv.GetLength(), mContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
SendResponse(aHeader, aMessageInfo);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadError EnergyScanClient::SendResponse(const Coap::Header &aRequestHeader, const Ip6::MessageInfo &aRequestInfo)
|
||||||
|
{
|
||||||
|
ThreadError error = kThreadError_None;
|
||||||
|
Message *message;
|
||||||
|
Coap::Header responseHeader;
|
||||||
|
Ip6::MessageInfo responseInfo;
|
||||||
|
|
||||||
|
VerifyOrExit((message = mCoapServer.NewMessage(0)) != NULL, error = kThreadError_NoBufs);
|
||||||
|
|
||||||
|
responseHeader.Init();
|
||||||
|
responseHeader.SetVersion(1);
|
||||||
|
responseHeader.SetType(Coap::Header::kTypeAcknowledgment);
|
||||||
|
responseHeader.SetCode(Coap::Header::kCodeChanged);
|
||||||
|
responseHeader.SetMessageId(aRequestHeader.GetMessageId());
|
||||||
|
responseHeader.SetToken(aRequestHeader.GetToken(), aRequestHeader.GetTokenLength());
|
||||||
|
responseHeader.Finalize();
|
||||||
|
SuccessOrExit(error = message->Append(responseHeader.GetBytes(), responseHeader.GetLength()));
|
||||||
|
|
||||||
|
memcpy(&responseInfo, &aRequestInfo, sizeof(responseInfo));
|
||||||
|
memset(&responseInfo.mSockAddr, 0, sizeof(responseInfo.mSockAddr));
|
||||||
|
SuccessOrExit(error = mCoapServer.SendMessage(*message, responseInfo));
|
||||||
|
|
||||||
|
exit:
|
||||||
|
|
||||||
|
if (error != kThreadError_None && message != NULL)
|
||||||
|
{
|
||||||
|
message->Free();
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnergyScanClient::HandleTimer(void *aContext)
|
||||||
|
{
|
||||||
|
static_cast<EnergyScanClient *>(aContext)->HandleTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnergyScanClient::HandleTimer(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnergyScanClient::HandleUdpReceive(void *aContext, otMessage aMessage, const otMessageInfo *aMessageInfo)
|
||||||
|
{
|
||||||
|
otLogInfoMeshCoP("received energy scan query response\r\n");
|
||||||
|
(void)aContext;
|
||||||
|
(void)aMessage;
|
||||||
|
(void)aMessageInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Thread
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016, 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* This file includes definitions for responding to PANID Query Requests.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ENERGY_SCAN_CLIENT_HPP_
|
||||||
|
#define ENERGY_SCAN_CLIENT_HPP_
|
||||||
|
|
||||||
|
#include <openthread-core-config.h>
|
||||||
|
#include <openthread-types.h>
|
||||||
|
#include <commissioning/commissioner.h>
|
||||||
|
#include <coap/coap_server.hpp>
|
||||||
|
#include <common/timer.hpp>
|
||||||
|
#include <net/ip6_address.hpp>
|
||||||
|
#include <net/udp6.hpp>
|
||||||
|
|
||||||
|
namespace Thread {
|
||||||
|
|
||||||
|
class ThreadNetif;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class implements handling PANID Query Requests.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class EnergyScanClient
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* This constructor initializes the object.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
EnergyScanClient(ThreadNetif &aThreadNetif);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sends an Energy Scan Query message.
|
||||||
|
*
|
||||||
|
* @param[in] aChannelMask The channel mask value.
|
||||||
|
* @param[in] aCount The number of energy measurements per channel.
|
||||||
|
* @param[in] aPeriod The time between energy measurements (milliseconds).
|
||||||
|
* @param[in] aScanDuration The scan duration for each energy measurement (milliseconds).
|
||||||
|
* @param[in] aAddress A pointer to the IPv6 destination.
|
||||||
|
* @param[in] aCallback A pointer to a function called on receiving an Energy Report message.
|
||||||
|
* @param[in] aContext A pointer to application-specific context.
|
||||||
|
*
|
||||||
|
* @retval kThreadError_None Successfully enqueued the Energy Scan Query message.
|
||||||
|
* @retval kThreadError_NoBufs Insufficient buffers to generate an Energy Scan Query message.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
ThreadError SendQuery(uint32_t aChannelMask, uint8_t aCount, uint16_t aPeriod, uint16_t aScanDuration,
|
||||||
|
const Ip6::Address &aAddress, otCommissionerEnergyReportCallback aCallback, void *aContext);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void HandleReport(void *aContext, Coap::Header &aHeader, Message &aMessage,
|
||||||
|
const Ip6::MessageInfo &aMessageInfo);
|
||||||
|
void HandleReport(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
|
||||||
|
|
||||||
|
static void HandleTimer(void *aContext);
|
||||||
|
void HandleTimer(void);
|
||||||
|
|
||||||
|
static void HandleUdpReceive(void *aContext, otMessage aMessage, const otMessageInfo *aMessageInfo);
|
||||||
|
|
||||||
|
ThreadError SendResponse(const Coap::Header &aRequestHeader, const Ip6::MessageInfo &aRequestMessageInfo);
|
||||||
|
|
||||||
|
otCommissionerEnergyReportCallback mCallback;
|
||||||
|
void *mContext;
|
||||||
|
|
||||||
|
Coap::Resource mEnergyScan;
|
||||||
|
Ip6::UdpSocket mSocket;
|
||||||
|
Timer mTimer;
|
||||||
|
|
||||||
|
Coap::Server &mCoapServer;
|
||||||
|
ThreadNetif &mNetif;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} // namespace Thread
|
||||||
|
|
||||||
|
#endif // ENERGY_SCAN_CLIENT_HPP_
|
||||||
@@ -59,6 +59,19 @@ public:
|
|||||||
*/
|
*/
|
||||||
PanIdQueryClient(ThreadNetif &aThreadNetif);
|
PanIdQueryClient(ThreadNetif &aThreadNetif);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sends a PAN ID Query message.
|
||||||
|
*
|
||||||
|
* @param[in] aPanId The PAN ID to query.
|
||||||
|
* @param[in] aChannelMask The channel mask value.
|
||||||
|
* @param[in] aAddress A pointer to the IPv6 destination.
|
||||||
|
* @param[in] aCallback A pointer to a function called on receiving an Energy Report message.
|
||||||
|
* @param[in] aContext A pointer to application-specific context.
|
||||||
|
*
|
||||||
|
* @retval kThreadError_None Successfully enqueued the PAN ID Query message.
|
||||||
|
* @retval kThreadError_NoBufs Insufficient buffers to generate a PAN ID Query message.
|
||||||
|
*
|
||||||
|
*/
|
||||||
ThreadError SendQuery(uint16_t aPanId, uint32_t aChannelMask, const Ip6::Address &aAddress,
|
ThreadError SendQuery(uint16_t aPanId, uint32_t aChannelMask, const Ip6::Address &aAddress,
|
||||||
otCommissionerPanIdConflictCallback aCallback, void *aContext);
|
otCommissionerPanIdConflictCallback aCallback, void *aContext);
|
||||||
|
|
||||||
|
|||||||
@@ -165,6 +165,16 @@
|
|||||||
#define OPENTHREAD_CONFIG_JOINER_UDP_PORT 1000
|
#define OPENTHREAD_CONFIG_JOINER_UDP_PORT 1000
|
||||||
#endif // OPENTHREAD_CONFIG_JOINER_UDP_PORT
|
#endif // OPENTHREAD_CONFIG_JOINER_UDP_PORT
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def OPENTHREAD_CONFIG_MAX_ENERGY_RESULTS
|
||||||
|
*
|
||||||
|
* The maximum number of Energy List entries.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef OPENTHREAD_CONFIG_MAX_ENERGY_RESULTS
|
||||||
|
#define OPENTHREAD_CONFIG_MAX_ENERGY_RESULTS 64
|
||||||
|
#endif // OPENTHREAD_CONFIG_MAX_ENERGY_RESULTS
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @def OPENTHREAD_CONFIG_LOG_LEVEL
|
* @def OPENTHREAD_CONFIG_LOG_LEVEL
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1325,6 +1325,15 @@ ThreadError otCommissionerStop(otInstance *)
|
|||||||
return sThreadNetif->GetCommissioner().Stop();
|
return sThreadNetif->GetCommissioner().Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThreadError otCommissionerEnergyScan(otInstance *, uint32_t aChannelMask, uint8_t aCount, uint16_t aPeriod,
|
||||||
|
uint16_t aScanDuration, const otIp6Address *aAddress,
|
||||||
|
otCommissionerEnergyReportCallback aCallback, void *aContext)
|
||||||
|
{
|
||||||
|
return sThreadNetif->GetCommissioner().mEnergyScan.SendQuery(aChannelMask, aCount, aPeriod, aScanDuration,
|
||||||
|
*static_cast<const Ip6::Address *>(aAddress),
|
||||||
|
aCallback, aContext);
|
||||||
|
}
|
||||||
|
|
||||||
ThreadError otCommissionerPanIdQuery(otInstance *, uint16_t aPanId, uint32_t aChannelMask,
|
ThreadError otCommissionerPanIdQuery(otInstance *, uint16_t aPanId, uint32_t aChannelMask,
|
||||||
const otIp6Address *aAddress,
|
const otIp6Address *aAddress,
|
||||||
otCommissionerPanIdConflictCallback aCallback, void *aContext)
|
otCommissionerPanIdConflictCallback aCallback, void *aContext)
|
||||||
|
|||||||
@@ -0,0 +1,301 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016, 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* This file implements the Energy Scan Server.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <coap/coap_header.hpp>
|
||||||
|
#include <common/code_utils.hpp>
|
||||||
|
#include <common/debug.hpp>
|
||||||
|
#include <common/logging.hpp>
|
||||||
|
#include <thread/meshcop_tlvs.hpp>
|
||||||
|
#include <thread/energy_scan_server.hpp>
|
||||||
|
#include <thread/thread_netif.hpp>
|
||||||
|
#include <thread/thread_uris.hpp>
|
||||||
|
|
||||||
|
namespace Thread {
|
||||||
|
|
||||||
|
EnergyScanServer::EnergyScanServer(ThreadNetif &aThreadNetif) :
|
||||||
|
mActive(false),
|
||||||
|
mEnergyScan(OPENTHREAD_URI_ENERGY_SCAN, &HandleRequest, this),
|
||||||
|
mSocket(aThreadNetif.GetIp6().mUdp),
|
||||||
|
mTimer(aThreadNetif.GetIp6().mTimerScheduler, &HandleTimer, this),
|
||||||
|
mCoapServer(aThreadNetif.GetCoapServer()),
|
||||||
|
mNetif(aThreadNetif)
|
||||||
|
{
|
||||||
|
mNetifCallback.Set(&HandleNetifStateChanged, this);
|
||||||
|
mNetif.RegisterCallback(mNetifCallback);
|
||||||
|
|
||||||
|
mCoapServer.AddResource(mEnergyScan);
|
||||||
|
mSocket.Open(HandleUdpReceive, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnergyScanServer::HandleRequest(void *aContext, Coap::Header &aHeader, Message &aMessage,
|
||||||
|
const Ip6::MessageInfo &aMessageInfo)
|
||||||
|
{
|
||||||
|
static_cast<EnergyScanServer *>(aContext)->HandleRequest(aHeader, aMessage, aMessageInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnergyScanServer::HandleRequest(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
|
||||||
|
{
|
||||||
|
MeshCoP::CountTlv count;
|
||||||
|
MeshCoP::PeriodTlv period;
|
||||||
|
MeshCoP::ScanDurationTlv scanDuration;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
MeshCoP::ChannelMaskEntry channelMaskEntry;
|
||||||
|
uint8_t channelMaskBuf[sizeof(MeshCoP::ChannelMaskEntry) + sizeof(uint32_t)];
|
||||||
|
};
|
||||||
|
uint16_t offset;
|
||||||
|
uint16_t length;
|
||||||
|
|
||||||
|
VerifyOrExit(aHeader.GetCode() == Coap::Header::kCodePost, ;);
|
||||||
|
|
||||||
|
SuccessOrExit(MeshCoP::Tlv::GetTlv(aMessage, MeshCoP::Tlv::kCount, sizeof(count), count));
|
||||||
|
VerifyOrExit(count.IsValid(), ;);
|
||||||
|
|
||||||
|
SuccessOrExit(MeshCoP::Tlv::GetTlv(aMessage, MeshCoP::Tlv::kPeriod, sizeof(period), period));
|
||||||
|
VerifyOrExit(period.IsValid(), ;);
|
||||||
|
|
||||||
|
SuccessOrExit(MeshCoP::Tlv::GetTlv(aMessage, MeshCoP::Tlv::kScanDuration, sizeof(scanDuration), scanDuration));
|
||||||
|
VerifyOrExit(scanDuration.IsValid(), ;);
|
||||||
|
|
||||||
|
SuccessOrExit(MeshCoP::Tlv::GetValueOffset(aMessage, MeshCoP::Tlv::kChannelMask, offset, length));
|
||||||
|
aMessage.Read(offset, sizeof(channelMaskBuf), channelMaskBuf);
|
||||||
|
VerifyOrExit(channelMaskEntry.GetChannelPage() == 0 &&
|
||||||
|
channelMaskEntry.GetMaskLength() == sizeof(uint32_t), ;);
|
||||||
|
|
||||||
|
mChannelMask =
|
||||||
|
(static_cast<uint32_t>(channelMaskBuf[sizeof(MeshCoP::ChannelMaskEntry) + 0]) << 0) |
|
||||||
|
(static_cast<uint32_t>(channelMaskBuf[sizeof(MeshCoP::ChannelMaskEntry) + 1]) << 8) |
|
||||||
|
(static_cast<uint32_t>(channelMaskBuf[sizeof(MeshCoP::ChannelMaskEntry) + 2]) << 16) |
|
||||||
|
(static_cast<uint32_t>(channelMaskBuf[sizeof(MeshCoP::ChannelMaskEntry) + 3]) << 24);
|
||||||
|
|
||||||
|
mChannelMaskCurrent = mChannelMask;
|
||||||
|
mCount = count.GetCount();
|
||||||
|
mPeriod = period.GetPeriod();
|
||||||
|
mScanDuration = scanDuration.GetScanDuration();
|
||||||
|
mScanResultsLength = 0;
|
||||||
|
mActive = true;
|
||||||
|
mTimer.Start(kScanDelay);
|
||||||
|
|
||||||
|
mCommissioner = aMessageInfo.GetPeerAddr();
|
||||||
|
SendResponse(aHeader, aMessageInfo);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadError EnergyScanServer::SendResponse(const Coap::Header &aRequestHeader, const Ip6::MessageInfo &aRequestInfo)
|
||||||
|
{
|
||||||
|
ThreadError error = kThreadError_None;
|
||||||
|
Message *message;
|
||||||
|
Coap::Header responseHeader;
|
||||||
|
Ip6::MessageInfo responseInfo;
|
||||||
|
|
||||||
|
VerifyOrExit(aRequestHeader.GetType() == Coap::Header::kTypeConfirmable, ;);
|
||||||
|
|
||||||
|
VerifyOrExit((message = mCoapServer.NewMessage(0)) != NULL, error = kThreadError_NoBufs);
|
||||||
|
|
||||||
|
responseHeader.Init();
|
||||||
|
responseHeader.SetVersion(1);
|
||||||
|
responseHeader.SetType(Coap::Header::kTypeAcknowledgment);
|
||||||
|
responseHeader.SetCode(Coap::Header::kCodeChanged);
|
||||||
|
responseHeader.SetMessageId(aRequestHeader.GetMessageId());
|
||||||
|
responseHeader.SetToken(aRequestHeader.GetToken(), aRequestHeader.GetTokenLength());
|
||||||
|
responseHeader.Finalize();
|
||||||
|
SuccessOrExit(error = message->Append(responseHeader.GetBytes(), responseHeader.GetLength()));
|
||||||
|
|
||||||
|
memcpy(&responseInfo, &aRequestInfo, sizeof(responseInfo));
|
||||||
|
memset(&responseInfo.mSockAddr, 0, sizeof(responseInfo.mSockAddr));
|
||||||
|
SuccessOrExit(error = mCoapServer.SendMessage(*message, responseInfo));
|
||||||
|
|
||||||
|
otLogInfoMeshCoP("sent energy scan query response\r\n");
|
||||||
|
|
||||||
|
exit:
|
||||||
|
|
||||||
|
if (error != kThreadError_None && message != NULL)
|
||||||
|
{
|
||||||
|
message->Free();
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnergyScanServer::HandleTimer(void *aContext)
|
||||||
|
{
|
||||||
|
static_cast<EnergyScanServer *>(aContext)->HandleTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnergyScanServer::HandleTimer(void)
|
||||||
|
{
|
||||||
|
VerifyOrExit(mActive, ;);
|
||||||
|
|
||||||
|
if (mCount)
|
||||||
|
{
|
||||||
|
// grab the lowest channel to scan
|
||||||
|
uint32_t channelMask = mChannelMaskCurrent & ~(mChannelMaskCurrent - 1);
|
||||||
|
otLogInfoMeshCoP("%x\r\n", channelMask);
|
||||||
|
mNetif.GetMac().EnergyScan(channelMask, mScanDuration, HandleScanResult, this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SendReport();
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnergyScanServer::HandleScanResult(void *aContext, otEnergyScanResult *aResult)
|
||||||
|
{
|
||||||
|
static_cast<EnergyScanServer *>(aContext)->HandleScanResult(aResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnergyScanServer::HandleScanResult(otEnergyScanResult *aResult)
|
||||||
|
{
|
||||||
|
VerifyOrExit(mActive, ;);
|
||||||
|
|
||||||
|
if (aResult)
|
||||||
|
{
|
||||||
|
mScanResults[mScanResultsLength++] = aResult->mMaxRssi;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// clear the lowest channel to scan
|
||||||
|
mChannelMaskCurrent &= mChannelMaskCurrent - 1;
|
||||||
|
|
||||||
|
if (mChannelMaskCurrent == 0)
|
||||||
|
{
|
||||||
|
mChannelMaskCurrent = mChannelMask;
|
||||||
|
mCount--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mCount)
|
||||||
|
{
|
||||||
|
mTimer.Start(mPeriod);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mTimer.Start(kReportDelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadError EnergyScanServer::SendReport(void)
|
||||||
|
{
|
||||||
|
ThreadError error = kThreadError_None;
|
||||||
|
Coap::Header header;
|
||||||
|
|
||||||
|
OT_TOOL_PACKED_BEGIN
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
MeshCoP::ChannelMaskTlv tlv;
|
||||||
|
MeshCoP::ChannelMaskEntry entry;
|
||||||
|
uint32_t mask;
|
||||||
|
} OT_TOOL_PACKED_END channelMask;
|
||||||
|
|
||||||
|
MeshCoP::EnergyListTlv energyList;
|
||||||
|
Ip6::MessageInfo messageInfo;
|
||||||
|
Message *message;
|
||||||
|
|
||||||
|
header.Init();
|
||||||
|
header.SetVersion(1);
|
||||||
|
header.SetType(Coap::Header::kTypeConfirmable);
|
||||||
|
header.SetCode(Coap::Header::kCodePost);
|
||||||
|
header.SetMessageId(0);
|
||||||
|
header.SetToken(NULL, 0);
|
||||||
|
header.AppendUriPathOptions(OPENTHREAD_URI_ENERGY_REPORT);
|
||||||
|
header.AppendContentFormatOption(Coap::Header::kApplicationOctetStream);
|
||||||
|
header.Finalize();
|
||||||
|
|
||||||
|
VerifyOrExit((message = mSocket.NewMessage(0)) != NULL, error = kThreadError_NoBufs);
|
||||||
|
SuccessOrExit(error = message->Append(header.GetBytes(), header.GetLength()));
|
||||||
|
|
||||||
|
channelMask.tlv.Init();
|
||||||
|
channelMask.tlv.SetLength(sizeof(channelMask) - sizeof(channelMask.tlv));
|
||||||
|
channelMask.entry.SetChannelPage(0);
|
||||||
|
channelMask.entry.SetMaskLength(sizeof(channelMask.mask));
|
||||||
|
channelMask.mask = HostSwap32(mChannelMask);
|
||||||
|
SuccessOrExit(error = message->Append(&channelMask, sizeof(channelMask)));
|
||||||
|
|
||||||
|
energyList.Init();
|
||||||
|
energyList.SetLength(mScanResultsLength);
|
||||||
|
SuccessOrExit(error = message->Append(&energyList, sizeof(energyList)));
|
||||||
|
SuccessOrExit(error = message->Append(mScanResults, mScanResultsLength));
|
||||||
|
|
||||||
|
memset(&messageInfo, 0, sizeof(messageInfo));
|
||||||
|
messageInfo.GetPeerAddr() = mCommissioner;
|
||||||
|
messageInfo.mPeerPort = kCoapUdpPort;
|
||||||
|
SuccessOrExit(error = mSocket.SendTo(*message, messageInfo));
|
||||||
|
|
||||||
|
otLogInfoMeshCoP("sent scan results\r\n");
|
||||||
|
|
||||||
|
exit:
|
||||||
|
|
||||||
|
if (error != kThreadError_None && message != NULL)
|
||||||
|
{
|
||||||
|
message->Free();
|
||||||
|
}
|
||||||
|
|
||||||
|
mActive = false;
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnergyScanServer::HandleUdpReceive(void *aContext, otMessage aMessage, const otMessageInfo *aMessageInfo)
|
||||||
|
{
|
||||||
|
(void)aContext;
|
||||||
|
(void)aMessage;
|
||||||
|
(void)aMessageInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnergyScanServer::HandleNetifStateChanged(uint32_t aFlags, void *aContext)
|
||||||
|
{
|
||||||
|
static_cast<EnergyScanServer *>(aContext)->HandleNetifStateChanged(aFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnergyScanServer::HandleNetifStateChanged(uint32_t aFlags)
|
||||||
|
{
|
||||||
|
uint8_t length;
|
||||||
|
|
||||||
|
if ((aFlags & OT_THREAD_NETDATA_UPDATED) != 0 &&
|
||||||
|
!mActive &&
|
||||||
|
mNetif.GetNetworkDataLeader().GetCommissioningData(length) == NULL)
|
||||||
|
{
|
||||||
|
mActive = false;
|
||||||
|
mTimer.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Thread
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016, 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* This file includes definitions for responding to Energy Scan Requests.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ENERGY_SCAN_SERVER_HPP_
|
||||||
|
#define ENERGY_SCAN_SERVER_HPP_
|
||||||
|
|
||||||
|
#include <openthread-core-config.h>
|
||||||
|
#include <openthread-types.h>
|
||||||
|
#include <coap/coap_server.hpp>
|
||||||
|
#include <common/timer.hpp>
|
||||||
|
#include <net/ip6_address.hpp>
|
||||||
|
#include <net/udp6.hpp>
|
||||||
|
|
||||||
|
namespace Thread {
|
||||||
|
|
||||||
|
class MeshForwarder;
|
||||||
|
class ThreadLastTransactionTimeTlv;
|
||||||
|
class ThreadMeshLocalEidTlv;
|
||||||
|
class ThreadNetif;
|
||||||
|
class ThreadTargetTlv;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class implements handling Energy Scan Requests.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class EnergyScanServer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* This constructor initializes the object.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
EnergyScanServer(ThreadNetif &aThreadNetif);
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
kScanDelay = 1000, ///< SCAN_DELAY (milliseconds)
|
||||||
|
kReportDelay = 500, ///< Delay before sending a report (milliseconds)
|
||||||
|
};
|
||||||
|
|
||||||
|
static void HandleRequest(void *aContext, Coap::Header &aHeader, Message &aMessage,
|
||||||
|
const Ip6::MessageInfo &aMessageInfo);
|
||||||
|
void HandleRequest(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
|
||||||
|
|
||||||
|
static void HandleScanResult(void *aContext, otEnergyScanResult *aResult);
|
||||||
|
void HandleScanResult(otEnergyScanResult *aResult);
|
||||||
|
|
||||||
|
static void HandleTimer(void *aContext);
|
||||||
|
void HandleTimer(void);
|
||||||
|
|
||||||
|
static void HandleUdpReceive(void *aContext, otMessage aMessage, const otMessageInfo *aMessageInfo);
|
||||||
|
|
||||||
|
static void HandleNetifStateChanged(uint32_t aFlags, void *aContext);
|
||||||
|
void HandleNetifStateChanged(uint32_t aFlags);
|
||||||
|
|
||||||
|
ThreadError SendReport();
|
||||||
|
ThreadError SendResponse(const Coap::Header &aRequestHeader, const Ip6::MessageInfo &aRequestMessageInfo);
|
||||||
|
|
||||||
|
Ip6::Address mCommissioner;
|
||||||
|
uint32_t mChannelMask;
|
||||||
|
uint32_t mChannelMaskCurrent;
|
||||||
|
uint16_t mPeriod;
|
||||||
|
uint16_t mScanDuration;
|
||||||
|
uint8_t mCount;
|
||||||
|
bool mActive;
|
||||||
|
|
||||||
|
int8_t mScanResults[OPENTHREAD_CONFIG_MAX_ENERGY_RESULTS];
|
||||||
|
uint8_t mScanResultsLength;
|
||||||
|
|
||||||
|
Coap::Resource mEnergyScan;
|
||||||
|
Ip6::UdpSocket mSocket;
|
||||||
|
Timer mTimer;
|
||||||
|
|
||||||
|
Ip6::NetifCallback mNetifCallback;
|
||||||
|
|
||||||
|
Coap::Server &mCoapServer;
|
||||||
|
ThreadNetif &mNetif;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} // namespace Thread
|
||||||
|
|
||||||
|
#endif // ENERGY_SCAN_SERVER_HPP_
|
||||||
@@ -83,6 +83,10 @@ public:
|
|||||||
kPendingTimestamp = OT_MESHCOP_TLV_PENDINGTIMESTAMP, ///< Pending Timestamp TLV
|
kPendingTimestamp = OT_MESHCOP_TLV_PENDINGTIMESTAMP, ///< Pending Timestamp TLV
|
||||||
kDelayTimer = OT_MESHCOP_TLV_DELAYTIMER, ///< Delay Timer TLV
|
kDelayTimer = OT_MESHCOP_TLV_DELAYTIMER, ///< Delay Timer TLV
|
||||||
kChannelMask = OT_MESHCOP_TLV_CHANNELMASK, ///< Channel Mask TLV
|
kChannelMask = OT_MESHCOP_TLV_CHANNELMASK, ///< Channel Mask TLV
|
||||||
|
kCount = OT_MESHCOP_TLV_COUNT, ///< Count TLV
|
||||||
|
kPeriod = OT_MESHCOP_TLV_PERIOD, ///< Period TLV
|
||||||
|
kScanDuration = OT_MESHCOP_TLV_SCAN_DURATION, ///< Scan Duration TLV
|
||||||
|
kEnergyList = OT_MESHCOP_TLV_ENERGY_LIST, ///< Energy List TLV
|
||||||
kDiscoveryRequest = OT_MESHCOP_TLV_DISCOVERYREQUEST, ///< Discovery Request TLV
|
kDiscoveryRequest = OT_MESHCOP_TLV_DISCOVERYREQUEST, ///< Discovery Request TLV
|
||||||
kDiscoveryResponse = OT_MESHCOP_TLV_DISCOVERYRESPONSE, ///< Discovery Response TLV
|
kDiscoveryResponse = OT_MESHCOP_TLV_DISCOVERYRESPONSE, ///< Discovery Response TLV
|
||||||
};
|
};
|
||||||
@@ -1289,6 +1293,159 @@ public:
|
|||||||
bool IsValid(void) const { return true; }
|
bool IsValid(void) const { return true; }
|
||||||
} OT_TOOL_PACKED_END;
|
} OT_TOOL_PACKED_END;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class implements Count TLV generation and parsing.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
OT_TOOL_PACKED_BEGIN
|
||||||
|
class CountTlv: public Tlv
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* This method initializes the TLV.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void Init(void) { SetType(kCount); SetLength(sizeof(*this) - sizeof(Tlv)); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method indicates whether or not the TLV appears to be well-formed.
|
||||||
|
*
|
||||||
|
* @retval TRUE If the TLV appears to be well-formed.
|
||||||
|
* @retval FALSE If the TLV does not appear to be well-formed.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(Tlv); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the Count value.
|
||||||
|
*
|
||||||
|
* @returns The Count value.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
uint8_t GetCount(void) const { return mCount; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets the Count value.
|
||||||
|
*
|
||||||
|
* @param[in] aCount The Count value.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void SetCount(uint8_t aCount) { mCount = aCount; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t mCount;
|
||||||
|
} OT_TOOL_PACKED_END;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class implements Period TLV generation and parsing.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
OT_TOOL_PACKED_BEGIN
|
||||||
|
class PeriodTlv: public Tlv
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* This method initializes the TLV.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void Init(void) { SetType(kPeriod); SetLength(sizeof(*this) - sizeof(Tlv)); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method indicates whether or not the TLV appears to be well-formed.
|
||||||
|
*
|
||||||
|
* @retval TRUE If the TLV appears to be well-formed.
|
||||||
|
* @retval FALSE If the TLV does not appear to be well-formed.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(Tlv); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the Period value.
|
||||||
|
*
|
||||||
|
* @returns The Period value.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
uint16_t GetPeriod(void) const { return HostSwap16(mPeriod); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets the Period value.
|
||||||
|
*
|
||||||
|
* @param[in] aPeriod The Period value.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void SetPeriod(uint16_t aPeriod) { mPeriod = HostSwap16(aPeriod); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint16_t mPeriod;
|
||||||
|
} OT_TOOL_PACKED_END;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class implements Scan Duration TLV generation and parsing.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
OT_TOOL_PACKED_BEGIN
|
||||||
|
class ScanDurationTlv: public Tlv
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* This method initializes the TLV.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void Init(void) { SetType(kScanDuration); SetLength(sizeof(*this) - sizeof(Tlv)); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method indicates whether or not the TLV appears to be well-formed.
|
||||||
|
*
|
||||||
|
* @retval TRUE If the TLV appears to be well-formed.
|
||||||
|
* @retval FALSE If the TLV does not appear to be well-formed.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(Tlv); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the Scan Duration value.
|
||||||
|
*
|
||||||
|
* @returns The Scan Duration value.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
uint16_t GetScanDuration(void) const { return HostSwap16(mScanDuration); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets the Scan Duration value.
|
||||||
|
*
|
||||||
|
* @param[in] aScanDuration The Scan Duration value.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void SetScanDuration(uint16_t aScanDuration) { mScanDuration = HostSwap16(aScanDuration); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint16_t mScanDuration;
|
||||||
|
} OT_TOOL_PACKED_END;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class implements Energy List TLV generation and parsing.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
OT_TOOL_PACKED_BEGIN
|
||||||
|
class EnergyListTlv: public Tlv
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* This method initializes the TLV.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void Init(void) { SetType(kEnergyList); SetLength(sizeof(*this) - sizeof(Tlv)); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method indicates whether or not the TLV appears to be well-formed.
|
||||||
|
*
|
||||||
|
* @retval TRUE If the TLV appears to be well-formed.
|
||||||
|
* @retval FALSE If the TLV does not appear to be well-formed.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool IsValid(void) const { return true; }
|
||||||
|
} OT_TOOL_PACKED_END;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class implements Discovery Request TLV generation and parsing.
|
* This class implements Discovery Request TLV generation and parsing.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -78,7 +78,8 @@ ThreadNetif::ThreadNetif(Ip6::Ip6 &aIp6):
|
|||||||
#endif // OPENTHREAD_ENABLE_JOINER
|
#endif // OPENTHREAD_ENABLE_JOINER
|
||||||
mJoinerRouter(*this),
|
mJoinerRouter(*this),
|
||||||
mLeader(*this),
|
mLeader(*this),
|
||||||
mPanIdQuery(*this)
|
mPanIdQuery(*this),
|
||||||
|
mEnergyScan(*this)
|
||||||
{
|
{
|
||||||
mKeyManager.SetMasterKey(kThreadMasterKey, sizeof(kThreadMasterKey));
|
mKeyManager.SetMasterKey(kThreadMasterKey, sizeof(kThreadMasterKey));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,7 @@
|
|||||||
#include <net/ip6_filter.hpp>
|
#include <net/ip6_filter.hpp>
|
||||||
#include <net/netif.hpp>
|
#include <net/netif.hpp>
|
||||||
#include <thread/address_resolver.hpp>
|
#include <thread/address_resolver.hpp>
|
||||||
|
#include <thread/energy_scan_server.hpp>
|
||||||
#include <thread/key_manager.hpp>
|
#include <thread/key_manager.hpp>
|
||||||
#include <thread/meshcop_dataset_manager.hpp>
|
#include <thread/meshcop_dataset_manager.hpp>
|
||||||
#include <thread/mesh_forwarder.hpp>
|
#include <thread/mesh_forwarder.hpp>
|
||||||
@@ -278,6 +279,7 @@ private:
|
|||||||
MeshCoP::JoinerRouter mJoinerRouter;
|
MeshCoP::JoinerRouter mJoinerRouter;
|
||||||
MeshCoP::Leader mLeader;
|
MeshCoP::Leader mLeader;
|
||||||
PanIdQueryServer mPanIdQuery;
|
PanIdQueryServer mPanIdQuery;
|
||||||
|
EnergyScanServer mEnergyScan;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -90,13 +90,29 @@ namespace Thread {
|
|||||||
*/
|
*/
|
||||||
#define OPENTHREAD_URI_ACTIVE_SET "c/as"
|
#define OPENTHREAD_URI_ACTIVE_SET "c/as"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def OPENTHREAD_URI_ENERGY_SCAN
|
||||||
|
*
|
||||||
|
* The URI Path for Energy Scan
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define OPENTHREAD_URI_ENERGY_SCAN "c/es"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def OPENTHREAD_URI_ENERGY_REPORT
|
||||||
|
*
|
||||||
|
* The URI Path for Energy Report
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define OPENTHREAD_URI_ENERGY_REPORT "c/er"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @def OPENTHREAD_URI_PENDING_GET
|
* @def OPENTHREAD_URI_PENDING_GET
|
||||||
*
|
*
|
||||||
* The URI Path for MGMT_PENDING_GET
|
* The URI Path for MGMT_PENDING_GET
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#define OPENTHREAD_URI_PENDING_GET "c/pg"
|
#define OPENTHREAD_URI_PENDING_GET "c/pg"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @def OPENTHREAD_URI_PENDING_SET
|
* @def OPENTHREAD_URI_PENDING_SET
|
||||||
@@ -104,7 +120,7 @@ namespace Thread {
|
|||||||
* The URI Path for MGMT_PENDING_SET
|
* The URI Path for MGMT_PENDING_SET
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#define OPENTHREAD_URI_PENDING_SET "c/ps"
|
#define OPENTHREAD_URI_PENDING_SET "c/ps"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @def OPENTHREAD_URI_SERVER_DATA
|
* @def OPENTHREAD_URI_SERVER_DATA
|
||||||
@@ -160,7 +176,7 @@ namespace Thread {
|
|||||||
* The URI Path for Leader Keep Alive
|
* The URI Path for Leader Keep Alive
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#define OPENTHREAD_URI_LEADER_KEEP_ALIVE "c/la"
|
#define OPENTHREAD_URI_LEADER_KEEP_ALIVE "c/la"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @def OPENTHREAD_URI_PANID_CONFLICT
|
* @def OPENTHREAD_URI_PANID_CONFLICT
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ EXTRA_DIST = \
|
|||||||
thread-cert/Cert_7_1_03_BorderRouterAsLeader.py \
|
thread-cert/Cert_7_1_03_BorderRouterAsLeader.py \
|
||||||
thread-cert/Cert_7_1_04_BorderRouterAsRouter.py \
|
thread-cert/Cert_7_1_04_BorderRouterAsRouter.py \
|
||||||
thread-cert/Cert_7_1_05_BorderRouterAsRouter.py \
|
thread-cert/Cert_7_1_05_BorderRouterAsRouter.py \
|
||||||
|
thread-cert/Cert_9_2_13_EnergyScan.py \
|
||||||
thread-cert/Cert_9_2_14_PanIdQuery.py \
|
thread-cert/Cert_9_2_14_PanIdQuery.py \
|
||||||
thread-cert/node.py \
|
thread-cert/node.py \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
@@ -217,6 +218,7 @@ check_SCRIPTS += \
|
|||||||
thread-cert/Cert_7_1_03_BorderRouterAsLeader.py \
|
thread-cert/Cert_7_1_03_BorderRouterAsLeader.py \
|
||||||
thread-cert/Cert_7_1_04_BorderRouterAsRouter.py \
|
thread-cert/Cert_7_1_04_BorderRouterAsRouter.py \
|
||||||
thread-cert/Cert_7_1_05_BorderRouterAsRouter.py \
|
thread-cert/Cert_7_1_05_BorderRouterAsRouter.py \
|
||||||
|
thread-cert/Cert_9_2_13_EnergyScan.py \
|
||||||
thread-cert/Cert_9_2_14_PanIdQuery.py \
|
thread-cert/Cert_9_2_14_PanIdQuery.py \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
@@ -249,6 +251,7 @@ XFAIL_NCP_TESTS = \
|
|||||||
thread-cert/Cert_7_1_03_BorderRouterAsLeader.py \
|
thread-cert/Cert_7_1_03_BorderRouterAsLeader.py \
|
||||||
thread-cert/Cert_7_1_04_BorderRouterAsRouter.py \
|
thread-cert/Cert_7_1_04_BorderRouterAsRouter.py \
|
||||||
thread-cert/Cert_7_1_05_BorderRouterAsRouter.py \
|
thread-cert/Cert_7_1_05_BorderRouterAsRouter.py \
|
||||||
|
thread-cert/Cert_9_2_13_EnergyScan.py \
|
||||||
thread-cert/Cert_9_2_14_PanIdQuery.py \
|
thread-cert/Cert_9_2_14_PanIdQuery.py \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
|||||||
+111
@@ -0,0 +1,111 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016, 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
import time
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import node
|
||||||
|
|
||||||
|
COMMISSIONER = 1
|
||||||
|
LEADER = 2
|
||||||
|
ROUTER1 = 3
|
||||||
|
ED1 = 4
|
||||||
|
|
||||||
|
class Cert_9_2_13_EnergyScan(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.nodes = {}
|
||||||
|
for i in range(1,5):
|
||||||
|
self.nodes[i] = node.Node(i)
|
||||||
|
|
||||||
|
self.nodes[COMMISSIONER].set_panid(0xface)
|
||||||
|
self.nodes[COMMISSIONER].set_mode('rsdn')
|
||||||
|
self.nodes[COMMISSIONER].add_whitelist(self.nodes[LEADER].get_addr64())
|
||||||
|
self.nodes[COMMISSIONER].enable_whitelist()
|
||||||
|
|
||||||
|
self.nodes[LEADER].set_panid(0xface)
|
||||||
|
self.nodes[LEADER].set_mode('rsdn')
|
||||||
|
self.nodes[LEADER].add_whitelist(self.nodes[COMMISSIONER].get_addr64())
|
||||||
|
self.nodes[LEADER].add_whitelist(self.nodes[ROUTER1].get_addr64())
|
||||||
|
self.nodes[LEADER].enable_whitelist()
|
||||||
|
|
||||||
|
self.nodes[ROUTER1].set_panid(0xface)
|
||||||
|
self.nodes[ROUTER1].set_mode('rsdn')
|
||||||
|
self.nodes[ROUTER1].add_whitelist(self.nodes[LEADER].get_addr64())
|
||||||
|
self.nodes[ROUTER1].add_whitelist(self.nodes[ED1].get_addr64())
|
||||||
|
self.nodes[ROUTER1].enable_whitelist()
|
||||||
|
|
||||||
|
self.nodes[ED1].set_panid(0xface)
|
||||||
|
self.nodes[ED1].set_mode('rs')
|
||||||
|
self.nodes[ED1].add_whitelist(self.nodes[ROUTER1].get_addr64())
|
||||||
|
self.nodes[ED1].enable_whitelist()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
for node in list(self.nodes.values()):
|
||||||
|
node.stop()
|
||||||
|
del self.nodes
|
||||||
|
|
||||||
|
def test(self):
|
||||||
|
self.nodes[LEADER].start()
|
||||||
|
self.nodes[LEADER].set_state('leader')
|
||||||
|
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
|
||||||
|
|
||||||
|
self.nodes[COMMISSIONER].start()
|
||||||
|
time.sleep(3)
|
||||||
|
self.assertEqual(self.nodes[COMMISSIONER].get_state(), 'router')
|
||||||
|
|
||||||
|
self.nodes[ROUTER1].start()
|
||||||
|
time.sleep(3)
|
||||||
|
self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
|
||||||
|
|
||||||
|
self.nodes[ED1].start()
|
||||||
|
time.sleep(3)
|
||||||
|
self.assertEqual(self.nodes[ED1].get_state(), 'child')
|
||||||
|
|
||||||
|
ipaddrs = self.nodes[ROUTER1].get_addrs()
|
||||||
|
for ipaddr in ipaddrs:
|
||||||
|
if ipaddr[0:4] != 'fe80':
|
||||||
|
break
|
||||||
|
|
||||||
|
self.nodes[COMMISSIONER].ping(ipaddr)
|
||||||
|
self.nodes[COMMISSIONER].energy_scan(0x50000, 0x02, 0x20, 0x3e8, ipaddr)
|
||||||
|
|
||||||
|
ipaddrs = self.nodes[ED1].get_addrs()
|
||||||
|
for ipaddr in ipaddrs:
|
||||||
|
if ipaddr[0:4] != 'fe80':
|
||||||
|
break
|
||||||
|
|
||||||
|
self.nodes[COMMISSIONER].ping(ipaddr)
|
||||||
|
self.nodes[COMMISSIONER].energy_scan(0x50000, 0x02, 0x20, 0x3e8, ipaddr)
|
||||||
|
|
||||||
|
self.nodes[COMMISSIONER].energy_scan(0x50000, 0x02, 0x20, 0x3e8, 'ff33:0040:fdde:ad00:beef:0:0:1')
|
||||||
|
|
||||||
|
self.nodes[COMMISSIONER].ping(ipaddr)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
@@ -92,9 +92,9 @@ class Cert_9_2_14_PanIdQuery(unittest.TestCase):
|
|||||||
if ipaddr[0:4] != 'fe80':
|
if ipaddr[0:4] != 'fe80':
|
||||||
break
|
break
|
||||||
|
|
||||||
self.nodes[COMMISSIONER].panid_query('0xdead', '0xffffffff', ipaddr)
|
self.nodes[COMMISSIONER].panid_query(0xdead, 0xffffffff, ipaddr)
|
||||||
|
|
||||||
self.nodes[COMMISSIONER].panid_query('0xdead', '0xffffffff', 'ff33:0040:fdde:ad00:beef:0:0:1')
|
self.nodes[COMMISSIONER].panid_query(0xdead, 0xffffffff, 'ff33:0040:fdde:ad00:beef:0:0:1')
|
||||||
|
|
||||||
self.nodes[COMMISSIONER].ping(ipaddr)
|
self.nodes[COMMISSIONER].ping(ipaddr)
|
||||||
|
|
||||||
|
|||||||
@@ -322,8 +322,13 @@ class Node:
|
|||||||
self.send_command('netdataregister')
|
self.send_command('netdataregister')
|
||||||
self.pexpect.expect('Done')
|
self.pexpect.expect('Done')
|
||||||
|
|
||||||
|
def energy_scan(self, mask, count, period, scan_duration, ipaddr):
|
||||||
|
cmd = 'commissioner energy ' + str(mask) + ' ' + str(count) + ' ' + str(period) + ' ' + str(scan_duration) + ' ' + ipaddr
|
||||||
|
self.send_command(cmd)
|
||||||
|
self.pexpect.expect('Energy:', timeout=8)
|
||||||
|
|
||||||
def panid_query(self, panid, mask, ipaddr):
|
def panid_query(self, panid, mask, ipaddr):
|
||||||
cmd = 'commissioner panid ' + panid + ' ' + mask + ' ' + ipaddr
|
cmd = 'commissioner panid ' + str(panid) + ' ' + str(mask) + ' ' + ipaddr
|
||||||
self.send_command(cmd)
|
self.send_command(cmd)
|
||||||
self.pexpect.expect('Conflict:', timeout=8)
|
self.pexpect.expect('Conflict:', timeout=8)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user