mirror of
https://github.com/espressif/openthread.git
synced 2026-08-07 11:17:46 +00:00
Add an initial Windows Universal App for OpenThread (#951)
* Add an initial Windows Universal App for OpenThread
This commit is contained in:
@@ -0,0 +1,407 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "pch.h"
|
||||
#include "MainPage.xaml.h"
|
||||
|
||||
using namespace Thread;
|
||||
|
||||
using namespace Platform;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
using namespace Windows::UI::Xaml::Controls::Primitives;
|
||||
using namespace Windows::UI::Xaml::Data;
|
||||
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)
|
||||
{
|
||||
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";
|
||||
}
|
||||
|
||||
return L"Unknown Role State";
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
MainPage::Current = this;
|
||||
_isFullScreen = false;
|
||||
|
||||
_apiInstance = nullptr;
|
||||
InterfaceConfigCancelButton->Click +=
|
||||
ref new RoutedEventHandler(
|
||||
[=](Platform::Object^, RoutedEventArgs^) {
|
||||
InterfaceConfiguration->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
|
||||
}
|
||||
);
|
||||
InterfaceConfigOkButton->Click +=
|
||||
ref new RoutedEventHandler(
|
||||
[=](Platform::Object^, RoutedEventArgs^) {
|
||||
InterfaceConfiguration->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
|
||||
ConnectNetwork(_currentInterface);
|
||||
}
|
||||
);
|
||||
InterfaceDetailsCloseButton->Click +=
|
||||
ref new RoutedEventHandler(
|
||||
[=](Platform::Object^, RoutedEventArgs^) {
|
||||
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)
|
||||
{
|
||||
// Register for device availability callbacks
|
||||
otSetDeviceAvailabilityChangedCallback(ApiInstance, ThreadDeviceAvailabilityCallback, nullptr);
|
||||
|
||||
// Build the initial list
|
||||
BuildInterfaceList();
|
||||
}
|
||||
}
|
||||
|
||||
void MainPage::OnUnloaded(Object^ sender, RoutedEventArgs^ e)
|
||||
{
|
||||
// Unregister for callbacks
|
||||
otSetDeviceAvailabilityChangedCallback(ApiInstance, nullptr, nullptr);
|
||||
|
||||
// Clean up api handle
|
||||
otApiFinalize(ApiInstance);
|
||||
_apiInstance = nullptr;
|
||||
}
|
||||
|
||||
void MainPage::OnResuming()
|
||||
{
|
||||
}
|
||||
|
||||
void MainPage::OnWindowSizeChanged(Object^ sender, SizeChangedEventArgs^ args)
|
||||
{
|
||||
_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 = otGetExtendedAddress(device);
|
||||
if (extendedAddress)
|
||||
{
|
||||
WCHAR szMac[256] = { 0 };
|
||||
swprintf_s(szMac, 256, MAC8_FORMAT, MAC8_ARG(extendedAddress));
|
||||
InterfaceMacAddress->Text = ref new String(szMac);
|
||||
|
||||
otFreeMemory(extendedAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
InterfaceMacAddress->Text = L"ERROR";
|
||||
}
|
||||
|
||||
auto ml_eid = otGetMeshLocalEid(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 = otGetRloc16(device);
|
||||
WCHAR szRloc[16] = { 0 };
|
||||
swprintf_s(szRloc, 16, L"%4x", rloc16);
|
||||
InterfaceRLOC->Text = ref new String(szRloc);
|
||||
|
||||
if (otGetDeviceRole(device) > kDeviceRoleChild)
|
||||
{
|
||||
uint8_t index = 0;
|
||||
otChildInfo childInfo;
|
||||
while (kThreadError_None == otGetChildInfoByIndex(device, 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;
|
||||
}
|
||||
|
||||
// Show the details
|
||||
InterfaceDetails->Visibility = Windows::UI::Xaml::Visibility::Visible;
|
||||
|
||||
otFreeMemory(device);
|
||||
}
|
||||
|
||||
UIElement^ MainPage::CreateNewInterface(Platform::Guid InterfaceGuid)
|
||||
{
|
||||
GUID deviceGuid = InterfaceGuid;
|
||||
|
||||
auto device = otInstanceInit(ApiInstance, &deviceGuid);
|
||||
auto deviceRole = otGetDeviceRole(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)
|
||||
{
|
||||
auto ConnectButton = ref new Button();
|
||||
ConnectButton->Content = ref new String(L"Connect");
|
||||
ConnectButton->Click +=
|
||||
ref new RoutedEventHandler(
|
||||
[=](Platform::Object^, RoutedEventArgs^) {
|
||||
_currentInterface = InterfaceGuid;
|
||||
InterfaceConfiguration->Visibility = Windows::UI::Xaml::Visibility::Visible;
|
||||
}
|
||||
);
|
||||
InterfaceStackPanel->Children->Append(ConnectButton);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto DetailsButton = ref new Button();
|
||||
DetailsButton->Content = ref new String(L"Details");
|
||||
DetailsButton->Click +=
|
||||
ref new RoutedEventHandler(
|
||||
[=](Platform::Object^, RoutedEventArgs^) {
|
||||
ShowInterfaceDetails(InterfaceGuid);
|
||||
}
|
||||
);
|
||||
InterfaceStackPanel->Children->Append(DetailsButton);
|
||||
|
||||
auto DisconnectButton = ref new Button();
|
||||
DisconnectButton->Content = ref new String(L"Disconnect");
|
||||
DisconnectButton->Click +=
|
||||
ref new RoutedEventHandler(
|
||||
[=](Platform::Object^, RoutedEventArgs^) {
|
||||
DisconnectNetwork(InterfaceGuid);
|
||||
}
|
||||
);
|
||||
InterfaceStackPanel->Children->Append(DisconnectButton);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
if (ApiInstance == nullptr) return;
|
||||
|
||||
GUID deviceGuid = InterfaceGuid;
|
||||
auto device = otInstanceInit(ApiInstance, &deviceGuid);
|
||||
|
||||
//
|
||||
// Configure
|
||||
//
|
||||
|
||||
otNetworkName networkName = {};
|
||||
wcstombs(networkName.m8, InterfaceConfigName->Text->Data(), sizeof(networkName.m8));
|
||||
otSetNetworkName(device, networkName.m8);
|
||||
|
||||
otMasterKey masterKey = {};
|
||||
wcstombs((char*)masterKey.m8, InterfaceConfigKey->Text->Data(), sizeof(masterKey.m8));
|
||||
otSetMasterKey(device, masterKey.m8, sizeof(masterKey.m8));
|
||||
|
||||
otSetChannel(device, (uint8_t)InterfaceConfigChannel->Value);
|
||||
otSetMaxAllowedChildren(device, (uint8_t)InterfaceConfigMaxChildren->Value);
|
||||
|
||||
otSetPanId(device, 0x4567);
|
||||
|
||||
//
|
||||
// Bring up the interface and start the Thread logic
|
||||
//
|
||||
|
||||
otInterfaceUp(device);
|
||||
|
||||
otThreadStart(device);
|
||||
|
||||
// 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
|
||||
//
|
||||
|
||||
otThreadStop(device);
|
||||
|
||||
otInterfaceDown(device);
|
||||
|
||||
// Cleanup
|
||||
otFreeMemory(device);
|
||||
}
|
||||
Reference in New Issue
Block a user