/* * 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(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); }