## For instruction on writing tutorials
## http://www.ros.org/wiki/WritingTutorials
####################################
##FILL ME IN
####################################
## for a custom note with links:
## note =
## for the canned note of "This tutorial assumes that you have completed the
## previous tutorials:" just add the links
## note.0=
## descriptive title for the tutorial
## title = Use the tango ROS node in your own app
## multi-line description to be displayed in search
## description = Tutorial to integrate the tango ROS node of [[http://wiki.ros.org/tango_ros_streamer|TangoRosStreamer|target="_blank"]] in your own app.
## the next tutorial description (optional)
## next =
## links to next tutorial (optional)
## next.0.link=
## next.1.link=
## what level user is this tutorial for
## level=IntermediateCategory
## keywords = tango, ros, node, rosjava, android, application
####################################
<<IncludeCSTemplate(TutorialCSHeaderTemplate)>>

The tango ROS node is the core of the [[http://wiki.ros.org/tango_ros_streamer|Tango Ros Streamer]] application. This tutorial explains how to integrate it in an another application. See the app's main webpage to see the description of available topics that can be published by the node.

The artifact corresponding to the node in its released version [[https://github.com/Intermodalics/tango_ros/releases/tag/v1.3.1|1.3.1]] available in 
[[https://github.com/rosjava/rosjava_mvn_repo|rosjava_mvn_repo]] for easy integration in other applications.

<<TOC(4)>>

== Pre requisites ==

Basic knowledge of ROS in Android is required to develop applications using this node. See [[http://wiki.ros.org/android|here]] for further reference.
The application should be created as a [[http://wiki.ros.org/rosjava_build_tools/Tutorials/indigo/Creating%20Android%20Packages|catkin-android package]].

== Installing dependencies ==

=== Module-level Gradle script ===
The tango ROS node is embedded inside a [[nodelet|nodelet]]. To use it on Android, you will need to include the module  called `tango_nodelet_manager` which contains the nodelet. You will also need to include the `tango_ros_common` module which contains some required utility classes. To do so, add the following lines to your module-level `build.gradle` file:

{{{
dependencies {
    compile('org.ros.android_core:android_10:[0.3, 0.4)') {
        exclude group: 'junit'
        exclude group: 'xml-apis'
    }

    compile 'eu.intermodalics:tango_nodelet_manager:1.3.1'
    compile 'eu.intermodalics:tango_ros_common:1.3.1'
}

android {
    ...
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    ...
}

}}}

=== Top level Gradle script ===

Your top-level Gradle file should be created using the latest Kinetic tools. If you are not using ROS Kinetic, you can use the following code as your top-level `build.gradle`:

{{{

task wrapper(type: Wrapper) {
    gradleVersion = '2.14.1'
}

buildscript {
    apply from: "https://github.com/rosjava/android_core/raw/kinetic/buildscript.gradle"
}

apply plugin: 'catkin'

allprojects {
    // A github url provides a good standard unique name for your project
    group 'com.example'
    version = project.catkin.pkg.version
}

subprojects {
    apply plugin: 'ros-android'

    afterEvaluate { project ->
        android {
            // Exclude a few files that are duplicated across our dependencies and
            // prevent packaging Android applications.
            packagingOptions {
                exclude "META-INF/LICENSE.txt"
                exclude "META-INF/NOTICE.txt"
            }
        }
    }
}

}}}

== Integrating the node to your application ==

Running the tango ROS node is somewhat similar to a standard rosjava nodes, but some previous steps have to be done before its execution to bind to Tango Service. We will assume that the code samples detailed below shall be inside your `Activity`, which should extend `RosActivity`, defined in `Android_10`.

=== Tango Service Binding ===
Create an Android `ServiceConnection` to handle binding to the Tango Service. The `TangoInitializationHelper` class provides a default service connection; it will help to override a callback that will be called after the service is bound.
Here's a code snippet:

{{{#!java
    // In your Android Activity
    ServiceConnection tangoServiceConnection = 
new TangoInitializationHelper.DefaultTangoServiceConnection(
        new AfterConnectionCallback() {
            @Override
            public void execute() {
                if (TangoInitializationHelper.isTangoServiceBound()) {
                // Handle successful service connection (e.g: send log msg.).
                } else {
                // Handle unsuccessful service connection (e.g: log msg. & app shutdown).
                }
            }
        });
}}}

=== Create and start node ===
In your `init` method of your `Activity` (which must be overridden from `RosActivity` as it is an abstract method), you can start the node calling a method like the following:

{{{#!java

    public void startTangoRosNode(NodeMainExecutor nodeMainExecutor) {

        // getRosHostname() and getMasterUri() are methods provided by RosActivity,
        // which work in combination with `MasterChooser` Activity provided by 
        // Rosjava Android_10.  
        String hostName = getRosHostname();
        NodeConfiguration nodeConfiguration = NodeConfiguration.newPublic(hostName);
        nodeConfiguration.setMasterUri(getMasterUri());

        // Create and start Tango ROS Node
        nodeConfiguration.setNodeName(TangoNodeletManager.NODE_NAME);
        if (TangoInitializationHelper.loadTangoSharedLibrary() != 
                  TangoInitializationHelper.ARCH_ERROR && 
                  TangoInitializationHelper.loadTangoRosNodeSharedLibrary() != 
                  TangoInitializationHelper.ARCH_ERROR) {
            TangoNodeletManager tangoNodeletManager = new TangoNodeletManager();
            TangoInitializationHelper.bindTangoService(this, tangoServiceConnection);
            if (TangoInitializationHelper.isTangoVersionOk()) {
                nodeMainExecutor.execute(tangoNodeletManager, nodeConfiguration);
            } else {
                // Handle Tango version error
            }
        } else {
            // Handle Tango lib error        
        } 
    }
}}}
If you would like to remap topics/parameters/services of the tango ROS node, you need to use the [[Remapping Arguments]]. To do so, replace the line {{{#!java 
TangoNodeletManager tangoNodeletManager = new TangoNodeletManager();
}}}
by the following lines:
{{{#!java 
String[] remappingArguments = {"/tango/laser_scan:=/laser", "/tango/point_cloud:=/pointcloud"};
TangoNodeletManager tangoNodeletManager = new TangoNodeletManager(remappingArguments);
}}}
In this example the topic `/tango/laser_scan` is remapped to `/laser` and the topic `/tango/point_cloud` is remapped to `/pointcloud`.

=== Start streaming Tango data ===
Now that the tango ROS node is running, we need to explicitly connect to the Tango Service to be able to stream Tango data. To do so, we need to make a service call to the `/tango/connect` service using the `TangoServiceClientNode`.
First, your activity has to declare an instance of `TangoServiceClientNode` and implements its `CallbackListener` interface:

{{{#!java

    public class MyRosActivity extends RosActivity implements
        TangoServiceClientNode.CallbackListener {
        ...
        private TangoServiceClientNode tangoServiceClientNode;
        ...
    }
}}}

Then, in the same function as before, add the following lines.

{{{#!java

    public void startTangoRosNode(NodeMainExecutor nodeMainExecutor) {

        ...
        tangoServiceClientNode = new TangoServiceClientNode();
        tangoServiceClientNode.setCallbackListener(this);
        nodeConfiguration.setNodeName(tangoServiceClientNode.getDefaultNodeName());
        nodeMainExecutor.execute(tangoServiceClientNode, nodeConfiguration);
        // Create and start Tango ROS Node
        nodeConfiguration.setNodeName(TangoNodeletManager.NODE_NAME);
        if (TangoInitializationHelper.loadTangoSharedLibrary() != 
                  TangoInitializationHelper.ARCH_ERROR && 
                  TangoInitializationHelper.loadTangoRosNodeSharedLibrary() != 
                  TangoInitializationHelper.ARCH_ERROR) {
            ...
            int maxTangoConnectionTry = 50;
            if (TangoInitializationHelper.isTangoVersionOk()) {
                nodeMainExecutor.execute(tangoNodeletManager, nodeConfiguration, 
new ArrayList<NodeListener>(){{
                    add(new DefaultNodeListener() {
                        @Override
                        public void onStart(ConnectedNode connectedNode) {
                            int count = 0;
                            while (count < maxTangoConnectionTry &&                           !tangoServiceClientNode.callTangoConnectService(TangoConnectRequest.CONNECT)) {
                                try {
                                    count++;
                                    Log.e(TAG, "Trying to connect to Tango, attempt " + count);
                                    Thread.sleep(200);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                            if (count >= maxTangoConnectionTry) {
                                // Handle Tango connection error.
                            }
                        }
                    });
                }});              
            } else {
                // Handle Tango version error.
            }
        } else {
            // Handle tango lib error.  
        } 
    }
}}}
The key line is `tangoServiceClientNode.callTangoConnectService(TangoConnectRequest.CONNECT)` (line 23). This will try to call the `/tango/connect` service which connects to the Tango Service.   Keep in mind that it returns true if the service is found, '''not''' when the call to the service is finished. 

Note that we need a while loop which tries to call the service until it is found. This is necessary because when the tango ROS node starts, the `/tango/connect` service is not immediately available.  

Finally, you will need to implement the callbacks from the `CallbackListener` interface of the `TangoServiceClientNode`. This is where you should add the code you want to execute once a specific service call is finished.  

In this minimal example, only `onTangoConnectServiceFinish` will be called, but we'll show an example of implementation for each callback.
Still in your `Activity`, add the following lines:

{{{#!java

    public class MyRosActivity extends RosActivity implements
        TangoServiceClientNode.CallbackListener {

        ...

        public void onSaveMapServiceCallFinish(boolean success, final String message,
                                               final String mapName, 
                                               final String mapUuid) {
            if (success) {
                Log.i(TAG, "Map " + mapName " has been successfully saved with uuid: "
+ mapUuid);
            } else {
                Log.e(TAG, "Error while saving map: " + message);
            }
        }

        public void onTangoConnectServiceFinish(int response, String message) {
            if (response == TangoConnectResponse.TANGO_SUCCESS) {
                Log.i(TAG, "Successfully connected to Tango.");
            } else {
                Log.e(TAG, "Error while connecting to Tango: " + response 
+ ", message: " + message);
            }
        }

        public void onTangoDisconnectServiceFinish(int response, String message) {
            if (response == TangoConnectResponse.TANGO_SUCCESS) {
                Log.i(TAG, "Successfully disconnected to Tango.");
            } else {
                Log.e(TAG, "Error while disconnecting to Tango: " + response 
+ ", message: " + message);
            }
        }

        public void onTangoReconnectServiceFinish(int response, String message) {
            if (response == TangoConnectResponse.TANGO_SUCCESS) {
                Log.i(TAG, "Successfully reconnected to Tango.");
            } else {
                Log.e(TAG, "Error while reconnecting to Tango: " + response 
+ ", message: " + message);
            }
        }

        public void onGetMapUuidsFinish(List<String> mapUuids, List<String> mapNames) {
            if (mapUuids == null || mapNames == null) return;
            assert(mapUuids.size() == mapNames.size());
            for (int i = 0; i < mapUuids.size(); ++i) {
                Log.i(TAG, "Uuid " + mapUuids.get(i) + " corresponds to map " 
+ mapNames.get(i));
            }
        }

        public void onTangoStatus(int status) {
            Log.i(TAG, "Current Tango status is " + status);
        }

        public void onLoadOccupancyGridServiceCallFinish(
            boolean success, final String message, boolean aligned, String mapUuid) {
            if (success) {
                if (aligned) {
                    Log.i(TAG, "Occupancy grid successfully loaded and aligned with
localization map.");
                } else {
                    Log.w(TAG, "Occupancy grid successfully loaded but not aligned with
current localization map. Please load localization map with uuid " + mapUuid);
                }
                Log.i(TAG, message);
            } else {
                Log.e(TAG, "Error while loading occupancy grid: " + message);
            }
        }
        ...
    }
}}}

If you really don't need to implement all callbacks, you can extend the `DefaultCallbackListener` of the `TangoServiceClientNode` class and override only the callbacks you are interested in. In this case your activity does not need to implement the `CallbackListener` interface.

That's it! Your `TangoNodeletManager` should be up and running if everything goes well.

== Additional information ==
You can check the available documentation in the source code of the node in  [[https://github.com/Intermodalics/tango_ros|Tango Ros Streamer public repository]]. For a quick reference, look into the node's main files:
 * [[https://github.com/Intermodalics/tango_ros/blob/v1.1/TangoRosStreamer/tango_nodelet_manager/src/main/java/eu/intermodalics/nodelet_manager/TangoNodeletManager.java|java wrapper]].
 * [[https://github.com/Intermodalics/tango_ros/blob/v1.1/tango_ros_common/tango_ros_native/src/tango_ros_node.cpp|native implementation]].
 * [[https://github.com/Intermodalics/tango_ros/blob/v1.1/TangoRosStreamer/tango_nodelet_manager/src/main/jni/jni.cc|jni interface]].
 * [[https://github.com/Intermodalics/tango_ros/blob/v1.1/TangoRosStreamer/tango_nodelet_manager/src/main/java/eu/intermodalics/nodelet_manager/TangoInitializationHelper.java|Initialization helper]].

## AUTOGENERATED DO NOT DELETE
## TutorialCategory
## FILL IN THE STACK TUTORIAL CATEGORY HERE