tango_ros_nodelet.cpp
Go to the documentation of this file.
00001 // Copyright 2016 Intermodalics All Rights Reserved.
00002 //
00003 // Licensed under the Apache License, Version 2.0 (the "License");
00004 // you may not use this file except in compliance with the License.
00005 // You may obtain a copy of the License at
00006 //
00007 //      http://www.apache.org/licenses/LICENSE-2.0
00008 //
00009 // Unless required by applicable law or agreed to in writing, software
00010 // distributed under the License is distributed on an "AS IS" BASIS,
00011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012 // See the License for the specific language governing permissions and
00013 // limitations under the License.
00014 #include "tango_ros_native/occupancy_grid_file_io.h"
00015 #include "tango_ros_native/tango_3d_reconstruction_helper.h"
00016 #include "tango_ros_native/tango_ros_conversions_helper.h"
00017 #include "tango_ros_native/tango_ros_nodelet.h"
00018 
00019 #include <cmath>
00020 #include <ctime>
00021 #include <iostream>
00022 #include <vector>
00023 #include <sstream>
00024 
00025 #include <glog/logging.h>
00026 
00027 #include <dynamic_reconfigure/config_tools.h>
00028 #include <dynamic_reconfigure/server.h>
00029 #include <geometry_msgs/PoseStamped.h>
00030 #include <pluginlib/class_list_macros.h>
00031 #include <sensor_msgs/image_encodings.h>
00032 #include <std_msgs/Int8.h>
00033 #include <tf2_geometry_msgs/tf2_geometry_msgs.h>
00034 
00035 PLUGINLIB_EXPORT_CLASS(tango_ros_native::TangoRosNodelet, nodelet::Nodelet)
00036 
00037 namespace {
00038 std::vector<std::string> SplitCommaSeparatedString(const std::string& comma_separated_string) {
00039   std::vector<std::string> output;
00040   std::stringstream ss(comma_separated_string);
00041 
00042   std::string string_element;
00043   while (std::getline(ss, string_element, ',')) {
00044     output.push_back(string_element);
00045   }
00046   return output;
00047 }
00048 // This function routes onPoseAvailable callback to the application object for
00049 // handling.
00050 // @param context, context will be a pointer to a TangoRosNodelet
00051 //        instance on which to call the callback.
00052 // @param pose, pose data to route to onPoseAvailable function.
00053 void OnPoseAvailableRouter(void* context, const TangoPoseData* pose) {
00054   tango_ros_native::TangoRosNodelet* app =
00055       static_cast<tango_ros_native::TangoRosNodelet*>(context);
00056   app->OnPoseAvailable(pose);
00057 }
00058 // This function routes onPointCloudAvailable callback to the application
00059 // object for handling.
00060 // @param context, context will be a pointer to a TangoRosNodelet
00061 //        instance on which to call the callback.
00062 // @param point_cloud, point cloud data to route to OnPointCloudAvailable
00063 //        function.
00064 void OnPointCloudAvailableRouter(void* context,
00065                                  const TangoPointCloud* point_cloud) {
00066   tango_ros_native::TangoRosNodelet* app =
00067       static_cast<tango_ros_native::TangoRosNodelet*>(context);
00068   app->OnPointCloudAvailable(point_cloud);
00069 }
00070 // This function routes OnFrameAvailable callback to the application
00071 // object for handling.
00072 // @param context, context will be a pointer to a TangoRosNodelet
00073 //        instance on which to call the callback.
00074 // @param camera_id, the ID of the camera. Only TANGO_CAMERA_COLOR and
00075 //        TANGO_CAMERA_FISHEYE are supported.
00076 // @param buffer, image data to route to OnFrameAvailable function.
00077 void OnFrameAvailableRouter(void* context, TangoCameraId camera_id,
00078                             const TangoImageBuffer* buffer) {
00079   tango_ros_native::TangoRosNodelet* app =
00080       static_cast<tango_ros_native::TangoRosNodelet*>(context);
00081   app->OnFrameAvailable(camera_id, buffer);
00082 }
00083 
00084 // Compute fisheye distorted coordinates from undistorted coordinates.
00085 // The distortion model used by the Tango fisheye camera is called FOV and is
00086 // described in 'Straight lines have to be straight' by Frederic Devernay and
00087 // Olivier Faugeras. See https://hal.inria.fr/inria-00267247/document.
00088 void ApplyFovModel(
00089     double xu, double yu, double w, double w_inverse, double two_tan_w_div_two,
00090     double* xd, double* yd) {
00091   double ru = sqrt(xu * xu + yu * yu);
00092   constexpr double epsilon = 1e-7;
00093   if (w < epsilon || ru < epsilon) {
00094     *xd = xu;
00095     *yd = yu ;
00096   } else {
00097     double rd_div_ru = std::atan(ru * two_tan_w_div_two) * w_inverse / ru;
00098     *xd = xu * rd_div_ru;
00099     *yd = yu * rd_div_ru;
00100   }
00101 }
00102 // Compute the warp maps to undistort the Tango fisheye image using the FOV
00103 // model. See OpenCV documentation for more information on warp maps:
00104 // http://docs.opencv.org/2.4/modules/imgproc/doc/geometric_transformations.html
00105 // @param fisheye_camera_info the fisheye camera intrinsics.
00106 // @param cv_warp_map_x the output map for the x direction.
00107 // @param cv_warp_map_y the output map for the y direction.
00108 void ComputeWarpMapsToRectifyFisheyeImage(
00109     const sensor_msgs::CameraInfo& fisheye_camera_info,
00110     cv::Mat* cv_warp_map_x, cv::Mat* cv_warp_map_y) {
00111   const double fx = fisheye_camera_info.K[0];
00112   const double fy = fisheye_camera_info.K[4];
00113   const double cx = fisheye_camera_info.K[2];
00114   const double cy = fisheye_camera_info.K[5];
00115   const double w = fisheye_camera_info.D[0];
00116   // Pre-computed variables for more efficiency.
00117   const double fy_inverse = 1.0 / fy;
00118   const double fx_inverse = 1.0 / fx;
00119   const double w_inverse = 1 / w;
00120   const double two_tan_w_div_two = 2.0 * std::tan(w * 0.5);
00121   // Compute warp maps in x and y directions.
00122   // OpenCV expects maps from dest to src, i.e. from undistorted to distorted
00123   // pixel coordinates.
00124   for(int iu = 0; iu < fisheye_camera_info.height; ++iu) {
00125     for (int ju = 0; ju < fisheye_camera_info.width; ++ju) {
00126       double xu = (ju - cx) * fx_inverse;
00127       double yu = (iu - cy) * fy_inverse;
00128       double xd, yd;
00129       ApplyFovModel(xu, yu, w, w_inverse, two_tan_w_div_two, &xd, &yd);
00130       double jd = cx + xd * fx;
00131       double id = cy + yd * fy;
00132       cv_warp_map_x->at<float>(iu, ju) = jd;
00133       cv_warp_map_y->at<float>(iu, ju) = id;
00134     }
00135   }
00136 }
00137 // Returns a string corresponding to current date and time.
00138 // The format is as follow: year-month-day_hour-min-sec.
00139 std::string GetCurrentDateAndTime() {
00140   std::time_t currentTime;
00141   struct tm* currentDateTime;
00142   std::time(&currentTime);
00143   currentDateTime = std::localtime(&currentTime);
00144   int day = currentDateTime->tm_mday;
00145   int month = currentDateTime->tm_mon + 1;
00146   int year = currentDateTime->tm_year + 1900;
00147   int hour = currentDateTime->tm_hour;
00148   int min = currentDateTime->tm_min;
00149   int sec = currentDateTime->tm_sec;
00150   std::ostringstream oss;
00151   oss << year << "-" << month << "-" << day << "_" << hour << "-" << min << "-" << sec;
00152   return oss.str();
00153 }
00154 // Returns device boottime in second.
00155 double GetBootTimeInSecond() {
00156   struct timespec res_boot;
00157   clock_gettime(CLOCK_BOOTTIME, &res_boot);
00158   return res_boot.tv_sec + (double) res_boot.tv_nsec / 1e9;
00159 }
00160 // Save an Area Description File (ADF) and set its name.
00161 // @param map_name Name of the ADF
00162 // @param[out] map_uuid Uuid of the ADF.
00163 // @param[out] message Contains an error message in case of failure.
00164 // Returns true if the ADF was successfully saved and named, false otherwise.
00165 bool SaveTangoAreaDescription(const std::string& map_name,
00166                               std::string& map_uuid, std::string& message) {
00167   TangoErrorType result;
00168   TangoUUID map_tango_uuid;
00169   result = TangoService_saveAreaDescription(&map_tango_uuid);
00170   if (result != TANGO_SUCCESS) {
00171     LOG(ERROR) << "Error while saving area description, error: " << result;
00172     message =  "Could not save the map. "
00173         "Did you allow the app to use area learning?";
00174     return false;
00175   }
00176   TangoAreaDescriptionMetadata metadata;
00177   result = TangoService_getAreaDescriptionMetadata(map_tango_uuid, &metadata);
00178   if (result != TANGO_SUCCESS) {
00179     LOG(ERROR) << "Error while trying to access area description metadata, error: " << result;
00180     message =  "Could not access map metadata";
00181     return false;
00182   }
00183   result = TangoAreaDescriptionMetadata_set(metadata, "name", map_name.capacity(), map_name.c_str());
00184   if (result != TANGO_SUCCESS) {
00185     LOG(ERROR) << "Error while trying to change area description metadata, error: " << result;
00186     message =  "Could not set the name of the map";
00187     return false;
00188   }
00189   result = TangoService_saveAreaDescriptionMetadata(map_tango_uuid, metadata);
00190   if (result != TANGO_SUCCESS) {
00191     LOG(ERROR) << "Error while saving new area description metadata, error: " << result;
00192     message =  "Could not save map metadata";
00193     return false;
00194   }
00195   result = TangoAreaDescriptionMetadata_free(metadata);
00196   if (result != TANGO_SUCCESS) {
00197     LOG(ERROR) << "Error while trying to free area description metadata, error: " << result;
00198     message =  "Could not free map metadata";
00199     return false;
00200   }
00201   map_uuid = static_cast<std::string>(map_tango_uuid);
00202   return true;
00203 }
00204 // Get the uuid of the ADF currently used by Tango.
00205 // @param tango_config Current configuration of Tango.
00206 // @param[out] adf_uuid Uuid of the current ADF, empty if Tango
00207 // is not using an ADF for localization.
00208 bool GetCurrentADFUuid(const TangoConfig& tango_config, std::string& adf_uuid) {
00209   char current_adf_uuid[TANGO_UUID_LEN];
00210   const char* config_load_area_description_UUID = "config_load_area_description_UUID";
00211   TangoErrorType result = TangoConfig_getString(
00212       tango_config, config_load_area_description_UUID, current_adf_uuid,
00213       TANGO_UUID_LEN);
00214   if (result != TANGO_SUCCESS) {
00215     LOG(ERROR) << "TangoConfig_getString "
00216         << config_load_area_description_UUID << " error: " << result;
00217     return false;
00218   }
00219   adf_uuid = std::string(current_adf_uuid);
00220   return true;
00221 }
00222 
00223 template<typename T>
00224 void SetDefaultValueIfParamDoesNotExist(
00225     const ros::NodeHandle& node_handle, const std::string& param_name,
00226     T default_value) {
00227   if (!node_handle.hasParam(param_name)) {
00228     node_handle.setParam(param_name, default_value);
00229   }
00230 }
00231 template<typename T>
00232 void GetParamValueAndSetDefaultValueIfParamDoesNotExist(
00233     const ros::NodeHandle& node_handle, const std::string& param_name,
00234     T default_value, T& param_value) {
00235   if (node_handle.hasParam(param_name)) {
00236     node_handle.getParam(param_name, param_value);
00237   } else {
00238     param_value = default_value;
00239     node_handle.setParam(param_name, default_value);
00240   }
00241 }
00242 
00243 void InvertTransformStamped(const geometry_msgs::TransformStamped& a_T_b,
00244                             geometry_msgs::TransformStamped* b_T_a) {
00245   geometry_msgs::Transform a_T_b_msg = a_T_b.transform;
00246   tf2::Transform a_T_b_tf;
00247   tf2::fromMsg(a_T_b_msg, a_T_b_tf);
00248   tf2::Transform b_T_a_tf = a_T_b_tf.inverse();
00249   geometry_msgs::Transform b_T_a_msg = tf2::toMsg(b_T_a_tf);
00250   b_T_a->transform = b_T_a_msg;
00251   b_T_a->header = a_T_b.header;
00252   b_T_a->header.frame_id = a_T_b.child_frame_id;
00253   b_T_a->child_frame_id = a_T_b.header.frame_id;
00254 }
00255 }  // namespace
00256 
00257 namespace tango_ros_native {
00258 TangoRosNodelet::TangoRosNodelet() : run_threads_(false), tango_config_(nullptr),
00259     t3dr_context_(nullptr), point_cloud_manager_(nullptr),
00260     image_buffer_manager_(nullptr), new_point_cloud_available_for_t3dr_(false) {}
00261 
00262 void TangoRosNodelet::onInit() {
00263   node_handle_ = getMTPrivateNodeHandle();
00264   const  uint32_t queue_size = 1;
00265   const bool latching = true;
00266   tango_status_publisher_ =
00267       node_handle_.advertise<std_msgs::Int8>(TANGO_STATUS_TOPIC_NAME,
00268                                              queue_size, latching);
00269   start_of_service_T_device_publisher_ =
00270       node_handle_.advertise<geometry_msgs::TransformStamped>(
00271           START_OF_SERVICE_T_DEVICE_TOPIC_NAME, queue_size, latching);
00272   area_description_T_start_of_service_publisher_ =
00273       node_handle_.advertise<geometry_msgs::TransformStamped>(
00274           AREA_DESCRIPTION_T_START_OF_SERVICE_TOPIC_NAME, queue_size, latching);
00275 
00276   image_transport_.reset(new image_transport::ImageTransport(node_handle_));
00277 
00278   static_occupancy_grid_publisher_ = node_handle_.advertise<nav_msgs::OccupancyGrid>(
00279       STATIC_OCCUPANCY_GRID_TOPIC_NAME, queue_size, latching);
00280 
00281   get_map_name_service_ = node_handle_.advertiseService<tango_ros_messages::GetMapName::Request,
00282       tango_ros_messages::GetMapName::Response>(GET_MAP_NAME_SERVICE_NAME,
00283                                              boost::bind(&TangoRosNodelet::GetMapNameServiceCallback, this, _1, _2));
00284 
00285   get_map_uuids_service_ = node_handle_.advertiseService<tango_ros_messages::GetMapUuids::Request,
00286       tango_ros_messages::GetMapUuids::Response>(GET_MAP_UUIDS_SERVICE_NAME,
00287                                              boost::bind(&TangoRosNodelet::GetMapUuidsServiceCallback, this, _1, _2));
00288 
00289   save_map_service_ = node_handle_.advertiseService<tango_ros_messages::SaveMap::Request,
00290       tango_ros_messages::SaveMap::Response>(SAVE_MAP_SERVICE_NAME,
00291                                              boost::bind(&TangoRosNodelet::SaveMapServiceCallback, this, _1, _2));
00292   load_occupancy_grid_service_ = node_handle_.advertiseService<tango_ros_messages::LoadOccupancyGrid::Request,
00293         tango_ros_messages::LoadOccupancyGrid::Response>(LOAD_OCCUPANCY_GRID_SERVICE_NAME,
00294                                                boost::bind(&TangoRosNodelet::LoadOccupancyGridServiceCallback, this, _1, _2));
00295 
00296   tango_connect_service_ = node_handle_.advertiseService<tango_ros_messages::TangoConnect::Request,
00297           tango_ros_messages::TangoConnect::Response>(
00298               CONNECT_SERVICE_NAME, boost::bind(
00299                   &TangoRosNodelet::TangoConnectServiceCallback, this, _1, _2));
00300 
00301   tango_status_ = TangoStatus::UNKNOWN;
00302 
00303   SetDefaultValueIfParamDoesNotExist(
00304       node_handle_, CREATE_NEW_MAP_PARAM_NAME, false);
00305   SetDefaultValueIfParamDoesNotExist(
00306       node_handle_, LOCALIZATION_MODE_PARAM_NAME, 2);
00307   SetDefaultValueIfParamDoesNotExist(
00308       node_handle_, LOCALIZATION_MAP_UUID_PARAM_NAME, "");
00309   SetDefaultValueIfParamDoesNotExist(
00310        node_handle_, OCCUPANCY_GRID_DIRECTORY_PARAM_NAME,
00311        OCCUPANCY_GRID_DEFAULT_DIRECTORY);
00312   SetDefaultValueIfParamDoesNotExist(
00313       node_handle_, DATASET_DIRECTORY_PARAM_NAME, DATASET_DEFAULT_DIRECTORY);
00314   SetDefaultValueIfParamDoesNotExist(
00315       node_handle_, DATASET_UUID_PARAM_NAME, "");
00316   SetDefaultValueIfParamDoesNotExist(node_handle_,
00317       tango_3d_reconstruction_helper::TANGO_3DR_RESOLUTION_PARAM_NAME,
00318       tango_3d_reconstruction_helper::TANGO_3DR_DEFAULT_RESOLUTION);
00319   SetDefaultValueIfParamDoesNotExist(node_handle_,
00320       tango_3d_reconstruction_helper::TANGO_3DR_USE_SPACE_CLEARING_PARAM_NAME,
00321       tango_3d_reconstruction_helper::TANGO_3DR_DEFAULT_USE_SPACE_CLEARING);
00322   SetDefaultValueIfParamDoesNotExist(node_handle_,
00323       tango_3d_reconstruction_helper::TANGO_3DR_MIN_NUM_VERTICES_PARAM_NAME,
00324       tango_3d_reconstruction_helper::TANGO_3DR_DEFAULT_MIN_NUM_VERTICES);
00325   SetDefaultValueIfParamDoesNotExist(node_handle_,
00326       tango_3d_reconstruction_helper::TANGO_3DR_UPDATE_METHOD_PARAM_NAME,
00327       tango_3d_reconstruction_helper::TANGO_3DR_DEFAULT_UPDATE_METHOD);
00328   SetDefaultValueIfParamDoesNotExist(node_handle_,
00329       tango_3d_reconstruction_helper::TANGO_3DR_MAX_VOXEL_WEIGHT_PARAM_NAME,
00330       tango_3d_reconstruction_helper::TANGO_3DR_DEFAULT_MAX_VOXEL_WEIGHT);
00331   SetDefaultValueIfParamDoesNotExist(node_handle_,
00332       tango_3d_reconstruction_helper::TANGO_3DR_FLOORPLAN_MAX_ERROR_PARAM_NAME,
00333       tango_3d_reconstruction_helper::TANGO_3DR_DEFAULT_FLOORPLAN_MAX_ERROR);
00334   SetDefaultValueIfParamDoesNotExist(
00335       node_handle_, USE_TF_STATIC_PARAM_NAME, true);
00336   GetParamValueAndSetDefaultValueIfParamDoesNotExist(
00337       node_handle_, PUBLISH_POSE_ON_TF_PARAM_NAME, true, publish_pose_on_tf_);
00338   GetParamValueAndSetDefaultValueIfParamDoesNotExist(
00339       node_handle_, ENABLE_DEPTH_PARAM_NAME, true, enable_depth_);
00340   GetParamValueAndSetDefaultValueIfParamDoesNotExist(
00341       node_handle_, ENABLE_COLOR_CAMERA_PARAM_NAME, true, enable_color_camera_);
00342   GetParamValueAndSetDefaultValueIfParamDoesNotExist(
00343       node_handle_, ENABLE_3DR_MESH_PARAM_NAME, true, enable_3dr_mesh_);
00344   GetParamValueAndSetDefaultValueIfParamDoesNotExist(
00345       node_handle_, ENABLE_3DR_OCCUPANCY_GRID_PARAM_NAME, true, enable_3dr_occupancy_grid_);
00346   int t3dr_occupancy_grid_threshold =
00347       tango_3d_reconstruction_helper::TANGO_3DR_OCCUPANCY_GRID_DEFAULT_THRESHOLD;
00348   GetParamValueAndSetDefaultValueIfParamDoesNotExist(node_handle_,
00349       tango_3d_reconstruction_helper::TANGO_3DR_OCCUPANCY_GRID_THRESHOLD_PARAM_NAME,
00350       static_cast<int>(
00351           tango_3d_reconstruction_helper::TANGO_3DR_OCCUPANCY_GRID_DEFAULT_THRESHOLD),
00352       t3dr_occupancy_grid_threshold);
00353   t3dr_occupancy_grid_threshold_ = t3dr_occupancy_grid_threshold;
00354   GetParamValueAndSetDefaultValueIfParamDoesNotExist(
00355       node_handle_, START_OF_SERVICE_FRAME_ID_PARAM_NAME,
00356       tango_ros_conversions_helper::toFrameId(TANGO_COORDINATE_FRAME_START_OF_SERVICE),
00357       start_of_service_frame_id_);
00358   GetParamValueAndSetDefaultValueIfParamDoesNotExist(
00359       node_handle_, AREA_DESCRIPTION_FRAME_ID_PARAM_NAME,
00360       tango_ros_conversions_helper::toFrameId(TANGO_COORDINATE_FRAME_AREA_DESCRIPTION),
00361       area_description_frame_id_);
00362 }
00363 
00364 TangoRosNodelet::~TangoRosNodelet() {
00365   TangoDisconnect();
00366 }
00367 
00368 TangoErrorType TangoRosNodelet::OnTangoServiceConnected() {
00369   const  uint32_t queue_size = 1;
00370   const bool latching = true;
00371   // Advertise topics that need depth camera and/or color camera only if cameras are enable.
00372   if (enable_depth_) {
00373     point_cloud_publisher_ =
00374         node_handle_.advertise<sensor_msgs::PointCloud2>(
00375             POINT_CLOUD_TOPIC_NAME, queue_size, latching);
00376     laser_scan_publisher_ =
00377         node_handle_.advertise<sensor_msgs::LaserScan>(
00378             LASER_SCAN_TOPIC_NAME, queue_size, latching);
00379   } else {
00380     point_cloud_publisher_.shutdown();
00381     laser_scan_publisher_.shutdown();
00382   }
00383   if (enable_color_camera_) {
00384     try {
00385       color_camera_publisher_ =
00386           image_transport_->advertiseCamera(COLOR_IMAGE_TOPIC_NAME,
00387                                             queue_size, latching);
00388       color_rectified_image_publisher_ =
00389           image_transport_->advertise(COLOR_RECTIFIED_IMAGE_TOPIC_NAME,
00390                                       queue_size, latching);
00391       } catch (const image_transport::Exception& e) {
00392         LOG(ERROR) << "Error while creating color image transport publishers" << e.what();
00393         return TANGO_ERROR;
00394       }
00395   } else {
00396     color_camera_publisher_.shutdown();
00397     color_rectified_image_publisher_.shutdown();
00398   }
00399   node_handle_.param<bool>(ENABLE_3DR_MESH_PARAM_NAME, enable_3dr_mesh_, true);
00400   if (enable_depth_ && enable_color_camera_ && enable_3dr_mesh_) {
00401     mesh_marker_publisher_ =
00402         node_handle_.advertise<visualization_msgs::MarkerArray>(
00403             COLOR_MESH_TOPIC_NAME, queue_size, latching);
00404   } else {
00405     mesh_marker_publisher_.shutdown();
00406   }
00407   node_handle_.param<bool>(ENABLE_3DR_OCCUPANCY_GRID_PARAM_NAME, enable_3dr_occupancy_grid_, true);
00408   if (enable_depth_ && enable_color_camera_ && enable_3dr_occupancy_grid_){
00409     occupancy_grid_publisher_ = node_handle_.advertise<nav_msgs::OccupancyGrid>(
00410         OCCUPANCY_GRID_TOPIC_NAME, queue_size, latching);
00411   } else {
00412     occupancy_grid_publisher_.shutdown();
00413   }
00414   if (fisheye_camera_available_) {
00415     try {
00416         fisheye_camera_publisher_ =
00417             image_transport_->advertiseCamera(FISHEYE_IMAGE_TOPIC_NAME,
00418                                               queue_size, latching);
00419         fisheye_rectified_image_publisher_ =
00420             image_transport_->advertise(FISHEYE_RECTIFIED_IMAGE_TOPIC_NAME,
00421                                        queue_size, latching);
00422       } catch (const image_transport::Exception& e) {
00423         LOG(ERROR) << "Error while creating fisheye image transport publishers" << e.what();
00424         return TANGO_ERROR;
00425       }
00426   } else {
00427     fisheye_camera_publisher_.shutdown();
00428     fisheye_rectified_image_publisher_.shutdown();
00429   }
00430 
00431   TangoCoordinateFramePair pair;
00432   pair.base = TANGO_COORDINATE_FRAME_START_OF_SERVICE;
00433   pair.target = TANGO_COORDINATE_FRAME_DEVICE;
00434   TangoPoseData pose;
00435   time_t current_time = time(NULL);
00436   time_t end = current_time + 10;
00437   while (current_time < end) {
00438     TangoService_getPoseAtTime(0.0, pair, &pose);
00439     if (pose.status_code == TANGO_POSE_VALID) {
00440       break;
00441     }
00442     sleep(1);
00443     current_time = time(NULL);
00444   }
00445   if (pose.status_code != TANGO_POSE_VALID) {
00446     LOG(ERROR) << "Error, could not get a first valid pose.";
00447     UpdateAndPublishTangoStatus(TangoStatus::NO_FIRST_VALID_POSE);
00448     return TANGO_INVALID;
00449   }
00450   time_offset_ = ros::Time::now().toSec() - GetBootTimeInSecond();
00451 
00452   TangoCameraIntrinsics tango_camera_intrinsics;
00453   TangoService_getCameraIntrinsics(TANGO_CAMERA_FISHEYE, &tango_camera_intrinsics);
00454   tango_ros_conversions_helper::toCameraInfo(tango_camera_intrinsics, &fisheye_camera_info_);
00455   fisheye_camera_info_manager_.reset(new camera_info_manager::CameraInfoManager(node_handle_));
00456   fisheye_camera_info_manager_->setCameraName("fisheye_1");
00457   // Cache warp maps for more efficiency.
00458   cv_warp_map_x_.create(fisheye_camera_info_.height, fisheye_camera_info_.width, CV_32FC1);
00459   cv_warp_map_y_.create(fisheye_camera_info_.height, fisheye_camera_info_.width, CV_32FC1);
00460   ComputeWarpMapsToRectifyFisheyeImage(fisheye_camera_info_, &cv_warp_map_x_, &cv_warp_map_y_);
00461   fisheye_image_rect_.create(fisheye_camera_info_.height, fisheye_camera_info_.width, CV_8UC1);
00462 
00463   TangoService_getCameraIntrinsics(TANGO_CAMERA_COLOR, &tango_camera_intrinsics);
00464   tango_ros_conversions_helper::toCameraInfo(tango_camera_intrinsics, &color_camera_info_);
00465   color_camera_info_manager_.reset(new camera_info_manager::CameraInfoManager(node_handle_));
00466   color_camera_info_manager_->setCameraName("color_1");
00467   // Cache camera model for more efficiency.
00468   color_camera_model_.fromCameraInfo(color_camera_info_);
00469 
00470   if (enable_3dr_mesh_ || enable_3dr_occupancy_grid_) {
00471     tango_ros_conversions_helper::toTango3DR_CameraCalibration(
00472         tango_camera_intrinsics, &t3dr_color_camera_intrinsics_);
00473     tango_3d_reconstruction_helper::TangoSetup3DRConfig(
00474         node_handle_, &t3dr_resolution_, &t3dr_context_, &t3dr_color_camera_intrinsics_);
00475   }
00476   return TANGO_SUCCESS;
00477 }
00478 
00479 TangoErrorType TangoRosNodelet::TangoSetupConfig() {
00480   const char* function_name = "TangoRosNodelet::TangoSetupConfig()";
00481 
00482   tango_config_ = TangoService_getConfig(TANGO_CONFIG_DEFAULT);
00483   if (tango_config_ == nullptr) {
00484     LOG(ERROR) << function_name << ", TangoService_getConfig error.";
00485     return TANGO_ERROR;
00486   }
00487 
00488   TangoErrorType result;
00489   const char* config_enable_motion_tracking = "config_enable_motion_tracking";
00490   result = TangoConfig_setBool(tango_config_, config_enable_motion_tracking, true);
00491   if(result != TANGO_SUCCESS) {
00492     LOG(ERROR) << function_name << ", TangoConfig_setBool "
00493         << config_enable_motion_tracking << " error: " << result;
00494     return result;
00495   }
00496   bool create_new_map;
00497   node_handle_.param(CREATE_NEW_MAP_PARAM_NAME, create_new_map, false);
00498   const char* config_enable_learning_mode = "config_enable_learning_mode";
00499   result = TangoConfig_setBool(tango_config_, config_enable_learning_mode, create_new_map);
00500   if (result != TANGO_SUCCESS) {
00501     LOG(ERROR) << function_name << ", TangoConfig_setBool "
00502         << config_enable_learning_mode << " error: " << result;
00503     return result;
00504   }
00505   if (!create_new_map) {
00506     bool enable_drift_correction = false;
00507     int localization_mode;
00508     node_handle_.param(LOCALIZATION_MODE_PARAM_NAME, localization_mode,
00509                        (int)LocalizationMode::ONLINE_SLAM);
00510     if (localization_mode == LocalizationMode::ONLINE_SLAM) {
00511       enable_drift_correction = true;
00512     }
00513     const char* config_enable_drift_correction = "config_enable_drift_correction";
00514     result = TangoConfig_setBool(tango_config_, config_enable_drift_correction, enable_drift_correction);
00515     if (result != TANGO_SUCCESS) {
00516       LOG(ERROR) << function_name << ", TangoConfig_setBool "
00517           << config_enable_drift_correction << " error: " << result;
00518       return result;
00519     }
00520     if (localization_mode == LocalizationMode::LOCALIZATION) {
00521       std::string map_uuid_to_load = "";
00522       node_handle_.param<std::string>(LOCALIZATION_MAP_UUID_PARAM_NAME, map_uuid_to_load, "");
00523       const char* config_load_area_description_UUID = "config_load_area_description_UUID";
00524       result = TangoConfig_setString(tango_config_, config_load_area_description_UUID, map_uuid_to_load.c_str());
00525       if (result != TANGO_SUCCESS) {
00526         LOG(ERROR) << function_name << ", TangoConfig_setString "
00527             << config_load_area_description_UUID << " error: " << result;
00528         return result;
00529       }
00530     }
00531   }
00532   const char* config_enable_auto_recovery = "config_enable_auto_recovery";
00533   result = TangoConfig_setBool(tango_config_, config_enable_auto_recovery, true);
00534   if (result != TANGO_SUCCESS) {
00535     LOG(ERROR) << function_name << ", TangoConfig_setBool "
00536         << config_enable_auto_recovery << " error: " << result;
00537     return result;
00538   }
00539   const char* config_enable_depth = "config_enable_depth";
00540   node_handle_.param<bool>(ENABLE_DEPTH_PARAM_NAME, enable_depth_, true);
00541   result = TangoConfig_setBool(tango_config_, config_enable_depth, enable_depth_);
00542   if (result != TANGO_SUCCESS) {
00543     LOG(ERROR) << function_name << ", TangoConfig_setBool "
00544         << config_enable_depth << " error: " << result;
00545     return result;
00546   }
00547   const char* config_depth_mode = "config_depth_mode";
00548   result = TangoConfig_setInt32(tango_config_, config_depth_mode, TANGO_POINTCLOUD_XYZC);
00549   if (result != TANGO_SUCCESS) {
00550     LOG(ERROR) << function_name << ", TangoConfig_setInt "
00551         << config_depth_mode << " error: " << result;
00552     return result;
00553   }
00554   const char* config_enable_color_camera = "config_enable_color_camera";
00555   node_handle_.param<bool>(ENABLE_COLOR_CAMERA_PARAM_NAME, enable_color_camera_, true);
00556   result = TangoConfig_setBool(tango_config_, config_enable_color_camera, enable_color_camera_);
00557   if (result != TANGO_SUCCESS) {
00558     LOG(ERROR) << function_name << ", TangoConfig_setBool "
00559         << config_enable_color_camera << " error: " << result;
00560     return result;
00561   }
00562   std::string datasets_path;
00563   node_handle_.param(DATASET_DIRECTORY_PARAM_NAME, datasets_path, DATASET_DEFAULT_DIRECTORY);
00564   const char* config_datasets_path = "config_datasets_path";
00565   result = TangoConfig_setString(tango_config_, config_datasets_path, datasets_path.c_str());
00566   if (result != TANGO_SUCCESS) {
00567     LOG(ERROR) << function_name << ", TangoConfig_setString "
00568                << config_datasets_path << " error: " << result;
00569     return result;
00570   }
00571   std::string dataset_uuid;
00572   node_handle_.param(DATASET_UUID_PARAM_NAME, dataset_uuid, std::string(""));
00573   const char* config_experimental_load_dataset_UUID = "config_experimental_load_dataset_UUID";
00574   result = TangoConfig_setString(tango_config_, config_experimental_load_dataset_UUID, dataset_uuid.c_str());
00575   if (result != TANGO_SUCCESS) {
00576     LOG(ERROR) << function_name << ", TangoConfig_setString "
00577                << config_experimental_load_dataset_UUID << " error: " << result;
00578     return result;
00579   }
00580   if (point_cloud_manager_ == nullptr) {
00581     int32_t max_point_cloud_elements;
00582     const char* config_max_point_cloud_elements = "max_point_cloud_elements";
00583     result = TangoConfig_getInt32(tango_config_, config_max_point_cloud_elements,
00584                                &max_point_cloud_elements);
00585     if (result != TANGO_SUCCESS) {
00586       LOG(ERROR) << function_name << ", Failed to query maximum number of point cloud elements.";
00587       return result;
00588     }
00589     result = TangoSupport_createPointCloudManager(max_point_cloud_elements, &point_cloud_manager_);
00590     if (result != TANGO_SUCCESS) {
00591       LOG(ERROR) << function_name << ", Failed to create point cloud manager.";
00592       return result;
00593     }
00594   }
00595   return TANGO_SUCCESS;
00596 }
00597 
00598 TangoErrorType TangoRosNodelet::TangoConnect() {
00599   const char* function_name = "TangoRosNodelet::TangoConnect()";
00600 
00601   TangoCoordinateFramePair pairs[2] = {
00602          {TANGO_COORDINATE_FRAME_START_OF_SERVICE,
00603           TANGO_COORDINATE_FRAME_DEVICE},
00604          {TANGO_COORDINATE_FRAME_AREA_DESCRIPTION,
00605           TANGO_COORDINATE_FRAME_START_OF_SERVICE}};
00606   TangoErrorType result =
00607       TangoService_connectOnPoseAvailable(2, pairs, OnPoseAvailableRouter);
00608   if (result != TANGO_SUCCESS) {
00609     LOG(ERROR) << function_name
00610         << ", TangoService_connectOnPoseAvailable error: " << result;
00611     return result;
00612   }
00613 
00614   result = TangoService_connectOnPointCloudAvailable(OnPointCloudAvailableRouter);
00615   if (result != TANGO_SUCCESS) {
00616     LOG(ERROR) << function_name
00617         << ", TangoService_connectOnPointCloudAvailable error: " << result;
00618     return result;
00619   }
00620 
00621   int android_api_level;
00622   node_handle_.param("android_api_level", android_api_level, 0);
00623   if (android_api_level < 24) {
00624     result = TangoService_connectOnFrameAvailable(
00625         TANGO_CAMERA_FISHEYE, this, OnFrameAvailableRouter);
00626     if (result != TANGO_SUCCESS) {
00627       LOG(ERROR) << function_name
00628           << ", TangoService_connectOnFrameAvailable TANGO_CAMERA_FISHEYE error: " << result;
00629       return result;
00630     }
00631   } else {
00632     LOG(WARNING) << "Android API Level is 24 or more, Fisheye camera data is not available";
00633     fisheye_camera_available_ = false;
00634   }
00635 
00636   result = TangoService_connectOnFrameAvailable(
00637       TANGO_CAMERA_COLOR, this, OnFrameAvailableRouter);
00638   if (result != TANGO_SUCCESS) {
00639     LOG(ERROR) << function_name
00640         << ", TangoService_connectOnFrameAvailable TANGO_CAMERA_COLOR error: " << result;
00641     return result;
00642   }
00643 
00644   result = TangoService_connect(this, tango_config_);
00645   if (result != TANGO_SUCCESS) {
00646     LOG(ERROR) << function_name << ", TangoService_connect error: " << result;
00647     return result;
00648   }
00649   return TANGO_SUCCESS;
00650 }
00651 
00652 void TangoRosNodelet::UpdateAndPublishTangoStatus(const TangoStatus& status) {
00653   tango_status_ = status;
00654   std_msgs::Int8 tango_status_msg;
00655   tango_status_msg.data = static_cast<int>(tango_status_);
00656   tango_status_publisher_.publish(tango_status_msg);
00657 }
00658 
00659 TangoErrorType TangoRosNodelet::ConnectToTangoAndSetUpNode() {
00660   if (tango_status_ == TangoStatus::SERVICE_CONNECTED) {
00661     // Connecting twice to Tango results in TANGO_ERROR. Early return to avoid
00662     // this.
00663     LOG(WARNING) << "Already connected to Tango service.";
00664     UpdateAndPublishTangoStatus(TangoStatus::SERVICE_CONNECTED);
00665     return TANGO_SUCCESS;
00666   }
00667   UpdateAndPublishTangoStatus(TangoStatus::UNKNOWN);
00668 
00669   TangoErrorType success;
00670   // Setup config.
00671   success = TangoSetupConfig();
00672   // Early return if config setup failed.
00673   if (success != TANGO_SUCCESS) {
00674     UpdateAndPublishTangoStatus(TangoStatus::SERVICE_NOT_CONNECTED);
00675     return success;
00676   }
00677   // Connect to Tango.
00678   success = TangoConnect();
00679   // Early return if Tango connect call failed.
00680   if (success != TANGO_SUCCESS) {
00681     UpdateAndPublishTangoStatus(TangoStatus::SERVICE_NOT_CONNECTED);
00682     return success;
00683   }
00684   // Publish static transforms.
00685   node_handle_.param(USE_TF_STATIC_PARAM_NAME, use_tf_static_, true);
00686   PublishStaticTransforms();
00687   success = OnTangoServiceConnected();
00688   if (success != TANGO_SUCCESS) {
00689     return success;
00690   }
00691   area_description_T_start_of_service_.child_frame_id = "";
00692   tango_data_available_ = true;
00693   // Create publishing threads.
00694   StartPublishing();
00695   UpdateAndPublishTangoStatus(TangoStatus::SERVICE_CONNECTED);
00696   return success;
00697 }
00698 
00699 void TangoRosNodelet::TangoDisconnect() {
00700   StopPublishing();
00701   if (tango_config_ != nullptr) {
00702     TangoConfig_free(tango_config_);
00703     tango_config_ = nullptr;
00704   }
00705   if (point_cloud_manager_ != nullptr) {
00706     TangoSupport_freePointCloudManager(point_cloud_manager_);
00707     point_cloud_manager_ = nullptr;
00708   }
00709   if (image_buffer_manager_ != nullptr) {
00710     TangoSupport_freeImageBufferManager(image_buffer_manager_);
00711     image_buffer_manager_ = nullptr;
00712   }
00713   if (t3dr_context_ != nullptr) {
00714     Tango3DR_clear(t3dr_context_);
00715     Tango3DR_ReconstructionContext_destroy(t3dr_context_);
00716     t3dr_context_ = nullptr;
00717   }
00718   TangoService_disconnect();
00719   UpdateAndPublishTangoStatus(TangoStatus::SERVICE_NOT_CONNECTED);
00720 }
00721 
00722 void TangoRosNodelet::PublishStaticTransforms() {
00723   std::vector<geometry_msgs::TransformStamped> tf_transforms;
00724   tf_transforms.reserve(NUMBER_OF_STATIC_TRANSFORMS);
00725   TangoCoordinateFramePair pair;
00726   TangoPoseData pose;
00727 
00728   pair.base = TANGO_COORDINATE_FRAME_DEVICE;
00729   pair.target = TANGO_COORDINATE_FRAME_IMU;
00730   TangoService_getPoseAtTime(0.0, pair, &pose);
00731   geometry_msgs::TransformStamped device_T_imu;
00732   tango_ros_conversions_helper::toTransformStamped(
00733       pose, time_offset_,
00734       tango_ros_conversions_helper::toFrameId(TANGO_COORDINATE_FRAME_DEVICE),
00735       tango_ros_conversions_helper::toFrameId(TANGO_COORDINATE_FRAME_IMU),
00736       &device_T_imu);
00737   tf_transforms.push_back(device_T_imu);
00738 
00739   pair.base = TANGO_COORDINATE_FRAME_DEVICE;
00740   pair.target = TANGO_COORDINATE_FRAME_CAMERA_DEPTH;
00741   TangoService_getPoseAtTime(0.0, pair, &pose);
00742   tango_ros_conversions_helper::toTransformStamped(
00743       pose, time_offset_,
00744       tango_ros_conversions_helper::toFrameId(TANGO_COORDINATE_FRAME_DEVICE),
00745       tango_ros_conversions_helper::toFrameId(TANGO_COORDINATE_FRAME_CAMERA_DEPTH),
00746       &device_T_camera_depth_);
00747   tf_transforms.push_back(device_T_camera_depth_);
00748 
00749   // According to the ROS documentation, laser scan angles are measured around
00750   // the Z-axis in the laser scan frame. To follow this convention the laser
00751   // scan frame has to be rotated of 90 degrees around x axis with respect to
00752   // the Tango point cloud frame.
00753   camera_depth_T_laser_ = tf::StampedTransform(
00754       tf::Transform(tf::Quaternion(1 / sqrt(2), 0, 0, 1 / sqrt(2))), ros::Time::now(),
00755       tango_ros_conversions_helper::toFrameId(TANGO_COORDINATE_FRAME_CAMERA_DEPTH),
00756       LASER_SCAN_FRAME_ID);
00757   geometry_msgs::TransformStamped camera_depth_T_laser_message;
00758   tf::transformStampedTFToMsg(camera_depth_T_laser_, camera_depth_T_laser_message);
00759   tf_transforms.push_back(camera_depth_T_laser_message);
00760 
00761   pair.base = TANGO_COORDINATE_FRAME_DEVICE;
00762   pair.target = TANGO_COORDINATE_FRAME_CAMERA_FISHEYE;
00763   TangoService_getPoseAtTime(0.0, pair, &pose);
00764   tango_ros_conversions_helper::toTransformStamped(
00765       pose, time_offset_,
00766       tango_ros_conversions_helper::toFrameId(TANGO_COORDINATE_FRAME_DEVICE),
00767       tango_ros_conversions_helper::toFrameId(TANGO_COORDINATE_FRAME_CAMERA_FISHEYE),
00768       &device_T_camera_fisheye_);
00769   tf_transforms.push_back(device_T_camera_fisheye_);
00770 
00771   pair.base = TANGO_COORDINATE_FRAME_DEVICE;
00772   pair.target = TANGO_COORDINATE_FRAME_CAMERA_COLOR;
00773   TangoService_getPoseAtTime(0.0, pair, &pose);
00774   tango_ros_conversions_helper::toTransformStamped(
00775       pose, time_offset_,
00776       tango_ros_conversions_helper::toFrameId(TANGO_COORDINATE_FRAME_DEVICE),
00777       tango_ros_conversions_helper::toFrameId(TANGO_COORDINATE_FRAME_CAMERA_COLOR),
00778       &device_T_camera_color_);
00779   tf_transforms.push_back(device_T_camera_color_);
00780 
00781   if (use_tf_static_) {
00782     tf_static_broadcaster_.sendTransform(tf_transforms);
00783   } else {
00784     tf_broadcaster_.sendTransform(tf_transforms);
00785   }
00786 }
00787 
00788 void TangoRosNodelet::OnPoseAvailable(const TangoPoseData* pose) {
00789   if (pose->frame.base == TANGO_COORDINATE_FRAME_START_OF_SERVICE &&
00790       pose->frame.target == TANGO_COORDINATE_FRAME_DEVICE) {
00791     if (pose->status_code == TANGO_POSE_VALID &&
00792         device_pose_thread_.data_available_mutex.try_lock()) {
00793       tango_ros_conversions_helper::toTransformStamped(
00794           *pose, time_offset_, start_of_service_frame_id_,
00795           tango_ros_conversions_helper::toFrameId(TANGO_COORDINATE_FRAME_DEVICE),
00796           &start_of_service_T_device_);
00797       device_pose_thread_.data_available_mutex.unlock();
00798       device_pose_thread_.data_available.notify_all();
00799     }
00800   } else if (pose->frame.base == TANGO_COORDINATE_FRAME_AREA_DESCRIPTION &&
00801       pose->frame.target == TANGO_COORDINATE_FRAME_START_OF_SERVICE) {
00802     if (pose->status_code == TANGO_POSE_VALID &&
00803         device_pose_thread_.data_available_mutex.try_lock()) {
00804       tango_ros_conversions_helper::toTransformStamped(
00805           *pose, time_offset_, area_description_frame_id_,
00806           start_of_service_frame_id_, &area_description_T_start_of_service_);
00807       device_pose_thread_.data_available_mutex.unlock();
00808       device_pose_thread_.data_available.notify_all();
00809     }
00810   }
00811 }
00812 
00813 void TangoRosNodelet::OnPointCloudAvailable(const TangoPointCloud* point_cloud) {
00814   if (point_cloud->num_points > 0) {
00815     if (point_cloud_publisher_.getNumSubscribers() > 0 &&
00816         point_cloud_thread_.data_available_mutex.try_lock()) {
00817       tango_ros_conversions_helper::toPointCloud2(*point_cloud, time_offset_,
00818                                                   &point_cloud_);
00819       point_cloud_.header.frame_id =
00820           tango_ros_conversions_helper::toFrameId(TANGO_COORDINATE_FRAME_CAMERA_DEPTH);
00821       point_cloud_thread_.data_available_mutex.unlock();
00822       point_cloud_thread_.data_available.notify_all();
00823     }
00824     if (laser_scan_publisher_.getNumSubscribers() > 0 &&
00825         laser_scan_thread_.data_available_mutex.try_lock()) {
00826       laser_scan_.angle_min = LASER_SCAN_ANGLE_MIN;
00827       laser_scan_.angle_max = LASER_SCAN_ANGLE_MAX;
00828       laser_scan_.angle_increment = LASER_SCAN_ANGLE_INCREMENT;
00829       laser_scan_.time_increment = LASER_SCAN_TIME_INCREMENT;
00830       laser_scan_.scan_time = LASER_SCAN_SCAN_TIME;
00831       laser_scan_.range_min = laser_scan_min_range_;
00832       laser_scan_.range_max = laser_scan_max_range_;
00833       // Determine amount of rays to create.
00834       uint32_t ranges_size = std::ceil((laser_scan_.angle_max - laser_scan_.angle_min)
00835                                        / laser_scan_.angle_increment);
00836       // Laser scan rays with no obstacle data will evaluate to infinity.
00837       laser_scan_.ranges.assign(ranges_size, std::numeric_limits<double>::infinity());
00838       tango_ros_conversions_helper::toLaserScan(*point_cloud, time_offset_, 
00839         laser_scan_min_height_, laser_scan_max_height_, laser_scan_min_range_,
00840         laser_scan_max_range_, camera_depth_T_laser_, &laser_scan_);
00841       laser_scan_.header.frame_id = LASER_SCAN_FRAME_ID;
00842       laser_scan_thread_.data_available_mutex.unlock();
00843       laser_scan_thread_.data_available.notify_all();
00844     }
00845 
00846     if (enable_3dr_mesh_ || enable_3dr_occupancy_grid_) {
00847       TangoCoordinateFramePair pair;
00848       pair.base = TANGO_COORDINATE_FRAME_START_OF_SERVICE;
00849       pair.target = TANGO_COORDINATE_FRAME_CAMERA_DEPTH;
00850       TangoPoseData start_of_service_T_camera_depth;
00851       TangoService_getPoseAtTime(point_cloud->timestamp, pair, &start_of_service_T_camera_depth);
00852       if (start_of_service_T_camera_depth.status_code != TANGO_POSE_VALID) {
00853         LOG(WARNING) << "Could not find a valid pose at time "
00854             << point_cloud->timestamp << " for the depth camera.";
00855         return;
00856       }
00857       tango_ros_conversions_helper::toTango3DR_Pose(start_of_service_T_camera_depth,
00858                                             &last_camera_depth_pose_);
00859       TangoSupport_updatePointCloud(point_cloud_manager_, point_cloud);
00860       new_point_cloud_available_for_t3dr_ = true;
00861     }
00862   }
00863 }
00864 
00865 void TangoRosNodelet::OnFrameAvailable(TangoCameraId camera_id, const TangoImageBuffer* buffer) {
00866   if (fisheye_camera_publisher_.getNumSubscribers() > 0 &&
00867        camera_id == TangoCameraId::TANGO_CAMERA_FISHEYE &&
00868        fisheye_image_thread_.data_available_mutex.try_lock()) {
00869     fisheye_image_ = cv::Mat(buffer->height + buffer->height / 2, buffer->width,
00870                              CV_8UC1, buffer->data, buffer->stride); // No deep copy.
00871     fisheye_image_header_.stamp.fromSec(buffer->timestamp + time_offset_);
00872     fisheye_image_header_.seq = buffer->frame_number;
00873     fisheye_image_header_.frame_id =
00874         tango_ros_conversions_helper::toFrameId(TANGO_COORDINATE_FRAME_CAMERA_FISHEYE);
00875     fisheye_image_thread_.data_available_mutex.unlock();
00876     fisheye_image_thread_.data_available.notify_all();
00877   }
00878   if (color_camera_publisher_.getNumSubscribers() > 0 &&
00879        camera_id == TangoCameraId::TANGO_CAMERA_COLOR &&
00880        color_image_thread_.data_available_mutex.try_lock()) {
00881     color_image_ = cv::Mat(buffer->height + buffer->height / 2, buffer->width,
00882                            CV_8UC1, buffer->data, buffer->stride); // No deep copy.
00883     color_image_header_.stamp.fromSec(buffer->timestamp + time_offset_);
00884     color_image_header_.seq = buffer->frame_number;
00885     color_image_header_.frame_id =
00886         tango_ros_conversions_helper::toFrameId(TANGO_COORDINATE_FRAME_CAMERA_COLOR);
00887     color_image_thread_.data_available_mutex.unlock();
00888     color_image_thread_.data_available.notify_all();
00889   }
00890 
00891   if ((enable_3dr_mesh_ || enable_3dr_occupancy_grid_) &&
00892       camera_id == TangoCameraId::TANGO_CAMERA_COLOR &&
00893       new_point_cloud_available_for_t3dr_ &&
00894       mesh_thread_.data_available_mutex.try_lock()) {
00895     if (image_buffer_manager_ == nullptr) {
00896       TangoErrorType result = TangoSupport_createImageBufferManager(
00897           buffer->format, buffer->width, buffer->height, &image_buffer_manager_);
00898       if (result != TANGO_SUCCESS) {
00899         LOG(ERROR) << "Failed to create image buffer manager.";
00900         return;
00901       }
00902     }
00903     TangoCoordinateFramePair pair;
00904     pair.base = TANGO_COORDINATE_FRAME_START_OF_SERVICE;
00905     pair.target = TANGO_COORDINATE_FRAME_CAMERA_COLOR;
00906     TangoPoseData start_of_service_T_camera_color;
00907     TangoService_getPoseAtTime(buffer->timestamp, pair, &start_of_service_T_camera_color);
00908     if (start_of_service_T_camera_color.status_code != TANGO_POSE_VALID) {
00909       LOG(WARNING) << "Could not find a valid pose at time "
00910           << buffer->timestamp << " for the color camera.";
00911       return;
00912     }
00913     tango_ros_conversions_helper::toTango3DR_Pose(start_of_service_T_camera_color,
00914                                           &last_camera_color_pose_);
00915     TangoSupport_updateImageBuffer(image_buffer_manager_, buffer);
00916     new_point_cloud_available_for_t3dr_ = false;
00917     mesh_thread_.data_available_mutex.unlock();
00918     mesh_thread_.data_available.notify_all();
00919   }
00920 }
00921 
00922 void TangoRosNodelet::StartPublishing() {
00923   run_threads_ = true;
00924   device_pose_thread_.publish_thread =
00925       std::thread(&TangoRosNodelet::PublishDevicePose, this);
00926   point_cloud_thread_.publish_thread =
00927       std::thread(&TangoRosNodelet::PublishPointCloud, this);
00928   laser_scan_thread_.publish_thread =
00929       std::thread(&TangoRosNodelet::PublishLaserScan, this);
00930   fisheye_image_thread_.publish_thread =
00931       std::thread(&TangoRosNodelet::PublishFisheyeImage, this);
00932   color_image_thread_.publish_thread =
00933       std::thread(&TangoRosNodelet::PublishColorImage, this);
00934   mesh_thread_.publish_thread =
00935       std::thread(&TangoRosNodelet::PublishMesh, this);
00936   ros_spin_thread_ = std::thread(&TangoRosNodelet::RunRosSpin, this);
00937 }
00938 
00939 void TangoRosNodelet::StopPublishing() {
00940   if (run_threads_) {
00941     run_threads_ = false;
00942     if (device_pose_thread_.publish_thread.joinable()) {
00943       if (!tango_data_available_) {
00944         device_pose_thread_.data_available.notify_all();
00945       }
00946       device_pose_thread_.publish_thread.join();
00947     }
00948     if (point_cloud_thread_.publish_thread.joinable()) {
00949       if (!tango_data_available_ || !enable_depth_ ||
00950           point_cloud_publisher_.getNumSubscribers() <= 0) {
00951         point_cloud_thread_.data_available.notify_all();
00952       }
00953       point_cloud_thread_.publish_thread.join();
00954     }
00955     if (laser_scan_thread_.publish_thread.joinable()) {
00956       if (!tango_data_available_ || !enable_depth_ ||
00957           laser_scan_publisher_.getNumSubscribers() <= 0) {
00958         laser_scan_thread_.data_available.notify_all();
00959       }
00960       laser_scan_thread_.publish_thread.join();
00961     }
00962     if (fisheye_image_thread_.publish_thread.joinable()) {
00963       if (!tango_data_available_ || !fisheye_camera_available_ ||
00964           fisheye_camera_publisher_.getNumSubscribers() <= 0) {
00965         fisheye_image_thread_.data_available.notify_all();
00966       }
00967       fisheye_image_thread_.publish_thread.join();
00968     }
00969     if (color_image_thread_.publish_thread.joinable()) {
00970       if (!tango_data_available_ || !enable_color_camera_ ||
00971           color_camera_publisher_.getNumSubscribers() <= 0) {
00972         color_image_thread_.data_available.notify_all();
00973       }
00974       color_image_thread_.publish_thread.join();
00975     }
00976     if (mesh_thread_.publish_thread.joinable()) {
00977       if (!tango_data_available_ || !enable_depth_ || !enable_color_camera_ ||
00978           (!enable_3dr_mesh_ && !enable_3dr_occupancy_grid_)) {
00979         mesh_thread_.data_available.notify_all();
00980       }
00981       mesh_thread_.publish_thread.join();
00982     }
00983     ros_spin_thread_.join();
00984   }
00985 }
00986 
00987 void TangoRosNodelet::PublishDevicePose() {
00988   while(ros::ok()) {
00989     if (!run_threads_) {
00990       break;
00991     }
00992     {
00993       std::unique_lock<std::mutex> lock(device_pose_thread_.data_available_mutex);
00994       device_pose_thread_.data_available.wait(lock);
00995       if (!use_tf_static_) {
00996         PublishStaticTransforms();
00997       }
00998       if (publish_pose_on_tf_) {
00999         tf_broadcaster_.sendTransform(start_of_service_T_device_);
01000         if (area_description_T_start_of_service_.child_frame_id != "") {
01001           // This transform can be empty. Don't publish it in this case.
01002           tf_static_broadcaster_.sendTransform(area_description_T_start_of_service_);
01003         }
01004       }
01005       if (start_of_service_T_device_publisher_.getNumSubscribers() > 0) {
01006         start_of_service_T_device_publisher_.publish(start_of_service_T_device_);
01007       }
01008       if (area_description_T_start_of_service_publisher_.getNumSubscribers() > 0 &&
01009           area_description_T_start_of_service_.child_frame_id != "") {
01010         // This transform can be empty. Don't publish it in this case.
01011         area_description_T_start_of_service_publisher_.publish(area_description_T_start_of_service_);
01012       }
01013     }
01014   }
01015 }
01016 
01017 void TangoRosNodelet::PublishPointCloud() {
01018   while(ros::ok()) {
01019     if (!run_threads_) {
01020       break;
01021     }
01022     {
01023       std::unique_lock<std::mutex> lock(point_cloud_thread_.data_available_mutex);
01024       point_cloud_thread_.data_available.wait(lock);
01025       if (point_cloud_publisher_.getNumSubscribers() > 0) {
01026         point_cloud_publisher_.publish(point_cloud_);
01027       }
01028     }
01029   }
01030 }
01031 
01032 void TangoRosNodelet::PublishLaserScan() {
01033   while(ros::ok()) {
01034     if (!run_threads_) {
01035       break;
01036     }
01037     {
01038       std::unique_lock<std::mutex> lock(laser_scan_thread_.data_available_mutex);
01039       laser_scan_thread_.data_available.wait(lock);
01040       if (laser_scan_publisher_.getNumSubscribers() > 0) {
01041         laser_scan_publisher_.publish(laser_scan_);
01042       }
01043     }
01044   }
01045 }
01046 
01047 void TangoRosNodelet::PublishFisheyeImage() {
01048   while(ros::ok()) {
01049     if (!run_threads_) {
01050       break;
01051     }
01052     {
01053       std::unique_lock<std::mutex> lock(fisheye_image_thread_.data_available_mutex);
01054       fisheye_image_thread_.data_available.wait(lock);
01055       if (fisheye_camera_publisher_.getNumSubscribers() > 0 ||
01056           fisheye_rectified_image_publisher_.getNumSubscribers() > 0) {
01057         // The Tango image encoding is not supported by ROS.
01058         // We need to convert it to gray.
01059         cv::Mat fisheye_image_gray;
01060         cv::cvtColor(fisheye_image_, fisheye_image_gray, cv::COLOR_YUV420sp2GRAY);
01061         cv_bridge::CvImage cv_bridge_fisheye_image;
01062         cv_bridge_fisheye_image.header = fisheye_image_header_;
01063         cv_bridge_fisheye_image.encoding = sensor_msgs::image_encodings::MONO8;
01064         cv_bridge_fisheye_image.image = fisheye_image_gray;
01065         fisheye_camera_info_.header = fisheye_image_header_;
01066         fisheye_camera_info_manager_->setCameraInfo(fisheye_camera_info_);
01067         sensor_msgs::Image fisheye_image_msg;
01068         cv_bridge_fisheye_image.toImageMsg(fisheye_image_msg);
01069         fisheye_camera_publisher_.publish(fisheye_image_msg, fisheye_camera_info_);
01070 
01071         if (fisheye_rectified_image_publisher_.getNumSubscribers() > 0) {
01072           cv::remap(fisheye_image_gray, fisheye_image_rect_, cv_warp_map_x_,
01073                     cv_warp_map_y_, cv::INTER_LINEAR, cv::BORDER_CONSTANT, 0);
01074           sensor_msgs::ImagePtr image_rect = cv_bridge::CvImage(
01075               cv_bridge_fisheye_image.header, cv_bridge_fisheye_image.encoding,
01076               fisheye_image_rect_).toImageMsg();
01077           fisheye_rectified_image_publisher_.publish(image_rect);
01078         }
01079       }
01080     }
01081   }
01082 }
01083 
01084 void TangoRosNodelet::PublishColorImage() {
01085   while(ros::ok()) {
01086     if (!run_threads_) {
01087       break;
01088     }
01089     {
01090       std::unique_lock<std::mutex> lock(color_image_thread_.data_available_mutex);
01091       color_image_thread_.data_available.wait(lock);
01092       if (color_camera_publisher_.getNumSubscribers() > 0 ||
01093           color_rectified_image_publisher_.getNumSubscribers() > 0) {
01094         // The Tango image encoding is not supported by ROS.
01095         // We need to convert it to RGB.
01096         cv::Mat color_image_rgb;
01097         cv::cvtColor(color_image_, color_image_rgb, cv::COLOR_YUV420sp2RGBA);
01098         cv_bridge::CvImage cv_bridge_color_image;
01099         cv_bridge_color_image.header = color_image_header_;
01100         cv_bridge_color_image.encoding = sensor_msgs::image_encodings::RGBA8;
01101         cv_bridge_color_image.image = color_image_rgb;
01102         color_camera_info_.header = color_image_header_;
01103         color_camera_info_manager_->setCameraInfo(color_camera_info_);
01104         sensor_msgs::Image color_image_msg;
01105         cv_bridge_color_image.toImageMsg(color_image_msg);
01106         color_camera_publisher_.publish(color_image_msg, color_camera_info_);
01107 
01108         if (color_rectified_image_publisher_.getNumSubscribers() > 0) {
01109           color_camera_model_.rectifyImage(color_image_rgb, color_image_rect_);
01110           sensor_msgs::ImagePtr image_rect = cv_bridge::CvImage(
01111               cv_bridge_color_image.header, cv_bridge_color_image.encoding,
01112               color_image_rect_).toImageMsg();
01113           color_rectified_image_publisher_.publish(image_rect);
01114         }
01115       }
01116     }
01117   }
01118 }
01119 
01120 void TangoRosNodelet::PublishMesh() {
01121   while(ros::ok()) {
01122     if (!run_threads_) {
01123       break;
01124     }
01125     if (enable_3dr_mesh_ || enable_3dr_occupancy_grid_) {
01126       Tango3DR_GridIndexArray t3dr_updated_indices;
01127       // Update Tango mesh with latest point cloud and color image.
01128       {
01129         std::unique_lock<std::mutex> lock(mesh_thread_.data_available_mutex);
01130         mesh_thread_.data_available.wait(lock);
01131         tango_3d_reconstruction_helper::UpdateMesh(
01132             t3dr_context_, point_cloud_manager_, image_buffer_manager_,
01133             &last_camera_depth_pose_, &last_camera_color_pose_,
01134             &t3dr_updated_indices);
01135       }
01136       // Publish Tango mesh as visualization marker.
01137       if (enable_3dr_mesh_ && mesh_marker_publisher_.getNumSubscribers() > 0) {
01138         visualization_msgs::MarkerArray mesh_marker_array;
01139         tango_3d_reconstruction_helper::ExtractMeshAndConvertToMarkerArray(
01140             t3dr_context_, t3dr_updated_indices, time_offset_,
01141             start_of_service_frame_id_, &mesh_marker_array);
01142         mesh_marker_publisher_.publish(mesh_marker_array);
01143         Tango3DR_Status result = Tango3DR_GridIndexArray_destroy(
01144             &t3dr_updated_indices);
01145         if (result != TANGO_3DR_SUCCESS) {
01146           LOG(ERROR) << "Tango3DR_GridIndexArray_destroy failed with error code: "
01147               << result;
01148         }
01149         if (mesh_marker_array.markers.empty()) {
01150           LOG(INFO) << "Empty mesh array!";
01151         }
01152       }
01153       // Publish Tango mesh as occupancy grid.
01154       if (enable_3dr_occupancy_grid_) {
01155         occupancy_grid_.data.clear();
01156         // We extract the floor plan and convert it to occupancy grid even if
01157         // there is no subscriber for the occupancy grid topic. That way we
01158         // avoid saving an empty occupancy grid.
01159         if (tango_3d_reconstruction_helper::ExtractFloorPlanImageAndConvertToOccupancyGrid(
01160             t3dr_context_, time_offset_, start_of_service_frame_id_,
01161             t3dr_resolution_, t3dr_occupancy_grid_threshold_, &occupancy_grid_) &&
01162             occupancy_grid_publisher_.getNumSubscribers() > 0)
01163           occupancy_grid_publisher_.publish(occupancy_grid_);
01164       }
01165     }
01166   }
01167 }
01168 
01169 void TangoRosNodelet::DynamicReconfigureCallback(PublisherConfig &config, uint32_t level) {
01170   laser_scan_max_height_ = config.laser_scan_max_height;
01171   laser_scan_min_height_ = config.laser_scan_min_height;
01172   laser_scan_min_range_ = config.laser_scan_min_range;
01173   laser_scan_max_range_ = config.laser_scan_max_range;
01174   LOG(INFO) << "laser_scan_min_height [m]: " << laser_scan_min_height_ << std::endl;
01175   LOG(INFO) << "laser_scan_max_height [m]: " << laser_scan_max_height_ << std::endl;
01176   LOG(INFO) << "laser_scan_min_range [m]: " << laser_scan_min_range_ << std::endl;
01177   LOG(INFO) << "laser_scan_max_range [m]: " << laser_scan_max_range_ << std::endl;
01178 }
01179 
01180 void TangoRosNodelet::RunRosSpin() {
01181   dynamic_reconfigure::Server<tango_ros_native::PublisherConfig> server;
01182   dynamic_reconfigure::Server<tango_ros_native::PublisherConfig>::CallbackType callback =
01183       boost::bind(&TangoRosNodelet::DynamicReconfigureCallback, this, _1, _2);
01184   server.setCallback(callback);
01185   while(ros::ok()) {
01186     if (!run_threads_) {
01187       break;
01188     }
01189     ros::spinOnce();
01190     std::this_thread::sleep_for(std::chrono::milliseconds(100));
01191   }
01192 }
01193 
01194 bool TangoRosNodelet::TangoConnectServiceCallback(
01195     const tango_ros_messages::TangoConnect::Request& request,
01196     tango_ros_messages::TangoConnect::Response& response) {
01197   switch (request.request) {
01198     case tango_ros_messages::TangoConnect::Request::CONNECT:
01199       // Connect to Tango Service.
01200       response.response = ConnectToTangoAndSetUpNode();
01201       break;
01202     case tango_ros_messages::TangoConnect::Request::DISCONNECT:
01203       // Disconnect from Tango Service.
01204       TangoDisconnect();
01205       response.response = tango_ros_messages::TangoConnect::Response::TANGO_SUCCESS;
01206       break;
01207     case tango_ros_messages::TangoConnect::Request::RECONNECT:
01208       // Disconnect and reconnect to Tango Service.
01209       TangoDisconnect();
01210       response.response = ConnectToTangoAndSetUpNode();
01211       break;
01212     default:
01213       LOG(ERROR) << "Did not understand request " << static_cast<int>(request.request)
01214                  << ", valid requests are (CONNECT: "
01215                  << tango_ros_messages::TangoConnect::Request::CONNECT
01216                  << ", DISCONNECT: "
01217                  << tango_ros_messages::TangoConnect::Request::DISCONNECT
01218                  << ", RECONNECT: "
01219                  << tango_ros_messages::TangoConnect::Request::RECONNECT
01220                  << ")";
01221       response.message = "Did not understand request.";
01222       return true;
01223     }
01224     return true;
01225 }
01226 
01227 bool TangoRosNodelet::GetMapNameServiceCallback(
01228     const tango_ros_messages::GetMapName::Request &req,
01229     tango_ros_messages::GetMapName::Response &res) {
01230   return GetMapNameFromUuid(req.map_uuid, res.map_name);
01231 }
01232 
01233 bool TangoRosNodelet::GetMapUuidsServiceCallback(
01234     const tango_ros_messages::GetMapUuids::Request& req,
01235     tango_ros_messages::GetMapUuids::Response& res) {
01236   if (!GetAvailableMapUuidsList(res.map_uuids) ) return false;
01237 
01238   res.map_names.resize(res.map_uuids.size());
01239   auto map_uuids_it = res.map_uuids.begin();
01240   auto map_names_it = res.map_names.begin();
01241   for (; map_uuids_it != res.map_uuids.end() && map_names_it != res.map_names.end();
01242        ++map_uuids_it, ++map_names_it) {
01243     if (!GetMapNameFromUuid(*map_uuids_it, *map_names_it)) return false;
01244   }
01245   return true;
01246 }
01247 
01248 bool TangoRosNodelet::SaveMapServiceCallback(
01249     const tango_ros_messages::SaveMap::Request& req,
01250     tango_ros_messages::SaveMap::Response& res) {
01251   bool save_localization_map = req.request & tango_ros_messages::SaveMap::Request::SAVE_LOCALIZATION_MAP;
01252   bool save_occupancy_grid = req.request & tango_ros_messages::SaveMap::Request::SAVE_OCCUPANCY_GRID;
01253   res.message = "";
01254   if (save_localization_map) {
01255     res.localization_map_name = req.map_name;
01256     if (!SaveTangoAreaDescription(res.localization_map_name, res.localization_map_uuid, res.message)) {
01257       res.success = false;
01258       return true;
01259     }
01260     tango_data_available_ = false;
01261     res.message += "\nLocalization map " + res.localization_map_uuid +
01262         " successfully saved with name " + res.localization_map_name;
01263   } else if (save_occupancy_grid) {
01264     if (!GetCurrentADFUuid(tango_config_, res.localization_map_uuid)) {
01265       res.message += "\nCould not get current localization map uuid.";
01266       res.success = false;
01267       return true;
01268     }
01269   }
01270   if (save_occupancy_grid) {
01271     std::string occupancy_grid_directory;
01272     node_handle_.param(OCCUPANCY_GRID_DIRECTORY_PARAM_NAME,
01273                        occupancy_grid_directory, OCCUPANCY_GRID_DEFAULT_DIRECTORY);
01274     res.occupancy_grid_name = req.map_name;
01275     if (!res.localization_map_uuid.empty()) {
01276       // Save the occupancy grid wrt the area description frame.
01277       geometry_msgs::TransformStamped start_of_service_T_area_description;
01278       InvertTransformStamped(area_description_T_start_of_service_, &start_of_service_T_area_description);
01279       geometry_msgs::PoseStamped stamped_origin;
01280       stamped_origin.header = occupancy_grid_.header;
01281       stamped_origin.pose = occupancy_grid_.info.origin;
01282       geometry_msgs::PoseStamped stamped_origin_in_area_description;
01283       tf2::doTransform(stamped_origin, stamped_origin_in_area_description,
01284                        start_of_service_T_area_description);
01285       occupancy_grid_.info.origin = stamped_origin_in_area_description.pose;
01286       occupancy_grid_.info.origin.position.z = 0.;
01287       occupancy_grid_.header.frame_id = area_description_frame_id_;
01288     }
01289     if (!occupancy_grid_file_io::SaveOccupancyGridToFiles(
01290         res.occupancy_grid_name, res.localization_map_uuid, occupancy_grid_directory, occupancy_grid_)) {
01291       res.message += "\nCould not save occupancy grid " + res.occupancy_grid_name
01292           + " in directory " + occupancy_grid_directory;
01293       res.success = false;
01294       return true;
01295     }
01296     res.message += "\nOccupancy grid successfully saved with name "
01297         + res.occupancy_grid_name + " in  directory " + occupancy_grid_directory;
01298     if (res.localization_map_uuid.empty()) {
01299       res.message += "\nThe occupancy grid has been saved without localization map uuid. "
01300           "This means it will not be aligned when loaded later.";
01301     }
01302   }
01303   res.success = true;
01304   return true;
01305 }
01306 
01307 bool TangoRosNodelet::LoadOccupancyGridServiceCallback(const tango_ros_messages::LoadOccupancyGrid::Request& req,
01308                            tango_ros_messages::LoadOccupancyGrid::Response& res) {
01309   nav_msgs::OccupancyGrid occupancy_grid;
01310   std::string occupancy_grid_directory;
01311   node_handle_.param(OCCUPANCY_GRID_DIRECTORY_PARAM_NAME,
01312                      occupancy_grid_directory, OCCUPANCY_GRID_DEFAULT_DIRECTORY);
01313 
01314   std::string map_uuid;
01315   res.aligned = true;
01316   if(!occupancy_grid_file_io::LoadOccupancyGridFromFiles(
01317       req.name, occupancy_grid_directory, &occupancy_grid, &map_uuid)) {
01318     LOG(ERROR) << "Error while loading occupancy grid from file.";
01319     res.message =  "Could not load occupancy grid from file " + req.name
01320               + " in directory " + occupancy_grid_directory;
01321     res.aligned = false;
01322     res.success = false;
01323     res.localization_map_uuid = "";
01324     return true;
01325   }
01326   res.localization_map_uuid = map_uuid;
01327   res.message = "Occupancy grid " + req.name + " successfully loaded from " + occupancy_grid_directory;
01328 
01329   std::string current_map_uuid;
01330   if (!GetCurrentADFUuid(tango_config_, current_map_uuid)) {
01331     res.message += "\nCould not get current localization map uuid.";
01332     res.aligned = false;
01333     res.success = false;
01334     return true;
01335   }
01336   if (current_map_uuid.empty()) {
01337     res.message += "\nThe occupancy grid is not aligned because "
01338         "no localization map is currently used.";
01339     res.aligned = false;
01340   }
01341   if (map_uuid.empty()) {
01342     res.message += "\nThe occupancy grid is not aligned because "
01343         "its localization map uuid is empty.";
01344     res.aligned = false;
01345   }
01346   if (map_uuid.compare(current_map_uuid) != 0) {
01347     res.message += "\nThe occupancy grid is not aligned because "
01348         "it does not correspond to the localization map currently used.";
01349     res.aligned = false;
01350   }
01351 
01352   res.success = true;
01353   if (map_uuid.empty()) {
01354     occupancy_grid.header.frame_id = start_of_service_frame_id_;
01355   } else {
01356     occupancy_grid.header.frame_id = area_description_frame_id_;
01357   }
01358   occupancy_grid.header.stamp = ros::Time::now();
01359   occupancy_grid.info.map_load_time = occupancy_grid.header.stamp;
01360   static_occupancy_grid_publisher_.publish(occupancy_grid);
01361   return true;
01362 }
01363 
01364 bool TangoRosNodelet::GetAvailableMapUuidsList(std::vector<std::string>& uuid_list) {
01365   char* c_uuid_list;
01366   TangoErrorType result = TangoService_getAreaDescriptionUUIDList(&c_uuid_list);
01367   if (result != TANGO_SUCCESS) {
01368     LOG(ERROR) << "Error while retrieving all available map UUIDs, error: " << result;
01369     return false;
01370   }
01371   if (c_uuid_list == NULL || c_uuid_list[0] == '\0') {
01372     LOG(WARNING) << "No area description file available.";
01373     return false;
01374   }
01375   LOG(INFO) << "UUID list: " << c_uuid_list;
01376   uuid_list = SplitCommaSeparatedString(std::string(c_uuid_list));
01377   return true;
01378 }
01379 
01380 bool TangoRosNodelet::GetMapNameFromUuid(const std::string& map_uuid, std::string& map_name) {
01381   size_t size = 0;
01382   char* value;
01383   TangoAreaDescriptionMetadata metadata;
01384   TangoErrorType result = TangoService_getAreaDescriptionMetadata(map_uuid.c_str(), &metadata);
01385   if (result != TANGO_SUCCESS) {
01386     LOG(ERROR) << "Error while trying to access area description metadata, error: " << result;
01387     return false;
01388   }
01389   result = TangoAreaDescriptionMetadata_get(metadata, "name", &size, &value);
01390   if (result != TANGO_SUCCESS) {
01391     LOG(ERROR) << "Error while trying to get area description metadata, error: " << result;
01392     return false;
01393   }
01394   map_name = std::string(value);
01395   result = TangoAreaDescriptionMetadata_free(metadata);
01396   if (result != TANGO_SUCCESS) {
01397     LOG(ERROR) << "Error while trying to free area description metadata, error: " << result;
01398   }
01399   LOG(INFO) << "Successfully retrieved map name: " << map_name << " from uuid " << map_uuid;
01400   return true;
01401 }
01402 } // namespace tango_ros_native


tango_ros_native
Author(s):
autogenerated on Thu Jun 6 2019 19:49:54