| /* | |
| * 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" | |
| #include "TalkGrid.xaml.h" | |
| using namespace ot; | |
| 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; | |
| #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] | |
| void otLog(PCSTR aFormat, ...) | |
| { | |
| va_list args; | |
| va_start(args, aFormat); | |
| CHAR logString[512] = { 0 }; | |
| int charsWritten = vsprintf_s(logString, sizeof(logString), aFormat, args); | |
| if (charsWritten > 0) OutputDebugStringA(logString); | |
| va_end(args); | |
| } | |
| MainPage::MainPage() : _otApi(nullptr) | |
| { | |
| InitializeComponent(); | |
| InterfaceConfigCancelButton->Click += | |
| ref new RoutedEventHandler( | |
| [=](Platform::Object^, RoutedEventArgs^) { | |
| this->InterfaceConfiguration->Visibility = Windows::UI::Xaml::Visibility::Collapsed; | |
| this->_curAdapter = nullptr; | |
| } | |
| ); | |
| InterfaceConfigOkButton->Click += | |
| ref new RoutedEventHandler( | |
| [=](Platform::Object^, RoutedEventArgs^) { | |
| this->InterfaceConfiguration->Visibility = Windows::UI::Xaml::Visibility::Collapsed; | |
| this->ConnectNetwork(_curAdapter); | |
| this->_curAdapter = nullptr; | |
| } | |
| ); | |
| InterfaceDetailsCloseButton->Click += | |
| ref new RoutedEventHandler( | |
| [=](Platform::Object^, RoutedEventArgs^) { | |
| this->InterfaceDetails->Visibility = Windows::UI::Xaml::Visibility::Collapsed; | |
| } | |
| ); | |
| Talk->Click += | |
| ref new RoutedEventHandler( | |
| [=](Platform::Object^, RoutedEventArgs^) { | |
| this->ThreadGrid->Visibility = Windows::UI::Xaml::Visibility::Collapsed; | |
| this->TalkGrid->Visibility = Windows::UI::Xaml::Visibility::Visible; | |
| } | |
| ); | |
| TlkGrid->Init(this); | |
| } | |
| void MainPage::OnNavigatedTo(NavigationEventArgs^ e) | |
| { | |
| Loaded += ref new RoutedEventHandler(this, &MainPage::OnLoaded); | |
| Unloaded += ref new RoutedEventHandler(this, &MainPage::OnUnloaded); | |
| } | |
| void MainPage::OnLoaded(Object^ sender, RoutedEventArgs^ e) | |
| { | |
| try | |
| { | |
| // Initialize api handle | |
| _otApi = ref new otApi(); | |
| // 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) | |
| { | |
| if (_otApi) | |
| { | |
| // Unregister | |
| _otApi->AdapterArrival -= _adapterArrivalToken; | |
| // 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::ShowInterfaceDetails(otAdapter^ adapter) | |
| { | |
| try | |
| { | |
| InterfaceMacAddress->Text = otApi::MacToString(adapter->ExtendedAddress); | |
| InterfaceML_EID->Text = adapter->MeshLocalEid->ToString(); | |
| InterfaceRLOC->Text = otApi::Rloc16ToString(adapter->Rloc16); | |
| if (adapter->State > otThreadState::Child) | |
| { | |
| uint8_t index = 0; | |
| otChildInfo childInfo; | |
| while (OT_ERROR_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; | |
| } | |
| // Show the details | |
| InterfaceDetails->Visibility = Windows::UI::Xaml::Visibility::Visible; | |
| } | |
| catch (Exception^) | |
| { | |
| } | |
| } | |
| void MainPage::AddAdapterToList(otAdapter^ adapter) | |
| { | |
| 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^) { | |
| this->_curAdapter = adapter; | |
| this->InterfaceConfiguration->Visibility = Windows::UI::Xaml::Visibility::Visible; | |
| } | |
| ); | |
| InterfaceStackPanel->Children->Append(ConnectButton); | |
| // 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^) { | |
| 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^) { | |
| 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::ThreadStateToString(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); | |
| } | |
| catch (Exception^) | |
| { | |
| } | |
| } | |
| 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^) | |
| { | |
| } | |
| } | |
| void MainPage::DisconnectNetwork(otAdapter^ adapter) | |
| { | |
| try | |
| { | |
| GUID interfaceGuid = adapter->InterfaceGuid; | |
| WCHAR szName[256] = { 0 }; | |
| swprintf_s(szName, 256, GUID_FORMAT, GUID_ARG(interfaceGuid)); | |
| otLog("%S disconnecting...\n", szName); | |
| // Stop the Thread network and bring down the interface | |
| adapter->ThreadEnabled = false; | |
| adapter->IpEnabled = false; | |
| } | |
| catch (Exception^) | |
| { | |
| } | |
| } | |
| Windows::UI::Xaml::UIElement^ | |
| MainPage::ThreadGrid::get() | |
| { | |
| return ThrdGrid; | |
| } | |
| Windows::UI::Xaml::UIElement^ | |
| MainPage::TalkGrid::get() | |
| { | |
| return TlkGrid; | |
| } |