blob: c2bc7c0c6a998827e52d632cbb6f6f5c98d0a808 [file] [log] [blame]
/*
* 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
//
otIp6SetEnabled(device, true);
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);
otIp6SetEnabled(device, false);
// Cleanup
otFreeMemory(device);
}