Program Listing for File maplab-server-node.h

Return to documentation for file (applications/maplab-server-node/include/maplab-server-node/maplab-server-node.h)

#ifndef MAPLAB_SERVER_NODE_MAPLAB_SERVER_NODE_H_
#define MAPLAB_SERVER_NODE_MAPLAB_SERVER_NODE_H_

#include <atomic>
#include <deque>
#include <list>
#include <map>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>

#include <aslam/common/thread-pool.h>
#include <map-manager/map-manager.h>
#include <resources-common/point-cloud.h>
#include <vi-map/vi-map.h>
#include <visualization/resource-visualization.h>
#include <visualization/viwls-graph-plotter.h>

namespace maplab {

struct SubmapProcess {
  // Name of the agent
  std::string robot_name;

  // Path to the map on the file system.
  std::string path;

  // Is true if the map has been loaded into the map manager already.
  bool is_loaded = false;

  // Map key of the map in the map manager.
  std::string map_key;

  // A unique hash to allow for quick lookup when multiple processes need to be
  // kept track of.
  size_t map_hash;

  // Is true if the map has been processed, i.e. all the submap processing steps
  // have been applied to the map.
  bool is_processed = false;

  // Is true if submap has been merged into global map.
  bool is_merged = false;

  mutable std::mutex mutex;
};

class MaplabServerNode final {
 public:
  explicit MaplabServerNode();

  ~MaplabServerNode();

  // Once the node is started, the configuration cannot be changed anymore.
  void start();
  void shutdown();

  bool loadAndProcessSubmap(
      const std::string& robot_name, const std::string& submap_path);

  // Save the map to disk.
  bool saveMap(const std::string& path);
  bool saveMap();

  enum class MapLookupStatus : int {
    kSuccess = 0,
    kNoSuchMission = 1,
    kNoSuchSensor = 2,
    kPoseNotAvailableYet = 3,
    kPoseNeverAvailable = 4
  };
  MapLookupStatus mapLookup(
      const std::string& robot_name, const vi_map::SensorType sensor_type,
      const int64_t timestamp_ns, const Eigen::Vector3d& p_S,
      Eigen::Vector3d* p_G, Eigen::Vector3d* sensor_p_G) const;

  // Initially blacklists the mission, the merging thread will then remove it
  // within one iteration. All new submaps of this mission that arrive will be
  // discarded. The mission can be identified with a partial hash of length 4 or
  // more.
  bool deleteMission(
      const std::string& partial_mission_id_string,
      std::string* status_message);

  // Initially blacklists the missions of this robot, the merging thread will
  // then remove them within one iteration. All new submaps of these missions
  // that arrive will be discarded.
  bool deleteAllRobotMissions(
      const std::string& robot_name, std::string* status_message);

  // Returns an accumulation of the dense map data in global frame within a
  // radius around a center.
  bool getDenseMapInRange(
      const backend::ResourceType resource_type,
      const Eigen::Vector3d& center_G, const double radius_m,
      resources::PointCloud* point_cloud_G);

  void visualizeMap();

  void registerPoseCorrectionPublisherCallback(
      std::function<void(
          const int64_t, const std::string&, const aslam::Transformation&,
          const aslam::Transformation&, const aslam::Transformation&,
          const aslam::Transformation&)>
          callback);

  void registerStatusCallback(std::function<void(const std::string&)> callback);

 protected:
  // Status thread functions:
  void printAndPublishServerStatus();

  // Submap processing functions:
  void updateRobotInfoBasedOnSubmap(const SubmapProcess& submap_process);
  void runSubmapProcessing(const SubmapProcess& submap_process);

  // Map merging function:

  // Deletes missions from the merged map that have been blacklisted. Returns
  // false if no missions are left in the merged map, which also deletes it from
  // the map manager.
  bool deleteBlacklistedMissions();

  bool appendAvailableSubmaps();

  void saveMapEveryInterval();

  void runOneIterationOfMapMergingAlgorithms();

  void publishDenseMap();

  void publishMostRecentVertexPoseAndCorrection();

  bool isSubmapBlacklisted(const std::string& map_key);

  struct RobotMissionInformation {
    // Contains the mission ids and whether the baseframe is known of this
    // robot, the most recent mission is at the front of the vector.
    std::list<std::pair<vi_map::MissionId, bool>>
        mission_ids_with_baseframe_status;

