#include #include #include #include #include #include #include #include #include #include #include #include namespace scwx { namespace qt { namespace manager { static const std::string logPrefix_ = "scwx::qt::manager::position_manager"; static const auto logger_ = scwx::util::Logger::Create(logPrefix_); class PositionManager::Impl { public: explicit Impl(PositionManager* self) : self_ {self}, trackingUuid_ {boost::uuids::random_generator()()} { auto& generalSettings = settings::GeneralSettings::Instance(); gpsParent_->moveToThread(gpsThread_); logger_->debug( "Available sources: {}", QGeoPositionInfoSource::availableSources().join(", ").toStdString()); CreatePositionSourceAsync(); positioningPluginCallbackUuid_ = generalSettings.positioning_plugin().RegisterValueChangedCallback( [this](const std::string&) { createPositionSourcePending_ = true; }); nmeaBaudRateCallbackUuid_ = generalSettings.nmea_baud_rate().RegisterValueChangedCallback( [this](const std::int64_t&) { createPositionSourcePending_ = true; }); nmeaSourceCallbackUuid_ = generalSettings.nmea_source().RegisterValueChangedCallback( [this](const std::string&) { createPositionSourcePending_ = true; }); connect(&SettingsManager::Instance(), &SettingsManager::SettingsSaved, self_, [this]() { if (createPositionSourcePending_) { CreatePositionSourceAsync(); } }); } ~Impl() { auto& generalSettings = settings::GeneralSettings::Instance(); generalSettings.positioning_plugin().UnregisterValueChangedCallback( positioningPluginCallbackUuid_); generalSettings.nmea_baud_rate().UnregisterValueChangedCallback( nmeaBaudRateCallbackUuid_); generalSettings.nmea_source().UnregisterValueChangedCallback( nmeaSourceCallbackUuid_); gpsParent_->deleteLater(); } void CreatePositionSource(); void CreatePositionSourceAsync(); void EnablePositionUpdates(boost::uuids::uuid uuid, bool enabled); PositionManager* self_; QThread* gpsThread_ {ThreadManager::Instance().thread("position_manager")}; boost::uuids::uuid trackingUuid_; bool trackingEnabled_ {false}; std::set uuids_ {}; std::mutex positionSourceMutex_ {}; QObject* gpsParent_ {new QObject}; QGeoPositionInfoSource* geoPositionInfoSource_ {}; QGeoPositionInfo position_ {}; types::PositioningPlugin lastPositioningPlugin_ { types::PositioningPlugin::Unknown}; std::int64_t lastNmeaBaudRate_ {-1}; std::string lastNmeaSource_ {"?"}; boost::uuids::uuid positioningPluginCallbackUuid_ {}; boost::uuids::uuid nmeaBaudRateCallbackUuid_ {}; boost::uuids::uuid nmeaSourceCallbackUuid_ {}; bool createPositionSourcePending_ {false}; }; PositionManager::PositionManager() : p(std::make_unique(this)) {} PositionManager::~PositionManager() = default; QGeoPositionInfo PositionManager::position() const { return p->position_; } bool PositionManager::IsLocationTracked() { return p->trackingEnabled_; } void PositionManager::Impl::CreatePositionSourceAsync() { QMetaObject::invokeMethod(QAbstractEventDispatcher::instance(gpsThread_), [this]() { CreatePositionSource(); }); } void PositionManager::Impl::CreatePositionSource() { auto& generalSettings = settings::GeneralSettings::Instance(); createPositionSourcePending_ = false; types::PositioningPlugin positioningPlugin = types::GetPositioningPlugin( generalSettings.positioning_plugin().GetValue()); std::int64_t nmeaBaudRate = generalSettings.nmea_baud_rate().GetValue(); std::string nmeaSource = generalSettings.nmea_source().GetValue(); if (positioningPlugin == lastPositioningPlugin_ && nmeaBaudRate == lastNmeaBaudRate_ && nmeaSource == lastNmeaSource_) { return; } QGeoPositionInfoSource* positionSource = nullptr; // TODO: macOS requires permission if (positioningPlugin == types::PositioningPlugin::Default) { positionSource = QGeoPositionInfoSource::createDefaultSource(gpsParent_); } else if (positioningPlugin == types::PositioningPlugin::Nmea) { QVariantMap params {}; params["nmea.source"] = QString::fromStdString(nmeaSource); params["nmea.baudrate"] = static_cast(nmeaBaudRate); positionSource = QGeoPositionInfoSource::createSource("nmea", params, gpsParent_); } if (positionSource != nullptr) { logger_->debug("Using position source: {}", positionSource->sourceName().toStdString()); QObject::connect(positionSource, &QGeoPositionInfoSource::positionUpdated, self_, [this](const QGeoPositionInfo& info) { auto coordinate = info.coordinate(); if (coordinate != position_.coordinate()) { logger_->trace("Position updated: {}, {}", coordinate.latitude(), coordinate.longitude()); } position_ = info; Q_EMIT self_->PositionUpdated(info); }); } else { logger_->error("Unable to create position source for plugin: {}", types::GetPositioningPluginName(positioningPlugin)); return; } lastPositioningPlugin_ = positioningPlugin; lastNmeaBaudRate_ = nmeaBaudRate; lastNmeaSource_ = nmeaSource; std::unique_lock lock {positionSourceMutex_}; if (geoPositionInfoSource_ != nullptr) { geoPositionInfoSource_->stopUpdates(); delete geoPositionInfoSource_; } geoPositionInfoSource_ = positionSource; if (!uuids_.empty()) { positionSource->startUpdates(); } } void PositionManager::EnablePositionUpdates(boost::uuids::uuid uuid, bool enabled) { QMetaObject::invokeMethod(QAbstractEventDispatcher::instance(p->gpsThread_), [=, this]() { p->EnablePositionUpdates(uuid, enabled); }); } void PositionManager::Impl::EnablePositionUpdates(boost::uuids::uuid uuid, bool enabled) { std::unique_lock lock {positionSourceMutex_}; if (geoPositionInfoSource_ == nullptr) { return; } if (enabled) { if (uuids_.empty()) { geoPositionInfoSource_->startUpdates(); } uuids_.insert(uuid); } else { uuids_.erase(uuid); if (uuids_.empty()) { geoPositionInfoSource_->stopUpdates(); } } } void PositionManager::TrackLocation(bool trackingEnabled) { p->trackingEnabled_ = trackingEnabled; EnablePositionUpdates(p->trackingUuid_, trackingEnabled); Q_EMIT LocationTrackingChanged(trackingEnabled); } std::shared_ptr PositionManager::Instance() { static std::weak_ptr positionManagerReference_ {}; static std::mutex instanceMutex_ {}; std::unique_lock lock(instanceMutex_); std::shared_ptr positionManager = positionManagerReference_.lock(); if (positionManager == nullptr) { positionManager = std::make_shared(); positionManagerReference_ = positionManager; } return positionManager; } } // namespace manager } // namespace qt } // namespace scwx