NemoInterface.cpp 54 KB
Newer Older
1 2 3 4 5 6 7 8 9
#include "NemoInterface.h"

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

10
#include <mutex>
11

12 13
#include <QJsonArray>
#include <QJsonObject>
14
#include <QTimer>
15
#include <QUrl>
16

17
#include "GenericSingelton.h"
18
#include "geometry/MeasurementArea.h"
19
#include "geometry/geometry.h"
20
#include "nemo_interface/MeasurementTile.h"
21
#include "nemo_interface/QNemoHeartbeat.h"
Valentin Platzgummer's avatar
Valentin Platzgummer committed
22 23
#include "nemo_interface/TaskDispatcher.h"

24 25
#include "ros_bridge/include/messages/geographic_msgs/geopoint.h"
#include "ros_bridge/include/messages/nemo_msgs/heartbeat.h"
26
#include "ros_bridge/include/messages/nemo_msgs/progress_array.h"
Valentin Platzgummer's avatar
Valentin Platzgummer committed
27
#include "ros_bridge/include/messages/nemo_msgs/tile.h"
28
#include "ros_bridge/include/messages/nemo_msgs/tile_array.h"
29
#include "rosbridge/rosbridge.h"
30

31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
/*
 * Here some rules:
 * * not threas safe functions are marked with *NotSafe(...)
 * * If a function is not safe:
 *     * the call to it must be protected with a Lock.
 *     * not safe functions are not allowed to emit signals directly (danger of
 * deadlock).
 *     * if a not safe function needs to emit a signal, defere it with
 *       QTimer::singleShot().
 *     * not safe functions are allowed to call other not safe functions
 *     * it is a bad idea to wait inside a not safe function for a asynchronous
 * operation (potential deadlock)
 * * Functions that are not marked with *NotSafe(...) must be thread safe!
 */

#define INVM(context, fun)                                                     \
  {                                                                            \
    auto value = QMetaObject::invokeMethod(context, fun);                      \
    Q_ASSERT(value == true);                                                   \
    Q_UNUSED(value);                                                           \
  }

Q_DECLARE_METATYPE(ProgressArray)

55 56
QGC_LOGGING_CATEGORY(NemoInterfaceLog, "NemoInterfaceLog")

57 58 59 60 61
#define NO_HEARTBEAT_TIMEOUT 5000   // ms
#define RESTART_INTERVAl 600000     // ms == 10 min
#define RESTART_RETRY_INTERVAl 2000 // ms
#define SYNC_INTERVAL 10000         // ms
#define SYNC_RETRY_INTERVAL 2000    // ms
62

63
static constexpr auto maxResponseTime = std::chrono::milliseconds(20000);
64

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
static char const *progressTopic = "/nemo/progress";
static char const *heartbeatTopic = "/nemo/heartbeat";

static char const *addTilesService = "/nemo/add_tiles";
static char const *removeTilesService = "/nemo/remove_tiles";
static char const *clearTilesService = "/nemo/clear_tiles";
static char const *getAllTilesService = "/nemo/get_all_tiles";
// static char const *getTilesService = "/nemo/get_tiles";
// static char const *containsTilesService = "/nemo/contains_tiles";
// static char const *extractTilesService = "/nemo/extract_tiles";
// static char const *sizeService = "/nemo/size";
// static char const *emptyService = "/nemo/empty";
static char const *getProgressService = "/nemo/get_progress";
static char const *getAllProgressService = "/nemo/get_all_progress";
static char const *getVersionService = "/nemo/get_version";

static const std::vector<char const *> requiredServices{
    addTilesService,    removeTilesService,    clearTilesService,
    getAllTilesService, getAllProgressService, getProgressService,
    getVersionService};

static const std::vector<char const *> requiredTopics{progressTopic,
                                                      heartbeatTopic};
88

Valentin Platzgummer's avatar
Valentin Platzgummer committed
89
using hrc = std::chrono::high_resolution_clock;
90
using ROSBridgePtr = std::shared_ptr<Rosbridge>;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
91 92 93 94

typedef ros_bridge::messages::nemo_msgs::tile::GenericTile<QGeoCoordinate,
                                                           QList>
    Tile;
95 96
typedef std::map<QString, std::shared_ptr<Tile>> TileMap;
typedef std::map<QString, std::shared_ptr<const Tile>> TileMapConst;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
97 98
typedef ros_bridge::messages::nemo_msgs::heartbeat::Heartbeat Heartbeat;
typedef nemo_interface::TaskDispatcher Dispatcher;
99
typedef std::unique_lock<std::mutex> Lock;
100 101

class NemoInterface::Impl {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
102 103
  enum class STATE {
    STOPPED,
Valentin Platzgummer's avatar
Valentin Platzgummer committed
104
    START_BRIDGE,
Valentin Platzgummer's avatar
Valentin Platzgummer committed
105
    WEBSOCKET_DETECTED,
106
    TRY_SETUP,
Valentin Platzgummer's avatar
Valentin Platzgummer committed
107 108
    USER_SYNC,
    SYS_SYNC,
109
    SYNC_ERROR,
Valentin Platzgummer's avatar
Valentin Platzgummer committed
110
    READY,
Valentin Platzgummer's avatar
Valentin Platzgummer committed
111 112
    WEBSOCKET_TIMEOUT,
    HEARTBEAT_TIMEOUT
Valentin Platzgummer's avatar
Valentin Platzgummer committed
113
  };
Valentin Platzgummer's avatar
Valentin Platzgummer committed
114

Valentin Platzgummer's avatar
Valentin Platzgummer committed
115
public:
116
  Impl(NemoInterface *p);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
117
  ~Impl();
118 119 120 121

  void start();
  void stop();

Valentin Platzgummer's avatar
Valentin Platzgummer committed
122 123
  // Tile editing.
  // 	Functions that require communication to device.
Valentin Platzgummer's avatar
Valentin Platzgummer committed
124 125 126
  std::shared_future<QVariant> addTiles(const TilePtrArray &tileArray);
  std::shared_future<QVariant> removeTiles(const IDArray &idArray);
  std::shared_future<QVariant> clearTiles();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
127 128

  // 	Functions that don't require communication to device.
129 130 131 132 133
  TileArray getTiles(const IDArray &idArray) const;
  TileArray getAllTiles() const;
  LogicalArray containsTiles(const IDArray &idArray) const;
  std::size_t size() const;
  bool empty() const;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
134 135

  // Progress.
136 137
  ProgressArray getProgress() const;
  ProgressArray getProgress(const IDArray &idArray) const;
138

139 140 141
  NemoInterface::STATUS status() const;
  bool running() const; // thread safe
  bool ready() const;   // thread safe
Valentin Platzgummer's avatar
Valentin Platzgummer committed
142

143 144
  const QString &infoString() const;
  const QString &warningString() const;
145 146

private:
147 148 149
  void _doSetup();
  void _doActionNotSafe();
  void _synchronize();
150
  void _tryRestart();
151
  bool _isSynchronizedNotSafe() const;
152
  bool _isSynchronized() const;
153
  void _onHeartbeatTimeout();
154
  void _onRosbridgeStateChanged();
155 156 157 158
  bool _updateProgress(const ProgressArray &pArray);
  void _onHeartbeatReceived(const QNemoHeartbeat &hb);
  void _setInfoStringNotSafe(const QString &info);
  void _setWarningStringNotSafe(const QString &warning);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
159 160
  void _setInfoString(const QString &info);
  void _setWarningString(const QString &warning);
161

162 163 164 165 166 167 168 169
  // state suff
  bool _userSync() const;
  bool _sysSync() const;
  bool _setStateNotSafe(STATE newState);
  static bool _readyNotSafe(STATE s);
  static bool _userSyncNotSafe(STATE s);
  static bool _sysSyncNotSafe(STATE s);
  static bool _runningNotSafe(STATE s);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
170 171 172
  static NemoInterface::STATUS _status(STATE state);
  static QString _toString(STATE s);
  static QString _toString(NemoInterface::STATUS s);
173

174 175 176 177 178 179 180 181 182 183
  // impl functions
  bool _addTilesImpl(
      std::shared_ptr<QVector<std::shared_ptr<const Tile>>> pTileArray,
      std::shared_ptr<const IDArray> pIdArray);
  bool _removeTilesImpl(std::shared_ptr<const IDArray> pIdArray);
  bool _clearTilesImpl();

  // Worker functions.
  static bool
  _callAddTiles(const QVector<std::shared_ptr<const Tile>> &tileArray,
184
                Dispatcher &disp, Rosbridge &rb);
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
  static bool _callClearTiles(Dispatcher &d, Rosbridge &rb);
  static ProgressArray _callGetProgress(const IDArray &pIdArray, Dispatcher &d,
                                        Rosbridge &rb);
  static ProgressArray _callGetAllProgress(Dispatcher &d, Rosbridge &rb);
  static QVector<std::shared_ptr<Tile>> _callGetAllTiles(Dispatcher &d,
                                                         Rosbridge &rb);
  static QString _callGetVersion(Dispatcher &d, Rosbridge &rb);
  static bool _callRemoveTiles(const IDArray &pIdArray, Dispatcher &d,
                               Rosbridge &rb);

