How-to use the library in your own projects

To use paarc in your own project, you basically have two options:
  • Use the reference Windows Phone client
  • Create an own Windows Phone application

If you choose the first option, you can fully concentrate on implementing your desktop application, and only have to worry about integrating the server-side component of paarc, which I explain in this document. However, since the reference Windows Phone client is a generic implementation that doesn't make any assumptions on the remote .NET application, it might not be suitable for you, or can be sub-optimal. Imagine for example that you want to trigger special features unique to your application, using separate buttons in the phone client. Other reasons you don't might want to use the built-in client are that you don't like the design (or you want your own branding) or how it triggers and handles certain things like text input. In these cases, you can also implement a custom phone client using the client-side components of the library directly. I'll only discuss some details of this briefly here (if there's more demand for it in the future I'll extend that); however both the reference client as well as the client-side API of the core library are well documented. Looking at the source code of the PAARC.WP7 project should give you a good idea how to create an own implementation quickly.

Using the server component

To integrate paarc with your .NET application, you have to reference to assemblies from the project:
  • PAARC.ServerCommunication
  • PAARC.Shared

The first assembly contains the server specific logic and implementation of the networking features, whereas the second one is a portable class library that contains the data types and definitions that are shared between the client (phone) and the server (.NET application).

A note on PAARC.Shared: If you are familiar with developing for Windows Phone or XNA, you will find that a few of the types included in the second assembly (like Vector or Matrix classes) are basically copies of the already existing features on the platform. This became necessary to enable access from both .NET and Windows Phone. None of the original functionality of these classes is available (as in mathematical features, operator overloads etc.), they're simple data containers for transportation. So if you intend to further process the data and perform mathematical computations, you have to map these structures back to suitable .NET types first.

The basic workflow

When you add paarc, the basic steps and process in your code to enable it and make it work are as follows:

Server-basic-workflow.png

Sample code

The following code snippets demonstrate these steps using fragments from the included Cube sample of the project.

// initialize controller
_phoneController = new PhoneControllerServer();
_phoneController.Error += PhoneController_Error;
_phoneController.StateChanged += PhoneController_StateChanged;
_phoneController.DataMessageReceived += PhoneController_DataMessageReceived;

The sample uses the first IP address of the local computer it can find. It's possible for a computer to have more than one network interface (phyically or virtually), so you should make sure you use the correct address or offer a selection of the available ones.

var addresses = Dns.GetHostAddresses(string.Empty);
var address = addresses.First(o => o.AddressFamily == AddressFamily.InterNetwork);
_phoneController.Initialize(address);

The DataMessageReceived event handler looks like this:

private void PhoneController_DataMessageReceived(object sender, DataMessageEventArgs e)
{
    // we have received data from the controller
    switch (e.DataMessage.DataType)
    {
        case DataType.ControllerInfo:
            // this info is received every time the controller connects
            // => a good place to get the device caps and trigger configuration
            var controllerInfo = e.DataMessage as ControllerInfoData;
            _supportsAccelerometer = controllerInfo.IsAccelerometerSupported;
            _supportsTouch = controllerInfo.IsTouchSupported;
            ConfigureController();
            break;
        case DataType.Accelerometer:
            // this is accelerometer data received from the phone
            // => we are going to rotate the model based on that information
            HandleAccelerometerData(e.DataMessage as AccelerometerData);
            break;
			
		// etc.
	}
}

