mirror of
https://github.com/espressif/openthread.git
synced 2026-07-03 02:40:27 +00:00
408 lines
13 KiB
C++
408 lines
13 KiB
C++
/*
|
|
* 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);
|
|
}
|