  // functions to manipulate _remoteTiles
  bool _addTilesRemote(const QVector<std::shared_ptr<const Tile>> tileArray);
  bool _addTilesRemote(const QVector<std::shared_ptr<Tile>> &tileArray);
  void _removeTilesRemote(const IDArray &idArray);
  void _clearTilesRemote();
  void _clearTilesRemoteNotSafe();

  // static and const members
  static const char *_localVersion;
  NemoInterface *const _parent;
205

206
  // thread safe members
207
  ROSBridgePtr _pRosbridge;
208 209 210 211 212
  Dispatcher _dispatcher;

  // protected by mutex
  mutable std::mutex _m;
  STATE _state;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
213 214
  TileMap _remoteTiles;
  TileMapConst _localTiles;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
215 216
  QString _infoString;
  QString _warningString;
217 218

  // Members belonging to _parent->thread()
Valentin Platzgummer's avatar
Valentin Platzgummer committed
219
  QTimer _timeoutTimer;
220 221
  QTimer _syncTimer;
  QTimer _restartTimer;
222 223
};

224
const char *NemoInterface::Impl::_localVersion("V_1.0");
225

226 227 228 229
using StatusMap = std::map<NemoInterface::STATUS, QString>;
static StatusMap statusMap{
    std::make_pair<NemoInterface::STATUS, QString>(
        NemoInterface::STATUS::NOT_CONNECTED, "Not Connected"),
230 231
    std::make_pair<NemoInterface::STATUS, QString>(NemoInterface::STATUS::ERROR,
                                                   "ERROR"),
Valentin Platzgummer's avatar
Valentin Platzgummer committed
232 233
    std::make_pair<NemoInterface::STATUS, QString>(NemoInterface::STATUS::SYNC,
                                                   "Synchronizing"),
Valentin Platzgummer's avatar
Valentin Platzgummer committed
234 235
    std::make_pair<NemoInterface::STATUS, QString>(NemoInterface::STATUS::READY,
                                                   "Ready"),
236 237 238 239 240 241
    std::make_pair<NemoInterface::STATUS, QString>(
        NemoInterface::STATUS::TIMEOUT, "Timeout"),
    std::make_pair<NemoInterface::STATUS, QString>(
        NemoInterface::STATUS::WEBSOCKET_DETECTED, "Websocket Detected")};

NemoInterface::Impl::Impl(NemoInterface *p)
242
    : _parent(p), _state(STATE::STOPPED) {
243 244 245 246 247 248 249

  // ROS Bridge.
  WimaSettings *wimaSettings =
      qgcApp()->toolbox()->settingsManager()->wimaSettings();
  auto connectionStringFact = wimaSettings->rosbridgeConnectionString();
  auto setConnectionString = [connectionStringFact, this] {
    auto connectionString = connectionStringFact->rawValue().toString();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
250

Valentin Platzgummer's avatar
Valentin Platzgummer committed
251 252
    bool wasRunning = this->running();
    this->stop();
253 254
    this->_pRosbridge = std::make_shared<Rosbridge>(
        QUrl(QString("ws://") + connectionString.toLocal8Bit().data()));
Valentin Platzgummer's avatar
Valentin Platzgummer committed
255 256 257
    if (wasRunning) {
      this->start();
    }
258
  };
259

260 261 262 263
  connect(connectionStringFact, &SettingsFact::rawValueChanged,
          setConnectionString);
  setConnectionString();

Valentin Platzgummer's avatar
Valentin Platzgummer committed
264
  // Heartbeat timeout.
265 266
  connect(&this->_timeoutTimer, &QTimer::timeout,
          std::bind(&Impl::_onHeartbeatTimeout, this));
Valentin Platzgummer's avatar
Valentin Platzgummer committed
267

268
  connect(this->_pRosbridge.get(), &Rosbridge::stateChanged, this->_parent,
269
          [this] { this->_onRosbridgeStateChanged(); });
Valentin Platzgummer's avatar
Valentin Platzgummer committed
270

271 272 273 274
  connect(&this->_restartTimer, &QTimer::timeout, this->_parent,
          [this] { this->_tryRestart(); });

  connect(&this->_syncTimer, &QTimer::timeout, this->_parent,
275 276 277 278
          [this] { this->_synchronize(); });

  static std::once_flag flag;
  std::call_once(flag, [] { qRegisterMetaType<ProgressArray>(); });
279 280
}

281
NemoInterface::Impl::~Impl() { this->_pRosbridge->stop(); }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
282

283
void NemoInterface::Impl::start() {
284 285 286 287
  Lock lk(this->_m);
  if (!_runningNotSafe(this->_state)) {
    this->_setStateNotSafe(STATE::START_BRIDGE);
    this->_doActionNotSafe();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
288
  }
289 290 291
}

