Windows App Updates (#1431)

* Initial new API helpers

* Finish up API helper

* Update App to use new API helpers

* More cleanup and added support for preferred router ID

* Clean up state change handlers

* Add logging

* Some more clean up and redesign

* Added small readme for Windows App.
This commit is contained in:
Nick Banks
2017-03-07 10:29:30 -08:00
committed by Jonathan Hui
parent 8054fd2f32
commit 974421da98
16 changed files with 1300 additions and 392 deletions
+5 -5
View File
@@ -114,7 +114,7 @@
/>
<TextBox
Name="InterfaceConfigKey"
Text="Pas$W0rd"
Text="00112233445566778899aabbccddeeff"
Grid.Row="1"
Grid.Column="1"
FontSize="18"
@@ -130,9 +130,9 @@
Name="InterfaceConfigMaxChildren"
Grid.Row="2"
Grid.Column="1"
Minimum="11"
Maximum="26"
Value="15"
Minimum="0"
Maximum="32"
Value="16"
VerticalAlignment="Center"
/>
<TextBlock
@@ -147,7 +147,7 @@
Grid.Column="1"
Minimum="11"
Maximum="24"
Value="5"
Value="11"
VerticalAlignment="Center"
/>
<StackPanel
+257 -267
View File
@@ -42,366 +42,356 @@ using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Navigation;
MainPage^ MainPage::Current = nullptr;
#define GUID_FORMAT L"{%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}"
#define GUID_ARG(guid) guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]
#define MAC8_FORMAT L"%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X"
#define MAC8_ARG(mac) mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], mac[6], mac[7]
PCWSTR ToString(otDeviceRole role)
void otLog(PCSTR aFormat, ...)
{
switch (role)
{
case kDeviceRoleOffline: return L"Offline";
case kDeviceRoleDisabled: return L"Disabled";
case kDeviceRoleDetached: return L"Disconnected";
case kDeviceRoleChild: return L"Connected - Child";
case kDeviceRoleRouter: return L"Connected - Router";
case kDeviceRoleLeader: return L"Connected - Leader";
}
va_list args;
va_start(args, aFormat);
return L"Unknown Role State";
CHAR logString[512] = { 0 };
int charsWritten = vsprintf_s(logString, sizeof(logString), aFormat, args);
if (charsWritten > 0) OutputDebugStringA(logString);
va_end(args);
}
void OTCALL
ThreadDeviceAvailabilityCallback(
bool /* aAdded */,
const GUID* /* aDeviceGuid */,
_In_ void* /* aContext */
)
{
// Trigger the interface list to update
MainPage::Current->Dispatcher->RunAsync(
Windows::UI::Core::CoreDispatcherPriority::Normal,
ref new Windows::UI::Core::DispatchedHandler(
[=]() {
MainPage::Current->BuildInterfaceList();
}
)
);
}
void OTCALL
ThreadStateChangeCallback(
uint32_t aFlags,
_In_ void* /* aContext */
)
{
if ((aFlags & OT_NET_ROLE) != 0)
{
// Trigger the interface list to update
MainPage::Current->Dispatcher->RunAsync(
Windows::UI::Core::CoreDispatcherPriority::Normal,
ref new Windows::UI::Core::DispatchedHandler(
[=]() {
MainPage::Current->BuildInterfaceList();
}
)
);
}
}
MainPage::MainPage()
MainPage::MainPage() : _otApi(nullptr)
{
InitializeComponent();
MainPage::Current = this;
_isFullScreen = false;
_apiInstance = nullptr;
InterfaceConfigCancelButton->Click +=
ref new RoutedEventHandler(
[=](Platform::Object^, RoutedEventArgs^) {
InterfaceConfiguration->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
this->InterfaceConfiguration->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
this->_curAdapter = nullptr;
}
);
InterfaceConfigOkButton->Click +=
ref new RoutedEventHandler(
[=](Platform::Object^, RoutedEventArgs^) {
InterfaceConfiguration->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
ConnectNetwork(_currentInterface);
this->InterfaceConfiguration->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
this->ConnectNetwork(_curAdapter);
this->_curAdapter = nullptr;
}
);
InterfaceDetailsCloseButton->Click +=
ref new RoutedEventHandler(
[=](Platform::Object^, RoutedEventArgs^) {
InterfaceDetails->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
this->InterfaceDetails->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
}
);
}
void MainPage::OnNavigatedTo(NavigationEventArgs^ e)
{
Window::Current->CoreWindow->VisibilityChanged += ref new TypedEventHandler<Windows::UI::Core::CoreWindow^, Windows::UI::Core::VisibilityChangedEventArgs^>(this, &MainPage::OnVisibilityChanged);
SizeChanged += ref new SizeChangedEventHandler(this, &MainPage::OnWindowSizeChanged);
Loaded += ref new RoutedEventHandler(this, &MainPage::OnLoaded);
Unloaded += ref new RoutedEventHandler(this, &MainPage::OnUnloaded);
_isVisible = Window::Current->CoreWindow->Visible;
}
void MainPage::OnLoaded(Object^ sender, RoutedEventArgs^ e)
{
// Initialize api handle
_apiInstance = otApiInit();
if (_apiInstance)
try
{
// Register for device availability callbacks
otSetDeviceAvailabilityChangedCallback(ApiInstance, ThreadDeviceAvailabilityCallback, nullptr);
// Initialize api handle
_otApi = ref new otApi();
// Build the initial list
BuildInterfaceList();
// Register for state changes
_adapterArrivalToken =
_otApi->AdapterArrival +=
ref new otAdapterArrivalDelegate(
[=](otAdapter^ adapter) {
// Update on the UI thread
this->Dispatcher->RunAsync(
Windows::UI::Core::CoreDispatcherPriority::Normal,
ref new Windows::UI::Core::DispatchedHandler(
[=]() {
this->AddAdapterToList(adapter);
}));
});
// Enumerate the adapter list
auto adapters = _otApi->GetAdapters();
for (auto&& adapter : adapters) {
AddAdapterToList(adapter);
}
}
catch (Exception^)
{
}
}
void MainPage::OnUnloaded(Object^ sender, RoutedEventArgs^ e)
{
// Unregister for callbacks
otSetDeviceAvailabilityChangedCallback(ApiInstance, nullptr, nullptr);
if (_otApi)
{
// Unregister
_otApi->AdapterArrival -= _adapterArrivalToken;
// Clean up api handle
otApiFinalize(ApiInstance);
_apiInstance = nullptr;
// Clear current adapter
_curAdapter = nullptr;
// Remove the adapter list
auto adapters = _otApi->GetAdapters();
for (auto&& adapter : adapters) {
adapter->InvokeAdapterRemoval();
}
// Free the api handle
_otApi = nullptr;
}
}
void MainPage::OnResuming()
{
}
void MainPage::OnWindowSizeChanged(Object^ sender, SizeChangedEventArgs^ args)
void MainPage::ShowInterfaceDetails(otAdapter^ adapter)
{
_windowSize = args->NewSize;
}
void MainPage::OnVisibilityChanged(Windows::UI::Core::CoreWindow^ coreWindow, Windows::UI::Core::VisibilityChangedEventArgs^ args)
{
// The Visible property is toggled when the app enters and exits minimized state.
// But obscuring the app with another window does not change Visible state, oddly enough.
_isVisible = args->Visible;
}
void MainPage::ShowInterfaceDetails(Platform::Guid InterfaceGuid)
{
if (ApiInstance == nullptr) return;
GUID deviceGuid = InterfaceGuid;
auto device = otInstanceInit(ApiInstance, &deviceGuid);
auto extendedAddress = otLinkGetExtendedAddress(device);
if (extendedAddress)
try
{
WCHAR szMac[256] = { 0 };
swprintf_s(szMac, 256, MAC8_FORMAT, MAC8_ARG(extendedAddress));
InterfaceMacAddress->Text = ref new String(szMac);
InterfaceMacAddress->Text = otApi::ToString(adapter->ExtendedAddress);
InterfaceML_EID->Text = adapter->MeshLocalEid->ToString();
InterfaceRLOC->Text = otApi::ToString(adapter->Rloc16);
otFreeMemory(extendedAddress);
}
else
{
InterfaceMacAddress->Text = L"ERROR";
}
auto ml_eid = otThreadGetMeshLocalEid(device);
if (ml_eid)
{
WCHAR szAddress[46] = { 0 };
RtlIpv6AddressToStringW((const PIN6_ADDR)ml_eid, szAddress);
InterfaceML_EID->Text = ref new String(szAddress);
otFreeMemory(ml_eid);
}
else
{
InterfaceML_EID->Text = L"ERROR";
}
auto rloc16 = otThreadGetRloc16(device);
WCHAR szRloc[16] = { 0 };
swprintf_s(szRloc, 16, L"%4x", rloc16);
InterfaceRLOC->Text = ref new String(szRloc);
if (otThreadGetDeviceRole(device) > kDeviceRoleChild)
{
uint8_t index = 0;
otChildInfo childInfo;
while (kThreadError_None == otThreadGetChildInfoByIndex(device, index, &childInfo))
if (adapter->State > otThreadState::Child)
{
index++;
uint8_t index = 0;
otChildInfo childInfo;
while (kThreadError_None == otThreadGetChildInfoByIndex((otInstance*)(void*)adapter->RawHandle, index, &childInfo))
{
index++;
}
WCHAR szText[64] = { 0 };
swprintf_s(szText, 64, L"%d", index);
InterfaceChildren->Text = ref new String(szText);
InterfaceNeighbors->Text = L"unknown";
InterfaceNeighbors->Visibility = Windows::UI::Xaml::Visibility::Visible;
InterfaceNeighborsText->Visibility = Windows::UI::Xaml::Visibility::Visible;
InterfaceChildren->Visibility = Windows::UI::Xaml::Visibility::Visible;
InterfaceChildrenText->Visibility = Windows::UI::Xaml::Visibility::Visible;
}
WCHAR szText[64] = { 0 };
swprintf_s(szText, 64, L"%d", index);
InterfaceChildren->Text = ref new String(szText);
InterfaceNeighbors->Text = L"unknown";
InterfaceNeighbors->Visibility = Windows::UI::Xaml::Visibility::Visible;
InterfaceNeighborsText->Visibility = Windows::UI::Xaml::Visibility::Visible;
InterfaceChildren->Visibility = Windows::UI::Xaml::Visibility::Visible;
InterfaceChildrenText->Visibility = Windows::UI::Xaml::Visibility::Visible;
// Show the details
InterfaceDetails->Visibility = Windows::UI::Xaml::Visibility::Visible;
}
catch (Exception^)
{
// Show the details
InterfaceDetails->Visibility = Windows::UI::Xaml::Visibility::Visible;
otFreeMemory(device);
}
}
UIElement^ MainPage::CreateNewInterface(Platform::Guid InterfaceGuid)
void MainPage::AddAdapterToList(otAdapter^ adapter)
{
GUID deviceGuid = InterfaceGuid;
auto device = otInstanceInit(ApiInstance, &deviceGuid);
auto deviceRole = otThreadGetDeviceRole(device);
WCHAR szText[256] = { 0 };
swprintf_s(szText, 256, L"%s\r\n\t" GUID_FORMAT L"\r\n\t%s",
L"openthread interface", // TODO ...
GUID_ARG(deviceGuid),
::ToString(deviceRole));
auto InterfaceStackPanel = ref new StackPanel();
InterfaceStackPanel->Orientation = Orientation::Horizontal;
auto InterfaceTextBlock = ref new TextBlock();
InterfaceTextBlock->Text = ref new String(szText);
InterfaceTextBlock->FontSize = 16;
InterfaceTextBlock->Margin = Thickness(10);
InterfaceTextBlock->TextWrapping = TextWrapping::Wrap;
InterfaceStackPanel->Children->Append(InterfaceTextBlock);
if (deviceRole == kDeviceRoleDisabled)
try
{
GUID interfaceGuid = adapter->InterfaceGuid;
WCHAR szName[256] = { 0 };
swprintf_s(szName, 256, GUID_FORMAT, GUID_ARG(interfaceGuid));
auto InterfaceStackPanel = ref new StackPanel();
InterfaceStackPanel->Name = ref new String(szName);
InterfaceStackPanel->Orientation = Orientation::Horizontal;
otLog("%S arrival!\n", InterfaceStackPanel->Name->Data());
// Basic description text
auto InterfaceTextBlock = ref new TextBlock();
InterfaceTextBlock->Text = ref new String(L"openthread interface");
InterfaceTextBlock->FontSize = 16;
InterfaceTextBlock->Margin = Thickness(10);
InterfaceTextBlock->TextWrapping = TextWrapping::Wrap;
InterfaceStackPanel->Children->Append(InterfaceTextBlock);
// Connect button
auto ConnectButton = ref new Button();
ConnectButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
ConnectButton->Content = ref new String(L"Connect");
ConnectButton->Click +=
ref new RoutedEventHandler(
[=](Platform::Object^, RoutedEventArgs^) {
_currentInterface = InterfaceGuid;
InterfaceConfiguration->Visibility = Windows::UI::Xaml::Visibility::Visible;
this->_curAdapter = adapter;
this->InterfaceConfiguration->Visibility = Windows::UI::Xaml::Visibility::Visible;
}
);
);
InterfaceStackPanel->Children->Append(ConnectButton);
}
else
{
// Details button
auto DetailsButton = ref new Button();
DetailsButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
DetailsButton->Content = ref new String(L"Details");
DetailsButton->Click +=
ref new RoutedEventHandler(
[=](Platform::Object^, RoutedEventArgs^) {
ShowInterfaceDetails(InterfaceGuid);
this->ShowInterfaceDetails(adapter);
}
);
);
InterfaceStackPanel->Children->Append(DetailsButton);
// Disconnect button
auto DisconnectButton = ref new Button();
DisconnectButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
DisconnectButton->Content = ref new String(L"Disconnect");
DisconnectButton->Click +=
ref new RoutedEventHandler(
[=](Platform::Object^, RoutedEventArgs^) {
DisconnectNetwork(InterfaceGuid);
this->DisconnectNetwork(adapter);
}
);
);
InterfaceStackPanel->Children->Append(DisconnectButton);
// Delegate for handling role changes
auto OnAdapterRoleChanged =
[=]() {
GUID interfaceGuid = adapter->InterfaceGuid;
auto state = adapter->State;
auto stateStr = otApi::ToString(adapter->State);
WCHAR szText[256] = { 0 };
swprintf_s(szText, 256, GUID_FORMAT L"\r\n\t%s\r\n\t%s",
GUID_ARG(interfaceGuid),
stateStr->Data(),
state >= otThreadState::Child ?
adapter->MeshLocalEid->ToString()->Data() :
L"");
InterfaceTextBlock->Text = ref new String(szText);
otLog("%S state = %S\n", InterfaceStackPanel->Name->Data(), stateStr->Data());
if (state == otThreadState::Disabled)
{
ConnectButton->Visibility = Windows::UI::Xaml::Visibility::Visible;
DetailsButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
DisconnectButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
}
else
{
ConnectButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
DetailsButton->Visibility = Windows::UI::Xaml::Visibility::Visible;
DisconnectButton->Visibility = Windows::UI::Xaml::Visibility::Visible;
}
};
// Register for role change callbacks
auto adapterRoleChangedToken =
adapter->NetRoleChanged +=
ref new otNetRoleChangedDelegate(
[=](auto sender) {
// Update the text on the UI thread
this->Dispatcher->RunAsync(
Windows::UI::Core::CoreDispatcherPriority::Normal,
ref new Windows::UI::Core::DispatchedHandler(
[=]() {
OnAdapterRoleChanged();
}
)
);
}
);
// Register for address change callbacks
auto adapterMeshLocalAddresChangedToken =
adapter->IpMeshLocalAddresChanged +=
ref new otIpMeshLocalAddresChangedDelegate(
[=](auto sender) {
// Update the text on the UI thread
this->Dispatcher->RunAsync(
Windows::UI::Core::CoreDispatcherPriority::Normal,
ref new Windows::UI::Core::DispatchedHandler(
[=]() {
OnAdapterRoleChanged();
}
)
);
}
);
// Register for adapter removal callbacks
Windows::Foundation::EventRegistrationToken adapterRemovalToken;
adapterRemovalToken =
adapter->AdapterRemoval +=
ref new otAdapterRemovalDelegate(
[=](otAdapter^ adapter) {
// Unregister
adapter->NetRoleChanged -= adapterRoleChangedToken;
adapter->IpMeshLocalAddresChanged -= adapterMeshLocalAddresChangedToken;
adapter->AdapterRemoval -= adapterRemovalToken;
// Remove the item on the UI thread
this->Dispatcher->RunAsync(
Windows::UI::Core::CoreDispatcherPriority::Normal,
ref new Windows::UI::Core::DispatchedHandler(
[=]() {
for (uint32_t i = 0; i < this->InterfaceList->Items->Size; i++)
{
auto Item = dynamic_cast<StackPanel^>(this->InterfaceList->Items->GetAt(i));
if (Item == InterfaceStackPanel)
{
otLog("%S removal!\n", InterfaceStackPanel->Name->Data());
this->InterfaceList->Items->RemoveAt(i);
break;
}
}
}));
});
// Trigger the initial role change
OnAdapterRoleChanged();
// Add the interface to the list
InterfaceList->Items->Append(InterfaceStackPanel);
}
// Register for callbacks on the device
otSetStateChangedCallback(device, ThreadStateChangeCallback, nullptr);
// Cache the device
_devices.push_back(device);
return InterfaceStackPanel;
}
void MainPage::BuildInterfaceList()
{
if (ApiInstance == nullptr) return;
// Clear all existing children
InterfaceList->Items->Clear();
// Clean up devices
for each (auto device in _devices)
catch (Exception^)
{
// Unregister for callbacks for the device
otSetStateChangedCallback((otInstance*)device, nullptr, nullptr);
// Free the device
otFreeMemory(device);
}
_devices.clear();
}
// Enumerate the new device list
auto deviceList = otEnumerateDevices(ApiInstance);
if (deviceList)
void MainPage::ConnectNetwork(otAdapter^ adapter)
{
try
{
GUID interfaceGuid = adapter->InterfaceGuid;
WCHAR szName[256] = { 0 };
swprintf_s(szName, 256, GUID_FORMAT, GUID_ARG(interfaceGuid));
otLog("%S starting connection...\n", szName);
// Configure
adapter->NetworkName = InterfaceConfigName->Text;
adapter->MasterKey = InterfaceConfigKey->Text;
adapter->Channel = (uint8_t)InterfaceConfigChannel->Value;
adapter->MaxAllowedChildren = (uint8_t)InterfaceConfigMaxChildren->Value;
adapter->PanId = 0x4567;
// Bring up the interface and start the Thread logic
adapter->IpEnabled = true;
adapter->ThreadEnabled = true;
}
catch (Exception^)
{
// Dump the results to the console
for (DWORD dwIndex = 0; dwIndex < deviceList->aDevicesLength; dwIndex++)
{
InterfaceList->Items->Append(
CreateNewInterface(deviceList->aDevices[dwIndex]));
}
otFreeMemory(deviceList);
}
}
void MainPage::ConnectNetwork(Platform::Guid InterfaceGuid)
void MainPage::DisconnectNetwork(otAdapter^ adapter)
{
if (ApiInstance == nullptr) return;
try
{
GUID interfaceGuid = adapter->InterfaceGuid;
WCHAR szName[256] = { 0 };
swprintf_s(szName, 256, GUID_FORMAT, GUID_ARG(interfaceGuid));
otLog("%S disconnecting...\n", szName);
GUID deviceGuid = InterfaceGuid;
auto device = otInstanceInit(ApiInstance, &deviceGuid);
// Stop the Thread network and bring down the interface
adapter->ThreadEnabled = false;
adapter->IpEnabled = false;
}
catch (Exception^)
{
//
// Configure
//
otNetworkName networkName = {};
wcstombs(networkName.m8, InterfaceConfigName->Text->Data(), sizeof(networkName.m8));
otThreadSetNetworkName(device, networkName.m8);
otMasterKey masterKey = {};
wcstombs((char*)masterKey.m8, InterfaceConfigKey->Text->Data(), sizeof(masterKey.m8));
otThreadSetMasterKey(device, masterKey.m8, sizeof(masterKey.m8));
otLinkSetChannel(device, (uint8_t)InterfaceConfigChannel->Value);
otThreadSetMaxAllowedChildren(device, (uint8_t)InterfaceConfigMaxChildren->Value);
otLinkSetPanId(device, 0x4567);
//
// Bring up the interface and start the Thread logic
//
otIp6SetEnabled(device, true);
otThreadSetEnabled(device, true);
// Cleanup
otFreeMemory(device);
}
void MainPage::DisconnectNetwork(Platform::Guid InterfaceGuid)
{
if (ApiInstance == nullptr) return;
GUID deviceGuid = InterfaceGuid;
auto device = otInstanceInit(ApiInstance, &deviceGuid);
//
// Start the Thread logic and the interface
//
otThreadSetEnabled(device, false);
otIp6SetEnabled(device, false);
// Cleanup
otFreeMemory(device);
}
}
+7 -21
View File
@@ -42,11 +42,9 @@ namespace Thread
void OnResuming();
void BuildInterfaceList();
void ConnectNetwork(Platform::Guid InterfaceGuid);
void ShowInterfaceDetails(Platform::Guid InterfaceGuid);
void DisconnectNetwork(Platform::Guid InterfaceGuid);
void ConnectNetwork(otAdapter^ adapter);
void ShowInterfaceDetails(otAdapter^ adapter);
void DisconnectNetwork(otAdapter^ adapter);
protected:
virtual void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override;
@@ -56,25 +54,13 @@ namespace Thread
void OnLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void OnUnloaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void OnWindowSizeChanged(Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ args);
void OnVisibilityChanged(Windows::UI::Core::CoreWindow^ coreWindow, Windows::UI::Core::VisibilityChangedEventArgs^ args);
UIElement^ CreateNewInterface(Platform::Guid InterfaceGuid);
bool _isVisible;
bool _isFullScreen;
Windows::Foundation::Size _windowSize;
void AddAdapterToList(otAdapter^ adapter);
void *_apiInstance;
#define ApiInstance ((otApiInstance*)_apiInstance)
otApi^ _otApi;
std::vector<void*> _devices;
Windows::Foundation::EventRegistrationToken _adapterArrivalToken;
Platform::Guid _currentInterface;
internal:
static MainPage^ Current;
otAdapter^ _curAdapter;
};
}
+15
View File
@@ -0,0 +1,15 @@
# OpenThread App for Windows #
This sample app provides an example of how to interface with the OpenThread API in a
[Universal Windows App](https://developer.microsoft.com/en-us/windows/apps). The app is written in C++ /CX
and provides a simple wrapper around the OpenThread API, hiding the raw C/C++ interface.
The main page of the App is a list of the available interfaces, their current connection state,
their current ML-EID IPv6 address, and buttons to connect/disconnect and to view some more details.
![Interface List](../../../doc/images/windows-app-interface-list.png)
The details list provides some more information, including extended MAC address, RLOC16 and information
about the current children.
![Interface List](../../../doc/images/windows-app-details.png)
+613
View File
@@ -0,0 +1,613 @@
/*
* 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.
*/
#pragma once
#define OTDLL 1
#include <openthread/openthread.h>
#include <openthread/commissioner.h>
#include <openthread/joiner.h>
#include <wrl.h>
#include <collection.h>
using namespace Platform;
using namespace Platform::Collections;
using namespace Platform::Metadata;
using namespace Windows::Foundation::Collections;
using namespace Windows::Networking;
namespace Thread
{
ref class otAdapter;
public delegate void otAdapterRemovalDelegate(otAdapter^ sender);
public delegate void otIpAddressAddedDelegate(otAdapter^ sender);
public delegate void otIpAddressRemovedDelegate(otAdapter^ sender);
public delegate void otIpRlocAddedDelegate(otAdapter^ sender);
public delegate void otIpRlocRemovedDelegate(otAdapter^ sender);
public delegate void otIpLinkLocalAddresChangedDelegate(otAdapter^ sender);
public delegate void otIpMeshLocalAddresChangedDelegate(otAdapter^ sender);
public delegate void otNetRoleChangedDelegate(otAdapter^ sender);
public delegate void otNetPartitionIdChangedDelegate(otAdapter^ sender);
public delegate void otNetKeySequenceCounterChangedDelegate(otAdapter^ sender);
public delegate void otThreadChildAddedDelegate(otAdapter^ sender);
public delegate void otThreadChildRemovedDelegate(otAdapter^ sender);
public delegate void otThreadNetDataUpdatedDelegate(otAdapter^ sender);
[Flags]
public enum class otLinkModeFlags : unsigned int
{
None = 0,
RxOnWhenIdle = 0x1, /* 1, if the sender has its receiver on when not transmitting. 0, otherwise. */
SecureDataRequests = 0x2, /* 1, if the sender will use IEEE 802.15.4 to secure all data requests. 0, otherwise. */
DeviceType = 0x4, /* 1, if the sender is an FFD. 0, otherwise. */
NetworkData = 0x8 /* 1, if the sender requires the full Network Data. 0, otherwise. */
};
public enum class otThreadState
{
Offline,
Disabled,
Detached,
Child,
Router,
Leader
};
// Helper class for OpenThread Interface/Adapter specific APIs
public ref class otAdapter sealed
{
private:
void *_Instance;
#define DeviceInstance ((otInstance*)_Instance)
#define ThrowOnFailure(exp) \
do { \
auto res = exp; \
if (res != 0) \
throw Exception::CreateException(TheadErrorToHResult(res), #exp); \
} while (false)
public:
#pragma region Events
event otAdapterRemovalDelegate^ AdapterRemoval;
event otIpAddressAddedDelegate^ IpAddressAdded;
event otIpAddressRemovedDelegate^ IpAddressRemoved;
event otIpRlocAddedDelegate^ IpRlocAdded;
event otIpRlocRemovedDelegate^ IpRlocRemoved;
event otIpLinkLocalAddresChangedDelegate^ IpLinkLocalAddresChanged;
event otIpMeshLocalAddresChangedDelegate^ IpMeshLocalAddresChanged;
event otNetRoleChangedDelegate^ NetRoleChanged;
event otNetPartitionIdChangedDelegate^ NetPartitionIdChanged;
event otNetKeySequenceCounterChangedDelegate^ NetKeySequenceCounterChanged;
event otThreadChildAddedDelegate^ ThreadChildAdded;
event otThreadChildRemovedDelegate^ ThreadChildRemoved;
event otThreadNetDataUpdatedDelegate^ ThreadNetDataUpdated;
#pragma endregion
#pragma region Properties
property IntPtr RawHandle
{
IntPtr get() { return _Instance; }
}
property Guid InterfaceGuid
{
Guid get() { return otGetDeviceGuid(DeviceInstance); }
}
property uint32_t IfIndex
{
uint32_t get() { return otGetDeviceIfIndex(DeviceInstance); }
}
property uint32_t CompartmentId
{
uint32_t get() { return otGetCompartmentId(DeviceInstance); }
}
#pragma region Link Layer
property signed int /*int8_t*/ MaxTransmitPower
{
signed int get() { return otLinkGetMaxTransmitPower(DeviceInstance); }
void set(signed int value)
{
if (value > 127) throw Exception::CreateException(E_INVALIDARG);
otLinkSetMaxTransmitPower(DeviceInstance, (int8_t)value);
}
}
property uint32_t PollPeriod
{
uint32_t get() { return otLinkGetPollPeriod(DeviceInstance); }
void set(uint32_t value) { otLinkSetPollPeriod(DeviceInstance, value); }
}
property uint8_t Channel
{
uint8_t get() { return otLinkGetChannel(DeviceInstance); }
void set(uint8_t value) { ThrowOnFailure(otLinkSetChannel(DeviceInstance, value)); }
}
property uint16_t PanId
{
uint16_t get() { return otLinkGetPanId(DeviceInstance); }
void set(uint16_t value) { ThrowOnFailure(otLinkSetPanId(DeviceInstance, value)); }
}
property uint16_t ShortAddress
{
uint16_t get() { return otLinkGetShortAddress(DeviceInstance); }
}
property uint64_t ExtendedAddress
{
uint64_t get()
{
auto addr = otLinkGetExtendedAddress(DeviceInstance);
auto ret = *(uint64_t*)addr;
otFreeMemory(addr);
return ret;
}
void set(uint64_t value)
{
ThrowOnFailure(otLinkSetExtendedAddress(DeviceInstance, (otExtAddress*)&value));
}
}
property uint64_t FactoryAssignedIeeeEui64
{
uint64_t get()
{
uint64_t addr;
otLinkGetFactoryAssignedIeeeEui64(DeviceInstance, (otExtAddress*)&addr);
return addr;
}
}
property uint64_t JoinerId
{
uint64_t get()
{
uint64_t addr;
otLinkGetJoinerId(DeviceInstance, (otExtAddress*)&addr);
return addr;
}
}
#pragma endregion
#pragma region IP Layer
property bool IpEnabled
{
bool get() { return otIp6IsEnabled(DeviceInstance); }
void set(bool value) { ThrowOnFailure(otIp6SetEnabled(DeviceInstance, value)); }
}
#pragma endregion
#pragma region Thread Layer
property uint64_t ExtendedPanId
{
uint64_t get()
{
auto panid = otThreadGetExtendedPanId(DeviceInstance);
auto ret = *(uint64_t*)panid;
otFreeMemory(panid);
return ret;
}
void set(uint64_t value)
{
otThreadSetExtendedPanId(DeviceInstance, (uint8_t*)&value);
}
}
property otLinkModeFlags LinkMode
{
otLinkModeFlags get()
{
auto linkmode = otThreadGetLinkMode(DeviceInstance);
otLinkModeFlags flags = otLinkModeFlags::None;
if (linkmode.mRxOnWhenIdle) flags = flags | otLinkModeFlags::RxOnWhenIdle;
if (linkmode.mSecureDataRequests) flags = flags | otLinkModeFlags::SecureDataRequests;
if (linkmode.mDeviceType) flags = flags | otLinkModeFlags::DeviceType;
if (linkmode.mNetworkData) flags = flags | otLinkModeFlags::NetworkData;
return flags;
}
void set(otLinkModeFlags value)
{
otLinkModeConfig linkmode = { 0 };
if ((value & otLinkModeFlags::RxOnWhenIdle) != otLinkModeFlags::None)
linkmode.mRxOnWhenIdle = true;
if ((value & otLinkModeFlags::SecureDataRequests) != otLinkModeFlags::None)
linkmode.mSecureDataRequests = true;
if ((value & otLinkModeFlags::DeviceType) != otLinkModeFlags::None)
linkmode.mDeviceType = true;
if ((value & otLinkModeFlags::NetworkData) != otLinkModeFlags::None)
linkmode.mNetworkData = true;
ThrowOnFailure(otThreadSetLinkMode(DeviceInstance, linkmode));
}
}
static uint32_t charToValue(wchar_t c)
{
if (c >= L'a' && c <= L'f')
{
return c - L'a';
}
else if (c >= L'A' && c <= L'F')
{
return c - L'A';
}
else if (c >= L'0' && c <= L'9')
{
return c - L'0';
}
else
{
throw Exception::CreateException(E_INVALIDARG);
}
}
property String^ MasterKey
{
String^ get()
{
constexpr char hexmap[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
uint8_t keyLen;
auto key = otThreadGetMasterKey(DeviceInstance, &keyLen);
WCHAR szKey[OT_MASTER_KEY_SIZE * 2 + 1] = { 0 };
for (uint8_t i = 0; i < keyLen; i++)
{
szKey[2 * i] = hexmap[(key[i] & 0xF0) >> 4];
szKey[2 * i + 1] = hexmap[key[i] & 0x0F];
}
otFreeMemory(key);
return ref new String(szKey);
}
void set(String^ value)
{
uint8_t key[OT_MASTER_KEY_SIZE];
uint8_t keyLen = 0;
if (value->Length() % 2 == 0)
{
for (uint32_t i = 0; i < value->Length(); i+=2)
{
key[keyLen++] = (uint8_t)((charToValue(value->Data()[i]) << 4) |
charToValue(value->Data()[i + 1]));
}
}
else
{
key[keyLen++] = (uint8_t)(charToValue(value->Data()[0]));
for (uint32_t i = 1; i < value->Length(); i += 2)
{
key[keyLen++] = (uint8_t)((charToValue(value->Data()[i]) << 4) |
charToValue(value->Data()[i + 1]));
}
}
ThrowOnFailure(otThreadSetMasterKey(DeviceInstance, key, keyLen));
}
}
property String^ NetworkName
{
String^ get()
{
auto _name = otThreadGetNetworkName(DeviceInstance);
WCHAR name[OT_NETWORK_NAME_MAX_SIZE + 1];
MultiByteToWideChar(CP_UTF8, 0, _name, -1, name, ARRAYSIZE(name));
otFreeMemory(_name);
return ref new String(name);
}
void set(String^ value)
{
char name[OT_NETWORK_NAME_MAX_SIZE + 1];
auto err = WideCharToMultiByte(CP_UTF8, WC_NO_BEST_FIT_CHARS, value->Data(), -1, name, ARRAYSIZE(name), nullptr, nullptr);
if (err == 0) ThrowOnFailure(otThreadSetNetworkName(DeviceInstance, name));
else throw Exception::CreateException(E_INVALIDARG);
}
}
property uint8_t MaxAllowedChildren
{
uint8_t get() { return otThreadGetMaxAllowedChildren(DeviceInstance); }
void set(uint8_t value) { ThrowOnFailure(otThreadSetMaxAllowedChildren(DeviceInstance, value)); }
}
property uint32_t ChildTimeout
{
uint32_t get() { return otThreadGetChildTimeout(DeviceInstance); }
void set(uint32_t value) { otThreadSetChildTimeout(DeviceInstance, value); }
}
property bool ThreadEnabled
{
//bool get() { return otIsThreadStarted(DeviceInstance); }
void set(bool value) { ThrowOnFailure(otThreadSetEnabled(DeviceInstance, value)); }
}
property bool AutoStart
{
bool get() { return otThreadGetAutoStart(DeviceInstance); }
void set(bool value) { ThrowOnFailure(otThreadSetAutoStart(DeviceInstance, value)); }
}
property bool Singleton
{
bool get() { return otThreadIsSingleton(DeviceInstance); }
}
property bool RouterRoleEnabled
{
bool get() { return otThreadIsRouterRoleEnabled(DeviceInstance); }
void set(bool value) { otThreadSetRouterRoleEnabled(DeviceInstance, value); }
}
property uint8_t PreferredRouterId
{
void set(uint8_t value) { ThrowOnFailure(otThreadSetPreferredRouterId(DeviceInstance, value)); }
}
property HostName^ MeshLocalEid
{
HostName^ get()
{
auto addr = otThreadGetMeshLocalEid(DeviceInstance);
WCHAR szAddr[46];
RtlIpv6AddressToString((IN6_ADDR*)addr, szAddr);
otFreeMemory(addr);
return ref new HostName(ref new String(szAddr));
}
}
property HostName^ LeaderRloc
{
HostName^ get()
{
IN6_ADDR addr;
ThrowOnFailure(otThreadGetLeaderRloc(DeviceInstance, (otIp6Address*)&addr));
WCHAR szAddr[46];
RtlIpv6AddressToString(&addr, szAddr);
return ref new HostName(ref new String(szAddr));
}
}
property uint8_t LocalLeaderWeight
{
uint8_t get() { return otThreadGetLocalLeaderWeight(DeviceInstance); }
void set(uint8_t value) { otThreadSetLocalLeaderWeight(DeviceInstance, value); }
}
property uint32_t LocalLeaderPartitionId
{
uint32_t get() { return otThreadGetLocalLeaderPartitionId(DeviceInstance); }
void set(uint32_t value) { otThreadSetLocalLeaderPartitionId(DeviceInstance, value); }
}
property uint8_t LeaderWeight
{
uint8_t get() { return otThreadGetLeaderWeight(DeviceInstance); }
}
property uint32_t LeaderRouterId
{
uint32_t get() { return otThreadGetLeaderRouterId(DeviceInstance); }
}
property uint32_t PartitionId
{
uint32_t get() { return otThreadGetPartitionId(DeviceInstance); }
}
property uint16_t Rloc16
{
uint16_t get() { return otThreadGetRloc16(DeviceInstance); }
}
property otThreadState State
{
otThreadState get()
{
return (otThreadState)otThreadGetDeviceRole(DeviceInstance);
}
}
#pragma endregion
#pragma endregion
#pragma region Constructor/Destructor
otAdapter(_In_ IntPtr /*otInstance**/ aInstance)
{
_Instance = (void*)aInstance;
IInspectable* pInspectable = reinterpret_cast<IInspectable*>(this);
// Register for device availability callbacks
otSetStateChangedCallback(DeviceInstance, ThreadStateChangeCallback, pInspectable);
}
virtual ~otAdapter()
{
// Unregister for callbacks for the device
otSetStateChangedCallback(DeviceInstance, nullptr, nullptr);
// Free the device
otFreeMemory(DeviceInstance);
}
#pragma endregion
#pragma region Functions
void PlatformReset()
{
otInstanceReset(DeviceInstance);
}
void FactoryReset()
{
otInstanceFactoryReset(DeviceInstance);
}
void BecomeRouter()
{
ThrowOnFailure(otThreadBecomeRouter(DeviceInstance));
}
void BecomeLeader()
{
ThrowOnFailure(otThreadBecomeLeader(DeviceInstance));
}
#pragma endregion
internal:
void InvokeAdapterRemoval()
{
AdapterRemoval(this);
}
private:
friend ref class otApi;
static void OTCALL
ThreadStateChangeCallback(
uint32_t aFlags,
_In_ void* aContext
)
{
IInspectable* pInspectable = (IInspectable*)aContext;
otAdapter^ pThis = reinterpret_cast<otAdapter^>(pInspectable);
if (aFlags & OT_IP6_ADDRESS_ADDED)
{
pThis->IpAddressAdded(pThis);
}
if (aFlags & OT_IP6_ADDRESS_REMOVED)
{
pThis->IpAddressRemoved(pThis);
}
if (aFlags & OT_IP6_RLOC_ADDED)
{
pThis->IpRlocAdded(pThis);
}
if (aFlags & OT_IP6_RLOC_REMOVED)
{
pThis->IpRlocRemoved(pThis);
}
if (aFlags & OT_IP6_LL_ADDR_CHANGED)
{
pThis->IpLinkLocalAddresChanged(pThis);
}
if (aFlags & OT_IP6_ML_ADDR_CHANGED)
{
pThis->IpMeshLocalAddresChanged(pThis);
}
if (aFlags & OT_NET_ROLE)
{
pThis->NetRoleChanged(pThis);
}
if (aFlags & OT_NET_PARTITION_ID)
{
pThis->NetPartitionIdChanged(pThis);
}
if (aFlags & OT_NET_KEY_SEQUENCE_COUNTER)
{
pThis->NetKeySequenceCounterChanged(pThis);
}
if (aFlags & OT_THREAD_CHILD_ADDED)
{
pThis->ThreadChildAdded(pThis);
}
if (aFlags & OT_THREAD_CHILD_REMOVED)
{
pThis->ThreadChildRemoved(pThis);
}
if (aFlags & OT_THREAD_NETDATA_UPDATED)
{
pThis->ThreadNetDataUpdated(pThis);
}
}
static HRESULT
TheadErrorToHResult(
int /* ThreadError */ error
)
{
switch (error)
{
case kThreadError_NoBufs: return E_OUTOFMEMORY;
case kThreadError_Drop:
case kThreadError_NoRoute: return HRESULT_FROM_WIN32(ERROR_NETWORK_UNREACHABLE);
case kThreadError_InvalidArgs: return E_INVALIDARG;
case kThreadError_Security: return E_ACCESSDENIED;
case kThreadError_NotCapable:
case kThreadError_NotImplemented: return E_NOTIMPL;
case kThreadError_InvalidState: return E_NOT_VALID_STATE;
case kThreadError_NotFound: return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
case kThreadError_Already: return HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
case kThreadError_ResponseTimeout: return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
default: return E_FAIL;
}
}
};
} // namespace Thread
+252
View File
@@ -0,0 +1,252 @@
/*
* 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.
*/
#pragma once
#include "otAdapter.h"
#include <collection.h>
using namespace Platform;
using namespace Platform::Collections;
using namespace Windows::Foundation::Collections;
#define MAC8_FORMAT L"%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X"
#define MAC8_ARG(mac) mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], mac[6], mac[7]
namespace Thread
{
public delegate void otAdapterArrivalDelegate(otAdapter^ aAdapter);
// Helper class for OpenThread API
public ref class otApi sealed
{
private:
void *_apiInstance;
#define ApiInstance ((otApiInstance*)_apiInstance)
CRITICAL_SECTION _cs;
Vector<otAdapter^>^ _adapters;
public:
// Event for device availability changes
event otAdapterArrivalDelegate^ AdapterArrival;
property IntPtr RawHandle
{
IntPtr get() { return _apiInstance; }
}
// Constructor
otApi() :
_adapters(ref new Vector<otAdapter^>())
{
// Initialize the API handle
_apiInstance = otApiInit();
if (_apiInstance == nullptr)
{
throw Exception::CreateException(E_UNEXPECTED, L"otApiInit failed.");
}
InitializeCriticalSection(&_cs);
IInspectable* pInspectable = reinterpret_cast<IInspectable*>(this);
// Register for device availability callbacks
otSetDeviceAvailabilityChangedCallback(ApiInstance, ThreadDeviceAvailabilityCallback, pInspectable);
// Query list of devices
auto deviceList = otEnumerateDevices(ApiInstance);
if (deviceList)
{
EnterCriticalSection(&_cs);
// Add each adapter to our cache unless it already was inserted from a notification
for (DWORD i = 0; i < deviceList->aDevicesLength; i++)
{
if (GetAdapter(deviceList->aDevices[i]) == nullptr)
{
auto deviceInstance = otInstanceInit(ApiInstance, &deviceList->aDevices[i]);
if (deviceInstance)
{
_adapters->Append(ref new otAdapter(deviceInstance));
}
}
}
LeaveCriticalSection(&_cs);
otFreeMemory(deviceList);
}
}
// Destructor
virtual ~otApi()
{
// Clear registration for callbacks
otSetDeviceAvailabilityChangedCallback(ApiInstance, nullptr, nullptr);
DeleteCriticalSection(&_cs);
// Clean up api
otApiFinalize(ApiInstance);
_apiInstance = nullptr;
}
// Returns the entire list of adapters
IVectorView<otAdapter^>^ GetAdapters()
{
IVectorView<otAdapter^>^ adapters;
EnterCriticalSection(&_cs);
adapters = _adapters->GetView(); // TODO - Need to copy
LeaveCriticalSection(&_cs);
return adapters;
}
// Helper to get an adapter, given its device guid
otAdapter^ GetAdapter(Guid aDeviceGuid)
{
otAdapter^ ret = nullptr;
EnterCriticalSection(&_cs);
for (auto&& adapter : _adapters)
{
if (adapter->InterfaceGuid == aDeviceGuid)
{
ret = adapter;
break;
}
}
LeaveCriticalSection(&_cs);
return ret;
}
// Helper function to convert mac address to string
static String^ ToString(uint64_t mac)
{
WCHAR szMac[64] = { 0 };
swprintf_s(szMac, 64, MAC8_FORMAT, MAC8_ARG(((UCHAR*)&mac)));
return ref new String(szMac);
}
// Helper function to convert RLOC16/PANID to string
static String^ ToString(uint16_t rloc)
{
WCHAR szRloc[16] = { 0 };
swprintf_s(szRloc, 16, L"0x%X", rloc);
return ref new String(szRloc);
}
// Helper function to convert state to string
static String^ ToString(otThreadState state)
{
switch (state)
{
default:
case otThreadState::Offline: return L"Offline";
case otThreadState::Disabled: return L"Disabled";
case otThreadState::Detached: return L"Disconnected";
case otThreadState::Child: return L"Connected - Child";
case otThreadState::Router: return L"Connected - Router";
case otThreadState::Leader: return L"Connected - Leader";
}
}
private:
// Callback from OpenThread indicating arrival or removal of interfaces
static void OTCALL
ThreadDeviceAvailabilityCallback(
bool aAdded,
const GUID* aDeviceGuid,
_In_ void* aContext
)
{
IInspectable* pInspectable = (IInspectable*)aContext;
otApi^ pThis = reinterpret_cast<otApi^>(pInspectable);
if (aAdded)
{
otAdapter^ adapter = nullptr;
// Add the device to the list, if it isn't already there
EnterCriticalSection(&pThis->_cs);
if (pThis->GetAdapter(*aDeviceGuid) == nullptr)
{
auto deviceInstance = otInstanceInit((otApiInstance*)pThis->_apiInstance, aDeviceGuid);
if (deviceInstance)
{
pThis->_adapters->Append(adapter = ref new otAdapter(deviceInstance));
}
}
LeaveCriticalSection(&pThis->_cs);
if (adapter)
{
// Send a notification of arrival
pThis->AdapterArrival(adapter);
}
}
else
{
otAdapter^ ret = nullptr;
Guid guid = *aDeviceGuid;
EnterCriticalSection(&pThis->_cs);
// Look up in the cached list of adapters to remove it
uint32_t i = 0;
for (auto&& adapter : pThis->_adapters)
{
if (adapter->InterfaceGuid == guid)
{
ret = adapter;
pThis->_adapters->RemoveAt(i);
break;
}
i++;
}
LeaveCriticalSection(&pThis->_cs);
if (ret)
{
ret->InvokeAdapterRemoval();
}
}
}
};
} // namespace Thread
+2 -4
View File
@@ -38,9 +38,7 @@
#include <ws2def.h>
#include <ws2ipdef.h>
#include <mstcpip.h>
#define OTDLL 1
#include <openthread/openthread.h>
#include <openthread/commissioner.h>
#include <openthread/joiner.h>
#include "otApi.h"
#include "App.xaml.h"