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