void NemoInterface::Impl::stop() {
292 293 294 295
  Lock lk(this->_m);
  if (_runningNotSafe(this->_state)) {
    this->_setStateNotSafe(STATE::STOPPED);
    this->_doActionNotSafe();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
296
  }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
297 298
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
299 300
std::shared_future<QVariant>
NemoInterface::Impl::addTiles(const TilePtrArray &tileArray) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
301 302
  using namespace nemo_interface;

303
  // qDebug() << "addTiles called";
304

Valentin Platzgummer's avatar
Valentin Platzgummer committed
305 306 307 308
  if (tileArray.size() > 0) {

    // copy unknown tiles
    auto pTileArray = std::make_shared<QVector<std::shared_ptr<const Tile>>>();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
309
    auto pIdArray = std::make_shared<IDArray>();
310 311

    Lock lk(this->_m);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
312 313 314 315 316 317 318 319 320
    for (const auto *pTile : tileArray) {
      auto id = pTile->id();
      const auto it = this->_localTiles.find(id);
      Q_ASSERT(it == _localTiles.end());
      if (Q_LIKELY(it == _localTiles.end())) {
        auto pTileCopy =
            std::make_shared<const Tile>(pTile->coordinateList(), 0.0, id);
        _localTiles.insert(std::make_pair(id, pTileCopy));
        pTileArray->push_back(pTileCopy);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
321
        pIdArray->push_back(id);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
322 323 324 325 326
      } else {
        qCDebug(NemoInterfaceLog)
            << "addTiles(): tile with id: " << pTile->id() << "already added.";
      }
    }
327
    if (pTileArray->size() > 0) {
328
      lk.unlock();
329
      emit this->_parent->tilesChanged();
330
      lk.lock();
331
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
332

Valentin Platzgummer's avatar
Valentin Platzgummer committed
333
    // ready for send?
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
    if (pTileArray->size() > 0 && (this->_readyNotSafe(this->_state) ||
                                   this->_userSyncNotSafe(this->_state))) {

      this->_setStateNotSafe(STATE::USER_SYNC);
      this->_doActionNotSafe();
      lk.unlock();

      // create task.
      auto pTask = std::make_unique<Task>([this, pTileArray, pIdArray] {
        auto ret = this->_addTilesImpl(pTileArray, pIdArray);

        if (ret) {
          Lock lk(this->_m);
          if (this->_isSynchronizedNotSafe()) {
            this->_setStateNotSafe(STATE::READY);
            this->_doActionNotSafe();
          }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
351
        }
352
        return ret;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
353 354 355
      });

      // dispatch command.
356 357
      auto ret = _dispatcher.dispatch(std::move(pTask));
      return ret.share();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
358 359 360 361 362 363 364 365 366 367 368 369
    }
  }

  std::promise<QVariant> p;
  p.set_value(QVariant(false));
  return p.get_future();
}

std::shared_future<QVariant>
NemoInterface::Impl::removeTiles(const IDArray &idArray) {
  using namespace nemo_interface;

370
  // qDebug() << "removeTiles called";
371

Valentin Platzgummer's avatar
Valentin Platzgummer committed
372 373 374 375
  if (idArray.size() > 0) {

    // copy known ids
    auto pIdArray = std::make_shared<IDArray>();
376 377

    Lock lk(this->_m);
378
    for (const auto &id : idArray) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
379 380 381 382 383 384 385
      const auto it = this->_localTiles.find(id);
      Q_ASSERT(it != _localTiles.end());
      if (Q_LIKELY(it != _localTiles.end())) {
        _localTiles.erase(it);
        pIdArray->push_back(id);
      } else {
        qCDebug(NemoInterfaceLog) << "removeTiles(): unknown id: " << id << ".";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
386
      }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
387
    }
388
    if (pIdArray->size() > 0) {
389
      lk.unlock();
390
      emit this->_parent->tilesChanged();
391
      lk.lock();
392
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
393 394

    // ready for send?
395 396
    if (pIdArray->size() > 0 && (this->_readyNotSafe(this->_state) ||
                                 this->_userSyncNotSafe(this->_state))) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
397

398 399 400
      this->_setStateNotSafe(STATE::USER_SYNC);
      this->_doActionNotSafe();
      lk.unlock();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
401 402

      // create command.
403 404 405 406 407 408 409 410 411 412 413 414 415
      auto cmd = std::make_unique<Task>([this, pIdArray] {
        auto ret = this->_removeTilesImpl(pIdArray);

        if (ret) {
          Lock lk(this->_m);
          if (this->_isSynchronizedNotSafe()) {
            this->_setStateNotSafe(STATE::READY);
            this->_doActionNotSafe();
          }
        }

        return ret;
      });
Valentin Platzgummer's avatar
Valentin Platzgummer committed
416 417 418 419 420 421 422 423 424 425 426 427

      // dispatch command and return.
      auto ret = _dispatcher.dispatch(std::move(cmd));
      auto sfut = ret.share();
      return sfut;
    }
  }

  std::promise<QVariant> p;
  p.set_value(QVariant(false));
  return p.get_future();
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
428

Valentin Platzgummer's avatar
Valentin Platzgummer committed
429 430
std::shared_future<QVariant> NemoInterface::Impl::clearTiles() {
  using namespace nemo_interface;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
431

432
  // qDebug() << "clearTiles called";
433

Valentin Platzgummer's avatar
Valentin Platzgummer committed
434
  // clear local tiles (_localTiles)
435
  Lock lk(this->_m);
436 437
  if (!_localTiles.empty()) {
    this->_localTiles.clear();
438
    lk.unlock();
439
    emit this->_parent->tilesChanged();
440
    lk.lock();
441
  }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
442

443
  if (this->_readyNotSafe(this->_state) || this->_readyNotSafe(this->_state)) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
444

445 446 447
    this->_setStateNotSafe(STATE::USER_SYNC);
    this->_doActionNotSafe();
    lk.unlock();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
448

Valentin Platzgummer's avatar
Valentin Platzgummer committed
449
    // create command.
450 451 452 453 454 455 456 457 458 459 460 461 462
    auto pTask = std::make_unique<Task>([this] {
      auto ret = this->_clearTilesImpl();

      if (ret) {
        Lock lk(this->_m);
        if (this->_isSynchronizedNotSafe()) {
          this->_setStateNotSafe(STATE::READY);
          this->_doActionNotSafe();
        }
      }

      return QVariant(ret);
    });
Valentin Platzgummer's avatar
Valentin Platzgummer committed
463 464

    // dispatch command and return.
Valentin Platzgummer's avatar
Valentin Platzgummer committed
465
    auto ret = _dispatcher.dispatch(std::move(pTask));
466
    return ret.share();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
467
  } else {
468
    lk.unlock();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
469 470 471 472 473
    std::promise<QVariant> p;
    p.set_value(QVariant(false));
    return p.get_future();
  }
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
474

475
TileArray NemoInterface::Impl::getTiles(const IDArray &idArray) const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
476 477
  TileArray tileArray;

478
  Lock lk(this->_m);
479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
  if (this->ready()) {
    for (const auto &id : idArray) {
      const auto it = _remoteTiles.find(id);
      if (it != _remoteTiles.end()) {
        MeasurementTile copy;
        copy.setId(it->second->id());
        copy.setProgress(it->second->progress());
        copy.setPath(it->second->tile());
        tileArray.append(std::move(copy));
      }
    }
  } else {
    for (const auto &id : idArray) {
      const auto it = _localTiles.find(id);
      if (it != _localTiles.end()) {
        MeasurementTile copy;
        copy.setId(it->second->id());
        copy.setProgress(it->second->progress());
        copy.setPath(it->second->tile());
        tileArray.append(std::move(copy));
      }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
500 501 502 503 504 505
    }
  }

  return tileArray;
}

506
TileArray NemoInterface::Impl::getAllTiles() const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
507 508
  TileArray tileArray;

509
  Lock lk(this->_m);
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
  if (this->ready()) {
    for (const auto &entry : _remoteTiles) {
      auto pTile = entry.second;
      MeasurementTile copy;
      copy.setId(pTile->id());
      copy.setProgress(pTile->progress());
      copy.setPath(pTile->tile());
      tileArray.append(std::move(copy));
    }

  } else {
    for (const auto &entry : _localTiles) {
      auto pTile = entry.second;
      MeasurementTile copy;
      copy.setId(pTile->id());
      copy.setProgress(pTile->progress());
      copy.setPath(pTile->tile());
      tileArray.append(std::move(copy));
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
529 530 531 532 533
  }

  return tileArray;
}

534
LogicalArray NemoInterface::Impl::containsTiles(const IDArray &idArray) const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
535 536
  LogicalArray logicalArray;

537
  Lock lk(this->_m);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
538
  for (const auto &id : idArray) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
539 540
    const auto &it = _localTiles.find(id);
    logicalArray.append(it != _localTiles.end());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
541 542 543 544 545
  }

  return logicalArray;
}

