NemoInterface.cpp 15.6 KB
Newer Older
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1
#include "NemoInterface.h"
2
#include "SnakeTilesLocal.h"
Valentin Platzgummer's avatar
Valentin Platzgummer committed
3 4 5 6 7 8 9 10 11 12 13 14 15

#include "QGCApplication.h"
#include "QGCToolbox.h"
#include "SettingsFact.h"
#include "SettingsManager.h"
#include "WimaSettings.h"

#include <shared_mutex>

#include <QTimer>

#include "QNemoHeartbeat.h"
#include "QNemoProgress.h"
16 17 18
#include "Wima/Geometry/WimaMeasurementArea.h"
#include "Wima/Snake/SnakeTile.h"
#include "Wima/Snake/snake.h"
Valentin Platzgummer's avatar
Valentin Platzgummer committed
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45

#include "ros_bridge/include/messages/geographic_msgs/geopoint.h"
#include "ros_bridge/include/messages/jsk_recognition_msgs/polygon_array.h"
#include "ros_bridge/include/messages/nemo_msgs/heartbeat.h"
#include "ros_bridge/include/messages/nemo_msgs/progress.h"
#include "ros_bridge/include/ros_bridge.h"
#include "ros_bridge/rapidjson/include/rapidjson/document.h"
#include "ros_bridge/rapidjson/include/rapidjson/ostreamwrapper.h"
#include "ros_bridge/rapidjson/include/rapidjson/writer.h"

#define EVENT_TIMER_INTERVAL 100 // ms
auto static timeoutInterval = std::chrono::milliseconds(3000);

using ROSBridgePtr = std::unique_ptr<ros_bridge::ROSBridge>;
using JsonDocUPtr = ros_bridge::com_private::JsonDocUPtr;
using UniqueLock = std::unique_lock<std::shared_timed_mutex>;
using SharedLock = std::shared_lock<std::shared_timed_mutex>;
using JsonDocUPtr = ros_bridge::com_private::JsonDocUPtr;

class NemoInterface::Impl {
  using TimePoint = std::chrono::time_point<std::chrono::high_resolution_clock>;

public:
  Impl(NemoInterface *p);

  void start();
  void stop();
46 47
  void setTileData(const TileData &tileData);
  bool hasTileData(const TileData &tileData) const;
48 49
  void setAutoPublish(bool ap);
  void setHoldProgress(bool hp);
50

51
  bool lockProgress();
52 53 54
  void publishTileData();

  NemoInterface::STATUS status();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
55
  QVector<int> progress();
56
  bool running();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
57 58 59 60

private:
  bool doTopicServiceSetup();
  void loop();
61 62
  static STATUS heartbeatToStatus(
      const ros_bridge::messages::nemo_msgs::heartbeat::Heartbeat &hb);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
  //!
  //! \brief Publishes tilesENU
  //! \pre this->tilesENUMutex must be locked
  //!
  void publishTilesENU();
  //!
  //! \brief Publishes ENUOrigin
  //! \pre this->ENUOriginMutex must be locked
  //!
  void publishENUOrigin();

  // Data.
  SnakeTilesLocal tilesENU;
  mutable std::shared_timed_mutex tilesENUMutex;
  QGeoCoordinate ENUOrigin;
  mutable std::shared_timed_mutex ENUOriginMutex;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
79
  QNemoProgress qProgress;
80
  QNemoProgress qProgressHolded;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
81
  mutable std::shared_timed_mutex progressMutex;
82
  NemoInterface::STATUS status_;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
83
  TimePoint nextTimeout;
84
  mutable std::shared_timed_mutex statusMutex;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
85

86 87 88
  // Not protected data.
  TileData tileData;

Valentin Platzgummer's avatar
Valentin Platzgummer committed
89
  // Internals
90
  std::atomic_bool running_;
91
  std::atomic_bool lockProgress_;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
92 93 94 95 96 97
  std::atomic_bool topicServiceSetupDone;
  ROSBridgePtr pRosBridge;
  QTimer loopTimer;
  NemoInterface *parent;
};

