Windows: UWP Sample App with WACK tests
authorIbrahim Esmat <iesmat@microsoft.com>
Wed, 7 Jun 2017 02:22:04 +0000 (19:22 -0700)
committerDan Mihai <Daniel.Mihai@microsoft.com>
Thu, 20 Jul 2017 16:32:24 +0000 (16:32 +0000)
An elevator client UWP sample app using the dynamic ipca and
octbstack dlls.

The Supported APIs WACK test is run if building for Release.

Change-Id: I54b03d366a21b51cb723c355a252572b71050cd8
Signed-off-by: Ibrahim Esmat <iesmat@microsoft.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/21315
Tested-by: jenkins-iotivity <jenkins@iotivity.org>
Reviewed-by: Dan Mihai <Daniel.Mihai@microsoft.com>
33 files changed:
build_common/windows/SConscript
resource/IPCA/samples/ElevatorClientUWP/App.xaml [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/App.xaml.cpp [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/App.xaml.h [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/Assets/LockScreenLogo.scale-200.png [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/Assets/SplashScreen.scale-200.png [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/Assets/Square150x150Logo.scale-200.png [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/Assets/Square44x44Logo.scale-200.png [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/Assets/Square44x44Logo.targetsize-24_altform-unplated.png [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/Assets/StoreLogo.png [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/Assets/Wide310x150Logo.scale-200.png [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/Elevator.cpp [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/Elevator.h [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/ElevatorClientUWP.sln [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/ElevatorClientUWP.vcxproj [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/ElevatorClientUWP.vcxproj.filters [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/ElevatorClientUWP_TemporaryKey.pfx [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/ElevatorDiscovery.cpp [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/ElevatorDiscovery.h [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/ElevatorViewModel.cpp [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/ElevatorViewModel.h [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/IPCALibs.targets [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/MainPage.xaml [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/MainPage.xaml.cpp [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/MainPage.xaml.h [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/Package.appxmanifest [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/SConscript [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/Util.cpp [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/Util.h [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/pch.cpp [new file with mode: 0644]
resource/IPCA/samples/ElevatorClientUWP/pch.h [new file with mode: 0644]
resource/IPCA/samples/SConscript
tools/scons/RunTest.py

index cd0fc4c..fd167e9 100644 (file)
@@ -2,7 +2,103 @@
 # This script includes windows specific config (MSVS/MSVC)
 ##
 Import('env')
-import os.path
+import os
+import winreg
+import platform
+
+def OpenRegKey(env, key, sub_key, reg_view_64bit=False):
+    # Default access
+    reg_access_mask = winreg.KEY_READ
+    if reg_view_64bit:
+        # Access the 64bit Registry View
+        reg_access_mask |= winreg.KEY_WOW64_64KEY
+
+    try:
+        hkey = winreg.OpenKey(key,
+                              sub_key,
+                              0, # Reserved
+                              reg_access_mask)
+    except WindowsError, err:
+        if err.errno != os.errno.ENOENT:
+            # Couldn't open Registry Key
+            Exit('Error: Could not Open %s Registry Key. Err=%s' % (sub_key, err.errno))
+        else:
+            # Registry Key not found
+            hkey = None
+
+    return hkey
+
+def ReadRegistryStringValue(env, key, sub_key, value_name, reg_view_64bit=False):
+    hkey = env.OpenRegKey(key, sub_key, reg_view_64bit)
+    if hkey:
+        try:
+            (value, type) = winreg.QueryValueEx(hkey, value_name)
+            if type != winreg.REG_SZ:
+                Exit('Error: Registry Value \'%s\' is not a String' % value_name)
+        except WindowsError, err:
+            if err.errno != os.errno.ENOENT:
+                Exit('Error: Could not QueryValueEx for Registry Value \'%s\'. Err=%s' \
+                     % (value_name, err.errno))
+            else:
+                # Registry Value not found
+                value = None
+
+        hkey.Close()
+    else:
+        value = None
+
+    return value
+
+def SetupMSBuildEnv(env):
+    # Add MSBuild path to path
+    msbuild_reg_path = 'SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\' + env.get('MSVC_VERSION')
+    # MSBuild executable arch needs to be the same as the python/SCons environment Arch.
+    # Open the Registry key with the same registry view as the environment (handled by the
+    # winreg.OpenKey function)
+    msbuild_path = env.ReadRegistryStringValue(winreg.HKEY_LOCAL_MACHINE,
+                                               msbuild_reg_path,
+                                               'MSBuildToolsPath')
+    if not msbuild_path:
+        Exit('Error: Could not Find the MSBuild Registry Key/Value')
+
+    env.AppendUnique(PATH = [msbuild_path])
+    # Need to update the 'ENV' dictionary PATH as that's what is used when executing
+    # commands
+    env['ENV']['PATH'] = env.get('PATH')
+
+def GetMSBuildArgs(env):
+    target_arch = env.get('TARGET_ARCH')
+    platform = target_arch
+    if target_arch in ['amd64']:
+        platform = 'x64'
+
+    # Set configuration value used by Visual Studio.
+    if env.get('RELEASE'):
+        configuration = 'Release'
+    else:
+        configuration = 'Debug /p:AppxPackageAllowDebugFrameworkReferencesInManifest=true'
+
+    # Get the MSBuildOutDir env variable for the output directory
+    # Note: This should only be set on a cloned Environment and not the global one
+    outdir = env.get('MSBuildOutDir')
+
+    return (configuration, platform, outdir)
+
+def MSBuildGenerator(source, target, env, for_signature):
+    env.SetupMSBuildEnv()
+    (configuration, platform, outdir) = env.GetMSBuildArgs()
+
+    msbuild_cmd = 'msbuild.exe %s /p:Configuration=%s /p:Platform=%s /p:OutDir="%s"' \
+                  % (source[0], configuration, platform, outdir)
+    return msbuild_cmd
+
+def MSBuildClean(env, target, solutionfile):
+    env.SetupMSBuildEnv()
+    (configuration, platform, outdir) = env.GetMSBuildArgs()
+
+    msbuild_cmd = 'msbuild.exe %s /p:Configuration=%s /p:Platform=%s /p:OutDir="%s" /t:Clean' \
+                  % (solutionfile, configuration, platform, outdir)
+    env.Execute(msbuild_cmd)
 
 help_vars = Variables()
 if env.get('BUILD_JAVA') == True:
@@ -26,6 +122,22 @@ if env.get('BUILD_JAVA') == True:
 help_vars.Update(env)
 Help(help_vars.GenerateHelpText(env))
 
+if env.get('UWP_APP') == '1':
+    # Create an MSBuilder that uses a generator to generate the MSBuild actions
+    MSBuilder = Builder(generator = MSBuildGenerator)
+    # Add the builder to the env
+    env.Append(BUILDERS = {'MSBuild' : MSBuilder})
+    # Add MSBuildClean function to env
+    env.AddMethod(MSBuildClean)
+    # Add SetupMSBuildEnv function to env
+    env.AddMethod(SetupMSBuildEnv)
+    # Add GetMSBuildArgs function to env
+    env.AddMethod(GetMSBuildArgs)
+    # Add OpenRegKey function to env
+    env.AddMethod(OpenRegKey)
+    # Add ReadRegistryStringValue function to env
+    env.AddMethod(ReadRegistryStringValue)
+
 # Set common flags
 if env['CC'] == 'cl':
     if env.get('UWP_APP') == '1':
diff --git a/resource/IPCA/samples/ElevatorClientUWP/App.xaml b/resource/IPCA/samples/ElevatorClientUWP/App.xaml
new file mode 100644 (file)
index 0000000..293ddf2
--- /dev/null
@@ -0,0 +1,8 @@
+<Application\r
+    x:Class="ElevatorClientUWP.App"\r
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"\r
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"\r
+    xmlns:local="using:ElevatorClientUWP"\r
+    RequestedTheme="Light">\r
+\r
+</Application>\r
diff --git a/resource/IPCA/samples/ElevatorClientUWP/App.xaml.cpp b/resource/IPCA/samples/ElevatorClientUWP/App.xaml.cpp
new file mode 100644 (file)
index 0000000..6c5f7e0
--- /dev/null
@@ -0,0 +1,143 @@
+/* *****************************************************************
+*
+* Copyright 2017 Microsoft
+*
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+******************************************************************/\r
+\r
+//\r
+// App.xaml.cpp\r
+// Implementation of the App class.\r
+//\r
+\r
+#include "pch.h"\r
+#include "MainPage.xaml.h"\r
+\r
+using namespace ElevatorClientUWP;\r
+\r
+using namespace Platform;\r
+using namespace Windows::ApplicationModel;\r
+using namespace Windows::ApplicationModel::Activation;\r
+using namespace Windows::Foundation;\r
+using namespace Windows::Foundation::Collections;\r
+using namespace Windows::UI::Xaml;\r
+using namespace Windows::UI::Xaml::Controls;\r
+using namespace Windows::UI::Xaml::Controls::Primitives;\r
+using namespace Windows::UI::Xaml::Data;\r
+using namespace Windows::UI::Xaml::Input;\r
+using namespace Windows::UI::Xaml::Interop;\r
+using namespace Windows::UI::Xaml::Media;\r
+using namespace Windows::UI::Xaml::Navigation;\r
+\r
+/// <summary>\r
+/// Initializes the singleton application object.  This is the first line of authored code\r
+/// executed, and as such is the logical equivalent of main() or WinMain().\r
+/// </summary>\r
+App::App()\r
+{\r
+    InitializeComponent();\r
+    Suspending += ref new SuspendingEventHandler(this, &App::OnSuspending);\r
+}\r
+\r
+/// <summary>\r
+/// Invoked when the application is launched normally by the end user.  Other entry points\r
+/// will be used such as when the application is launched to open a specific file.\r
+/// </summary>\r
+/// <param name="e">Details about the launch request and process.</param>\r
+void App::OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs^ e)\r
+{\r
+#if _DEBUG\r
+    // Show graphics profiling information while debugging.\r
+    if (IsDebuggerPresent())\r
+    {\r
+        // Display the current frame rate counters\r
+         DebugSettings->EnableFrameRateCounter = true;\r
+    }\r
+#endif\r
+    auto rootFrame = dynamic_cast<Frame^>(Window::Current->Content);\r
+\r
+    // Do not repeat app initialization when the Window already has content,\r
+    // just ensure that the window is active\r
+    if (rootFrame == nullptr)\r
+    {\r
+        // Create a Frame to act as the navigation context and associate it with\r
+        // a SuspensionManager key\r
+        rootFrame = ref new Frame();\r
+\r
+        rootFrame->NavigationFailed += ref new Windows::UI::Xaml::Navigation::NavigationFailedEventHandler(this, &App::OnNavigationFailed);\r
+\r
+        if (e->PreviousExecutionState == ApplicationExecutionState::Terminated)\r
+        {\r
+            // TODO: Restore the saved session state only when appropriate, scheduling the\r
+            // final launch steps after the restore is complete\r
+\r
+        }\r
+\r
+        if (e->PrelaunchActivated == false)\r
+        {\r
+            if (rootFrame->Content == nullptr)\r
+            {\r
+                // When the navigation stack isn't restored navigate to the first page,\r
+                // configuring the new page by passing required information as a navigation\r
+                // parameter\r
+                rootFrame->Navigate(TypeName(MainPage::typeid), e->Arguments);\r
+            }\r
+            // Place the frame in the current Window\r
+            Window::Current->Content = rootFrame;\r
+            // Ensure the current window is active\r
+            Window::Current->Activate();\r
+        }\r
+    }\r
+    else\r
+    {\r
+        if (e->PrelaunchActivated == false)\r
+        {\r
+            if (rootFrame->Content == nullptr)\r
+            {\r
+                // When the navigation stack isn't restored navigate to the first page,\r
+                // configuring the new page by passing required information as a navigation\r
+                // parameter\r
+                rootFrame->Navigate(TypeName(MainPage::typeid), e->Arguments);\r
+            }\r
+            // Ensure the current window is active\r
+            Window::Current->Activate();\r
+        }\r
+    }\r
+}\r
+\r
+/// <summary>\r
+/// Invoked when application execution is being suspended.  Application state is saved\r
+/// without knowing whether the application will be terminated or resumed with the contents\r
+/// of memory still intact.\r
+/// </summary>\r
+/// <param name="sender">The source of the suspend request.</param>\r
+/// <param name="e">Details about the suspend request.</param>\r
+void App::OnSuspending(Object^ sender, SuspendingEventArgs^ e)\r
+{\r
+    (void) sender;  // Unused parameter\r
+    (void) e;   // Unused parameter\r
+\r
+    //TODO: Save application state and stop any background activity\r
+}\r
+\r
+/// <summary>\r
+/// Invoked when Navigation to a certain page fails\r
+/// </summary>\r
+/// <param name="sender">The Frame which failed navigation</param>\r
+/// <param name="e">Details about the navigation failure</param>\r
+void App::OnNavigationFailed(Platform::Object ^sender, Windows::UI::Xaml::Navigation::NavigationFailedEventArgs ^e)\r
+{\r
+    throw ref new FailureException("Failed to load Page " + e->SourcePageType.Name);\r
+}
\ No newline at end of file
diff --git a/resource/IPCA/samples/ElevatorClientUWP/App.xaml.h b/resource/IPCA/samples/ElevatorClientUWP/App.xaml.h
new file mode 100644 (file)
index 0000000..9e0011b
--- /dev/null
@@ -0,0 +1,46 @@
+/* *****************************************************************
+*
+* Copyright 2017 Microsoft
+*
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+******************************************************************/\r
+\r
+//\r
+// App.xaml.h\r
+// Declaration of the App class.\r
+//\r
+\r
+#pragma once\r
+\r
+#include "App.g.h"\r
+\r
+namespace ElevatorClientUWP\r
+{\r
+       /// <summary>\r
+       /// Provides application-specific behavior to supplement the default Application class.\r
+       /// </summary>\r
+       ref class App sealed\r
+       {\r
+       protected:\r
+               virtual void OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs^ e) override;\r
+\r
+       internal:\r
+               App();\r
+\r
+       private:\r
+               void OnSuspending(Platform::Object^ sender, Windows::ApplicationModel::SuspendingEventArgs^ e);\r
+               void OnNavigationFailed(Platform::Object ^sender, Windows::UI::Xaml::Navigation::NavigationFailedEventArgs ^e);\r
+       };\r
+}\r
diff --git a/resource/IPCA/samples/ElevatorClientUWP/Assets/LockScreenLogo.scale-200.png b/resource/IPCA/samples/ElevatorClientUWP/Assets/LockScreenLogo.scale-200.png
new file mode 100644 (file)
index 0000000..735f57a
Binary files /dev/null and b/resource/IPCA/samples/ElevatorClientUWP/Assets/LockScreenLogo.scale-200.png differ
diff --git a/resource/IPCA/samples/ElevatorClientUWP/Assets/SplashScreen.scale-200.png b/resource/IPCA/samples/ElevatorClientUWP/Assets/SplashScreen.scale-200.png
new file mode 100644 (file)
index 0000000..023e7f1
Binary files /dev/null and b/resource/IPCA/samples/ElevatorClientUWP/Assets/SplashScreen.scale-200.png differ
diff --git a/resource/IPCA/samples/ElevatorClientUWP/Assets/Square150x150Logo.scale-200.png b/resource/IPCA/samples/ElevatorClientUWP/Assets/Square150x150Logo.scale-200.png
new file mode 100644 (file)
index 0000000..af49fec
Binary files /dev/null and b/resource/IPCA/samples/ElevatorClientUWP/Assets/Square150x150Logo.scale-200.png differ
diff --git a/resource/IPCA/samples/ElevatorClientUWP/Assets/Square44x44Logo.scale-200.png b/resource/IPCA/samples/ElevatorClientUWP/Assets/Square44x44Logo.scale-200.png
new file mode 100644 (file)
index 0000000..ce342a2
Binary files /dev/null and b/resource/IPCA/samples/ElevatorClientUWP/Assets/Square44x44Logo.scale-200.png differ
diff --git a/resource/IPCA/samples/ElevatorClientUWP/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/resource/IPCA/samples/ElevatorClientUWP/Assets/Square44x44Logo.targetsize-24_altform-unplated.png
new file mode 100644 (file)
index 0000000..f6c02ce
Binary files /dev/null and b/resource/IPCA/samples/ElevatorClientUWP/Assets/Square44x44Logo.targetsize-24_altform-unplated.png differ
diff --git a/resource/IPCA/samples/ElevatorClientUWP/Assets/StoreLogo.png b/resource/IPCA/samples/ElevatorClientUWP/Assets/StoreLogo.png
new file mode 100644 (file)
index 0000000..7385b56
Binary files /dev/null and b/resource/IPCA/samples/ElevatorClientUWP/Assets/StoreLogo.png differ
diff --git a/resource/IPCA/samples/ElevatorClientUWP/Assets/Wide310x150Logo.scale-200.png b/resource/IPCA/samples/ElevatorClientUWP/Assets/Wide310x150Logo.scale-200.png
new file mode 100644 (file)
index 0000000..288995b
Binary files /dev/null and b/resource/IPCA/samples/ElevatorClientUWP/Assets/Wide310x150Logo.scale-200.png differ
diff --git a/resource/IPCA/samples/ElevatorClientUWP/Elevator.cpp b/resource/IPCA/samples/ElevatorClientUWP/Elevator.cpp
new file mode 100644 (file)
index 0000000..b42f7f7
--- /dev/null
@@ -0,0 +1,487 @@
+/* *****************************************************************
+ *
+ * Copyright 2017 Microsoft
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************/
+
+#include "pch.h"
+#include <string>
+#include <iostream>
+#include "ipca.h"
+#include "Elevator.h"
+#include "ElevatorViewModel.h"
+
+using namespace ElevatorClientUWP;\r
+\r
+using namespace std;\r
+using namespace Platform;\r
+using namespace Windows::ApplicationModel::Core;\r
+using namespace Windows::Foundation;\r
+using namespace Windows::Foundation::Collections;\r
+using namespace Windows::UI::Core;\r
+using namespace Windows::UI::Xaml;\r
+using namespace Windows::UI::Xaml::Controls;\r
+using namespace Windows::UI::Xaml::Controls::Primitives;\r
+using namespace Windows::UI::Xaml::Data;\r
+using namespace Windows::UI::Xaml::Input;\r
+using namespace Windows::UI::Xaml::Media;\r
+using namespace Windows::UI::Xaml::Navigation;
+
+// Callback from IPCAObserveResource().
+void IPCA_CALL ResourceChangeNotificationCallback(
+    IPCAStatus result,
+    void* context,
+    IPCAPropertyBagHandle propertyBagHandle)
+{
+    if (context)\r
+    {\r
+        Elevator^ dev = (reinterpret_cast<WeakReference*>(context))->Resolve<Elevator>();\r
+        if (dev)\r
+        {\r
+            dev->ResourceChangeNotificationCallback(result, propertyBagHandle);\r
+        }\r
+    }
+}
+
+void IPCA_CALL SetPropertiesCallback(
+    IPCAStatus result,
+    void* context,
+    IPCAPropertyBagHandle propertyBagHandle)
+{
+    if (context)\r
+    {\r
+        Elevator^ dev = (reinterpret_cast<WeakReference*>(context))->Resolve<Elevator>();\r
+        if (dev)\r
+        {\r
+            dev->SetPropertiesCallback(result, propertyBagHandle);\r
+        }\r
+    }
+}
+
+void IPCA_CALL AuthCompletionCallback(IPCAStatus completionStatus, void* context)
+{
+    if (context)\r
+    {\r
+        Elevator^ dev = (reinterpret_cast<WeakReference*>(context))->Resolve<Elevator>();\r
+        if (dev)\r
+        {\r
+            dev->AuthCompletionCallback(completionStatus);\r
+        }\r
+    }
+}
+
+Elevator::Elevator() :
+    m_initialized(false),
+    m_dispatcher(nullptr),
+    m_ipcaAppHandle(nullptr),
+    m_observeHandle(nullptr),
+    m_deviceName(nullptr),
+    m_deviceHandle(nullptr),
+    m_deviceInfo(nullptr),
+    m_platformInfo(nullptr),
+    m_requestedAccess(false),
+    m_requestAccessHandle(nullptr)
+{
+    m_thisWeakRef = new WeakReference(this);
+    m_dispatcher = CoreApplication::MainView->Dispatcher;
+}
+
+Elevator::~Elevator()
+{
+    StopObservation();
+
+    if (m_requestAccessHandle != nullptr)
+    {
+        IPCACloseHandle(m_requestAccessHandle, nullptr, nullptr);
+        m_requestAccessHandle = nullptr;
+    }
+    
+    if (m_deviceHandle != nullptr)
+    {
+        IPCACloseDevice(m_deviceHandle);
+        m_deviceHandle = nullptr;
+    }
+
+    if (m_deviceInfo != nullptr)
+    {
+        IPCAFreeDeviceInfo(m_deviceInfo);
+        m_deviceInfo = nullptr;
+    }
+
+    if (m_platformInfo != nullptr)
+    {
+        IPCAFreePlatformInfo(m_platformInfo);
+        m_platformInfo = nullptr;
+    }
+}
+
+String^ Elevator::Name::get()
+{
+    String^ ret = m_deviceName;
+
+    if ((ret == nullptr) || ret->IsEmpty())\r
+    {\r
+        ret = Util::ConvertStrtoPlatformStr(m_deviceId.c_str());\r
+    }\r
+    
+    return ret;
+}
+
+void Elevator::Name::set(String^ value)\r
+{\r
+    if (m_deviceName != value)\r
+    {\r
+        m_deviceName = value;\r
+        OnPropertyChanged("Name");\r
+    }\r
+}
+
+String^ Elevator::CurrentFloor::get()\r
+{\r
+    return m_curFloor;\r
+}\r
+\r
+void Elevator::CurrentFloor::set(String^ value)\r
+{\r
+    if (m_curFloor != value)\r
+    {\r
+        m_curFloor = value;\r
+        OnPropertyChanged("CurrentFloor");\r
+    }\r
+}
+
+void Elevator::OnPropertyChanged(String^ propertyName)\r
+{\r
+    m_dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler(\r
+    [this, propertyName]\r
+    {\r
+        PropertyChanged(this, ref new PropertyChangedEventArgs(propertyName));\r
+    }));\r
+}\r
+\r
+IPCAStatus Elevator::OpenDevice()
+{
+    if (m_deviceHandle != nullptr)
+    {
+        return IPCA_OK;
+    }
+    else
+    {
+        return IPCAOpenDevice(m_ipcaAppHandle, m_deviceId.c_str(), &m_deviceHandle);
+    }
+}\r
+\r
+void Elevator::UpdateDeviceInfo()\r
+{\r
+    IPCAStatus status;
+
+    if (!m_initialized)
+    {
+        return;
+    }
+
+    if (OpenDevice() != IPCA_OK)
+    {
+        return;
+    }
+
+    if (m_deviceInfo != nullptr)
+    {
+        IPCAFreeDeviceInfo(m_deviceInfo);
+        m_deviceInfo = nullptr;
+    }
+
+    status = IPCAGetDeviceInfo(m_deviceHandle, &m_deviceInfo);
+    if (IPCA_OK != status)
+    {
+        return;
+    }
+
+    Name = Util::ConvertStrtoPlatformStr(m_deviceInfo->deviceName);\r
+}\r
+\r
+void Elevator::UpdatePlatformInfo()\r
+{\r
+    IPCAStatus status;
+
+    if (!m_initialized)
+    {
+        return;
+    }
+
+    if (OpenDevice() != IPCA_OK)
+    {
+        return;
+    }
+
+    if (m_platformInfo != nullptr)
+    {
+        IPCAFreePlatformInfo(m_platformInfo);
+        m_platformInfo = nullptr;
+    }
+
+    status = IPCAGetPlatformInfo(m_deviceHandle, &m_platformInfo);
+    if (IPCA_OK != status)
+    {
+        return;
+    }\r
+}\r
+\r
+void Elevator::UpdateResourceInfo()\r
+{\r
+    IPCAStatus status;
+
+    ResourceList emptyResourceList;
+
+    if (!m_initialized)
+    {
+        return;
+    }
+
+    if (OpenDevice() != IPCA_OK)
+    {
+        return;
+    }
+
+    char** resourcePathList;
+    size_t resourceListCount;
+    status = IPCAGetResources(m_deviceHandle,
+        nullptr, nullptr, &resourcePathList, &resourceListCount);
+    if (IPCA_OK != status)
+    {
+        return;
+    }
+
+    m_resourceList.clear();
+    for (size_t i = 0; i < resourceListCount; i++)
+    {
+        char** resourceTypes;
+        size_t resourceTypeCount;
+        status = IPCAGetResourceTypes(m_deviceHandle,
+            resourcePathList[i], &resourceTypes, &resourceTypeCount);
+        if (IPCA_OK == status)
+        {
+            for (size_t j = 0; j < resourceTypeCount; j++)
+            {
+                m_resourceList[resourcePathList[i]].push_back(resourceTypes[j]);
+            }
+            IPCAFreeStringArray(resourceTypes, resourceTypeCount);
+        }
+        else
+        {
+            // Log failure
+        }
+    }
+
+    IPCAFreeStringArray(resourcePathList, resourceListCount);\r
+}
+
+bool Elevator::Init(IPCAAppHandle appHandle, Platform::String^ name, std::string id)\r
+{\r
+    if (!m_initialized)\r
+    {\r
+        m_ipcaAppHandle = appHandle;\r
+        m_deviceName = name;\r
+        m_deviceId = id;\r
+        m_initialized = true;\r
+    }\r
+\r
+    return m_initialized;\r
+}\r
+\r
+void Elevator::StartObservation()\r
+{\r
+    if (OpenDevice() != IPCA_OK)\r
+    {\r
+        throw ref new FailureException("Failed to open device");\r
+    }\r
+\r
+    StopObservation();
+
+    if (IPCAObserveResource(
+        m_deviceHandle,
+        &::ResourceChangeNotificationCallback,
+        m_thisWeakRef,
+        "/ipca/sample/elevator",
+        nullptr,
+        &m_observeHandle) != IPCA_OK)\r
+    {\r
+        throw ref new FailureException("IPCAObserveResource failed.");\r
+    }\r
+}\r
+\r
+void Elevator::StopObservation()\r
+{\r
+    if (m_observeHandle != nullptr)
+    {
+        IPCACloseHandle(m_observeHandle, nullptr, nullptr);
+        m_observeHandle = nullptr;
+    }\r
+}\r
+\r
+void Elevator::ResourceChangeNotificationCallback(IPCAStatus result,\r
+    IPCAPropertyBagHandle propertyBagHandle)\r
+{\r
+    int observedCurrentFloor = -1;
+
+    if (result == IPCA_ACCESS_DENIED)
+    {
+        std::lock_guard<std::mutex> lock(m_requestAccessMutex);
+        if (!m_requestedAccess)
+        {
+            if (m_requestAccessHandle != nullptr)
+            {
+                IPCACloseHandle(m_requestAccessHandle, nullptr, nullptr);
+                m_requestAccessHandle = nullptr;
+            }
+
+            IPCAStatus reqResult = IPCARequestAccess(
+                m_deviceHandle,
+                nullptr,
+                &::AuthCompletionCallback,
+                m_thisWeakRef,
+                &m_requestAccessHandle);
+            if (reqResult == IPCA_OK)
+            {
+                m_requestedAccess = true;
+            }
+        }
+        return;
+    }
+    else if ((result != IPCA_OK) && (result != IPCA_DEVICE_APPEAR_OFFLINE))
+    {
+        String^ err = "Failure in Resource Change Notification. result=" +
+            static_cast<int>(result).ToString();
+        Util::ShowErrorMsg(m_dispatcher, err);
+        return;
+    }
+
+    if (IPCAPropertyBagGetValueInt(propertyBagHandle, "x.org.iotivity.CurrentFloor",
+        &observedCurrentFloor) == IPCA_OK)
+    {
+        CurrentFloor = observedCurrentFloor.ToString();
+    }\r
+}\r
+\r
+void Elevator::AuthCompletionCallback(IPCAStatus completionStatus)\r
+{\r
+    if ((completionStatus != IPCA_SECURITY_UPDATE_REQUEST_FINISHED) &&
+        (completionStatus != IPCA_DEVICE_APPEAR_OFFLINE))
+    {
+        std::lock_guard<std::mutex> lock(m_requestAccessMutex);
+        // Reset requested access to enable request access again.
+        m_requestedAccess = false;
+
+        String^ err = "Failure in Auth Callback. result=" +
+            static_cast<int>(completionStatus).ToString();
+        Util::ShowErrorMsg(m_dispatcher, err);
+        OnAuthFailure();
+        return;
+    }\r
+\r
+    // Restart observation\r
+    StartObservation();\r
+}\r
+\r
+void Elevator::SetPropertiesCallback(IPCAStatus result, IPCAPropertyBagHandle propertyBagHandle)\r
+{\r
+    if ((result != IPCA_OK) && (result != IPCA_DEVICE_APPEAR_OFFLINE))
+    {
+        String^ err = "Failure trying to Set Properties. result=" +
+            static_cast<int>(result).ToString();
+        Util::ShowErrorMsg(m_dispatcher, err);
+    }\r
+}\r
+\r
+void Elevator::OnUpdatedInfo()\r
+{\r
+    std::lock_guard<std::mutex> lock(m_deviceMutex);\r
+    UpdateDeviceInfo();\r
+    UpdatePlatformInfo();\r
+    UpdateResourceInfo();\r
+}\r
+\r
+IPCADeviceInfo* Elevator::GetDeviceInfo()
+{
+    std::lock_guard<std::mutex> lock(m_deviceMutex);
+    if (!m_initialized)
+    {
+        return nullptr;
+    }
+
+    return m_deviceInfo;
+}
+
+IPCAPlatformInfo* Elevator::GetPlatformInfo()
+{
+    std::lock_guard<std::mutex> lock(m_deviceMutex);
+    if (!m_initialized)
+    {
+        return nullptr;
+    }
+
+    return m_platformInfo;
+}
+
+ResourceList Elevator::GetResourceInfo()
+{
+    std::lock_guard<std::mutex> lock(m_deviceMutex);
+    ResourceList emptyResourceList;
+
+    if (!m_initialized)
+    {
+        return emptyResourceList;
+    }
+
+    return m_resourceList;
+}\r
+\r
+bool Elevator::SetTargetFloor(int floor)\r
+{\r
+    if (OpenDevice() != IPCA_OK)
+    {
+        return false;
+    }\r
+\r
+    bool ret = false;\r
+    IPCAPropertyBagHandle propertyBagHandle;
+    IPCAStatus status = IPCAPropertyBagCreate(&propertyBagHandle);
+    if (IPCA_OK == status)
+    {
+        status = IPCAPropertyBagSetValueInt(propertyBagHandle,
+            "x.org.iotivity.TargetFloor", floor);
+
+        if (IPCA_OK == status)
+        {
+            status = IPCASetProperties(m_deviceHandle,
+                &::SetPropertiesCallback,
+                m_thisWeakRef,
+                "/ipca/sample/elevator",
+                "x.org.iotivity.sample.elevator",
+                nullptr,
+                propertyBagHandle,
+                nullptr);
+
+            if (IPCA_OK == status)
+            {
+                ret = true;
+            }
+        }
+        IPCAPropertyBagDestroy(propertyBagHandle);
+    }\r
+\r
+    return ret;\r
+}
diff --git a/resource/IPCA/samples/ElevatorClientUWP/Elevator.h b/resource/IPCA/samples/ElevatorClientUWP/Elevator.h
new file mode 100644 (file)
index 0000000..df02603
--- /dev/null
@@ -0,0 +1,107 @@
+/* *****************************************************************
+ *
+ * Copyright 2017 Microsoft
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************/
+
+#pragma once
+
+#include <mutex>
+#include <array>
+#include <string>
+#include <vector>
+#include <iostream>
+#include <map>
+#include <thread>
+#include <chrono>
+#include <memory>
+#include <functional>
+
+#include "ipca.h"
+
+namespace ElevatorClientUWP
+{
+    public delegate void AuthFailure();
+    // Key of map is resource path. Value is array of resource types.
+    typedef std::map<std::string, std::vector<std::string>> ResourceList;
+
+    /**
+     * Each Elevator object represents a device discovered by IPCA.
+     */
+    [Windows::UI::Xaml::Data::Bindable]
+    public ref class Elevator sealed : Windows::UI::Xaml::Data::INotifyPropertyChanged
+    {
+    public:
+        Elevator();
+        virtual ~Elevator();
+    internal:
+        bool Init(IPCAAppHandle appHandle, Platform::String^ name, std::string id);
+        void StartObservation();
+        void StopObservation();
+        void ResourceChangeNotificationCallback(IPCAStatus result, IPCAPropertyBagHandle propertyBagHandle);
+        void SetPropertiesCallback(IPCAStatus result, IPCAPropertyBagHandle propertyBagHandle);
+        void AuthCompletionCallback(IPCAStatus completionStatus);
+        void OnUpdatedInfo();
+
+        IPCADeviceInfo* GetDeviceInfo();
+        IPCAPlatformInfo* GetPlatformInfo();
+        ResourceList GetResourceInfo();
+
+        bool SetTargetFloor(int floor);
+
+    public:
+        virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged;
+        event AuthFailure^ OnAuthFailure;
+
+        property Platform::String^ Name\r
+        {\r
+            Platform::String^ get();\r
+            void set(Platform::String^ value);\r
+        }
+
+        property Platform::String^ CurrentFloor\r
+        {\r
+            Platform::String^ get();\r
+            void set(Platform::String^ value);\r
+        }
+
+    private:
+        void OnPropertyChanged(Platform::String^ propertyName);
+        IPCAStatus OpenDevice();
+        void UpdateDeviceInfo();
+        void UpdatePlatformInfo();
+        void UpdateResourceInfo();
+
+    private:
+        Platform::WeakReference* m_thisWeakRef;
+        Windows::UI::Core::CoreDispatcher^ m_dispatcher;
+        bool m_initialized;
+        Platform::String^ m_deviceName;
+        Platform::String^ m_curFloor;
+        IPCAAppHandle m_ipcaAppHandle;
+        IPCAHandle m_observeHandle;
+        IPCAHandle m_requestAccessHandle;
+        std::string m_deviceId;
+        std::string m_hostAddress;
+        IPCADeviceHandle m_deviceHandle;      // from IPCAOpenDevice();
+        IPCADeviceInfo* m_deviceInfo;         // valid between IPCAOpenDevice() and IPCACloseDevice().
+        IPCAPlatformInfo* m_platformInfo;     // valid between IPCAOpenDevice() and IPCACloseDevice().
+        ResourceList m_resourceList;
+        std::mutex m_deviceMutex;
+        bool m_requestedAccess;
+        std::mutex m_requestAccessMutex;
+    };
+}
diff --git a/resource/IPCA/samples/ElevatorClientUWP/ElevatorClientUWP.sln b/resource/IPCA/samples/ElevatorClientUWP/ElevatorClientUWP.sln
new file mode 100644 (file)
index 0000000..86629fc
--- /dev/null
@@ -0,0 +1,39 @@
+Microsoft Visual Studio Solution File, Format Version 12.00\r
+# Visual Studio 14\r
+VisualStudioVersion = 14.0.25420.1\r
+MinimumVisualStudioVersion = 10.0.40219.1\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ElevatorClientUWP", "ElevatorClientUWP.vcxproj", "{D30AE502-76EE-4061-87A3-C7142283D03E}"\r
+EndProject\r
+Global\r
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
+               Debug|ARM = Debug|ARM\r
+               Debug|x64 = Debug|x64\r
+               Debug|x86 = Debug|x86\r
+               Release|ARM = Release|ARM\r
+               Release|x64 = Release|x64\r
+               Release|x86 = Release|x86\r
+       EndGlobalSection\r
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution\r
+               {D30AE502-76EE-4061-87A3-C7142283D03E}.Debug|ARM.ActiveCfg = Debug|ARM\r
+               {D30AE502-76EE-4061-87A3-C7142283D03E}.Debug|ARM.Build.0 = Debug|ARM\r
+               {D30AE502-76EE-4061-87A3-C7142283D03E}.Debug|ARM.Deploy.0 = Debug|ARM\r
+               {D30AE502-76EE-4061-87A3-C7142283D03E}.Debug|x64.ActiveCfg = Debug|x64\r
+               {D30AE502-76EE-4061-87A3-C7142283D03E}.Debug|x64.Build.0 = Debug|x64\r
+               {D30AE502-76EE-4061-87A3-C7142283D03E}.Debug|x64.Deploy.0 = Debug|x64\r
+               {D30AE502-76EE-4061-87A3-C7142283D03E}.Debug|x86.ActiveCfg = Debug|Win32\r
+               {D30AE502-76EE-4061-87A3-C7142283D03E}.Debug|x86.Build.0 = Debug|Win32\r
+               {D30AE502-76EE-4061-87A3-C7142283D03E}.Debug|x86.Deploy.0 = Debug|Win32\r
+               {D30AE502-76EE-4061-87A3-C7142283D03E}.Release|ARM.ActiveCfg = Release|ARM\r
+               {D30AE502-76EE-4061-87A3-C7142283D03E}.Release|ARM.Build.0 = Release|ARM\r
+               {D30AE502-76EE-4061-87A3-C7142283D03E}.Release|ARM.Deploy.0 = Release|ARM\r
+               {D30AE502-76EE-4061-87A3-C7142283D03E}.Release|x64.ActiveCfg = Release|x64\r
+               {D30AE502-76EE-4061-87A3-C7142283D03E}.Release|x64.Build.0 = Release|x64\r
+               {D30AE502-76EE-4061-87A3-C7142283D03E}.Release|x64.Deploy.0 = Release|x64\r
+               {D30AE502-76EE-4061-87A3-C7142283D03E}.Release|x86.ActiveCfg = Release|Win32\r
+               {D30AE502-76EE-4061-87A3-C7142283D03E}.Release|x86.Build.0 = Release|Win32\r
+               {D30AE502-76EE-4061-87A3-C7142283D03E}.Release|x86.Deploy.0 = Release|Win32\r
+       EndGlobalSection\r
+       GlobalSection(SolutionProperties) = preSolution\r
+               HideSolutionNode = FALSE\r
+       EndGlobalSection\r
+EndGlobal\r
diff --git a/resource/IPCA/samples/ElevatorClientUWP/ElevatorClientUWP.vcxproj b/resource/IPCA/samples/ElevatorClientUWP/ElevatorClientUWP.vcxproj
new file mode 100644 (file)
index 0000000..a4a3b43
--- /dev/null
@@ -0,0 +1,261 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
+  <PropertyGroup Label="Globals">\r
+    <ProjectGuid>{d30ae502-76ee-4061-87a3-c7142283d03e}</ProjectGuid>\r
+    <RootNamespace>ElevatorClientUWP</RootNamespace>\r
+    <DefaultLanguage>en-US</DefaultLanguage>\r
+    <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>\r
+    <AppContainerApplication>true</AppContainerApplication>\r
+    <ApplicationType>Windows Store</ApplicationType>\r
+    <WindowsTargetPlatformVersion>10.0.14393.0</WindowsTargetPlatformVersion>\r
+    <WindowsTargetPlatformMinVersion>10.0.10586.0</WindowsTargetPlatformMinVersion>\r
+    <ApplicationTypeRevision>10.0</ApplicationTypeRevision>\r
+    <PackageCertificateThumbprint>1D05309EEC0622608A78ABCCA0E20C7528C4C79E</PackageCertificateThumbprint>\r
+  </PropertyGroup>\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />\r
+  <ItemGroup Label="ProjectConfigurations">\r
+    <ProjectConfiguration Include="Debug|ARM">\r
+      <Configuration>Debug</Configuration>\r
+      <Platform>ARM</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Debug|Win32">\r
+      <Configuration>Debug</Configuration>\r
+      <Platform>Win32</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Debug|x64">\r
+      <Configuration>Debug</Configuration>\r
+      <Platform>x64</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Release|ARM">\r
+      <Configuration>Release</Configuration>\r
+      <Platform>ARM</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Release|Win32">\r
+      <Configuration>Release</Configuration>\r
+      <Platform>Win32</Platform>\r
+    </ProjectConfiguration>\r
+    <ProjectConfiguration Include="Release|x64">\r
+      <Configuration>Release</Configuration>\r
+      <Platform>x64</Platform>\r
+    </ProjectConfiguration>\r
+  </ItemGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">\r
+    <ConfigurationType>Application</ConfigurationType>\r
+    <UseDebugLibraries>true</UseDebugLibraries>\r
+    <PlatformToolset>v140</PlatformToolset>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">\r
+    <ConfigurationType>Application</ConfigurationType>\r
+    <UseDebugLibraries>true</UseDebugLibraries>\r
+    <PlatformToolset>v140</PlatformToolset>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">\r
+    <ConfigurationType>Application</ConfigurationType>\r
+    <UseDebugLibraries>true</UseDebugLibraries>\r
+    <PlatformToolset>v140</PlatformToolset>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">\r
+    <ConfigurationType>Application</ConfigurationType>\r
+    <UseDebugLibraries>false</UseDebugLibraries>\r
+    <WholeProgramOptimization>true</WholeProgramOptimization>\r
+    <PlatformToolset>v140</PlatformToolset>\r
+    <UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">\r
+    <ConfigurationType>Application</ConfigurationType>\r
+    <UseDebugLibraries>false</UseDebugLibraries>\r
+    <WholeProgramOptimization>true</WholeProgramOptimization>\r
+    <PlatformToolset>v140</PlatformToolset>\r
+    <UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">\r
+    <ConfigurationType>Application</ConfigurationType>\r
+    <UseDebugLibraries>false</UseDebugLibraries>\r
+    <WholeProgramOptimization>true</WholeProgramOptimization>\r
+    <PlatformToolset>v140</PlatformToolset>\r
+    <UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>\r
+  </PropertyGroup>\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />\r
+  <ImportGroup Label="ExtensionSettings">\r
+  </ImportGroup>\r
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">\r
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+  </ImportGroup>\r
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">\r
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+  </ImportGroup>\r
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">\r
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+  </ImportGroup>\r
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">\r
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+  </ImportGroup>\r
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">\r
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+  </ImportGroup>\r
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">\r
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+  </ImportGroup>\r
+  <PropertyGroup Condition="'$(Platform)'=='x64'">\r
+    <PackageCertificateKeyFile>ElevatorClientUWP_TemporaryKey.pfx</PackageCertificateKeyFile>\r
+    <AppxAutoIncrementPackageRevision>False</AppxAutoIncrementPackageRevision>\r
+    <AppxBundle>Always</AppxBundle>\r
+    <AppxBundlePlatforms>x64</AppxBundlePlatforms>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Platform)'=='Win32'">\r
+    <PackageCertificateKeyFile>ElevatorClientUWP_TemporaryKey.pfx</PackageCertificateKeyFile>\r
+    <AppxAutoIncrementPackageRevision>False</AppxAutoIncrementPackageRevision>\r
+    <AppxBundle>Always</AppxBundle>\r
+    <AppxBundlePlatforms>x86</AppxBundlePlatforms>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Platform)'=='ARM'">\r
+    <PackageCertificateKeyFile>ElevatorClientUWP_TemporaryKey.pfx</PackageCertificateKeyFile>\r
+    <AppxAutoIncrementPackageRevision>False</AppxAutoIncrementPackageRevision>\r
+    <AppxBundle>Always</AppxBundle>\r
+    <AppxBundlePlatforms>arm</AppxBundlePlatforms>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">\r
+    <IncludePath>$(ProjectDir)\..\..\inc;$(ProjectDir)\..\..\..\c_common;$(IncludePath)</IncludePath>\r
+    <OutDir>$(SolutionDir)Out\$(Configuration)\$(PlatformTarget)\$(MSBuildProjectName)\</OutDir>\r
+    <IntDir>$(PlatformTarget)\$(Configuration)\</IntDir>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">\r
+    <IncludePath>$(ProjectDir)\..\..\inc;$(ProjectDir)\..\..\..\c_common;$(IncludePath)</IncludePath>\r
+    <OutDir>$(SolutionDir)Out\$(Configuration)\$(PlatformTarget)\$(MSBuildProjectName)\</OutDir>\r
+    <IntDir>$(PlatformTarget)\$(Configuration)\</IntDir>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">\r
+    <IncludePath>$(ProjectDir)\..\..\inc;$(ProjectDir)\..\..\..\c_common;$(IncludePath)</IncludePath>\r
+    <OutDir>$(SolutionDir)Out\$(Configuration)\$(PlatformTarget)\$(MSBuildProjectName)\</OutDir>\r
+    <IntDir>$(PlatformTarget)\$(Configuration)\</IntDir>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">\r
+    <IncludePath>$(ProjectDir)\..\..\inc;$(ProjectDir)\..\..\..\c_common;$(IncludePath)</IncludePath>\r
+    <OutDir>$(SolutionDir)Out\$(Configuration)\$(PlatformTarget)\$(MSBuildProjectName)\</OutDir>\r
+    <IntDir>$(PlatformTarget)\$(Configuration)\</IntDir>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">\r
+    <IncludePath>$(ProjectDir)\..\..\inc;$(ProjectDir)\..\..\..\c_common;$(IncludePath)</IncludePath>\r
+    <OutDir>$(SolutionDir)Out\$(Configuration)\$(PlatformTarget)\$(MSBuildProjectName)\</OutDir>\r
+    <IntDir>$(PlatformTarget)\$(Configuration)\</IntDir>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">\r
+    <IncludePath>$(ProjectDir)\..\..\inc;$(ProjectDir)\..\..\..\c_common;$(IncludePath)</IncludePath>\r
+    <OutDir>$(SolutionDir)Out\$(Configuration)\$(PlatformTarget)\$(MSBuildProjectName)\</OutDir>\r
+    <IntDir>$(PlatformTarget)\$(Configuration)\</IntDir>\r
+  </PropertyGroup>\r
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">\r
+    <ClCompile>\r
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>\r
+      <DisableSpecificWarnings>4453;28204</DisableSpecificWarnings>\r
+    </ClCompile>\r
+    <Link>\r
+      <AdditionalDependencies>WindowsApp.lib;ipca.lib;%(AdditionalDependencies)</AdditionalDependencies>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">\r
+    <ClCompile>\r
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>\r
+      <DisableSpecificWarnings>4453;28204</DisableSpecificWarnings>\r
+    </ClCompile>\r
+    <Link>\r
+      <AdditionalDependencies>WindowsApp.lib;ipca.lib;%(AdditionalDependencies)</AdditionalDependencies>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">\r
+    <ClCompile>\r
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>\r
+      <DisableSpecificWarnings>4453;28204</DisableSpecificWarnings>\r
+    </ClCompile>\r
+    <Link>\r
+      <AdditionalDependencies>WindowsApp.lib;ipca.lib;%(AdditionalDependencies)</AdditionalDependencies>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">\r
+    <ClCompile>\r
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>\r
+      <DisableSpecificWarnings>4453;28204</DisableSpecificWarnings>\r
+    </ClCompile>\r
+    <Link>\r
+      <AdditionalDependencies>WindowsApp.lib;ipca.lib;%(AdditionalDependencies)</AdditionalDependencies>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">\r
+    <ClCompile>\r
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>\r
+      <DisableSpecificWarnings>4453;28204</DisableSpecificWarnings>\r
+    </ClCompile>\r
+    <Link>\r
+      <AdditionalDependencies>WindowsApp.lib;ipca.lib;%(AdditionalDependencies)</AdditionalDependencies>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">\r
+    <ClCompile>\r
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>\r
+      <DisableSpecificWarnings>4453;28204</DisableSpecificWarnings>\r
+    </ClCompile>\r
+    <Link>\r
+      <AdditionalDependencies>WindowsApp.lib;ipca.lib;%(AdditionalDependencies)</AdditionalDependencies>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+  <ItemGroup>\r
+    <ClInclude Include="ElevatorDiscovery.h" />\r
+    <ClInclude Include="ElevatorViewModel.h" />\r
+    <ClInclude Include="Elevator.h" />\r
+    <ClInclude Include="pch.h" />\r
+    <ClInclude Include="App.xaml.h">\r
+      <DependentUpon>App.xaml</DependentUpon>\r
+    </ClInclude>\r
+    <ClInclude Include="MainPage.xaml.h">\r
+      <DependentUpon>MainPage.xaml</DependentUpon>\r
+    </ClInclude>\r
+    <ClInclude Include="Util.h" />\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <ApplicationDefinition Include="App.xaml">\r
+      <SubType>Designer</SubType>\r
+    </ApplicationDefinition>\r
+    <Page Include="MainPage.xaml">\r
+      <SubType>Designer</SubType>\r
+    </Page>\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <AppxManifest Include="Package.appxmanifest">\r
+      <SubType>Designer</SubType>\r
+    </AppxManifest>\r
+    <None Include="ElevatorClientUWP_TemporaryKey.pfx" />\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <Image Include="Assets\LockScreenLogo.scale-200.png" />\r
+    <Image Include="Assets\SplashScreen.scale-200.png" />\r
+    <Image Include="Assets\Square150x150Logo.scale-200.png" />\r
+    <Image Include="Assets\Square44x44Logo.scale-200.png" />\r
+    <Image Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />\r
+    <Image Include="Assets\StoreLogo.png" />\r
+    <Image Include="Assets\Wide310x150Logo.scale-200.png" />\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <ClCompile Include="App.xaml.cpp">\r
+      <DependentUpon>App.xaml</DependentUpon>\r
+    </ClCompile>\r
+    <ClCompile Include="ElevatorDiscovery.cpp" />\r
+    <ClCompile Include="ElevatorViewModel.cpp" />\r
+    <ClCompile Include="MainPage.xaml.cpp">\r
+      <DependentUpon>MainPage.xaml</DependentUpon>\r
+    </ClCompile>\r
+    <ClCompile Include="Elevator.cpp" />\r
+    <ClCompile Include="pch.cpp">\r
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>\r
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>\r
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">Create</PrecompiledHeader>\r
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">Create</PrecompiledHeader>\r
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>\r
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>\r
+    </ClCompile>\r
+    <ClCompile Include="Util.cpp" />\r
+  </ItemGroup>\r
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r
+  <ImportGroup Label="ExtensionTargets">\r
+  </ImportGroup>\r
+  <Import Project="$(ProjectDir)IPCALibs.targets" />\r
+</Project>
\ No newline at end of file
diff --git a/resource/IPCA/samples/ElevatorClientUWP/ElevatorClientUWP.vcxproj.filters b/resource/IPCA/samples/ElevatorClientUWP/ElevatorClientUWP.vcxproj.filters
new file mode 100644 (file)
index 0000000..fb55832
--- /dev/null
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
+  <ItemGroup>\r
+    <Filter Include="Common">\r
+      <UniqueIdentifier>d30ae502-76ee-4061-87a3-c7142283d03e</UniqueIdentifier>\r
+    </Filter>\r
+    <Filter Include="Assets">\r
+      <UniqueIdentifier>12bbc0a4-0b14-4fc7-bc95-3eef4bdbe84e</UniqueIdentifier>\r
+      <Extensions>bmp;fbx;gif;jpg;jpeg;tga;tiff;tif;png</Extensions>\r
+    </Filter>\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <ApplicationDefinition Include="App.xaml" />\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <ClCompile Include="App.xaml.cpp" />\r
+    <ClCompile Include="MainPage.xaml.cpp" />\r
+    <ClCompile Include="pch.cpp" />\r
+    <ClCompile Include="ElevatorViewModel.cpp" />\r
+    <ClCompile Include="ElevatorDiscovery.cpp" />\r
+    <ClCompile Include="Elevator.cpp" />\r
+    <ClCompile Include="Util.cpp" />\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <ClInclude Include="pch.h" />\r
+    <ClInclude Include="App.xaml.h" />\r
+    <ClInclude Include="MainPage.xaml.h" />\r
+    <ClInclude Include="ElevatorViewModel.h" />\r
+    <ClInclude Include="ElevatorDiscovery.h" />\r
+    <ClInclude Include="Elevator.h" />\r
+    <ClInclude Include="Util.h" />\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <Image Include="Assets\LockScreenLogo.scale-200.png">\r
+      <Filter>Assets</Filter>\r
+    </Image>\r
+    <Image Include="Assets\SplashScreen.scale-200.png">\r
+      <Filter>Assets</Filter>\r
+    </Image>\r
+    <Image Include="Assets\Square150x150Logo.scale-200.png">\r
+      <Filter>Assets</Filter>\r
+    </Image>\r
+    <Image Include="Assets\Square44x44Logo.scale-200.png">\r
+      <Filter>Assets</Filter>\r
+    </Image>\r
+    <Image Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png">\r
+      <Filter>Assets</Filter>\r
+    </Image>\r
+    <Image Include="Assets\StoreLogo.png">\r
+      <Filter>Assets</Filter>\r
+    </Image>\r
+    <Image Include="Assets\Wide310x150Logo.scale-200.png">\r
+      <Filter>Assets</Filter>\r
+    </Image>\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <AppxManifest Include="Package.appxmanifest" />\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <None Include="ElevatorClientUWP_TemporaryKey.pfx" />\r
+    <None Include="@(NativeLibs)" />\r
+    <None Include="@(NativeLibs)" />\r
+    <None Include="@(NativeLibs)" />\r
+    <None Include="@(NativeLibs)" />\r
+    <None Include="@(NativeLibs)" />\r
+    <None Include="@(NativeLibs)" />\r
+    <None Include="@(NativeLibs)" />\r
+    <None Include="@(NativeLibs)" />\r
+    <None Include="@(NativeLibs)" />\r
+    <None Include="@(NativeLibs)" />\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <Page Include="MainPage.xaml" />\r
+  </ItemGroup>\r
+</Project>
\ No newline at end of file
diff --git a/resource/IPCA/samples/ElevatorClientUWP/ElevatorClientUWP_TemporaryKey.pfx b/resource/IPCA/samples/ElevatorClientUWP/ElevatorClientUWP_TemporaryKey.pfx
new file mode 100644 (file)
index 0000000..2a6baaa
Binary files /dev/null and b/resource/IPCA/samples/ElevatorClientUWP/ElevatorClientUWP_TemporaryKey.pfx differ
diff --git a/resource/IPCA/samples/ElevatorClientUWP/ElevatorDiscovery.cpp b/resource/IPCA/samples/ElevatorClientUWP/ElevatorDiscovery.cpp
new file mode 100644 (file)
index 0000000..8577bb3
--- /dev/null
@@ -0,0 +1,395 @@
+/* *****************************************************************
+*
+* Copyright 2017 Microsoft
+*
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+******************************************************************/\r
+\r
+#include "pch.h"\r
+#include "ElevatorDiscovery.h"\r
+\r
+#include "iotivity_config.h"
+
+#include <mutex>
+#include <array>
+#include <string>
+#include <vector>
+#include <iostream>
+#include <map>
+#include <thread>
+#include <chrono>
+#include <condition_variable>
+#include <climits>
+#include <sstream>
+
+#include "ipca.h"
+#include "Elevator.h"\r
+#include "ElevatorViewModel.h"\r
+\r
+using namespace ElevatorClientUWP;\r
+\r
+using namespace concurrency;\r
+\r
+using namespace std;\r
+using namespace Platform;\r
+using namespace Platform::Collections;\r
+using namespace Windows::ApplicationModel::Core;\r
+using namespace Windows::Foundation;\r
+using namespace Windows::Foundation::Collections;\r
+using namespace Windows::UI::Core;\r
+using namespace Windows::UI::Xaml;\r
+using namespace Windows::UI::Xaml::Controls;\r
+using namespace Windows::UI::Xaml::Controls::Primitives;\r
+using namespace Windows::UI::Xaml::Data;\r
+using namespace Windows::UI::Xaml::Input;\r
+using namespace Windows::UI::Xaml::Media;\r
+using namespace Windows::UI::Xaml::Navigation;\r
+\r
+#define TIMEOUT_MS  3000\r
+\r
+String^ ElevatorDiscovery::guidStr = "{ab2593e4-2994-401e-a526-c92fd9b71414}";\r
+\r
+static void IPCA_CALL DiscoverDevicesCallback(
+    void* context,
+    IPCADeviceStatus deviceStatus,
+    const IPCADiscoveredDeviceInfo* deviceInfo)\r
+{\r
+    if (context)\r
+    {\r
+        ElevatorDiscovery^ elvDisc = \r
+            (reinterpret_cast<WeakReference*>(context))->Resolve<ElevatorDiscovery>();\r
+        if (elvDisc)\r
+        {\r
+            elvDisc->DiscoverDevicesCallback(deviceStatus, deviceInfo);\r
+        }\r
+    }\r
+}\r
+\r
+static IPCAStatus IPCA_CALL PwdInputCallback(
+    void* context,
+    const IPCADeviceInfo* deviceInformation,
+    const IPCAPlatformInfo* platformInformation,
+    IPCAOwnershipTransferType type,
+    char* passwordBuffer,
+    size_t passwordBufferSize)
+{
+    if (context)\r
+    {\r
+        ElevatorDiscovery^ elvDisc =\r
+            (reinterpret_cast<WeakReference*>(context))->Resolve<ElevatorDiscovery>();\r
+        if (elvDisc)\r
+        {\r
+            return elvDisc->PwdInputCallback(deviceInformation, platformInformation, type,\r
+                passwordBuffer, passwordBufferSize);\r
+        }\r
+    }
+
+    // If reached here means context is either null or wrong type
+    return IPCA_INVALID_ARGUMENT;
+}
+
+static IPCAStatus IPCA_CALL PwdDisplayCallback(
+    void* context,
+    const IPCADeviceInfo* deviceInformation,
+    const IPCAPlatformInfo* platformInformation,
+    IPCAOwnershipTransferType type,
+    const char* password)
+{
+    if (context)\r
+    {\r
+        ElevatorDiscovery^ elvDisc =\r
+            (reinterpret_cast<WeakReference*>(context))->Resolve<ElevatorDiscovery>();\r
+        if (elvDisc)\r
+        {\r
+            return elvDisc->PwdDisplayCallback(deviceInformation, platformInformation, type, password);\r
+        }\r
+    }
+
+    // If reached here means context is either null or wrong type
+    return IPCA_INVALID_ARGUMENT;
+}\r
+\r
+ElevatorDiscovery::ElevatorDiscovery() :\r
+    m_ipcaAppHandle(nullptr),\r
+    m_deviceHandle(nullptr),\r
+    m_discoverDeviceHandle(nullptr),\r
+    m_deviceDiscoveryHandle(nullptr),\r
+    m_targetElevatorDiscovered(false),\r
+    m_currentFloor(0)\r
+{\r
+    m_thisWeakRef = new WeakReference(this);\r
+    m_dispatcher = CoreApplication::MainView->Dispatcher;\r
+\r
+    GUID guid;\r
+    HRESULT hr = IIDFromString(guidStr->Data(), &guid);\r
+    if (FAILED(hr))\r
+    {\r
+        if (hr == E_INVALIDARG)\r
+        {\r
+            throw ref new InvalidArgumentException("IIDFromString invalid arg");\r
+        }\r
+        else if (hr == E_OUTOFMEMORY)\r
+        {\r
+            throw ref new OutOfMemoryException("IIDFromString out of memory");\r
+        }\r
+        else\r
+        {\r
+            throw ref new FailureException("IIDFromString failed");\r
+        }\r
+    }\r
+\r
+    IPCAUuid appId;
+    byte* guidBytes = reinterpret_cast<byte*>(&guid);\r
+    for (int i = 0; i < 16; i++)\r
+    {\r
+        appId.uuid[i] = guidBytes[i];\r
+    }
+
+    IPCAAppInfo ipcaAppInfo = { appId, "ElevatorClientUWP", "1.0.0", "Microsoft" };
+
+    IPCAStatus status = IPCAOpen(&ipcaAppInfo, IPCA_VERSION_1, &m_ipcaAppHandle);
+    if (status != IPCA_OK)
+    {
+        String^ err = "IPCAOpen() Failed. Status: " + static_cast<int>(status).ToString();
+        throw ref new FailureException(err);
+    }\r
+\r
+    status = IPCASetPasswordCallbacks(m_ipcaAppHandle, &::PwdInputCallback,
+        &::PwdDisplayCallback, m_thisWeakRef);
+    if (status != IPCA_OK)
+    {
+        String^ err = "IPCASetPasswordCallbacks() Failed. Status: " + static_cast<int>(status).ToString();
+        throw ref new FailureException(err);
+    }\r
+\r
+    m_ElevatorList = ref new Map<String^, Elevator^>();\r
+\r
+    // Generate Password Input Content Dialog\r
+    TextBlock^ tbPwdInput = ref new TextBlock();\r
+    tbPwdInput->Text = "Please enter Elevator Password:";\r
+    tbPwdInput->Margin = Thickness(0, 5, 0, 0);\r
+\r
+    m_pbPwdInput = ref new PasswordBox();\r
+    m_pbPwdInput->PasswordRevealMode = PasswordRevealMode::Peek;\r
+    m_pbPwdInput->Margin = Thickness(10);\r
+    m_pbPwdInput->MinWidth = 250;\r
+\r
+    StackPanel^ spPwdInput = ref new StackPanel();\r
+    spPwdInput->Orientation = Orientation::Vertical;\r
+    spPwdInput->Children->Append(tbPwdInput);\r
+    spPwdInput->Children->Append(m_pbPwdInput);\r
+\r
+    m_passInputDiag = ref new ContentDialog();\r
+    m_passInputDiag->Title = "Authentication Needed";\r
+    m_passInputDiag->Content = spPwdInput;\r
+    m_passInputDiag->PrimaryButtonText = "Authenticate";\r
+    m_passInputDiag->SecondaryButtonText = "Cancel";\r
+\r
+    // Generate Password Display Content Dialog\r
+    TextBlock^ tbPwdDisplay = ref new TextBlock();\r
+    tbPwdDisplay->Text = "Elevator Password is:";\r
+    tbPwdDisplay->Margin = Thickness(0, 5, 0, 0);\r
+\r
+    m_txtPwdDisplay = ref new TextBox();\r
+    m_txtPwdDisplay->IsReadOnly = true;\r
+    m_txtPwdDisplay->Margin = 10;\r
+    m_txtPwdDisplay->BorderThickness = 0;\r
+\r
+    StackPanel^ spPwdDisplay = ref new StackPanel();\r
+    spPwdDisplay->Orientation = Orientation::Vertical;\r
+    spPwdDisplay->Children->Append(tbPwdDisplay);\r
+    spPwdDisplay->Children->Append(m_txtPwdDisplay);\r
+\r
+    m_passDisplayDiag = ref new ContentDialog();\r
+    m_passDisplayDiag->Title = "Authentication Info";\r
+    m_passDisplayDiag->Content = spPwdDisplay;\r
+    m_passDisplayDiag->PrimaryButtonText = "OK";\r
+}\r
+\r
+ElevatorDiscovery::~ElevatorDiscovery()\r
+{\r
+    StopElevatorDiscovery();
+
+    std::lock_guard<std::recursive_mutex> lock(m_ElevatorListMutex);
+    m_ElevatorList->Clear();
+
+    if (m_ipcaAppHandle != nullptr)
+    {
+        IPCAClose(m_ipcaAppHandle);
+    }\r
+\r
+    delete m_thisWeakRef;\r
+}\r
+\r
+bool ElevatorDiscovery::StartElevatorDiscovery()\r
+{\r
+    std::unique_lock<std::mutex> lock(m_deviceDiscoveryMutex);
+
+    if (!m_deviceDiscoveryHandle)
+    {
+        // Start the discovery if not already started
+
+        const char* resourceTypes[] = {
+            "x.org.iotivity.sample.elevator",
+            "x.org.iotivity.sample.elevator2",
+            "x.org.iotivity.sample.elevator3",
+            "x.org.iotivity.sample.elevator4",
+        };
+
+        const int NUMBER_OF_RESOURCE_TYPES = sizeof(resourceTypes) / sizeof(char*);
+
+        IPCAStatus status = IPCADiscoverDevices(
+            m_ipcaAppHandle,
+            &::DiscoverDevicesCallback,
+            m_thisWeakRef,
+            resourceTypes,
+            NUMBER_OF_RESOURCE_TYPES,
+            &m_deviceDiscoveryHandle);
+
+        if (status != IPCA_OK)
+        {
+            return false;
+        }
+    }\r
+\r
+    return true;\r
+}\r
+\r
+void ElevatorDiscovery::StopElevatorDiscovery()\r
+{\r
+    std::unique_lock<std::mutex> lock(m_deviceDiscoveryMutex);
+    // Stop discovery.
+    if (m_deviceDiscoveryHandle)
+    {
+        IPCACloseHandle(m_deviceDiscoveryHandle, nullptr, nullptr);
+        m_deviceDiscoveryHandle = nullptr;
+    }\r
+}\r
+\r
+void ElevatorDiscovery::DiscoverDevicesCallback(IPCADeviceStatus deviceStatus,\r
+    const IPCADiscoveredDeviceInfo* deviceInfo)\r
+{\r
+    std::lock_guard<std::recursive_mutex> lock(m_ElevatorListMutex);
+
+    std::string deviceIdStr = deviceInfo->deviceId;
+    String^ deviceId = Util::ConvertStrtoPlatformStr(deviceInfo->deviceId);
+    String^ deviceName = Util::ConvertStrtoPlatformStr(deviceInfo->deviceName);
+    std::vector<std::string> deviceUris;
+    for (size_t i = 0; i < deviceInfo->deviceUriCount; i++)
+    {
+        deviceUris.push_back(deviceInfo->deviceUris[i]);
+    }
+
+    Elevator^ elevator = nullptr;
+
+    try
+    {
+        elevator = m_ElevatorList->Lookup(deviceId);
+    }
+    catch (OutOfBoundsException^ e)
+    {
+        if (deviceStatus != IPCA_DEVICE_DISCOVERED)
+        {
+            // Log Unexpected discovery status
+        }
+        // Device not in the Map
+        elevator = ref new Elevator();
+        elevator->Init(m_ipcaAppHandle, deviceName, deviceIdStr);
+        m_ElevatorList->Insert(deviceId, elevator);
+    }
+
+    switch (deviceStatus)
+    {
+    case IPCA_DEVICE_DISCOVERED:
+        OnElevatorDiscovered(elevator);
+        break;
+    case IPCA_DEVICE_UPDATED_INFO:
+        elevator->OnUpdatedInfo();
+        break;
+    case IPCA_DEVICE_STOPPED_RESPONDING:
+        OnElevatorDisconnected(elevator);
+        m_ElevatorList->Remove(deviceId);
+        break;
+    default:
+        break;
+    }\r
+}\r
+\r
+IPCAStatus ElevatorDiscovery::PwdInputCallback(\r
+    const IPCADeviceInfo* deviceInformation,\r
+    const IPCAPlatformInfo* platformInformation,\r
+    IPCAOwnershipTransferType type,\r
+    char* passwordBuffer,\r
+    size_t passwordBufferSize)\r
+{\r
+    IAsyncOperation<ContentDialogResult>^ showPwdDiagAsync;\r
+    create_task(m_dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler(\r
+        [this, &showPwdDiagAsync]\r
+        {\r
+            m_pbPwdInput->Password = nullptr;\r
+            showPwdDiagAsync = m_passInputDiag->ShowAsync();\r
+        }))\r
+    ).get();\r
+\r
+    ContentDialogResult result = create_task(showPwdDiagAsync).get();\r
+    // Get the password\r
+    if (result == ContentDialogResult::Primary)\r
+    {\r
+        String^ pwd = nullptr;\r
+        create_task(m_dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler(\r
+            [this, &pwd]\r
+            {\r
+                pwd = m_pbPwdInput->Password;\r
+            }))\r
+        ).get();\r
+\r
+        std::string pwdStr = Util::ConvertWStrtoStr(pwd->Data());\r
+        if (pwdStr.empty() || ((pwdStr.length() + 1) > passwordBufferSize)) // +1 for null char\r
+        {\r
+            return IPCA_INVALID_ARGUMENT;\r
+        }\r
+\r
+        strcpy_s(passwordBuffer, passwordBufferSize, pwdStr.c_str());\r
+    }\r
+    else\r
+    {\r
+        return IPCA_INVALID_ARGUMENT;\r
+    }\r
+\r
+    return IPCA_OK;\r
+}\r
+\r
+IPCAStatus ElevatorDiscovery::PwdDisplayCallback(\r
+    const IPCADeviceInfo* deviceInformation,\r
+    const IPCAPlatformInfo* platformInformation,\r
+    IPCAOwnershipTransferType type,\r
+    const char* password)\r
+{\r
+    String^ pwd = Util::ConvertStrtoPlatformStr(password);\r
+    if (pwd == nullptr)\r
+    {\r
+        return IPCA_INVALID_ARGUMENT;\r
+    }\r
+\r
+    m_dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler(\r
+        [this, pwd]\r
+        {\r
+            m_txtPwdDisplay->Text = pwd;\r
+            m_passDisplayDiag->ShowAsync();\r
+        }\r
+    ));\r
+\r
+    return IPCA_OK;\r
+}\r
diff --git a/resource/IPCA/samples/ElevatorClientUWP/ElevatorDiscovery.h b/resource/IPCA/samples/ElevatorClientUWP/ElevatorDiscovery.h
new file mode 100644 (file)
index 0000000..9fd631a
--- /dev/null
@@ -0,0 +1,98 @@
+/* *****************************************************************
+*
+* Copyright 2017 Microsoft
+*
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+******************************************************************/\r
+\r
+#pragma once\r
+\r
+#include "ipca.h"\r
+#include "Elevator.h"\r
+\r
+namespace ElevatorClientUWP\r
+{\r
+    public delegate void ElevatorEvent(Elevator^ elevator);\r
+\r
+    public ref class ElevatorDiscovery sealed\r
+    {\r
+    public:\r
+        ElevatorDiscovery();\r
+        virtual ~ElevatorDiscovery();\r
+\r
+        bool StartElevatorDiscovery();\r
+        void StopElevatorDiscovery();\r
+\r
+    public:\r
+        event ElevatorEvent^ OnElevatorDiscovered;\r
+        event ElevatorEvent^ OnElevatorDisconnected;\r
+\r
+    internal:\r
+        void DiscoverDevicesCallback(
+            IPCADeviceStatus deviceStatus,
+            const IPCADiscoveredDeviceInfo* deviceInfo);\r
+\r
+        IPCAStatus PwdInputCallback(
+            const IPCADeviceInfo* deviceInformation,
+            const IPCAPlatformInfo* platformInformation,
+            IPCAOwnershipTransferType type,
+            char* passwordBuffer,
+            size_t passwordBufferSize);\r
+\r
+        IPCAStatus PwdDisplayCallback(
+            const IPCADeviceInfo* deviceInformation,
+            const IPCAPlatformInfo* platformInformation,
+            IPCAOwnershipTransferType type,
+            const char* password);\r
+\r
+    private:\r
+        static Platform::String^ guidStr;\r
+        \r
+        Platform::WeakReference* m_thisWeakRef;\r
+        Windows::UI::Core::CoreDispatcher^ m_dispatcher;\r
+\r
+        std::string m_targetDeviceId;\r
+\r
+        IPCAAppHandle m_ipcaAppHandle;\r
+        IPCADeviceHandle m_deviceHandle;\r
+        IPCAHandle m_discoverDeviceHandle;\r
+        IPCAHandle m_deviceDiscoveryHandle;\r
+\r
+        bool m_targetElevatorDiscovered;\r
+\r
+        Windows::UI::Xaml::Controls::PasswordBox^ m_pbPwdInput;\r
+        Windows::UI::Xaml::Controls::TextBox^ m_txtPwdDisplay;\r
+        Windows::UI::Xaml::Controls::ContentDialog^ m_passInputDiag;\r
+        Windows::UI::Xaml::Controls::ContentDialog^ m_passDisplayDiag;\r
+\r
+        // Key is device id.  Value is pointer to Elevator.
+        Windows::Foundation::Collections::IMap<Platform::String^, Elevator^>^ m_ElevatorList;
+
+        // Sync access to m_ElevatorList.
+        std::recursive_mutex m_ElevatorListMutex;
+        // Discovery mutex and cond var
+        std::mutex m_deviceDiscoveredCbMutex;
+        std::condition_variable m_deviceDiscoveredCV;\r
+        std::mutex m_deviceDiscoveryMutex;\r
+        std::condition_variable m_deviceDiscoveryCV;\r
+        // Device mutex\r
+        std::mutex m_deviceMutex;\r
+        // Get properties mutex and cond var\r
+        std::mutex m_getPropsMutex;\r
+        std::condition_variable m_getPropsCV;\r
+\r
+        int m_currentFloor;\r
+    };\r
+}
\ No newline at end of file
diff --git a/resource/IPCA/samples/ElevatorClientUWP/ElevatorViewModel.cpp b/resource/IPCA/samples/ElevatorClientUWP/ElevatorViewModel.cpp
new file mode 100644 (file)
index 0000000..a81a1b3
--- /dev/null
@@ -0,0 +1,172 @@
+/* *****************************************************************
+*
+* Copyright 2017 Microsoft
+*
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+******************************************************************/\r
+\r
+#include "pch.h"\r
+#include "ElevatorViewModel.h"\r
+#include "ElevatorDiscovery.h"\r
+\r
+using namespace ElevatorClientUWP;\r
+\r
+using namespace concurrency;\r
+\r
+using namespace Platform;\r
+using namespace Platform::Collections;\r
+using namespace Windows::ApplicationModel::Core;\r
+using namespace Windows::Foundation;\r
+using namespace Windows::Foundation::Collections;\r
+using namespace Windows::UI::Core;\r
+using namespace Windows::UI::Xaml;\r
+using namespace Windows::UI::Xaml::Controls;\r
+using namespace Windows::UI::Xaml::Controls::Primitives;\r
+using namespace Windows::UI::Xaml::Data;\r
+using namespace Windows::UI::Xaml::Input;\r
+using namespace Windows::UI::Xaml::Media;\r
+using namespace Windows::UI::Xaml::Navigation;\r
+\r
+ElevatorViewModel::ElevatorViewModel() : m_dispatcher(nullptr), m_elevatorDisc(nullptr)\r
+{\r
+    m_dispatcher = CoreApplication::MainView->Dispatcher;\r
+\r
+    m_deviceList = ref new Vector<Elevator^>();\r
+\r
+    try\r
+    {\r
+        ElevatorDiscovery^ elvDisc = ref new ElevatorDiscovery();\r
+        elvDisc->OnElevatorDiscovered += ref new ElevatorEvent(this, &ElevatorViewModel::ElevatorDiscovered);\r
+        elvDisc->OnElevatorDisconnected += ref new ElevatorEvent(this, &ElevatorViewModel::ElevatorDisconnected);\r
+        m_elevatorDisc = elvDisc;\r
+    }\r
+    catch (Exception^ e)\r
+    {\r
+        Util::ShowErrorMsg(nullptr, e->Message);\r
+    }\r
+}\r
+\r
+ElevatorViewModel::~ElevatorViewModel()\r
+{\r
+    if (m_elevatorDisc)\r
+    {\r
+        m_elevatorDisc->StopElevatorDiscovery();\r
+    }\r
+}\r
+\r
+void ElevatorViewModel::DiscoverElevators()\r
+{\r
+    if (!m_elevatorDisc->StartElevatorDiscovery())\r
+    {\r
+        Util::ShowErrorMsg(nullptr, "Couldn't Start Elevator Discovery");\r
+    }\r
+}\r
+\r
+void ElevatorViewModel::ElevatorDiscovered(Elevator^ elevator)\r
+{\r
+    elevator->OnAuthFailure += ref new ElevatorClientUWP::AuthFailure(this,\r
+        &ElevatorViewModel::AuthFailure);\r
+\r
+    m_dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler(\r
+        [this, elevator]\r
+        {\r
+            unsigned int index;\r
+            if (!m_deviceList->IndexOf(elevator, &index))\r
+            {\r
+                m_deviceList->Append(elevator);\r
+            }\r
+        }\r
+    ));\r
+}\r
+\r
+void ElevatorViewModel::ElevatorDisconnected(Elevator^ elevator)\r
+{\r
+    m_dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler(\r
+        [this, elevator]\r
+        {\r
+            unsigned int index;\r
+            if (m_deviceList->IndexOf(elevator, &index))\r
+            {\r
+                m_deviceList->RemoveAt(index);\r
+            }\r
+        }\r
+    ));\r
+}\r
+\r
+void ElevatorViewModel::AuthFailure()\r
+{\r
+    m_dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([this]\r
+    {\r
+        SelectedDevice = nullptr;\r
+    }));\r
+}\r
+\r
+void ElevatorViewModel::SetFloor(int floor)\r
+{\r
+    if (m_selectedDevice != nullptr)\r
+    {\r
+        if (!m_selectedDevice->SetTargetFloor(floor))\r
+        {\r
+            Util::ShowErrorMsg(nullptr, "Failed to set the target floor");\r
+        }\r
+    }\r
+}\r
+\r
+IVector<Elevator^>^ ElevatorViewModel::DeviceList::get()\r
+{\r
+    return m_deviceList;\r
+}\r
+\r
+Elevator^ ElevatorViewModel::SelectedDevice::get()\r
+{\r
+    return m_selectedDevice;\r
+}\r
+\r
+void ElevatorViewModel::SelectedDevice::set(Elevator^ value)\r
+{\r
+    if (m_selectedDevice != value)\r
+    {\r
+        // Stop Observation for the previously selected device\r
+        if (m_selectedDevice)\r
+        {\r
+            m_selectedDevice->StopObservation();\r
+        }\r
+\r
+        // Set the new device\r
+        m_selectedDevice = value;\r
+        OnPropertyChanged("SelectedDevice");\r
+\r
+        // Start Observation for the newly selected device\r
+        if (m_selectedDevice)\r
+        {\r
+            try\r
+            {\r
+                m_selectedDevice->StartObservation();\r
+            }\r
+            catch (Exception^ e)\r
+            {\r
+                Util::ShowErrorMsg(nullptr, e->Message);\r
+            }\r
+        }\r
+    }\r
+}\r
+\r
+void ElevatorViewModel::OnPropertyChanged(String^ propertyName)\r
+{\r
+    m_dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([this, propertyName]\r
+    {\r
+        PropertyChanged(this, ref new PropertyChangedEventArgs(propertyName));\r
+    }));\r
+}\r
diff --git a/resource/IPCA/samples/ElevatorClientUWP/ElevatorViewModel.h b/resource/IPCA/samples/ElevatorClientUWP/ElevatorViewModel.h
new file mode 100644 (file)
index 0000000..b7b0e63
--- /dev/null
@@ -0,0 +1,64 @@
+/* *****************************************************************
+*
+* Copyright 2017 Microsoft
+*
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+******************************************************************/\r
+\r
+#pragma once\r
+\r
+#include "Elevator.h"\r
+\r
+namespace ElevatorClientUWP\r
+{\r
+    ref class ElevatorDiscovery;\r
+\r
+    [Windows::UI::Xaml::Data::Bindable]\r
+    public ref class ElevatorViewModel sealed : Windows::UI::Xaml::Data::INotifyPropertyChanged\r
+    {\r
+    public:\r
+        ElevatorViewModel();\r
+        virtual ~ElevatorViewModel();\r
+        void DiscoverElevators();\r
+        void SetFloor(int floor);\r
+\r
+    public:\r
+        virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged;\r
+\r
+        property Windows::Foundation::Collections::IVector<Elevator^>^ DeviceList\r
+        {\r
+            Windows::Foundation::Collections::IVector<Elevator^>^ get();\r
+        }\r
+\r
+        property Elevator^ SelectedDevice\r
+        {\r
+            Elevator^ get();\r
+            void set(Elevator^ value);\r
+        }\r
+\r
+    private:\r
+        void OnPropertyChanged(Platform::String^ propertyName);\r
+        void ElevatorDiscovered(Elevator^ elevator);\r
+        void ElevatorDisconnected(Elevator ^elevator);\r
+        void AuthFailure();\r
+\r
+    private:\r
+        Windows::UI::Core::CoreDispatcher^ m_dispatcher;\r
+        ElevatorDiscovery^ m_elevatorDisc;\r
+        Platform::String^ m_targetDeviceId;\r
+        Platform::Collections::Vector<Elevator^>^ m_deviceList;\r
+        Elevator^ m_selectedDevice;\r
+    };\r
+}\r
diff --git a/resource/IPCA/samples/ElevatorClientUWP/IPCALibs.targets b/resource/IPCA/samples/ElevatorClientUWP/IPCALibs.targets
new file mode 100644 (file)
index 0000000..1b439ba
--- /dev/null
@@ -0,0 +1,21 @@
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
+  <PropertyGroup Condition="'$(Platform)'=='x64'">\r
+    <IoTivityOutDir>$(ProjectDir)..\..\..\..\out\windows\uwp\amd64\$(Configuration)</IoTivityOutDir>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition="'$(Platform)'!='x64'">\r
+    <IoTivityOutDir>$(ProjectDir)..\..\..\..\out\windows\uwp\$(PlatformTarget)\$(Configuration)</IoTivityOutDir>\r
+  </PropertyGroup>\r
+  <ItemGroup>\r
+    <NativeLibs Include="$(IoTivityOutDir)\ipca.dll;$(IoTivityOutDir)\octbstack.dll" />\r
+    <None Include="@(NativeLibs)">\r
+      <Link>%(RecursiveDir)%(FileName)%(Extension)</Link>\r
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>\r
+    </None>\r
+    <ReferenceCopyLocalPaths Include="@(NativeLibs)" />\r
+  </ItemGroup>\r
+  <ItemDefinitionGroup>\r
+    <Link>\r
+      <AdditionalLibraryDirectories>$(IoTivityOutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\r
+    </Link>\r
+  </ItemDefinitionGroup>\r
+</Project>\r
diff --git a/resource/IPCA/samples/ElevatorClientUWP/MainPage.xaml b/resource/IPCA/samples/ElevatorClientUWP/MainPage.xaml
new file mode 100644 (file)
index 0000000..2bd594e
--- /dev/null
@@ -0,0 +1,38 @@
+<Page\r
+    x:Class="ElevatorClientUWP.MainPage"\r
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"\r
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"\r
+    xmlns:local="using:ElevatorClientUWP"\r
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"\r
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"\r
+    mc:Ignorable="d">\r
+\r
+    <Page.Resources>\r
+        <Thickness x:Key="DefaultMargin">10</Thickness>\r
+    </Page.Resources>\r
+\r
+    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">\r
+        <StackPanel Orientation="Vertical">\r
+            <StackPanel Orientation="Vertical" Margin="{StaticResource DefaultMargin}">\r
+                <TextBlock HorizontalAlignment="Left" Margin="10" TextWrapping="Wrap" Text="Elevators:" VerticalAlignment="Top"/>\r
+                <ComboBox Margin="10,0,10,10" MinWidth="320" ItemsSource="{Binding DeviceList}" SelectedItem="{Binding SelectedDevice, Mode=TwoWay}" DisplayMemberPath="Name"\r
+                          SelectionChanged="Elevators_SelectionChanged" />\r
+            </StackPanel>\r
+            <StackPanel Orientation="Horizontal">\r
+                <TextBlock x:Name="curFloorTBlock" HorizontalAlignment="Left" TextWrapping="Wrap"\r
+                           Text="Current Floor:" VerticalAlignment="Top" Margin="{StaticResource DefaultMargin}"/>\r
+                <TextBlock Text="{Binding SelectedDevice.CurrentFloor}" Margin="{StaticResource DefaultMargin}"\r
+                           VerticalAlignment="Center"/>\r
+            </StackPanel>\r
+            <StackPanel Orientation="Horizontal">\r
+                <TextBlock x:Name="setFloorTBlock" HorizontalAlignment="Left" TextWrapping="Wrap"\r
+                           Text="Set Floor:" VerticalAlignment="Top" Margin="{StaticResource DefaultMargin}"/>\r
+                <TextBox x:Name="setFloorText" HorizontalAlignment="Left" TextWrapping="Wrap"\r
+                         VerticalAlignment="Top" Margin="{StaticResource DefaultMargin}"/>\r
+                <Button x:Name="setFloorBtn" Content="Set Floor" HorizontalAlignment="Left"\r
+                        VerticalAlignment="Top" Margin="{StaticResource DefaultMargin}"\r
+                        Click="setFloorBtn_Click"/>\r
+            </StackPanel>\r
+        </StackPanel>\r
+    </Grid>\r
+</Page>\r
diff --git a/resource/IPCA/samples/ElevatorClientUWP/MainPage.xaml.cpp b/resource/IPCA/samples/ElevatorClientUWP/MainPage.xaml.cpp
new file mode 100644 (file)
index 0000000..af10c3d
--- /dev/null
@@ -0,0 +1,72 @@
+/* *****************************************************************
+*
+* Copyright 2017 Microsoft
+*
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+******************************************************************/\r
+\r
+//\r
+// MainPage.xaml.cpp\r
+// Implementation of the MainPage class.\r
+//\r
+\r
+#include "pch.h"\r
+#include "MainPage.xaml.h"\r
+\r
+using namespace ElevatorClientUWP;\r
+using namespace concurrency;\r
+using namespace Platform;\r
+using namespace Windows::Foundation;\r
+using namespace Windows::Foundation::Collections;\r
+using namespace Windows::UI::Xaml;\r
+using namespace Windows::UI::Xaml::Controls;\r
+using namespace Windows::UI::Xaml::Controls::Primitives;\r
+using namespace Windows::UI::Xaml::Data;\r
+using namespace Windows::UI::Xaml::Input;\r
+using namespace Windows::UI::Xaml::Media;\r
+using namespace Windows::UI::Xaml::Navigation;\r
+using namespace Windows::UI::Core;\r
+\r
+MainPage::MainPage()\r
+{\r
+       InitializeComponent();\r
+    ViewModel = ref new ElevatorViewModel();\r
+    DataContext = ViewModel;\r
+    this->Loaded += ref new RoutedEventHandler(this, &MainPage::OnLoaded);\r
+}\r
+\r
+void MainPage::setFloorBtn_Click(Object^ sender, RoutedEventArgs^ e)\r
+{\r
+    int floor = 0;\r
+    try\r
+    {\r
+        floor = std::stoi(setFloorText->Text->Data());\r
+    }\r
+    catch (std::exception e)\r
+    {\r
+        Util::ShowErrorMsg(nullptr, "Invalid Floor Number");\r
+    }\r
+    ViewModel->SetFloor(floor);\r
+}\r
+\r
+void MainPage::Elevators_SelectionChanged(Object^ sender, SelectionChangedEventArgs^ e)\r
+{\r
+    setFloorText->Text = "";\r
+}\r
+\r
+void ElevatorClientUWP::MainPage::OnLoaded(Object ^sender, RoutedEventArgs ^e)\r
+{\r
+    ViewModel->DiscoverElevators();\r
+}\r
diff --git a/resource/IPCA/samples/ElevatorClientUWP/MainPage.xaml.h b/resource/IPCA/samples/ElevatorClientUWP/MainPage.xaml.h
new file mode 100644 (file)
index 0000000..be40b05
--- /dev/null
@@ -0,0 +1,48 @@
+/* *****************************************************************
+*
+* Copyright 2017 Microsoft
+*
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+******************************************************************/\r
+\r
+//\r
+// MainPage.xaml.h\r
+// Declaration of the MainPage class.\r
+//\r
+\r
+#pragma once\r
+\r
+#include "MainPage.g.h"\r
+#include "ElevatorViewModel.h"\r
+\r
+namespace ElevatorClientUWP\r
+{\r
+       /// <summary>\r
+       /// An empty page that can be used on its own or navigated to within a Frame.\r
+       /// </summary>\r
+       public ref class MainPage sealed\r
+       {\r
+       public:\r
+               MainPage();\r
+\r
+    private:\r
+        void setFloorBtn_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);\r
+        void Elevators_SelectionChanged(Platform::Object^ sender,\r
+                                        Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e);\r
+        void OnLoaded(Platform::Object ^sender, Windows::UI::Xaml::RoutedEventArgs ^e);\r
+\r
+        ElevatorViewModel^ ViewModel;\r
+    };\r
+}\r
diff --git a/resource/IPCA/samples/ElevatorClientUWP/Package.appxmanifest b/resource/IPCA/samples/ElevatorClientUWP/Package.appxmanifest
new file mode 100644 (file)
index 0000000..4ee9060
--- /dev/null
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" IgnorableNamespaces="uap mp">\r
+  <Identity Name="ab2593e4-2994-401e-a526-c92fd9b71414" Publisher="CN=ElevatorClientUWP" Version="1.0.0.0" />\r
+  <mp:PhoneIdentity PhoneProductId="ab2593e4-2994-401e-a526-c92fd9b71414" PhonePublisherId="00000000-0000-0000-0000-000000000000" />\r
+  <Properties>\r
+    <DisplayName>ElevatorClientUWP</DisplayName>\r
+    <PublisherDisplayName>Microsoft</PublisherDisplayName>\r
+    <Logo>Assets\StoreLogo.png</Logo>\r
+  </Properties>\r
+  <Dependencies>\r
+    <TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />\r
+  </Dependencies>\r
+  <Resources>\r
+    <Resource Language="x-generate" />\r
+  </Resources>\r
+  <Applications>\r
+    <Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="ElevatorClientUWP.App">\r
+      <uap:VisualElements DisplayName="ElevatorClientUWP" Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Square44x44Logo.png" Description="ElevatorClientUWP" BackgroundColor="transparent">\r
+        <uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png">\r
+        </uap:DefaultTile>\r
+        <uap:SplashScreen Image="Assets\SplashScreen.png" />\r
+      </uap:VisualElements>\r
+    </Application>\r
+  </Applications>\r
+  <Capabilities>\r
+    <Capability Name="internetClient" />\r
+    <Capability Name="privateNetworkClientServer" />\r
+  </Capabilities>\r
+</Package>
\ No newline at end of file
diff --git a/resource/IPCA/samples/ElevatorClientUWP/SConscript b/resource/IPCA/samples/ElevatorClientUWP/SConscript
new file mode 100644 (file)
index 0000000..fc4d3b7
--- /dev/null
@@ -0,0 +1,70 @@
+#******************************************************************
+#
+# Copyright 2017 Microsoft
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+import os
+
+Import('ipca_env')
+target_arch = ipca_env.get('TARGET_ARCH')
+elevator_client_uwp_env = ipca_env.Clone()
+
+# Convert architecture to the platform value used by Visual Studio.
+platform_str = target_arch
+if platform_str == 'amd64':
+    platform_str = 'x64'
+elif platform_str == 'x86':
+    platform_str = 'Win32'
+
+app_package_folder_name = 'ElevatorClientUWP_1.0.0.0_Test'
+if not elevator_client_uwp_env.get('RELEASE'):
+    platform_str += '_Debug'
+    app_package_folder_name = 'ElevatorClientUWP_1.0.0.0_Debug_Test'
+
+solutiondir = 'resource\\IPCA\\samples\\ElevatorClientUWP'
+solutionfile = os.path.join(elevator_client_uwp_env.get('SRC_DIR'),
+                            solutiondir,
+                            'ElevatorClientUWP.sln')
+
+# Construct output directory
+build_path = 'resource\\IPCA\\samples\\ElevatorClientUWP'
+build_outdir = os.path.normpath(elevator_client_uwp_env.get('BUILD_DIR') + build_path) + '\\'
+outdir = build_outdir + '\\'
+
+app_output_name = 'ElevatorClientUWP_1.0.0.0_' + platform_str
+appx_file = build_outdir + 'ElevatorClientUWP\\' + app_output_name + '.appx'
+
+# Set the MSBuildOutDir env variable so that the MSBuilder can use it
+# Note: This should only be set on a cloned Environment and not the global one
+elevator_client_uwp_env['MSBuildOutDir'] = outdir
+
+if elevator_client_uwp_env.GetOption('clean'):
+    elevator_client_uwp_env.MSBuildClean(appx_file, solutionfile)
+else:
+    elevator_client_uwp_build = elevator_client_uwp_env.MSBuild(appx_file, solutionfile)
+    elevator_client_uwp_env.AlwaysBuild('elevator_client_uwp_build')
+
+# Run the WACK test if we are building/running Release Tests
+if elevator_client_uwp_env.get('TEST') == '1' \
+   and elevator_client_uwp_env.get('RELEASE') \
+   and not elevator_client_uwp_env.GetOption('clean'):
+    cert_file =  build_outdir + 'ElevatorClientUWP\\AppPackages\\' + app_package_folder_name + \
+                 '\\' + app_output_name + '.cer'
+
+    from tools.scons.RunTest import *
+    run_uwp_wack_test(elevator_client_uwp_env, cert_file, appx_file, build_outdir)
diff --git a/resource/IPCA/samples/ElevatorClientUWP/Util.cpp b/resource/IPCA/samples/ElevatorClientUWP/Util.cpp
new file mode 100644 (file)
index 0000000..360f328
--- /dev/null
@@ -0,0 +1,143 @@
+/* *****************************************************************
+*
+* Copyright 2017 Microsoft
+*
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+******************************************************************/\r
+\r
+#include "pch.h"\r
+#include "Util.h"\r
+\r
+using namespace ElevatorClientUWP;\r
+using namespace Platform;\r
+using namespace Windows::UI::Core;\r
+using namespace Windows::UI::Popups;\r
+\r
+String^ Util::ConvertStrtoPlatformStr(PCSTR str)\r
+{\r
+    String^ wstrRet = nullptr;
+    PWSTR wstr = nullptr;
+
+    int wstrLength = MultiByteToWideChar(
+        CP_UTF8,
+        MB_ERR_INVALID_CHARS,
+        str,
+        -1,
+        nullptr,
+        0);
+
+    if (wstrLength == 0)
+    {
+        goto exit;
+    }
+
+    // wstrLength includes null char
+    wstr = new WCHAR[wstrLength];
+    if (wstr == nullptr)
+    {
+        goto exit;
+    }
+
+    int retLen = MultiByteToWideChar(
+        CP_UTF8,
+        0,
+        str,
+        -1,
+        wstr,
+        wstrLength);
+
+    if (retLen != wstrLength)
+    {
+        goto exit;
+    }
+
+    wstrRet = ref new String(wstr);
+
+exit:
+    if (wstr != nullptr)
+    {
+        delete[] wstr;
+    }
+    return wstrRet;\r
+}\r
+\r
+// This function converts a wide char string to a standard char string.
+std::string Util::ConvertWStrtoStr(PCWSTR wstr)
+{
+    std::string strRet;
+    char* str = nullptr;
+
+    int strLength = WideCharToMultiByte(
+        CP_UTF8,
+        WC_ERR_INVALID_CHARS,
+        wstr,
+        -1,
+        nullptr,
+        0,
+        nullptr,
+        nullptr);
+
+    if (strLength == 0)
+    {
+        goto exit;
+    }
+
+    // strLength includes null char
+    str = new char[strLength];
+    if (str == nullptr)
+    {
+        goto exit;
+    }
+
+    int retLen = WideCharToMultiByte(
+        CP_UTF8,
+        0,
+        wstr,
+        -1,
+        str,
+        strLength,
+        nullptr,
+        nullptr);
+
+    if (retLen != strLength)
+    {
+        goto exit;
+    }
+
+    strRet = str;
+
+exit:
+    if (str != nullptr)
+    {
+        delete[] str;
+    }
+    return strRet;
+}\r
+\r
+void Util::ShowErrorMsg(CoreDispatcher^ dispatcher, String^ msg)\r
+{\r
+    Platform::Agile<MessageDialog^> msgDlg(ref new MessageDialog(msg, "Error"));\r
+    if (dispatcher)\r
+    {\r
+        dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([msgDlg]\r
+        {\r
+            msgDlg->ShowAsync();\r
+        }));\r
+    }\r
+    else\r
+    {\r
+        msgDlg->ShowAsync();\r
+    }\r
+}\r
diff --git a/resource/IPCA/samples/ElevatorClientUWP/Util.h b/resource/IPCA/samples/ElevatorClientUWP/Util.h
new file mode 100644 (file)
index 0000000..2c57fdc
--- /dev/null
@@ -0,0 +1,34 @@
+/* *****************************************************************
+*
+* Copyright 2017 Microsoft
+*
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+******************************************************************/\r
+\r
+#pragma once\r
+\r
+#include "Windows.h"\r
+\r
+namespace ElevatorClientUWP\r
+{\r
+    public ref class Util sealed\r
+    {\r
+    internal:\r
+        static Platform::String^ ConvertStrtoPlatformStr(PCSTR str);\r
+        static std::string Util::ConvertWStrtoStr(PCWSTR wstr);\r
+        static void ShowErrorMsg(Windows::UI::Core::CoreDispatcher^ dispatcher,\r
+                                 Platform::String^ msg);\r
+    };\r
+}\r
diff --git a/resource/IPCA/samples/ElevatorClientUWP/pch.cpp b/resource/IPCA/samples/ElevatorClientUWP/pch.cpp
new file mode 100644 (file)
index 0000000..ef288d8
--- /dev/null
@@ -0,0 +1,6 @@
+//\r
+// pch.cpp\r
+// Include the standard header and generate the precompiled header.\r
+//\r
+\r
+#include "pch.h"\r
diff --git a/resource/IPCA/samples/ElevatorClientUWP/pch.h b/resource/IPCA/samples/ElevatorClientUWP/pch.h
new file mode 100644 (file)
index 0000000..f0ebf6e
--- /dev/null
@@ -0,0 +1,12 @@
+//\r
+// pch.h\r
+// Header for standard system include files.\r
+//\r
+\r
+#pragma once\r
+\r
+#include <collection.h>\r
+#include <ppltasks.h>\r
+\r
+#include "App.xaml.h"\r
+#include "Util.h"\r
index 320316a..7ec909a 100644 (file)
@@ -28,3 +28,5 @@ Import('ipca_env')
 SConscript('ipcaapp/SConscript', 'ipca_env')
 SConscript('ElevatorServer/SConscript', 'ipca_env')
 SConscript('ElevatorClient/SConscript', 'ipca_env')
+if ipca_env.get('UWP_APP') == '1':
+    SConscript('ElevatorClientUWP/SConscript', 'ipca_env')
index fee90aa..6a0c659 100644 (file)
@@ -15,6 +15,7 @@
 # ------------------------------------------------------------------------
 
 import os
+import platform
 
 def run_test(env, xml_file, test, test_targets = ['test']):
     """
@@ -69,3 +70,43 @@ def run_test(env, xml_file, test, test_targets = ['test']):
     ut = env.Command('ut' + test, None, test_cmd)
     env.Depends(ut, test_targets)
     env.AlwaysBuild(ut)
+
+def run_uwp_wack_test(env, cert_file, appx_file, report_output_path):
+    if env.get('TARGET_OS') != 'windows' or env.get('UWP_APP') != '1':
+        return
+
+    import winreg
+
+    # Add WACK path to path
+    appcert_reg_path = 'SOFTWARE\\Microsoft\\Windows App Certification Kit'
+    # The WACK registry key is only in the default location, i.e. SOFTWARE\\Microsoft\...
+    # If the machine is 64bit, then we need to look into the 64bit Registry View for the key
+    reg_view_64bit = (platform.machine() == 'AMD64')
+    appcert_path = env.ReadRegistryStringValue(winreg.HKEY_LOCAL_MACHINE,
+                                               appcert_reg_path,
+                                               'InstallLocation',
+                                               reg_view_64bit)
+    if not appcert_path:
+        Exit('Error: Could not Find the WACK Registry Key/Value')
+
+    env.AppendUnique(PATH = [appcert_path])
+    # Need to update the 'ENV' dictionary PATH as that's what is used when executing
+    # commands
+    env['ENV']['PATH'] = env.get('PATH')
+
+    # testid 38 is the "Supported APIs" test and verifies that all APIs used are available for
+    # store apps.
+    # Note that appcert.exe requires admin privileges and will prompt for elevation unless the
+    # build is run from an elevated command shell.
+    report_output_file = os.path.join(report_output_path, 'wack_output.xml')
+    command = 'certutil -addstore root %s' % (cert_file)
+    command += ' && appcert.exe reset'
+    command += ' && appcert.exe test -AppxPackagePath %s -reportoutputpath %s -testid [38]' \
+               % (appx_file, report_output_file)
+    command += ' && certutil %s | for /f "delims=: tokens=1*" %%i in (\'findstr "Serial Number:"\') do (certutil -delstore root %%j)' % (cert_file)
+    command += ' && findstr OVERALL_RESULT=.PASS %s' % (report_output_file)
+
+    print 'Running WACK Test'
+    wack_test = env.Command(report_output_file, appx_file, command)
+    env.AlwaysBuild(wack_test)
+