    // These keep track of the end/start poses of submaps as they came in
    // and the most recent submap end pose in the optimized map. This is used
    // to compute the correction T_B_old_B_new that is published by the
    // server. This correction can then be used to coorect any poses that were
    // expressed in the odometry frame that was used to build the map
    // initially.
    std::map<int64_t, aslam::Transformation> T_M_B_submaps_input;
    std::map<int64_t, aslam::Transformation> T_G_M_submaps_input;
  };

 private:
  // Threads
  // Threadpool to individuall and concurrently process incomming submaps.
  aslam::ThreadPool submap_loading_thread_pool_;
  // Single merging thread that attaches processed submaps to the global map and
  // optimizes it.
  std::thread submap_merging_thread_;
  // Fast status loop that reads current the thread status from merging and
  // submap thread and summarizes it.
  std::thread status_thread_;

  // Map management
  const std::string kMergedMapKey = "merged_map";
  // Stores all submaps and the merged map.
  vi_map::VIMapManager map_manager_;
  // Map visualization
  std::unique_ptr<visualization::ViwlsGraphRvizPlotter> plotter_;

  // Concurrently accessed variables
  // Accessed by all threads to allow them to aboart early if a shutdown is
  // requested.
  std::atomic<bool> shut_down_requested_;

  // Submap processing thread status variables.
  // Accessed by submap and status threads.
  std::mutex running_submap_process_mutex_;
  std::map<size_t, std::string> running_submap_process_;

  // Merging thread status variables
  // Accessed by merging and status thread.
  std::atomic<bool> merging_thread_busy_;
  std::mutex running_merging_process_mutex_;
  std::string running_merging_process_;
  std::atomic<double> duration_last_merging_loop_s_;
  std::atomic<double> optimization_trust_region_radius_;
  // Keep strack of the total number of merged submaps into the global map.
  std::atomic<uint32_t> total_num_merged_submaps_;

  // Server status and map management variables
  // Accessed by all threads to map between robot names and missions.
  mutable std::mutex robot_to_mission_id_map_mutex_;
  std::unordered_map<std::string, RobotMissionInformation>
      robot_to_mission_id_map_;
  std::unordered_map<vi_map::MissionId, std::string> mission_id_to_robot_map_;
  // Accessed by main thread and submapping threads, used to blacklist missions
  // after deleting them, such that submaps arriving late from the same mapping
  // session of that robot are ignored.
  mutable std::mutex blacklisted_missions_mutex_;
  std::unordered_map<vi_map::MissionId, std::string> blacklisted_missions_;
  // Accessed by the main thread, the submap processing threads and the merging
  // thread. Queue at the interface between submap processing and merging
  // threads. The submap processes are launched based on submaps loaded into
  // this queue. After the completion of the submap processing, the merging
  // threads extracts the finished submaps and adds them to the global map.
  std::mutex submap_processing_queue_mutex_;
  std::deque<SubmapProcess> submap_processing_queue_;

  // Callbacks
  // Callback that is called at the end of every merging thread loop and provide
  // an updated set of transformations related to the initial value and the most
  // recent optmized value of the newest pose and baseframe of every robot.
  std::function<void(
      const int64_t, const std::string&, const aslam::Transformation&,
      const aslam::Transformation&, const aslam::Transformation&,
      const aslam::Transformation&)>
      pose_correction_publisher_callback_;
  // Callback that is called in every iteration of the status thread to forward
  // the server status summary.
  std::function<void(const std::string&)> status_publisher_callback_;

  // Server settings and status.
  bool is_running_ = false;
  const int kSecondsToSleepBetweenAttempts = 1;
  const int kSecondsToSleepBetweenStatus = 1;

  // Exclusively accessed by the merging thread, to keep track of how often it
  // should save the map.
  double time_of_last_map_backup_s_;
  // Exclusively accessed by the merging thread, keeps track of number of
  // submaps at the the last time the trust region has been
  // reset.
  uint32_t num_submaps_at_last_trust_region_reset = 0;

  // Protects the whole server from concurrent access from the outside.
  mutable std::mutex mutex_;
};

}  // namespace maplab

#endif  // MAPLAB_SERVER_NODE_MAPLAB_SERVER_NODE_H_