98 99 100 101 102 103 104 105 106 107 108 109 110
using StatusMap = std::map<NemoInterface::STATUS, QString>;
StatusMap statusMap{
    std::make_pair<NemoInterface::STATUS, QString>(
        NemoInterface::STATUS::NOT_CONNECTED, "Not Connected"),
    std::make_pair<NemoInterface::STATUS, QString>(
        NemoInterface::STATUS::HEARTBEAT_DETECTED, "Heartbeat Detected"),
    std::make_pair<NemoInterface::STATUS, QString>(
        NemoInterface::STATUS::TIMEOUT, "Timeout"),
    std::make_pair<NemoInterface::STATUS, QString>(
        NemoInterface::STATUS::INVALID_HEARTBEAT, "Error"),
    std::make_pair<NemoInterface::STATUS, QString>(
        NemoInterface::STATUS::WEBSOCKET_DETECTED, "Websocket Detected")};

Valentin Platzgummer's avatar
Valentin Platzgummer committed
111
NemoInterface::Impl::Impl(NemoInterface *p)
112
    : status_(STATUS::NOT_CONNECTED), nextTimeout(TimePoint::max()),
113
      running_(false), lockProgress_(false), topicServiceSetupDone(false),
114
      parent(p) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139

  // ROS Bridge.
  WimaSettings *wimaSettings =
      qgcApp()->toolbox()->settingsManager()->wimaSettings();
  auto connectionStringFact = wimaSettings->rosbridgeConnectionString();
  auto setConnectionString = [connectionStringFact, this] {
    auto connectionString = connectionStringFact->rawValue().toString();
    if (ros_bridge::isValidConnectionString(
            connectionString.toLocal8Bit().data())) {
      this->pRosBridge.reset(
          new ros_bridge::ROSBridge(connectionString.toLocal8Bit().data()));
    } else {
      QString defaultString("localhost:9090");
      qgcApp()->showMessage("ROS Bridge connection string invalid: " +
                            connectionString);
      qgcApp()->showMessage("Resetting connection string to: " + defaultString);
      connectionStringFact->setRawValue(
          QVariant(defaultString)); // calls this function recursively
    }
  };
  connect(connectionStringFact, &SettingsFact::rawValueChanged,
          setConnectionString);
  setConnectionString();

  // Periodic.
Valentin Platzgummer's avatar
Valentin Platzgummer committed
140
  connect(&this->loopTimer, &QTimer::timeout, [this] { this->loop(); });
Valentin Platzgummer's avatar
Valentin Platzgummer committed
141 142 143
  this->loopTimer.start(EVENT_TIMER_INTERVAL);
}