546 547 548 549 550
std::size_t NemoInterface::Impl::size() const {

  Lock lk(this->_m);
  return _localTiles.size();
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
551

552 553 554 555
bool NemoInterface::Impl::empty() const {
  Lock lk(this->_m);
  return _localTiles.empty();
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
556

557
ProgressArray NemoInterface::Impl::getProgress() const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
558 559
  ProgressArray progressArray;

560 561
  Lock lk(this->_m);
  if (this->_isSynchronizedNotSafe()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
562 563 564 565 566 567 568 569 570
    for (const auto &entry : _remoteTiles) {
      progressArray.append(
          LabeledProgress{entry.second->progress(), entry.second->id()});
    }
  } else {
    for (const auto &entry : _localTiles) {
      progressArray.append(
          LabeledProgress{entry.second->progress(), entry.second->id()});
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
571 572 573 574 575
  }

  return progressArray;
}

576
ProgressArray NemoInterface::Impl::getProgress(const IDArray &idArray) const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
577 578
  ProgressArray progressArray;

579 580
  Lock lk(this->_m);
  if (this->_isSynchronizedNotSafe()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
581 582 583 584 585 586 587 588 589 590 591 592 593 594
    for (const auto &id : idArray) {
      const auto it = _remoteTiles.find(id);
      if (it != _remoteTiles.end()) {
        progressArray.append(
            LabeledProgress{it->second->progress(), it->second->id()});
      }
    }
  } else {
    for (const auto &id : idArray) {
      const auto it = _localTiles.find(id);
      if (it != _localTiles.end()) {
        progressArray.append(
            LabeledProgress{it->second->progress(), it->second->id()});
      }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
595 596 597 598
    }
  }

  return progressArray;
599 600
}

601
NemoInterface::STATUS NemoInterface::Impl::status() const {
602
  Lock lk(this->_m);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
603
  return _status(this->_state);
604 605
}

606 607 608 609
bool NemoInterface::Impl::running() const {
  Lock lk1(this->_m);
  return _runningNotSafe(this->_state);
}
610

611 612 613 614
bool NemoInterface::Impl::ready() const {
  Lock lk1(this->_m);
  return _readyNotSafe(this->_state);
}
615

616 617 618
bool NemoInterface::Impl::_sysSync() const {
  Lock lk1(this->_m);
  return _sysSyncNotSafe(this->_state);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
619 620
}

621
void NemoInterface::Impl::_onHeartbeatTimeout() {
622 623 624
  Lock lk1(this->_m);
  this->_setStateNotSafe(STATE::HEARTBEAT_TIMEOUT);
  this->_doActionNotSafe();
625 626
}

627
void NemoInterface::Impl::_onRosbridgeStateChanged() {
628 629 630
  auto rbState = this->_pRosbridge->state();
  if (rbState == Rosbridge::STATE::CONNECTED) {
    Lock lk1(this->_m);
631 632
    if (this->_state == STATE::START_BRIDGE ||
        this->_state == STATE::WEBSOCKET_TIMEOUT) {
633 634
      this->_setStateNotSafe(STATE::WEBSOCKET_DETECTED);
      this->_doActionNotSafe();
635
    }
636 637
  } else if (rbState == Rosbridge::STATE::TIMEOUT) {
    Lock lk1(this->_m);
638
    if (this->_state == STATE::TRY_SETUP || this->_state == STATE::READY ||
639 640
        this->_state == STATE::WEBSOCKET_DETECTED ||
        this->_state == STATE::HEARTBEAT_TIMEOUT) {
641 642
      this->_setStateNotSafe(STATE::WEBSOCKET_TIMEOUT);
      this->_doActionNotSafe();
643 644 645 646
    }
  }
}

647 648 649 650
bool NemoInterface::Impl::_userSync() const {
  Lock lk1(this->_m);
  return _userSyncNotSafe(this->_state);
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
651

652 653 654 655
const QString &NemoInterface::Impl::infoString() const {
  Lock lk1(this->_m);
  return _infoString;
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
656

657
const QString &NemoInterface::Impl::warningString() const {
658
  Lock lk1(this->_m);
659 660
  return _warningString;
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
661

662
bool NemoInterface::Impl::_updateProgress(const ProgressArray &pArray) {
663

664
  ProgressArray copy;
665
  bool error = false;
666 667 668

  Lock lk1(this->_m);
  for (auto itLP = pArray.begin(); itLP != pArray.end(); ++itLP) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
669

Valentin Platzgummer's avatar
Valentin Platzgummer committed
670
    auto it = _remoteTiles.find(itLP->id());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
671

Valentin Platzgummer's avatar
Valentin Platzgummer committed
672 673
    if (Q_LIKELY(it != _remoteTiles.end())) {
      it->second->setProgress(itLP->progress());
674
      copy.push_back(*itLP);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
675 676
    } else {
      qCDebug(NemoInterfaceLog)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
677
          << "_updateProgress(): tile with id " << itLP->id() << " not found.";
678
      error = true;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
679 680
    }
  }
681
  lk1.unlock();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
682

683 684
  if (copy.size() > 0) {
    emit _parent->progressChanged(copy);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
685
  }
686

687
  return !error;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
688 689
}

690 691 692 693 694 695 696 697 698
void NemoInterface::Impl::_onHeartbeatReceived(const QNemoHeartbeat &) {
  INVM(this->_parent,
       ([this]() mutable { this->_timeoutTimer.start(NO_HEARTBEAT_TIMEOUT); }))

  Lock lk(this->_m);
  if (this->_state == STATE::TRY_SETUP ||
      this->_state == STATE::HEARTBEAT_TIMEOUT) {
    this->_setStateNotSafe(STATE::READY);
    this->_doActionNotSafe();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
699 700 701
  }
}

702
void NemoInterface::Impl::_setInfoStringNotSafe(const QString &info) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
703 704
  if (_infoString != info) {
    _infoString = info;
705 706 707 708

    // emit signal later, can't emit while mutex locked!
    QTimer::singleShot(5, this->_parent,
                       [this] { emit this->_parent->infoStringChanged(); });
Valentin Platzgummer's avatar
Valentin Platzgummer committed
709 710 711
  }
}

712
void NemoInterface::Impl::_setWarningStringNotSafe(const QString &warning) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
713 714
  if (_warningString != warning) {
    _warningString = warning;
715 716 717 718

    // emit signal later, can't emit while mutex locked!
    QTimer::singleShot(5, this->_parent,
                       [this] { emit this->_parent->warningStringChanged(); });
Valentin Platzgummer's avatar
Valentin Platzgummer committed
719 720 721
  }
}

722 723 724 725 726 727 728 729 730
void NemoInterface::Impl::_setInfoString(const QString &info) {
  Lock lk(this->_m);
  _setInfoStringNotSafe(info);
}

void NemoInterface::Impl::_setWarningString(const QString &warning) {
  Lock lk(this->_m);
  _setWarningStringNotSafe(warning);
}
731

732 733
void NemoInterface::Impl::_doSetup() {
  // create task
734
  auto pTask = std::make_unique<nemo_interface::Task>([this] {
735 736
    auto rb = this->_pRosbridge;
    auto &disp = this->_dispatcher;
737

738 739 740 741
    // check if required services are available
    auto cond = [&disp] { return !disp.isInterruptionRequested(); };
    for (const auto &cc : requiredServices) {
      QString s(cc);
742

743
      this->_setInfoString("Waiting for required service " + s);
744

745 746 747
      auto available = rb->waitForService(cc, cond);
      if (!available) {
        return QVariant(false);
748
      }
749
    }
750

751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773
    // check if version is compatible{
    this->_setInfoString("Checking version.");
    auto version = _callGetVersion(disp, *rb);
    if (version != _localVersion) {
      this->_setWarningString(
          "Version checking failed. Local protocol version (" +
          QString(_localVersion) + ") does not match remote version (" +
          version + ").");
      return QVariant(false);
    }

    // check if required topics are available
    for (const auto &cc : requiredTopics) {
      QString s(cc);
      this->_setInfoString("Waiting for required topic " + s);
      auto available = rb->waitForTopic(cc, cond);
      if (!available) {
        return QVariant(false);
      }
    }

    // subscribe topics
    rb->subscribeTopic(progressTopic, [this](const QJsonObject &o) {
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
      ros_bridge::messages::nemo_msgs::progress_array::ProgressArray
          progressArray;
      if (ros_bridge::messages::nemo_msgs::progress_array::fromJson(
              o, progressArray)) {

        // correct range errors of progress
        for (auto &lp : progressArray.progress_array()) {
          bool rangeError = false;
          if (lp.progress() < 0) {
            lp.setProgress(0);
            rangeError = true;
          }
          if (lp.progress() > 100) {
            lp.setProgress(100);
            rangeError = true;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
789
          }
790

791
          if (rangeError) {
792 793
            qCWarning(NemoInterfaceLog) << progressTopic
                                        << " progress out "
794 795 796
                                           "of range, value was set to: "
                                        << lp.progress();
          }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
797
        }
798 799 800
        auto ret = this->_updateProgress(progressArray.progress_array());
        if (!ret)
          this->_setWarningString("Progress update failed.");
801
      } else {
802 803
        qCWarning(NemoInterfaceLog) << progressTopic
                                    << " not able to "
804 805 806 807 808 809
                                       "create ProgressArray form json: "
                                    << o;
      }
    });

    using namespace ros_bridge::messages;
810 811 812 813 814 815 816 817 818 819 820
    rb->subscribeTopic(heartbeatTopic, [this](const QJsonObject &o) {
      nemo_msgs::heartbeat::Heartbeat heartbeat;
      if (nemo_msgs::heartbeat::fromJson(o, heartbeat)) {
        this->_onHeartbeatReceived(heartbeat);
      } else {
        qCWarning(NemoInterfaceLog) << heartbeatTopic
                                    << " not able to "
                                       "create Heartbeat form json: "
                                    << o;
      }
    });
821

822 823 824 825 826 827
    // now ready
    INVM(this->_parent, ([this]() mutable {
           this->_timeoutTimer.start(NO_HEARTBEAT_TIMEOUT);
         }))
    _setInfoString("");
    _setWarningString("");
828 829 830
    return QVariant(true);
  });

831 832
  auto f = _dispatcher.dispatch(std::move(pTask));
  Q_UNUSED(f);
833 834
}

835 836 837
void NemoInterface::Impl::_synchronize() {
  Lock lk(this->_m);
  if (this->_state == STATE::READY || this->_state == STATE::SYNC_ERROR) {
838

839 840
    _setStateNotSafe(STATE::SYS_SYNC);
    _doActionNotSafe();
841

842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868
    // copy tiles.
    auto pTileArray = std::make_shared<QVector<std::shared_ptr<const Tile>>>();
    for (auto it = _localTiles.begin(); it != _localTiles.end(); ++it) {
      pTileArray->push_back(it->second);
    }
    lk.unlock();

    // create cmd.
    auto pTask = std::make_unique<nemo_interface::Task>([this, pTileArray] {
      auto rb = this->_pRosbridge;
      auto &disp = this->_dispatcher;
      // are _localTiles and _remoteTiles synced?

      auto remoteTiles = this->_callGetAllTiles(disp, *rb);

      // create tile map;
      std::map<QString, std::shared_ptr<Tile>> tempMap;
      for (auto &&pTile : remoteTiles) {
        auto it = tempMap.find(pTile->id());
        if (Q_LIKELY(it == tempMap.end())) {
          tempMap.insert(std::make_pair(pTile->id(), pTile));
        } else {
          qCDebug(NemoInterfaceLog)
              << "_synchronizeIfNeccessary(): remoteTiles contains "
                 "duplicate id";
        }
      }
869

870 871 872 873 874 875 876 877 878
      // compare with pTileArray
      bool synced = true;
      for (auto &&pTile : *pTileArray) {
        auto it = tempMap.find(pTile->id());
        if (it == tempMap.end() || it->second->tile() != pTile->tile()) {
          synced = false;
          break;
        }
      }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
879

880 881 882
      if (!synced) {
        auto success = this->_clearTilesImpl();
        if (!success) {
883 884 885 886 887
          Lock lk1(this->_m);
          if (this->_state == STATE::SYS_SYNC) {
            _setStateNotSafe(STATE::SYNC_ERROR);
            _doActionNotSafe();
          }
888 889
          return QVariant(false);
        }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
890

891 892 893 894
        auto pIdArray = std::make_shared<IDArray>();
        for (auto &&pTile : *pTileArray) {
          pIdArray->push_back(pTile->id());
        }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
895

896
        success = this->_addTilesImpl(pTileArray, pIdArray);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
897

898
        if (!success) {
899 900 901 902 903
          Lock lk2(this->_m);
          if (this->_state == STATE::SYS_SYNC) {
            _setStateNotSafe(STATE::SYNC_ERROR);
            _doActionNotSafe();
          }
904 905 906 907 908 909 910 911 912 913 914
          return QVariant(false);
        }
      } else {
        // update progress
        this->_clearTilesRemote();
        auto ret = this->_addTilesRemote(*pTileArray);

        if (ret) {
          ProgressArray progress;
          for (auto &&pTile : remoteTiles) {
            progress.push_back(LabeledProgress(pTile->progress(), pTile->id()));
Valentin Platzgummer's avatar
Valentin Platzgummer committed
915
          }
916 917
          ret = _updateProgress(progress);
        }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
918

919 920
        if (!ret) {
          Lock lk(this->_m);
921 922 923 924 925 926 927 928
          if (this->_state == STATE::SYS_SYNC) {
            this->_setStateNotSafe(STATE::SYNC_ERROR);
            this->_doActionNotSafe();
            lk.unlock();
            this->_setWarningString("Getting progress failed.");
            qCDebug(NemoInterfaceLog)
                << "_addTilesImpl(): _updateProgress failed: unknown id.";
          }
929 930
          return QVariant(false);
        }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
931
      }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
932

933 934 935 936 937 938 939 940 941 942
      // check if local versions are still synced (user could habe modified
      // _localTiles).
      Lock lk(this->_m);
      if (_isSynchronizedNotSafe()) {
        _setStateNotSafe(STATE::READY);
        _doActionNotSafe();
      } else {
        INVM(this->_parent, [this] { this->_synchronize(); })
      }
      lk.unlock();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
943

944 945
      return QVariant(true);
    });
946 947 948

    // dispatch command.
    auto ret = _dispatcher.dispatch(std::move(pTask));
949 950
    Q_UNUSED(ret);
    INVM(this->_parent, [this] { this->_syncTimer.start(SYNC_INTERVAL); })
951
  } else {
952
    INVM(this->_parent, [this] { this->_syncTimer.start(SYNC_RETRY_INTERVAL); })
953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968
  }
}

void NemoInterface::Impl::_tryRestart() {
  if (this->running()) {
    if (_dispatcher.idle()) {
      qDebug() << "_tryRestart: restarting";
      this->stop();
      this->start();
      _restartTimer.start(RESTART_INTERVAl);
    } else {
      _restartTimer.start(RESTART_RETRY_INTERVAl);
    }
  }
}

969
bool NemoInterface::Impl::_isSynchronizedNotSafe() const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
970
  return _localTiles.size() == _remoteTiles.size() &&
Valentin Platzgummer's avatar
Valentin Platzgummer committed
971 972 973 974 975
         std::equal(
             _localTiles.begin(), _localTiles.end(), _remoteTiles.begin(),
             [](const auto &a, const auto &b) { return a.first == b.first; });
}