The phone will automatically send you a ControllerInfo data message as the first data after it connected. This message contains information about the device, like the screen dimensions, that can be helpful for processing the data that is acquired from the touch panel, for example. It also contains information about the capabilities of the device, for example if the device has gyroscope support or not. You can only use the data sources that are available on the device - triggering non-supported sensors has no effect (but also doesn't result in errors). Once you have obtained the capabilities of the connected client, it's a good idea to configure the phone:

private void ConfigureController()
{
    if (_phoneController != null && _phoneController.State == PhoneControllerState.Ready)
    {
        // you can send configuration data remotely to the phone to set up basic parameters
        var configuration = ControlCommandFactory.CreateConfigurationCommand();

        // tells the phone to try and reconnect to the server after
        // reactivation (e.g. after tombstoning or after the lock screen becomes active).
        configuration.Configuration.AutoReconnectOnActivation = true;

        _phoneController.SendCommandAsync(configuration);
		
		// etc.

Sending a ConfigurationControlCommand allows you to set basic global parameters. For example, in the above snippets the phone is configured to try to reconnect automatically after activation (when the user navigated away from the client and returned back to the application). You can also limit the rate individual data sources are reported at, or throttle the data rate globally. This configuration also allows defining a margin around the touch sensitive area of the reference client, for example to make it easier for the user to reach the extremes (corners) of the touch screen.

After the configuration, you can send start and stop commands for individual data types to tell the phone what you're interested in. Those data types are then received in the DataMessageReceived event handler above.

// send accelerometer start command (accelerometer is supported on all devices, but check to be sure)
if (_supportsAccelerometer)
{
    SendStartControlCommand(DataType.Accelerometer);
}

The payload of the received messages contains the original data obtained from the phone. In the case of accelerometer readings, these are the X, Y, Z coordinates and a timestamp, for example. That data directly corresponds to the original data structure of the data types (e.g. sensor readings) on the phone, so you can easily use the Windows Phone developer documentation to get more information on that data and how to use it.

When you're done, it's a good habit to gracefully shut down the server:

// called when the application closes
if (_phoneController != null)
{
    _phoneController.Error -= PhoneController_Error;
    _phoneController.StateChanged -= PhoneController_StateChanged;
    _phoneController.DataMessageReceived -= PhoneController_DataMessageReceived;

    _phoneController.Shutdown();
}

In the case of any errors, the PhoneControllerServer will transition to the Error state. It will also raise the Error event that contains more details about the error that occurred. Some possible causes for this are:
  • Incompatible client version: there is a versioning mechanism built into the library. The version number used by the library is increased every time a breaking change in the protocol is introduced that would cause consistency issues with older clients. If a client with a wrong version number tries to connect, it is rejected, and you will receive an Error notification. In that case, the event arguments contain a ControllerVersionMismatchException with more details.
  • The client or network connection was deactivated or shut down unexpectedly. Just like you should do on the server side, the client also closes connections gracefully when the application is closed or deactivated. However, there's always a possibility-especially on mobile platforms-that network connections are shutting down unexpectedly or other unforeseen things happen. In that case the PhoneControllerServer also triggers an Error event with more details and transitions to Error state.

Once in Error state, you should still call Shutdown to make sure all resources are cleaned up correctly. After that, the PhoneControllerServer transitions to Closed state. In that state you are allowed to immediately call Initialize again, meaning that you can reuse a PhoneControllerServer after it has been closed, without the need to create a new one from scratch. A valid pattern on the server side therefore is something like:

private void PhoneController_StateChanged(object sender, PhoneControllerStateEventArgs e)
{
    // decide what to do
    switch (e.State)
    {
        case PhoneControllerState.Closed:
            // simply try to initialize
            InitializeController();
            break;
    }
}

This ensures that the server is available for a new (or the same) client immediately after the connection was shut down, which ensures a good user experience for example when the phone client is interrupted by a phone call and the user wants to return to the app a few seconds later.

Implementing a custom client application

If you don't want to use the reference client but implement your own Windows Phone UI for the library (or integrate the library in an existing application, for example), you need to reference the following assemblies:
  • PAARC.ClientCommunication: the client-side communication implementation
  • PAARC.Shared: the data types and definitions shared between the client and server
  • PAARC.DataAcquisition*: the default data acquisition implementation that uses e.g. the TouchPanel and SensorBase<T> derived classes of the phone runtime.

Note: The data acquisition is decoupled from the actual client communcation implementation to allow different implementations of the data acquisition to be used. If you are not satisfied with the default implementation or want to create your own data acquisition for other reasons, you can implement the IDataSource interface and use this for the PhoneControllerClient instance (see below).

The basic workflow

The basic workflow and fundamental steps to perform using the client-side library are:

Client-basic-workflow.png

The data flow between the data source and the client communication happens automatically, meaning that the data source reports directly to the client communication implementation, which in turn sends the data to the server. That means that you usually do not need to hook the DataAcquired event of the data source and/or explicitly pass on data to the phone controller client. If for some reason you're interested in that data too, you are of course free to make use of this event. Please note that for performance reasons, the event is not raised on the SynchronizationContext that was used to create the data source, but from a background thread.

The data source also reports when the data types acquisition is active for changes, for example by control commands sent from the server. In the same way, the phone controller client reports changes in its configuration e.g. when the server sends a new configuration command. You are also allowed to manually configure the client using the respective method, for example to restore a previous configuration when the application is started or activated.

To connect to a remote servers, two overloads of the ConnectAsync method exist: one that takes an IP address and tries to connect to that address directy, and the other one without any arguments. That latter overload uses the multicast client to broadcast and discover a server on the network automatically. If it is not able to receive a server answer and connect within five seconds, an Error event is raised (using a TimeOut error code). Just as with the server side, you are allowed to shut down and reuse a phone controller client, for example after an Error or when the remote server has closed the connection.

Additionally required steps

The library on the phone depends on certain device specific parameters, for example the current screen orientation and resolution. The helper class that monitors e.g. device orientation is named DeviceInfo (located in the DataAcquisition project). For this class to function properly, it is mandatory to call it's Initialize method very early in the phone application startup process. In the reference client, this is done in the CompleteInitializePhoneApplication method in App.xaml.cs:

private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
{
    // Set the root visual to allow the application to render
    if (RootVisual != RootFrame)
        RootVisual = RootFrame;

    // Remove this handler since it is no longer needed
    RootFrame.Navigated -= CompleteInitializePhoneApplication;

    // initialize the device info class
    DeviceInfo.Initialize();
}

The class depends on the PhoneApplicationFrame that is the RootVisual of the application internally. Since the RootVisual is set here, this is the earliest possible place to perform the DeviceInfo initialization. You are free to move this to a different place in your own client implementation, but make sure it is called before the device capabilities are sent to the remote server (e.g. before you call ConnectAsync for the first time).

Last edited Nov 2, 2011 at 10:23 PM by Mister_Goodcat, version 2

Comments

Saad29 Feb 26, 2013 at 12:47 PM 
please provide a working sample or example for the second method. for windows phone 8 developers..it would be greatly appreciated
thanks in advance :)