144 145 146 147
void NemoInterface::Impl::start() {
  this->running_ = true;
  emit this->parent->runningChanged();
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
148

149 150 151 152
void NemoInterface::Impl::stop() {
  this->running_ = false;
  emit this->parent->runningChanged();
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
153

154 155 156 157 158 159 160 161 162 163 164 165 166 167
void NemoInterface::Impl::setTileData(const TileData &tileData) {
  this->tileData = tileData;
  if (tileData.tiles.count() > 0) {
    std::lock(this->ENUOriginMutex, this->tilesENUMutex);
    UniqueLock lk1(this->ENUOriginMutex, std::adopt_lock);
    UniqueLock lk2(this->tilesENUMutex, std::adopt_lock);

    const auto *obj = tileData.tiles.get(0);
    const auto *tile = qobject_cast<const SnakeTile *>(obj);
    if (tile != nullptr) {
      if (tile->coordinateList().size() > 0) {
        this->ENUOrigin = tile->coordinateList().first();
        const auto &origin = this->ENUOrigin;
        this->tilesENU.polygons().clear();
168 169 170
        for (int i = 0; i < tileData.tiles.count(); ++i) {
          obj = tileData.tiles.get(i);
          tile = qobject_cast<const SnakeTile *>(obj);
171
          if (tile != nullptr) {
172 173
            SnakeTileLocal tileENU;
            snake::areaToEnu(origin, tile->coordinateList(), tileENU.path());
174 175 176 177 178 179 180 181 182 183
            this->tilesENU.polygons().push_back(std::move(tileENU));
          } else {
            qWarning() << "NemoInterface::Impl::setTileData(): nullptr.";
            break;
          }
        }
      } else {
        qWarning() << "NemoInterface::Impl::setTileData(): nullptr.";
      }
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
184 185 186
  }
}

187
bool NemoInterface::Impl::hasTileData(const TileData &tileData) const {
188
  return this->tileData == tileData;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
189 190
}

191
void NemoInterface::Impl::setHoldProgress(bool hp) {
192 193 194
  if (this->lockProgress_ != hp) {
    this->lockProgress_ = hp;
    emit this->parent->lockProgressChanged();
195

196
    if (!this->lockProgress_) {
197 198 199 200 201 202 203 204 205 206
      UniqueLock lk(this->progressMutex);
      if (this->qProgress != this->qProgressHolded) {
        this->qProgressHolded = this->qProgress;
        lk.unlock();
        emit this->parent->progressChanged();
      }
    }
  }
}

207
bool NemoInterface::Impl::lockProgress() { return this->lockProgress_.load(); }
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223

void NemoInterface::Impl::publishTileData() {
  std::lock(this->ENUOriginMutex, this->tilesENUMutex);
  UniqueLock lk1(this->ENUOriginMutex, std::adopt_lock);
  UniqueLock lk2(this->tilesENUMutex, std::adopt_lock);

  if (this->tilesENU.polygons().size() > 0 && this->running_ &&
      this->topicServiceSetupDone) {
    this->publishENUOrigin();
    this->publishTilesENU();
  }
}

NemoInterface::STATUS NemoInterface::Impl::status() {
  SharedLock lk(this->statusMutex);
  return status_;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
224 225 226 227
}

QVector<int> NemoInterface::Impl::progress() {
  SharedLock lk(this->progressMutex);
228
  return this->qProgressHolded.progress();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
229 230
}

231 232
bool NemoInterface::Impl::running() { return this->running_.load(); }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
233 234 235
bool NemoInterface::Impl::doTopicServiceSetup() {
  using namespace ros_bridge::messages;

236
  // snake tiles.
Valentin Platzgummer's avatar
Valentin Platzgummer committed
237 238 239 240 241 242 243 244 245 246
  {
    SharedLock lk(this->tilesENUMutex);

    if (this->tilesENU.polygons().size() == 0)
      return false;
    this->pRosBridge->advertiseTopic(
        "/snake/tiles",
        jsk_recognition_msgs::polygon_array::messageType().c_str());
  }

247
  // snake origin.
Valentin Platzgummer's avatar
Valentin Platzgummer committed
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
  {
    SharedLock lk(this->ENUOriginMutex);

    this->pRosBridge->advertiseTopic(
        "/snake/origin", geographic_msgs::geo_point::messageType().c_str());
  }

  // Subscribe nemo progress.
  this->pRosBridge->subscribe(
      "/nemo/progress",
      /* callback */ [this](JsonDocUPtr pDoc) {
        std::lock(this->progressMutex, this->tilesENUMutex,
                  this->ENUOriginMutex);
        UniqueLock lk1(this->progressMutex, std::adopt_lock);
        UniqueLock lk2(this->tilesENUMutex, std::adopt_lock);
        UniqueLock lk3(this->ENUOriginMutex, std::adopt_lock);

        int requiredSize = this->tilesENU.polygons().size();
266
        auto hold = this->lockProgress_.load();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
267
        auto &progressMsg = this->qProgress;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
268 269 270 271
        if (!nemo_msgs::progress::fromJson(*pDoc, progressMsg) ||
            progressMsg.progress().size() !=
                requiredSize) { // Some error occured.
          progressMsg.progress().clear();
272 273 274
          qgcApp()->showMessage("Invalid progress message received.");
        } else if (!hold) {
          this->qProgressHolded = this->qProgress;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
275 276 277 278 279
        }
        lk1.unlock();
        lk2.unlock();
        lk3.unlock();

280 281 282
        if (!hold) {
          emit this->parent->progressChanged();
        }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
283 284 285 286 287 288
      });

  // Subscribe /nemo/heartbeat.
  this->pRosBridge->subscribe(
      "/nemo/heartbeat",
      /* callback */ [this](JsonDocUPtr pDoc) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
289
        //        auto start = std::chrono::high_resolution_clock::now();
290 291
        nemo_msgs::heartbeat::Heartbeat heartbeatMsg;
        UniqueLock lk(this->statusMutex);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
292
        if (!nemo_msgs::heartbeat::fromJson(*pDoc, heartbeatMsg)) {
293
          status_ = STATUS::INVALID_HEARTBEAT;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
294
        } else {
295 296 297 298 299
          status_ = heartbeatToStatus(heartbeatMsg);
        }
        if (status_ == STATUS::INVALID_HEARTBEAT) {
          this->nextTimeout = TimePoint::max();
        } else if (status_ == STATUS::HEARTBEAT_DETECTED) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
300 301 302
          this->nextTimeout =
              std::chrono::high_resolution_clock::now() + timeoutInterval;
        }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
303
        lk.unlock();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
304

Valentin Platzgummer's avatar
Valentin Platzgummer committed
305
        emit this->parent->statusChanged();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
306 307 308 309 310 311
        //        auto delta =
        //        std::chrono::duration_cast<std::chrono::milliseconds>(
        //            std::chrono::high_resolution_clock::now() - start);
        //        std::cout << "/nemo/heartbeat callback time: " <<
        //        delta.count() << " ms"
        //                  << std::endl;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
312 313 314 315 316 317 318
      });

  // Advertise /snake/get_origin.
  this->pRosBridge->advertiseService(
      "/snake/get_origin", "snake_msgs/GetOrigin",
      [this](JsonDocUPtr) -> JsonDocUPtr {
        using namespace ros_bridge::messages;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
319
        SharedLock lk(this->ENUOriginMutex);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337

        JsonDocUPtr pDoc(
            std::make_unique<rapidjson::Document>(rapidjson::kObjectType));
        auto &origin = this->ENUOrigin;
        rapidjson::Value jOrigin(rapidjson::kObjectType);
        bool ret = geographic_msgs::geo_point::toJson(origin, jOrigin,
                                                      pDoc->GetAllocator());
        lk.unlock();
        Q_ASSERT(ret);
        (void)ret;
        pDoc->AddMember("origin", jOrigin, pDoc->GetAllocator());
        return pDoc;
      });

  // Advertise /snake/get_tiles.
  this->pRosBridge->advertiseService(
      "/snake/get_tiles", "snake_msgs/GetTiles",
      [this](JsonDocUPtr) -> JsonDocUPtr {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
338
        SharedLock lk(this->tilesENUMutex);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355

        JsonDocUPtr pDoc(
            std::make_unique<rapidjson::Document>(rapidjson::kObjectType));
        rapidjson::Value jSnakeTiles(rapidjson::kObjectType);
        bool ret = jsk_recognition_msgs::polygon_array::toJson(
            this->tilesENU, jSnakeTiles, pDoc->GetAllocator());
        Q_ASSERT(ret);
        (void)ret;
        pDoc->AddMember("tiles", jSnakeTiles, pDoc->GetAllocator());
        return pDoc;
      });

  return true;
}

void NemoInterface::Impl::loop() {
  // Check ROS Bridge status and do setup if necessary.
356
  if (this->running_) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
357 358 359 360
    if (!this->pRosBridge->isRunning()) {
      this->pRosBridge->start();
    } else if (this->pRosBridge->isRunning() && this->pRosBridge->connected() &&
               !this->topicServiceSetupDone) {
361
      if (this->doTopicServiceSetup()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
362
        this->topicServiceSetupDone = true;
363 364 365 366 367
        UniqueLock lk(this->statusMutex);
        this->status_ = STATUS::WEBSOCKET_DETECTED;
        lk.unlock();
        emit this->parent->statusChanged();
      }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
368 369 370 371 372
    } else if (this->pRosBridge->isRunning() &&
               !this->pRosBridge->connected() && this->topicServiceSetupDone) {
      this->pRosBridge->reset();
      this->pRosBridge->start();
      this->topicServiceSetupDone = false;
373 374 375 376 377

      UniqueLock lk(this->statusMutex);
      this->status_ = STATUS::TIMEOUT;
      lk.unlock();
      emit this->parent->statusChanged();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
378 379 380 381 382 383 384
    }
  } else if (this->pRosBridge->isRunning()) {
    this->pRosBridge->reset();
    this->topicServiceSetupDone = false;
  }

  // Check if heartbeat timeout occured.
385 386
  if (this->running_ && this->topicServiceSetupDone) {
    UniqueLock lk(this->statusMutex);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
387 388
    if (this->nextTimeout != TimePoint::max() &&
        this->nextTimeout < std::chrono::high_resolution_clock::now()) {
389 390 391 392 393
      if (this->pRosBridge->isRunning() && this->pRosBridge->connected()) {
        this->status_ = STATUS::WEBSOCKET_DETECTED;
      } else {
        this->status_ = STATUS::TIMEOUT;
      }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
394
      lk.unlock();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
395 396 397 398 399
      emit this->parent->statusChanged();
    }
  }
}