976 977 978 979 980 981
bool NemoInterface::Impl::_isSynchronized() const {
  Lock lk(this->_m);
  return _isSynchronizedNotSafe();
}

void NemoInterface::Impl::_doActionNotSafe() {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
982
  static bool resetDone = false;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
983
  switch (this->_state) {
984 985 986 987 988 989 990
  case STATE::STOPPED: {
    INVM(this->_parent, ([this]() mutable {
           this->_timeoutTimer.stop();
           this->_restartTimer.stop();
           this->_syncTimer.stop();
         }))

991 992
    if (this->_pRosbridge->state() != Rosbridge::STATE::STOPPED) {
      this->_pRosbridge->stop();
993
    }
994 995 996 997 998 999 1000 1001 1002 1003
    _dispatcher.stop();
    _dispatcher.clear();

    _setInfoStringNotSafe("");
    _setWarningStringNotSafe("");
  } break;

  case STATE::START_BRIDGE: {
    INVM(this->_parent,
         ([this]() mutable { this->_restartTimer.start(RESTART_INTERVAl); }))
1004

1005
    this->_pRosbridge->start();
1006 1007
    _setInfoStringNotSafe("");
    _setWarningStringNotSafe("");
1008
  } break;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1009
  case STATE::WEBSOCKET_DETECTED:
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1010
    resetDone = false;
1011 1012
    this->_setStateNotSafe(STATE::TRY_SETUP);
    this->_doActionNotSafe();
1013 1014
    _setInfoStringNotSafe("");
    _setWarningStringNotSafe("");
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1015
    break;
1016
  case STATE::TRY_SETUP:
1017
    this->_doSetup();
1018 1019
    _setInfoStringNotSafe("");
    _setWarningStringNotSafe("");
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1020
    break;
1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032
  case STATE::READY: {
    INVM(this->_parent,
         ([this]() mutable { this->_syncTimer.start(SYNC_INTERVAL); }))

    if (!_isSynchronizedNotSafe()) {
      // can't call this here directly.
      QTimer::singleShot(100, this->_parent, [this] { this->_synchronize(); });
    }

    _setInfoStringNotSafe("");
    _setWarningStringNotSafe("");
  } break;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1033 1034
  case STATE::USER_SYNC:
  case STATE::SYS_SYNC:
1035
  case STATE::SYNC_ERROR:
1036 1037
    _setInfoStringNotSafe("");
    _setWarningStringNotSafe("");
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1038
    break;
1039 1040
  case STATE::HEARTBEAT_TIMEOUT: {
    INVM(this->_parent, ([this]() mutable { this->_syncTimer.stop(); }))
1041 1042
    _setInfoStringNotSafe("");
    _setWarningStringNotSafe("");
1043 1044 1045 1046 1047 1048 1049
  } break;
  case STATE::WEBSOCKET_TIMEOUT: {
    INVM(this->_parent, ([this]() mutable {
           this->_timeoutTimer.stop();
           this->_syncTimer.stop();
         }))

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1050 1051
    if (!resetDone) {
      resetDone = true;
1052 1053
      this->_pRosbridge->stop();
      this->_pRosbridge->start();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1054
    }
1055 1056 1057 1058 1059 1060
    _dispatcher.stop();
    _dispatcher.clear();

    _setInfoStringNotSafe("");
    _setWarningStringNotSafe("");
  } break;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1061
  };
1062 1063
}

1064
bool NemoInterface::Impl::_callAddTiles(
1065
    const QVector<std::shared_ptr<const Tile>> &tileArray, Dispatcher &disp,
1066
    Rosbridge &rb) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1067

1068
  // qDebug() << "_callAddTiles called";
1069

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1070
  // create json object
1071
  QJsonArray jsonTileArray;
1072
  for (auto &&tile : tileArray) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1073
    using namespace ros_bridge::messages;
1074 1075
    QJsonObject jsonTile;
    if (!nemo_msgs::tile::toJson(*tile, jsonTile)) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1076 1077 1078 1079 1080
      qCDebug(NemoInterfaceLog)
          << "addTiles(): not able to create json object: tile id: "
          << tile->id() << " progress: " << tile->progress()
          << " points: " << tile->tile();
    }
1081
    jsonTileArray.append(std::move(jsonTile));
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1082
  } // for
1083

1084 1085
  QJsonObject req;
  req["in_tile_array"] = std::move(jsonTileArray);
1086

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1087 1088 1089
  // create response handler.
  auto promise_response = std::make_shared<std::promise<bool>>();
  auto future_response = promise_response->get_future();
1090 1091
  auto responseHandler = [promise_response](const QJsonObject &o) mutable {
    // check if transaction was successfull
1092 1093
    if (o.contains("success") && o["success"].isBool()) {
      promise_response->set_value(o["success"].toBool());
1094
    } else {
1095
      qCWarning(NemoInterfaceLog)
1096
          << addTilesService << " no \"success\" key or wrong type: " << o;
1097 1098 1099
      promise_response->set_value(false);
    }
  };
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1100 1101

  // call service.
1102
  rb.callService(addTilesService, responseHandler, req);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1103 1104 1105 1106

  // wait for response.
  auto tStart = hrc::now();
  bool abort = true;
1107 1108 1109
  while (hrc::now() - tStart < maxResponseTime &&
         !disp.isInterruptionRequested()) {
    auto status = future_response.wait_for(std::chrono::milliseconds(10));
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1110 1111 1112 1113
    if (status == std::future_status::ready) {
      abort = false;
      break;
    }
1114
  }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1115 1116 1117 1118

  if (abort) {
    qCWarning(NemoInterfaceLog)
        << "addTiles(): Websocket not responding to request.";
1119
    return false;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1120 1121 1122 1123
  }

  // transaction error?
  if (!future_response.get()) {
1124
    return false;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1125 1126 1127
  }

  // return success
1128
  return true;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1129 1130
}

