Windows App Updates (#1431)

* Initial new API helpers

* Finish up API helper

* Update App to use new API helpers

* More cleanup and added support for preferred router ID

* Clean up state change handlers

* Add logging

* Some more clean up and redesign

* Added small readme for Windows App.
This commit is contained in:
Nick Banks
2017-03-07 10:29:30 -08:00
committed by Jonathan Hui
parent 8054fd2f32
commit 974421da98
16 changed files with 1300 additions and 392 deletions
+257 -267
View File
@@ -42,366 +42,356 @@ 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)
void otLog(PCSTR aFormat, ...)
{
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";
}
va_list args;
va_start(args, aFormat);
return L"Unknown Role State";
CHAR logString[512] = { 0 };
int charsWritten = vsprintf_s(logString, sizeof(logString), aFormat, args);
if (charsWritten > 0) OutputDebugStringA(logString);
va_end(args);
}
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()
MainPage::MainPage() : _otApi(nullptr)
{
InitializeComponent();
MainPage::Current = this;
_isFullScreen = false;
_apiInstance = nullptr;
InterfaceConfigCancelButton->Click +=
ref new RoutedEventHandler(
[=](Platform::Object^, RoutedEventArgs^) {
InterfaceConfiguration->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
this->InterfaceConfiguration->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
this->_curAdapter = nullptr;
}
);
InterfaceConfigOkButton->Click +=
ref new RoutedEventHandler(
[=](Platform::Object^, RoutedEventArgs^) {
InterfaceConfiguration->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
ConnectNetwork(_currentInterface);
this->InterfaceConfiguration->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
this->ConnectNetwork(_curAdapter);
this->_curAdapter = nullptr;
}
);
InterfaceDetailsCloseButton->Click +=
ref new RoutedEventHandler(
[=](Platform::Object^, RoutedEventArgs^) {
InterfaceDetails->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
this->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)
try
{
// Register for device availability callbacks
otSetDeviceAvailabilityChangedCallback(ApiInstance, ThreadDeviceAvailabilityCallback, nullptr);
// Initialize api handle
_otApi = ref new otApi();
// Build the initial list
BuildInterfaceList();
// 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)
{
// Unregister for callbacks
otSetDeviceAvailabilityChangedCallback(ApiInstance, nullptr, nullptr);
if (_otApi)
{
// Unregister
_otApi->AdapterArrival -= _adapterArrivalToken;
// Clean up api handle
otApiFinalize(ApiInstance);
_apiInstance = nullptr;
// 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::OnWindowSizeChanged(Object^ sender, SizeChangedEventArgs^ args)
void MainPage::ShowInterfaceDetails(otAdapter^ adapter)
{
_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 = otLinkGetExtendedAddress(device);
if (extendedAddress)
try
{
WCHAR szMac[256] = { 0 };
swprintf_s(szMac, 256, MAC8_FORMAT, MAC8_ARG(extendedAddress));
InterfaceMacAddress->Text = ref new String(szMac);
InterfaceMacAddress->Text = otApi::ToString(adapter->ExtendedAddress);
InterfaceML_EID->Text = adapter->MeshLocalEid->ToString();
InterfaceRLOC->Text = otApi::ToString(adapter->Rloc16);
otFreeMemory(extendedAddress);
}
else
{
InterfaceMacAddress->Text = L"ERROR";
}
auto ml_eid = otThreadGetMeshLocalEid(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 = otThreadGetRloc16(device);
WCHAR szRloc[16] = { 0 };
swprintf_s(szRloc, 16, L"%4x", rloc16);
InterfaceRLOC->Text = ref new String(szRloc);
if (otThreadGetDeviceRole(device) > kDeviceRoleChild)
{
uint8_t index = 0;
otChildInfo childInfo;
while (kThreadError_None == otThreadGetChildInfoByIndex(device, index, &childInfo))
if (adapter->State > otThreadState::Child)
{
index++;
uint8_t index = 0;
otChildInfo childInfo;
while (kThreadError_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;
}
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^)
{
// Show the details
InterfaceDetails->Visibility = Windows::UI::Xaml::Visibility::Visible;
otFreeMemory(device);
}
}
UIElement^ MainPage::CreateNewInterface(Platform::Guid InterfaceGuid)
void MainPage::AddAdapterToList(otAdapter^ adapter)
{
GUID deviceGuid = InterfaceGuid;
auto device = otInstanceInit(ApiInstance, &deviceGuid);
auto deviceRole = otThreadGetDeviceRole(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)
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^) {
_currentInterface = InterfaceGuid;
InterfaceConfiguration->Visibility = Windows::UI::Xaml::Visibility::Visible;
this->_curAdapter = adapter;
this->InterfaceConfiguration->Visibility = Windows::UI::Xaml::Visibility::Visible;
}
);
);
InterfaceStackPanel->Children->Append(ConnectButton);
}
else
{
// 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^) {
ShowInterfaceDetails(InterfaceGuid);
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^) {
DisconnectNetwork(InterfaceGuid);
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::ToString(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);
}
// 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)
catch (Exception^)
{
// 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)
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^)
{
// 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)
void MainPage::DisconnectNetwork(otAdapter^ adapter)
{
if (ApiInstance == nullptr) return;
try
{
GUID interfaceGuid = adapter->InterfaceGuid;
WCHAR szName[256] = { 0 };
swprintf_s(szName, 256, GUID_FORMAT, GUID_ARG(interfaceGuid));
otLog("%S disconnecting...\n", szName);
GUID deviceGuid = InterfaceGuid;
auto device = otInstanceInit(ApiInstance, &deviceGuid);
// Stop the Thread network and bring down the interface
adapter->ThreadEnabled = false;
adapter->IpEnabled = false;
}
catch (Exception^)
{
//
// Configure
//
otNetworkName networkName = {};
wcstombs(networkName.m8, InterfaceConfigName->Text->Data(), sizeof(networkName.m8));
otThreadSetNetworkName(device, networkName.m8);
otMasterKey masterKey = {};
wcstombs((char*)masterKey.m8, InterfaceConfigKey->Text->Data(), sizeof(masterKey.m8));
otThreadSetMasterKey(device, masterKey.m8, sizeof(masterKey.m8));
otLinkSetChannel(device, (uint8_t)InterfaceConfigChannel->Value);
otThreadSetMaxAllowedChildren(device, (uint8_t)InterfaceConfigMaxChildren->Value);
otLinkSetPanId(device, 0x4567);
//
// Bring up the interface and start the Thread logic
//
otIp6SetEnabled(device, true);
otThreadSetEnabled(device, true);
// 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
//
otThreadSetEnabled(device, false);
otIp6SetEnabled(device, false);
// Cleanup
otFreeMemory(device);
}
}