400 401 402 403 404 405 406 407
NemoInterface::STATUS NemoInterface::Impl::heartbeatToStatus(
    const ros_bridge::messages::nemo_msgs::heartbeat::Heartbeat &hb) {
  if (STATUS(hb.status()) == STATUS::HEARTBEAT_DETECTED)
    return STATUS::HEARTBEAT_DETECTED;
  else
    return STATUS::INVALID_HEARTBEAT;
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
408
void NemoInterface::Impl::publishTilesENU() {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
409
  using namespace ros_bridge::messages;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
410 411
  JsonDocUPtr jSnakeTiles(
      std::make_unique<rapidjson::Document>(rapidjson::kObjectType));
Valentin Platzgummer's avatar
Valentin Platzgummer committed
412
  bool ret = jsk_recognition_msgs::polygon_array::toJson(
Valentin Platzgummer's avatar
Valentin Platzgummer committed
413 414 415 416 417 418 419
      this->tilesENU, *jSnakeTiles, jSnakeTiles->GetAllocator());
  Q_ASSERT(ret);
  (void)ret;
  this->pRosBridge->publish(std::move(jSnakeTiles), "/snake/tiles");
}

void NemoInterface::Impl::publishENUOrigin() {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
420
  using namespace ros_bridge::messages;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
421 422 423 424 425 426 427 428 429
  JsonDocUPtr jOrigin(
      std::make_unique<rapidjson::Document>(rapidjson::kObjectType));
  bool ret = geographic_msgs::geo_point::toJson(this->ENUOrigin, *jOrigin,
                                                jOrigin->GetAllocator());
  Q_ASSERT(ret);
  (void)ret;
  this->pRosBridge->publish(std::move(jOrigin), "/snake/origin");
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
430 431 432 433 434 435 436
// ===============================================================
// NemoInterface
NemoInterface::NemoInterface(QObject *parent)
    : QObject(parent), pImpl(std::make_unique<NemoInterface::Impl>(this)) {}

NemoInterface::~NemoInterface() {}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
437 438 439 440
void NemoInterface::start() { this->pImpl->start(); }

void NemoInterface::stop() { this->pImpl->stop(); }

441 442 443 444 445 446 447
void NemoInterface::publishTileData() { this->pImpl->publishTileData(); }

void NemoInterface::requestProgress() {
  qWarning() << "NemoInterface::requestProgress(): dummy.";
}

void NemoInterface::setTileData(const TileData &tileData) {
448
  this->pImpl->setTileData(tileData);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
449 450
}

451 452
bool NemoInterface::hasTileData(const TileData &tileData) const {
  return this->pImpl->hasTileData(tileData);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
453 454
}

455 456 457 458 459 460 461
void NemoInterface::setHoldProgress(bool hp) {
  this->pImpl->setHoldProgress(hp);
}

int NemoInterface::status() const { return integral(this->pImpl->status()); }

NemoInterface::STATUS NemoInterface::statusEnum() const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
462 463 464
  return this->pImpl->status();
}

465 466 467 468
QString NemoInterface::statusString() const {
  return statusMap.at(this->pImpl->status());
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
469
QVector<int> NemoInterface::progress() const { return this->pImpl->progress(); }
470 471 472 473 474 475 476

QString NemoInterface::editorQml() {
  return QStringLiteral("NemoInterface.qml");
}

bool NemoInterface::running() { return this->pImpl->running(); }

477
bool NemoInterface::lockProgress() { return this->pImpl->lockProgress(); }