1131 1132
bool NemoInterface::Impl::_callRemoveTiles(const IDArray &pIdArray,
                                           Dispatcher &disp, Rosbridge &rb) {
1133
  // qDebug() << "_callRemoveTiles called";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1134

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1135
  // create json object
1136
  QJsonArray jsonIdArray;
1137
  for (auto &&id : pIdArray) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1138
    using namespace ros_bridge::messages;
1139
    jsonIdArray.append(id);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1140
  } // for
1141 1142
  QJsonObject req;
  req["ids"] = std::move(jsonIdArray);
1143

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1144 1145 1146
  // create response handler.
  auto promise_response = std::make_shared<std::promise<bool>>();
  auto future_response = promise_response->get_future();
1147 1148 1149
  auto responseHandler = [promise_response](const QJsonObject &o) mutable {
    // check if transaction was successfull
    QString msg = QJsonDocument(o).toJson(QJsonDocument::JsonFormat::Compact);
1150 1151
    if (o.contains("success") && o["success"].isBool()) {
      promise_response->set_value(o["success"].toBool());
1152
    } else {
1153
      qCWarning(NemoInterfaceLog)
1154
          << removeTilesService << " no \"success\" key or wrong type: " << msg;
1155 1156 1157
      promise_response->set_value(false);
    }
  };
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1158 1159

  // call service.
1160
  rb.callService(removeTilesService, responseHandler, req);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1161 1162 1163 1164

  // wait for response.
  auto tStart = hrc::now();
  bool abort = true;
1165 1166 1167
  while (hrc::now() - tStart < maxResponseTime &&
         !disp.isInterruptionRequested()) {
    auto status = future_response.wait_for(std::chrono::milliseconds(10));
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1168 1169 1170 1171
    if (status == std::future_status::ready) {
      abort = false;
      break;
    }
1172
  }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1173 1174 1175 1176

  if (abort) {
    qCWarning(NemoInterfaceLog)
        << "remove_tiles(): Websocket not responding to request.";
1177
    return false;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1178 1179 1180 1181
  }

  // transaction error?
  if (!future_response.get()) {
1182
    return false;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1183 1184
  }

1185
  return true;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1186 1187
}

1188
bool NemoInterface::Impl::_callClearTiles(Dispatcher &disp, Rosbridge &rb) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1189

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1190 1191 1192
  // create response handler.
  auto promise_response = std::make_shared<std::promise<bool>>();
  auto future_response = promise_response->get_future();
1193
  auto responseHandler = [promise_response](const QJsonObject &) mutable {
1194
    // check if transaction was successfull
1195
    promise_response->set_value(true);
1196
  };
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1197 1198

  // call service.
1199
  rb.callService(clearTilesService, responseHandler, QJsonObject());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1200 1201 1202 1203

  // wait for response.
  auto tStart = hrc::now();
  bool abort = true;
1204 1205 1206
  while (hrc::now() - tStart < maxResponseTime &&
         !disp.isInterruptionRequested()) {
    auto status = future_response.wait_for(std::chrono::milliseconds(10));
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1207 1208 1209 1210
    if (status == std::future_status::ready) {
      abort = false;
      break;
    }
1211
  }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1212 1213 1214

  if (abort) {
    qCWarning(NemoInterfaceLog) << "Websocket not responding to request.";
1215
    return false;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1216 1217 1218 1219
  }

  // transaction failed?
  if (!future_response.get()) {
1220
    return false;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1221 1222
  }

1223
  return true;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1224 1225
}

1226 1227 1228
ProgressArray NemoInterface::Impl::_callGetProgress(const IDArray &pIdArray,
                                                    Dispatcher &disp,
                                                    Rosbridge &rb) {
1229
  // qDebug() << "_callGetProgress called";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1230

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1231
  // create json object
1232
  QJsonArray jsonIdArray;
1233
  for (auto &&id : pIdArray) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1234
    using namespace ros_bridge::messages;
1235
    jsonIdArray.append(id);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1236
  } // for
1237 1238
  QJsonObject req;
  req["ids"] = std::move(jsonIdArray);
1239

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1240 1241 1242 1243
  // create response handler.
  typedef std::shared_ptr<ProgressArray> ResponseType;
  auto promise_response = std::make_shared<std::promise<ResponseType>>();
  auto future_response = promise_response->get_future();
1244 1245
  auto responseHandler = [promise_response](const QJsonObject &o) mutable {
    // check if transaction was successfull
1246 1247 1248 1249 1250 1251 1252
    ros_bridge::messages::nemo_msgs::progress_array::ProgressArray
        progressArrayMsg;
    if (ros_bridge::messages::nemo_msgs::progress_array::fromJson(
            o, progressArrayMsg)) {
      auto pArray = std::make_shared<ProgressArray>();
      *pArray = std::move(progressArrayMsg.progress_array());
      promise_response->set_value(pArray);
1253
    } else {
1254 1255 1256
      qCWarning(NemoInterfaceLog) << getProgressService
                                  << " error while creating ProgressArray "
                                     "from json.";
1257 1258 1259
      promise_response->set_value(nullptr);
    }
  };
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1260 1261

  // call service.
1262
  rb.callService(getProgressService, responseHandler, req);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1263 1264 1265 1266

  // wait for response.
  auto tStart = hrc::now();
  bool abort = true;
1267 1268 1269
  while (hrc::now() - tStart < maxResponseTime &&
         !disp.isInterruptionRequested()) {
    auto status = future_response.wait_for(std::chrono::milliseconds(10));
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1270 1271 1272 1273
    if (status == std::future_status::ready) {
      abort = false;
      break;
    }
1274
  }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1275 1276 1277

  if (abort) {
    qCWarning(NemoInterfaceLog)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1278
        << "all_remove_tiles(): Websocket not responding to request.";
1279
    return ProgressArray();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1280 1281 1282
  }

  // return success
1283
  return *future_response.get();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1284 1285
}

1286 1287
ProgressArray NemoInterface::Impl::_callGetAllProgress(Dispatcher &disp,
                                                       Rosbridge &rb) {
1288
  // qDebug() << "_callGetAllProgress called";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1289 1290 1291 1292 1293

  // create response handler.
  typedef std::shared_ptr<ProgressArray> ResponseType;
  auto promise_response = std::make_shared<std::promise<ResponseType>>();
  auto future_response = promise_response->get_future();
1294
  auto responseHandler = [promise_response](const QJsonObject &o) mutable {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1295
    // check if transaction was successfull
1296 1297 1298 1299 1300 1301 1302
    ros_bridge::messages::nemo_msgs::progress_array::ProgressArray
        progressArrayMsg;
    if (ros_bridge::messages::nemo_msgs::progress_array::fromJson(
            o, progressArrayMsg)) {
      auto pArray = std::make_shared<ProgressArray>();
      *pArray = std::move(progressArrayMsg.progress_array());
      promise_response->set_value(pArray);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1303
    } else {
1304 1305 1306 1307
      qCWarning(NemoInterfaceLog) << getAllProgressService
                                  << " error while creating ProgressArray "
                                     "from json. msg: "
                                  << o;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1308 1309 1310 1311 1312
      promise_response->set_value(nullptr);
    }
  };

  // call service.
1313
  rb.callService(getAllProgressService, responseHandler, QJsonObject());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1314 1315 1316 1317

  // wait for response.
  auto tStart = hrc::now();
  bool abort = true;
1318 1319 1320
  while (hrc::now() - tStart < maxResponseTime &&
         !disp.isInterruptionRequested()) {
    auto status = future_response.wait_for(std::chrono::milliseconds(10));
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1321 1322 1323 1324
    if (status == std::future_status::ready) {
      abort = false;
      break;
    }
1325
  }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1326 1327 1328

  if (abort) {
    qCWarning(NemoInterfaceLog)
1329 1330
        << "_callGetAllProgress(): Websocket not responding to request.";
    return ProgressArray();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1331 1332 1333
  }

  // transaction error?
1334
  return *future_response.get();
1335 1336
}

1337 1338
QVector<std::shared_ptr<Tile>>
NemoInterface::Impl::_callGetAllTiles(Dispatcher &disp, Rosbridge &rb) {
1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359
  // qDebug() << "_callGetAllProgress called";

  // create response handler.
  typedef std::shared_ptr<QVector<std::shared_ptr<Tile>>> ResponseType;
  auto promise_response = std::make_shared<std::promise<ResponseType>>();
  auto future_response = promise_response->get_future();

  auto responseHandler = [promise_response](const QJsonObject &o) mutable {
    const char *const tileArrayKey = "tile_array";
    // check if transaction was successfull
    if (o.contains(tileArrayKey) && o[tileArrayKey].isArray()) {
      auto pArray = std::make_shared<QVector<std::shared_ptr<Tile>>>();
      const auto jsonArray = o[tileArrayKey].toArray();
      for (int i = 0; i < jsonArray.size(); ++i) {
        if (jsonArray[i].isObject()) {
          QJsonObject o = jsonArray[i].toObject();
          auto tile = std::make_shared<Tile>();
          if (ros_bridge::messages::nemo_msgs::tile::fromJson(o, *tile)) {
            pArray->push_back(tile);
          } else {
            qCWarning(NemoInterfaceLog)
1360
                << getAllTilesService << " error while creating tile.";
1361 1362 1363 1364
            promise_response->set_value(nullptr);
          }
        } else {
          qCWarning(NemoInterfaceLog)
1365
              << getAllTilesService << " json array does not contain objects.";
1366 1367 1368 1369 1370 1371 1372
          promise_response->set_value(nullptr);
        }
      }
      // success!
      promise_response->set_value(pArray);
    } else {
      qCWarning(NemoInterfaceLog)
1373
          << getAllTilesService << " no tile_array key or wrong type.";
1374 1375 1376 1377 1378
      promise_response->set_value(nullptr);
    }
  };

  // call service.
1379
  rb.callService(getAllTilesService, responseHandler, QJsonObject());
1380 1381 1382 1383

  // wait for response.
  auto tStart = hrc::now();
  bool abort = true;
1384 1385 1386
  while (hrc::now() - tStart < maxResponseTime &&
         !disp.isInterruptionRequested()) {
    auto status = future_response.wait_for(std::chrono::milliseconds(10));
1387 1388 1389 1390
    if (status == std::future_status::ready) {
      abort = false;
      break;
    }
1391
  }
1392 1393 1394 1395

  if (abort) {
    qCWarning(NemoInterfaceLog)
        << "all_remove_tiles(): Websocket not responding to request.";
1396
    return QVector<std::shared_ptr<Tile>>();
1397 1398
  }

1399
  return *future_response.get();
1400 1401
}

1402
QString NemoInterface::Impl::_callGetVersion(Dispatcher &disp, Rosbridge &rb) {
1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416

  // create response handler.
  typedef QString ResponseType;
  auto promise_response = std::make_shared<std::promise<ResponseType>>();
  auto future_response = promise_response->get_future();

  auto responseHandler = [promise_response](const QJsonObject &o) mutable {
    const char *const versionKey = "version";
    // check if transaction was successfull
    if (o.contains(versionKey) && o[versionKey].isString()) {
      const auto version = o[versionKey].toString();
      promise_response->set_value(version);
    } else {
      qCWarning(NemoInterfaceLog)
1417
          << getVersionService << " no version key or wrong type.";
1418 1419 1420 1421 1422
      promise_response->set_value("error!");
    }
  };

  // call service.
1423
  rb.callService(getVersionService, responseHandler, QJsonObject());
1424 1425 1426 1427

  // wait for response.
  auto tStart = hrc::now();
  bool abort = true;
1428 1429 1430
  while (hrc::now() - tStart < maxResponseTime &&
         !disp.isInterruptionRequested()) {
    auto status = future_response.wait_for(std::chrono::milliseconds(10));
1431 1432 1433 1434
    if (status == std::future_status::ready) {
      abort = false;
      break;
    }
1435
  }
1436 1437 1438 1439

  if (abort) {
    qCWarning(NemoInterfaceLog)
        << "all_remove_tiles(): Websocket not responding to request.";
1440
    return "unknown_version";
1441 1442 1443 1444 1445
  }

  // transaction error?
  auto version = future_response.get();
  if (version == "error!") {
1446
    return "unknown_version";
1447 1448 1449
  }

  // return success
1450
  return version;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1451 1452
}

1453 1454
bool NemoInterface::Impl::_addTilesRemote(
    const QVector<std::shared_ptr<const Tile>> tileArray) {
1455

1456
  // qDebug() << "_addTilesRemote called";
1457

1458 1459 1460
  QVector<std::shared_ptr<Tile>> copy;
  for (auto &&pTile : tileArray) {
    copy.push_back(std::make_shared<Tile>(*pTile));
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1461
  }
1462
  return _addTilesRemote(copy);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1463 1464
}

1465 1466
bool NemoInterface::Impl::_addTilesRemote(
    const QVector<std::shared_ptr<Tile>> &tileArray) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1467

1468
  bool error = false;
1469
  bool anyChange = false;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1470

1471 1472
  Lock lk(this->_m);
  for (auto &&pTile : tileArray) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1473 1474 1475 1476
    auto id = pTile->id();
    auto it = _remoteTiles.find(id);
    if (Q_LIKELY(it == _remoteTiles.end())) {
      auto ret = _remoteTiles.insert(std::make_pair(id, pTile));
1477
      anyChange = true;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1478 1479 1480
      Q_ASSERT(ret.second == true);
      Q_UNUSED(ret);
    } else {
1481
      if (pTile->tile() != it->second->tile()) {
1482 1483
        qCWarning(NemoInterfaceLog)
            << "_addTilesRemote: tiles differ but have the same id.";
1484 1485
        error = true;
      }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1486 1487 1488
    }
  }

1489 1490 1491
  lk.unlock();
  if (anyChange > 0) {
    emit this->_parent->tilesChanged();
1492 1493
  }

1494
  return !error;
1495 1496
}

1497
void NemoInterface::Impl::_removeTilesRemote(const IDArray &idArray) {
1498
  // qDebug() << "_removeTilesRemote called";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1499 1500
  bool anyChange = false;

1501 1502
  Lock lk(this->_m);
  for (auto &&id : idArray) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513
    auto it = _remoteTiles.find(id);
    if (Q_LIKELY(it != _remoteTiles.end())) {
      _remoteTiles.erase(it);
      anyChange = true;
    } else {
      qCWarning(NemoInterfaceLog)
          << "_removeTilesRemote: tile with unknown id " << id << ".";
    }
  }

  if (anyChange) {
1514
    emit this->_parent->tilesChanged();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1515
  }
1516
}
1517

1518 1519 1520
void NemoInterface::Impl::_clearTilesRemote() {
  Lock lk(this->_m);
  _clearTilesRemoteNotSafe();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1521 1522
}

1523
void NemoInterface::Impl::_clearTilesRemoteNotSafe() {
1524
  // qDebug() << "_clearTilesRemote called";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1525 1526 1527 1528 1529
  if (_remoteTiles.size() > 0) {
    _remoteTiles.clear();
  }
}

1530
bool NemoInterface::Impl::_setStateNotSafe(STATE newState) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1531
  if (newState != this->_state) {
1532
    auto oldState = this->_state;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1533 1534 1535 1536 1537 1538 1539
    this->_state = newState;

    qCDebug(NemoInterfaceLog)
        << "state: " << _toString(oldState) << " -> " << _toString(newState);
    auto oldStatus = _status(oldState);
    auto newStatus = _status(newState);
    if (oldStatus != newStatus) {
1540 1541 1542
      // emit signal later
      QTimer::singleShot(5, this->_parent,
                         [this] { emit this->_parent->statusChanged(); });
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1543 1544
    }

1545 1546 1547 1548
    if (_runningNotSafe(oldState) != _runningNotSafe(newState)) {
      // emit signal later
      QTimer::singleShot(5, this->_parent,
                         [this] { emit this->_parent->runningChanged(); });
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1549 1550
    }

1551 1552 1553 1554 1555 1556
    return true;
  } else {
    return false;
  }
}

1557
bool NemoInterface::Impl::_readyNotSafe(NemoInterface::Impl::STATE s) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1558 1559 1560
  return s == STATE::READY;
}

1561
bool NemoInterface::Impl::_userSyncNotSafe(NemoInterface::Impl::STATE s) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1562
  return s == STATE::USER_SYNC;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1563 1564
}

1565
bool NemoInterface::Impl::_sysSyncNotSafe(NemoInterface::Impl::STATE s) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1566
  return s == STATE::SYS_SYNC;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1567 1568
}

1569
bool NemoInterface::Impl::_runningNotSafe(NemoInterface::Impl::STATE s) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1570 1571 1572
  return s != STATE::STOPPED;
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585
NemoInterface::STATUS
NemoInterface::Impl::_status(NemoInterface::Impl::STATE state) {
  NemoInterface::STATUS status;
  switch (state) {
  case STATE::STOPPED:
    status = NemoInterface::STATUS::NOT_CONNECTED;
    break;
  case STATE::START_BRIDGE:
    status = NemoInterface::STATUS::NOT_CONNECTED;
    break;
  case STATE::WEBSOCKET_DETECTED:
    status = NemoInterface::STATUS::WEBSOCKET_DETECTED;
    break;
1586
  case STATE::TRY_SETUP:
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1587 1588 1589 1590 1591
    status = NemoInterface::STATUS::WEBSOCKET_DETECTED;
    break;
  case STATE::READY:
    status = NemoInterface::STATUS::READY;
    break;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1592 1593
  case STATE::USER_SYNC:
  case STATE::SYS_SYNC:
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1594 1595
    status = NemoInterface::STATUS::SYNC;
    break;
1596 1597 1598
  case STATE::SYNC_ERROR:
    status = NemoInterface::STATUS::ERROR;
    break;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615
  case STATE::WEBSOCKET_TIMEOUT:
  case STATE::HEARTBEAT_TIMEOUT:
    status = NemoInterface::STATUS::TIMEOUT;
    break;
  }

  return status;
}

QString NemoInterface::Impl::_toString(NemoInterface::Impl::STATE s) {
  switch (s) {
  case STATE::STOPPED:
    return QString("STOPPED");
  case STATE::START_BRIDGE:
    return QString("START_BRIDGE");
  case STATE::WEBSOCKET_DETECTED:
    return QString("WEBSOCKET_DETECTED");
1616
  case STATE::TRY_SETUP:
1617
    return QString("TRY_SETUP");
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1618 1619
  case STATE::READY:
    return QString("READY");
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1620
  case STATE::USER_SYNC:
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1621
    return QString("SYNC_USER");
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1622
  case STATE::SYS_SYNC:
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1623
    return QString("SYNC_SYS");
1624 1625
  case STATE::SYNC_ERROR:
    return QString("SYNC_ERROR");
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637
  case STATE::WEBSOCKET_TIMEOUT:
    return QString("WEBSOCKET_TIMEOUT");
  case STATE::HEARTBEAT_TIMEOUT:
    return QString("HEARTBEAT_TIMEOUT");
  }
  return "unknown state!";
}

QString NemoInterface::Impl::_toString(NemoInterface::STATUS s) {
  switch (s) {
  case NemoInterface::STATUS::NOT_CONNECTED:
    return QString("NOT_CONNECTED");
1638 1639
  case NemoInterface::STATUS::ERROR:
    return QString("ERROR");
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651
  case NemoInterface::STATUS::READY:
    return QString("READY");
  case NemoInterface::STATUS::TIMEOUT:
    return QString("TIMEOUT");
  case NemoInterface::STATUS::WEBSOCKET_DETECTED:
    return QString("WEBSOCKET_DETECTED");
  case NemoInterface::STATUS::SYNC:
    return QString("SYNC");
  }
  return "unknown state!";
}

1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668
bool NemoInterface::Impl::_addTilesImpl(
    std::shared_ptr<QVector<std::shared_ptr<const Tile>>> pTileArray,
    std::shared_ptr<const IDArray> pIdArray) {
  auto rb = this->_pRosbridge;
  auto &disp = this->_dispatcher;

  // add tiles
  bool success = this->_callAddTiles(*pTileArray, disp, *rb);
  if (!success) {
    this->_setWarningString(
        "Adding tiles failed. This might indicate a poor connection.");
    return false;
  }

  auto ret = this->_addTilesRemote(*pTileArray);
  if (!ret) {
    Lock lk(this->_m);
1669 1670 1671 1672 1673 1674 1675 1676
    if (this->_state == STATE::SYS_SYNC || this->_state == STATE::USER_SYNC) {
      this->_setStateNotSafe(STATE::SYNC_ERROR);
      this->_doActionNotSafe();
      lk.unlock();
      this->_setWarningString("Adding tiles failed.");
      qCDebug(NemoInterfaceLog) << "_addTilesImpl(): _addTilesRemote return "
                                   "false: different tiles with same id.";
    }
1677 1678 1679 1680 1681 1682
    return false;
  }

  // fetch progress
  auto array = this->_callGetProgress(*pIdArray, disp, *rb);
  if (array.size() != pIdArray->size()) {
1683 1684 1685 1686 1687 1688 1689 1690
    Lock lk1(this->_m);
    if (this->_state == STATE::SYS_SYNC || this->_state == STATE::USER_SYNC) {
      this->_setStateNotSafe(STATE::SYNC_ERROR);
      this->_doActionNotSafe();
      lk1.unlock();
      this->_setWarningString("Getting progress failed. This might "
                              "indicate a poor connection.");
    }
1691 1692 1693 1694 1695
    return false;
  }
  ret = this->_updateProgress(array);

  if (!ret) {
1696 1697 1698 1699 1700 1701 1702 1703 1704
    Lock lk2(this->_m);
    if (this->_state == STATE::SYS_SYNC || this->_state == STATE::USER_SYNC) {
      this->_setStateNotSafe(STATE::SYNC_ERROR);
      this->_doActionNotSafe();
      lk2.unlock();
      this->_setWarningString("Getting progress failed.");
      qCDebug(NemoInterfaceLog)
          << "_addTilesImpl(): _updateProgress failed: unknown id.";
    }
1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718
    return false;
  }

  return true;
}

bool NemoInterface::Impl::_removeTilesImpl(
    std::shared_ptr<const IDArray> pIdArray) {
  auto rb = this->_pRosbridge;
  auto &disp = this->_dispatcher;

  auto success = this->_callRemoveTiles(*pIdArray, disp, *rb);
  if (!success) {
    Lock lk(this->_m);
1719 1720 1721 1722 1723 1724 1725
    if (this->_state == STATE::SYS_SYNC || this->_state == STATE::USER_SYNC) {
      this->_setStateNotSafe(STATE::SYNC_ERROR);
      this->_doActionNotSafe();
      lk.unlock();
      this->_setWarningString("Removing tiles failed. This might "
                              "indicate a poor connection.");
    }
1726 1727
    return false;
  }
1728

1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740
  this->_removeTilesRemote(*pIdArray);

  return true;
}

bool NemoInterface::Impl::_clearTilesImpl() {
  auto rb = this->_pRosbridge;
  auto &disp = this->_dispatcher;

  auto success = this->_callClearTiles(disp, *rb);
  if (!success) {
    Lock lk(this->_m);
1741 1742 1743 1744 1745 1746 1747
    if (this->_state == STATE::SYS_SYNC || this->_state == STATE::USER_SYNC) {
      this->_setStateNotSafe(STATE::SYNC_ERROR);
      this->_doActionNotSafe();
      lk.unlock();
      this->_setWarningString("Clear tiles failed. This might "
                              "indicate a poor connection.");
    }
1748 1749 1750 1751 1752 1753 1754
    return false;
  }
  this->_clearTilesRemote();

  return true;
}

1755 1756
// ===============================================================
// NemoInterface
1757 1758 1759 1760 1761 1762 1763 1764 1765
NemoInterface::NemoInterface()
    : QObject(), pImpl(std::make_unique<NemoInterface::Impl>(this)) {}

NemoInterface *NemoInterface::createInstance() { return new NemoInterface(); }

NemoInterface *NemoInterface::instance() {
  return GenericSingelton<NemoInterface>::instance(
      NemoInterface::createInstance);
}
1766 1767 1768 1769 1770 1771 1772

NemoInterface::~NemoInterface() {}

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

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

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1773 1774 1775 1776 1777 1778 1779
std::shared_future<QVariant>
NemoInterface::addTiles(const TileArray &tileArray) {
  TilePtrArray ptrArray;
  for (const auto &tile : tileArray) {
    ptrArray.push_back(const_cast<MeasurementTile *>(&tile));
  }
  return this->pImpl->addTiles(ptrArray);
1780 1781
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1782 1783 1784
std::shared_future<QVariant>
NemoInterface::addTiles(const TilePtrArray &tileArray) {
  return this->pImpl->addTiles(tileArray);
1785 1786
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1787 1788 1789 1790 1791 1792 1793 1794
std::shared_future<QVariant>
NemoInterface::removeTiles(const IDArray &idArray) {
  return this->pImpl->removeTiles(idArray);
}

std::shared_future<QVariant> NemoInterface::clearTiles() {
  return this->pImpl->clearTiles();
}
1795

1796
TileArray NemoInterface::getTiles(const IDArray &idArray) const {
1797 1798 1799
  return this->pImpl->getTiles(idArray);
}

1800 1801 1802
TileArray NemoInterface::getAllTiles() const {
  return this->pImpl->getAllTiles();
}
1803

1804
LogicalArray NemoInterface::containsTiles(const IDArray &idArray) const {
1805 1806 1807
  return this->pImpl->containsTiles(idArray);
}

1808
std::size_t NemoInterface::size() const { return this->pImpl->size(); }
1809

1810
bool NemoInterface::empty() const { return this->pImpl->empty(); }
1811

1812
ProgressArray NemoInterface::getProgress() const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1813 1814 1815
  return this->pImpl->getProgress();
}

1816
ProgressArray NemoInterface::getProgress(const IDArray &idArray) const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1817 1818 1819
  return this->pImpl->getProgress(idArray);
}

1820
NemoInterface::STATUS NemoInterface::status() const {
1821 1822 1823 1824 1825 1826 1827
  return this->pImpl->status();
}

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

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1828 1829 1830 1831 1832
QString NemoInterface::infoString() const { return this->pImpl->infoString(); }

QString NemoInterface::warningString() const {
  return this->pImpl->warningString();
}
1833 1834 1835 1836 1837

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

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